0
This repository has been archived on 2025-04-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Minecraft-Overviewer/overviewer_core/aux_files/genPOI.py
Aaron Griffith df732bfa25 Revert "Merge remote-tracking branch 'exhuma/master', PR #963"
This reverts commit e26727943f, reversing
changes made to 7bbb077704.
2013-12-02 07:40:00 -05:00

383 lines
16 KiB
Python
Executable File

#!/usr/bin/python2
'''
genPOI.py
Scans regionsets for TileEntities and Entities, filters them, and writes out
POI/marker info.
A markerSet is list of POIs to display on a tileset. It has a display name,
and a group name.
markersDB.js holds a list of POIs in each group
markers.js holds a list of which markerSets are attached to each tileSet
'''
import os
import logging
import json
import sys
import re
import Queue
import multiprocessing
from multiprocessing import Process
from multiprocessing import Pool
from functools import partial
from optparse import OptionParser
from overviewer_core import logger
from overviewer_core import nbt
from overviewer_core import configParser, world
def replaceBads(s):
"Replaces bad characters with good characters!"
bads = [" ", "(", ")"]
x=s
for bad in bads:
x = x.replace(bad,"_")
return x
def parseBucketChunks(bucket, rset):
pid = multiprocessing.current_process().pid
pois = dict(TileEntities=[], Entities=[]);
i = 0
cnt = 0
l = len(bucket)
for b in bucket:
data = rset.get_chunk(b[0],b[1])
pois['TileEntities'] += data['TileEntities']
pois['Entities'] += data['Entities']
# Perhaps only on verbose ?
i = i + 1
if i == 250:
i = 0
cnt = 250 + cnt
logging.info("Found %d entities and %d tile entities in thread %d so far at %d chunks", len(pois['Entities']), len(pois['TileEntities']), pid, cnt);
return pois
def handleEntities(rset, outputdir, render, rname, config):
# if we're already handled the POIs for this region regionset, do nothing
if hasattr(rset, "_pois"):
return
logging.info("Looking for entities in %r", rset)
filters = render['markers']
rset._pois = dict(TileEntities=[], Entities=[])
numbuckets = config['processes'];
if numbuckets < 0:
numbuckets = multiprocessing.cpu_count()
if numbuckets == 1:
for (x,z,mtime) in rset.iterate_chunks():
data = rset.get_chunk(x,z)
rset._pois['TileEntities'] += data['TileEntities']
rset._pois['Entities'] += data['Entities']
else:
buckets = [[] for i in range(numbuckets)];
for (x,z,mtime) in rset.iterate_chunks():
i = x / 32 + z / 32
i = i % numbuckets
buckets[i].append([x,z])
for b in buckets:
logging.info("Buckets has %d entries", len(b));
# create the partial to lock the rset
dest = partial(parseBucketChunks, rset=rset)
# Create a pool of processes and run all the functions
pool = Pool(processes=numbuckets)
results = pool.map(dest, buckets)
logging.info("All the threads completed")
# Fix up all the quests in the reset
for data in results:
rset._pois['TileEntities'] += data['TileEntities']
rset._pois['Entities'] += data['Entities']
logging.info("Done.")
def handlePlayers(rset, render, worldpath):
if not hasattr(rset, "_pois"):
rset._pois = dict(TileEntities=[], Entities=[])
# only handle this region set once
if 'Players' in rset._pois:
return
dimension = None
try:
dimension = {None: 0,
'DIM-1': -1,
'DIM1': 1}[rset.get_type()]
except KeyError, e:
mystdim = re.match(r"^DIM_MYST(\d+)$", e.message) # Dirty hack. Woo!
if mystdim:
dimension = int(mystdim.group(1))
else:
raise
playerdir = os.path.join(worldpath, "players")
if os.path.isdir(playerdir):
playerfiles = os.listdir(playerdir)
playerfiles = [x for x in playerfiles if x.endswith(".dat")]
isSinglePlayer = False
else:
playerfiles = [os.path.join(worldpath, "level.dat")]
isSinglePlayer = True
rset._pois['Players'] = []
for playerfile in playerfiles:
try:
data = nbt.load(os.path.join(playerdir, playerfile))[1]
if isSinglePlayer:
data = data['Data']['Player']
except IOError:
logging.warning("Skipping bad player dat file %r", playerfile)
continue
playername = playerfile.split(".")[0]
if isSinglePlayer:
playername = 'Player'
if data['Dimension'] == dimension:
# Position at last logout
data['id'] = "Player"
data['EntityId'] = playername
data['x'] = int(data['Pos'][0])
data['y'] = int(data['Pos'][1])
data['z'] = int(data['Pos'][2])
rset._pois['Players'].append(data)
if "SpawnX" in data and dimension == 0:
# Spawn position (bed or main spawn)
spawn = {"id": "PlayerSpawn",
"EntityId": playername,
"x": data['SpawnX'],
"y": data['SpawnY'],
"z": data['SpawnZ']}
rset._pois['Players'].append(spawn)
def handleManual(rset, manualpois):
if not hasattr(rset, "_pois"):
rset._pois = dict(TileEntities=[], Entities=[])
rset._pois['Manual'] = []
if manualpois:
rset._pois['Manual'].extend(manualpois)
def main():
if os.path.basename(sys.argv[0]) == """genPOI.py""":
helptext = """genPOI.py
%prog --config=<config file> [--quiet]"""
else:
helptext = """genPOI
%prog --genpoi --config=<config file> [--quiet]"""
logger.configure()
parser = OptionParser(usage=helptext)
parser.add_option("--config", dest="config", action="store", help="Specify the config file to use.")
parser.add_option("--quiet", dest="quiet", action="count", help="Reduce logging output")
parser.add_option("--skip-scan", dest="skipscan", action="store_true", help="Skip scanning for entities when using GenPOI")
options, args = parser.parse_args()
if not options.config:
parser.print_help()
return
if options.quiet > 0:
logger.configure(logging.WARN, False)
# Parse the config file
mw_parser = configParser.MultiWorldParser()
mw_parser.parse(options.config)
try:
config = mw_parser.get_validated_config()
except Exception:
logging.exception("An error was encountered with your configuration. See the info below.")
return 1
destdir = config['outputdir']
# saves us from creating the same World object over and over again
worldcache = {}
markersets = set()
markers = dict()
for rname, render in config['renders'].iteritems():
try:
worldpath = config['worlds'][render['world']]
except KeyError:
logging.error("Render %s's world is '%s', but I could not find a corresponding entry in the worlds dictionary.",
rname, render['world'])
return 1
render['worldname_orig'] = render['world']
render['world'] = worldpath
# find or create the world object
if (render['world'] not in worldcache):
w = world.World(render['world'])
worldcache[render['world']] = w
else:
w = worldcache[render['world']]
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
for f in render['markers']:
markersets.add(((f['name'], f['filterFunction']), rset))
name = replaceBads(f['name']) + hex(hash(f['filterFunction']))[-4:] + "_" + hex(hash(rset))[-4:]
to_append = dict(groupName=name,
displayName = f['name'],
icon=f.get('icon', 'signpost_icon.png'),
createInfoWindow=f.get('createInfoWindow',True),
checked = f.get('checked', False))
try:
l = markers[rname]
l.append(to_append)
except KeyError:
markers[rname] = [to_append]
if not options.skipscan:
handleEntities(rset, os.path.join(destdir, rname), render, rname, config)
handlePlayers(rset, render, worldpath)
handleManual(rset, render['manualpois'])
logging.info("Done handling POIs")
logging.info("Writing out javascript files")
markerSetDict = dict()
for (flter, rset) in markersets:
# generate a unique name for this markerset. it will not be user visible
filter_name = flter[0]
filter_function = flter[1]
name = replaceBads(filter_name) + hex(hash(filter_function))[-4:] + "_" + hex(hash(rset))[-4:]
markerSetDict[name] = dict(created=False, raw=[], name=filter_name)
for poi in rset._pois['Entities']:
result = filter_function(poi)
if result:
if isinstance(result, basestring):
d = dict(x=poi['Pos'][0], y=poi['Pos'][1], z=poi['Pos'][2], text=result, hovertext=result)
elif type(result) == tuple:
d = dict(x=poi['Pos'][0], y=poi['Pos'][1], z=poi['Pos'][2], text=result[1], hovertext=result[0])
if "icon" in poi:
d.update({"icon": poi['icon']})
if "createInfoWindow" in poi:
d.update({"createInfoWindow": poi['createInfoWindow']})
markerSetDict[name]['raw'].append(d)
for poi in rset._pois['TileEntities']:
result = filter_function(poi)
if result:
if isinstance(result, basestring):
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, hovertext=result)
elif type(result) == tuple:
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result[1], hovertext=result[0])
# Dict support to allow more flexible things in the future as well as polylines on the map.
elif type(result) == dict:
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result['text'])
# Use custom hovertext if provided...
if 'hovertext' in result and isinstance(result['hovertext'], basestring):
d['hovertext'] = result['hovertext']
else: # ...otherwise default to display text.
d['hovertext'] = result['text']
if 'polyline' in result and type(result['polyline']) == tuple: #if type(result.get('polyline', '')) == tuple:
d['polyline'] = []
for point in result['polyline']:
# This poor man's validation code almost definately needs improving.
if type(point) == dict:
d['polyline'].append(dict(x=point['x'],y=point['y'],z=point['z']))
if isinstance(result['color'], basestring):
d['strokeColor'] = result['color']
if "icon" in poi:
d.update({"icon": poi['icon']})
if "createInfoWindow" in poi:
d.update({"createInfoWindow": poi['createInfoWindow']})
markerSetDict[name]['raw'].append(d)
for poi in rset._pois['Players']:
result = filter_function(poi)
if result:
if isinstance(result, basestring):
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, hovertext=result)
elif type(result) == tuple:
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result[1], hovertext=result[0])
# Dict support to allow more flexible things in the future as well as polylines on the map.
elif type(result) == dict:
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result['text'])
# Use custom hovertext if provided...
if 'hovertext' in result and isinstance(result['hovertext'], basestring):
d['hovertext'] = result['hovertext']
else: # ...otherwise default to display text.
d['hovertext'] = result['text']
if 'polyline' in result and type(result['polyline']) == tuple: #if type(result.get('polyline', '')) == tuple:
d['polyline'] = []
for point in result['polyline']:
# This poor man's validation code almost definately needs improving.
if type(point) == dict:
d['polyline'].append(dict(x=point['x'],y=point['y'],z=point['z']))
if isinstance(result['color'], basestring):
d['strokeColor'] = result['color']
if "icon" in poi:
d.update({"icon": poi['icon']})
if "createInfoWindow" in poi:
d.update({"createInfoWindow": poi['createInfoWindow']})
markerSetDict[name]['raw'].append(d)
for poi in rset._pois['Manual']:
result = filter_function(poi)
if result:
if isinstance(result, basestring):
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, hovertext=result)
elif type(result) == tuple:
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result[1], hovertext=result[0])
# Dict support to allow more flexible things in the future as well as polylines on the map.
elif type(result) == dict:
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result['text'])
# Use custom hovertext if provided...
if 'hovertext' in result and isinstance(result['hovertext'], basestring):
d['hovertext'] = result['hovertext']
else: # ...otherwise default to display text.
d['hovertext'] = result['text']
if 'polyline' in result and type(result['polyline']) == tuple: #if type(result.get('polyline', '')) == tuple:
d['polyline'] = []
for point in result['polyline']:
# This poor man's validation code almost definately needs improving.
if type(point) == dict:
d['polyline'].append(dict(x=point['x'],y=point['y'],z=point['z']))
if isinstance(result['color'], basestring):
d['strokeColor'] = result['color']
if "icon" in poi:
d.update({"icon": poi['icon']})
if "createInfoWindow" in poi:
d.update({"createInfoWindow": poi['createInfoWindow']})
markerSetDict[name]['raw'].append(d)
#print markerSetDict
with open(os.path.join(destdir, "markersDB.js"), "w") as output:
output.write("var markersDB=")
json.dump(markerSetDict, output, indent=2)
output.write(";\n");
with open(os.path.join(destdir, "markers.js"), "w") as output:
output.write("var markers=")
json.dump(markers, output, indent=2)
output.write(";\n");
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.collections.haveSigns=true;\n")
logging.info("Done")
if __name__ == "__main__":
main()