diff --git a/.travis.yml b/.travis.yml index 9a5aebe..b50f7dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ python: - "3.5" - "3.6" - "3.7" - - "3.8-dev" + - "3.8" env: - MC_VERSION=1.15 PIL_INCLUDE_DIR=. before_install: diff --git a/overviewer_core/aux_files/genPOI.py b/overviewer_core/aux_files/genPOI.py index 655872f..4c84d10 100755 --- a/overviewer_core/aux_files/genPOI.py +++ b/overviewer_core/aux_files/genPOI.py @@ -418,18 +418,36 @@ def create_marker_from_filter_result(poi, result): else: # ...otherwise default to display text. d['hovertext'] = result['text'] - if 'polyline' in result and hasattr(result['polyline'], '__iter__'): - d['polyline'] = [] - for point in result['polyline']: - # point.copy() would work, but this validates better - d['polyline'].append(dict(x=point['x'], y=point['y'], z=point['z'])) - if isinstance(result['color'], str): - d['strokeColor'] = result['color'] + if "icon" in result: + d["icon"] = result['icon'] + if "createInfoWindow" in result: + d["createInfoWindow"] = result['createInfoWindow'] - if "icon" in result: - d["icon"] = result['icon'] - if "createInfoWindow" in result: - d["createInfoWindow"] = result['createInfoWindow'] + # Polylines and polygons + if ('polyline' in result and hasattr(result['polyline'], '__iter__')) or \ + 'polygon' in result and hasattr(result['polygon'], '__iter__'): + # If the points form a line or closed shape + d['isLine'] = 'polyline' in result + # Collect points + d['points'] = [] + for point in (result['polyline'] if d['isLine'] else result['polygon']): + d['points'].append(dict(x=point['x'], y=point['y'], z=point['z'])) + + # Options and default values + if 'color' in result: + d['strokeColor'] = result['color'] + else: + d['strokeColor'] = 'red' + + if 'fill' in result: + d['fill'] = result['fill'] + else: + d['fill'] = not d['isLine'] # fill polygons by default + + if 'weight' in result: + d['strokeWeight'] = result['weight'] + else: + d['strokeWeight'] = 2 else: raise ValueError("Got an %s as result for POI with id %s" % (type(result).__name__, poi['id'])) diff --git a/overviewer_core/data/js_src/overviewer.js b/overviewer_core/data/js_src/overviewer.js index 559376b..cf9a65f 100644 --- a/overviewer_core/data/js_src/overviewer.js +++ b/overviewer_core/data/js_src/overviewer.js @@ -39,7 +39,7 @@ overviewer.collections = { /** * When switching regionsets, where should we zoom to? - * Defaults to spawn. Stored as map of world names to [latlng, zoom] + * Defaults to spawn. Stored as map of maps: world names to layer names to [latlng, zoom] */ 'centers': {}, diff --git a/overviewer_core/data/js_src/util.js b/overviewer_core/data/js_src/util.js index cb0e6b2..507bce9 100644 --- a/overviewer_core/data/js_src/util.js +++ b/overviewer_core/data/js_src/util.js @@ -142,8 +142,11 @@ overviewer.util = { // save current view for the current_world - overviewer.collections.centers[overviewer.current_world][0] = overviewer.map.getCenter(); - overviewer.collections.centers[overviewer.current_world][1] = overviewer.map.getZoom(); + let current_center = [overviewer.map.getCenter(), overviewer.map.getZoom()]; + let current_layer = overviewer.current_layer[overviewer.current_world] || + Object.values(overviewer.collections.mapTypes[overviewer.current_world])[0]; + let layer_name = current_layer.tileSetConfig.path; + overviewer.collections.centers[overviewer.current_world][layer_name] = current_center; overviewer.layerCtrl.remove(); @@ -190,17 +193,15 @@ overviewer.util = { } } - var center = overviewer.collections.centers[selected_world]; + let selected_layer_name = overviewer.collections.mapTypes[selected_world] && overviewer.current_layer[selected_world] ? + overviewer.current_layer[selected_world].tileSetConfig.path : + Object.keys(overviewer.collections.mapTypes[selected_world])[0]; + + let center = overviewer.collections.centers[selected_world][selected_layer_name]; overviewer.map.setView(center[0], center[1]); overviewer.current_world = selected_world; - - if (overviewer.collections.mapTypes[selected_world] && overviewer.current_layer[selected_world]) { - overviewer.map.addLayer(overviewer.collections.mapTypes[selected_world][overviewer.current_layer[selected_world].tileSetConfig.path]); - } else { - var tset_name = Object.keys(overviewer.collections.mapTypes[selected_world])[0] - overviewer.map.addLayer(overviewer.collections.mapTypes[selected_world][tset_name]); - } + overviewer.map.addLayer(overviewer.collections.mapTypes[selected_world][selected_layer_name]); }, onAdd: function() { console.log("onAdd mycontrol"); @@ -378,27 +379,48 @@ overviewer.util = { console.log("this tileset has markers:", obj); obj.marker_groups = {}; + // For every group of markers for (var mkidx = 0; mkidx < markers[obj.path].length; mkidx++) { + // Create a Leaflet layer group var marker_group = new L.layerGroup(); var marker_entry = markers[obj.path][mkidx]; L.Util.setOptions(marker_group, {default_checked: marker_entry.checked}); var icon = L.divIcon({html: ``}); + // For every marker in group for (var dbidx = 0; dbidx < markersDB[marker_entry.groupName].raw.length; dbidx++) { - var db = markersDB[marker_entry.groupName].raw[dbidx]; - var latlng = overviewer.util.fromWorldToLatLng(db.x, db.y, db.z, obj); - var m_icon; - if (db.icon != undefined) { - m_icon = L.divIcon({html: ``}); + let db = markersDB[marker_entry.groupName].raw[dbidx]; + var layerObj = undefined; + + // Shape or marker? + if ('points' in db) { + // Convert all coords + plLatLng = db['points'].map(function(p) { + return overviewer.util.fromWorldToLatLng(p.x, p.y, p.z, obj); + }); + options = { + color: db['strokeColor'], + weight: db['strokeWeight'], + fill: db['fill'] + }; + layerObj = db['isLine'] ? L.polyline(plLatLng, options) : L.polygon(plLatLng, options); + // TODO: add other config options (fill color, fill opacity) } else { - m_icon = icon; + // Convert coords + let latlng = overviewer.util.fromWorldToLatLng(db.x, db.y, db.z, obj); + // Set icon and use default icon if not specified + let m_icon = L.divIcon({html: ``}); + // Create marker + layerObj = new L.marker(latlng, {icon: m_icon, title: db.hovertext}); } - let new_marker = new L.marker(latlng, {icon: m_icon, title: db.hovertext}); + // Add popup to marker if (marker_entry.createInfoWindow) { - new_marker.bindPopup(db.text); + layerObj.bindPopup(db.text); } - marker_group.addLayer(new_marker); + // Add the polyline or marker to the layer + marker_group.addLayer(layerObj); } + // Save marker group obj.marker_groups[marker_entry.displayName] = marker_group; } } @@ -406,12 +428,15 @@ overviewer.util = { myLayer["tileSetConfig"] = obj; + if (!overviewer.collections.centers[obj.world]) { + overviewer.collections.centers[obj.world] = {}; + } if (typeof(obj.center) == "object") { var latlng = overviewer.util.fromWorldToLatLng(obj.center[0], obj.center[1], obj.center[2], obj); - overviewer.collections.centers[obj.world] = [ latlng, obj.defaultZoom ]; + overviewer.collections.centers[obj.world][obj.path] = [ latlng, obj.defaultZoom ]; } else { - overviewer.collections.centers[obj.world] = [ [0, 0], obj.defaultZoom ]; + overviewer.collections.centers[obj.world][obj.path] = [ [0, 0], obj.defaultZoom ]; } }); @@ -423,7 +448,8 @@ overviewer.util = { .addTo(overviewer.map); overviewer.current_world = overviewerConfig.worlds[0]; - let center = overviewer.collections.centers[overviewer.current_world]; + let default_layer_name = Object.keys(overviewer.collections.mapTypes[overviewer.current_world])[0]; + let center = overviewer.collections.centers[overviewer.current_world][default_layer_name]; overviewer.map.setView(center[0], center[1]); if (!overviewer.util.initHash()) { diff --git a/overviewer_core/data/web_assets/regions.js b/overviewer_core/data/web_assets/regions.js index cef6ff0..21da8f2 100644 --- a/overviewer_core/data/web_assets/regions.js +++ b/overviewer_core/data/web_assets/regions.js @@ -7,11 +7,10 @@ var world = "top"; markersDB[groupName] = { "raw": [ { - "fillColor": "#00FF00", - "fillOpacity": 0.2, "strokeColor": "#FF0000", - "strokeOpacity": 1, - "polygon" : [ + "strokeWeight": 2, + "fill": true, + "polyline" : [ {"x": 347, "y": 67, "z": 95}, {"x": 347, "y": 77, "z": 95}, {"x": 347, "y": 77, "z": 105}, @@ -19,7 +18,7 @@ markersDB[groupName] = { {"x": 347, "y": 67, "z": 105} ]} ], - "name": "Regions", + "name": groupName, "created": false } @@ -31,4 +30,4 @@ markers[world].push( "displayName": displayName, "checked": true }); -*/ \ No newline at end of file +*/ diff --git a/overviewer_core/observer.py b/overviewer_core/observer.py index 9db9baf..f467a8d 100644 --- a/overviewer_core/observer.py +++ b/overviewer_core/observer.py @@ -148,12 +148,12 @@ class LoggingObserver(Observer): def _need_update(self): cur_val = self.get_current_value() - if cur_val < 100: - return cur_val - self.last_update > 10 - elif cur_val < 500: - return cur_val - self.last_update > 50 + if cur_val <= 100: + return cur_val - self.last_update >= 10 + elif cur_val <= 500: + return cur_val - self.last_update >= 50 else: - return cur_val - self.last_update > 100 + return cur_val - self.last_update >= 100 default_widgets = [ diff --git a/overviewer_core/src/block_class.c b/overviewer_core/src/block_class.c index c07f47d..6112260 100644 --- a/overviewer_core/src/block_class.c +++ b/overviewer_core/src/block_class.c @@ -164,7 +164,6 @@ const mc_block_t block_class_ancil[] = { block_flowing_water, block_water, block_glass, - block_chest, block_redstone_wire, block_ice, block_fence, @@ -190,7 +189,6 @@ const mc_block_t block_class_ancil[] = { block_double_plant, block_stained_glass_pane, block_stained_glass, - block_trapped_chest, block_spruce_fence, block_birch_fence, block_jungle_fence, diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index f27e400..3635090 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -316,43 +316,6 @@ generate_pseudo_data(RenderState* state, uint16_t ancilData) { } return final_data; - } else if (block_class_is_subset(state->block, (mc_block_t[]){block_chest, block_trapped_chest}, 2)) { - /* Orientation is given by ancilData, pseudo data needed to - * choose from single or double chest and the correct half of - * the chest. */ - - /* Add two bits to ancilData to store single or double chest - * and which half of the chest it is: bit 0x10 = second half - * bit 0x8 = first half */ - - uint8_t chest_data = 0, final_data = 0; - - /* search for adjacent chests of the same type */ - chest_data = check_adjacent_blocks(state, x, y, z, state->block); - - if (chest_data == 1) { /* another chest in the upper-left */ - final_data = final_data | 0x10 | ancilData; - - } else if (chest_data == 2) { /* in the bottom-left */ - final_data = final_data | 0x8 | ancilData; - - } else if (chest_data == 4) { /*in the bottom-right */ - final_data = final_data | 0x8 | ancilData; - - } else if (chest_data == 8) { /*in the upper-right */ - final_data = final_data | 0x10 | ancilData; - - } else if (chest_data == 0) { - /* Single chest, determine the orientation */ - final_data = ancilData; - - } else { - /* more than one adjacent chests! That shouldn't be - * possible! render as normal chest */ - return 0; - } - return final_data; - } else if (block_class_is_subset(state->block, (mc_block_t[]){block_iron_bars, block_glass_pane, block_stained_glass_pane}, 3)) { /* iron bars and glass panes: * they seem to stick to almost everything but air, diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 3debcf9..568ffe0 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -31,7 +31,7 @@ // increment this value if you've made a change to the c extension // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 80 +#define OVERVIEWER_EXTENSION_VERSION 82 #include #include diff --git a/overviewer_core/src/primitives/cave.c b/overviewer_core/src/primitives/cave.c index 49313a2..66d976e 100644 --- a/overviewer_core/src/primitives/cave.c +++ b/overviewer_core/src/primitives/cave.c @@ -63,6 +63,8 @@ static bool cave_hidden(void* data, RenderState* state, int32_t x, int32_t y, int32_t z) { RenderPrimitiveCave* self; int32_t dy = 0; + uint16_t blockID; + uint32_t blockUpID; self = (RenderPrimitiveCave*)data; /* check if the block is touching skylight */ @@ -78,15 +80,17 @@ cave_hidden(void* data, RenderState* state, int32_t x, int32_t y, int32_t z) { * at this point of the code the block has no skylight * but a deep sea can be completely dark */ - if ((getArrayShort3D(state->blocks, x, y, z) == 9) || - (get_data(state, BLOCKS, x, y + 1, z) == 9)) { + blockID = getArrayShort3D(state->blocks, x, y, z); + blockUpID = get_data(state, BLOCKS, x, y + 1, z); + if (blockID == 9 || blockID == 8 || blockUpID == 9 || blockUpID == 8) { for (dy = y + 1; dy < (SECTIONS_PER_CHUNK - state->chunky) * 16; dy++) { /* go up and check for skylight */ if (get_data(state, SKYLIGHT, x, dy, z) != 0) { return true; } - if (get_data(state, BLOCKS, x, dy, z) != 9) { + blockUpID = get_data(state, BLOCKS, x, dy, z); + if (blockUpID != 8 && blockUpID != 9) { /* we are out of the water! and there's no skylight * , i.e. is a cave lake or something similar */ break; diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 02d3f71..1c34ee6 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -2341,7 +2341,8 @@ def chests(self, blockid, data): alpha_over(side_r, side_r_top, (1, 1)) alpha_over(side_r, side_r_bottom, (1, 5)) - if data & 24 == 8: # double chest, first half + # double chest, left half + if ((data & 24 == 8 and data & 7 in [3, 5]) or (data & 24 == 16 and data & 7 in [2, 4])): top = top.crop((0, 0, 16, 16)) top.load() front = front.crop((0, 0, 16, 16)) @@ -2350,7 +2351,8 @@ def chests(self, blockid, data): back.load() #~ side = side_l - elif data & 24 == 16: # double, second half + # double chest, right half + elif ((data & 24 == 16 and data & 7 in [3, 5]) or (data & 24 == 8 and data & 7 in [2, 4])): top = top.crop((16, 0, 32, 16)) top.load() front = front.crop((16, 0, 32, 16)) @@ -4521,7 +4523,7 @@ def beacon(self, blockid, data): # cobblestone and mossy cobblestone walls, chorus plants, mossy stone brick walls # one additional bit of data value added for mossy and cobblestone -@material(blockid=[199, *range(21000,21013+1)], data=list(range(32)), transparent=True, nospawn=True) +@material(blockid=[199]+list(range(21000,21013+1)), data=list(range(32)), transparent=True, nospawn=True) def cobblestone_wall(self, blockid, data): walls_id_to_tex = { 199: "assets/minecraft/textures/block/chorus_plant.png", # chorus plants diff --git a/overviewer_core/world.py b/overviewer_core/world.py index faa172d..db41f2f 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -318,7 +318,7 @@ class RegionSet(object): 'minecraft:sapling': (6, 0), 'minecraft:bedrock': (7, 0), 'minecraft:water': (8, 0), - 'minecraft:lava': (10, 0), + 'minecraft:lava': (11, 0), 'minecraft:sand': (12, 0), 'minecraft:red_sand': (12, 1), 'minecraft:gravel': (13, 0), @@ -1022,6 +1022,10 @@ class RegionSet(object): elif key in ['minecraft:ladder', 'minecraft:chest', 'minecraft:ender_chest', 'minecraft:trapped_chest', 'minecraft:furnace']: facing = palette_entry['Properties']['facing'] data = {'north': 2, 'south': 3, 'west': 4, 'east': 5}[facing] + if key in ['minecraft:chest', 'minecraft:trapped_chest']: + # type property should exist, but default to 'single' just in case + chest_type = palette_entry['Properties'].get('type', 'single') + data |= {'left': 0x8, 'right': 0x10, 'single': 0x0}[chest_type] elif key in ['minecraft:beehive', 'minecraft:bee_nest']: facing = palette_entry['Properties']['facing'] honey_level = int(palette_entry['Properties']['honey_level']) @@ -1547,9 +1551,24 @@ class RegionSetWrapper(object): itertools.groupby, which needs sorted keys, and Python 2 somehow just sorted objects like ???????? how????? why????? """ - if isinstance(other, RegionSetWrapper): - other = other._r - return self._r.regiondir < other.regiondir + return self.regiondir < other.regiondir + + @property + def regiondir(self): + """ + RegionSetWrapper are wrappers around a RegionSet and thus should have all variables the RegionSet has. + + Reason for addition: Issue #1706 + The __lt__ check in RegionSet did not check if it is a RegionSetWrapper Instance + """ + return self._r.regiondir + + @regiondir.setter + def regiondir(self, value): + """ + For completeness adding the setter to the property + """ + self._r.regiondir = value def get_type(self): return self._r.get_type()