From b818a959267f3c7669781075c01fb43dbabc105d Mon Sep 17 00:00:00 2001 From: poikilos <7557867+poikilos@users.noreply.github.com> Date: Wed, 24 Feb 2016 02:45:51 -0500 Subject: [PATCH] save metadata as yaml Discard minetestmapper numpy output after analyzing and saving relevant data as YAML. Read YAML in php to get size of map tiles. --- README.md | 1 + chunkymap-regen.py | 515 ++++++++++++++++++++++++++++++------------ web/chunkymap.php | 170 +++++++++++++- web/index-example.php | 3 + 4 files changed, 538 insertions(+), 151 deletions(-) diff --git a/README.md b/README.md index 04e6e9d..24de494 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ This program comes without any warranty, to the extent permitted by applicable l * edit chunkymap_regen.py and change world_name to your world name ## Known Issues +* index-example.php should read the size of the chunks -- see near is_file($chunk_genresult_path) in chunkymap.php * Show player location (and optionally turn off) * Make a php file that shows the map on an html5 canvas (refresh players every 10 seconds, check for new map chunks every minute) * Make players invisible if they stay in one spot too long (consider them logged out by that method alone since not requiring mods) diff --git a/chunkymap-regen.py b/chunkymap-regen.py index a96c52c..bf5eb2a 100644 --- a/chunkymap-regen.py +++ b/chunkymap-regen.py @@ -1,6 +1,7 @@ #!/usr/bin/env python2 import os import subprocess +import traceback # REQUIRES: see README.md # The way to do a full render is deleting all files from the folder self.chunkymap_data_path such as /var/www/html/minetest/chunkymapdata (or chunkymap in current directory on Windows) @@ -15,15 +16,52 @@ import subprocess class MTChunk: x = None z = None - is_player_here = None + is_player_in_this_chunk = None is_fresh = None + width = None + height = None + is_marked = None + is_marked_empty = None + image_w = None + image_h = None + image_left = None + image_top = None + image_right = None + image_bottom = None def __init__(self): - self.is_player_here = False + self.is_player_in_this_chunk = False self.is_fresh = False + self.is_marked = False + self.is_marked_empty = False + + def save_yaml(self, yml_path): + try: + outs = open(yml_path, 'w') + outs.write("is_marked_empty:"+str(self.is_marked_empty)+"\n") + outs.write("is_marked:"+str(self.is_marked)+"\n") + if self.width is not None: + outs.write("width:"+str(self.width)+"\n") + if self.height is not None: + outs.write("height:"+str(self.height)+"\n") + if self.image_w is not None: + outs.write("image_w:"+str(self.image_w)+"\n") + if self.image_h is not None: + outs.write("image_h:"+str(self.image_h)+"\n") + if self.image_left is not None: + outs.write("image_left:"+str(self.image_left)+"\n") + if self.image_top is not None: + outs.write("image_top:"+str(self.image_top)+"\n") + if self.image_right is not None: + outs.write("image_right:"+str(self.image_right)+"\n") + if self.image_bottom is not None: + outs.write("image_bottom:"+str(self.image_bottom)+"\n") + outs.close() + except: + print("Could not finish saving chunk metadata to '"+str(yml_path)+"': "+str(traceback.format_exc())) class MTChunks: - + website_root = None username = None os_name = None @@ -36,7 +74,7 @@ class MTChunks: colors_path = None python_exe_path = None chunks = None - + #region values to save to YAML world_name = None world_path = None @@ -44,7 +82,7 @@ class MTChunks: chunkz_min = 0 chunkx_max = 0 chunkz_max = 0 - chunk_size = 80 + chunk_size = None #values for command arguments: maxheight = 50 minheight = -25 @@ -52,7 +90,9 @@ class MTChunks: #ALSO save to YAML: #total_generated_count = 0 #endregion values to save to YAML - + + world_blacklist = None + def __init__(self): #formerly checkpaths() in global scope self.chunks = {} self.username = "owner" @@ -66,7 +106,7 @@ class MTChunks: #input_string = input("Which self.username contains minetest/util/minetestmapper-numpy.py (minetest not .minetest) ["+self.username+"]?") if (len(input_string)>0): self.username = input_string - + #input_string = input("What is the root folder of your minetest website ["+self.website_root+"]?") if (len(input_string)>0): self.website_root = input_string @@ -87,29 +127,33 @@ class MTChunks: self.worlds_path = os.path.join(self.dotminetest_path,"worlds") self.world_path = os.path.join(self.worlds_path, self.world_name) auto_chosen_world = False + self.world_blacklist = list() + self.world_blacklist.append("abiyahhgamebv7world1") if not os.path.isdir(self.world_path): #for item in os.walk(self.worlds_path): print ("LOOKING FOR WORLDS IN " + self.worlds_path) for dirname, dirnames, filenames in os.walk(self.worlds_path): - index = 0 - for j in range(0,len(dirnames)): - i = len(dirnames) - 0 - 1 - if dirnames[i][0] == ".": - print (" SKIPPING "+dirnames[i]) - dirnames.remove_at(i) + #index = 0 + #for j in range(0,len(dirnames)): + # i = len(dirnames) - 0 - 1 + # if dirnames[i][0] == ".": + # print (" SKIPPING "+dirnames[i]) + # dirnames.remove_at(i) for subdirname in dirnames: print (" EXAMINING "+subdirname) - if (index == len(dirnames)-1): # skip first one because the one on my computer is big - self.world_name = subdirname - self.world_path = os.path.join(dirname, subdirname) # os.path.join(self.worlds_path, "try7amber") - print (" CHOSE "+self.world_path) - auto_chosen_world = True - break - index += 1 + if subdirname[0]!=".": + #if (index == len(dirnames)-1): # skip first one because the one on my computer is big + if subdirname not in self.world_blacklist: + self.world_name = subdirname + self.world_path = os.path.join(dirname, subdirname) # os.path.join(self.worlds_path, "try7amber") + print (" CHOSE "+self.world_path) + auto_chosen_world = True + break + #index += 1 if auto_chosen_world: break self.python_exe_path = "python" - + self.is_save_output_ok = True # this is probably required to avoid minutely writes @@ -132,11 +176,82 @@ class MTChunks: self.chunkx_max = 0 self.chunkz_max = 0 - self.chunk_size = 80 - self.maxheight = 50 - self.minheight = -25 + self.chunk_size = 16 + self.maxheight = 64 + self.minheight = -32 self.pixelspernode = 1 + def set_from_genresult(self, mtchunk, chunk_luid): + dest_genresult_path = self.get_chunk_genresult_path(chunk_luid) + result = False + if os.path.isfile(dest_genresult_path): + #may have data such as: + #Result image (w=80 h=80) will be written to chunk_x0z0.png + #Unknown node names: meze:meze default:stone_with_iron air default:dirt_with_snow default:stone_with_copper default:snow + #Unknown node ids: 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 + #Drawing image + #Saving to: chunk_x0z0.png + #('PNG Region: ', [0, 64, 0, 64]) + #('Pixels PerNode: ', 1) + #('border: ', 0) + mtchunk.is_marked = True + ins = open(dest_genresult_path, 'r') + line = True + while line: + line = ins.readline() + if line: + line_strip = line.strip() + if "data does not exist" in line_strip: + mtchunk.is_marked_empty = True + break + elif "Result image" in line_strip: + oparen_index = line_strip.find("(") + if (oparen_index>-1): + cparen_index = line_strip.find(")", oparen_index+1) + if (cparen_index>-1): + operations_string = line_strip[oparen_index+1:cparen_index] + operation_list = operations_string.split(" ") + #if len(operation_list)==2: + for operation_string in operation_list: + if "=" in operation_string: + chunks = operation_string.split("=") + if len(chunks)==2: + if chunks[0].strip()=="w": + try: + mtchunk.image_w=int(chunks[1].strip()) + except: + print("Bad value for image w:"+str(chunks[1])) + elif chunks[0].strip()=="h": + try: + mtchunk.image_h=int(chunks[1].strip()) + except: + print("Bad value for image h:"+str(chunks[1])) + else: + print("Bad name for image variable so ignoring variable named '"+str(chunks[0])+"'") + else: + print("Bad assignment (not 2 sides) so ignoring command '"+operation_string+"'") + else: + print("Bad assignment (operator) so ignoring command '"+operation_string+"'") + #else: + # print("Bad assignment count so ignoring operations string '"+operations_string+"'") + elif "PNG Region" in line_strip: + obracket_index = line_strip.find("[") + if obracket_index>-1: + cbracket_index = line_strip.find("]", obracket_index+1) + if cbracket_index>-1: + rect_values_string = line_strip[obracket_index+1:cbracket_index] + rect_values_list = rect_values_string.split(",") + if len(rect_values_list)==4: + #pngregion=[pngminx, pngmaxx, pngminz, pngmaxz] #from minetestmapper-numpy.py + mtchunk.image_left=int(rect_values_list[0].strip()) + mtchunk.image_right=int(rect_values_list[1].strip()) + mtchunk.image_top=int(rect_values_list[2].strip()) + mtchunk.image_bottom=int(rect_values_list[3].strip()) + else: + print("Bad map rect, so ignoring: "+rect_values_string) + ins.close() + + def get_dict_from_conf_file(self, path,assignment_operator="="): results = None print ("Checking "+str(path)+" for settings...") @@ -180,7 +295,7 @@ class MTChunks: #locally unique identifier (unique to world only) def get_chunk_luid(self, x,z): return "x"+str(x)+"z"+str(z) - + def get_chunk_image_name(self, chunk_luid): return "chunk_"+chunk_luid+".png" @@ -196,22 +311,67 @@ class MTChunks: def get_chunk_genresult_tmp_path(self, chunk_luid): return os.path.join(os.path.dirname(__file__), self.get_chunk_genresult_name(chunk_luid)) + def get_chunk_yaml_name(self, chunk_luid): + return "chunk_"+chunk_luid+".yml" + + def is_chunk_yaml_present(self, chunk_luid): + return os.path.isfile(self.get_chunk_yaml_path(chunk_luid)) + + def get_chunk_yaml_path(self, chunk_luid): + return os.path.join(self.chunkymap_data_path, self.get_chunk_yaml_name(chunk_luid)) + + def is_chunk_yaml_marked(self, chunk_luid): + yaml_path = self.get_chunk_yaml_path(chunk_luid) + result = False + if os.path.isfile(yaml_path): + result = True + #ins = open(yaml_path, 'r') + #line = True + #while line: + # line = ins.readline() + # if line: + # line_strip = line.strip() + # if "is_marked_empty:" in line_strip: + # result = True + # break + #ins.close() + return result + + def is_chunk_yaml_marked_empty(self, chunk_luid): + yaml_path = self.get_chunk_yaml_path(chunk_luid) + result = False + if os.path.isfile(yaml_path): + ins = open(yaml_path, 'r') + line = True + while line: + line = ins.readline() + if line: + line_strip = line.strip() + prevalue_string="is_marked_empty:" + if line_strip[:len(prevalue_string)]==prevalue_string: + result = bool(line_strip[len(prevalue_string):].strip()) + break + ins.close() + return result + + def get_chunk_genresult_path(self, chunk_luid): return os.path.join(self.chunkymap_data_path, self.get_chunk_genresult_name(chunk_luid)) + #deprecated def is_genresult_marked(self, chunk_luid): result = False dest_genresult_path = self.get_chunk_genresult_path(chunk_luid) - #is_empty_chunk = False if os.path.isfile(dest_genresult_path): result = True return result + #deprecated def is_genresult_marked_empty(self, chunk_luid): dest_genresult_path = self.get_chunk_genresult_path(chunk_luid) result = False if os.path.isfile(dest_genresult_path): - ins = open(dest_genresult_path) + ins = open(dest_genresult_path, 'r') line = True while line: line = ins.readline() @@ -233,43 +393,43 @@ class MTChunks: def remove_chunk_image(self, chunk_luid): result = False - png_path = self.get_chunk_image_path(chunk_luid) - if os.path.isfile(png_path): + tmp_png_path = self.get_chunk_image_path(chunk_luid) + if os.path.isfile(tmp_png_path): result = True - os.remove(png_path) + os.remove(tmp_png_path) return result - + def remove_chunk(self, chunk_luid): result = False out_path = self.get_chunk_genresult_path(chunk_luid) - png_path = self.get_chunk_image_path(chunk_luid) + tmp_png_path = self.get_chunk_image_path(chunk_luid) if os.path.isfile(out_path): os.remove(out_path) result = True - if os.path.isfile(png_path): - os.remove(png_path) + if os.path.isfile(tmp_png_path): + os.remove(tmp_png_path) result = True return result def is_chunk_rendered_on_dest(self, chunk_luid): #formerly is_chunk_empty_on_dest (reversed) is_rendered = False - #is_chunk_out_empty = self.is_genresult_marked_empty(chunk_luid) + #is_chunk_out_empty = self.is_chunk_yaml_marked_empty(chunk_luid) #dest_genresult_path = self.get_chunk_genresult_path(chunk_luid) - dest_png_path = self.get_chunk_image_name(chunk_luid) + dest_png_path = self.get_chunk_image_path(chunk_luid) if os.path.isfile(dest_png_path): #os.remove(dest_genresult_path) is_rendered = True return is_rendered - + def prepare_chunk_meta(self, chunk_luid): if chunk_luid not in self.chunks.keys(): self.chunks[chunk_luid] = MTChunk() - + def render_chunk(self, x, z): result = False chunk_luid = self.get_chunk_luid(x,z) png_name = self.get_chunk_image_name(chunk_luid) - png_path = self.get_chunk_image_tmp_path(chunk_luid) + tmp_png_path = self.get_chunk_image_tmp_path(chunk_luid) cmd_suffix = "" genresult_name = self.get_chunk_genresult_name(chunk_luid) genresult_path = self.get_chunk_genresult_tmp_path(chunk_luid) @@ -279,15 +439,15 @@ class MTChunks: x_max = x * self.chunk_size + self.chunk_size - 1 z_min = z * self.chunk_size z_max = z * self.chunk_size + self.chunk_size - 1 - + #print ("generating x = " + str(x_min) + " to " + str(x_max) + " , z = " + str(z_min) + " to " + str(z_max)) - cmd_string = self.python_exe_path + " \""+self.mtmn_path + "\" --region " + str(x_min) + " " + str(x_max) + " " + str(z_min) + " " + str(z_max) + " --maxheight "+str(self.maxheight)+" --minheight "+str(self.minheight)+" --pixelspernode "+str(self.pixelspernode)+" \""+self.world_path+"\" \""+png_path+"\"" + cmd_suffix + cmd_string = self.python_exe_path + " \""+self.mtmn_path + "\" --region " + str(x_min) + " " + str(x_max) + " " + str(z_min) + " " + str(z_max) + " --maxheight "+str(self.maxheight)+" --minheight "+str(self.minheight)+" --pixelspernode "+str(self.pixelspernode)+" \""+self.world_path+"\" \""+tmp_png_path+"\"" + cmd_suffix dest_png_path = self.get_chunk_image_path(chunk_luid) dest_genresult_path = self.get_chunk_genresult_path(chunk_luid) - #is_empty_chunk = is_genresult_marked(chunk_luid) and is_genresult_marked_empty(chunk_luid) + #is_empty_chunk = is_chunk_yaml_marked(chunk_luid) and is_chunk_yaml_marked_empty(chunk_luid) print (cmd_string) subprocess.call(cmd_string, shell=True) # TODO: remember not to allow arbitrary command execution, which could happen if input contains ';' when using shell=True - if os.path.isfile(png_path): + if os.path.isfile(tmp_png_path): result = True try: if (os.path.isfile(dest_png_path)): @@ -295,26 +455,36 @@ class MTChunks: except: print ("Could not finish deleting '"+dest_png_path+"'") try: - os.rename(png_path, dest_png_path) + os.rename(tmp_png_path, dest_png_path) print("(moved to '"+dest_png_path+"')") self.prepare_chunk_meta(chunk_luid) self.chunks[chunk_luid].is_fresh = True except: - print ("Could not finish moving '"+png_path+"' to '"+dest_png_path+"'") + print ("Could not finish moving '"+tmp_png_path+"' to '"+dest_png_path+"'") try: if (os.path.isfile(dest_genresult_path)): os.remove(dest_genresult_path) if self.is_save_output_ok: - os.rename(genresult_path, dest_genresult_path) - print("(moved to '"+dest_genresult_path+"')") + + #os.rename(genresult_path, dest_genresult_path) + #print("(moved to '"+dest_genresult_path+"')") + + tmp_chunk = MTChunk() + self.set_from_genresult(tmp_chunk,chunk_luid) + chunk_yaml_path = self.get_chunk_yaml_path(chunk_luid) + tmp_chunk.save_yaml(chunk_yaml_path) + print("(saved yaml to '"+chunk_yaml_path+"')") + os.remove(genresult_path) else: if os.path.isfile(genresult_path): os.remove(genresult_path) except: print ("Could not finish deleting/moving output") - + + + return result - + def check_players(self): self.chunkymap_data_path=os.path.join(self.website_root,"chunkymapdata") chunkymap_players_name = "players" @@ -325,66 +495,83 @@ class MTChunks: if not os.path.isfile(htaccess_path): self.deny_http_access(chunkymap_players_path) - players_path = os.path.join(self.world_path, "players") - for dirname, dirnames, filenames in os.walk(players_path): - for filename in filenames: - file_fullname = os.path.join(players_path,filename) - #print (" EXAMINING "+filename) - badstart_string = "." - player_name = None - player_position = None - if (filename[:len(badstart_string)]!=badstart_string): - ins = open(file_fullname) - line = True - has_enough_data = False - while line: - line = ins.readline() - if line: - ao_index = line.find("=") - if ao_index > 0: - found_name = line[:ao_index].strip() - found_value = line[ao_index+1:].strip() - if found_name=="name": - player_name = found_value - elif found_name=="position": - player_position = found_value - - if (player_name is not None) and (player_position is not None): - has_enough_data = True - break - ins.close() - player_dest_path = os.path.join(chunkymap_players_path,filename+".yml") + players_path = os.path.join(self.world_path, "players") + player_count = 0 + player_written_count = 0 + for dirname, dirnames, filenames in os.walk(players_path): + for filename in filenames: + file_fullname = os.path.join(players_path,filename) + #print (" EXAMINING "+filename) + badstart_string = "." + player_name = None + player_position = None + if (filename[:len(badstart_string)]!=badstart_string): + ins = open(file_fullname, 'r') + line = True + is_enough_data = False + while line: + line = ins.readline() + if line: + ao_index = line.find("=") + if ao_index > 0: + found_name = line[:ao_index].strip() + found_value = line[ao_index+1:].strip() + if found_name=="name": + player_name = found_value + elif found_name=="position": + player_position = found_value + + if (player_name is not None) and (player_position is not None): + is_enough_data = True + break + ins.close() + player_dest_path = os.path.join(chunkymap_players_path,filename+".yml") + if player_position is not None: + # mark chunk + tuple_noparen_pos_string = player_position.strip("() \n\r") + pos_strings = tuple_noparen_pos_string.split(",") + if len(pos_strings) == 3: + player_x = None + player_y = None + player_z = None + try: + player_x = float(pos_strings[0]) + player_y = float(pos_strings[1]) + player_z = float(pos_strings[2]) + except: + player_x = int(pos_strings[0]) + player_y = int(pos_strings[1]) + player_z = int(pos_strings[2]) + chunk_x = int((float(player_x)/self.chunk_size)) + chunk_y = int((float(player_y)/self.chunk_size)) + chunk_z = int((float(player_z)/self.chunk_size)) + chunk_luid = self.get_chunk_luid(chunk_x, chunk_z) + self.prepare_chunk_meta(chunk_luid) + self.chunks[chunk_luid].is_player_in_this_chunk = True + else: + print("Player '"+filename+"' has bad position data--should be 3-length (x,y,z) in position value: "+str(pos_strings)) + + #if is_enough_data: + #if player_name!="singleplayer": + map_player_dict = self.get_dict_from_conf_file(player_dest_path,":") + if (map_player_dict is None) or (map_player_dict["position"]!=player_position): + outs = open(player_dest_path, 'w') + if player_name is not None: + outs.write("name:"+player_name+"\n") # python automatically uses correct newline for your os when you put "\n" if player_position is not None: - # mark chunk - tuple_noparen_pos_string = player_position.strip("() \n\r") - pos_strings = tuple_noparen_pos_string.split(",") - if len(pos_strings) == 3: - player_x = int(pos_strings[player_x]) - player_y = int(pos_strings[player_y]) - player_z = int(pos_strings[player_z]) - chunk_x = int((float(player_x)/self.chunk_size)) - chunk_y = int((float(player_y)/self.chunk_size)) - chunk_z = int((float(player_z)/self.chunk_size)) - chunk_luid = self.get_chunk_luid(chunk_x, chunk_z) - self.prepare_chunk_meta() - self.chunks[chunk_luid].is_player_here = True - else: - print("Player '"+filename+"' has bad position data--should be 3-length (x,y,z) in position value: "+str(pos_strings)) - - if has_enough_data: - #if player_name!="singleplayer": - map_player_dict = self.get_dict_from_conf_file(player_dest_path,":") - if (map_player_dict is None) or (map_player_dict["position"]!=player_position): - outs = open(player_dest_path, 'w') - outs.write("name:"+player_name+"\n") # python automatically uses correct newline for your os when you put "\n" - outs.write("position:"+player_position+"\n") - outs.close() + outs.write("position:"+player_position+"\n") + outs.write("is_enough_data:"+str(is_enough_data)) + outs.close() + player_written_count += 1 + player_count += 1 + + def is_player_at_luid(self, chunk_luid): result = False if chunk_luid in self.chunks.keys(): - result = self.chunks[chunk_luid].is_player_here + result = self.chunks[chunk_luid].is_player_in_this_chunk return result - + def is_chunk_fresh(self, chunk_luid): result = False if chunk_luid in self.chunks.keys(): @@ -396,7 +583,7 @@ class MTChunks: self.check_players() self.chunkymap_data_path=os.path.join(self.website_root,"chunkymapdata") yaml_name = "generated.yml" - yaml_path = os.path.join(self.chunkymap_data_path, yaml_name) + world_yaml_path = os.path.join(self.chunkymap_data_path, yaml_name) if not os.path.isdir(self.chunkymap_data_path): os.mkdir(self.chunkymap_data_path) @@ -405,30 +592,43 @@ class MTChunks: os.makedirs(self.chunkymap_data_path) if not os.path.isfile(htaccess_path): self.deny_http_access(self.chunkymap_data_path) - - mapvars = self.get_dict_from_conf_file(yaml_path,":") + + mapvars = self.get_dict_from_conf_file(world_yaml_path,":") #is_testonly == (os_name=="windows") - if mapvars is not None and set(['self.world_name']).issubset(mapvars): + if mapvars is not None and set(['world_name']).issubset(mapvars): #print (" (FOUND self.world_name)") - if mapvars["self.world_name"] != self.world_name: - print ("REMOVING data since from different world (map '"+str(mapvars["self.world_name"])+"' is not '"+str(self.world_name)+"')...") + if mapvars["world_name"] != self.world_name: + print ("REMOVING data since from different world (map '"+str(mapvars["world_name"])+"' is not '"+str(self.world_name)+"')...") for dirname, dirnames, filenames in os.walk(self.chunkymap_data_path): - index = 0 - for j in range(0,len(filenames)): - i = len(filenames) - 0 - 1 - if filenames[i][0] == ".": - print (" SKIPPING "+filenames[i]) - filenames.remove_at(i) + #index = 0 + #for j in range(0,len(filenames)): + # i = len(filenames) - 0 - 1 + # if filenames[i][0] == ".": + # print (" SKIPPING "+filenames[i]) + # filenames.remove_at(i) for filename in filenames: - file_fullname = os.path.join(self.chunkymap_data_path,filename) - print (" EXAMINING "+filename) - badstart_string = "chunk" - if (len(filename) >= len(badstart_string)) and (filename[:len(badstart_string)]==badstart_string): - os.remove(file_fullname) - elif filename==yaml_name: - os.remove(file_fullname) - + if filename[0] != ".": + file_fullname = os.path.join(self.chunkymap_data_path,filename) + print (" EXAMINING "+filename) + badstart_string = "chunk" + if (len(filename) >= len(badstart_string)) and (filename[:len(badstart_string)]==badstart_string): + os.remove(file_fullname) + elif filename==yaml_name: + os.remove(file_fullname) + for dirname, dirnames, filenames in os.walk(os.path.join(self.chunkymap_data_path, "players")): + #for j in range(0,len(filenames)): + # i = len(filenames) - 0 - 1 + # if filenames[i][0] == ".": + # print (" SKIPPING "+filenames[i]) + # filenames.remove_at(i) + for filename in filenames: + if filename[0] != ".": + file_fullname = os.path.join(self.chunkymap_data_path,filename) + print (" EXAMINING "+filename) + badend_string = ".yml" + if (len(filename) >= len(badend_string)) and (filename[len(filename)-len(badend_string):]==badend_string): + os.remove(file_fullname) self.chunkx_min = 0 self.chunkz_min = 0 self.chunkx_max = 0 @@ -437,44 +637,62 @@ class MTChunks: newchunk_luid_list = list() outline_generates_count = 1 + is_changed = False + is_different_world = False + #if str(self.world_name) != str(mapvars["world_name"]): + # is_different_world = True + # print("FULL RENDER since chosen world name '"+self.world_name+"' does not match previously rendered world name '"+mapvars["world_name"]+"'") + while outline_generates_count > 0: outline_generates_count = 0 for z in range (self.chunkz_min,self.chunkz_max+1): for x in range(self.chunkx_min,self.chunkx_max+1): #python ~/minetest/util/minetestmapper-numpy.py --region -1200 800 -1200 800 --drawscale --maxheight 100 --minheight -50 --pixelspernode 1 ~/.minetest/worlds/FCAGameAWorld ~/map.png #sudo mv ~/map.png /var/www/html/minetest/images/map.png - + #only generate the edges (since started with region 0 0 0 0) and expanding from there until no png is created: is_outline = (x==self.chunkx_min) or (x==self.chunkx_max) or (z==self.chunkz_min) or (z==self.chunkz_max) if is_outline: chunk_luid = self.get_chunk_luid(x,z) - - is_player_here = self.is_player_at_luid(chunk_luid) - + + #if (is_different_world): #instead, see above where all chunk files and player files are deleted + # self.remove_chunk(chunk_luid) + + is_player_in_this_chunk = self.is_player_at_luid(chunk_luid) + is_render_needed = False - + if not self.is_chunk_fresh(chunk_luid): - if is_player_here: - if self.is_genresult_marked(chunk_luid): - if self.is_genresult_marked_empty(chunk_luid): + if is_player_in_this_chunk: + if self.is_chunk_yaml_marked(chunk_luid): + if self.is_chunk_yaml_marked_empty(chunk_luid): is_render_needed = True - print (chunk_luid+": RENDERING non-fresh marked empty chunk (player present in it)") + print (chunk_luid+": RENDERING nonfresh previously marked empty (player in it)") else: - print (chunk_luid+": SKIPPING non-fresh marked chunk (player present in it)") + print (chunk_luid+": SKIPPING nonfresh previously marked (player in it)") else: is_render_needed = True - print (chunk_luid+": RENDERING non-fresh unmarked chunk (player present in it)") + print (chunk_luid+": RENDERING nonfresh unmarked (player in it)") else: - if (not self.is_genresult_marked(chunk_luid)): + if (not self.is_chunk_yaml_marked(chunk_luid)): is_render_needed = True - print (chunk_luid+": RENDERING non-fresh unmarked chunk (skipped other checks since player not present in it)") + print (chunk_luid+": RENDERING nonfresh unmarked (simple check since has no player)") else: - print (chunk_luid+": SKIPPING non-fresh marked chunk (skipped other checks since player not present in it)") + print (chunk_luid+": SKIPPING nonfresh previously marked (simple check since has no player)") else: print (chunk_luid+": SKIPPING fresh chunk") - #if (not self.is_genresult_marked(chunk_luid)): + #if (not self.is_chunk_yaml_marked(chunk_luid)): #is_render_needed = True - + + # This should never happen since keeping the output of minetestmapper-numpy.py (after analyzing that output) is deprecated: + #if self.is_genresult_marked(chunk_luid) and not self.is_chunk_yaml_present(chunk_luid): + # tmp_chunk = MTChunk() + # self.set_from_genresult(tmp_chunk,chunk_luid) + # chunk_yaml_path = self.get_chunk_yaml_path(chunk_luid) + # tmp_chunk.save_yaml(chunk_yaml_path) + # print("(saved yaml to '"+chunk_yaml_path+"')") + + if is_render_needed: if (self.render_chunk(x,z)): total_generated_count += 1 @@ -483,10 +701,12 @@ class MTChunks: if self.is_chunk_rendered_on_dest(chunk_luid): total_generated_count += 1 outline_generates_count += 1 - png_path = self.get_chunk_image_path(chunk_luid) - print(chunk_luid+": Skipping existing map tile file " + png_path + " (delete it to re-render)") + tmp_png_path = self.get_chunk_image_path(chunk_luid) + print(chunk_luid+": Skipping existing map tile file " + tmp_png_path + " (delete it to re-render)") #elif is_empty_chunk: #print("Skipping empty chunk " + chunk_luid) + #else: + #print(chunk_luid+": Not rendered on dest.") print ("") # blank line before next z so output is more readable self.chunkx_min -= 1 self.chunkz_min -= 1 @@ -506,22 +726,21 @@ class MTChunks: new_map_dict["world_path"]=str(self.world_path) new_map_dict["chunkymap_data_path"]=str(self.chunkymap_data_path) new_map_dict["total_generated_count"]=str(total_generated_count) - is_changed = False if mapvars is None: - print ("SAVING '" + yaml_path + "' since nothing was loaded or it did not exist") + print ("SAVING '" + world_yaml_path + "' since nothing was loaded or it did not exist") is_changed = True else: for this_key in new_map_dict.iterkeys(): if (this_key not in mapvars.keys()): is_changed = True - print ("SAVING '" + yaml_path + "' since " + str(this_key) + " not in mapvars") + print ("SAVING '" + world_yaml_path + "' since " + str(this_key) + " not in mapvars") break elif (str(mapvars[this_key]) != str(new_map_dict[this_key])): is_changed = True - print ("SAVING '" + yaml_path + "' since new " + this_key + " value " + str(mapvars[this_key]) + " not same as saved value " + str(mapvars[this_key]) + "") + print ("SAVING '" + world_yaml_path + "' since new " + this_key + " value " + str(new_map_dict[this_key]) + " not same as saved value " + str(mapvars[this_key]) + "") break if is_changed: - outs = open(yaml_path, 'w') + outs = open(world_yaml_path, 'w') outs.write("world_name:"+str(self.world_name) + "\n") outs.write("chunk_size:"+str(self.chunk_size) + "\n") outs.write("pixelspernode:"+str(self.pixelspernode) + "\n") @@ -538,7 +757,7 @@ class MTChunks: outs.write("total_generated_count:"+str(total_generated_count) + "\n") outs.close() else: - print ("(Not saving '"+yaml_path+"' since same value of each current variable is already in file as loaded)") + print ("(Not saving '"+world_yaml_path+"' since same value of each current variable is already in file as loaded)") else: print ("failed since this folder must contain colors.txt and minetestmapper-numpy.py") diff --git a/web/chunkymap.php b/web/chunkymap.php index 91eb6e2..ae1d13e 100644 --- a/web/chunkymap.php +++ b/web/chunkymap.php @@ -2,6 +2,7 @@ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); + //NOTE: for parse errors, MUST add the following line to php.ini (such as /etc/php5/apache2/php.ini): display_errors = on if (is_file('browser.php')) { include_once('browser.php'); @@ -259,6 +260,65 @@ function echo_chunkymap_table() { $chunkz_min = 0; $chunkx_max = 0; $chunkz_max = 0; + global $showplayers; + $players = array(); + $player_count = 0; + + if ($showplayers==true) { + $chunkymap_players_path = $chunkymapdata_path."/players"; + if ($handle = opendir($chunkymap_players_path)) { + while (false !== ($file = readdir($handle))) { + if (substr($file, 0, 1) != ".") { + $file_lower = strtolower($file); + if (endsWith($file_lower, ".yml")) { + $player_id=substr($file,0,strlen($file)-4); //-4 for .yml + $file_path = $chunkymap_players_path."/".$file; + $player_dict = get_dict_from_conf($file_path); + $player_dict["id"]=$player_id; + //$players[$player_count]=get_dict_from_conf($file_path); + //$players[$player_count]["id"]=$player_id; + if (isset($player_dict["position"])) { + $tuple_string=trim($player_dict["position"]); + if ( startsWith($tuple_string, "(") and endsWith($tuple_string, ")") ) { + $tuple_string=substr($tuple_string,1,strlen($tuple_string)-2); + } + $coordinates = explode(",", $tuple_string); + if (count($coordinates)==3) { + $chunk_luid = "x".$x."z".$z; + if (!isset($chunk_assoc[$chunk_luid])) { + $chunk_assoc[$chunk_luid] = array(); + } + if (!isset($chunk_assoc[$chunk_luid]["players"])) { + $chunk_assoc[$chunk_luid]["players"] = array(); + } + if (!isset($chunk_assoc[$chunk_luid]["players_count"])) { + $chunk_assoc[$chunk_luid]["players_count"] = 0; + } + if (isset($player_dict["name"])) { + $chunk_assoc[$chunk_luid][ "players" ][ $chunk_assoc[$chunk_luid]["players_count"] ][ "name" ] = $player_dict["name"] + } + else { + $chunk_assoc[$chunk_luid][ "players" ][ $chunk_assoc[$chunk_luid]["players_count"] ][ "name" ] = $player_dict["id"] + } + $chunk_assoc[$chunk_luid]["players_count"] += 1; + } + else { + echo_error("Bad coordinates $tuple_string for player."); + } + } + + //$player_count++; + } + } + } + } + } + if (isset($chunk_assoc[$chunk_luid]["players_count"])) { + $nonprivate_name_beginning_char_count = 2; + for ($player_count=0; $player_count<$chunk_assoc[$chunk_luid]["players_count"]; $player_count++) { + //echo "
".substr($chunk_assoc[$chunk_luid]["players"][$player_count]["name"], 0, $nonprivate_name_beginning_char_count)."
"; + } + } //if ($map_dict != null) { // $chunkx_min = $map_dict["chunkx_min"]; // $chunkz_min = $map_dict["chunkz_min"]; @@ -281,7 +341,10 @@ function echo_chunkymap_table() { $z = substr($file_lower, $z_opener_index + strlen($z_opener), $z_len); if (is_int_string($x) and is_int_string($z)) { $chunk_luid = "x".$x."z".$z; - $chunk_assoc[$chunk_luid] = true; + if (!isset($chunk_assoc[$chunk_luid])) { + $chunk_assoc[$chunk_luid] = array(); + } + $chunk_assoc[$chunk_luid]["is_rendered"] = true; if ($is_verbose) echo "$chunk_luid,"; if ($x<$chunkx_min) { $chunkx_min=(int)$x; @@ -320,22 +383,123 @@ function echo_chunkymap_table() { $scale=(float)$chunkymap_view_zoom_multiplier; // no longer /100 $zoomed_w=(int)((float)$chunkymap_tile_original_w*$scale+.5); $zoomed_h=(int)((float)$chunkymap_tile_original_h*$scale+.5); + $genresult_suffix_then_dot_then_ext="_mapper_result.txt"; + $dot_yaml=".yml"; while ($z >= $chunkz_min) { echo_hold( " \r\n"); $x = (int)$chunkx_min; while ($x <= $chunkx_max) { - echo_hold( " "); + $this_zoomed_w = $zoomed_w; + $this_zoomed_h = $zoomed_h; + + $chunk_yaml_name = $x_opener.$x.$z_opener.$z.$dot_yaml; + $chunk_yaml_path = $chunkymapdata_path.'/'.$chunk_yaml_name; + //$chunk_genresult_name = $x_opener.$x.$z_opener.$z.$genresult_suffix_then_dot_then_ext; + //$chunk_genresult_path = $chunkymapdata_path.'/'.$chunk_img_name; + $td_style_suffix=""; + $element_align_style_suffix=""; + $alignment_comment=""; + //if (is_file($chunk_genresult_path)) { + // contains lines such as: + // Result image (w=80 h=64) will be written to chunk_x0z1.png + // ('PNG Region: ', [0, 64, 80, 128]) + // ('Pixels PerNode: ', 1) + // where PNG Region's list value is an exclusive rect ordered as x1, y1, x2, y2 + //$found_original_w = null; + //$found_original_h = null; + //$this_zoomed_w=(int)((float)$found_original_w*$scale+.5); + //this_zoomed_h=(int)((float)$found_original_h*$scale+.5); + //} + if (is_file($chunk_yaml_path)) { + // contains lines such as: + //is_marked_empty:False + //is_marked:True + //image_w:80 + //image_h:80 + //image_left:0 + //image_top:64 + //image_right:-80 + //image_bottom:-16 + // where if is_marked_empty, the remaining values don't exist + $expected_left = (int)$x * (int)$chunkymap_tile_original_w; + $expected_top = (int)$z * (int)$chunkymap_tile_original_h; + $expected_right = (int)$x + (int)$chunkymap_tile_original_w; + $expected_bottom = (int)$z + (int)$chunkymap_tile_original_h; + $chunk_dict = get_dict_from_conf($chunk_yaml_path,":"); + if (isset($chunk_dict["image_w"])) { + $this_zoomed_w=(int)((float)$chunk_dict["image_w"]*$scale+.5); + } + if (isset($chunk_dict["image_h"])) { + $this_zoomed_h=(int)((float)$chunk_dict["image_h"]*$scale+.5); + } + //TODO: use image_* to determine (if the doesn't touch certain sides of image_w x image_h rect, change the following accordingly) + if (isset($chunk_dict["image_left"])) { + if (isset($chunk_dict["image_right"])) { + if ( (int)$chunk_dict["image_left"] > $expected_left ) { + $td_style_suffix.="text-align:right;"; + $alignment_comment.=""; + } + //elseif ( (int)$chunk_dict["image_right"] < $expected_right ) { + // $td_style_suffix.="text-align:left;"; + //} + else { + $td_style_suffix.="text-align:left;"; + $alignment_comment.=""; + } + } + } + + //if (isset($chunk_dict["image_right"])) { + // if ( (int)$chunk_dict["image_right"] != $expected_right ) { + // $td_style_suffix.="text-align:left;"; + // } + // //else { + // // $td_style_suffix.="text-align:left;"; + // //} + //} + + if (isset($chunk_dict["image_top"])) { + if (isset($chunk_dict["image_bottom"])) { + if ( (int)$chunk_dict["image_top"] > $expected_top) { + $element_align_style_suffix.="vertical-align:bottom;"; + $alignment_comment.=""; + } + //elseif ( (int)$chunk_dict["image_bottom"] < $expected_bottom) { + // $element_align_style_suffix.="vertical-align:top;"; + //} + else { + $element_align_style_suffix.="vertical-align:top;"; + $alignment_comment.=""; + } + } + } + //if (isset($chunk_dict["image_bottom"])) { + // if ( (int)$chunk_dict["image_bottom"] != $expected_bottom) { + // $element_align_style_suffix.="vertical-align:top;"; + // } + // //else { + // // $element_align_style_suffix.="vertical-align:bottom;"; + // //} + //} + + //$element_align_style_suffix.="vertical-align:left;"; + } + + echo_hold( " "); $chunk_luid = "x".$x."z".$z; $chunk_img_name = $x_opener.$x.$z_opener.$z."$dot_and_ext"; $chunk_img_path = $chunkymapdata_path.'/'.$chunk_img_name; + + if (is_file($chunk_img_path)) { - echo_hold( ""); } else { //echo_hold( " "); } //echo "
".$x.",0,".$z; + echo_hold($alignment_comment); echo_hold( "\r\n"); $x++; } diff --git a/web/index-example.php b/web/index-example.php index 33646b1..3c94447 100644 --- a/web/index-example.php +++ b/web/index-example.php @@ -2,6 +2,9 @@ Chunkymap Example Page