diff --git a/README.md b/README.md index 1bbac8e..a980275 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,15 @@ To avoid this problem, instead derive the paths from the parent paths using your if minetestinfo.contains("primary_world_path"): world_path = minetestinfo.get_var("primary_world_path") world_name = os.path.basename(world_path) + #region alternative method: + gameid = None + game_path = None + if world_path is not None and os.path.isdir(world_path): + gameid=get_world_var("gameid") + if gameid is not None and games_path is not None: + game_path = os.path.join(games_path, gameid) + #endregion alternative method: + ``` * 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 diff --git a/minetestinfo.py b/minetestinfo.py index dd6a13d..ab961d4 100644 --- a/minetestinfo.py +++ b/minetestinfo.py @@ -8,6 +8,8 @@ from expertmm import * #config_path #profile_path +profile_path = None + worldgen_mod_list = list() worldgen_mod_list.append("caverealms") worldgen_mod_list.append("ethereal") @@ -281,6 +283,7 @@ def get_game_path_from_gameid(gameid): def init_minetestinfo(): global dict_entries_modified_count + global profile_path if not minetestinfo.contains("www_minetest_path"): default_www_minetest_path = "/var/www/html/minetest" if os_name=="windows": @@ -305,7 +308,7 @@ def init_minetestinfo(): minetestinfo.prepare_var("www_minetest_path", default_www_minetest_path, "your web server directory (or other folder where minetest website features and data should be placed)") - profile_path = None + if os_name=="windows": profile_path = os.environ['USERPROFILE'] else: @@ -354,7 +357,7 @@ def init_minetestinfo(): colors_repos_folder_path = os.path.join(colors_folder_path, "repos") colors_fragments_folder_path = os.path.join(colors_folder_path, "fragments") head_colors_txt = os.path.join( colors_repos_folder_path, "VenessaE.txt") - + dest_colors_txt = os.path.join( os.path.dirname(os.path.abspath(__file__)), "colors.txt") if not os.path.isfile(dest_colors_txt): print("") @@ -430,7 +433,7 @@ def init_minetestinfo(): line_strip = line.strip() if len(line_strip)>0: exclusions_list.append(line_strip) - + ins.close() print("Listed "+str(len(exclusions_list))+" invisible blocks to exclude using '"+exclusions_name+"'.") else: @@ -439,7 +442,7 @@ def init_minetestinfo(): if this_key in exclusions_list: merged_colors.remove(this_key) print("Removed invisible block '"+this_key+"'") - + save_conf_from_dict(dest_colors_txt, merged_colors, assignment_operator=" ") print("Finished writing "+str(len(merged_colors))+" value(s) to '"+dest_colors_txt+"'") else: diff --git a/u_skin_adder.py b/u_skin_adder.py index 279d3c1..23035f1 100644 --- a/u_skin_adder.py +++ b/u_skin_adder.py @@ -3,23 +3,52 @@ import os from expertmm import * from minetestinfo import * #paths and FLAG_EMPTY_HEXCOLOR = "#010000" from PIL import Image, ImageDraw, ImageFont, ImageColor +import shutil -u_skins_mod_path = "C:\\Users\\jgustafson\\Desktop\\Backup\\fcalocal\\usr\\local\\share\\minetest\\games\\fca_game_a\\mods\\u_skins\\u_skins" +u_skins_mod_path = os.path.join(profile_path,"Desktop\\Backup\\fcalocal\\usr\\local\\share\\minetest\\games\\fca_game_a\\mods\\u_skins\\u_skins") +games_path = os.path.join(minetestinfo.get_var("shared_minetest_path"), "games") + +verbose_enable = True +image_size = (64,32) +preview_size = (16,32) + +visual_debug_enable = False +dump_path = os.path.join(u_skins_mod_path, "debug") # only created or used if visual_debug_enable + +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) + print("Using world '"+world_name+"'") + +#game_name = game_path_from_gameid_dict( +gameid = None +game_path = None +mods_path = None if not os.path.isdir(u_skins_mod_path): - print("Looking for u_skins mod in u_skins modpack...") - u_skins_mod_path = os.path.join( os.path.join( os.path.join(game_path, "mods"), "u_skins" ), "u_skins" ) # get the u_skins mod in the u_skins modpack - print(" trying '"+u_skins_mod_path+"'") + if world_path is not None and os.path.isdir(world_path): + gameid=get_world_var("gameid") + print("Using game '"+str(gameid)+"'") + if gameid is not None and games_path is not None: + game_path = os.path.join(games_path, gameid) + mods_path=os.path.join(game_path, "mods") + print("Using mods_path '"+mods_path+"'") + print("Looking for u_skins mod in u_skins modpack...") + #u_skins_mod_path = os.path.join( os.path.join( os.path.join(game_path, "mods"), "u_skins" ), "u_skins" ) # get the u_skins mod in the u_skins modpack + #print(" trying '"+u_skins_mod_path+"'") + + u_skins_modpack_path = os.path.join(mods_path, "u_skins") + u_skins_mod_path = os.path.join(u_skins_modpack_path, "u_skins") + else: + print("Unknown world, so can't detect game.") meta_path = os.path.join(u_skins_mod_path,"meta") textures_path = os.path.join(u_skins_mod_path,"textures") -in_path = "C:\\Users\\jgustafson\\Downloads\\skins-to-add" image_prefix = "character_" preview_suffix = "_preview" -default_license = "CC BY-SA 3.0" +default_license_string = "CC BY-SA 3.0" -if not os.path.isdir(in_path): - in_path = "." - print("Looking for new textures in current directory") png_count = 0 @@ -27,12 +56,12 @@ class RectTransferInfo: source_rect_tuple = None dest_rect_tuple = None flip_h = None - + def __init__(self, source_rect_tuple, dest_rect_tuple, flip_h): self.flip_h = flip_h self.source_rect_tuple = source_rect_tuple self.dest_rect_tuple = dest_rect_tuple - + rect_trans_list = list() rect_trans_list.append(RectTransferInfo((8,8,8,8),(4,0,8,8),False)) # face rect_trans_list.append(RectTransferInfo((20,20,8,12),(4,8,8,12),False)) # shirt @@ -44,86 +73,187 @@ rect_trans_list.append(RectTransferInfo((4,20,4,12),(8,20,4,12),False)) # leg.l rect_trans_list.append(RectTransferInfo((4,20,4,12),(4,20,4,12),True)) # leg.r (True since on legs, right one must be flipped manually) #yes, the flipping is different for leg vs arm -image_size = (64,32) -preview_size = (16,32) class USkinInfo: author_string = None name_string = None license_name_string = None - + #region temp source_image_path = None #endregion temp - + def __init__(self): pass - + def set_from_skindb_skin_file_path(self, file_path, license_name_string): + self.author_string = None + self.name_string = None + self.license_name_string=license_name_string self.source_image_path = file_path file_name = os.path.basename(self.source_image_path) - self.license_name_string=license_name_string noext_name = file_name dot_index = file_name.rfind(".") if dot_index>=0: noext_name = file_name[:dot_index] by_string = "_by_" by_index = noext_name.rfind(by_string) - print("noext_name:"+noext_name) + #print("noext_name:"+noext_name) if by_index>=0: self.author_string = noext_name[by_index+len(by_string):] self.name_string = noext_name[:by_index] else: self.author_string = "" self.name_string = noext_name - + + def set_from_metadata_path(self, metadata_file_path): + is_ok = False + self.name_string = None + self.author_string = None + self.license_name_string = None + if os.path.isfile(metadata_file_path): + ins = open(metadata_file_path, 'r') + line = True + counting_number = 1 + while line: + participle = "reading line "+str(counting_number) + line = ins.readline() + if line: + line_strip = line.strip() + if len(line_strip)>0: + if self.name_string is None: + self.name_string = line_strip + elif self.author_string is None: + self.author_string = line_strip + is_ok = True + elif self.license_name_string is None: + self.license_name_string = line_strip + counting_number += 1 + ins.close() + if not is_ok: + raw_input("ERROR: Did not find line 2 for name_string in '"+metadata_file_path+"'") + else: + raw_input("Missing '"+metadata_file_path+"' -- press enter to continue...") + return is_ok + def print_dump(self, min_indent): - print(min_indent+"author_string:"+self.author_string) print(min_indent+"name_string:"+self.name_string) - print(min_indent+"author_string:"+self.license_name_string) - + print(min_indent+"author_string:"+self.author_string) + print(min_indent+"license_name_string:"+self.license_name_string) + def _save_metadata(self, metadata_file_path): outs = open(metadata_file_path, 'w') - outs.write(self.author_string+"\n") outs.write(self.name_string+"\n") + outs.write(self.author_string+"\n") outs.write(self.license_name_string+"\n") outs.close() - - - def push_next_skin_file(self): + + + def push_next_skin_file_if_self_is_new(self): + result = False os.listdir(textures_path) this_index = 1 while os.path.isfile( get_png_path_from_index(this_index) ): this_index += 1 - #image_name = get_png_name_from_index(this_index) - image_path = get_png_path_from_index(this_index) - metadata_name = get_metadata_name_from_index(this_index) - metadata_path = os.path.join(meta_path, metadata_name) - #preview_name = get_preview_name_from_index(this_index) - preview_path = get_preview_path_from_index(this_index) - print("saving to image_path:"+image_path) - print("saving to preview_path:"+preview_path) - print("saving to metadata_path:"+metadata_path) - #actually save the skin and metadata files: - print("Not yet implemented.") - self.print_dump(" ") - - preview_im = Image.new("RGBA", preview_size, "#000000") - fill_image_with_transparency(preview_im) - skin_im = Image.open(open(self.source_image_path, 'rb')) # double-open to make sure file is finished writing - #NOTE: PIL automatically closes, otherwise you can do something like https://bytes.com/topic/python/answers/24308-pil-do-i-need-close - #fp = open(file_name, "rb") - #im = Image.open(fp) # open from file object - #im.load() # make sure PIL has read the data - #fp.close() - for rect_trans in rect_trans_list: - partial_im = skin_im.crop(rect_trans.source_rect_tuple) - left, top, right, bottom = rect_trans.dest_rect_tuple - preview_im.paste(partial_im, (left, top)) - - preview_im.save(preview_path) - print("Saved preview to '"+preview_path+"'") - print("") + if not skin_exists(self.name_string, self.author_string): + #image_name = get_png_name_from_index(this_index) + image_path = get_png_path_from_index(this_index) + metadata_name = get_metadata_name_from_index(this_index) + metadata_path = os.path.join(meta_path, metadata_name) + #preview_name = get_preview_name_from_index(this_index) + preview_path = get_preview_path_from_index(this_index) + print("saving to image_path:"+image_path) + print("saving to metadata_path:"+metadata_path) + self._save_metadata(metadata_path) + #actually save the skin and metadata files: + print("saving to preview_path:"+preview_path) + self.print_dump(" ") + + shutil.copy(self.source_image_path, image_path) + result = True + preview_im = Image.new("RGBA", preview_size, "#000000") + fill_image_with_transparency(preview_im) + skin_im = Image.open(open(self.source_image_path, 'rb')) # double-open to make sure file is finished writing + #NOTE: PIL automatically closes, otherwise you can do something like https://bytes.com/topic/python/answers/24308-pil-do-i-need-close + #fp = open(file_name, "rb") + #im = Image.open(fp) # open from file object + #im.load() # make sure PIL has read the data + #fp.close() + for rect_trans in rect_trans_list: + source_left, source_top, source_right, source_bottom = rect_trans.source_rect_tuple + source_right+=source_left + source_bottom+=source_top + pil_source_rect_tuple = source_left, source_top, source_right, source_bottom + + partial_im = skin_im.crop(pil_source_rect_tuple) + + dest_left, dest_top, dest_right, dest_bottom = rect_trans.dest_rect_tuple + dest_right+=dest_left + dest_bottom+=dest_top + pil_dest_rect_tuple = dest_left, dest_top, dest_right, dest_bottom + + preview_im.paste(partial_im, (dest_left, dest_top)) + debug_img_name="debug "+str(pil_source_rect_tuple)+".png" + if visual_debug_enable: + #if visual_debug_enable: + #raw_input("Press enter to save temp cropping images to '"+dump_path+"'") + + if not os.path.isdir(dump_path): + os.makedirs(dump_path) + debug_img_path = os.path.join(dump_path, debug_img_name) + #if not os.path.isfile(debug_img_path): + print(" saving "+debug_img_path) + print(" (after pasting to destination rect "+str(pil_dest_rect_tuple)+")") + partial_im.save(debug_img_path) + + preview_im.save(preview_path) + print("Saved preview to '"+preview_path+"'") + print("") + else: + print("Skin already exists: "+self.name_string+" by "+self.author_string) + +def get_png_path_from_index(this_index): + return os.path.join(textures_path, get_png_name_from_index(this_index)) + +def get_png_name_from_index(this_index): + return image_prefix+str(this_index)+".png" + +def get_preview_path_from_index(this_index): + return os.path.join(textures_path, get_preview_name_from_index(this_index)) + +def get_preview_name_from_index(this_index): + return image_prefix+str(this_index)+preview_suffix+".png" + +def get_metadata_name_from_index(this_index): + return image_prefix+str(this_index)+".txt" + +def get_metadata_path_from_index(this_index): + return os.path.join(meta_path, get_metadata_name_from_index(this_index)) + +print("Loading existing skin metadata to avoid duplication (but ignoring metadata files that do not have pngs)") +si_list = list() +this_index = 1 +while os.path.isfile( get_png_path_from_index(this_index) ): + existing_metadata_path = get_metadata_path_from_index(this_index) + this_si = USkinInfo() + is_ok=this_si.set_from_metadata_path(get_metadata_path_from_index(this_index)) + if is_ok: + #if not skin_exists(this_si.name_string, this_si.author_string): + si_list.append(this_si) + #if verbose_enable: + # print("Added skin metadata:") + # this_si.print_dump(" ") + this_index += 1 +print(" Found metadata for "+str(len(si_list))+" png file(s).") +print(" The functions in "+__file__+" are now ready.") +print(" * These functions mark destination as '"+default_license_string+"' license unless you") +print(" first change the global default_license_string variable") +print(" in your program that has:") +print(" from u_skin_adder import *") +print(" * Skin filename should include _by_ (with underscores) to specify author.") +print(" * Examples:") +print(" load_new_skins_from_folder(folder_path)") +print(" add_skin_if_new(file_path)") def fill_image_with_transparency(im): #modified version of: unutbu. "Python PIL: how to make area transparent in PNG? (answer 7 Dec 2010 at 19:08)" . 7 Dec 2010. 8 Apr 2016. @@ -136,43 +266,85 @@ def fill_image_with_transparency(im): mask=Image.new('L', im.size, color=255) draw=ImageDraw.Draw(mask) - + draw.rectangle(transparent_area, fill=0) im.putalpha(mask) #im.save('/tmp/output.png') -def get_png_path_from_index(this_index): - return os.path.join(textures_path, get_png_name_from_index(this_index)) -def get_png_name_from_index(this_index): - return image_prefix+str(this_index)+".png" +show_no_dest_warnings = True +def skin_exists(name_string, author_string): + global show_no_dest_warnings + #global si_list + result = False + count = 0 + #if verbose_enable: + # print(" Checking for existing "+name_string+" by "+author_string+":") + for si in si_list: + if si.name_string==name_string and si.author_string==author_string: + result = True + break + #else: + # if verbose_enable: + # print(" "+si.name_string+" by "+si.author_string+" is not it.") + count += 1 + if not result: + if count<1: + #if show_no_dest_warnings: + raw_input("WARNING: 0 skins during skin_exists check. Press enter to continue...") + #show_no_dest_warnings = False + return result -def get_preview_path_from_index(this_index): - return os.path.join(textures_path, get_preview_name_from_index(this_index)) -def get_preview_name_from_index(this_index): - return image_prefix+str(this_index)+preview_suffix+".png" -def get_metadata_name_from_index(this_index): - return image_prefix+str(this_index)+".txt" +#if os.path.isdir(meta_path): +# + +#accepts CC BY 3.0 skins, and looks for _by_ in name, followed by author (otherwise puts on author line of metadata txt file in u_skin/meta folder) +def add_skin_if_new(sub_path): + this_usi = USkinInfo() + this_usi.set_from_skindb_skin_file_path(sub_path, default_license_string) + return this_usi.push_next_skin_file_if_self_is_new() + +def load_new_skins_from_folder(in_path): + #in_path = os.path.join(profile_path,"Downloads\\skins-to-add") + #if not os.path.isdir(in_path): + # in_path = "." + # print("Looking for new textures in current directory") -if os.path.isdir(meta_path): - if os.path.isdir(textures_path): - if os.path.isdir(in_path): - folder_path = in_path - for sub_name in os.listdir(folder_path): - sub_path = os.path.join(folder_path,sub_name) - if os.path.isfile(sub_path): - if (sub_name[:1]!="."): - if len(sub_name)>4 and sub_name[-4:]==".png": - this_usi = USkinInfo() - this_usi.set_from_skindb_skin_file_path(sub_path, default_license) - this_usi.push_next_skin_file() + if not skin_exists("Sam 0","Jordach"): + print("") + print("WARNING: Missing 'Sam 0' by 'Jordach'") + print(" among "+str(len(si_list))+" skin(s).") + print(" Only continue if you expected that skin to not be there.") + raw_input(" Press enter to continue...") + + + if os.path.isdir(meta_path): + if os.path.isdir(textures_path): + if os.path.isdir(in_path): + folder_path = in_path + new_count = 0 + found_count = 0 + old_count = 0 + for sub_name in os.listdir(folder_path): + sub_path = os.path.join(folder_path,sub_name) + if os.path.isfile(sub_path): + if (sub_name[:1]!="."): + if len(sub_name)>4 and sub_name[-4:]==".png": + found_count += 1 + if add_skin_if_new(sub_path): + new_count += 1 + else: + old_count += 1 + print("Added "+str(new_count)+" new skins(s) among "+str(found_count)+" discovered in specified source folder.") + if old_count>0: + print(" "+str(old_count)+" (with matching author and title) were already in destination.") + else: + print("ERROR: Failed to get new texture files since in_path does not exist:'"+in_path+"'") else: - print("ERROR: Failed to get new texture files since in_path does not exist:'"+in_path+"'") + print("ERROR: missing textures_path (tried '"+textures_path+"')") else: - print("ERROR: missing textures_path (tried '"+textures_path+"')") -else: - print("ERROR: missing meta_path (tried '"+meta_path+"')") - -input("Press return to exit.") + print("ERROR: missing meta_path (tried '"+meta_path+"')") + +#raw_input("Press return to exit.")