Merge branch 'master' into my_genpoi
Conflicts: overviewer_core/aux_files/genPOI.py
This commit is contained in:
@@ -377,6 +377,31 @@ Observers
|
||||
|
||||
**Required**
|
||||
|
||||
``RConObserver(target, password[, port][, pct_interval])``
|
||||
This Observer will announce render progress with the server's ``say``
|
||||
command through RCon.
|
||||
|
||||
* ``target=<address>``
|
||||
Address of the target Minecraft server.
|
||||
|
||||
**Required**
|
||||
|
||||
* ``password=<rcon password>``
|
||||
The server's rcon password.
|
||||
|
||||
**Required**
|
||||
|
||||
* ``port=<port number>``
|
||||
Port on which the Minecraft server listens for incoming RCon connections.
|
||||
|
||||
**Default:** ``25575``
|
||||
|
||||
* ``pct_interval=<update rate, in percent>``
|
||||
Percentage interval in which the progress should be announced, the same as
|
||||
for ``ServerAnnounceObserver``.
|
||||
|
||||
**Default:** ``10``
|
||||
|
||||
|
||||
|
||||
Custom web assets
|
||||
@@ -683,8 +708,8 @@ Image options
|
||||
Using image optimizers will increase render times significantly.
|
||||
|
||||
This option specifies which additional tools overviewer should use to
|
||||
optimize the filesize of png tiles.
|
||||
The tools used must be placed somewhere, where overviewer can find them, for
|
||||
optimize the filesize of rendered tiles.
|
||||
The tools used must be placed somewhere where overviewer can find them, for
|
||||
example the "PATH" environment variable or a directory like /usr/bin.
|
||||
|
||||
The option is a list of Optimizer objects, which are then executed in
|
||||
@@ -766,6 +791,34 @@ Image options
|
||||
|
||||
**Default:** ``False``
|
||||
|
||||
``jpegoptim``
|
||||
jpegoptim can do both lossy and lossless JPEG optimisation. If no options are specified,
|
||||
jpegoptim will only do lossless optimisations.
|
||||
Available settings:
|
||||
|
||||
``quality``
|
||||
A number between 0 and 100 that corresponds to the jpeg quality level. If the input
|
||||
image has a lower quality specified than the output image, jpegoptim will only do
|
||||
lossless optimisations.
|
||||
|
||||
If this option is specified and the above condition does not apply, jpegoptim will
|
||||
do lossy optimisation.
|
||||
|
||||
**Default:** ``None`` *(= Unspecified)*
|
||||
|
||||
``target_size``
|
||||
Either a percentage of the original filesize (e.g. ``"50%"``) or a target filesize
|
||||
in kilobytes (e.g. ``15``). jpegoptim will then try to reach this as its target size.
|
||||
|
||||
If specified, jpegoptim will do lossy optimisation.
|
||||
|
||||
.. warning::
|
||||
This appears to have a greater performance impact than just setting ``quality``.
|
||||
Unless predictable filesizes are a thing you need, you should probably use ``quality``
|
||||
instead.
|
||||
|
||||
**Default:** ``None`` *(= Unspecified)*
|
||||
|
||||
**Default:** ``[]``
|
||||
|
||||
Zoom
|
||||
@@ -1099,6 +1152,32 @@ MineralOverlay
|
||||
|
||||
MineralOverlay(minerals=[(64,(255,255,0)), (13,(127,0,127))])
|
||||
|
||||
StructureOverlay
|
||||
Color the map according to patterns of blocks. With this rail overlays
|
||||
or overlays for other small structures can be realized. It can also be
|
||||
a MineralOverlay with alpha support.
|
||||
|
||||
This Overlay colors according to a patterns that are specified as
|
||||
multiple tuples of the form ``(relx, rely, relz, blockid)``. So
|
||||
by specifying ``(0, -1, 0, 4)`` the block below the current one has to
|
||||
be a cobblestone.
|
||||
|
||||
One color is then specified as
|
||||
``((relblockid1, relblockid2, ...), (r, g, b, a))`` where the
|
||||
``relblockid*`` are relative coordinates and the blockid as specified
|
||||
above. The ``relblockid*`` must match all at the same time for the
|
||||
color to apply.
|
||||
|
||||
Example::
|
||||
|
||||
MineralOverlay(minerals=[(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)),
|
||||
(((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255))])
|
||||
|
||||
In this example all rails(66) on top of cobblestone are rendered in
|
||||
pure red. And all powerrails(27) are rendered in green.
|
||||
|
||||
If ``minerals`` is not provided, a default rail coloring is used.
|
||||
|
||||
BiomeOverlay
|
||||
Color the map according to the biome at that point. Either use on
|
||||
top of other modes or on top of ClearBase to create a pure overlay.
|
||||
|
||||
@@ -194,7 +194,6 @@ is typically correct.
|
||||
generation, and ONLY generates markers. See :ref:`signsmarkers` on how to
|
||||
configure POI options.
|
||||
|
||||
|
||||
.. cmdoption:: -p <procs>, --processes <procs>
|
||||
|
||||
This specifies the number of worker processes to spawn on the local machine
|
||||
@@ -203,6 +202,17 @@ is typically correct.
|
||||
|
||||
This option can also be specified in the config file as :ref:`processes <processes>`
|
||||
|
||||
.. cmdoption:: --skip-scan
|
||||
|
||||
.. note::
|
||||
Don't use this flag without first reading :ref:`signsmarkers`!
|
||||
|
||||
When generating POI markers, this option prevents scanning for entities and
|
||||
tile entities, and only creates the markers specified in the config file.
|
||||
This considerably speeds up the POI marker generation process if no entities
|
||||
or tile entities are being used for POI markers. See :ref:`signsmarkers` on
|
||||
how to configure POI options.
|
||||
|
||||
.. cmdoption:: -v, --verbose
|
||||
|
||||
Activate a more verbose logging format and turn on debugging output. This
|
||||
|
||||
@@ -50,6 +50,16 @@ def main():
|
||||
# bootstrap the logger with defaults
|
||||
logger.configure()
|
||||
|
||||
if os.name == "posix":
|
||||
if os.geteuid() == 0:
|
||||
logging.warning("You are running Overviewer as root. "
|
||||
"It is recommended that you never do this, "
|
||||
"as it is dangerous for your system. If you are running "
|
||||
"into permission errors, fix your file/directory "
|
||||
"permissions instead. Overviewer does not need access to "
|
||||
"critical system resources and therefore does not require "
|
||||
"root access.")
|
||||
|
||||
try:
|
||||
cpus = multiprocessing.cpu_count()
|
||||
except NotImplementedError:
|
||||
@@ -478,8 +488,8 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
||||
logging.error("Sorry, I can't find anything to render! Are you sure there are .mca files in the world directory?")
|
||||
return 1
|
||||
if rset == None: # indicates no such dimension was found:
|
||||
logging.error("Sorry, you requested dimension '%s' for %s, but I couldn't find it", render['dimension'][0], render_name)
|
||||
return 1
|
||||
logging.warn("Sorry, you requested dimension '%s' for %s, but I couldn't find it", render['dimension'][0], render_name)
|
||||
continue
|
||||
|
||||
#################
|
||||
# Apply any regionset transformations here
|
||||
@@ -518,6 +528,12 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
||||
tset = tileset.TileSet(w, rset, assetMrg, tex, tileSetOpts, tileset_dir)
|
||||
tilesets.append(tset)
|
||||
|
||||
# If none of the requested dimenstions exist, tilesets will be empty
|
||||
if not tilesets:
|
||||
logging.error("There are no tilesets to render! There's nothing to do, so exiting.")
|
||||
return 1
|
||||
|
||||
|
||||
# Do tileset preprocessing here, before we start dispatching jobs
|
||||
logging.info("Preprocessing...")
|
||||
for ts in tilesets:
|
||||
|
||||
@@ -385,8 +385,8 @@ def main():
|
||||
# get the regionset for this dimension
|
||||
rset = w.get_regionset(render['dimension'][1])
|
||||
if rset == None: # indicates no such dimension was found:
|
||||
logging.error("Sorry, you requested dimension '%s' for the render '%s', but I couldn't find it", render['dimension'][0], rname)
|
||||
return 1
|
||||
logging.warn("Sorry, you requested dimension '%s' for the render '%s', but I couldn't find it", render['dimension'][0], rname)
|
||||
continue
|
||||
|
||||
# find filters for this render
|
||||
for f in render['markers']:
|
||||
@@ -462,6 +462,7 @@ def main():
|
||||
with open(os.path.join(destdir, "baseMarkers.js"), "w") as output:
|
||||
output.write("overviewer.util.injectMarkerScript('markersDB.js');\n")
|
||||
output.write("overviewer.util.injectMarkerScript('markers.js');\n")
|
||||
output.write("overviewer.util.injectMarkerScript('regions.js');\n")
|
||||
output.write("overviewer.collections.haveSigns=true;\n")
|
||||
logging.info("Done")
|
||||
|
||||
|
||||
@@ -496,6 +496,26 @@ overviewer.views.SignControlView = Backbone.View.extend({
|
||||
'strokeColor': entity['strokeColor']
|
||||
});
|
||||
dataRoot[i].markerObjs.push(polyline);
|
||||
}
|
||||
|
||||
// Polygons
|
||||
if (typeof entity['polygon'] != 'undefined') {
|
||||
var polypath = new Array();
|
||||
for (point in entity.polygon) {
|
||||
polypath.push(overviewer.util.fromWorldToLatLng(entity.polygon[point].x, entity.polygon[point].y, entity.polygon[point].z, overviewer.mapView.options.currentTileSet));
|
||||
}
|
||||
|
||||
var polygon = new google.maps.Polygon({
|
||||
'clickable': false,
|
||||
'fillColor': entity['fillColor'],
|
||||
'fillOpacity': entity['fillOpacity'],
|
||||
'map': overviewer.map,
|
||||
'path': polypath,
|
||||
'strokeColor': entity['strokeColor'],
|
||||
'strokeOpacity': entity['strokeOpacity'],
|
||||
'visible': false
|
||||
});
|
||||
dataRoot[i].markerObjs.push(polygon);
|
||||
}
|
||||
}
|
||||
dataRoot[i].created = true;
|
||||
|
||||
@@ -1,9 +1,34 @@
|
||||
// This is just an example. You can run some scripts to generate regions that
|
||||
// will be drawn here.
|
||||
overviewer.collections.regionDatas.push([
|
||||
// {"color": "#FFAA00", "opacity": 0.5, "closed": true, "path": [
|
||||
// {"x": 0, "y": 0, "z": 0},
|
||||
// {"x": 0, "y": 10, "z": 0},
|
||||
// {"x": 0, "y": 0, "z": 10}
|
||||
// ]},
|
||||
]);
|
||||
/*
|
||||
var groupName = "Regions";
|
||||
var displayName = "Areas";
|
||||
var world = "top";
|
||||
|
||||
markersDB[groupName] = {
|
||||
"raw": [
|
||||
{
|
||||
"fillColor": "#00FF00",
|
||||
"fillOpacity": 0.2,
|
||||
"strokeColor": "#FF0000",
|
||||
"strokeOpacity": 1,
|
||||
"polygon" : [
|
||||
{"x": 347, "y": 67, "z": 95},
|
||||
{"x": 347, "y": 77, "z": 95},
|
||||
{"x": 347, "y": 77, "z": 105},
|
||||
{"x": 347, "y": 67, "z": 105},
|
||||
{"x": 347, "y": 67, "z": 105}
|
||||
]}
|
||||
],
|
||||
"name": "Regions",
|
||||
"created": false
|
||||
}
|
||||
|
||||
markers[world].push(
|
||||
{
|
||||
"groupName": groupName,
|
||||
"icon": "signpost_icon.png",
|
||||
"createInfoWindow": false,
|
||||
"displayName": displayName,
|
||||
"checked": true
|
||||
});
|
||||
*/
|
||||
@@ -65,7 +65,9 @@ class NBTFileReader(object):
|
||||
# compile the unpacker's into a classes
|
||||
_byte = struct.Struct("b")
|
||||
_short = struct.Struct(">h")
|
||||
_ushort = struct.Struct(">H")
|
||||
_int = struct.Struct(">i")
|
||||
_uint = struct.Struct(">I")
|
||||
_long = struct.Struct(">q")
|
||||
_float = struct.Struct(">f")
|
||||
_double = struct.Struct(">d")
|
||||
@@ -128,17 +130,17 @@ class NBTFileReader(object):
|
||||
return self._double.unpack(bytes)[0]
|
||||
|
||||
def _read_tag_byte_array(self):
|
||||
length = self._read_tag_int()
|
||||
length = self._uint.unpack(self._file.read(4))[0]
|
||||
bytes = self._file.read(length)
|
||||
return bytes
|
||||
|
||||
def _read_tag_int_array(self):
|
||||
length = self._read_tag_int()
|
||||
length = self._uint.unpack(self._file.read(4))[0]
|
||||
int_bytes = self._file.read(length*4)
|
||||
return struct.unpack(">%ii" % length, int_bytes)
|
||||
|
||||
def _read_tag_string(self):
|
||||
length = self._read_tag_short()
|
||||
length = self._ushort.unpack(self._file.read(2))[0]
|
||||
# Read the string
|
||||
string = self._file.read(length)
|
||||
# decode it and return
|
||||
@@ -146,7 +148,7 @@ class NBTFileReader(object):
|
||||
|
||||
def _read_tag_list(self):
|
||||
tagid = self._read_tag_byte()
|
||||
length = self._read_tag_int()
|
||||
length = self._uint.unpack(self._file.read(4))[0]
|
||||
|
||||
read_method = self._read_tagmap[tagid]
|
||||
l = []
|
||||
|
||||
@@ -19,6 +19,7 @@ import progressbar
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import rcon
|
||||
|
||||
class Observer(object):
|
||||
"""Base class that defines the observer interface.
|
||||
@@ -392,3 +393,43 @@ class ServerAnnounceObserver(Observer):
|
||||
self.target_handle.write('say %s\n' % output)
|
||||
self.target_handle.flush()
|
||||
|
||||
|
||||
# Fair amount of code duplication incoming
|
||||
# Perhaps both ServerAnnounceObserver and RConObserver
|
||||
# could share the percentage interval code.
|
||||
|
||||
class RConObserver(Observer):
|
||||
"""Send the output to a Minecraft server via rcon"""
|
||||
|
||||
def __init__(self, target, password, port=25575, pct_interval=10):
|
||||
self.pct_interval = pct_interval
|
||||
self.conn = rcon.RConConnection(target, port)
|
||||
self.conn.login(password)
|
||||
self.last_update = 0
|
||||
super(RConObserver, self).__init__()
|
||||
|
||||
def start(self, max_value):
|
||||
self._send_output("Starting render of %d total tiles" % max_value)
|
||||
super(RConObserver, self).start(max_value)
|
||||
|
||||
def finish(self):
|
||||
self._send_output("Render complete!")
|
||||
super(RConObserver, self).finish()
|
||||
self.conn.close()
|
||||
|
||||
def update(self, current_value):
|
||||
super(RConObserver, self).update(current_value)
|
||||
if self._need_update():
|
||||
self._send_output('Rendered %d of %d tiles, %d%% complete' %
|
||||
(self.get_current_value(), self.get_max_value(),
|
||||
self.get_percentage()))
|
||||
self.last_update = current_value
|
||||
|
||||
def _need_update(self):
|
||||
return self.get_percentage() - \
|
||||
(self.last_update * 100.0 / self.get_max_value()) >= self.pct_interval
|
||||
|
||||
def _send_output(self, output):
|
||||
self.conn.command("say", output)
|
||||
|
||||
|
||||
|
||||
@@ -121,6 +121,42 @@ class optipng(Optimizer, PNGOptimizer):
|
||||
def is_crusher(self):
|
||||
return True
|
||||
|
||||
class jpegoptim(Optimizer, JPEGOptimizer):
|
||||
binaryname = "jpegoptim"
|
||||
crusher = True
|
||||
quality = None
|
||||
target_size = None
|
||||
|
||||
def __init__(self, quality = None, target_size = None):
|
||||
if quality is not None:
|
||||
if quality < 0 or quality > 100:
|
||||
raise Exception("Invalid target quality %d for jpegoptim" % quality)
|
||||
self.quality = quality
|
||||
|
||||
if target_size is not None:
|
||||
self.target_size = target_size
|
||||
|
||||
def optimize(self, img):
|
||||
args = [self.binaryname, "-q", "-p"]
|
||||
if self.quality is not None:
|
||||
args.append("-m" + str(self.quality))
|
||||
|
||||
if self.target_size is not None:
|
||||
args.append("-S" + str(self.target_size))
|
||||
|
||||
args.append(img)
|
||||
|
||||
Optimizer.fire_and_forget(self, args)
|
||||
|
||||
def is_crusher(self):
|
||||
# Technically, optimisation is lossless if input image quality
|
||||
# is below target quality, but this is irrelevant in this case
|
||||
if (self.quality is not None) or (self.target_size is not None):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def optimize_image(imgpath, imgformat, optimizers):
|
||||
for opt in optimizers:
|
||||
|
||||
95
overviewer_core/rcon.py
Normal file
95
overviewer_core/rcon.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# This file is part of the Minecraft Overviewer.
|
||||
#
|
||||
# Minecraft Overviewer is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or (at
|
||||
# your option) any later version.
|
||||
#
|
||||
# Minecraft Overviewer is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import socket
|
||||
#from enum import Enum
|
||||
import struct
|
||||
import select
|
||||
|
||||
class RConException(Exception):
|
||||
def __init__(self, request_id, reason):
|
||||
self.request_id = request_id
|
||||
self.reason = reason
|
||||
|
||||
def __str__(self):
|
||||
return ("Failed RCon request with request ID %d, reason %s" %
|
||||
(self.request_id, self.reason))
|
||||
|
||||
# In D, enums are just that, enums. They're a group of named constants,
|
||||
# sometimes with a tag, sometimes anonymous.
|
||||
# In Python, Enums use the same syntax as class objects that derive from
|
||||
# the "Enum" base class, even though they are not normal python classes
|
||||
# and work as singletons anyway, but instead of using a different syntax,
|
||||
# Python instead decided to have a chapter in their docs about how Enums
|
||||
# are different from regular classes while looking exactly the same.
|
||||
# You can look at said document of failure right here:
|
||||
# https://docs.python.org/3/library/enum.html#how-are-enums-different
|
||||
#
|
||||
# "D has too much shit going on for me" -- agrif, 2014
|
||||
#
|
||||
# Fortunately, we're not allowed to use Enums in Python 2.
|
||||
|
||||
#class RConType(Enum):
|
||||
# command = 2
|
||||
# login = 3
|
||||
|
||||
|
||||
class RConConnection():
|
||||
rid = 0
|
||||
|
||||
def __init__(self, target, port):
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.connect((target, port))
|
||||
|
||||
def send(self, t, payload):
|
||||
self.rid = self.rid + 1
|
||||
header = struct.pack("<iii",
|
||||
len(payload) + 4 + 4 + 2, # rid, type and padding
|
||||
self.rid,
|
||||
t)
|
||||
data = header + payload + '\x00\x00'
|
||||
self.sock.send(data)
|
||||
|
||||
|
||||
toread = select.select([self.sock], [], [], 30)
|
||||
|
||||
if not toread:
|
||||
raise RConException(self.rid, "Request timed out.")
|
||||
|
||||
res_len, res_id, res_type = struct.unpack("<iii", self.sock.recv(12))
|
||||
res_data = self.sock.recv(res_len - 4 - 4)
|
||||
res_data = res_data[:-2]
|
||||
|
||||
if res_id == -1:
|
||||
if t == 3:
|
||||
raise RConException(self.rid, "Login failed.")
|
||||
else:
|
||||
raise RConException(self.rid, "Request failed due to invalid login.")
|
||||
elif res_id != self.rid:
|
||||
raise RConException(self.rid,
|
||||
"Received unexpected response number: %d" % res_id)
|
||||
|
||||
return res_data
|
||||
|
||||
|
||||
|
||||
def login(self, password):
|
||||
self.send(3, password)
|
||||
|
||||
def command(self, com, args):
|
||||
self.send(2, com + " " + args)
|
||||
|
||||
def close(self):
|
||||
self.sock.close()
|
||||
@@ -205,6 +205,19 @@ class SpawnOverlay(Overlay):
|
||||
class SlimeOverlay(Overlay):
|
||||
name = "overlay-slime"
|
||||
|
||||
|
||||
class StructureOverlay(Overlay):
|
||||
name = "overlay-structure"
|
||||
options = {
|
||||
'structures': ('a list of ((((relx, rely, relz), blockid), ...), (r, g, b, a)) tuples for coloring minerals',
|
||||
[(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)),
|
||||
(((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255)),
|
||||
(((0, 0, 0, 28), (0, -1, 0, 4)), (255, 255, 0, 255)),
|
||||
(((0, 0, 0, 157), (0, -1, 0, 4)), (255, 100, 0, 255)),
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
class MineralOverlay(Overlay):
|
||||
name = "overlay-mineral"
|
||||
options = {
|
||||
|
||||
242
overviewer_core/src/primitives/overlay-structure.c
Normal file
242
overviewer_core/src/primitives/overlay-structure.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* This file is part of the Minecraft Overviewer.
|
||||
*
|
||||
* Minecraft Overviewer is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Minecraft Overviewer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "overlay.h"
|
||||
|
||||
typedef enum { false, true } bool;
|
||||
|
||||
typedef struct {
|
||||
/* inherits from overlay */
|
||||
RenderPrimitiveOverlay parent;
|
||||
void *structures;
|
||||
int numcolors;
|
||||
} RenderPrimitiveStructure;
|
||||
|
||||
struct Condition{
|
||||
int relx, rely, relz;
|
||||
unsigned char block;
|
||||
};
|
||||
|
||||
struct Color {
|
||||
int numconds;
|
||||
struct Condition *conditions;
|
||||
unsigned char r, g, b, a;
|
||||
};
|
||||
|
||||
static void get_color(void *data,
|
||||
RenderState *state,
|
||||
unsigned char *r,
|
||||
unsigned char *g,
|
||||
unsigned char *b,
|
||||
unsigned char *a) {
|
||||
/**
|
||||
* Calculate the color at the current position and store the values to r,g,b,a.
|
||||
**/
|
||||
RenderPrimitiveStructure *self = (RenderPrimitiveStructure *)data;
|
||||
int x = state->x, z = state->z, y_max, y, col, cond;
|
||||
struct Color *structures = (struct Color *)(self->structures);
|
||||
struct Condition * c = NULL;
|
||||
bool all = true;
|
||||
y_max = state->y + 1;
|
||||
|
||||
/**
|
||||
* Check for every color in every y level if all its Conditions are met.
|
||||
* If all conditions are met for one y level set r,b,g,a accordingly.
|
||||
**/
|
||||
// iterate over all the colors
|
||||
for ( col = 0; col < self->numcolors; col++) {
|
||||
// iterate over all y levels
|
||||
for (y = state->chunky * -16; y <= y_max; y++) {
|
||||
// iterate over all the conditions
|
||||
for (cond = 0; cond < structures[col].numconds; cond++) {
|
||||
all = true;
|
||||
c = (struct Condition *)&structures[col].conditions[cond];
|
||||
// check if the condition does apply and break from the conditions loop if not.
|
||||
if(!(c->block == get_data(state, BLOCKS, x+c->relx, y+c->rely, z+c->relz))) {
|
||||
all = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all){
|
||||
// set the color
|
||||
*r = structures[col].r;
|
||||
*g = structures[col].g;
|
||||
*b = structures[col].b;
|
||||
*a = structures[col].a;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int overlay_structure_start(void *data, RenderState *state, PyObject *support) {
|
||||
/**
|
||||
* Initializing the search for structures by parsing the arguments and storing them into
|
||||
* appropriate structures. If no arguments are passed create and use default values.
|
||||
**/
|
||||
PyObject *opt;
|
||||
RenderPrimitiveStructure* self;
|
||||
|
||||
/* first, chain up */
|
||||
int ret = primitive_overlay.start(data, state, support);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* now do custom initializations */
|
||||
self = (RenderPrimitiveStructure *)data;
|
||||
|
||||
// opt is a borrowed reference. do not deref
|
||||
// store the structures python object into opt.
|
||||
if (!render_mode_parse_option(support, "structures", "O", &(opt)))
|
||||
return 1;
|
||||
|
||||
/**
|
||||
* Check if a sane option was passed.
|
||||
**/
|
||||
if (opt && opt != Py_None) {
|
||||
struct Color *structures = NULL;
|
||||
struct Condition *cond = NULL;
|
||||
Py_ssize_t structures_size = 0, i, cond_size = 0, n = 0;
|
||||
bool cont = true;
|
||||
|
||||
opt = PySequence_Fast(opt, "expected a sequence");
|
||||
if (!opt) {
|
||||
PyErr_SetString(PyExc_TypeError, "'structures' must be a a sequence");
|
||||
return 1;
|
||||
}
|
||||
|
||||
structures_size = PySequence_Fast_GET_SIZE(opt);
|
||||
// Getting space on the heap and do not forget to set self->numcolors.
|
||||
structures = self->structures = calloc(structures_size, sizeof(struct Color));
|
||||
self->numcolors = structures_size;
|
||||
if (structures == NULL) {
|
||||
PyErr_SetString(PyExc_MemoryError, "failed to allocate memory");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse the definitions of conditions and colors.
|
||||
**/
|
||||
if (cont) {
|
||||
for (i = 0; i < structures_size; i++) {
|
||||
PyObject *structure = PyList_GET_ITEM(opt, i);
|
||||
// condspy holding the conditions tuple of variable length (python object)
|
||||
PyObject *condspy;
|
||||
// colorpy holding the 4 tuple with r g b a values of the color
|
||||
PyObject *colorpy;
|
||||
|
||||
// getting the condspy and colorpy out of the structures.
|
||||
if (!PyArg_ParseTuple(structure, "OO", &condspy, &colorpy)) {
|
||||
// Exception set automatically
|
||||
free(structures);
|
||||
self->structures = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parse colorpy into a c-struct.
|
||||
if (!PyArg_ParseTuple( colorpy, "bbbb",
|
||||
&structures[i].r,
|
||||
&structures[i].g,
|
||||
&structures[i].b,
|
||||
&structures[i].a)) {
|
||||
free(structures);
|
||||
self->structures = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Convert condspy to a fast sequence
|
||||
condspy = PySequence_Fast(condspy, "Failed to parse conditions");
|
||||
if(condspy == NULL) {
|
||||
free(structures);
|
||||
self->structures = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get the number of conditions.
|
||||
structures[i].numconds = PySequence_Fast_GET_SIZE(condspy);
|
||||
// reserve enough memory for the conditions.
|
||||
cond = calloc(structures[i].numconds, sizeof(struct Condition));
|
||||
structures[i].conditions = cond;
|
||||
|
||||
if (structures[i].conditions == NULL) {
|
||||
PyErr_SetString(PyExc_MemoryError, "failed to allocate memory");
|
||||
free(structures);
|
||||
self->structures = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// iterate over all the conditions and read them.
|
||||
for (n = 0; n < structures[i].numconds; n++) {
|
||||
PyObject *ccond = PySequence_Fast_GET_ITEM(condspy, n);
|
||||
if(!PyArg_ParseTuple( ccond, "iiib",
|
||||
&cond[n].relx,
|
||||
&cond[n].rely,
|
||||
&cond[n].relz,
|
||||
&cond[n].block)){
|
||||
int x = 0;
|
||||
for(x = 0; x < structures_size; x++){
|
||||
free(structures[x].conditions);
|
||||
}
|
||||
free(structures);
|
||||
self->structures = NULL;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* setup custom color */
|
||||
self->parent.get_color = get_color;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void overlay_structure_finish(void *data, RenderState *state) {
|
||||
/* first free all *our* stuff */
|
||||
RenderPrimitiveStructure* self = (RenderPrimitiveStructure *)data;
|
||||
int i = 0;
|
||||
|
||||
if(self->structures) {
|
||||
// freeing the nested structure
|
||||
struct Color * m = self->structures;
|
||||
for(i = 0; i < self->numcolors; i++){
|
||||
if(m[i].conditions)
|
||||
free(m[i].conditions);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->structures) {
|
||||
free(self->structures);
|
||||
self->structures = NULL;
|
||||
}
|
||||
|
||||
/* now, chain up */
|
||||
primitive_overlay.finish(data, state);
|
||||
}
|
||||
|
||||
RenderPrimitiveInterface primitive_overlay_structure = {
|
||||
"overlay-structure",
|
||||
sizeof(RenderPrimitiveStructure),
|
||||
overlay_structure_start,
|
||||
overlay_structure_finish,
|
||||
NULL,
|
||||
NULL,
|
||||
overlay_draw,
|
||||
};
|
||||
|
||||
@@ -3437,7 +3437,7 @@ def huge_mushroom(self, blockid, data):
|
||||
img = self.build_full_block(cap, None, None, cap, porous)
|
||||
|
||||
if data == 2: # east side
|
||||
img = self.build_full_block(cap, None, None, porous, porous)
|
||||
img = self.build_full_block(cap, None, None, porous, cap)
|
||||
|
||||
if data == 3: # south-east corner
|
||||
img = self.build_full_block(cap, None, None, porous, cap)
|
||||
|
||||
@@ -689,6 +689,25 @@ class TileSet(object):
|
||||
# 2 is now 2/1
|
||||
# 3 is now 3/0
|
||||
# then all that needs to be done is to regenerate the new top level
|
||||
|
||||
def rollback_mkdir(dnum):
|
||||
p = getpath("new" + str(dnum))
|
||||
if os.path.exists(p):
|
||||
os.rmdir(p)
|
||||
|
||||
def rollback_filerename(dnum):
|
||||
newnum = (3,2,1,0)[dnum]
|
||||
qimg = getpath("new%d/%d.%s" % (dnum, newnum, self.imgextension))
|
||||
qdir = getpath("new%d/%d" % (dnum, newnum))
|
||||
|
||||
if os.path.exists(qimg):
|
||||
os.rename(qimg, getpath("%d.%s" % (dnum, self.imgextension)))
|
||||
if os.path.exists(qdir):
|
||||
os.rename(qdir, getpath(str(dnum)))
|
||||
|
||||
def rollback_dirrename(dnum):
|
||||
os.rename(getpath(str(dnum)), getpath("new" + str(dnum)))
|
||||
|
||||
for dirnum in range(4):
|
||||
newnum = (3,2,1,0)[dirnum]
|
||||
|
||||
@@ -698,12 +717,31 @@ class TileSet(object):
|
||||
files = [str(dirnum)+"."+self.imgextension, str(dirnum)]
|
||||
newfiles = [str(newnum)+"."+self.imgextension, str(newnum)]
|
||||
|
||||
try:
|
||||
try:
|
||||
os.mkdir(newdirpath)
|
||||
try:
|
||||
for f, newf in zip(files, newfiles):
|
||||
p = getpath(f)
|
||||
if os.path.exists(p):
|
||||
os.rename(p, getpath(newdir, newf))
|
||||
except:
|
||||
rollback_filerename(dirnum)
|
||||
raise
|
||||
except:
|
||||
rollback_mkdir(dirnum)
|
||||
raise
|
||||
os.rename(newdirpath, getpath(str(dirnum)))
|
||||
except:
|
||||
logging.warning("Overviewer was interrupted during tile "
|
||||
"re-arrangement.")
|
||||
logging.warning("Rolling back changes...")
|
||||
# Moonwalk the fuck out of here
|
||||
for lastdir in range(dirnum - 1, -1, -1):
|
||||
rollback_dirrename(lastdir)
|
||||
rollback_filerename(lastdir)
|
||||
rollback_mkdir(lastdir)
|
||||
raise
|
||||
|
||||
def _decrease_depth(self):
|
||||
"""If the map size decreases, or perhaps the user has a depth override
|
||||
|
||||
@@ -105,6 +105,10 @@ class World(object):
|
||||
|
||||
# Hard-code this to only work with format version 19133, "Anvil"
|
||||
if not ('version' in data and data['version'] == 19133):
|
||||
if 'version' in data and data['version'] == 0:
|
||||
logging.debug("Note: Allowing a version of zero in level.dat!")
|
||||
## XXX temporary fix for #1194
|
||||
else:
|
||||
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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user