Merge branch 'devel'
This commit is contained in:
@@ -42,11 +42,6 @@ directory.
|
||||
self.outputdir = outputdir
|
||||
self.renders = dict()
|
||||
|
||||
# stores Points Of Interest to be mapped with markers
|
||||
# This is a dictionary of lists of dictionaries
|
||||
# Each regionset's name is a key in this dictionary
|
||||
self.POI = dict()
|
||||
|
||||
# look for overviewerConfig in self.outputdir
|
||||
try:
|
||||
with open(os.path.join(self.outputdir, "overviewerConfig.js")) as c:
|
||||
@@ -65,13 +60,6 @@ directory.
|
||||
return dict()
|
||||
|
||||
|
||||
|
||||
def found_poi(self, regionset, poi_type, contents, chunkX, chunkY):
|
||||
if regionset.name not in self.POI.keys():
|
||||
POI[regionset.name] = []
|
||||
# TODO based on the type, so something
|
||||
POI[regionset.name].append
|
||||
|
||||
def initialize(self, tilesets):
|
||||
"""Similar to finalize() but calls the tilesets' get_initial_data()
|
||||
instead of get_persistent_data() to compile the generated javascript
|
||||
@@ -152,6 +140,12 @@ directory.
|
||||
global_assets = os.path.join(util.get_program_path(), "web_assets")
|
||||
mirror_dir(global_assets, self.outputdir)
|
||||
|
||||
# write a dummy baseMarkers.js if none exists
|
||||
if not os.path.exists(os.path.join(self.outputdir, "baseMarkers.js")):
|
||||
with open(os.path.join(self.outputdir, "baseMarkers.js"), "w") as f:
|
||||
f.write("// if you wants signs, please see genPOI.py\n");
|
||||
|
||||
|
||||
# create overviewer.js from the source js files
|
||||
js_src = os.path.join(util.get_program_path(), "overviewer_core", "data", "js_src")
|
||||
if not os.path.isdir(js_src):
|
||||
|
||||
@@ -29,7 +29,19 @@ overviewer.collections = {
|
||||
*/
|
||||
'infoWindow': null,
|
||||
|
||||
'worldViews': []
|
||||
'worldViews': [],
|
||||
|
||||
'haveSigns': false,
|
||||
|
||||
/**
|
||||
* Hold the raw marker data for each tilest
|
||||
*/
|
||||
'markerInfo': {},
|
||||
|
||||
/**
|
||||
* holds a reference to the spawn marker.
|
||||
*/
|
||||
'spawnMarker': null,
|
||||
};
|
||||
|
||||
overviewer.classes = {
|
||||
|
||||
@@ -58,6 +58,13 @@ overviewer.util = {
|
||||
var coordsdiv = new overviewer.views.CoordboxView({tagName: 'DIV'});
|
||||
coordsdiv.render();
|
||||
|
||||
if (overviewer.collections.haveSigns) {
|
||||
var signs = new overviewer.views.SignControlView();
|
||||
signs.registerEvents(signs);
|
||||
}
|
||||
|
||||
var spawnmarker = new overviewer.views.SpawnIconView();
|
||||
|
||||
// Update coords on mousemove
|
||||
google.maps.event.addListener(overviewer.map, 'mousemove', function (event) {
|
||||
coordsdiv.updateCoords(event.latLng);
|
||||
@@ -72,6 +79,7 @@ overviewer.util = {
|
||||
overviewer.mapView.updateCurrentTileset();
|
||||
|
||||
compass.render();
|
||||
spawnmarker.render();
|
||||
|
||||
// re-center on the last viewport
|
||||
var currentWorldView = overviewer.mapModel.get("currentWorldView");
|
||||
@@ -102,6 +110,7 @@ overviewer.util = {
|
||||
overviewer.map.setZoom(zoom);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'});
|
||||
@@ -116,15 +125,43 @@ overviewer.util = {
|
||||
// Jump to the hash if given
|
||||
overviewer.util.initHash();
|
||||
|
||||
overviewer.util.initializeMarkers();
|
||||
|
||||
/*
|
||||
overviewer.util.initializeMapTypes();
|
||||
overviewer.util.initializeMap();
|
||||
overviewer.util.initializeMarkers();
|
||||
overviewer.util.initializeRegions();
|
||||
overviewer.util.createMapControls();
|
||||
*/
|
||||
},
|
||||
|
||||
'injectMarkerScript': function(url) {
|
||||
var m = document.createElement('script'); m.type = 'text/javascript'; m.async = false;
|
||||
m.src = url;
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.appendChild(m);
|
||||
},
|
||||
|
||||
'initializeMarkers': function() {
|
||||
return;
|
||||
|
||||
},
|
||||
|
||||
'createMarkerInfoWindow': function(marker) {
|
||||
var windowContent = '<div class="infoWindow"><img src="' + marker.icon +
|
||||
'"/><p>' + marker.title.replace(/\n/g,'<br/>') + '</p></div>';
|
||||
var infowindow = new google.maps.InfoWindow({
|
||||
'content': windowContent
|
||||
});
|
||||
google.maps.event.addListener(marker, 'click', function() {
|
||||
if (overviewer.collections.infoWindow) {
|
||||
overviewer.collections.infoWindow.close();
|
||||
}
|
||||
infowindow.open(overviewer.map, marker);
|
||||
overviewer.collections.infoWindow = infowindow;
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* This adds some methods to these classes because Javascript is stupid
|
||||
* and this seems like the best way to avoid re-creating the same methods
|
||||
@@ -239,7 +276,6 @@ overviewer.util = {
|
||||
|
||||
var zoomLevels = model.get("zoomLevels");
|
||||
var north_direction = model.get('north_direction');
|
||||
//console.log("fromWorldToLatLng: north_direction is %r", north_direction);
|
||||
|
||||
// the width and height of all the highest-zoom tiles combined,
|
||||
// inverted
|
||||
@@ -406,7 +442,6 @@ overviewer.util = {
|
||||
// save this info is a nice easy to parse format
|
||||
var currentWorldView = overviewer.mapModel.get("currentWorldView");
|
||||
currentWorldView.options.lastViewport = [x,y,z,zoom];
|
||||
//console.log("Updated lastViewport: %r" , [x,y,z,zoom]);
|
||||
window.location.replace("#/" + Math.floor(x) + "/" + Math.floor(y) + "/" + Math.floor(z) + "/" + zoom + "/" + w + "/" + maptype);
|
||||
},
|
||||
'updateHash': function() {
|
||||
|
||||
@@ -3,13 +3,9 @@ overviewer.views= {}
|
||||
|
||||
overviewer.views.WorldView = Backbone.View.extend({
|
||||
initialize: function(opts) {
|
||||
//console.log("WorldView::initialize()");
|
||||
//console.log(this.model.get("tileSets"));
|
||||
this.options.mapTypes = [];
|
||||
this.options.mapTypeIds = [];
|
||||
this.model.get("tileSets").each(function(tset, index, list) {
|
||||
//console.log(" eaching");
|
||||
//console.log(" Working on tileset %s" , tset.get("name"));
|
||||
var ops = {
|
||||
getTileUrl: overviewer.gmap.getTileUrlGenerator(tset.get("path"), tset.get("base"), tset.get("imgextension")),
|
||||
'tileSize': new google.maps.Size(
|
||||
@@ -57,7 +53,6 @@ overviewer.views.WorldSelectorView = Backbone.View.extend({
|
||||
"change select": "changeWorld"
|
||||
},
|
||||
changeWorld: function() {
|
||||
//console.log("change world!");
|
||||
var selectObj = this.$("select")[0];
|
||||
var selectedOption = selectObj.options[selectObj.selectedIndex];
|
||||
|
||||
@@ -120,11 +115,8 @@ overviewer.views.CoordboxView = Backbone.View.extend({
|
||||
|
||||
overviewer.views.GoogleMapView = Backbone.View.extend({
|
||||
initialize: function(opts) {
|
||||
//console.log(this);
|
||||
this.options.map = null;
|
||||
var curWorld = this.model.get("currentWorldView").model;
|
||||
//console.log("Current world:");
|
||||
//console.log(curWorld);
|
||||
|
||||
var curTset = curWorld.get("tileSets").at(0);
|
||||
|
||||
@@ -145,12 +137,6 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
||||
|
||||
var mapOptions = {};
|
||||
//
|
||||
curWorld.get("tileSets").each(function(elem, index, list) {
|
||||
//console.log("Setting up map for:");
|
||||
//console.log(elem);
|
||||
//console.log("for %s generating url func with %s and %s", elem.get("name"), elem.get("path"), elem.get("base"));
|
||||
|
||||
});
|
||||
// init the map with some default options. use the first tileset in the first world
|
||||
this.options.mapOptions = {
|
||||
zoom: curTset.get("defaultZoom"),
|
||||
@@ -174,7 +160,6 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
||||
// register every ImageMapType with the map
|
||||
$.each(overviewer.collections.worldViews, function( index, worldView) {
|
||||
$.each(worldView.options.mapTypes, function(i_index, maptype) {
|
||||
//console.log("registered %s with the maptype registery", worldView.model.get("name") + maptype.shortname);
|
||||
overviewer.map.mapTypes.set(overviewerConfig.CONST.mapDivId +
|
||||
worldView.model.get("name") + maptype.shortname , maptype);
|
||||
});
|
||||
@@ -185,7 +170,6 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
||||
* Should be called when the current world has changed in GoogleMapModel
|
||||
*/
|
||||
render: function() {
|
||||
//console.log("GoogleMapView::render()");
|
||||
var view = this.model.get("currentWorldView");
|
||||
this.options.mapOptions.mapTypeControlOptions = {
|
||||
mapTypeIds: view.options.mapTypeIds};
|
||||
@@ -200,14 +184,11 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
||||
* Keeps track of the currently visible tileset
|
||||
*/
|
||||
updateCurrentTileset: function() {
|
||||
//console.log("GoogleMapView::updateCurrentTileset()");
|
||||
var currentWorldView = this.model.get("currentWorldView");
|
||||
var gmapCurrent = overviewer.map.getMapTypeId();
|
||||
for (id in currentWorldView.options.mapTypeIds) {
|
||||
if (currentWorldView.options.mapTypeIds[id] == gmapCurrent) {
|
||||
//console.log("updating currenttileset");
|
||||
this.options.currentTileSet = currentWorldView.model.get("tileSets").at(id);
|
||||
//console.log(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,3 +200,188 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SignControlView
|
||||
*/
|
||||
overviewer.views.SignControlView = Backbone.View.extend({
|
||||
/** SignControlView::initialize
|
||||
*/
|
||||
initialize: function(opts) {
|
||||
$(this.el).addClass("customControl");
|
||||
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(this.el);
|
||||
|
||||
},
|
||||
registerEvents: function(me) {
|
||||
google.maps.event.addListener(overviewer.map, 'maptypeid_changed', function(event) {
|
||||
overviewer.mapView.updateCurrentTileset();
|
||||
|
||||
// workaround IE issue. bah!
|
||||
if (typeof markers=="undefined") { return; }
|
||||
me.render();
|
||||
// hide markers, if necessary
|
||||
// for each markerSet, check:
|
||||
// if the markerSet isnot part of this tileset, hide all of the markers
|
||||
var curMarkerSet = overviewer.mapView.options.currentTileSet.attributes.path;
|
||||
var dataRoot = markers[curMarkerSet];
|
||||
if (!dataRoot) {
|
||||
// this tileset has no signs, so hide all of them
|
||||
for (markerSet in markersDB) {
|
||||
if (markersDB[markerSet].created) {
|
||||
jQuery.each(markersDB[markerSet].raw, function(i, elem) {
|
||||
elem.markerObj.setVisible(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
var groupsForThisTileSet = jQuery.map(dataRoot, function(elem, i) { return elem.groupName;})
|
||||
for (markerSet in markersDB) {
|
||||
if (jQuery.inArray(markerSet, groupsForThisTileSet) == -1){
|
||||
// hide these
|
||||
if (markersDB[markerSet].created) {
|
||||
jQuery.each(markersDB[markerSet].raw, function(i, elem) {
|
||||
elem.markerObj.setVisible(false);
|
||||
});
|
||||
}
|
||||
markersDB[markerSet].checked=false;
|
||||
}
|
||||
// make sure the checkboxes checked if necessary
|
||||
$("[_mc_groupname=" + markerSet + "]").attr("checked", markersDB[markerSet].checked);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
/**
|
||||
* SignControlView::render
|
||||
*/
|
||||
render: function() {
|
||||
|
||||
var curMarkerSet = overviewer.mapView.options.currentTileSet.attributes.path;
|
||||
//var dataRoot = overviewer.collections.markerInfo[curMarkerSet];
|
||||
var dataRoot = markers[curMarkerSet];
|
||||
|
||||
this.el.innerHTML=""
|
||||
|
||||
// if we have no markerSets for this tileset, do nothing:
|
||||
if (!dataRoot) { return; }
|
||||
|
||||
|
||||
var controlText = document.createElement('DIV');
|
||||
controlText.innerHTML = "Signs";
|
||||
|
||||
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='';
|
||||
|
||||
// add the functionality to toggle visibility of the items
|
||||
$(controlText).click(function() {
|
||||
$(controlBorder).toggleClass('top-active');
|
||||
$(dropdownDiv).toggle();
|
||||
});
|
||||
|
||||
|
||||
// add some menus
|
||||
for (i in dataRoot) {
|
||||
var group = dataRoot[i];
|
||||
this.addItem({label: group.displayName, groupName:group.groupName, action:function(this_item, checked) {
|
||||
markersDB[this_item.groupName].checked = checked;
|
||||
jQuery.each(markersDB[this_item.groupName].raw, function(i, elem) {
|
||||
elem.markerObj.setVisible(checked);
|
||||
});
|
||||
}});
|
||||
}
|
||||
|
||||
iconURL = overviewerConfig.CONST.image.signMarker;
|
||||
//dataRoot['markers'] = [];
|
||||
//
|
||||
for (i in dataRoot) {
|
||||
var groupName = dataRoot[i].groupName;
|
||||
if (!markersDB[groupName].created) {
|
||||
for (j in markersDB[groupName].raw) {
|
||||
var entity = markersDB[groupName].raw[j];
|
||||
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),
|
||||
'icon': iconURL,
|
||||
'visible': false
|
||||
});
|
||||
if (entity['id'] == 'Sign') {
|
||||
overviewer.util.createMarkerInfoWindow(marker);
|
||||
}
|
||||
jQuery.extend(entity, {markerObj: marker});
|
||||
}
|
||||
markersDB[groupName].created = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
addItem: function(item) {
|
||||
var itemDiv = document.createElement('div');
|
||||
var itemInput = document.createElement('input');
|
||||
itemInput.type='checkbox';
|
||||
|
||||
// give it a name
|
||||
$(itemInput).data('label',item.label);
|
||||
$(itemInput).attr("_mc_groupname", item.groupName);
|
||||
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');
|
||||
if(item.icon) {
|
||||
textNode.innerHTML = '<img width="15" height="15" src="' +
|
||||
item.icon + '">' + item.label + '<br/>';
|
||||
} else {
|
||||
textNode.innerHTML = item.label + '<br/>';
|
||||
}
|
||||
|
||||
itemDiv.appendChild(textNode);
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* SpawnIconView
|
||||
*/
|
||||
overviewer.views.SpawnIconView = Backbone.View.extend({
|
||||
render: function() {
|
||||
//
|
||||
var curTileSet = overviewer.mapView.options.currentTileSet;
|
||||
if (overviewer.collections.spawnMarker) {
|
||||
overviewer.collections.spawnMarker.setMap(null);
|
||||
overviewer.collections.spawnMarker = null;
|
||||
}
|
||||
var spawn = curTileSet.get("spawn");
|
||||
if (spawn) {
|
||||
overviewer.collections.spawnMarker = new google.maps.Marker({
|
||||
'position': overviewer.util.fromWorldToLatLng(spawn[0],
|
||||
spawn[1], spawn[2], overviewer.mapView.options.currentTileSet),
|
||||
'map': overviewer.map,
|
||||
'title': 'spawn',
|
||||
'icon': overviewerConfig.CONST.image.spawnMarker,
|
||||
'visible': false
|
||||
});
|
||||
overviewer.collections.spawnMarker.setVisible(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<script type="text/javascript" src="backbone.js"></script>
|
||||
<script type="text/javascript" src="overviewerConfig.js"></script>
|
||||
<script type="text/javascript" src="overviewer.js"></script>
|
||||
<script type="text/javascript" src="baseMarkers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ renders = Setting(required=True, default=util.OrderedDict(),
|
||||
"rerenderprob": Setting(required=True, validator=validateFloat, 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=[]),
|
||||
|
||||
# Remove this eventually (once people update their configs)
|
||||
"worldname": Setting(required=False, default=None,
|
||||
|
||||
@@ -43,6 +43,13 @@ def checkBadEscape(s):
|
||||
fixed = True
|
||||
return (fixed, fixed_string)
|
||||
|
||||
def validateMarkers(filterlist):
|
||||
if type(filterlist) != list:
|
||||
raise ValidationException("Markers must specify a list of filters")
|
||||
for x in filterlist:
|
||||
if not callable(x):
|
||||
raise ValidationException("%r must be a function"% x)
|
||||
return filterlist
|
||||
|
||||
def validateWorldPath(worldpath):
|
||||
_, worldpath = checkBadEscape(worldpath)
|
||||
|
||||
@@ -178,10 +178,7 @@ class TileSet(object):
|
||||
This class does nothing with it except pass it through.
|
||||
|
||||
outputdir is the absolute path to the tile output directory where the
|
||||
tiles are saved. It is assumed to exist already.
|
||||
TODO: This should probably be relative to the asset manager's output
|
||||
directory to avoid redundancy.
|
||||
|
||||
tiles are saved. It is created if it doesn't exist
|
||||
|
||||
Current valid options for the options dictionary are shown below. All
|
||||
the options must be specified unless they are not relevant. If the
|
||||
@@ -211,12 +208,15 @@ class TileSet(object):
|
||||
partial interrupted render left off.
|
||||
|
||||
1
|
||||
For render-tiles, render all whose chunks have an mtime greater
|
||||
than the mtime of the tile on disk, and their upper-tile
|
||||
ancestors.
|
||||
"check-tiles" mode. For render-tiles, render all whose chunks
|
||||
have an mtime greater than the mtime of the tile on disk, and
|
||||
their upper-tile ancestors.
|
||||
|
||||
Also check all other upper-tiles and render any that have
|
||||
children with more rencent mtimes than itself.
|
||||
|
||||
Also remove tiles and directory trees that do exist but
|
||||
shouldn't.
|
||||
|
||||
This is slower due to stat calls to determine tile mtimes, but
|
||||
safe if the last render was interrupted.
|
||||
@@ -270,6 +270,7 @@ class TileSet(object):
|
||||
self.regionset = regionsetobj
|
||||
self.am = assetmanagerobj
|
||||
self.textures = texturesobj
|
||||
self.outputdir = os.path.abspath(outputdir)
|
||||
|
||||
config = self.am.get_tileset_config(self.options.get("name"))
|
||||
self.config = config
|
||||
@@ -277,17 +278,42 @@ class TileSet(object):
|
||||
self.last_rendertime = config.get('last_rendertime', 0)
|
||||
|
||||
if "renderchecks" not in self.options:
|
||||
# renderchecks was not given, this indicates it was not specified
|
||||
# in either the config file or the command line. The following code
|
||||
# attempts to detect the most appropriate mode
|
||||
if not config:
|
||||
# No persistent config? This is a full render then.
|
||||
# No persistent config?
|
||||
if os.path.exists(self.outputdir):
|
||||
# Somehow there's no config but the output dir DOES exist.
|
||||
# That's strange!
|
||||
logging.warning(
|
||||
"For render '%s' I couldn't find any persistent config, "
|
||||
"but I did find my tile directory already exists. This "
|
||||
"shouldn't normally happen, something may be up, but I "
|
||||
"think I can continue...", self.options['name'])
|
||||
logging.info("Switching to --check-tiles mode")
|
||||
self.options['renderchecks'] = 1
|
||||
else:
|
||||
# This is the typical code path for an initial render, make
|
||||
# this a "forcerender"
|
||||
self.options['renderchecks'] = 2
|
||||
logging.debug("This is the first time rendering %s. Doing" +
|
||||
" a full-render",
|
||||
self.options['name'])
|
||||
elif not os.path.exists(self.outputdir):
|
||||
# Somehow the outputdir got erased but the metadata file is
|
||||
# still there. That's strange!
|
||||
logging.warning(
|
||||
"This is strange. There is metadata for render '%s' but "
|
||||
"the output directory is missing. This shouldn't "
|
||||
"normally happen. I guess we have no choice but to do a "
|
||||
"--forcerender", self.options['name'])
|
||||
self.options['renderchecks'] = 2
|
||||
logging.debug("This is the first time rendering %s. Doing" +
|
||||
" a full-render",
|
||||
self.options['name'])
|
||||
elif config.get("render_in_progress", False):
|
||||
# The last render must have been interrupted. The default should be
|
||||
# 1 then, not 0
|
||||
# a check-tiles render then
|
||||
logging.warning(
|
||||
"The last render for %s didn't finish. I'll be " +
|
||||
"The last render for '%s' didn't finish. I'll be " +
|
||||
"scanning all the tiles to make sure everything's up "+
|
||||
"to date.",
|
||||
self.options['name'],
|
||||
@@ -304,9 +330,15 @@ class TileSet(object):
|
||||
)
|
||||
self.options['renderchecks'] = 0
|
||||
|
||||
# Throughout the class, self.outputdir is an absolute path to the
|
||||
# directory where we output tiles. It is assumed to exist.
|
||||
self.outputdir = os.path.abspath(outputdir)
|
||||
if not os.path.exists(self.outputdir):
|
||||
if self.options['renderchecks'] != 2:
|
||||
logging.warning(
|
||||
"The tile directory didn't exist, but you have specified "
|
||||
"explicitly not to do a --fullrender (which is the default for "
|
||||
"this situation). I'm overriding your decision and setting "
|
||||
"--fullrender for just this run")
|
||||
self.options['rednerchecks'] = 2
|
||||
os.mkdir(self.outputdir)
|
||||
|
||||
# Set the image format according to the options
|
||||
if self.options['imgformat'] == 'png':
|
||||
@@ -375,6 +407,7 @@ class TileSet(object):
|
||||
This method returns an iterator over (obj, [dependencies, ...])
|
||||
"""
|
||||
|
||||
# The following block of code implementes the changelist functionality.
|
||||
fd = self.options.get("changelist", None)
|
||||
if fd:
|
||||
logging.debug("Changelist activated for %s (fileno %s)", self, fd)
|
||||
@@ -485,6 +518,8 @@ class TileSet(object):
|
||||
last_rendertime = self.max_chunk_mtime,
|
||||
imgextension = self.imgextension,
|
||||
)
|
||||
if (self.regionset.get_type() == "overworld"):
|
||||
d.update({"spawn": self.options.get("spawn")})
|
||||
try:
|
||||
d['north_direction'] = self.regionset.north_dir
|
||||
except AttributeError:
|
||||
@@ -492,7 +527,6 @@ class TileSet(object):
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def _find_chunk_range(self):
|
||||
"""Finds the chunk range in rows/columns and stores them in
|
||||
self.minrow, self.maxrow, self.mincol, self.maxcol
|
||||
@@ -569,6 +603,11 @@ class TileSet(object):
|
||||
logging.warning("Your map seems to have shrunk. Did you delete some chunks? No problem. Re-arranging tiles, just a sec...")
|
||||
for _ in xrange(curdepth - self.treedepth):
|
||||
self._decrease_depth()
|
||||
logging.info(
|
||||
"There done. I'm switching to --check-tiles mode for "
|
||||
"this one render. This will make sure any old tiles that "
|
||||
"should no longer exist are deleted.")
|
||||
self.options['renderchecks'] = 1
|
||||
|
||||
def _increase_depth(self):
|
||||
"""Moves existing tiles into place for a larger tree"""
|
||||
@@ -928,11 +967,17 @@ class TileSet(object):
|
||||
os.utime(tmppath, (max_chunk_mtime, max_chunk_mtime))
|
||||
|
||||
def _iterate_and_check_tiles(self, path):
|
||||
"""A generator function over all tiles that need rendering in the
|
||||
subtree identified by path. This yields, in order, all tiles that need
|
||||
"""A generator function over all tiles that should exist in the subtree
|
||||
identified by path. This yields, in order, all tiles that need
|
||||
rendering in a post-traversal order, including this node itself.
|
||||
|
||||
This method actually yields tuples in this form:
|
||||
This method takes one parameter:
|
||||
|
||||
path
|
||||
The path of a tile that should exist
|
||||
|
||||
|
||||
This method yields tuples in this form:
|
||||
(path, mtime, needs_rendering)
|
||||
path
|
||||
is the path tuple of the tile that needs rendering
|
||||
@@ -945,8 +990,9 @@ class TileSet(object):
|
||||
is a boolean indicating this tile does in fact need rendering.
|
||||
|
||||
(Since this is a recursive generator, tiles that don't need rendering
|
||||
are not propagated all the way out of the recursive stack, but the
|
||||
immediate parent call needs to know its mtime)
|
||||
are not propagated all the way out of the recursive stack, but are
|
||||
still yielded to the immediate parent because it needs to know its
|
||||
childs' mtimes)
|
||||
|
||||
"""
|
||||
if len(path) == self.treedepth:
|
||||
@@ -967,7 +1013,16 @@ class TileSet(object):
|
||||
# If a tile has been modified more recently than any of its
|
||||
# chunks, then this could indicate a potential issue with
|
||||
# this or future renders.
|
||||
logging.warning("I found a tile with a more recent modification time than any of its chunks. This can happen when a tile has been modified with an outside program, or by a copy utility that doesn't preserve mtimes. Overviewer uses the filesystem's mtimes to determine which tiles need rendering and which don't, so it's important to preserve the mtimes Overviewer sets. Please see our FAQ page on docs.overviewer.org or ask us in IRC for more information")
|
||||
logging.warning(
|
||||
"I found a tile with a more recent modification time "
|
||||
"than any of its chunks. This can happen when a tile has "
|
||||
"been modified with an outside program, or by a copy "
|
||||
"utility that doesn't preserve mtimes. Overviewer uses "
|
||||
"the filesystem's mtimes to determine which tiles need "
|
||||
"rendering and which don't, so it's important to "
|
||||
"preserve the mtimes Overviewer sets. Please see our FAQ "
|
||||
"page on docs.overviewer.org or ask us in IRC for more "
|
||||
"information")
|
||||
logging.warning("Tile was: %s", imgpath)
|
||||
|
||||
if max_chunk_mtime > tile_mtime:
|
||||
@@ -975,20 +1030,23 @@ class TileSet(object):
|
||||
yield (path, None, True)
|
||||
else:
|
||||
# This doesn't need rendering. Return mtime to parent in case
|
||||
# they do need to render
|
||||
# its mtime is less, indicating the parent DOES need a render
|
||||
yield path, max_chunk_mtime, False
|
||||
|
||||
else:
|
||||
# A composite-tile.
|
||||
# First, recurse to each of our children
|
||||
render_me = False
|
||||
max_child_mtime = 0
|
||||
|
||||
# First, recurse to each of our children
|
||||
for childnum in xrange(4):
|
||||
childpath = path + (childnum,)
|
||||
|
||||
# Check if this sub-tree should actually exist, so that we only
|
||||
# end up checking tiles that actually exist
|
||||
if not self.dirtytree.query_path(childpath):
|
||||
# Here check if it *does* exist, and if so, nuke it.
|
||||
self._nuke_path(childpath)
|
||||
continue
|
||||
|
||||
for child_path, child_mtime, child_needs_rendering in \
|
||||
@@ -1034,6 +1092,31 @@ class TileSet(object):
|
||||
# Nope.
|
||||
yield path, max_child_mtime, False
|
||||
|
||||
def _nuke_path(self, path):
|
||||
"""Given a quadtree path, erase it from disk. This is called by
|
||||
_iterate_and_check_tiles() as a helper-method.
|
||||
|
||||
"""
|
||||
if len(path) == self.treedepth:
|
||||
# path referrs to a single tile
|
||||
tileobj = RenderTile.from_path(path)
|
||||
imgpath = tileobj.get_filepath(self.outputdir, self.imgextension)
|
||||
if os.path.exists(imgpath):
|
||||
# No need to catch ENOENT since this is only called from the
|
||||
# master process
|
||||
logging.debug("Found an image that shouldn't exist. Deleting it: %s", imgpath)
|
||||
os.remove(imgpath)
|
||||
else:
|
||||
# path referrs to a composite tile, and by extension a directory
|
||||
dirpath = os.path.join(self.outputdir, *(str(x) for x in path))
|
||||
imgpath = dirpath + "." + self.imgextension
|
||||
if os.path.exists(imgpath):
|
||||
logging.debug("Found an image that shouldn't exist. Deleting it: %s", imgpath)
|
||||
os.remove(imgpath)
|
||||
if os.path.exists(dirpath):
|
||||
logging.debug("Found a subtree that shouldn't exist. Deleting it: %s", dirpath)
|
||||
shutil.rmtree(dirpath)
|
||||
|
||||
##
|
||||
## Functions for converting (x, z) to (col, row) and back
|
||||
##
|
||||
|
||||
@@ -19,6 +19,8 @@ import os.path
|
||||
from glob import glob
|
||||
import logging
|
||||
import hashlib
|
||||
import time
|
||||
import random
|
||||
|
||||
import numpy
|
||||
|
||||
@@ -91,8 +93,16 @@ class World(object):
|
||||
if not os.path.exists(os.path.join(self.worlddir, "level.dat")):
|
||||
raise ValueError("level.dat not found in %s" % self.worlddir)
|
||||
|
||||
# Hard-code this to only work with format version 19133, "Anvil"
|
||||
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
|
||||
# it seems that reading a level.dat file is unstable, particularly with respect
|
||||
# to the spawnX,Y,Z variables. So we'll try a few times to get a good reading
|
||||
# empirically, it seems that 0,50,0 is a "bad" reading
|
||||
# update: 0,50,0 is the default spawn, and may be valid is some cases
|
||||
# more info is needed
|
||||
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
|
||||
|
||||
|
||||
# Hard-code this to only work with format version 19133, "Anvil"
|
||||
if not ('version' in data and data['version'] == 19133):
|
||||
logging.critical("Sorry, This version of Minecraft-Overviewer only works with the 'Anvil' chunk format")
|
||||
raise ValueError("World at %s is not compatible with Overviewer" % self.worlddir)
|
||||
@@ -163,7 +173,7 @@ class World(object):
|
||||
# location
|
||||
|
||||
## read spawn info from level.dat
|
||||
data = self.data
|
||||
data = self.leveldat
|
||||
disp_spawnX = spawnX = data['SpawnX']
|
||||
spawnY = data['SpawnY']
|
||||
disp_spawnZ = spawnZ = data['SpawnZ']
|
||||
@@ -175,24 +185,32 @@ class World(object):
|
||||
## clamp spawnY to a sane value, in-chunk value
|
||||
if spawnY < 0:
|
||||
spawnY = 0
|
||||
if spawnY > 127:
|
||||
spawnY = 127
|
||||
if spawnY > 255:
|
||||
spawnY = 255
|
||||
|
||||
# Open up the chunk that the spawn is in
|
||||
regionset = self.get_regionset(0)
|
||||
regionset = self.get_regionset("overworld")
|
||||
try:
|
||||
chunk = regionset.get_chunk(chunkX, chunkZ)
|
||||
except ChunkDoesntExist:
|
||||
return (spawnX, spawnY, spawnZ)
|
||||
|
||||
def getBlock(y):
|
||||
"This is stupid and slow but I don't care"
|
||||
targetSection = spawnY//16
|
||||
for section in chunk['Sections']:
|
||||
if section['Y'] == targetSection:
|
||||
blockArray = section['Blocks']
|
||||
return blockArray[inChunkX, inChunkZ, y % 16]
|
||||
|
||||
|
||||
blockArray = chunk['Blocks']
|
||||
|
||||
## The block for spawn *within* the chunk
|
||||
inChunkX = spawnX - (chunkX*16)
|
||||
inChunkZ = spawnZ - (chunkZ*16)
|
||||
|
||||
## find the first air block
|
||||
while (blockArray[inChunkX, inChunkZ, spawnY] != 0) and spawnY < 127:
|
||||
while (getBlock(spawnY) != 0) and spawnY < 256:
|
||||
spawnY += 1
|
||||
|
||||
return spawnX, spawnY, spawnZ
|
||||
|
||||
Reference in New Issue
Block a user