0

Merge branch 'master' into my_genpoi

Conflicts:
	overviewer_core/aux_files/genPOI.py
This commit is contained in:
MasterofJOKers
2015-02-08 14:19:21 +01:00
15 changed files with 664 additions and 42 deletions

View File

@@ -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")

View File

@@ -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;

View File

@@ -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
});
*/

View File

@@ -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 = []

View File

@@ -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)

View File

@@ -120,6 +120,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):

95
overviewer_core/rcon.py Normal file
View 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()

View File

@@ -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 = {

View 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,
};

View File

@@ -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)

View File

@@ -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)]
os.mkdir(newdirpath)
for f, newf in zip(files, newfiles):
p = getpath(f)
if os.path.exists(p):
os.rename(p, getpath(newdir, newf))
os.rename(newdirpath, getpath(str(dirnum)))
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

View File

@@ -105,8 +105,12 @@ class World(object):
# 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)
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)
# This isn't much data, around 15 keys and values for vanilla worlds.
self.leveldat = data