0

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Andrew Chin
2011-08-05 08:40:52 -04:00
47 changed files with 877 additions and 316 deletions

16
.gitignore vendored
View File

@@ -1,5 +1,8 @@
*.pyc
build
MANIFEST
build/
dist/
Minecraft_Overviewer.egg-info
terrain.png
cachedir*
@@ -14,10 +17,13 @@ ImPlatform.h
Imaging.h
# various forms of compiled c_overviewer extensions
c_overviewer.so
c_overviewer.pyd
c_overviewer_d.pyd
c_overviewer.dylib
overviewer_core/c_overviewer.so
overviewer_core/c_overviewer.pyd
overviewer_core/c_overviewer_d.pyd
overviewer_core/c_overviewer.dylib
# generated version file
overviewer_core/overviewer_version.py
# Mac OS X noise
.DS_Store

View File

@@ -4,7 +4,8 @@ Contributors
This file contains a list of every person who has contributed code to
Overviewer. It was created from the git commit log, and should include
everyone, but we may have missed a few.
everyone, but we may have missed a few and it is manually updated
now. If you feel like you've been left out, feel free to tell us!
Not currently included (but hopefully soon) are countless testers and bug
reporters that helped fixed the many bugs that have popped up in the course of
@@ -40,14 +41,18 @@ feature.
* arrai <array.of.intellect@gmail.com>
* Kyle Brantley <kyle@averageurl.com>
* but2002 <barryt_9@hotmail.com>
* Eric Carr <eric@carr.no>
* cbarber <CraigBarber@taryx.com>
* Alex Cline <cline@vivisimo.com>
* Andrew Clunis <andrew@orospakr.ca>
* CounterPillow <spam@tes-cheese.ch>
* Stephen Fluin <stephen@mistuph.com>
* Benjamin Herr <ben@0x539.de>
* Ryan Hitchman <hitchmanr@gmail.com>
* Jenny <jennytoo@gmail.com>
* Michael Jensen <emjay1988@gmail.com>
* Maciej Małecki <maciej.malecki@hotmail.com>
* Ryan McCue <ryanmccue@cubegames.net>
* Morlok8k <otis.spankmeyer@gmail.com>
* Gregory Short <gshort2@gmail.com>

9
MANIFEST.in Normal file
View File

@@ -0,0 +1,9 @@
include COPYING.txt
include README.rst
include CONTRIBUTORS.rst
include overviewer.py
include sample.settings.py
recursive-include contrib/ *.py
recursive-include overviewer_core/ *.py
recursive-include overviewer_core/src/ *.c *.h
recursive-include overviewer_core/data/ *

View File

@@ -169,6 +169,10 @@ Options
*Note*: Currently only the overviewer.dat file is deleted when you run with
this option
--forcerender
Force re-rendering the entire map (or the given regionlist). This
is an easier way to completely re-render without deleting the map.
--regionlist=regionlist
Use this option to specify manually a list of regions to consider for
updating. Without this option, every chunk in every region is checked for

View File

@@ -22,14 +22,13 @@ if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
import os
import os.path
from configParser import ConfigOptionParser
import re
import subprocess
import multiprocessing
import time
import logging
import util
import platform
from overviewer_core import util
logging.basicConfig(level=logging.INFO,format="%(asctime)s [%(levelname)s] %(message)s")
@@ -37,10 +36,19 @@ this_dir = util.get_program_path()
# make sure the c_overviewer extension is available
try:
import c_overviewer
from overviewer_core import c_overviewer
except ImportError:
## if this is a frozen windows package, the following error messages about
## building the c_overviewer extension are not appropriate
if hasattr(sys, "frozen"):
print "Something has gone wrong importing the c_overviewer extension. Please"
print "make sure the 2008 and 2010 redistributable packages from Microsoft"
print "are installed."
sys.exit(1)
## try to find the build extension
ext = os.path.join(this_dir, "c_overviewer.%s" % ("pyd" if platform.system() == "Windows" else "so"))
ext = os.path.join(this_dir, "overviewer_core", "c_overviewer.%s" % ("pyd" if platform.system() == "Windows" else "so"))
if os.path.exists(ext):
print "Something has gone wrong importing the c_overviewer extension. Please"
print "make sure it is up-to-date (clean and rebuild)"
@@ -48,14 +56,17 @@ except ImportError:
print "You need to compile the c_overviewer module to run Minecraft Overviewer."
print "Run `python setup.py build`, or see the README for details."
import traceback
traceback.print_exc()
sys.exit(1)
if hasattr(sys, "frozen"):
pass # we don't bother with a compat test since it should always be in sync
elif "extension_version" in dir(c_overviewer):
# check to make sure the binary matches the headers
if os.path.exists(os.path.join(this_dir, "src", "overviewer.h")):
with open(os.path.join(this_dir, "src", "overviewer.h")) as f:
if os.path.exists(os.path.join(this_dir, "overviewer_core", "src", "overviewer.h")):
with open(os.path.join(this_dir, "overviewer_core", "src", "overviewer.h")) as f:
lines = f.readlines()
lines = filter(lambda x: x.startswith("#define OVERVIEWER_EXTENSION_VERSION"), lines)
if lines:
@@ -67,12 +78,10 @@ else:
print "Please rebuild your c_overviewer module. It is out of date!"
sys.exit(1)
from overviewer_core.configParser import ConfigOptionParser
from overviewer_core import optimizeimages, world, quadtree
from overviewer_core import googlemap, rendernode
import optimizeimages
import world
import quadtree
import googlemap
import rendernode
helptext = """
%prog [OPTIONS] <World # / Name / Path to World> <tiles dest dir>
@@ -115,14 +124,14 @@ def main():
if options.version:
print "Minecraft-Overviewer"
print "Git version: %s" % util.findGitVersion()
try:
import overviewer_version
if hasattr(sys, "frozen"):
print "py2exe version build on %s" % overviewer_version.BUILD_DATE
import overviewer_core.overviewer_version as overviewer_version
print "Minecraft-Overviewer %s" % overviewer_version.VERSION
print "Git commit: %s" % overviewer_version.HASH
print "built on %s" % overviewer_version.BUILD_DATE
print "Build machine: %s %s" % (overviewer_version.BUILD_PLATFORM, overviewer_version.BUILD_OS)
except:
print "version info not found"
pass
sys.exit(0)
@@ -168,6 +177,11 @@ def main():
logging.error("Invalid world number")
sys.exit(1)
# final sanity check for worlddir
if not os.path.exists(os.path.join(worlddir, 'level.dat')):
logging.error("Invalid world path -- does not contain level.dat")
sys.exit(1)
if len(args) < 2:
if options.delete:
return delete_all(worlddir, None)

View File

View File

@@ -114,10 +114,10 @@ def get_tileentity_data(level):
return data
# This set holds blocks ids that can be seen through, for occlusion calculations
transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 30, 31, 32, 37, 38,
39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85,
90, 92, 93, 94, 96])
transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33,
34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79,
81, 83, 85, 90, 92, 93, 94, 96])
# This set holds block ids that are solid blocks
solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
@@ -321,67 +321,6 @@ class ChunkRenderer(object):
return self._up_left_skylight
up_left_skylight = property(_load_up_left_skylight)
def generate_pseudo_ancildata(self,x,y,z,blockid, north_position = 0 ):
""" Generates a pseudo ancillary data for blocks that depend of
what are surrounded and don't have ancillary data
This uses a binary number of 4 digits to encode the info.
The encode is:
Bit: 1 2 3 4
Side: x y -x -y
Values: bit = 0 -> The corresponding side block has different blockid
bit = 1 -> The corresponding side block has same blockid
Example: if the bit1 is 1 that means that there is a block with
blockid in the side of the +x direction.
You can rotate the pseudo data multiplying by 2 and
if it is > 15 subtracting 15 and adding 1. (moving bits
in the left direction is like rotate 90 degree in anticlockwise
direction). In this way can be used for maps with other
north orientation.
North position can have the values 0, 1, 2, 3, corresponding to
north in bottom-left, bottom-right, top-right and top-left of
the screen.
The rotation feature is not used anywhere yet.
"""
blocks = self.blocks
up_left_blocks = self.up_left_blocks
up_right_blocks = self.up_right_blocks
left_blocks = self.left_blocks
right_blocks = self.right_blocks
pseudo_data = 0
# first check if we are in the border of a chunk, next check for chunks adjacent to this
# and finally check for a block with same blockid. I we aren't in the border of a chunk,
# check for the block having the sme blockid.
if (up_right_blocks is not None and up_right_blocks[0,y,z] == blockid) if x == 15 else blocks[x+1,y,z] == blockid:
pseudo_data = pseudo_data | 0b1000
if (right_blocks is not None and right_blocks[x,0,z] == blockid) if y == 15 else blocks[x,y + 1,z] == blockid:
pseudo_data = pseudo_data | 0b0100
if (left_blocks is not None and left_blocks[15,y,z] == blockid) if x == 0 else blocks[x - 1,y,z] == blockid:
pseudo_data = pseudo_data | 0b0010
if (up_left_blocks is not None and up_left_blocks[x,15,z] == blockid) if y == 0 else blocks[x,y - 1,z] == blockid:
pseudo_data = pseudo_data | 0b0001
# rotate the bits for other north orientations
while north_position > 0:
pseudo_data *= 2
if pseudo_data > 15:
pseudo_data -= 16
pseudo_data +=1
north_position -= 1
return pseudo_data
def chunk_render(self, img=None, xoff=0, yoff=0, cave=False):
"""Renders a chunk with the given parameters, and returns the image.
If img is given, the chunk is rendered to that image object. Otherwise,

View File

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 563 B

View File

Before

Width:  |  Height:  |  Size: 401 B

After

Width:  |  Height:  |  Size: 401 B

View File

Before

Width:  |  Height:  |  Size: 672 B

After

Width:  |  Height:  |  Size: 672 B

View File

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 374 B

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -100,3 +100,34 @@ body {
background-color: #fff; /* fallback */
background-color: rgba(255,255,255,0.8);
}
#searchControl {
padding: 5px;
height: 20px;
font-family: Arial, sans-serif;
}
#searchControl > input {
border: 2px solid #000;
font-size: 12pt;
width: 20em;
background-colour: #fff;
}
div#searchDropDown {
border: 1px solid #000;
width: 17em;
font-size: 14pt;
background-color: #fff;
display: none;
}
div.searchResultItem {
overflow: hidden;
text-overflow: ellipsis;
}
div.searchResultItem img {
width: 24px;
height: 24px;
}

View File

@@ -96,6 +96,25 @@ var overviewer = {
return div;
};
},
/**
* Quote an arbitrary string for use in a regex matcher.
* WTB parametized regexes, JavaScript...
*
* From http://kevin.vanzonneveld.net
* original by: booeyOH
* improved by: Ates Goral (http://magnetiq.com)
* improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
* bugfixed by: Onno Marsman
* example 1: preg_quote("$40");
* returns 1: '\$40'
* example 2: preg_quote("*RRRING* Hello?");
* returns 2: '\*RRRING\* Hello\?'
* example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
* returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:'
*/
"pregQuote": function(str) {
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
},
/**
* Setup the varous mapTypes before we actually create the map. This used
* to be a bunch of crap down at the bottom of functions.js
@@ -146,6 +165,9 @@ var overviewer = {
var zoom = overviewerConfig.map.defaultZoom;
var mapcenter;
var queryParams = overviewer.util.parseQueryString();
if (queryParams.debug) {
overviewerConfig.map.debug=true;
}
if (queryParams.lat) {
lat = parseFloat(queryParams.lat);
}
@@ -291,6 +313,9 @@ var overviewer = {
'title': jQuery.trim(item.msg),
'icon': overviewerConfig.CONST.image.queryMarker
});
google.maps.event.addListener(marker, 'click', function(){ marker.setVisible(false); });
continue;
}
@@ -317,6 +342,7 @@ var overviewer = {
'icon': iconURL,
'visible': false
});
item.marker = marker;
overviewer.util.debug(label);
overviewer.collections.markers[label].push(marker);
if (item.type == 'sign') {
@@ -339,6 +365,7 @@ var overviewer = {
'icon': iconURL,
'visible': false
});
item.marker = marker;
if (overviewer.collections.markers['__others__']) {
overviewer.collections.markers['__others__'].push(marker);
} else {
@@ -370,16 +397,11 @@ var overviewer = {
point.x, point.y, point.z));
}
for (k in overviewerConfig.objectGroups.regions) {
var regionGroup = overviewerConfig.objectGroups.regions[k];
var clickable = regionGroup.clickable;
var label = regionGroup.label;
if(region.label) {
var name = region.label
if (region.label) {
var name = region.label;
} else {
var name = 'rawr';
clickable = false; // if it doesn't have a name, we dont have to show it.
var name = "rawr";
}
if(region.opacity) {
@@ -390,39 +412,61 @@ var overviewer = {
var fillOpacity = region.fillOpacity;
}
var shapeOptions = {
'name': name,
'geodesic': false,
'map': null,
'strokeColor': region.color,
'strokeOpacity': strokeOpacity,
'strokeWeight': overviewerConfig.CONST.regionStrokeWeight,
'zIndex': j
};
if (region.closed) {
var shape = new google.maps.Polygon({
'name': name,
'clickable': clickable,
'geodesic': false,
'map': null,
'strokeColor': region.color,
'strokeOpacity': strokeOpacity,
'strokeWeight': overviewerConfig.CONST.regionStrokeWeight,
'fillColor': region.color,
'fillOpacity': fillOpacity,
'zIndex': j,
'paths': converted
});
shapeOptions["fillColor"] = region.color;
shapeOptions["fillOpacity"] = fillOpacity;
shapeOptions["paths"] = converted;
} else {
var shape = new google.maps.Polyline({
'name': name,
'clickable': clickable,
'geodesic': false,
'map': null,
'strokeColor': region.color,
'strokeOpacity': strokeOpacity,
'strokeWeight': overviewerConfig.CONST.regionStrokeWeight,
'zIndex': j,
'path': converted
});
shapeOptions["path"] = converted;
}
var matched = false;
for (k in overviewerConfig.objectGroups.regions) {
var regionGroup = overviewerConfig.objectGroups.regions[k];
var clickable = regionGroup.clickable;
var label = regionGroup.label;
if (!regionGroup.match(region))
continue;
matched = true;
if (!region.label) {
clickable = false; // if it doesn't have a name, we dont have to show it.
}
if (region.closed) {
var shape = new google.maps.Polygon(shapeOptions);
} else {
var shape = new google.maps.Polyline(shapeOptions);
}
overviewer.collections.regions[label].push(shape);
if (clickable) {
overviewer.util.createRegionInfoWindow(shape);
}
}
// if we haven't matched anything, go ahead and add it
if (!matched) {
if (region.closed) {
var shape = new google.maps.Polygon(shapeOptions);
} else {
var shape = new google.maps.Polyline(shapeOptions);
}
shape.setMap(overviewer.map);
}
}
}
},
@@ -490,7 +534,7 @@ var overviewer = {
// the width and height of all the highest-zoom tiles combined,
// inverted
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize *
Math.pow(2, overviewerConfig.map.maxZoom));
Math.pow(2, overviewerConfig.map.zoomLevels));
// This information about where the center column is may change with
// a different drawing implementation -- check it again after any
@@ -498,13 +542,13 @@ var overviewer = {
// point (0, 0, 127) is at (0.5, 0.0) of tile (tiles/2 - 1, tiles/2)
// so the Y coordinate is at 0.5, and the X is at 0.5 -
// ((tileSize / 2) / (tileSize * 2^maxZoom))
// or equivalently, 0.5 - (1 / 2^(maxZoom + 1))
var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1));
// ((tileSize / 2) / (tileSize * 2^zoomLevels))
// or equivalently, 0.5 - (1 / 2^(zoomLevels + 1))
var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.zoomLevels + 1));
var lat = 0.5;
// the following metrics mimic those in ChunkRenderer.chunk_render
// in "chunk.py" or, equivalently, chunk_render in src/iterate.c
// the following metrics mimic those in
// chunk_render in src/iterate.c
// each block on X axis adds 12px to x and subtracts 6px from y
lng += 12 * x * perPixel;
@@ -542,11 +586,11 @@ var overviewer = {
// the width and height of all the highest-zoom tiles combined,
// inverted
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize *
Math.pow(2, overviewerConfig.map.maxZoom));
Math.pow(2, overviewerConfig.map.zoomLevels));
// Revert base positioning
// See equivalent code in fromWorldToLatLng()
lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1));
lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.zoomLevels + 1));
lat -= 0.5;
// I'll admit, I plugged this into Wolfram Alpha:
@@ -595,7 +639,10 @@ var overviewer = {
var coordsDiv = document.createElement('DIV');
coordsDiv.id = 'coordsDiv';
coordsDiv.innerHTML = '';
if (overviewerConfig.map.controls.coordsBox) {
overviewer.map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(coordsDiv);
}
// Update coords on mousemove
google.maps.event.addListener(overviewer.map, 'mousemove', function (event) {
var worldcoords = overviewer.util.fromLatLngToWorld(event.latLng.lat(), event.latLng.lng());
@@ -662,7 +709,7 @@ var overviewer = {
overviewer.util.createDropDown('Regions', items);
}
if (overviewer.collections.overlays.length > 0) {
if (overviewerConfig.map.controls.overlays && overviewer.collections.overlays.length > 0) {
// overlay maps control
var items = [];
for (i in overviewer.collections.overlays) {
@@ -690,6 +737,9 @@ var overviewer = {
}
overviewer.util.createDropDown('Overlays', items);
}
// call out to create search box, as it's pretty complicated
overviewer.util.createSearchBox();
},
/**
* Reusable method for creating drop-down menus
@@ -758,6 +808,79 @@ var overviewer = {
itemDiv.appendChild(textNode);
}
},
/**
* Create search box and dropdown in the top right google maps area.
*/
'createSearchBox': function() {
var searchControl = document.createElement("div");
searchControl.id = "searchControl";
var searchInput = document.createElement("input");
searchInput.type = "text";
searchInput.value = "Sign Search"
searchInput.title = "Sign Search"
/* Hey dawg, I heard you like functions.
* So we defined a function inside your function.
*/
searchInput.onfocus = function() {
if (searchInput.value == "Sign Search") {
searchInput.value = "";
}
};
searchInput.onblur = function() {
if (searchInput.value == "") {
searchInput.value = "Sign Search";
}
};
searchControl.appendChild(searchInput);
var searchDropDown = document.createElement("div");
searchDropDown.id = "searchDropDown";
searchControl.appendChild(searchDropDown);
$(searchInput).keyup(function(e) {
var newline_stripper = new RegExp("[\\r\\n]", "g")
if(searchInput.value.length !== 0) {
$(searchDropDown).fadeIn();
$(searchDropDown).empty();
overviewer.collections.markerDatas.forEach(function(marker_list) {
marker_list.forEach(function(sign) {
var regex = new RegExp(overviewer.util.pregQuote(searchInput.value), "mi");
if(sign.msg.match(regex)) {
if(sign.marker !== undefined && sign.marker.getVisible()) {
var t = document.createElement("div");
t.className = "searchResultItem";
var i = document.createElement("img");
i.src = sign.marker.getIcon();
t.appendChild(i);
var s = document.createElement("span");
$(s).text(sign.msg.replace(newline_stripper, ""));
t.appendChild(s);
searchDropDown.appendChild(t);
$(t).click(function(e) {
$(searchDropDown).fadeOut();
overviewer.map.setZoom(7);
overviewer.map.setCenter(sign.marker.getPosition());
});
}
}
});
});
} else {
$(searchDropDown).fadeOut();
}
});
if (overviewerConfig.map.controls.searchBox) {
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchControl);
}
},
/**
* Create the pop-up infobox for when you click on a region, this can't
* be done in-line because of stupid Javascript scoping problems with

View File

@@ -50,9 +50,18 @@ var overviewerConfig = {
*/
'mapType': true,
/**
* The small box at the bottom that displays the link to the current map view.
* The coordsBox control is the box showing the XYZ coordinates
* under the cursor.
*/
'link': true
'coordsBox': true,
/**
* The overlays control is the drop-down box for selecting overlays.
*/
'overlays': true,
/**
* The searchBox control is the search box for markers.
*/
'searchBox': true
},
/**
* The zoom level when the page is loaded without a specific zoom setting
@@ -66,6 +75,12 @@ var overviewerConfig = {
* This controls how close you can zoom in.
*/
'maxZoom': {maxzoom},
/**
* This tells us how many total zoom levels Overviewer rendered.
* DO NOT change this, even if you change minZoom and maxZoom, because
* it's used for marker position calculations and map resizing.
*/
'zoomLevels': {zoomlevels},
/**
* Center on this point, in world coordinates. Should be an array, ex:
* [0,0,0]

View File

Before

Width:  |  Height:  |  Size: 368 B

After

Width:  |  Height:  |  Size: 368 B

View File

Before

Width:  |  Height:  |  Size: 708 B

After

Width:  |  Height:  |  Size: 708 B

View File

Before

Width:  |  Height:  |  Size: 253 B

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -24,6 +24,7 @@ import json
import util
from c_overviewer import get_render_mode_inheritance
import overviewer_version
"""
This module has routines related to generating a Google Maps-based
@@ -52,10 +53,15 @@ def mirror_dir(src, dst, entities=None):
elif os.path.isfile(os.path.join(src,entry)):
try:
shutil.copy(os.path.join(src, entry), os.path.join(dst, entry))
except IOError:
except IOError as outer:
try:
# maybe permission problems?
os.chmod(os.path.join(src, entry), stat.S_IRUSR)
os.chmod(os.path.join(dst, entry), stat.S_IWUSR)
src_stat = os.stat(os.path.join(src, entry))
os.chmod(os.path.join(src, entry), src_stat.st_mode | stat.S_IRUSR)
dst_stat = os.stat(os.path.join(dst, entry))
os.chmod(os.path.join(dst, entry), dst_stat.st_mode | stat.S_IWUSR)
except OSError: # we don't care if this fails
pass
shutil.copy(os.path.join(src, entry), os.path.join(dst, entry))
# if this stills throws an error, let it propagate up
@@ -98,7 +104,11 @@ class MapGen(object):
blank.save(os.path.join(tileDir, "blank."+quadtree.imgformat))
# copy web assets into destdir:
mirror_dir(os.path.join(util.get_program_path(), "web_assets"), self.destdir)
global_assets = os.path.join(util.get_program_path(), "overviewer_core", "data", "web_assets")
if not os.path.isdir(global_assets):
global_assets = os.path.join(util.get_program_path(), "web_assets")
mirror_dir(global_assets, self.destdir)
# do the same with the local copy, if we have it
if self.web_assets_path:
mirror_dir(self.web_assets_path, self.destdir)
@@ -109,6 +119,8 @@ class MapGen(object):
"{minzoom}", str(0))
config = config.replace(
"{maxzoom}", str(zoomlevel))
config = config.replace(
"{zoomlevels}", str(zoomlevel))
config = config.replace("{spawn_coords}",
json.dumps(list(self.world.spawn)))
@@ -131,9 +143,9 @@ class MapGen(object):
indexpath = os.path.join(self.destdir, "index.html")
index = open(indexpath, 'r').read()
index = index.replace(
"{time}", str(strftime("%a, %d %b %Y %H:%M:%S %Z", localtime())))
index = index.replace("{version}", util.findGitVersion())
index = index.replace("{time}", str(strftime("%a, %d %b %Y %H:%M:%S %Z", localtime())))
versionstr = "%s (%s)" % (overviewer_version.VERSION, overviewer_version.HASH[:7])
index = index.replace("{version}", versionstr)
with open(os.path.join(self.destdir, "index.html"), 'w') as output:
output.write(index)

View File

@@ -120,7 +120,7 @@ class QuadtreeGen(object):
indexfile = os.path.join(self.destdir, "overviewerConfig.js")
if not os.path.exists(indexfile):
return -1
matcher = re.compile(r"maxZoom.*:\s*(\d+)")
matcher = re.compile(r"zoomLevels(?:\'|\")\s*:\s*(\d+)")
p = -1
for line in open(indexfile, "r"):
res = matcher.search(line)
@@ -426,6 +426,11 @@ class QuadtreeGen(object):
needs_rerender = False
get_region_mtime = world.get_region_mtime
for col, row, chunkx, chunky, regionfile in chunks:
region, regionMtime = get_region_mtime(regionfile)
# don't even check if it's not in the regionlist
if self.world.regionlist and os.path.abspath(region._filename) not in self.world.regionlist:
continue
# bail early if forcerender is set
if self.forcerender:
@@ -433,14 +438,9 @@ class QuadtreeGen(object):
break
# check region file mtime first.
region,regionMtime = get_region_mtime(regionfile)
if regionMtime <= tile_mtime:
continue
# don't even check if it's not in the regionlist
if self.world.regionlist and os.path.abspath(region._filename) not in self.world.regionlist:
continue
# checking chunk mtime
if region.get_chunk_timestamp(chunkx, chunky) > tile_mtime:
needs_rerender = True

View File

@@ -132,8 +132,8 @@ class RenderNode(object):
else:
if not complete % 1000 == 0:
return
logging.info("{0}/{1} tiles complete on level {2}/{3}".format(
complete, total, level, self.max_p))
logging.info("{0}/{1} ({4}%) tiles complete on level {2}/{3}".format(
complete, total, level, self.max_p, '%.1f' % ( (100.0 * complete) / total) ))
def go(self, procs):
"""Renders all tiles"""

View File

@@ -33,13 +33,13 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) {
return NULL;
}
textures = PyImport_ImportModule("textures");
textures = PyImport_ImportModule("overviewer_core.textures");
/* ensure none of these pointers are NULL */
if ((!textures)) {
return NULL;
}
chunk_mod = PyImport_ImportModule("chunk");
chunk_mod = PyImport_ImportModule("overviewer_core.chunk");
/* ensure none of these pointers are NULL */
if ((!chunk_mod)) {
return NULL;
@@ -174,12 +174,18 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) {
data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f);
return data;
}
} else if ((state->block == 20) || (state->block == 79)) { /* glass and ice */
/* an aditional bit for top is added to the 4 bits of check_adjacent_blocks */
if ((z != 127) && (getArrayByte3D(state->blocks, x, y, z+1) == 20)) {
data = 0;
} else {
data = 16;
}
data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f) | data;
return data;
} else if (state->block == 85) { /* fences */
return check_adjacent_blocks(state, x, y, z, state->block);
} else if (state->block == 55) { /* redstone */
/* three addiotional bit are added, one for on/off state, and
* another two for going-up redstone wire in the same block
@@ -354,6 +360,10 @@ chunk_render(PyObject *self, PyObject *args) {
up_right_blocks_py = PyObject_GetAttrString(state.self, "up_right_blocks");
state.up_right_blocks = up_right_blocks_py;
/* set up the random number generator again for each chunk
so tallgrass is in the same place, no matter what mode is used */
srand(1);
for (state.x = 15; state.x > -1; state.x--) {
for (state.y = 0; state.y < 16; state.y++) {
PyObject *blockid = NULL;
@@ -402,8 +412,18 @@ chunk_render(PyObject *self, PyObject *args) {
PyObject *tmp;
unsigned char ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z);
if ((state.block == 85) || (state.block == 9) || (state.block == 55) || (state.block == 54) || (state.block == 2) || (state.block == 90)) {
state.block_data = ancilData;
/* block that need pseudo ancildata:
* grass, water, glass, chest, restone wire,
* ice, fence and portal. */
if ((state.block == 2) || (state.block == 9) ||
(state.block == 20) || (state.block == 54) ||
(state.block == 55) || (state.block == 79) ||
(state.block == 85) || (state.block == 90)) {
ancilData = generate_pseudo_data(&state, ancilData);
state.block_pdata = ancilData;
} else {
state.block_pdata = 0;
}
tmp = PyTuple_New(2);
@@ -421,6 +441,7 @@ chunk_render(PyObject *self, PyObject *args) {
if (t != NULL && t != Py_None)
{
PyObject *src, *mask, *mask_light;
int randx = 0, randy = 0;
src = PyTuple_GetItem(t, 0);
mask = PyTuple_GetItem(t, 1);
mask_light = PyTuple_GetItem(t, 2);
@@ -428,7 +449,21 @@ chunk_render(PyObject *self, PyObject *args) {
if (mask == Py_None)
mask = src;
if (state.block == 31) {
/* add a random offset to the postion of the tall grass to make it more wild */
randx = rand() % 6 + 1 - 3;
randy = rand() % 6 + 1 - 3;
state.imgx += randx;
state.imgy += randy;
}
rendermode->draw(rm_data, &state, src, mask, mask_light);
if (state.block == 31) {
/* undo the random offsets */
state.imgx -= randx;
state.imgy -= randy;
}
}
}

View File

@@ -70,6 +70,8 @@ typedef struct {
/* the block position and type, and the block array */
int x, y, z;
unsigned char block;
unsigned char block_data;
unsigned char block_pdata;
PyObject *blockdata_expanded;
PyObject *blocks;
PyObject *up_left_blocks;

View File

@@ -293,7 +293,22 @@ rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject
self = (RenderModeLighting *)data;
x = state->x, y = state->y, z = state->z;
if (is_transparent(state->block)) {
if ((state->block == 9) || (state->block == 79)) { /* special case for water and ice */
/* looks like we need a new case for lighting, there are
* blocks that are transparent for occlusion calculations and
* need per-face shading if the face is drawn. */
if ((state->block_pdata & 16) == 16) {
do_shading_with_mask(self, state, x, y, z+1, self->facemasks[0]);
}
if ((state->block_pdata & 2) == 2) { /* bottom left */
do_shading_with_mask(self, state, x-1, y, z, self->facemasks[1]);
}
if ((state->block_pdata & 4) == 4) { /* bottom right */
do_shading_with_mask(self, state, x, y+1, z, self->facemasks[2]);
}
/* leaves are transparent for occlusion calculations but they
* per face-shading to look as in game */
} else if (is_transparent(state->block) && (state->block != 18)) {
/* transparent: do shading on whole block */
do_shading_with_mask(self, state, x, y, z, mask_light);
} else {

View File

@@ -125,7 +125,6 @@ rendermode_normal_occluded(void *data, RenderState *state) {
static void
rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
RenderModeNormal *self = (RenderModeNormal *)data;
int randx = 0,randy = 0;
unsigned char data_byte;
/* first, check to see if we should use biome-compatible src, mask */
@@ -133,11 +132,6 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *
if (state->block == 18) {
src = mask = self->leaf_texture;
} else if (state->block == 31) {
/* add a random offset to the postion of the tall grass to make it more wild */
randx = rand() % 6 + 1 - 3;
randy = rand() % 6 + 1 - 3;
state->imgx = state->imgx + randx;
state->imgy = state->imgy + randy;
data_byte = getArrayByte3D(state->blockdata_expanded, state->x, state->y, state->z);
if (data_byte == 1) {
src = mask = self->tall_grass_texture;

View File

@@ -14,6 +14,7 @@
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
import sys
import imp
import os
import os.path
import zipfile
@@ -27,13 +28,13 @@ import util
import composite
_find_file_local_path = None
def _find_file(filename, mode="rb"):
def _find_file(filename, mode="rb", verbose=False):
"""Searches for the given file and returns an open handle to it.
This searches the following locations in this order:
* the textures_path given in the config file (if present)
* The program dir (same dir as this file)
* The program dir / textures
* The program dir (same dir as overviewer.py)
* The overviewer_core textures dir
* On Darwin, in /Applications/Minecraft
* Inside minecraft.jar, which is looked for at these locations
@@ -46,20 +47,29 @@ def _find_file(filename, mode="rb"):
if _find_file_local_path:
path = os.path.join(_find_file_local_path, filename)
if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode)
programdir = util.get_program_path()
path = os.path.join(programdir, filename)
if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode)
path = os.path.join(programdir, "overviewer_core", "data", "textures", filename)
if os.path.exists(path):
return open(path, mode)
elif hasattr(sys, "frozen") or imp.is_frozen("__main__"):
# windows special case, when the package dir doesn't exist
path = os.path.join(programdir, "textures", filename)
if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode)
if sys.platform == "darwin":
path = os.path.join("/Applications/Minecraft", filename)
if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode)
# Find minecraft.jar.
@@ -79,6 +89,7 @@ def _find_file(filename, mode="rb"):
if os.path.exists(jarpath):
try:
jar = zipfile.ZipFile(jarpath)
if verbose: print "Found %s in '%s'" % (filename, jarpath)
return jar.open(filename)
except (KeyError, IOError):
pass
@@ -151,13 +162,13 @@ def transform_image_side(img, blockID=None):
# (don't just crop img, since we want the size of
# img to be unchanged
mask = img.crop((0,8,16,16))
n = Image.new(img.mode, img.size, (38,92,255,0))
n = Image.new(img.mode, img.size, bgcolor)
composite.alpha_over(n, mask,(0,0,16,8), mask)
img = n
if blockID in (78,): # snow
# make the top three quarters transparent
mask = img.crop((0,12,16,16))
n = Image.new(img.mode, img.size, (38,92,255,0))
n = Image.new(img.mode, img.size, bgcolor)
composite.alpha_over(n, mask,(0,12,16,16), mask)
img = n
@@ -235,7 +246,7 @@ def _build_block(top, side, blockID=None):
top and side should be 16x16 image objects. Returns a 24x24 image
"""
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
original_texture = top.copy()
top = transform_image(top, blockID)
@@ -315,36 +326,36 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
A non transparent block uses top, side 3 and side 4.
If top is a tuple then first member is the top image and the second
member is an increment (integer) from 0 to 12. This increment will
used to crop the side images to look like a block and to paste all
the images increment pixels lower. Using increment = 6 will create
a half-block.
If top is a tuple then first item is the top image and the second
item is an increment (integer) from 0 to 16 (pixels in the
original minecraft texture). This increment will be used to crop the
side images and to paste the top image increment pixels lower, so if
you use an increment of 8, it willll draw a half-block.
NOTE: this method uses the top of the texture image (as done in
minecraft with beds)
NOTE: this method uses the bottom of the texture image (as done in
minecraft with beds and cackes)
"""
increment = 0
if isinstance(top, tuple):
increment = top[1]
crop_height = int(increment * 16./12.)
increment = int(round((top[1] / 16.)*12.)) # range increment in the block height in pixels (half texture size)
crop_height = increment
top = top[0]
if side1 != None:
side1 = side1.copy()
ImageDraw.Draw(side1).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(side1).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
if side2 != None:
side2 = side2.copy()
ImageDraw.Draw(side2).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(side2).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
if side3 != None:
side3 = side3.copy()
ImageDraw.Draw(side3).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(side3).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
if side4 != None:
side4 = side4.copy()
ImageDraw.Draw(side4).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(side4).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
# first back sides
if side1 != None :
@@ -356,7 +367,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side1 = ImageEnhance.Brightness(side1).enhance(0.9)
side1.putalpha(sidealpha)
composite.alpha_over(img, side1, (0,0 + increment), side1)
composite.alpha_over(img, side1, (0,0), side1)
if side2 != None :
@@ -367,11 +378,11 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side2 = ImageEnhance.Brightness(side2).enhance(0.8)
side2.putalpha(sidealpha2)
composite.alpha_over(img, side2, (12,0 + increment), side2)
composite.alpha_over(img, side2, (12,0), side2)
if bottom != None :
bottom = transform_image(bottom, blockID)
composite.alpha_over(img, bottom, (0,12), top)
composite.alpha_over(img, bottom, (0,12), bottom)
# front sides
if side3 != None :
@@ -382,7 +393,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side3 = ImageEnhance.Brightness(side3).enhance(0.9)
side3.putalpha(sidealpha)
composite.alpha_over(img, side3, (0,6 + increment), side3)
composite.alpha_over(img, side3, (0,6), side3)
if side4 != None :
side4 = transform_image_side(side4, blockID)
@@ -393,7 +404,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side4 = ImageEnhance.Brightness(side4).enhance(0.8)
side4.putalpha(sidealpha)
composite.alpha_over(img, side4, (12,6 + increment), side4)
composite.alpha_over(img, side4, (12,6), side4)
if top != None :
top = transform_image(top, blockID)
@@ -414,13 +425,13 @@ def _build_blockimages():
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
topids = [ -1, 1, 0, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33,
# 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
34, -1, 52, 48, 49,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1,
34, -1, 52, 48, -1,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1,
# 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 9, 4,
# 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
36, 37, -1, -1, 65, -1, -1, -1, 50, 24, -1, -1, 86, -1, -1, -1,
# 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
-1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, 67,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1,
# 80 81 82 83 84 85 86 87 88 89 90 91
66, 69, 72, 73, 75, -1,102,103,104,105,-1, 102 # clay?
]
@@ -431,13 +442,13 @@ def _build_blockimages():
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
sideids = [ -1, 1, 3, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33,
# 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
34, -1, 52, 48, 49,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1,
34, -1, 52, 48, -1,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1,
# 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 35,
# 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
36, 37, -1, -1, 65, -1, -1,101, 50, 24, -1, -1, 86, -1, -1, -1,
# 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
-1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, 67,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1,
# 80 81 82 83 84 85 86 87 88 89 90 91
66, 70, 72, 73, 74,-1 ,118,103,104,105, -1, 118
]
@@ -502,9 +513,7 @@ def generate_texture_tuple(img, blockid):
def generate_special_texture(blockID, data):
"""Generates a special texture, such as a correctly facing minecraft track"""
#print "%s has ancillary data: %X" %(blockID, data)
# TODO ladders, stairs, levers, buttons, and signs
# all need to behandled here (and in chunkpy)
# blocks need to be handled here (and in chunk.py)
if blockID == 2: # grass
# data & 0x10 means SNOW sides
@@ -542,33 +551,41 @@ def generate_special_texture(blockID, data):
return generate_texture_tuple(img, blockID)
if blockID == 9: # spring water, flowing water and waterfall water
watertexture = _load_image("water.png")
if blockID == 9 or blockID == 20 or blockID == 79: # spring water, flowing water and waterfall water, AND glass, AND ice
# water,glass and ice share the way to be rendered
if blockID == 9:
texture = _load_image("water.png")
elif blockID == 20:
texture = terrain_images[49]
else:
texture = terrain_images[67]
if (data & 0b10000) == 16:
top = watertexture
top = texture
else: top = None
if (data & 0b0001) == 1:
side1 = watertexture # top left
side1 = texture # top left
else: side1 = None
if (data & 0b1000) == 8:
side2 = watertexture # top right
side2 = texture # top right
else: side2 = None
if (data & 0b0010) == 2:
side3 = watertexture # bottom left
side3 = texture # bottom left
else: side3 = None
if (data & 0b0100) == 4:
side4 = watertexture # bottom right
side4 = texture # bottom right
else: side4 = None
img = _build_full_block(top,None,None,side3,side4)
# if nothing shown do not draw at all
if top == side3 == side4 == None:
return None
img = _build_full_block(top,None,None,side3,side4)
return generate_texture_tuple(img, blockID)
@@ -594,7 +611,7 @@ def generate_special_texture(blockID, data):
if blockID == 26: # bed
increment = 5
increment = 8
left_face = None
right_face = None
if data & 0x8 == 0x8: # head of the bed
@@ -638,6 +655,7 @@ def generate_special_texture(blockID, data):
return generate_texture_tuple(img, blockID)
if blockID == 31: # tall grass
if data == 0: # dead shrub
texture = terrain_images[55]
@@ -652,6 +670,132 @@ def generate_special_texture(blockID, data):
return generate_texture_tuple(img,31)
if blockID in (29,33): # sticky and normal body piston.
if blockID == 29: # sticky
piston_t = terrain_images[106].copy()
else: # normal
piston_t = terrain_images[107].copy()
# other textures
side_t = terrain_images[108].copy()
back_t = terrain_images[109].copy()
interior_t = terrain_images[110].copy()
if data & 0x08 == 0x08: # pushed out, non full blocks, tricky stuff
# remove piston texture from piston body
ImageDraw.Draw(side_t).rectangle((0, 0,16,3),outline=(0,0,0,0),fill=(0,0,0,0))
if data & 0x07 == 0x0: # down
side_t = side_t.rotate(180)
img = _build_full_block(back_t ,None ,None ,side_t, side_t)
elif data & 0x07 == 0x1: # up
img = _build_full_block((interior_t, 4) ,None ,None ,side_t, side_t)
elif data & 0x07 == 0x2: # east
img = _build_full_block(side_t , None, None ,side_t.rotate(90), back_t)
elif data & 0x07 == 0x3: # west
img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), None)
temp = transform_image_side(interior_t, blockID)
temp = temp.transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, temp, (9,5), temp)
elif data & 0x07 == 0x4: # north
img = _build_full_block(side_t.rotate(90) ,None ,None , None, side_t.rotate(270))
temp = transform_image_side(interior_t, blockID)
composite.alpha_over(img, temp, (3,5), temp)
elif data & 0x07 == 0x5: # south
img = _build_full_block(side_t.rotate(270) ,None , None ,back_t, side_t.rotate(90))
else: # pushed in, normal full blocks, easy stuff
if data & 0x07 == 0x0: # down
side_t = side_t.rotate(180)
img = _build_full_block(back_t ,None ,None ,side_t, side_t)
elif data & 0x07 == 0x1: # up
img = _build_full_block(piston_t ,None ,None ,side_t, side_t)
elif data & 0x07 == 0x2: # east
img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), back_t)
elif data & 0x07 == 0x3: # west
img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t)
elif data & 0x07 == 0x4: # north
img = _build_full_block(side_t.rotate(90) ,None ,None ,piston_t, side_t.rotate(270))
elif data & 0x07 == 0x5: # south
img = _build_full_block(side_t.rotate(270) ,None ,None ,back_t, side_t.rotate(90))
return generate_texture_tuple(img, blockID)
if blockID == 34: # piston extension (sticky and normal)
if (data & 0x8) == 0x8: # sticky
piston_t = terrain_images[106].copy()
else: # normal
piston_t = terrain_images[107].copy()
# other textures
side_t = terrain_images[108].copy()
back_t = terrain_images[107].copy()
# crop piston body
ImageDraw.Draw(side_t).rectangle((0, 4,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
# generate the horizontal piston extension stick
h_stick = Image.new("RGBA", (24,24), bgcolor)
temp = transform_image_side(side_t, blockID)
composite.alpha_over(h_stick, temp, (1,7), temp)
temp = transform_image(side_t.rotate(90))
composite.alpha_over(h_stick, temp, (1,1), temp)
# Darken it
sidealpha = h_stick.split()[3]
h_stick = ImageEnhance.Brightness(h_stick).enhance(0.85)
h_stick.putalpha(sidealpha)
# generate the vertical piston extension stick
v_stick = Image.new("RGBA", (24,24), bgcolor)
temp = transform_image_side(side_t.rotate(90), blockID)
composite.alpha_over(v_stick, temp, (12,6), temp)
temp = temp.transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(v_stick, temp, (1,6), temp)
# Darken it
sidealpha = v_stick.split()[3]
v_stick = ImageEnhance.Brightness(v_stick).enhance(0.85)
v_stick.putalpha(sidealpha)
# Piston orientation is stored in the 3 first bits
if data & 0x07 == 0x0: # down
side_t = side_t.rotate(180)
img = _build_full_block((back_t, 12) ,None ,None ,side_t, side_t)
composite.alpha_over(img, v_stick, (0,-3), v_stick)
elif data & 0x07 == 0x1: # up
img = Image.new("RGBA", (24,24), bgcolor)
img2 = _build_full_block(piston_t ,None ,None ,side_t, side_t)
composite.alpha_over(img, v_stick, (0,4), v_stick)
composite.alpha_over(img, img2, (0,0), img2)
elif data & 0x07 == 0x2: # east
img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), None)
temp = transform_image_side(back_t, blockID).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, temp, (2,2), temp)
composite.alpha_over(img, h_stick, (6,3), h_stick)
elif data & 0x07 == 0x3: # west
img = Image.new("RGBA", (24,24), bgcolor)
img2 = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t)
composite.alpha_over(img, h_stick, (0,0), h_stick)
composite.alpha_over(img, img2, (0,0), img2)
elif data & 0x07 == 0x4: # north
img = _build_full_block(side_t.rotate(90) ,None ,None , piston_t, side_t.rotate(270))
composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (0,0), h_stick.transpose(Image.FLIP_LEFT_RIGHT))
elif data & 0x07 == 0x5: # south
img = Image.new("RGBA", (24,24), bgcolor)
img2 = _build_full_block(side_t.rotate(270) ,None ,None ,None, side_t.rotate(90))
temp = transform_image_side(back_t, blockID)
composite.alpha_over(img2, temp, (10,2), temp)
composite.alpha_over(img, img2, (0,0), img2)
composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (-3,2), h_stick.transpose(Image.FLIP_LEFT_RIGHT))
return generate_texture_tuple(img, blockID)
if blockID == 35: # wool
if data == 0: # white
top = side = terrain_images[64]
@@ -719,7 +863,7 @@ def generate_special_texture(blockID, data):
# compose a torch bigger than the normal
# (better for doing transformations)
torch = Image.new("RGBA", (16,16), (38,92,255,0))
torch = Image.new("RGBA", (16,16), bgcolor)
composite.alpha_over(torch,small,(-4,-3))
composite.alpha_over(torch,small,(-5,-2))
composite.alpha_over(torch,small,(-3,-2))
@@ -745,17 +889,17 @@ def generate_special_texture(blockID, data):
elif data == 5: # standing on the floor
# compose a "3d torch".
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
small_crop = small.crop((2,2,14,14))
slice = small_crop.copy()
ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0))
composite.alpha_over(img, slice, (6,4))
composite.alpha_over(img, small_crop, (5,5))
composite.alpha_over(img, small_crop, (6,5))
composite.alpha_over(img, slice, (6,6))
composite.alpha_over(img, slice, (7,5))
composite.alpha_over(img, small_crop, (6,6))
composite.alpha_over(img, small_crop, (7,6))
composite.alpha_over(img, slice, (7,7))
return generate_texture_tuple(img, blockID)
@@ -765,7 +909,7 @@ def generate_special_texture(blockID, data):
side1 = transform_image_side(firetexture)
side2 = transform_image_side(firetexture).transpose(Image.FLIP_LEFT_RIGHT)
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
composite.alpha_over(img, side1, (12,0), side1)
composite.alpha_over(img, side2, (0,0), side2)
@@ -812,14 +956,14 @@ def generate_special_texture(blockID, data):
composite.alpha_over(img, tmp2, (0,6))
elif data == 1: # ascending north
img = Image.new("RGBA", (24,24), (38,92,255,0)) # first paste the texture in the back
img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back
tmp1 = transform_image(half_block_r)
composite.alpha_over(img, tmp1, (0,6))
tmp2 = _build_full_block(half_block_l, None, None, texture, side)
composite.alpha_over(img, tmp2)
elif data == 2: # ascending west
img = Image.new("RGBA", (24,24), (38,92,255,0)) # first paste the texture in the back
img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back
tmp1 = transform_image(half_block_u)
composite.alpha_over(img, tmp1, (0,6))
tmp2 = _build_full_block(half_block_d, None, None, side, texture)
@@ -936,7 +1080,7 @@ def generate_special_texture(blockID, data):
bottom = redstone_wire_t.copy().rotate(90)
else:
bottom = Image.new("RGBA", (16,16), (38,92,255,0))
bottom = Image.new("RGBA", (16,16), bgcolor)
if (data & 0b0001) == 1:
composite.alpha_over(bottom,branch_top_left)
@@ -980,7 +1124,7 @@ def generate_special_texture(blockID, data):
crop2 = transform_image_side(raw_crop, blockID)
crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT)
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
composite.alpha_over(img, crop1, (0,12), crop1)
composite.alpha_over(img, crop2, (6,3), crop2)
composite.alpha_over(img, crop3, (6,3), crop3)
@@ -1031,7 +1175,7 @@ def generate_special_texture(blockID, data):
texture_stick = texture_stick.resize((12,12), Image.ANTIALIAS)
ImageDraw.Draw(texture_stick).rectangle((2,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0))
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
# W N ~90 E S ~270
angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.)
@@ -1069,8 +1213,8 @@ def generate_special_texture(blockID, data):
swung=False
# mask out the high bits to figure out the orientation
img = Image.new("RGBA", (24,24), (38,92,255,0))
if (data & 0x03) == 0:
img = Image.new("RGBA", (24,24), bgcolor)
if (data & 0x03) == 0: # northeast corner
if not swung:
tex = transform_image_side(raw_door)
composite.alpha_over(img, tex, (0,6), tex)
@@ -1080,7 +1224,7 @@ def generate_special_texture(blockID, data):
tex = tex.transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (0,0), tex)
if (data & 0x03) == 1:
if (data & 0x03) == 1: # southeast corner
if not swung:
tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (0,0), tex)
@@ -1088,7 +1232,7 @@ def generate_special_texture(blockID, data):
tex = transform_image_side(raw_door)
composite.alpha_over(img, tex, (12,0), tex)
if (data & 0x03) == 2:
if (data & 0x03) == 2: # southwest corner
if not swung:
tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
composite.alpha_over(img, tex, (12,0), tex)
@@ -1096,7 +1240,7 @@ def generate_special_texture(blockID, data):
tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (12,6), tex)
if (data & 0x03) == 3:
if (data & 0x03) == 3: # northwest corner
if not swung:
tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (12,6), tex)
@@ -1108,7 +1252,7 @@ def generate_special_texture(blockID, data):
if blockID == 65: # ladder
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
raw_texture = terrain_images[83]
#print "ladder is facing: %d" % data
if data == 5:
@@ -1133,7 +1277,7 @@ def generate_special_texture(blockID, data):
if blockID in (27, 28, 66): # minetrack:
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
if blockID == 27: # powered rail
if data & 0x8 == 0: # unpowered
@@ -1216,7 +1360,7 @@ def generate_special_texture(blockID, data):
texture.putpixel((x,y),(0,0,0,255))
"""
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
incrementx = 0
if data == 2: # east
@@ -1250,7 +1394,6 @@ def generate_special_texture(blockID, data):
ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(fence_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
@@ -1270,10 +1413,10 @@ def generate_special_texture(blockID, data):
fence_other_side.putalpha(othersidealpha)
# Compose the fence big stick
fence_big = Image.new("RGBA", (24,24), (38,92,255,0))
fence_big = Image.new("RGBA", (24,24), bgcolor)
composite.alpha_over(fence_big,fence_side, (5,4),fence_side)
composite.alpha_over(fence_big,fence_other_side, (7,4),fence_other_side)
composite.alpha_over(fence_big,fence_top, (0,1),fence_top)
composite.alpha_over(fence_big,fence_top, (0,0),fence_top)
# Now render the small sticks.
# Create needed images
@@ -1301,7 +1444,7 @@ def generate_special_texture(blockID, data):
fence_small_side.putalpha(sidealpha)
# Create img to compose the fence
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
# Position of fence small sticks in img.
# These postitions are strange because the small sticks of the
@@ -1351,7 +1494,7 @@ def generate_special_texture(blockID, data):
if blockID == 90: # portal
portaltexture = _load_image("portal.png")
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
side = transform_image_side(portaltexture)
otherside = side.transpose(Image.FLIP_TOP_BOTTOM)
@@ -1380,23 +1523,21 @@ def generate_special_texture(blockID, data):
otherside = ImageEnhance.Brightness(otherside).enhance(0.8)
otherside.putalpha(othersidealpha)
img = Image.new("RGBA", (24,24), (38,92,255,0))
img = Image.new("RGBA", (24,24), bgcolor)
composite.alpha_over(img, side, (1,12), side)
composite.alpha_over(img, otherside, (11,13), otherside) # workaround, fixes a hole
composite.alpha_over(img, otherside, (12,12), otherside)
composite.alpha_over(img, side, (1,6), side)
composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole
composite.alpha_over(img, otherside, (12,6), otherside)
composite.alpha_over(img, top, (0,6), top)
return generate_texture_tuple(img, blockID)
if blockID in (93, 94): # redstone repeaters, ON and OFF
# NOTE: this function uses the redstone torches generated above,
# this must run after the function of the torches.
if blockID in (93, 94): # redstone repeaters (diodes), ON and OFF
# generate the diode
top = terrain_images[131] if blockID == 93 else terrain_images[147]
side = terrain_images[5]
increment = 9
increment = 13
if (data & 0x3) == 0: # pointing east
pass
@@ -1412,11 +1553,21 @@ def generate_special_texture(blockID, data):
img = _build_full_block( (top, increment), None, None, side, side)
# paste redstone torches everywhere!
t = specialblockmap[(75,5)] if blockID == 93 else specialblockmap[(76,5)]
torch = t[0].copy() # textures are stored as tuples (RGB,A)
torch.putalpha(t[1])
# compose a "3d" redstone torch
t = terrain_images[115].copy() if blockID == 93 else terrain_images[99].copy()
torch = Image.new("RGBA", (24,24), bgcolor)
t_crop = t.crop((2,2,14,14))
slice = t_crop.copy()
ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0))
composite.alpha_over(torch, slice, (6,4))
composite.alpha_over(torch, t_crop, (5,5))
composite.alpha_over(torch, t_crop, (6,5))
composite.alpha_over(torch, slice, (6,6))
# paste redstone torches everywhere!
# the torch is too tall for the repeater, crop the bottom.
ImageDraw.Draw(torch).rectangle((0,16,24,24),outline=(0,0,0,0),fill=(0,0,0,0))
@@ -1521,7 +1672,7 @@ def generate_special_texture(blockID, data):
img = _build_full_block(None, None, None, texture, None)
elif data & 0x4 == 0: # closed trapdoor
img = _build_full_block((texture, 9), None, None, texture, texture)
img = _build_full_block((texture, 12), None, None, texture, texture)
return generate_texture_tuple(img, blockID)
@@ -1598,9 +1749,10 @@ def getBiomeData(worlddir, chunkX, chunkY):
# (when adding new blocks here and in generate_special_textures,
# please, if possible, keep the ascending order of blockid value)
special_blocks = set([ 2, 6, 9, 17, 18, 26, 23, 27, 28, 31, 35, 43, 44,
50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66,
67, 68, 71, 75, 76, 85, 86, 90, 91, 92, 93, 94, 96])
special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33,
34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62,
63, 64, 65, 66, 67, 68, 71, 75, 76, 79, 85, 86, 90,
91, 92, 93, 94, 96])
# this is a map of special blockIDs to a list of all
# possible values for ancillary data that it might have.
@@ -1610,10 +1762,14 @@ special_map = {}
special_map[6] = range(16) # saplings: usual, spruce, birch and future ones (rendered as usual saplings)
special_map[9] = range(32) # water: spring,flowing, waterfall, and others (unknown) ancildata values, uses pseudo data
special_map[17] = range(3) # wood: normal, birch and pine
special_map[20] = range(32) # glass, used to only render the exterior surface, uses pseudo data
special_map[26] = range(12) # bed, orientation
special_map[23] = range(6) # dispensers, orientation
special_map[27] = range(14) # powered rail, orientation/slope and powered/unpowered
special_map[28] = range(6) # detector rail, orientation/slope
special_map[29] = (0,1,2,3,4,5,8,9,10,11,12,13) # sticky piston body, orientation, pushed in/out
special_map[33] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal piston body, orientation, pushed in/out
special_map[34] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal and sticky piston extension, orientation, sticky/normal
special_map[35] = range(16) # wool, colored and white
special_map[43] = range(4) # stone, sandstone, wooden and cobblestone double-slab
special_map[44] = range(4) # stone, sandstone, wooden and cobblestone slab
@@ -1622,7 +1778,7 @@ special_map[51] = range(16) # fire, position in the block (not implemented)
special_map[53] = range(4) # wooden stairs, orientation
special_map[54] = range(12) # chests, orientation and type (single or double), uses pseudo data
special_map[55] = range(128) # redstone wire, all the possible combinations, uses pseudo data
special_map[58] = (0,) # crafting table
special_map[58] = (0,) # crafting table, it has 2 different sides
special_map[59] = range(8) # crops, grow from 0 to 7
special_map[61] = range(6) # furnace, orientation
special_map[62] = range(6) # burning furnace, orientation
@@ -1635,13 +1791,14 @@ special_map[68] = (2,3,4,5) # wall sing, orientation
special_map[71] = range(16) # iron door, open/close and orientation
special_map[75] = (1,2,3,4,5) # off redstone torch, orientation
special_map[76] = (1,2,3,4,5) # on redstone torch, orientation
special_map[79] = range(32) # ice, used to only render the exterior surface, uses pseudo data
special_map[85] = range(17) # fences, all the possible combination, uses pseudo data
special_map[86] = range(5) # pumpkin, orientation
special_map[90] = (1,2,4,8) # portal, in 2 orientations, 4 cases, uses pseudo data
special_map[91] = range(5) # jack-o-lantern, orientation
special_map[92] = range(6) # cake!
special_map[93] = range(16) # OFF redstone repeater, orientation and delay (delay not implemented)
special_map[94] = range(16) # ON redstone repeater, orientation and delay (delay not implemented)
special_map[92] = range(6) # cake, eaten amount, (not implemented)
special_map[93] = range(16) # OFF redstone repeater, orientation and delay
special_map[94] = range(16) # ON redstone repeater, orientation and delay
special_map[96] = range(8) # trapdoor, open, closed, orientation
# grass and leaves are graysacle in terrain.png
@@ -1656,6 +1813,7 @@ special_map[18] = range(16) # leaves, birch, normal or pine leaves (not implemen
special_map[31] = range(3) # tall grass, dead shrub, fern and tall grass itself
# placeholders that are generated in generate()
bgcolor = None
terrain_images = None
blockmap = None
biome_grass_texture = None
@@ -1664,9 +1822,12 @@ biome_tall_fern_texture = None
biome_leaf_texture = None
specialblockmap = None
def generate(path=None):
global _find_file_local_path
def generate(path=None,texture_size=24,bgc = (26,26,26,0)):
global bgcolor
bgcolor = bgc
global _find_file_local_path, texture_dimensions
_find_file_local_path = path
texture_dimensions = (texture_size, texture_size)
# This maps terainids to 16x16 images
global terrain_images
@@ -1690,3 +1851,30 @@ def generate(path=None):
for blockID in special_blocks:
for data in special_map[blockID]:
specialblockmap[(blockID, data)] = generate_special_texture(blockID, data)
if texture_size != 24:
# rescale biome textures.
biome_grass_texture = biome_grass_texture.resize(texture_dimensions, Image.ANTIALIAS)
biome_leaf_texture = biome_leaf_texture.resize(texture_dimensions, Image.ANTIALIAS)
biome_tall_grass_texture = biome_tall_grass_texture.resize(texture_dimensions, Image.ANTIALIAS)
biome_tall_fern_texture = biome_tall_fern_texture.resize(texture_dimensions, Image.ANTIALIAS)
# rescale the normal block images
for i in range(len(blockmap)):
if blockmap[i] != None:
block = blockmap[i]
alpha = block[1]
block = block[0]
block.putalpha(alpha)
scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS)
blockmap[i] = generate_texture_tuple(scaled_block, i)
# rescale the special block images
for blockid, data in iter(specialblockmap):
block = specialblockmap[(blockid,data)]
if block != None:
alpha = block[1]
block = block[0]
block.putalpha(alpha)
scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS)
specialblockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid)

View File

@@ -21,19 +21,22 @@ import imp
import os
import os.path
import sys
from subprocess import Popen, PIPE
def get_program_path():
if hasattr(sys, "frozen") or imp.is_frozen("__main__"):
return os.path.dirname(sys.executable)
else:
try:
return os.path.dirname(__file__)
# normally, we're in ./overviewer_core/util.py
# we want ./
return os.path.dirname(os.path.dirname(__file__))
except NameError:
return os.path.dirname(sys.argv[0])
def findGitVersion():
# does not require git, very likely to work everywhere
def findGitHash():
this_dir = get_program_path()
if os.path.exists(os.path.join(this_dir,".git")):
with open(os.path.join(this_dir,".git","HEAD")) as f:
@@ -46,6 +49,24 @@ def findGitVersion():
else:
return data
else:
try:
import overviewer_version
return overviewer_version.HASH
except:
return "unknown"
def findGitVersion():
try:
p = Popen(['git', 'describe', '--tags'], stdout=PIPE, stderr=PIPE)
p.stderr.close()
line = p.stdout.readlines()[0]
if line.startswith('release-'):
line = line.split('-', 1)[1]
# turn 0.1.2-50-somehash into 0.1.2-50
# and 0.1.3 into 0.1.3
line = '-'.join(line.split('-', 2)[:2])
return line.strip()
except:
try:
import overviewer_version
return overviewer_version.VERSION

View File

@@ -209,9 +209,9 @@ class World(object):
chunkX = spawnX/16
chunkY = spawnZ/16
try:
## The filename of this chunk
chunkFile = self.get_region_path(chunkX, chunkY)
if chunkFile is not None:
data = nbt.load_from_region(chunkFile, chunkX, chunkY)[1]
if data is not None:
@@ -227,7 +227,9 @@ class World(object):
spawnY += 1
if spawnY == 128:
break
except ChunkCorrupt:
#ignore corrupt spawn, and continue
pass
self.POI.append( dict(x=spawnX, y=spawnY, z=spawnZ,
msg="Spawn", type="spawn", chunk=(chunkX, chunkY)))
self.spawn = (spawnX, spawnY, spawnZ)

218
setup.py Normal file → Executable file
View File

@@ -1,25 +1,90 @@
from distutils.core import setup, Extension
#!/usr/bin/env python
from distutils.core import setup
from distutils.extension import Extension
from distutils.command.build import build
from distutils.command.clean import clean
from distutils.command.build_ext import build_ext
from distutils.command.sdist import sdist
from distutils.cmd import Command
from distutils.dir_util import remove_tree
from distutils.sysconfig import get_python_inc
from distutils import log
import os, os.path
import sys, os, os.path
import glob
import platform
import time
import overviewer_core.util as util
try:
import py2exe
except ImportError:
py2exe = None
try:
import py2app
from setuptools.extension import Extension
except ImportError:
py2app = None
# now, setup the keyword arguments for setup
# (because we don't know until runtime if py2exe is available)
# (because we don't know until runtime if py2exe/py2app is available)
setup_kwargs = {}
setup_kwargs['options'] = {}
setup_kwargs['ext_modules'] = []
setup_kwargs['cmdclass'] = {}
setup_kwargs['options'] = {}
#
# metadata
#
# Utility function to read the README file.
# Used for the long_description. It's nice, because now 1) we have a top level
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup_kwargs['name'] = 'Minecraft-Overviewer'
setup_kwargs['version'] = util.findGitVersion()
setup_kwargs['description'] = 'Generates large resolution images of a Minecraft map.'
setup_kwargs['url'] = 'http://overviewer.org/'
setup_kwargs['author'] = 'Andrew Brown'
setup_kwargs['author_email'] = 'brownan@gmail.com'
setup_kwargs['license'] = 'GNU General Public License v3'
setup_kwargs['long_description'] = read('README.rst')
# top-level files that should be included as documentation
doc_files = ['COPYING.txt', 'README.rst', 'CONTRIBUTORS.rst', 'sample.settings.py']
# helper to create a 'data_files'-type sequence recursively for a given dir
def recursive_data_files(src, dest=None):
if dest is None:
dest = src
ret = []
for dirpath, dirnames, filenames in os.walk(src):
current_dest = os.path.relpath(dirpath, src)
if current_dest == '.':
current_dest = dest
else:
current_dest = os.path.join(dest, current_dest)
current_sources = map(lambda p: os.path.join(dirpath, p), filenames)
ret.append((current_dest, current_sources))
return ret
# helper to create a 'package_data'-type sequence recursively for a given dir
def recursive_package_data(src, package_dir='overviewer_core'):
full_src = os.path.join(package_dir, src)
ret = []
for dirpath, dirnames, filenames in os.walk(full_src, topdown=False):
current_path = os.path.relpath(dirpath, package_dir)
for filename in filenames:
ret.append(os.path.join(current_path, filename))
return ret
#
# py2exe options
@@ -27,9 +92,9 @@ setup_kwargs['cmdclass'] = {}
if py2exe is not None:
setup_kwargs['console'] = ['overviewer.py']
setup_kwargs['data_files'] = [('textures', ['textures/lava.png', 'textures/water.png', 'textures/fire.png', 'textures/portal.png']),
('', ['COPYING.txt', 'README.rst']),
('web_assets', glob.glob('web_assets/*'))]
setup_kwargs['data_files'] = [('', doc_files)]
setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/textures', 'textures')
setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/web_assets', 'web_assets')
setup_kwargs['zipfile'] = None
if platform.system() == 'Windows' and '64bit' in platform.architecture():
b = 3
@@ -37,6 +102,27 @@ if py2exe is not None:
b = 1
setup_kwargs['options']['py2exe'] = {'bundle_files' : b, 'excludes': 'Tkinter'}
#
# py2app options
#
if py2app is not None:
setup_kwargs['app'] = ['overviewer.py']
setup_kwargs['options']['py2app'] = {'argv_emulation' : False}
setup_kwargs['setup_requires'] = ['py2app']
#
# script, package, and data
#
setup_kwargs['packages'] = ['overviewer_core']
setup_kwargs['scripts'] = ['overviewer.py']
setup_kwargs['package_data'] = {'overviewer_core': recursive_package_data('data/textures') + recursive_package_data('data/web_assets')}
if py2exe is None:
setup_kwargs['data_files'] = [('share/doc/minecraft-overviewer', doc_files)]
#
# c_overviewer extension
#
@@ -52,25 +138,31 @@ except AttributeError:
try:
pil_include = os.environ['PIL_INCLUDE_DIR'].split(os.pathsep)
except:
pil_include = []
pil_include = [ os.path.join(get_python_inc(plat_specific=1), 'Imaging') ]
if not os.path.exists(pil_include[0]):
pil_include = [ ]
# used to figure out what files to compile
render_modes = ['normal', 'overlay', 'lighting', 'night', 'spawn', 'cave']
c_overviewer_files = ['src/main.c', 'src/composite.c', 'src/iterate.c', 'src/endian.c', 'src/rendermodes.c']
c_overviewer_files += map(lambda mode: 'src/rendermode-%s.c' % (mode,), render_modes)
c_overviewer_files += ['src/Draw.c']
c_overviewer_includes = ['src/overviewer.h', 'src/rendermodes.h']
c_overviewer_files = ['main.c', 'composite.c', 'iterate.c', 'endian.c', 'rendermodes.c']
c_overviewer_files += map(lambda mode: 'rendermode-%s.c' % (mode,), render_modes)
c_overviewer_files += ['Draw.c']
c_overviewer_includes = ['overviewer.h', 'rendermodes.h']
c_overviewer_files = map(lambda s: 'overviewer_core/src/'+s, c_overviewer_files)
c_overviewer_includes = map(lambda s: 'overviewer_core/src/'+s, c_overviewer_includes)
setup_kwargs['ext_modules'].append(Extension('overviewer_core.c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include] + pil_include, depends=c_overviewer_includes, extra_link_args=[]))
setup_kwargs['ext_modules'].append(Extension('c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include] + pil_include, depends=c_overviewer_includes, extra_link_args=[]))
# tell build_ext to build the extension in-place
# (NOT in build/)
setup_kwargs['options']['build_ext'] = {'inplace' : 1}
# tell the build command to only run build_ext
build.sub_commands = [('build_ext', None)]
# custom clean command to remove in-place extension
# and the version file
class CustomClean(clean):
def run(self):
# do the normal cleanup
@@ -79,7 +171,7 @@ class CustomClean(clean):
# try to remove '_composite.{so,pyd,...}' extension,
# regardless of the current system's extension name convention
build_ext = self.get_finalized_command('build_ext')
pretty_fname = build_ext.get_ext_filename('c_overviewer')
pretty_fname = build_ext.get_ext_filename('overviewer_core.c_overviewer')
fname = pretty_fname
if os.path.exists(fname):
try:
@@ -93,7 +185,54 @@ class CustomClean(clean):
log.debug("'%s' does not exist -- can't clean it",
pretty_fname)
class CustomBuild(build_ext):
versionpath = os.path.join("overviewer_core", "overviewer_version.py")
if os.path.exists(versionpath):
try:
if not self.dry_run:
os.remove(versionpath)
log.info("removing '%s'", versionpath)
except OSError:
log.warn("'%s' could not be cleaned -- permission denied", versionpath)
else:
log.debug("'%s' does not exist -- can't clean it", versionpath)
# now try to purge all *.pyc files
for root, dirs, files in os.walk(os.path.join(os.path.dirname(__file__), ".")):
for f in files:
if f.endswith(".pyc"):
if self.dry_run:
log.warn("Would remove %s", os.path.join(root,f))
else:
os.remove(os.path.join(root, f))
def generate_version_py():
try:
outstr = ""
outstr += "VERSION=%r\n" % util.findGitVersion()
outstr += "HASH=%r\n" % util.findGitHash()
outstr += "BUILD_DATE=%r\n" % time.asctime()
outstr += "BUILD_PLATFORM=%r\n" % platform.processor()
outstr += "BUILD_OS=%r\n" % platform.platform()
f = open("overviewer_core/overviewer_version.py", "w")
f.write(outstr)
f.close()
except:
print "WARNING: failed to build overviewer_version file"
class CustomSDist(sdist):
def run(self):
# generate the version file
generate_version_py()
sdist.run(self)
class CustomBuild(build):
def run(self):
# generate the version file
generate_version_py()
build.run(self)
print "\nBuild Complete"
class CustomBuildExt(build_ext):
def build_extensions(self):
c = self.compiler.compiler_type
if c == "msvc":
@@ -101,32 +240,39 @@ class CustomBuild(build_ext):
for e in self.extensions:
e.extra_link_args.append("/MANIFEST")
# build in place, and in the build/ tree
self.inplace = False
build_ext.build_extensions(self)
self.inplace = True
build_ext.build_extensions(self)
if py2exe is not None:
# define a subclass of py2exe to build our version file on the fly
class CustomPy2exe(py2exe.build_exe.py2exe):
class CheckTerrain(Command):
user_options=[]
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
from overviewer_core.textures import _find_file
import hashlib
import zipfile
print "checking..."
try:
import util
f = open("overviewer_version.py", "w")
f.write("VERSION=%r\n" % util.findGitVersion())
f.write("BUILD_DATE=%r\n" % time.asctime())
f.write("BUILD_PLATFORM=%r\n" % platform.processor())
f.write("BUILD_OS=%r\n" % platform.platform())
f.close()
setup_kwargs['data_files'].append(('.', ['overviewer_version.py']))
except:
print "WARNING: failed to build overview_version file"
py2exe.build_exe.py2exe.run(self)
setup_kwargs['cmdclass']['py2exe'] = CustomPy2exe
f = _find_file("terrain.png", verbose=True)
except IOError:
log.error("Could not find the file terrain.png")
return
h = hashlib.sha1()
h.update(f.read())
log.info("Hash of terrain.png file is: %s", h.hexdigest())
setup_kwargs['cmdclass']['clean'] = CustomClean
setup_kwargs['cmdclass']['build_ext'] = CustomBuild
setup_kwargs['cmdclass']['sdist'] = CustomSDist
setup_kwargs['cmdclass']['build'] = CustomBuild
setup_kwargs['cmdclass']['build_ext'] = CustomBuildExt
setup_kwargs['cmdclass']['check_terrain'] = CheckTerrain
###
setup(**setup_kwargs)
print "\nBuild Complete"