diff --git a/README.md b/README.md index 8edc51f..dfa7dbe 100644 --- a/README.md +++ b/README.md @@ -54,10 +54,15 @@ This should be hard to corrupt since id is used as the indexer for the players d * games_path, mods_path, players_path, and other subdirectories of the major ones should not be stored in minetestmeta.yml, since otherwise the values may deviate from the parent directories when the parent directories change. To avoid this problem, instead derive the paths from the parent paths using your favorite language such as in the following examples: games_path = os.path.join(minetestinfo.get_var("shared_minetest_path"), "games") - #KEEP IN MIND: gameid in world.mt is the name of the game FOLDER (NOT the name variable in the folder's game.conf) mods_path = os.path.join(minetestinfo.get_var("game_path"), "mods") players_path = os.path.join(minetestinfo.get_var("primary_world_path"), "players") + world_path = None + world_name = None + if minetestinfo.contains("primary_world_path"): + world_path = minetestinfo.get_var("primary_world_path") + world_name = os.path.basename(world_path) * Keep in mind that gameid (in game.conf in a subgame folder, and world.mt in a world folder) is NOT case-sensitive: for example, minetest_game has the gameid 'Minetest' (first letter capitalized) but the worlds generated by Minetest client have the gameid 'minetest' (lowercase) in their world.mt + Yet somehow for everything else, gameid in world.mt is the name of the game FOLDER (NOT the name variable in the folder's game.conf) * the map update function is only able to detect new chunks, and only checks edge chunks if player is present in one * The following are saved to chunkymap.yml if not already set: www_minetest_path (such as /var/www/html/minetest) diff --git a/chunkymap-regen.py b/chunkymap-regen.py index 705069b..6d9d00e 100644 --- a/chunkymap-regen.py +++ b/chunkymap-regen.py @@ -15,9 +15,13 @@ import math from minetestinfo import * from expertmm import * +#python_exe_path is from: +from pythoninfo import * from PIL import Image, ImageDraw, ImageFont, ImageColor +#mode_to_bpp dict is from Antti Haapala. . 7 Mar 2015. 28 Feb 2016. mode_to_bpp = {'1':1, 'L':8, 'P':8, 'RGB':24, 'RGBA':32, 'CMYK':32, 'YCbCr':24, 'I':32, 'F':32} + #best_timer = timeit.default_timer #if sys.platform == "win32": # on Windows, the best timer is time.clock() @@ -26,7 +30,8 @@ mode_to_bpp = {'1':1, 'L':8, 'P':8, 'RGB':24, 'RGBA':32, 'CMYK':32, 'YCbCr':24, # on most other platforms, the best timer is time.time() # best_timer = time.time # REQUIRES: see README.md -# The way to do a full render is deleting all files from the folder www_minetest_path/chunkymapdata such as /var/www/html/minetest/chunkymapdata (or chunkymap in current directory on Windows) + +# The way to do a full render is deleting all files from the world folder under chunkymapdata under your system's www_minetest_path such as /var/www/html/minetest/chunkymapdata/world #minetestmapper-numpy.py calculates the region as follows: #(XMIN','XMAX','ZMIN','ZMAX'), default = (-2000,2000,-2000,2000) @@ -52,150 +57,7 @@ mode_to_bpp = {'1':1, 'L':8, 'P':8, 'RGB':24, 'RGBA':32, 'CMYK':32, 'YCbCr':24, #def size(self): #return len(self.items) -class MTDecaChunk: - - metadata = None - last_changed_utc_second = None - - def __init__(self): - self.metadata = {} - self.metadata["last_saved_utc_second"] = None - self.metadata["luid_list"] = None # what chunks this decachunk contains (as saved to 160px image) - - def load_yaml(self, yml_path): - self.metadata = get_dict_modified_by_conf_file(self.metadata,yml_path,":") - - def save_yaml(self, yml_path): - save_conf_from_dict(yml_path, self.metadata, assignment_operator=":", save_nulls_enable=False) - -class MTChunk: - #x = None - #z = None - metadata = None - is_fresh = None - #luid = None - - def __init__(self): - # NOTE: variables that need to be saved (and only they) should be stored in dict - self.metadata = {} - self.is_fresh = False - - self.metadata["is_empty"] = False # formerly is_marked_empty - self.metadata["is_marked"] = False - self.metadata["width"] = None - self.metadata["height"] = None - self.metadata["image_w"] = None - self.metadata["image_h"] = None - self.metadata["image_left"] = None - self.metadata["image_top"] = None - self.metadata["image_right"] = None - self.metadata["image_bottom"] = None - self.metadata["is_traversed"] = False - self.metadata["tags"] = None - - def load_yaml(self, yml_path): - self.metadata = get_dict_modified_by_conf_file(self.metadata,yml_path,":") - - def save_yaml(self, yml_path): - save_conf_from_dict(yml_path, self.metadata, assignment_operator=":", save_nulls_enable=False) - - #requires output such as from minetestmapper-numpy.py - #returns whether save is needed (whether metadata was changed) - def set_from_genresult(self, this_genresult_path): - #this_genresult_path = mtchunks.get_chunk_genresult_path(chunk_luid) - participle = "getting copy of dict" - try: - is_changed = False - old_meta = get_dict_deepcopy(self.metadata) - if os.path.isfile(this_genresult_path): - #may have data such as: - #Result image (w=16 h=16) 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) - self.metadata["is_marked"] = True - participle = "opening '"+this_genresult_path+"'" - ins = open(this_genresult_path, 'r') - line = True - counting_number = 1 - while line: - participle = "reading line "+str(counting_number) - line = ins.readline() - if line: - line_strip = line.strip() - try: - if ("does not exist" in line_strip): # official minetestmapper.py says "World does not exist" but expertmm fork and minetestmapper-numpy.py say "data does not exist" - self.metadata["is_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: - self.metadata["image_w"]=int(chunks[1].strip()) - except: - print("Bad value for image w:"+str(chunks[1])) - elif chunks[0].strip()=="h": - try: - self.metadata["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 - self.metadata["image_left"]=int(rect_values_list[0].strip()) - self.metadata["image_right"]=int(rect_values_list[1].strip()) - self.metadata["image_top"]=int(rect_values_list[2].strip()) - self.metadata["image_bottom"]=int(rect_values_list[3].strip()) - else: - print("Bad map rect, so ignoring: "+rect_values_string) - elif (len(line_strip)>5) and (line_strip[:5]=="xmin:"): - self.metadata["image_left"] = int(line_strip[5:].strip()) - elif (len(line_strip)>5) and (line_strip[:5]=="xmax:"): - self.metadata["image_right"] = int(line_strip[5:].strip()) - elif (len(line_strip)>5) and (line_strip[:5]=="zmin:"): - #(zmin is bottom since cartesian) - self.metadata["image_bottom"] = int(line_strip[5:].strip()) - elif (len(line_strip)>5) and (line_strip[:5]=="zmax:"): - #(zmax is top since cartesian) - self.metadata["image_top"] = int(line_strip[5:].strip()) - except: - print("#failed to parse line:"+str(line_strip)) - counting_number += 1 - ins.close() - participle = "checking for changes" - is_changed = is_dict_subset(self.metadata, old_meta, False) - except: - print("Could not finish "+participle+" in set_from_genresult:") - view_traceback() - return is_changed + class MTChunks: chunkymap_data_path = None @@ -243,18 +105,17 @@ class MTChunks: chunkymap_players_path = None data_16px_path = None data_160px_path = None - FLAG_EMPTY_HEXCOLOR = "#010000" + FLAG_COLORS_LIST = None world_name = None chunkymap_thisworld_data_path = None genresult_name_opener_string = "chunk_" - genresult_name_closer_string = "_mapper_result.txt" min_indent = None def __init__(self): #formerly checkpaths() in global scope #self.force_rerender_decachunks_enable = True self.FLAG_COLORS_LIST = list() - self.FLAG_COLOR_CHANNELS = get_list_from_hex(self.FLAG_EMPTY_HEXCOLOR) + self.FLAG_COLOR_CHANNELS = get_list_from_hex(FLAG_EMPTY_HEXCOLOR) self.FLAG_COLORS_LIST.append(self.FLAG_COLOR_CHANNELS) self.FLAG_COLORS_LIST.append((255,255,255)) #for compatibility with maps generated by earlier versions ONLY self.FLAG_COLORS_LIST.append((0,0,0)) #for compatibility with maps generated by earlier versions ONLY @@ -297,17 +158,6 @@ class MTChunks: # print("(ERROR: missing, so please close immediately and update primary_world_path in '"+minetestinfo._config_path+"' before next run)") #print("") - self.python_exe_path = "python" - if os_name=="windows": - try: - alt_path = "C:\\python27\python.exe" - if os.path.isfile(alt_path): - self.python_exe_path = alt_path - #else may be in path--assume installer worked - except: - pass # do nothing - - worldmt_path = os.path.join(minetestinfo.get_var("primary_world_path"), "world.mt") self.backend_string="sqlite3" if (os.path.isfile(worldmt_path)): @@ -334,8 +184,8 @@ class MTChunks: else: print("WARNING: Database backend cannot be detected (unable to ensure image generator script will render map)") + #region the following is also in singleimage.py self.minetestmapper_fast_sqlite_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmapper-numpy.py") - self.minetestmapper_custom_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmapper-expertmm.py") self.minetestmapper_py_path = self.minetestmapper_fast_sqlite_path if (self.backend_string!="sqlite3"): @@ -348,6 +198,7 @@ class MTChunks: if not os.path.isfile(self.colors_path): print("ERROR: missing '"+self.colors_path+"', so exiting "+__file__+".") sys.exit(2) + #endregion the following is also in singleimage.py self.chunkymap_data_path=os.path.join(minetestinfo.get_var("www_minetest_path"),"chunkymapdata") @@ -493,6 +344,7 @@ class MTChunks: install_list.append(InstalledFile("start.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) install_list.append(InstalledFile("target_start.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) install_list.append(InstalledFile("compass-rose.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) + install_list.append(InstalledFile("loading.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) install_list.append(InstalledFile("arrow-wide-up.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) install_list.append(InstalledFile("arrow-wide-down.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) install_list.append(InstalledFile("arrow-wide-left.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) @@ -623,7 +475,7 @@ class MTChunks: result = True break else: - raw_input("ERROR: FLAG_COLOR (obtained from FLAG_EMPTY_HEXCOLOR) has "+len(FLAG_COLOR)+" element(s) (3 or 4 expected)") + raw_input("ERROR: FLAG_COLOR (obtained from FLAG_EMPTY_HEXCOLOR in minetestinfo.py) has "+len(FLAG_COLOR)+" element(s) (3 or 4 expected)") return result def get_index_of_chunk_on_todo_list(self, chunky_pos, allow_current_chunk_enable=False): @@ -722,7 +574,7 @@ class MTChunks: else: print(" USING ("+str(len(chunky_coord_list))+") chunks (region "+str(chunky_min_x)+":"+str(chunky_max_x)+","+str(chunky_min_z)+":"+str(chunky_max_z)+")") decachunk_global_coords = decachunky_x*160, decachunky_z*160 - im = Image.new("RGB", (160, 160), self.FLAG_EMPTY_HEXCOLOR) + im = Image.new("RGB", (160, 160), FLAG_EMPTY_HEXCOLOR) decachunk_yaml_path = self.get_decachunk_yaml_path_from_decachunk(decachunky_x, decachunky_z) decachunk_image_path = self.get_decachunk_image_path_from_decachunk(decachunky_x, decachunky_z) combined_count = 0 @@ -848,10 +700,10 @@ class MTChunks: def get_chunk_genresult_name(self, chunky_x, chunky_z): chunk_luid = self.get_chunk_luid(chunky_x, chunky_z) - return self.genresult_name_opener_string+chunk_luid+self.genresult_name_closer_string + return self.genresult_name_opener_string+chunk_luid+genresult_name_closer_string def get_chunk_luid_from_genresult_name(self, file_name): - return file_name[len(self.genresult_name_opener_string):-1*len(self.genresult_name_closer_string)] + return file_name[len(self.genresult_name_opener_string):-1*len(genresult_name_closer_string)] def get_chunk_genresult_tmp_folder(self, chunky_x, chunky_z): #coords = self.get_coords_from_luid(chunk_luid) @@ -995,7 +847,7 @@ class MTChunks: cmd_suffix = "" cmd_suffix = " > \""+genresult_path+"\"" #self.mapper_id = "minetestmapper-region" - cmd_no_out_string = self.python_exe_path + " \""+self.minetestmapper_py_path + "\" --region " + str(min_x) + " " + str(max_x) + " " + str(min_z) + " " + str(max_z) + " --maxheight "+str(self.mapvars["maxheight"])+" --minheight "+str(self.mapvars["minheight"])+" --pixelspernode "+str(self.mapvars["pixelspernode"])+" \""+minetestinfo.get_var("primary_world_path")+"\" \""+tmp_png_path+"\"" + cmd_no_out_string = python_exe_path + " \""+self.minetestmapper_py_path + "\" --region " + str(min_x) + " " + str(max_x) + " " + str(min_z) + " " + str(max_z) + " --maxheight "+str(self.mapvars["maxheight"])+" --minheight "+str(self.mapvars["minheight"])+" --pixelspernode "+str(self.mapvars["pixelspernode"])+" \""+minetestinfo.get_var("primary_world_path")+"\" \""+tmp_png_path+"\"" cmd_string = cmd_no_out_string + cmd_suffix if self.minetestmapper_py_path==self.minetestmapper_custom_path:#if self.backend_string!="sqlite3": #if self.mapper_id=="minetestmapper-region": @@ -1016,9 +868,15 @@ class MTChunks: #if os.path.isfile(region_capable_script_path): #script_path = region_capable_script_path geometry_string = str(min_x)+":"+str(min_z)+"+"+str(int(max_x)-int(min_x)+1)+"+"+str(int(max_z)-int(min_z)+1) # +1 since max-min is exclusive and width must be inclusive for minetestmapper.py + geometry_param = " --geometry "+geometry_string #expertmm_region_string = str(min_x) + ":" + str(max_x) + "," + str(min_z) + ":" + str(max_z) #cmd_string="sudo python "+script_path+" --input \""+minetestinfo.get_var("primary_world_path")+"\" --geometry "+geometry_value_string+" --output \""+tmp_png_path+"\""+cmd_suffix - cmd_no_out_string = self.python_exe_path+" "+self.minetestmapper_py_path+" --bgcolor '"+self.FLAG_EMPTY_HEXCOLOR+"' --input \""+minetestinfo.get_var("primary_world_path")+"\" --geometry "+geometry_string+" --output \""+tmp_png_path+"\"" + world_path = minetestinfo.get_var("primary_world_path") + io_string = " --input \""+world_path+"\" --output \""+tmp_png_path+"\"" + #if "numpy" in self.minetestmapper_py_path: + # io_string = " \""+world_path+"\" \""+tmp_png_path+"\"" + # geometry_param = " --region " + str(min_x) + " " + str(max_x) + " " + str(min_z) + " " + str(max_z) + cmd_no_out_string = python_exe_path+" "+self.minetestmapper_py_path+" --bgcolor '"+FLAG_EMPTY_HEXCOLOR+"'"+geometry_param+io_string cmd_string = cmd_no_out_string + cmd_suffix #sudo python /home/owner/minetest/util/minetestmapper.py --bgcolor '#010000' --input "/home/owner/.minetest/worlds/FCAGameAWorld" --output /var/www/html/minetest/chunkymapdata/entire.png > entire-mtmresult.txt #sudo python /home/owner/minetest/util/chunkymap/minetestmapper.py --input "/home/owner/.minetest/worlds/FCAGameAWorld" --geometry 0:0+16+16 --output /var/www/html/minetest/chunkymapdata/chunk_x0z0.png > /home/owner/minetest/util/chunkymap-genresults/chunk_x0z0_mapper_result.txt @@ -1132,7 +990,7 @@ class MTChunks: self.create_chunk_folder(chunky_x, chunky_z) self.chunks[chunk_luid].save_yaml(chunk_yaml_path) print(min_indent+"(saved yaml to '"+chunk_yaml_path+"')") - + def get_new_player_index(self): result = None if self.players is not None: @@ -1300,7 +1158,7 @@ class MTChunks: is_moved = True if is_moved: is_changed = True - + #if (this_player is None) or not is_same_fvec3( map_player_position_tuple, player_position_tuple): #if (this_player is None) or (saved_player_x is None) or (saved_player_z is None) or (int(saved_player_x)!=int(player_x)) or (int(saved_player_y)!=int(player_y)) or (int(saved_player_z)!=int(player_z)): @@ -1511,7 +1369,7 @@ class MTChunks: #must check_decachunk_containing_chunk AFTER _check_map_pseudorecursion_branchfrom so check_decachunk_containing_chunk can see if there are more to do before rendering superchunk #always check since already checks queue and doesn't render decachunk on last rendered chunk, but instead on last queued chunk in decachunk #if self.rendered_this_session_count>prev_rendered_this_session_count or self.force_rerender_decachunks_enable: - + #Now is ok to check_decachunk_containing_chunk, since does not count current index as unfinished (allow_current_chunk_enable=False): self.check_decachunk_containing_chunk(chunky_x, chunky_z) if self.verbose_enable: @@ -1590,7 +1448,7 @@ class MTChunks: player_position = None #if (file_name[:len(badstart_string)]!=badstart_string): if (file_name[:1]!="."): - if len(file_name)>=len(self.genresult_name_opener_string)+4+len(self.genresult_name_closer_string): + if len(file_name)>=len(self.genresult_name_opener_string)+4+len(genresult_name_closer_string): chunk_luid = self.get_chunk_luid_from_genresult_name(file_name) coords = self.get_coords_from_luid(chunk_luid) if coords is not None: diff --git a/colors.txt b/colors.txt index 8a325bb..fdf055c 100644 --- a/colors.txt +++ b/colors.txt @@ -75,4 +75,3 @@ default:apple 200 0 0 default:desert_sand 210 180 50 default:desert_stone 150 100 30 default:dry_shrub 100 80 40 - diff --git a/etc/canvas coords - converting from cartesian tile coords.png b/etc/canvas coords - converting from cartesian tile coords.png new file mode 100644 index 0000000..eee92d3 Binary files /dev/null and b/etc/canvas coords - converting from cartesian tile coords.png differ diff --git a/etc/loading.xcf b/etc/loading.xcf new file mode 100644 index 0000000..83e193a Binary files /dev/null and b/etc/loading.xcf differ diff --git a/expertmm.py b/expertmm.py index af32fab..cecbccb 100644 --- a/expertmm.py +++ b/expertmm.py @@ -5,6 +5,10 @@ import copy verbose_enable = False +os_name = "GNU/Linux" +if os.sep=="\\": + os_name = "windows" + print("Windows detected") #formerly pcttext: #uppercase_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -33,7 +37,7 @@ class InstalledFile: self.file_name=file_name self.source_dir_path=source_dir_path self.dest_dir_path=dest_dir_path - + class ConfigManager: @@ -265,7 +269,7 @@ def save_conf_from_dict(path, this_dict, assignment_operator="=", save_nulls_ena outs.close() except: pass - + def get_list_from_hex(hex_string): results = None if hex_string is not None: @@ -283,7 +287,7 @@ def get_list_from_hex(hex_string): if len(hex_string)-index >= 2: results.append(int(hex_string[index:index+2], 16)) index += 2 - + return results def get_tuple_from_notation(line, debug_src_name=""): diff --git a/minetestinfo.py b/minetestinfo.py index aa48237..ec24a98 100644 --- a/minetestinfo.py +++ b/minetestinfo.py @@ -44,6 +44,7 @@ after_broken["default:stone_with_diamond"] = "default:diamond" #after_broken[""] = "" #after_broken[""] = "" +genresult_name_closer_string = "_mapper_result.txt" loaded_mod_list = list() @@ -55,11 +56,154 @@ user_excluded_mod_count = 0 minetestinfo = ConfigManager(os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmeta.yml"), ":") -os_name="linux" -if (os.path.sep!="/"): - os_name="windows" - print("Windows detected") game_path_from_gameid_dict = {} +FLAG_EMPTY_HEXCOLOR = "#010000" + + +class MTDecaChunk: + + metadata = None + last_changed_utc_second = None + + def __init__(self): + self.metadata = {} + self.metadata["last_saved_utc_second"] = None + self.metadata["luid_list"] = None # what chunks this decachunk contains (as saved to 160px image) + + def load_yaml(self, yml_path): + self.metadata = get_dict_modified_by_conf_file(self.metadata,yml_path,":") + + def save_yaml(self, yml_path): + save_conf_from_dict(yml_path, self.metadata, assignment_operator=":", save_nulls_enable=False) + +class MTChunk: + #x = None + #z = None + metadata = None + is_fresh = None + #luid = None + + def __init__(self): + # NOTE: variables that need to be saved (and only they) should be stored in dict + self.metadata = {} + self.is_fresh = False + + self.metadata["is_empty"] = False # formerly is_marked_empty + self.metadata["is_marked"] = False + self.metadata["width"] = None + self.metadata["height"] = None + self.metadata["image_w"] = None + self.metadata["image_h"] = None + self.metadata["image_left"] = None + self.metadata["image_top"] = None + self.metadata["image_right"] = None + self.metadata["image_bottom"] = None + self.metadata["is_traversed"] = False + self.metadata["tags"] = None + + def load_yaml(self, yml_path): + self.metadata = get_dict_modified_by_conf_file(self.metadata,yml_path,":") + + def save_yaml(self, yml_path): + save_conf_from_dict(yml_path, self.metadata, assignment_operator=":", save_nulls_enable=False) + + #requires output such as from minetestmapper-numpy.py + #returns whether save is needed (whether metadata was changed) + def set_from_genresult(self, this_genresult_path): + #this_genresult_path = mtchunks.get_chunk_genresult_path(chunk_luid) + participle = "getting copy of dict" + try: + is_changed = False + old_meta = get_dict_deepcopy(self.metadata) + if os.path.isfile(this_genresult_path): + #may have data such as: + #Result image (w=16 h=16) 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) + self.metadata["is_marked"] = True + participle = "opening '"+this_genresult_path+"'" + ins = open(this_genresult_path, 'r') + line = True + counting_number = 1 + while line: + participle = "reading line "+str(counting_number) + line = ins.readline() + if line: + line_strip = line.strip() + try: + if ("does not exist" in line_strip): # official minetestmapper.py says "World does not exist" but expertmm fork and minetestmapper-numpy.py say "data does not exist" + self.metadata["is_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: + self.metadata["image_w"]=int(chunks[1].strip()) + except: + print("Bad value for image w:"+str(chunks[1])) + elif chunks[0].strip()=="h": + try: + self.metadata["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 + self.metadata["image_left"]=int(rect_values_list[0].strip()) + self.metadata["image_right"]=int(rect_values_list[1].strip()) + self.metadata["image_top"]=int(rect_values_list[2].strip()) + self.metadata["image_bottom"]=int(rect_values_list[3].strip()) + else: + print("Bad map rect, so ignoring: "+rect_values_string) + elif (len(line_strip)>5) and (line_strip[:5]=="xmin:"): + self.metadata["image_left"] = int(line_strip[5:].strip()) + elif (len(line_strip)>5) and (line_strip[:5]=="xmax:"): + self.metadata["image_right"] = int(line_strip[5:].strip()) + elif (len(line_strip)>5) and (line_strip[:5]=="zmin:"): + #(zmin is bottom since cartesian) + self.metadata["image_bottom"] = int(line_strip[5:].strip()) + elif (len(line_strip)>5) and (line_strip[:5]=="zmax:"): + #(zmax is top since cartesian) + self.metadata["image_top"] = int(line_strip[5:].strip()) + except: + print("#failed to parse line:"+str(line_strip)) + counting_number += 1 + ins.close() + participle = "checking for changes" + is_changed = is_dict_subset(self.metadata, old_meta, False) + except: + print("Could not finish "+participle+" in set_from_genresult:") + view_traceback() + return is_changed def get_gameid_from_game_path(path): result = None diff --git a/nocolors.txt b/nocolors.txt new file mode 100644 index 0000000..abbbea8 --- /dev/null +++ b/nocolors.txt @@ -0,0 +1,189 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +1a +1b +1c +1d +1e +1f +20 +21 + +ethereal:willow_trunk +moretrees:oak_trunk +technic:mineral_lead +moretrees:apple_tree_trunk +ethereal:banana +bushes:BushLeaves1 +bushes:BushLeaves2 +flowers:dandelion_white +ethereal:bush +tsm_pyramids:deco_stone3 +tsm_pyramids:deco_stone2 +whiteshell:whiteshell +ethereal:onion_4 +ethereal:quicksand2 +bushes:youngtree2_bottom +default:stone_with_iron +moretrees:apple_tree_leaves +seacoral:seacoraldirtcyan +ethereal:gray_dirt +seacoral:seacoraldirtlime +youngtrees:youngtree_middle +vines:vine_end +seaplants:seaplantsdirtseagrassgreen +ferns:fern_trunk_big +noairblocks:water_sourcex +ethereal:mushroom_dirt +default:pine_tree +ignore +seacoral:seacoraldirtmagenta +flowers:mushroom_red +ethereal:sandy +ethereal:jungle_dirt +ethereal:mushroom +clams:dirtalgae +flowers:tulip +seacoral:seacoralsandcyan +ethereal:willow_twig +default:sandstonebrick +ferns:tree_fern_leaves_giant +ethereal:orange +moretrees:sequoia_leaves +trunks:twig_9 +trunks:twig_7 +trunks:twig_5 +seacoral:seacoraldirtskyblue +moreores:mineral_silver +ethereal:banana_trunk +moretrees:pine_cone +seacoral:coralredviolet +moretrees:acorn +vines:vine_middle +technic:marble +seacoral:seacoralsandskyblue +ferns:fern_03 +ferns:fern_02 +ferns:fern_01 +moretrees:palm_trunk +seacoral:seacoralsandmagenta +seacoral:seacoralsandlime +default:pine_needles +ferns:fern_trunk_big_top +seaplants:seaplantsdirtkelpbrown +tsm_pyramids:trap +ethereal:birch_trunk +ferns:horsetail_02 +ferns:horsetail_03 +bushes:bushbranches4 +ferns:horsetail_01 +bushes:bushbranches2 +bushes:bushbranches3 +ferns:horsetail_04 +bushes:bushbranches1 +clams:sandalgae +moretrees:oak_leaves +ethereal:mushroom_pore +ferns:tree_fern_leave_big +bakedclay:orange +seacoral:seacoraldirtaqua +moretrees:sequoia_trunk +bakedclay:red +seaplants:seaplantssandkelpgreen +stairs:stair_cobble +ethereal:snowygrass +woodsoils:dirt_with_leaves_1 +moreores:mineral_tin +flowers:seaweed +ethereal:bamboo_leaves +ethereal:palm_trunk +seacoral:seacoralsandaqua +trunks:moss +seaplants:seaplantsdirtseagrassred +default:acacia_tree +default:jungleleaves +flowers:viola +seacoral:seacoralsandredviolet +flowers:rose +ethereal:bamboo +default:stone_with_copper +moretrees:jungletree_leaves_yellow +moretrees:coconut +ethereal:palmleaves +ethereal:green_dirt +seaplants:seaplantssandseagrassgreen +trunks:twig_8 +trunks:moss_fungus +air +flowers:waterlily +ethereal:redwood_leaves +trunks:twig_10 +trunks:twig_11 +trunks:twig_12 +trunks:twig_13 +seacoral:corallime +technic:mineral_zinc +ethereal:strawberry_7 +ethereal:prairie_dirt +default:grass_1 +default:grass_3 +default:grass_2 +default:grass_5 +default:grass_4 +ethereal:coconut +ethereal:bamboo_dirt +cavestuff:desert_pebble_2 +cavestuff:desert_pebble_1 +flowers:geranium +ethereal:bananaleaves +moretrees:jungletree_trunk +flowers:mushroom_brown +bakedclay:grey +default:acacia_leaves +youngtrees:youngtree_bottom +ferns:tree_fern_leave_big_end +lapis:pyrite_ore +flowers:dandelion_yellow +flowers:seaweed_2 +flowers:seaweed_3 +flowers:seaweed_4 +ethereal:orange_leaves +tsm_mines:dummy +moretrees:jungletree_leaves_red +seaplants:seaplantsdirtkelpgreen +ethereal:grove_dirt +tsm_pyramids:spawner_mummy +ethereal:mushroom_trunk +ethereal:fern +ethereal:birch_leaves +moretrees:palm_leaves +seacoral:coralcyan +ethereal:redwood_trunk +seaplants:seaplantssandkelpbrown +youngtrees:youngtree_top +seacoral:seacoraldirtredviolet +seaplants:seaplantssandseagrassred diff --git a/pythoninfo.py b/pythoninfo.py new file mode 100644 index 0000000..f5ca820 --- /dev/null +++ b/pythoninfo.py @@ -0,0 +1,13 @@ +import os +from expertmm import * + +python_exe_path = "python" + +if os_name=="windows": + try: + alt_path = "C:\\python27\python.exe" + if os.path.isfile(alt_path): + python_exe_path = alt_path + #else may be in path--assume installer worked + except: + pass # do nothing diff --git a/singleimage.py b/singleimage.py new file mode 100644 index 0000000..3ac3d74 --- /dev/null +++ b/singleimage.py @@ -0,0 +1,105 @@ +import subprocess +import os +from minetestinfo import * +#python_exe_path is from: +from pythoninfo import * + + +class ChunkymapOfflineRenderer: + + minetestmapper_fast_sqlite_path = None + minetestmapper_custom_path = None + minetestmapper_py_path = None + backend_string = None + world_path = None + world_name = None + + def __init__(self): + + self.backend_string = get_world_var("backend") + + #region the following is also in singleimage.py + self.minetestmapper_fast_sqlite_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmapper-numpy.py") + self.minetestmapper_custom_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmapper-expertmm.py") + self.minetestmapper_py_path = self.minetestmapper_fast_sqlite_path + if (self.backend_string!="sqlite3"): + # Since minetestmapper-numpy has trouble with leveldb: + self.minetestmapper_py_path = self.minetestmapper_custom_path + print("Chose image generator script: "+self.minetestmapper_py_path) + if not os.path.isfile(self.minetestmapper_py_path): + print("ERROR: script does not exist, so exiting "+__file__+".") + sys.exit(2) + self.colors_path = os.path.join(os.path.dirname(os.path.abspath(self.minetestmapper_py_path)), "colors.txt") + if not os.path.isfile(self.colors_path): + print("ERROR: missing '"+self.colors_path+"', so exiting "+__file__+".") + sys.exit(2) + self.world_path = minetestinfo.get_var("primary_world_path") + if not os.path.isdir(self.world_path): + print("ERROR: missing world '"+self.world_path+"', so exiting "+__file__+".") + sys.exit(2) + else: + self.world_name = os.path.basename(self.world_path) + #endregion the following is also in singleimage.py + + def RenderSingleImage(self): + genresults_folder_path = os.path.join( os.path.join(os.path.dirname(os.path.abspath(__file__)), "chunkymap-genresults"), self.world_name) + if not os.path.isdir(genresults_folder_path): + os.makedirs(genresults_folder_path) + genresult_path = os.path.join(genresults_folder_path, "singleimage"+genresult_name_closer_string) + cmd_suffix = " > \""+genresult_path+"\"" + + #blank since singleimage mode: + #geometry_string = str(min_x)+":"+str(min_z)+"+"+str(int(max_x)-int(min_x)+1)+"+"+str(int(max_z)-int(min_z)+1) # +1 since max-min is exclusive and width must be inclusive for minetestmapper.py + #geometry_param = " --geometry "+geometry_string + geometry_param = "" + #expertmm_region_string = str(min_x) + ":" + str(max_x) + "," + str(min_z) + ":" + str(max_z) + + #cmd_no_out_string = python_exe_path+" "+self.minetestmapper_py_path+" --bgcolor '"+self.FLAG_EMPTY_HEXCOLOR+"' --input \""+minetestinfo.get_var("primary_world_path")+"\" --geometry "+geometry_string+" --output \""+tmp_png_path+"\"" + png_name = "singleimage.png" + tmp_png_path = os.path.join(genresults_folder_path, png_name) + squote = "" + if os_name!="windows": + squote = "'" + io_string = " --input \""+self.world_path+"\" --output \""+tmp_png_path+"\"" + if "numpy" in self.minetestmapper_py_path: + io_string = " \""+self.world_path+"\" \""+tmp_png_path+"\"" + #geometry_param = " --region " + str(min_x) + " " + str(max_x) + " " + str(min_z) + " " + str(max_z) + cmd_no_out_string = python_exe_path+" "+self.minetestmapper_py_path+" --bgcolor "+squote+FLAG_EMPTY_HEXCOLOR+squote+io_string + cmd_string = cmd_no_out_string + cmd_suffix + print("") + print("") + print("Running") + print(" "+cmd_string) + print(" # (this may take a while...)") + if os.path.isfile(tmp_png_path): + os.remove(tmp_png_path) + subprocess.call(cmd_string, shell=True) + final_png_path = tmp_png_path + www_chunkymapdata_path = os.path.join(minetestinfo.get_var("www_minetest_path"), "chunkymapdata") + www_chunkymapdata_worlds_path = os.path.join(www_chunkymapdata_path, "worlds") + www_chunkymapdata_world_path = os.path.join(www_chunkymapdata_worlds_path, self.world_name) + if os.path.isfile(tmp_png_path): + if not os.path.isdir(www_chunkymapdata_world_path): + os.makedirs(www_chunkymapdata_world_path) + if minetestinfo.contains("www_minetest_path"): + dest_png_path = os.path.join(www_chunkymapdata_world_path, png_name) + if os.path.isfile(dest_png_path): + os.remove(dest_png_path) + os.rename(tmp_png_path, dest_png_path) + final_png_path = dest_png_path + print("Map image saved to:") + print(" "+final_png_path) + if os.path.isfile(genresult_path): + print("Results:") + print(" "+genresult_path) + mtchunk = MTChunk() + mtchunk.set_from_genresult(genresult_path) + mtchunk.metadata["is_traversed"] = True + dest_yaml_name = "singleimage.yml" + dest_yaml_path = os.path.join(www_chunkymapdata_world_path, dest_yaml_name) + mtchunk.save_yaml(dest_yaml_path) + else: + print("No image could be generated from '"+self.world_path+"'") + +cmor = ChunkymapOfflineRenderer() +cmor.RenderSingleImage() diff --git a/web/chunkymap.php b/web/chunkymap.php index 1208d25..5a1e59f 100644 --- a/web/chunkymap.php +++ b/web/chunkymap.php @@ -55,7 +55,6 @@ $chunkymap_camera_pan_delta=.5; $chunkymap_view_min_zoom=0.0173415299; //1.0/$chunk_dimension_min; //should be a number that would get to exactly 100 eventually if multiplied by chunkymap_zoom_delta repeatedly (such as 0.09765625 if chunkymap_zoom_delta were 2); 0.005 was avoided since tiles used to be 80x80 pixels $chunkymap_view_max_zoom=16585998.48141; //13107200.0; - $decachunk_prefix_string="decachunk_"; $decachunk_prefix_then_x_string=$decachunk_prefix_string."x"; $chunk_prefix_string="chunk_"; @@ -251,6 +250,18 @@ function get_javascript_bool_value($this_bool) { return $result; } +function get_javascript_int_value($this_val) { + $result = $this_val; + if ($this_val === null) { + $result = "null"; + } + else { + $this_val = intval($this_val); + $result = "$this_val"; + } + return $result; +} + //chunk_mode_enable: shows chunk png images instead of decachunk jpg images (slower) //visual_debug_enable: draws colored rectangles based on yml files instead of drawing images function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_mode_enable) { @@ -263,6 +274,7 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_ global $chunkymap_change_zoom_multiplier; global $chunkymap_camera_pan_delta; global $world_name; + global $chunkymapdata_thisworld_path; check_world(); @@ -335,6 +347,43 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_ $tile_x_count = $max_tiley_x-$min_tiley_x+1; $tile_z_count = $max_tiley_z-$min_tiley_z+1; + $si_yml_path = "$chunkymapdata_thisworld_path/singleimage.yml"; + $si_metadata = null; + + $si_left = null; + $si_top = null; + $si_w = null; + $si_h = null; + $si_bottom = null; + $si_right = null; + if (is_file($si_yml_path)) { + $si_metadata = get_dict_from_conf($si_yml_path, ":"); + if ($si_metadata!==null) { + if (isset($si_metadata["image_top"]) and isset($si_metadata["image_bottom"])) { + if ($si_metadata["image_top"]<$si_metadata["image_bottom"]) { + $cartesian_bottom = $si_metadata["image_top"]; + $si_metadata["image_top"] = $si_metadata["image_bottom"]; + $si_metadata["image_bottom"] = $cartesian_bottom; + } + } + if (isset($si_metadata["image_left"])) { $si_left=$si_metadata["image_left"]; } + if (isset($si_metadata["image_top"])) { $si_top=$si_metadata["image_top"]; } + if (isset($si_metadata["image_w"])) { $si_w=$si_metadata["image_w"]; } + if (isset($si_metadata["image_h"])) { $si_h=$si_metadata["image_h"]; } + if (isset($si_metadata["image_bottom"])) { + $si_bottom = $si_metadata["image_bottom"]; + } + elseif (isset($si_metadata["image_top"]) and isset($si_metadata["image_h"])) { + $si_bottom=$si_top-$si_h; //minus since cartesian (which is assured above) + } + if (isset($si_metadata["image_right"])) { + $si_right = $si_metadata["image_right"]; + } + elseif (isset($si_metadata["image_left"]) and isset($si_metadata["image_w"])) { + $si_right=$si_left+$si_w; + } + } + } if ($html4_mode_enable!==true) { echo ' '; echo '