Browse Source

working on modularization and decachunks

plus folder detection and regression suite
master
poikilos 9 years ago
committed by Jacob Gustafson
parent
commit
1b3a4e8cb3
  1. 1
      .gitignore
  2. 229
      chunkymap-regen.py
  3. 374
      expertmm.py
  4. 535
      expertmmregressionsuite.py
  5. 10
      expertmmregressiontmp.py
  6. 2
      install-chunkymap-on-windows.py
  7. 250
      minetestinfo.py
  8. 35
      minetestmeta.py
  9. 15
      web/chunkymap.php

1
.gitignore

@ -2,6 +2,7 @@
chunkymapdata/ chunkymapdata/
chunkymap-genresults/ chunkymap-genresults/
chunkymap.yml chunkymap.yml
minetestmeta.yml
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/

229
chunkymap-regen.py

@ -13,7 +13,7 @@ import time
import shutil import shutil
import math import math
from minetestmeta import * from minetestinfo import *
from expertmm import * from expertmm import *
from PIL import Image, ImageDraw, ImageFont, ImageColor from PIL import Image, ImageDraw, ImageFont, ImageColor
@ -240,9 +240,6 @@ class MTChunks:
is_backend_detected = None is_backend_detected = None
chunkymap_players_name = None chunkymap_players_name = None
chunkymap_players_path = None chunkymap_players_path = None
config = None
config_name = None
config_path = None
data_16px_path = None data_16px_path = None
data_160px_path = None data_160px_path = None
FLAG_EMPTY_HEXCOLOR = "#010000" FLAG_EMPTY_HEXCOLOR = "#010000"
@ -257,21 +254,8 @@ class MTChunks:
self.min_indent = " " self.min_indent = " "
self.decachunks = {} self.decachunks = {}
self.rendered_this_session_count = 0 self.rendered_this_session_count = 0
os_name="linux"
if (os.path.sep!="/"):
os_name="windows"
print("Windows detected")
self.is_backend_detected = False self.is_backend_detected = False
self.mapvars = {} self.mapvars = {}
self.config = {}
self.config_name = "chunkymap.yml"
self.config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), self.config_name)
self.config = get_dict_modified_by_conf_file(self.config, self.config_path, ":")
is_config_changed = False
if not os.path.isfile(self.config_path):
is_config_changed = True
print("Creating '"+self.config_path+"'")
#if self.config is None:
self.mapvars["total_generated_count"] = 0 self.mapvars["total_generated_count"] = 0
self.rendered_count = 0 self.rendered_count = 0
self.preload_all_enable = True self.preload_all_enable = True
@ -283,33 +267,6 @@ class MTChunks:
self.refresh_map_enable = True self.refresh_map_enable = True
self.refresh_players_enable = True self.refresh_players_enable = True
self.chunks = {} self.chunks = {}
if "www_minetest_path" not in self.config.keys():
self.config["www_minetest_path"] = "/var/www/html/minetest"
if os_name=="windows":
self.config["www_minetest_path"] = None
prioritized_try_paths = list()
prioritized_try_paths.append("C:\\wamp\\www")
prioritized_try_paths.append("C:\\www")
prioritized_try_paths.append("C:\\Program Files\\Apache Software Foundation\\Apache2.2\\htdocs")
#prioritized_try_paths.append("C:\\Program Files\\Apache Software Foundation\\Apache2.2\\htdocs\\folder_test\\website")
for try_path in prioritized_try_paths:
try:
if os.path.isdir(try_path):
self.config["www_minetest_path"] = try_path
break
except:
pass
if self.config["www_minetest_path"] is None:
self.config["www_minetest_path"] = os.path.dirname(os.path.abspath(__file__))
input_string = raw_input("Minetest website (blank for ["+self.config["www_minetest_path"]+"]): ")
if (len(input_string)>0):
self.config["www_minetest_path"] = input_string
is_config_changed = True
#print("Set www_minetest_path to '"+self.config["www_minetest_path"]+"'")
#else:
print("Using www_minetest_path '"+self.config["www_minetest_path"]+"'")
print("")
self.refresh_map_seconds = 30 #does one chunk at a time so as not to interrupt player updates too often self.refresh_map_seconds = 30 #does one chunk at a time so as not to interrupt player updates too often
self.refresh_players_seconds = 5 self.refresh_players_seconds = 5
@ -319,83 +276,19 @@ class MTChunks:
input_string = "" input_string = ""
if minetestinfo.get_var("primary_world_path") is not None:
profile_path = None if os.path.isdir(minetestinfo.get_var("primary_world_path")):
if os_name=="windows": print ("Using primary_world_path '"+minetestinfo.get_var("primary_world_path")+"'")
profile_path = os.environ['USERPROFILE'] else:
print ("ERROR: Missing world '"+minetestinfo.get_var("primary_world_path")+"'")
sys.exit(2)
else: else:
profile_path = os.environ['HOME'] print ("ERROR: No primary_world_path")
sys.exit(2)
if "profile_minetest_path" not in self.config.keys():
self.config["profile_minetest_path"] = os.path.join(profile_path,".minetest") #if not os.path.isdir(minetestinfo.get_var("primary_world_path")):
if (os_name=="windows"): # print("(ERROR: missing, so please close immediately and update primary_world_path in '"+minetestinfo._config_path+"' before next run)")
self.config["profile_minetest_path"] = "C:\\games\\Minetest" #print("")
input_string = raw_input("user minetest path containing worlds folder (blank for ["+self.config["profile_minetest_path"]+"]): ")
if (len(input_string)>0):
self.config["profile_minetest_path"] = input_string
is_config_changed = True
print("Using profile_minetest_path '"+self.config["profile_minetest_path"]+"'")
if not os.path.isdir(self.config["profile_minetest_path"]):
print("(WARNING: missing, so please close and update profile_minetest_path in '"+self.config_path+"' before next run)")
print("")
if "worlds_path" not in self.config.keys():
self.config["worlds_path"] = os.path.join(self.config["profile_minetest_path"],"worlds")
is_config_changed = True
auto_chosen_world = False
is_missing_world = False
if "world_path" in self.config.keys():
if not os.path.isdir(self.config["world_path"]):
is_missing_world = True
if ("world_path" not in self.config.keys()) or is_missing_world:
print ("LOOKING FOR WORLDS IN " + self.config["worlds_path"])
for base_path, dirnames, filenames in os.walk(self.config["worlds_path"]):
#for j in range(0,len(dirnames)):
# i = len(dirnames) - 0 - 1
# if dirnames[i][0] == ".":
# print (" SKIPPING "+dirnames[i])
# dirnames.remove_at(i)
world_count = 0
for subdirname in dirnames:
print (" EXAMINING "+subdirname)
if subdirname[0]!=".":
world_count += 1
index = 0
world_number = 0
for subdirname in dirnames:
print (" EXAMINING "+subdirname)
if subdirname[0]!=".":
#if (index == len(dirnames)-1): # skip first one because the one on my computer is big
if (subdirname!="world") or (world_number==(world_count-1)):
self.config["world_path"] = os.path.join(base_path, subdirname) # os.path.join(self.config["worlds_path"], "try7amber")
auto_chosen_world = True
break
world_number += 1
index += 1
if auto_chosen_world:
is_config_changed = True
break
if is_missing_world:
print("MISSING WORLD '"+self.config["world_path"]+"'")
if auto_chosen_world:
print("(so a default was picked below that you can change)")
else:
print("(and no world could be found in worlds_path '"+self.config["worlds_path"]+"')")
input_string = raw_input("World path (or world name if above; blank for ["+self.config["world_path"]+"]): ")
if (len(input_string)>0):
try_path = os.path.join(self.config["worlds_path"], input_string)
this_world_path = input_string
if (not os.path.isdir(this_world_path)) and os.path.isdir(try_path):
this_world_path = try_path
self.config["world_path"] = this_world_path
auto_chosen_world = False
is_config_changed = True
print ("Using world_path '"+self.config["world_path"]+"'")
if not os.path.isdir(self.config["world_path"]):
print("(ERROR: missing, so please close immediately and update world_path in '"+self.config_path+"' before next run)")
print("")
self.python_exe_path = "python" self.python_exe_path = "python"
if os_name=="windows": if os_name=="windows":
@ -408,7 +301,7 @@ class MTChunks:
pass # do nothing pass # do nothing
worldmt_path = os.path.join(self.config["world_path"], "world.mt") worldmt_path = os.path.join(minetestinfo.get_var("primary_world_path"), "world.mt")
self.backend_string="sqlite3" self.backend_string="sqlite3"
if (os.path.isfile(worldmt_path)): if (os.path.isfile(worldmt_path)):
ins = open(worldmt_path, 'r') ins = open(worldmt_path, 'r')
@ -443,13 +336,14 @@ class MTChunks:
print("Chose image generator script: "+self.minetestmapper_py_path) print("Chose image generator script: "+self.minetestmapper_py_path)
if not os.path.isfile(self.minetestmapper_py_path): if not os.path.isfile(self.minetestmapper_py_path):
print("ERROR: script does not exist, so exiting "+__file__+".") print("ERROR: script does not exist, so exiting "+__file__+".")
sys.exit() sys.exit(2)
self.colors_path = os.path.join(os.path.dirname(os.path.abspath(self.minetestmapper_py_path)), "colors.txt") 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): if not os.path.isfile(self.colors_path):
print("ERROR: missing '"+self.colors_path+"', so exiting "+__file__+".") print("ERROR: missing '"+self.colors_path+"', so exiting "+__file__+".")
sys.exit() sys.exit(2)
self.chunkymap_data_path=os.path.join(self.config["www_minetest_path"],"chunkymapdata") self.chunkymap_data_path=os.path.join(minetestinfo.get_var("www_minetest_path"),"chunkymapdata")
self.chunkymapdata_worlds_path=os.path.join(self.chunkymap_data_path, "worlds") self.chunkymapdata_worlds_path=os.path.join(self.chunkymap_data_path, "worlds")
print("Using chunkymap_data_path '"+self.chunkymap_data_path+"'") print("Using chunkymap_data_path '"+self.chunkymap_data_path+"'")
#if not os.path.isdir(self.chunkymap_data_path): #if not os.path.isdir(self.chunkymap_data_path):
@ -470,9 +364,7 @@ class MTChunks:
self.deny_http_access(self.chunkymapdata_worlds_path) self.deny_http_access(self.chunkymapdata_worlds_path)
print(" (created .htaccess)") print(" (created .htaccess)")
self.world_name = os.path.basename(minetestinfo.get_var("primary_world_path"))
self.world_name = os.path.basename(self.config["world_path"])
self.chunkymap_thisworld_data_path = os.path.join(self.chunkymapdata_worlds_path, self.world_name) self.chunkymap_thisworld_data_path = os.path.join(self.chunkymapdata_worlds_path, self.world_name)
if not os.path.isdir(self.chunkymap_thisworld_data_path): if not os.path.isdir(self.chunkymap_thisworld_data_path):
os.makedirs(self.chunkymap_thisworld_data_path) os.makedirs(self.chunkymap_thisworld_data_path)
@ -566,8 +458,6 @@ class MTChunks:
self.mapvars["max_chunkz"] = 0 self.mapvars["max_chunkz"] = 0
if is_mapvars_changed: if is_mapvars_changed:
self.save_mapvars_if_changed() self.save_mapvars_if_changed()
if is_config_changed:
self.save_config()
#def install_default_world_data(self): #def install_default_world_data(self):
#source_web_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "web") #source_web_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "web")
@ -582,8 +472,8 @@ class MTChunks:
source_web_chunkymapdata_path = os.path.join(source_web_path, "chunkymapdata_default") source_web_chunkymapdata_path = os.path.join(source_web_path, "chunkymapdata_default")
source_web_chunkymapdata_world_path = os.path.join(source_web_chunkymapdata_path, "world") source_web_chunkymapdata_world_path = os.path.join(source_web_chunkymapdata_path, "world")
source_web_chunkymapdata_images_path = os.path.join(source_web_chunkymapdata_path, "images") source_web_chunkymapdata_images_path = os.path.join(source_web_chunkymapdata_path, "images")
dest_web_path = self.config["www_minetest_path"] dest_web_path = minetestinfo.get_var("www_minetest_path")
dest_web_chunkymapdata_path = os.path.join(self.config["www_minetest_path"],"chunkymapdata") dest_web_chunkymapdata_path = os.path.join(minetestinfo.get_var("www_minetest_path"),"chunkymapdata")
dest_web_chunkymapdata_images_path = os.path.join(dest_web_chunkymapdata_path,"images") dest_web_chunkymapdata_images_path = os.path.join(dest_web_chunkymapdata_path,"images")
install_list = list() install_list = list()
install_list.append(InstalledFile("browser.php",source_web_path,dest_web_path)) install_list.append(InstalledFile("browser.php",source_web_path,dest_web_path))
@ -596,6 +486,12 @@ class MTChunks:
install_list.append(InstalledFile("start.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) 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("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("compass-rose.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))
install_list.append(InstalledFile("arrow-wide-right.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("chunk-blank.jpg", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("decachunk-blank.jpg", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
source_chunkymapdata_players = os.path.join(source_web_chunkymapdata_world_path, "players") source_chunkymapdata_players = os.path.join(source_web_chunkymapdata_world_path, "players")
dest_chunkymapdata_players = os.path.join(self.chunkymap_thisworld_data_path, "players") dest_chunkymapdata_players = os.path.join(self.chunkymap_thisworld_data_path, "players")
install_list.append(InstalledFile("singleplayer.png", source_chunkymapdata_players, dest_chunkymapdata_players)) install_list.append(InstalledFile("singleplayer.png", source_chunkymapdata_players, dest_chunkymapdata_players))
@ -616,7 +512,7 @@ class MTChunks:
if source_mtime_seconds>installed_mtime_seconds: if source_mtime_seconds>installed_mtime_seconds:
shutil.copyfile(source_path, installed_path) # DOES replace destination file shutil.copyfile(source_path, installed_path) # DOES replace destination file
else: else:
print("WARNING: cannot update file since can't find '"+source_path+"'") raw_input("WARNING: cannot update file since can't find '"+source_path+"'")
def deny_http_access(self, dir_path): def deny_http_access(self, dir_path):
@ -634,8 +530,6 @@ class MTChunks:
outs.write("</Files>"+"\n") outs.write("</Files>"+"\n")
outs.close() outs.close()
def save_config(self):
save_conf_from_dict(self.config_path, self.config, ":")
#locally unique identifier (unique to world only) #locally unique identifier (unique to world only)
def get_chunk_luid(self, chunky_x, chunky_z): def get_chunk_luid(self, chunky_x, chunky_z):
@ -696,9 +590,9 @@ class MTChunks:
decachunky_x = self.get_decachunky_coord_from_chunky_coord(chunky_x) decachunky_x = self.get_decachunky_coord_from_chunky_coord(chunky_x)
decachunky_z = self.get_decachunky_coord_from_chunky_coord(chunky_z) decachunky_z = self.get_decachunky_coord_from_chunky_coord(chunky_z)
chunky_min_x = decachunky_x*10 chunky_min_x = decachunky_x*10
chunky_max_x = chunky_min_x + 15 # NOTE: + 15 even if negative since originally, floor was used chunky_max_x = chunky_min_x + 9 # NOTE: ADD even if negative since originally, floor was used
chunky_min_z = decachunky_z*10 chunky_min_z = decachunky_z*10
chunky_max_z = chunky_min_z + 15 # NOTE: + 15 even if negative since originally, floor was used chunky_max_z = chunky_min_z + 9 # NOTE: ADD even if negative since originally, floor was used
x_chunky_count = chunky_max_x-chunky_min_x+1 x_chunky_count = chunky_max_x-chunky_min_x+1
z_chunky_count = chunky_max_z-chunky_min_z+1 z_chunky_count = chunky_max_z-chunky_min_z+1
is_any_part_queued = False is_any_part_queued = False
@ -706,6 +600,7 @@ class MTChunks:
queued_chunk_coords = None queued_chunk_coords = None
chunky_offset_z = 0 chunky_offset_z = 0
chunky_z = chunky_min_z chunky_z = chunky_min_z
queued_index = None
while chunky_z <= chunky_max_z: while chunky_z <= chunky_max_z:
preview_strings[chunky_offset_z] = "" preview_strings[chunky_offset_z] = ""
chunky_x = chunky_min_x chunky_x = chunky_min_x
@ -716,6 +611,10 @@ class MTChunks:
for index in range(self.todo_index,len(self.todo_positions)): for index in range(self.todo_index,len(self.todo_positions)):
if ivec2_equals(self.todo_positions[index], coords): if ivec2_equals(self.todo_positions[index], coords):
is_any_part_queued = True is_any_part_queued = True
if queued_chunk_coords is None:
queued_chunk_coords = list()
queued_chunk_coords.append(coords)
queued_index = index
break break
if is_any_part_queued: if is_any_part_queued:
break break
@ -730,10 +629,10 @@ class MTChunks:
print("") print("")
print(" Rendering 160px decachunk "+str((decachunky_x, decachunky_z))) print(" Rendering 160px decachunk "+str((decachunky_x, decachunky_z)))
if self.verbose_enable: if self.verbose_enable:
print(" USING ("+str(len(chunky_coord_list))+") chunks (region "++":"++","++":"++"): "+str(chunky_coord_list)) 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)+"): "+str(chunky_coord_list))
print("") print("")
else: else:
print(" USING ("+str(len(chunky_coord_list))+") chunks") 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 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), self.FLAG_EMPTY_HEXCOLOR)
decachunk_yaml_path = self.get_decachunk_yaml_path_from_decachunk(decachunky_x, decachunky_z) decachunk_yaml_path = self.get_decachunk_yaml_path_from_decachunk(decachunky_x, decachunky_z)
@ -763,17 +662,17 @@ class MTChunks:
im.paste(chunk_im, offset) im.paste(chunk_im, offset)
contains_chunk_luids.append(self.get_chunk_luid(chunky_x, chunky_z)) contains_chunk_luids.append(self.get_chunk_luid(chunky_x, chunky_z))
except: except:
print("Could not finish "+participle+" in check_decachunk_containing_chunk:") print(self.min_indent+"Could not finish "+participle+" in check_decachunk_containing_chunk:")
view_traceback() view_traceback()
else: else:
preview_strings[chunky_offset_z] += "0" preview_strings[chunky_offset_z] += "0"
chunky_offset_z = z_chunky_count - 1 chunky_offset_z = z_chunky_count - 1
try: try:
print(self.min_indent+"Usable chunk images mask:") print(self.min_indent+"Usable chunk images mask (height:"+str(z_chunky_count)+"):")
while chunky_offset_z>=0: while chunky_offset_z>=0:
if preview_strings[chunky_offset_z] is None: if preview_strings[chunky_offset_z] is None:
preview_strings[chunky_offset_z] = "<None>" preview_strings[chunky_offset_z] = "<None>"
print(self.min_indent+" "+preview_strings[chunky_offset_z]) print(self.min_indent+" "+str(chunky_offset_z)+":"+preview_strings[chunky_offset_z])
chunky_offset_z -= 1 chunky_offset_z -= 1
except: except:
print(self.min_indent+"Could not finish showing mask (this should never happen)") print(self.min_indent+"Could not finish showing mask (this should never happen)")
@ -788,10 +687,10 @@ class MTChunks:
decachunk_folder_path = self.get_decachunk_folder_path_from_decachunk(decachunky_x, decachunky_z) decachunk_folder_path = self.get_decachunk_folder_path_from_decachunk(decachunky_x, decachunky_z)
if not os.path.isdir(decachunk_folder_path): if not os.path.isdir(decachunk_folder_path):
os.makedirs(decachunk_folder_path) os.makedirs(decachunk_folder_path)
print(" Made folder '"+decachunk_folder_path+"'") print(self.min_indent+"Made folder '"+decachunk_folder_path+"'")
else: else:
print(" Found folder '"+decachunk_folder_path+"'") print(self.min_indent+"Found folder '"+decachunk_folder_path+"'")
print(" Saving '"+decachunk_image_path+"'") print(self.min_indent+"Saving '"+decachunk_image_path+"'")
im.save(decachunk_image_path) im.save(decachunk_image_path)
decachunk_luid = self.get_decachunk_luid_from_decachunk(decachunky_x, decachunky_z) decachunk_luid = self.get_decachunk_luid_from_decachunk(decachunky_x, decachunky_z)
self.prepare_decachunk_meta_from_decachunk(decachunky_x, decachunky_z) self.prepare_decachunk_meta_from_decachunk(decachunky_x, decachunky_z)
@ -804,7 +703,8 @@ class MTChunks:
self.decachunks[decachunk_luid].metadata["contains_chunk_luids"] = None self.decachunks[decachunk_luid].metadata["contains_chunk_luids"] = None
self.decachunks[decachunk_luid].save_yaml(decachunk_yaml_path) self.decachunks[decachunk_luid].save_yaml(decachunk_yaml_path)
else: else:
print("Not rendering decachunk "+str((decachunky_x,decachunky_z))+" yet since contains queued chunk "+str(queued_chunk_coords)+".") print(self.min_indent+"Not rendering decachunk "+str((decachunky_x,decachunky_z))+" yet since contains queued chunk "+str(queued_chunk_coords))
print(self.min_indent+" (index:["+str(queued_index)+"]; len:"+str(len(self.todo_positions))+") .")
def get_chunk_folder_path(self, chunky_x, chunky_z): def get_chunk_folder_path(self, chunky_x, chunky_z):
result = None result = None
@ -829,7 +729,7 @@ class MTChunks:
if decachunky_x is not None and decachunky_z is not None: if decachunky_x is not None and decachunky_z is not None:
hectochunky_x = int(math.floor(float(decachunky_x)/10.0)) hectochunky_x = int(math.floor(float(decachunky_x)/10.0))
hectochunky_z = int(math.floor(float(decachunky_z)/10.0)) hectochunky_z = int(math.floor(float(decachunky_z)/10.0))
result = os.path.join( os.path.join(self.data_160px_path, str(hectochunky_x)), str(hectochunky_x) ) result = os.path.join( os.path.join(self.data_160px_path, str(hectochunky_x)), str(hectochunky_z) )
return result return result
def create_chunk_folder(self, chunky_x, chunky_z): def create_chunk_folder(self, chunky_x, chunky_z):
@ -1001,7 +901,7 @@ class MTChunks:
cmd_suffix = "" cmd_suffix = ""
cmd_suffix = " > \""+genresult_path+"\"" cmd_suffix = " > \""+genresult_path+"\""
#self.mapper_id = "minetestmapper-region" #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"])+" \""+self.config["world_path"]+"\" \""+tmp_png_path+"\"" 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_string = cmd_no_out_string + cmd_suffix 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": if self.minetestmapper_py_path==self.minetestmapper_custom_path:#if self.backend_string!="sqlite3": #if self.mapper_id=="minetestmapper-region":
@ -1011,7 +911,7 @@ class MTChunks:
# mapper_id = "minetest-mapper" # mapper_id = "minetest-mapper"
# NOTE: minetest-mapper is part of the minetest-data package, which can be installed alongside the git version of minetestserver # NOTE: minetest-mapper is part of the minetest-data package, which can be installed alongside the git version of minetestserver
# BUT *buntu Trusty version of it does NOT have geometry option # BUT *buntu Trusty version of it does NOT have geometry option
# cmd_string = "/usr/games/minetest-mapper --input \""+self.config["world_path"]+"\" --draworigin --geometry "+geometry_value_string+" --output \""+tmp_png_path+"\""+cmd_suffix # cmd_string = "/usr/games/minetest-mapper --input \""+minetestinfo.get_var("primary_world_path")+"\" --draworigin --geometry "+geometry_value_string+" --output \""+tmp_png_path+"\""+cmd_suffix
# such as sudo python minetestmapper --input "/home/owner/.minetest/worlds/FCAGameAWorld" --geometry -32:-32+64+64 --output /var/www/html/minetest/try1.png # such as sudo python minetestmapper --input "/home/owner/.minetest/worlds/FCAGameAWorld" --geometry -32:-32+64+64 --output /var/www/html/minetest/try1.png
# OR try PYTHON version (looks for expertmm fork which has geometry option like C++ version does): # OR try PYTHON version (looks for expertmm fork which has geometry option like C++ version does):
#script_path = "/home/owner/minetest/util/minetestmapper.py" #script_path = "/home/owner/minetest/util/minetestmapper.py"
@ -1023,8 +923,8 @@ class MTChunks:
#script_path = 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_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
#expertmm_region_string = str(min_x) + ":" + str(max_x) + "," + str(min_z) + ":" + str(max_z) #expertmm_region_string = str(min_x) + ":" + str(max_x) + "," + str(min_z) + ":" + str(max_z)
#cmd_string="sudo python "+script_path+" --input \""+self.config["world_path"]+"\" --geometry "+geometry_value_string+" --output \""+tmp_png_path+"\""+cmd_suffix #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 \""+self.config["world_path"]+"\" --geometry "+geometry_string+" --output \""+tmp_png_path+"\"" 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+"\""
cmd_string = cmd_no_out_string + cmd_suffix 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/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 #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
@ -1137,7 +1037,7 @@ class MTChunks:
def check_players(self): def check_players(self):
print("PROCESSING PLAYERS") print("PROCESSING PLAYERS")
players_path = os.path.join(self.config["world_path"], "players") players_path = os.path.join(minetestinfo.get_var("primary_world_path"), "players")
player_count = 0 player_count = 0
player_written_count = 0 player_written_count = 0
players_moved_count = 0 players_moved_count = 0
@ -1368,20 +1268,22 @@ class MTChunks:
#print(min_indent+chunk_luid+": Not rendered on dest.") #print(min_indent+chunk_luid+": Not rendered on dest.")
return result return result
def _check_map_pseudorecursion_branchfrom(self, chunky_x, chunky_z): def _check_map_pseudorecursion_branchfrom(self, chunky_x, chunky_z):
chunk_luid = self.get_chunk_luid(chunky_x, chunky_z) chunk_luid = self.get_chunk_luid(chunky_x, chunky_z)
branched_pos = chunky_x-1, chunky_z branched_pos = chunky_x-1, chunky_z
#only add if not in list already, to prevent infinite re-branching #only add if not in list already, to prevent infinite re-branching
if branched_pos not in self.todo_positions: if vec2_not_in(branched_pos,self.todo_positions):
self.todo_positions.append(branched_pos) self.todo_positions.append(branched_pos)
branched_pos = chunky_x+1, chunky_z branched_pos = chunky_x+1, chunky_z
if branched_pos not in self.todo_positions: if vec2_not_in(branched_pos, self.todo_positions):
self.todo_positions.append(branched_pos) self.todo_positions.append(branched_pos)
branched_pos = chunky_x, chunky_z-1 branched_pos = chunky_x, chunky_z-1
if branched_pos not in self.todo_positions: if vec2_not_in(branched_pos, self.todo_positions):
self.todo_positions.append(branched_pos) self.todo_positions.append(branched_pos)
branched_pos = chunky_x, chunky_z+1 branched_pos = chunky_x, chunky_z+1
if branched_pos not in self.todo_positions: if vec2_not_in(branched_pos, self.todo_positions):
self.todo_positions.append(branched_pos) self.todo_positions.append(branched_pos)
def check_map_pseudorecursion_iterate(self): # , redo_empty_enable=False): def check_map_pseudorecursion_iterate(self): # , redo_empty_enable=False):
@ -1449,7 +1351,6 @@ class MTChunks:
return result return result
def apply_auto_tags_by_worldgen_mods(self, chunky_x, chunky_z): def apply_auto_tags_by_worldgen_mods(self, chunky_x, chunky_z):
chunk_luid = self.get_chunk_luid(chunky_x, chunky_z) chunk_luid = self.get_chunk_luid(chunky_x, chunky_z)
if chunk_luid not in self.chunks.keys(): if chunk_luid not in self.chunks.keys():
self.prepare_chunk_meta(chunky_x, chunky_z) self.prepare_chunk_meta(chunky_x, chunky_z)
@ -1463,12 +1364,16 @@ class MTChunks:
tags_list[index]=tags_list[index].strip() tags_list[index]=tags_list[index].strip()
else: else:
tags_list = list() tags_list = list()
#TODO: finish this
for mod_name in mtmeta.worldgen_mod_list: for mod_name in worldgen_mod_list:
mod_path = os.path.join(mtmeta.config["mods_path"], mod_name) if mod_name in loaded_mod_list:
#if os.path.isdir(mod_path) if mod_name not in tags_list:
#if is_changed: tags_list.append(mod_name)
# self.save_chunk_meta(chunky_x, chunky_z) is_changed = True
if is_changed:
self.chunks[chunk_luid].metadata["tags"] = ','.join(tags_list)
self.save_chunk_meta(chunky_x, chunky_z)
def correct_genresults_paths(self): def correct_genresults_paths(self):
count = 0 count = 0

374
expertmm.py

@ -2,6 +2,26 @@ import os
import sys import sys
import traceback import traceback
verbose_enable = False
#formerly pcttext:
#uppercase_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#lowercase_chars = uppercase_chars.lower()
#letter_chars = uppercase_chars+lowercase_chars
digit_chars = "0123456789"
#identifier_chars = letter_chars+"_"+digit_chars
#identifier_and_dot_chars = identifier_chars + "."
#formerly from expertmmregressionsuite:
alpha_upper_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
alpha_lower_chars = alpha_upper_chars.lower()
alpha_chars = alpha_upper_chars+alpha_lower_chars
#numeric_chars = "1234567890"
alnum_chars = alpha_chars+digit_chars
identifier_chars = alnum_chars+"_"
identifier_and_dot_chars = identifier_chars+"."
class InstalledFile: class InstalledFile:
source_dir_path = None source_dir_path = None
dest_dir_path = None dest_dir_path = None
@ -12,7 +32,7 @@ class InstalledFile:
self.source_dir_path=source_dir_path self.source_dir_path=source_dir_path
self.dest_dir_path=dest_dir_path self.dest_dir_path=dest_dir_path
class ConsoleInputConfigFile: class ConfigManager:
#config_name = None #config_name = None
_config_path = None _config_path = None
_data = None _data = None
@ -25,29 +45,49 @@ class ConsoleInputConfigFile:
self._ao = assignment_operator_string self._ao = assignment_operator_string
self._data = get_dict_modified_by_conf_file(self._data, self._config_path, self._ao) self._data = get_dict_modified_by_conf_file(self._data, self._config_path, self._ao)
#DOES ask for user input if does not exist #DOES ask for user input if does not exist. If default_value is none, do not add to _data if not given
def ask_for_value_if_not_loaded(name, default_value, description): def load_var_or_ask_console_input(self, name, default_value, description):
is_changed = False
if name not in self._data: if name not in self._data:
print("")
if default_value is None: if default_value is None:
print("WARNING: this program does not have a default value for "+name+".")
default_value = "" default_value = ""
answer = raw_input("Please specify "+description+" [blank for "+default_value+"]: ") answer = raw_input("Please enter "+description+" ("+name+") [blank for "+default_value+"]: ")
if answer is not None: if answer is not None:
answer = answer.strip() answer = answer.strip()
if len(answer)>0:
self_data[name] = answer if answer is not None and len(answer)>0:
else: self._data[name] = answer
self_data[name] = default_value else:
self._data[name] = default_value
print("Using "+name+" '"+self._data[name]+"'")
is_changed = True
if not os.path.isfile(self._config_path): if not os.path.isfile(self._config_path):
is_changed = True is_changed = True
print("Creating '"+self._config_path+"'") print("Creating '"+self._config_path+"'")
if is_changed: if is_changed:
self.save_yaml() self.save_yaml()
def prepare_var(self, name, default_value, description):
self.load_var_or_ask_console_input(name, default_value, description)
def contains(self, name):
return (name in self._data.keys())
def remove_var(self, name):
try:
del self._data[name]
self.save_yaml()
except KeyError:
pass
#DOES autosave IF different val #DOES autosave IF different val
def set_val(name, val): def set_var(self, name, val):
is_changed = False is_changed = False
if name not in self._data.keys(): if name not in self._data.keys():
print("WARNING to developer: run prepare_var before set_val, so that variable has a default.")
is_changed = True is_changed = True
elif self._data[name] != val: elif self._data[name] != val:
is_changed = True is_changed = True
@ -55,13 +95,13 @@ class ConsoleInputConfigFile:
self._data[name] = val self._data[name] = val
self.save_yaml() self.save_yaml()
def get_val(name): def get_var(self, name):
result = None result = None
if name in self._data: if name in self._data:
result = self._data[name] result = self._data[name]
return result return result
def save_yaml() def save_yaml(self):
save_conf_from_dict(self._config_path, self._data, self._ao, save_nulls_enable=False) save_conf_from_dict(self._config_path, self._data, self._ao, save_nulls_enable=False)
def get_dict_deepcopy(old_dict): def get_dict_deepcopy(old_dict):
@ -98,6 +138,15 @@ def is_dict_subset(new_dict, old_dict, verbose_messages_enable, verbose_dest_des
view_traceback() view_traceback()
return is_changed return is_changed
def vec2_not_in(this_list, this_vec):
result = False
if this_list is not None and this_vec is not None:
for try_vec in this_list:
if try_vec[0]==this_vec[0] and try_vec[1]==this_vec[1]:
result = True
break
return result
def ivec2_equals(pos1, pos2): def ivec2_equals(pos1, pos2):
return (int(pos1[0])==int(pos2[0])) and (int(pos1[1])==int(pos2[1])) return (int(pos1[0])==int(pos2[0])) and (int(pos1[1])==int(pos2[1]))
@ -159,7 +208,7 @@ def print_file(path, min_indent=""):
pass pass
return line_count return line_count
def get_dict_modified_by_conf_file(this_dict, path,assignment_operator="="): def get_dict_modified_by_conf_file(this_dict, path, assignment_operator="=", comment_delimiter="#", inline_comments_enable=False):
results = this_dict results = this_dict
#print ("Checking "+str(path)+" for settings...") #print ("Checking "+str(path)+" for settings...")
if (results is None) or (type(results) is not dict): if (results is None) or (type(results) is not dict):
@ -171,8 +220,10 @@ def get_dict_modified_by_conf_file(this_dict, path,assignment_operator="="):
line = ins.readline() line = ins.readline()
if line and len(line)>0: if line and len(line)>0:
line_strip=line.strip() line_strip=line.strip()
if len(line_strip)>0 and not line_strip[0]=="#": # if not comment if len(line_strip)>0 and line_strip[0]!=comment_delimiter: # if not comment
if not line_strip[0]=="-": # ignore yaml arrays if not line_strip[0]=="-": # ignore yaml arrays
if inline_comments_enable:
comment_index = line_strip.find(comment_delimiter)
ao_index = line_strip.find(assignment_operator) ao_index = line_strip.find(assignment_operator)
if ao_index>=1: # intentionally skip zero-length variable names if ao_index>=1: # intentionally skip zero-length variable names
if ao_index<len(line_strip)-1: # skip yaml implicit nulls or yaml objects if ao_index<len(line_strip)-1: # skip yaml implicit nulls or yaml objects
@ -237,3 +288,300 @@ def is_same_fvec3(list_a, list_b):
if len(list_a)>=3 and len(list_b)>=3: if len(list_a)>=3 and len(list_b)>=3:
result = (float(list_a[0]) == float(list_b[0])) and (float(list_a[1]) == float(list_b[1])) and (float(list_a[2]) == float(list_b[2])) result = (float(list_a[0]) == float(list_b[0])) and (float(list_a[1]) == float(list_b[1])) and (float(list_a[2]) == float(list_b[2]))
return False return False
def lastchar(val):
result = None
if (val is not None) and (len(val) > 0):
result = val[len(val)-1]
return result
def get_indent_string(line):
ender_index = find_any_not(line," \t")
result = ""
if ender_index > -1:
result = line[:ender_index]
return result
def is_identifier_valid(val, is_dot_allowed):
result = False
these_id_chars = identifier_chars
if is_dot_allowed:
these_id_chars = identifier_and_dot_chars
for index in range(0,len(val)):
if val[index] in these_id_chars:
result = True
else:
result = False
break
return result
#formerly get_params_len
def get_operation_chunk_len(val, start=0, step=1, line_counting_number=None):
result = 0
openers = "([{"
closers = ")]}"
quotes = "'\""
ender = len(val)
direction_msg = "after opening"
if step < 0:
tmp = openers
openers = closers
closers = tmp
ender = -1
direction_msg = "before closing"
opens = ""
closes = ""
index = start
in_quote = None
line_message = ""
if (line_counting_number is not None) and (line_counting_number>-1):
line_message = "line "+str(line_counting_number)+": "
while (step > 0 and index < ender) or (step < 0 and index > ender):
opener_number = openers.find(val[index])
closer_number = closers.find(val[index])
expected_closer = None
if (len(closes)>0):
expected_closer = lastchar(closes)
quote_number = quotes.find(val[index])
if (in_quote == None) and (opener_number > -1):
opens += openers[opener_number]
closes += closers[opener_number]
elif (in_quote == None) and (closer_number > -1):
if closers[closer_number] == expected_closer:
opens = opens[:len(opens)-1]
closes = closes[:len(closes)-1]
elif quote_number > -1:
if in_quote is None:
in_quote = val[index]
else:
if in_quote == val[index]:
if (index-1 == -1) or (val[index-1]!="\\"):
in_quote = None
index += step
result += 1
if (in_quote is None) and (len(opens)==0) and ((index>=len(val)) or (val[index] not in identifier_and_dot_chars)):
break
return result
def find_identifier(line, identifier_string, start=0):
result = -1
start_index = start
if (identifier_string is not None) and (len(identifier_string) > 0) and (line is not None) and (len(line) > 0):
while True:
try_index = find_unquoted_not_commented(line, identifier_string, start=start_index)
if (try_index > -1):
if ((try_index==0) or (line[try_index-1] not in identifier_chars)) and ((try_index+len(identifier_string)==len(line)) or (line[try_index+len(identifier_string)] not in identifier_chars)):
result = try_index
#input(identifier_string+"starts after '"+line[try_index]+"' ends before '"+line[try_index+len(identifier_string)]+"'")
break
else:
#match is part of a different identifier, so skip it
#input(identifier_string+" does not after '"+line[try_index]+"' ends before '"+line[try_index+len(identifier_string)]+"'")
start_index = try_index + len(identifier_string)
else:
break
return result
def get_newline_in_data(data):
newline = None
cr = "\r"
lf = "\n"
cr_index = -1
lf_index = -1
cr_index = data.find(cr)
lf_index = data.find(lf)
if (cr_index > -1) and (lf_index > -1):
if cr_index < lf_index:
newline = cr+lf
else:
newline = lf+cr
elif cr_index > -1:
newline = cr
elif lf_index > -1:
newline = lf
return newline
def re_escape_visible(val):
result = val.replace("\n","\\n").replace("\n","\\n")
return result
def get_newline(file_path):
data = None
with open (file_path, "r") as myfile:
data=myfile.read()
return get_newline_in_data(data)
def is_allowed_in_variable_name_char(one_char):
result = False
if len(one_char) == 1:
if one_char in identifier_chars:
result = True
else:
print("error in is_allowed_in_variable_name_char: one_char must be 1 character")
return result
def find_any_not(haystack, char_needles, start=None, step = 1):
result = -1
if (len(char_needles)>0) and (len(haystack)>0):
endbefore = len(haystack)
if start is None:
if step > 0:
start = 0
elif step < 0:
start = len(haystack)-1
if step < 0:
endbefore = -1
index = start
while (step>0 and index<endbefore) or (step<0 and index>endbefore):
if not haystack[index:index+1] in char_needles:
result = index
break
index += step
return result
def explode_unquoted(haystack, delimiter):
elements = list()
while True:
index = find_unquoted_not_commented(haystack, delimiter)
if index >= 0:
elements.append(haystack[:index])
haystack = haystack[index+1:]
else:
break
elements.append(haystack) #rest of haystack is the param after last comma, else beginning if none
return elements
#Finds needle in haystack where not quoted, taking into account escape
# sequence for single-quoted or double-quoted string inside haystack.
def find_unquoted_MAY_BE_COMMENTED(haystack, needle, start=0, endbefore=-1, step=1):
result = -1
prev_char = None
if (haystack is not None) and (needle is not None) and (len(needle)>0):
in_quote = None
if endbefore > len(haystack):
endbefore = len(haystack)
if endbefore<0:
endbefore = len(haystack)
index = start
if step<0:
index = endbefore - 1
if verbose_enable:
print(" find_unquoted_not_commented in "+haystack.strip()+":")
while (step>0 and index<=(endbefore-len(needle))) or (step<0 and (index>=0)):
this_char = haystack[index:index+1]
if verbose_enable:
print(" {"
+"index:"+str(index)+";"
+"this_char:"+str(this_char)+";"
+"in_quote:"+str(in_quote)+";"
+"}")
if in_quote is None:
if (this_char == '"') or (this_char == "'"):
in_quote = this_char
elif haystack[index:index+len(needle)] == needle:
result = index
break
else:
if (this_char == in_quote) and (prev_char != "\\"):
in_quote = None
elif haystack[index:index+len(needle)] == needle:
result = index
break
prev_char = this_char
index += step
return result
#DISCARDS whitespace, and never matches None to None
def find_dup(this_list, discard_whitespace_ignore_None_enable=True, ignore_list=None, ignore_numbers_enable=False):
result = -1
if type(this_list) is list:
for i1 in range(0,len(this_list)):
for i2 in range(0,len(this_list)):
i1_strip = None
i2_strip = None
if this_list[i1] is not None:
i1_strip = this_list[i1].strip()
if this_list[i2] is not None:
i2_strip = this_list[i2].strip()
if i1_strip!=None and len(i1_strip)>0 and i2_strip!=None and len(i2_strip)>0:
if (i1!=i2) and (ignore_list is None or i1_strip not in ignore_list) and i1_strip==i2_strip:
number1 = None
#number2 = None
if ignore_numbers_enable:
try:
number1 = int(i1_strip)
except:
try:
number1 = float(i1_strip)
except:
pass
#only need one since they already are known to match as text
#try:
#number2 = int(i2_strip)
#except:
#try:
#number2 = float(i2_strip)
#except:
#pass
if (ignore_numbers_enable and number1 is None) or ((not ignore_numbers_enable)):
result = i2
if verbose_enable:
print("["+str(i1)+"]:"+str(this_list[i1])+" matches ["+str(i2)+"]:"+str(this_list[i2]))
break
if result>-1:
break
else:
input("ERROR in has_dups: "+str(this_list)+" is not a list")
return result
def has_dups(this_list):
return find_dup(this_list)>-1
#region formerly pcttext.py
def find_unquoted_not_commented(haystack, needle, start=0, endbefore=-1, step=1, comment_delimiter="#"):
result = -1
prev_char = None
if (haystack is not None) and (needle is not None) and (len(needle)>0):
in_quote = None
if endbefore > len(haystack):
endbefore = len(haystack)
if endbefore<0:
endbefore = len(haystack)
index = start
if step<0:
index = endbefore - 1
if verbose_enable:
print(" find_unquoted_not_commented in "+haystack.strip()+":")
while (step>0 and index<=(endbefore-len(needle))) or (step<0 and (index>=0)):
this_char = haystack[index:index+1]
if verbose_enable:
print(" {"
+"index:"+str(index)+";"
+"this_char:"+str(this_char)+";"
+"in_quote:"+str(in_quote)+";"
+"}")
if in_quote is None:
if (this_char == comment_delimiter) or (haystack[index:index+3]=="\"\"\""):
break
elif (this_char == '"') or (this_char == "'"):
in_quote = this_char
elif haystack[index:index+len(needle)] == needle:
result = index
break
else:
if (this_char == in_quote) and (prev_char != "\\"):
in_quote = None
elif haystack[index:index+len(needle)] == needle:
result = index
break
prev_char = this_char
index += step
return result
#endregion formerly pcttext.py

535
expertmmregressionsuite.py

@ -12,6 +12,10 @@ module_list.append("keyword")
module_list.append("inspect") module_list.append("inspect")
module_list.append("traceback") module_list.append("traceback")
from expertmm import *
def view_traceback(): def view_traceback():
ex_type, ex, tb = sys.exc_info() ex_type, ex, tb = sys.exc_info()
print(str(ex_type)) print(str(ex_type))
@ -24,7 +28,7 @@ class RegressionMismatch:
side_b_string = None side_b_string = None
endswith_enable = None endswith_enable = None
startswith_enable = None startswith_enable = None
def __init__(self, side_a_string, side_b_string, startswith_enable, endswith_enable): def __init__(self, side_a_string, side_b_string, startswith_enable, endswith_enable):
self.side_a_string = side_a_string self.side_a_string = side_a_string
self.side_b_string = side_b_string self.side_b_string = side_b_string
@ -35,9 +39,23 @@ global_case_sensitive_enable = None
y_enable = False y_enable = False
print("Initializing...") print("Initializing...")
regression_mismatches = list() regression_mismatches = list()
independent_list = ["index","suffix","prefix","decachunk_x_path"] independent_list = ["index","suffix","prefix"]
print(" (Ignoring the following independent variables:") independent_list.append("self")
print(','.join(independent_list)+")") independent_list.append("int")
independent_list.append("float")
independent_list.append("double")
independent_list.append("long")
independent_list.append("bool")
independent_list.append("str")
independent_list.append("string")
independent_list.append("strlen")
independent_list.append("len")
independent_list.append("os.path.join")
independent_list.append("open")
independent_list.append("close")
independent_list.append("None")
independent_list.append("null")
independent_list.append("NULL")
#NOTE: "for decachunk_z_name in os.listdir(decachunk_x_path):" is ok since z folders are in x folder #NOTE: "for decachunk_z_name in os.listdir(decachunk_x_path):" is ok since z folders are in x folder
independent_endswith_list = list() independent_endswith_list = list()
for word in independent_list: for word in independent_list:
@ -83,7 +101,32 @@ if global_case_sensitive_enable is True:
if y_enable: if y_enable:
regression_mismatches.append(RegressionMismatch("X_","Y_",True,False)) regression_mismatches.append(RegressionMismatch("X_","Y_",True,False))
regression_mismatches.append(RegressionMismatch("Y_","Z_",True,False)) regression_mismatches.append(RegressionMismatch("Y_","Z_",True,False))
def is_dependend_variable(name):
result = False
for regression_mismatch in regression_mismatches:
if regression_mismatch.startswith_enable:
if startswith(name, regression_mismatch.side_a_string):
result = True
break
if startswith(name, regression_mismatch.side_b_string):
result = True
break
elif regression_mismatch.endswith_enable:
if endswith(name, regression_mismatch.side_a_string):
result = True
break
if endswith(name, regression_mismatch.side_b_string):
result = True
break
else:
if regression_mismatch.side_a_string in name:
result = True
break
elif regression_mismatch.side_a_string in name:
result = True
break
return result
#splits by any non-alphanumeric characters #splits by any non-alphanumeric characters
#print(keyword.kwlist) #print(keyword.kwlist)
# DOESN'T WORK (from linuxbochs on http://stackoverflow.com/questions/6315496/display-a-list-of-user-defined-functions-in-the-python-idle-session ): # DOESN'T WORK (from linuxbochs on http://stackoverflow.com/questions/6315496/display-a-list-of-user-defined-functions-in-the-python-idle-session ):
@ -103,13 +146,16 @@ print(','.join(function_names)+")")
print("") print("")
print("") print("")
def split_non_alnum(haystack, strip_enable=True, skip_keywords_enable=True): def split_non_alnum(haystack, strip_enable=True, skip_keywords_enable=True):
global independent_list
global function_names
global module_list
results = list() results = list()
index = 0 index = 0
start_index = 0 start_index = 0
while index <= len(haystack): while index <= len(haystack):
if index==len(haystack) or not haystack[index].isalnum(): if index==len(haystack) or not haystack[index].isalnum():
word = haystack[start_index:index] word = haystack[start_index:index]
if (not skip_keywords_enable) or (word not in keyword.kwlist and word not in function_names): if (not skip_keywords_enable) or (word not in keyword.kwlist and word not in function_names and word not in independent_list and word not in module_list):
if strip_enable: if strip_enable:
results.append(word.strip()) results.append(word.strip())
else: else:
@ -118,36 +164,41 @@ def split_non_alnum(haystack, strip_enable=True, skip_keywords_enable=True):
index += 1 index += 1
return results return results
alpha_upper_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
alpha_lower_chars = alpha_upper_chars.lower()
alpha_chars = alpha_upper_chars+alpha_lower_chars
numeric_chars = "1234567890"
alnum_chars = alpha_chars+numeric_chars
identifier_chars = alnum_chars+"_"
issue_count = 0 issue_count = 0
file_list = list() file_list = list()
def is_identifier(needle): #def is_identifier(needle, dot_continues_enable):
result = True # result = True
for index in range(0,len(needle)): #
if needle[index] not in identifier_chars: # for index in range(0,len(needle)):
result = False # if needle[index] not in identifier_chars:
break # result = False
return result # break
# return result
def split_non_identifier(haystack, strip_enable=True, skip_keywords_enable=True, skip_disoriented_enable=True):
quote_list = "'\""
def split_non_identifier(haystack, strip_enable=True, skip_keywords_enable=True, skip_independent_enable=True, dot_continues_enable=True):
results = list() results = list()
index = 0 index = 0
start_index = 0 start_index = 0
is_in_quote_char = None
prev_char = None
escaped_dq_enable = True
while index <= len(haystack): while index <= len(haystack):
if index==len(haystack) or not is_identifier(haystack[index]): if index<len(haystack) and (is_in_quote_char is None) and (haystack[index] in quote_list):
is_in_quote_char = haystack[index]
elif index<len(haystack) and (is_in_quote_char is not None) and (haystack[index]==is_in_quote_char and (prev_char!="\\" or is_in_quote_char!="\"")):
is_in_quote_char = None
elif index==len(haystack) or ((is_in_quote_char is None) and not is_identifier_valid(haystack[index], dot_continues_enable)):
word = haystack[start_index:index] word = haystack[start_index:index]
if (not skip_keywords_enable) or word not in keyword.kwlist: if (not skip_keywords_enable) or word not in keyword.kwlist:
if (not skip_disoriented_enable) or (word not in independent_list and not endswith_any(word, independent_endswith_list)): if (not skip_independent_enable) or (word not in independent_list and not endswith_any(word, independent_endswith_list)):
if strip_enable: if strip_enable:
results.append(word.strip()) results.append(word.strip())
else: else:
results.append(word) results.append(word)
start_index = index+1 start_index = index+1
if index<len(haystack):
prev_char = haystack[index]
index += 1 index += 1
return results return results
@ -209,7 +260,27 @@ def any_startswith(haystacks, needle, case_sensitive_enable=False):
break break
return result return result
#only works on strings IF case_sensitive_enable, since then would do lower() on each element of haystacks def startswith_any(haystack, needles, case_sensitive_enable=False):
if global_case_sensitive_enable is not None:
case_sensitive_enable = global_case_sensitive_enable
result = False
for needle in needles:
if startswith(haystack, needle, case_sensitive_enable):
result = True
break
return result
def endswith_any(haystack, needles, case_sensitive_enable=False):
if global_case_sensitive_enable is not None:
case_sensitive_enable = global_case_sensitive_enable
result = False
for needle in needles:
if endswith(haystack, needle, case_sensitive_enable):
result = True
break
return result
#only works on strings IF case_sensitive_enable, since then would do lower() on each element of haystacks
def in_any_string(needle, haystacks, case_sensitive_enable=False): def in_any_string(needle, haystacks, case_sensitive_enable=False):
if global_case_sensitive_enable is not None: if global_case_sensitive_enable is not None:
case_sensitive_enable = global_case_sensitive_enable case_sensitive_enable = global_case_sensitive_enable
@ -227,13 +298,13 @@ def in_any_string(needle, haystacks, case_sensitive_enable=False):
break break
return result return result
min_indent = "" min_indent = ""
def increase_indent(): def increase_indent():
global min_indent global min_indent
min_indent+=" " min_indent+=" "
def decrease_indent(): def decrease_indent():
global min_indent global min_indent
if len(min_indent)>=2: if len(min_indent)>=2:
@ -251,161 +322,295 @@ def check_coord_mismatch(file_path):
ins = open(file_path, 'r') ins = open(file_path, 'r')
line = True line = True
global min_indent global min_indent
is_imported = False
problematic_line_count = 0
inline_comment_delimiter = "#"
inline_line_break = None
file_path_lower = file_path.lower()
if len(file_path)>3 and (file_path_lower[-3:]==".cs"):
inline_comment_delimiter = "//"
inline_line_break = ";"
elif len(file_path)>2 and (file_path_lower[-2:]==".c"):
inline_comment_delimiter = "//"
inline_line_break = ";"
elif len(file_path)>2 and (file_path_lower[-2:]==".h"):
inline_comment_delimiter = "//"
inline_line_break = ";"
elif len(file_path)>4 and (file_path_lower[-4:]==".hpp"):
inline_comment_delimiter = "//"
inline_line_break = ";"
elif len(file_path)>4 and (file_path_lower[-4:]==".cpp"):
inline_comment_delimiter = "//"
inline_line_break = ";"
elif len(file_path)>4 and (file_path_lower[-4:]==".php"):
inline_comment_delimiter = "//"
inline_line_break = ";"
elif len(file_path)>3 and (file_path_lower[-3:]==".py"):
inline_comment_delimiter = "#"
elif len(file_path)>4 and (file_path_lower[-4:]==".pyw"):
inline_comment_delimiter = "#"
else:
answer=raw_input("what is the inline comment delimiter for the sourcecode file type of "+file_path+" [blank for "+inline_comment_delimiter+"]? ")
if len(answer)>0:
inline_comment_delimiter = answer
dup_ignore_list = function_names+independent_list+module_list
while line: while line:
line = ins.readline() line = ins.readline()
prev_issue_count = issue_count
if line: if line:
line_strip = line.strip() line_strip = line.strip()
if (len(line_strip)>0) and (line_strip[0]!="#"): comment_index = find_unquoted_MAY_BE_COMMENTED(line_strip, inline_comment_delimiter)
ao_index = line_strip.find("=") if comment_index>-1:
import_string = "import " line_strip = line_strip[:comment_index]
from_string = "from " sublines = list()
import_index = -1
if line_strip[:len(import_string)]==import_string:
import_index = 0 if inline_line_break is not None:
elif line_strip[:len(from_string)]==from_string: remaining_string = line_strip
import_string = from_string while True:
import_index = line_strip.find(import_string) inline_line_break_index = find_unquoted_MAY_BE_COMMENTED(remaining_string, inline_line_break) # do NOT check for INLINE COMMENT since already removed it using detected delimiter above
if import_index>=0: if inline_line_break_index<0:
module_string = line_strip[import_index+len(import_string):].strip() break
space_index = module_string.find(" ") sublines.append(remaining_string[:inline_line_break_index])
if space_index>-1: remaining_string = remaining_string[inline_line_break_index+1:]
module_string = module_string[:space_index]
if module_string not in module_list: if len(remaining_string)>0:
module_list.append(module_string) sublines.append(remaining_string)
try: else:
#tmp_tuples = list() sublines.append(line_strip)
import_string = "import "+module_string for subline in sublines:
exec_string = "tmp_tuples = inspect.getmembers("+module_string+", inspect.isroutine)" line_strip = subline
#exec exec_string if (len(line_strip)>0) and (line_strip[0]!="#"):
try_enable = False ao_index = line_strip.find("=")
outs = open('expertmmregressiontmp.py','w') import_string = "import "
outs.write("def get_module_contents():"+"\n") from_string = "from "
outs_indent = " " import_index = -1
outs.write(outs_indent+"results = None"+"\n") if line_strip[:len(import_string)]==import_string:
if try_enable: import_index = 0
outs.write(outs_indent+"try:"+"\n") elif line_strip[:len(from_string)]==from_string:
outs_indent = " " import_string = from_string
outs.write(outs_indent+"import inspect"+"\n") import_index = line_strip.find(import_string)
outs.write(outs_indent+import_string+"\n") if import_index>=0:
outs.write(outs_indent+exec_string+"\n") module_string = line_strip[import_index+len(import_string):].strip()
outs.write(outs_indent+"results = list()"+"\n") space_index = module_string.find(" ")
outs.write(outs_indent+"for function_tuple in tmp_tuples:"+"\n") if space_index>-1:
outs.write(outs_indent+" results.append(function_tuple[0])"+"\n") module_string = module_string[:space_index]
if module_string not in module_list:
outs_indent = " " module_list.append(module_string)
if try_enable: try:
outs.write(outs_indent+"except:"+"\n") #tmp_tuples = list()
outs_indent = " " import_string = "import "+module_string
outs.write(outs_indent+"print(\"Could not finish get_module_contents\")"+"\n") #exec exec_string
outs_indent = " " try_enable = False
outs.write(outs_indent+"return results"+"\n") outs = open('expertmmregressiontmp.py','w')
outs.write("\n") outs.write("def get_module_contents():"+"\n")
outs.close() outs_indent = " "
import expertmmregressiontmp outs.write(outs_indent+"results = None"+"\n")
tmp_list = expertmmregressiontmp.get_module_contents() if try_enable:
new_list = None outs.write(outs_indent+"try:"+"\n")
if tmp_list is not None: outs_indent = " "
new_list = list() outs.write(outs_indent+"import inspect"+"\n")
for routine_string in tmp_list: outs.write(outs_indent+import_string+"\n")
if routine_string not in function_list: exec_string = "tmp_tuples = inspect.getmembers("+module_string+", inspect.isroutine)"
new_list.append(routine_string) outs.write(outs_indent+exec_string+"\n")
if new_list is not None: outs.write(outs_indent+"results = list()"+"\n")
function_list += new_list outs.write(outs_indent+"for function_tuple in tmp_tuples:"+"\n")
print("Found "+str(len(new_list))+" new method(s) from '"+module_string+"' to ignore: "+','.join(new_list)) outs.write(outs_indent+" results.append(function_tuple[0])"+"\n")
else:
print("unable to import module named '"+module_string+"', so some routines may not be successfully ignored:") outs_indent = " "
except: if try_enable:
print("Could not finish importing module named '"+module_string+"', so some routines may not be successfully ignored:") outs.write(outs_indent+"except:"+"\n")
view_traceback() outs_indent = " "
if ao_index<0: outs.write(outs_indent+"print(\"Could not finish get_module_contents\")"+"\n")
ao_index = line_strip.find(">") outs_indent = " "
if ao_index<0: outs.write(outs_indent+"return results"+"\n")
ao_index = line_strip.find("<") outs.write("\n")
if ao_index<0: outs.close()
ao_index = line_strip.find(" in ") if is_imported:
if ao_index>0: # intentionally >0 instead of = reload(expertmmregressiontmp)
increase_indent() else:
names_string = line_strip[:ao_index].strip() import expertmmregressiontmp
values_string = line_strip[ao_index+1:].strip() tmp_list = expertmmregressiontmp.get_module_contents()
name_list = split_non_identifier(names_string) os.remove("expertmmregressiontmp.py")
value_list = split_non_identifier(values_string) new_list = None
message = None if tmp_list is not None:
is_line_problematic = False new_list = list()
for regression_mismatch in regression_mismatches: for routine_string in tmp_list:
both_present = False if routine_string not in function_list:
new_list.append(routine_string)
if new_list is not None:
function_list += new_list
print("Found "+str(len(new_list))+" new method(s) from '"+module_string+"' to ignore: "+','.join(new_list))
if len(new_list)>0:
dup_ignore_list = function_names+independent_list+module_list
else:
print("unable to import module named '"+module_string+"', so some routines may not be successfully ignored:")
del expertmmregressiontmp
except:
print("Could not finish importing module named '"+module_string+"', so some routines may not be successfully ignored:")
view_traceback()
if ao_index<0:
ao_index = line_strip.find(">")
if ao_index<0:
ao_index = line_strip.find("<")
if ao_index<0:
ao_index = line_strip.find(" in ")
if ao_index>0: # intentionally >0 instead of =
increase_indent()
names_string = line_strip[:ao_index].strip()
values_string = line_strip[ao_index+1:].strip()
name_list = split_non_identifier(names_string)
value_list = split_non_identifier(values_string)
message = None
message_prefix = " WARNING: " message_prefix = " WARNING: "
only_mismatched_coord_is_present_string = " ERROR (only has mismatch): " this_start_index = 0
if regression_mismatch.startswith_enable: names_string = line_strip[:ao_index].strip()
if (any_startswith(name_list,regression_mismatch.side_a_string) and not any_startswith(name_list,regression_mismatch.side_b_string)) and any_startswith(value_list,regression_mismatch.side_b_string): values_string = line_strip[ao_index+1:].strip()
name_index = line.find(regression_mismatch.side_a_string) + 1 name_list = split_non_identifier(names_string)
if not both_present: value_list = split_non_identifier(values_string)
both_present = any_startswith(value_list,regression_mismatch.side_a_string)
message_prefix = only_mismatched_coord_is_present_string #while True:
if message is None: #this_chunk_len = get_operation_chunk_len(line_strip,this_start_index,1,line_counting_number)
#if this_chunk_len<1:
#break
#partial_string = line_strip[this_start_index:this_start_index+this_chunk_len]
#this_start_index += this_chunk_len
duplicate_index = find_dup(name_list,ignore_numbers_enable=True)
if duplicate_index>-1:
if name_list[duplicate_index] not in dup_ignore_list:
if is_dependend_variable(name_list[duplicate_index]):
name_index = line.find(name_list[duplicate_index])
message_prefix = " WARNING: "
issue_count += 1
message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"name '"+name_list[duplicate_index]+"' is used twice (perhaps other coord should have been used)")
print(message)
break
duplicate_index = find_dup(value_list)
if duplicate_index>-1:
if value_list[duplicate_index] not in dup_ignore_list:
if is_dependend_variable(value_list[duplicate_index]):
name_index = line.find(value_list[duplicate_index])
message_prefix = " WARNING: "
issue_count += 1
message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"value '"+value_list[duplicate_index]+"' is used twice (perhaps other coord should have been used)")
print(message)
break
message = None
for regression_mismatch in regression_mismatches:
both_present = False
only_mismatched_coord_is_present_string = " ERROR (only has mismatch): "
message_prefix = " WARNING: "
if regression_mismatch.startswith_enable:
if (any_startswith(name_list,regression_mismatch.side_a_string) and not any_startswith(name_list,regression_mismatch.side_b_string)) and any_startswith(value_list,regression_mismatch.side_b_string):
name_index = line.find(regression_mismatch.side_a_string) + 1
if not both_present:
both_present = any_startswith(value_list,regression_mismatch.side_a_string)
message_prefix = only_mismatched_coord_is_present_string
#if message is None:
message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"name starts with "+regression_mismatch.side_a_string+", but "+regression_mismatch.side_b_string+" on right in check/assignment") message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"name starts with "+regression_mismatch.side_a_string+", but "+regression_mismatch.side_b_string+" on right in check/assignment")
is_line_problematic = True print(message)
break issue_count += 1
break
elif (any_startswith(name_list,regression_mismatch.side_b_string) and not any_startswith(name_list,regression_mismatch.side_a_string)) and any_startswith(value_list,regression_mismatch.side_a_string):
name_index = line.find(regression_mismatch.side_b_string) + 1 elif (any_startswith(name_list,regression_mismatch.side_b_string) and not any_startswith(name_list,regression_mismatch.side_a_string)) and any_startswith(value_list,regression_mismatch.side_a_string):
if not both_present: name_index = line.find(regression_mismatch.side_b_string) + 1
both_present = any_startswith(value_list,regression_mismatch.side_b_string) if not both_present:
message_prefix = only_mismatched_coord_is_present_string both_present = any_startswith(value_list,regression_mismatch.side_b_string)
if message is None: message_prefix = only_mismatched_coord_is_present_string
#if message is None:
message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"name starts with "+regression_mismatch.side_b_string+", but "+regression_mismatch.side_a_string+" on right in check/assignment") message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"name starts with "+regression_mismatch.side_b_string+", but "+regression_mismatch.side_a_string+" on right in check/assignment")
is_line_problematic = True print(message)
break issue_count += 1
elif regression_mismatch.endswith_enable: break
if (any_endswith(name_list,regression_mismatch.side_a_string) and not any_endswith(name_list,regression_mismatch.side_b_string)) and any_endswith(value_list,regression_mismatch.side_b_string): elif regression_mismatch.endswith_enable:
name_index = line.find(regression_mismatch.side_a_string) + 1 if (any_endswith(name_list,regression_mismatch.side_a_string) and not any_endswith(name_list,regression_mismatch.side_b_string)) and any_endswith(value_list,regression_mismatch.side_b_string):
if not both_present: name_index = line.find(regression_mismatch.side_a_string) + 1
both_present = any_endswith(value_list,regression_mismatch.side_a_string) if not both_present:
message_prefix = only_mismatched_coord_is_present_string both_present = any_endswith(value_list,regression_mismatch.side_a_string)
if message is None: message_prefix = only_mismatched_coord_is_present_string
#if message is None:
message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"name ends with "+regression_mismatch.side_a_string+", but "+regression_mismatch.side_b_string+" on right in check/assignment") message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"name ends with "+regression_mismatch.side_a_string+", but "+regression_mismatch.side_b_string+" on right in check/assignment")
is_line_problematic = True print(message)
break issue_count += 1
break
elif (any_endswith(name_list,regression_mismatch.side_b_string) and not any_endswith(name_list,regression_mismatch.side_a_string)) and any_endswith(value_list,regression_mismatch.side_a_string):
name_index = line.find(regression_mismatch.side_b_string) + 1 elif (any_endswith(name_list,regression_mismatch.side_b_string) and not any_endswith(name_list,regression_mismatch.side_a_string)) and any_endswith(value_list,regression_mismatch.side_a_string):
if not both_present: name_index = line.find(regression_mismatch.side_b_string) + 1
both_present = any_endswith(value_list,regression_mismatch.side_b_string) if not both_present:
message_prefix = only_mismatched_coord_is_present_string both_present = any_endswith(value_list,regression_mismatch.side_b_string)
if message is None: message_prefix = only_mismatched_coord_is_present_string
message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"name ends with "+regression_mismatch.side_b_string+", but "+regression_mismatch.side_a_string+" on right in check/assignment") if message is None:
is_line_problematic = True message = (file_path+" ("+str(line_counting_number)+","+str(name_index)+")"+message_prefix+"name ends with "+regression_mismatch.side_b_string+", but "+regression_mismatch.side_a_string+" on right in check/assignment")
break print(message)
issue_count += 1
else: break
if (in_any_string(regression_mismatch.side_a_string, name_list) and not in_any_string(regression_mismatch.side_b_string, name_list)) and in_any_string(regression_mismatch.side_b_string, value_list):
value_index = line.find(regression_mismatch.side_a_string) + 1 else:
if not both_present: if (in_any_string(regression_mismatch.side_a_string, name_list) and not in_any_string(regression_mismatch.side_b_string, name_list)) and in_any_string(regression_mismatch.side_b_string, value_list):
both_present = in_any_string(regression_mismatch.side_a_string, value_list) value_index = line.find(regression_mismatch.side_a_string) + 1
message_prefix = only_mismatched_coord_is_present_string if not both_present:
if message is None: both_present = in_any_string(regression_mismatch.side_a_string, value_list)
message_prefix = only_mismatched_coord_is_present_string
#if message is None:
message = (file_path+" ("+str(line_counting_number)+","+str(value_index)+")"+message_prefix+"name contains "+regression_mismatch.side_a_string+", but "+regression_mismatch.side_b_string+" on right in check/assignment") message = (file_path+" ("+str(line_counting_number)+","+str(value_index)+")"+message_prefix+"name contains "+regression_mismatch.side_a_string+", but "+regression_mismatch.side_b_string+" on right in check/assignment")
is_line_problematic = True print(message)
break issue_count += 1
elif (in_any_string(regression_mismatch.side_b_string, name_list) and not in_any_string(regression_mismatch.side_a_string, name_list)) and in_any_string(regression_mismatch.side_a_string, value_list): break
value_index = line.find(regression_mismatch.side_b_string) + 1 elif (in_any_string(regression_mismatch.side_b_string, name_list) and not in_any_string(regression_mismatch.side_a_string, name_list)) and in_any_string(regression_mismatch.side_a_string, value_list):
if not both_present: value_index = line.find(regression_mismatch.side_b_string) + 1
both_present = in_any_string(regression_mismatch.side_b_string, value_list) if not both_present:
message_prefix = only_mismatched_coord_is_present_string both_present = in_any_string(regression_mismatch.side_b_string, value_list)
if message is None: message_prefix = only_mismatched_coord_is_present_string
#if message is None:
message = (file_path+" ("+str(line_counting_number)+","+str(value_index)+")"+message_prefix+"name contains "+regression_mismatch.side_b_string+", but "+regression_mismatch.side_a_string+" on right side of check/assignment") message = (file_path+" ("+str(line_counting_number)+","+str(value_index)+")"+message_prefix+"name contains "+regression_mismatch.side_b_string+", but "+regression_mismatch.side_a_string+" on right side of check/assignment")
is_line_problematic = True print(message)
break issue_count += 1
if message is not None: break
print("") if issue_count>prev_issue_count:
print(message) print("line "+str(line_counting_number)+": "+line.strip())
print(line_strip) if verbose_enable:
if is_line_problematic: print(" ao_index:"+str(ao_index))
issue_count += 1 print(" names: "+','.join(name_list))
print(" values: "+','.join(value_list))
print("")
problematic_line_count += 1
line_counting_number += 1 line_counting_number += 1
ins.close() ins.close()
independent_list.append("decachunk_x_path")
independent_list.append("chunk_assoc")
independent_list.append("chunk_luid")
independent_list.append("file_name")
independent_list.append("chunkymap_view_zoom_multiplier")
independent_list.append("\"0\"")
independent_list.append("temp")
independent_list.append("haystack")
independent_list.append("needle")
independent_list.append("line_strip")
independent_list.append("self.chunks")
independent_list.append("self.mapvars")
independent_list.append(".metadata")
independent_list.append("\" \\\"\"")
independent_list.append("\" \"")
independent_list.append("player_position_tuple")
print(" (Ignoring the following independent variables:")
print(','.join(independent_list)+")")
#verbose_enable = True
check_coord_mismatch("chunkymap-regen.py") check_coord_mismatch("chunkymap-regen.py")
check_coord_mismatch(os.path.join("web","chunkymap.php")) check_coord_mismatch(os.path.join("web","chunkymap.php"))
print("Found "+str(issue_count)+" issue(s) in "+str(len(file_list))+" file(s)") print("Found "+str(issue_count)+" issue(s) in "+str(len(file_list))+" file(s)")
if (issue_count>0):
print("Please run again after these issues are fixed to check for more on same lines.")
raw_input("Press enter to exit...") raw_input("Press enter to exit...")

10
expertmmregressiontmp.py

@ -1,10 +0,0 @@
def get_module_contents():
results = None
import inspect
import PIL
tmp_tuples = inspect.getmembers(PIL, inspect.isroutine)
results = list()
for function_tuple in tmp_tuples:
results.append(function_tuple[0])
return results

2
install-chunkymap-on-windows.py

@ -76,7 +76,7 @@ if is_detected:
detected_msg = " (detected above)" detected_msg = " (detected above)"
print("") print("")
answer = raw_input("Enter #of bits in your python architecture [blank for "+os_bits+detected_msg+"]: ") answer = raw_input("Enter #of bits in your PYTHON architecture (may be different from #of bits of Windows) [blank for "+os_bits+detected_msg+"]: ")
if answer is not None: if answer is not None:
answer = answer.strip() answer = answer.strip()

250
minetestinfo.py

@ -0,0 +1,250 @@
import os
from expertmm import *
#variables to eliminate from chunkymap-regen (and manage here instead):
#os_name
#self.config (use minetestinfo.get_val instead)
#config_name
#config_path
#profile_path
worldgen_mod_list = list()
worldgen_mod_list.append("caverealms")
worldgen_mod_list.append("ethereal")
worldgen_mod_list.append("lapis")
worldgen_mod_list.append("mines")
worldgen_mod_list.append("mg") # this delays/prevents chunk generation and sometimes crashes in 0.4.13 release (tested on Windows 10)
worldgen_mod_list.append("moretrees")
worldgen_mod_list.append("moreores")
#worldgen_mod_list.append("nature_classic") # NOTE: plantlife_modpack has this and other stuff, but detecting this could help since it is unique to the modpack
worldgen_mod_list.append("plantlife_modpack") #ok if installed as modpack instead of putting individual mods in mods folder
worldgen_mod_list.append("pyramids")
worldgen_mod_list.append("railcorridors")
worldgen_mod_list.append("sea")
worldgen_mod_list.append("technic")
worldgen_mod_list.append("technic_worldgen")
worldgen_mod_list.append("tsm_mines")
worldgen_mod_list.append("tsm_pyramids")
worldgen_mod_list.append("tsm_railcorridors")
loaded_mod_list = list()
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")
def init_minetestinfo():
if not minetestinfo.contains("www_minetest_path"):
default_www_minetest_path = "/var/www/html/minetest"
if os_name=="windows":
default_www_minetest_path = None
prioritized_try_paths = list()
prioritized_try_paths.append("C:\\wamp\\www")
prioritized_try_paths.append("C:\\www")
prioritized_try_paths.append("C:\\Program Files\\Apache Software Foundation\\Apache2.2\\htdocs")
prioritized_try_paths.append("C:\\Inetpub\\Wwwroot")
#prioritized_try_paths.append("C:\\Program Files\\Apache Software Foundation\\Apache2.2\\htdocs\\folder_test\\website")
for try_path in prioritized_try_paths:
try:
if os.path.isdir(try_path):
default_www_minetest_path = try_path
break
except:
pass
if default_www_minetest_path is None:
print("WARNING: could not detect website directory automatically. You need WAMP or similar web server with php 5 or higher to use minetest website scripts. You can change www_minetest_path to your server's website root later by editing '"+minetestinfo._config_path+"'")
default_www_minetest_path = os.path.dirname(os.path.abspath(__file__))
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:
profile_path = os.environ['HOME']
default_profile_minetest_path = os.path.join(profile_path,".minetest")
if (os_name=="windows"):
default_profile_minetest_path = "C:\\games\\Minetest"
minetestinfo.prepare_var("profile_minetest_path", default_profile_minetest_path, "user minetest path containing worlds folder and debug.txt")
if not os.path.isdir(minetestinfo.get_var("profile_minetest_path")):
print("(WARNING: missing "+minetestinfo.get_var("profile_minetest_path")+", so please close and update profile_minetest_path in '"+minetestinfo._config_path+"' before next run)")
print("")
if not minetestinfo.contains("worlds_path"):
minetestinfo._data["worlds_path"] = os.path.join(minetestinfo.get_var("profile_minetest_path"),"worlds")
minetestinfo.save_yaml()
default_shared_minetest_path = "/usr/share/games/minetest"
try_path = "/usr/local/share/minetest"
if os_name == "windows":
default_shared_minetest_path = "C:\\Games\\Minetest"
elif os.path.isdir(try_path):
default_shared_minetest_path = try_path
while True:
minetestinfo.prepare_var("shared_minetest_path", default_shared_minetest_path, "path containing Minetest's games folder")
games_path = os.path.join(minetestinfo.get_var("shared_minetest_path"), "games")
if not os.path.isdir(games_path):
answer=raw_input("WARNING: '"+minetestinfo.get_var("shared_minetest_path")+"' does not contain a games folder. If you use this shared_minetest_path, some features may not work correctly (such as adding worldgen mod labels to chunks, and future programs that may use this metadata to install minetest games). Are you sure you want to use y/n [blank for 'n' (no)]? ")
if answer.lower()=="y" or answer.lower()=="yes":
print("You can change the value of shared_minetest_path later by editing '"+minetestinfo._config_path+"'.")
print("")
break
else:
minetestinfo.remove_var("shared_minetest_path")
else:
break
load_world_and_mod_data()
def load_world_and_mod_data():
#if games_path =
global loaded_mod_list
while len(loaded_mod_list) > 0 : loaded_mod_list.pop() # instead of remaking, pop to ensure global is changed
is_world_changed = False
auto_chosen_world = False
is_missing_world = False
default_world_path = None
if minetestinfo.contains("primary_world_path"):
if not os.path.isdir(minetestinfo.get_var("primary_world_path")):
is_missing_world = True
print("primary_world_path ERROR: '"+minetestinfo.get_var("primary_world_path")+"' is not a folder.")
if (not minetestinfo.contains("primary_world_path")) or is_missing_world:
print ("LOOKING FOR WORLDS IN " + minetestinfo.get_var("worlds_path"))
for base_path, dirnames, filenames in os.walk(minetestinfo.get_var("worlds_path")):
#for j in range(0,len(dirnames)):
# i = len(dirnames) - 0 - 1
# if dirnames[i][0] == ".":
# print (" SKIPPING "+dirnames[i])
# dirnames.remove_at(i)
world_count = 0
for subdirname in dirnames:
print (" EXAMINING "+subdirname)
if subdirname[0]!=".":
world_count += 1
index = 0
world_number = 0
for subdirname in dirnames:
print (" EXAMINING "+subdirname)
if subdirname[0]!=".":
#if (index == len(dirnames)-1): # skip first one because the one on my computer is big
if (subdirname!="world") or (world_number==(world_count-1)):
default_world_path = os.path.join(base_path, subdirname) # os.path.join(minetestinfo.get_var("worlds_path"), "try7amber")
auto_chosen_world = True
break
world_number += 1
index += 1
if auto_chosen_world:
break
if is_missing_world:
print("MISSING WORLD '"+minetestinfo.get_var("primary_world_path")+"'")
if default_world_path is not None:
print("(so a default was picked below that you can change)")
else:
print("(and no world could be found in worlds_path '"+minetestinfo.get_var("worlds_path")+"')")
default_message = ""
if default_world_path is not None:
default_message = " (or world name if above; blank for ["+default_world_path+"])"
input_string = raw_input("World path"+default_message+": ")
if (len(input_string)>0):
try_path = os.path.join(minetestinfo.get_var("worlds_path"), input_string)
this_primary_world_path = input_string
if (not os.path.isdir(this_primary_world_path)) and os.path.isdir(try_path):
this_primary_world_path = try_path
minetestinfo._data["primary_world_path"] = this_primary_world_path
auto_chosen_world = False
minetestinfo.save_yaml()
if get_world_var("gameid") != minetestinfo.get_var("game_path"):
is_world_changed = True
if minetestinfo.contains("game_path") or is_world_changed:
if minetestinfo.contains("game_path"):
default_game_name = minetestinfo.get_var("game_path")
if default_game_name is None:
default_game_name = get_world_var("gameid")
if default_game_name is not None:
print("gameid "+default_game_name+" detected in world.")
games_path = os.path.join(minetestinfo.get_var("shared_minetest_path"), "games")
game_blacklist = list() # is only used if there is no game defined in world
game_blacklist.append("minetest_game")
games_list = list()
if default_game_name is None:
if os.path.isdir(games_path):
folder_path = games_path
sub_names = os.listdir(folder_path)
real_count = 0
for sub_name in sub_names:
if (sub_name[:1]!="."):
real_count += 1
real_index = 0
for sub_name in sub_names:
sub_path = os.path.join(folder_path,sub_name)
if os.path.isdir(sub_path):
if (sub_name[:1]!="."):
if (sub_name != "minetest_game") or (real_index>=real_count-1):
if default_game_name is None:
default_game_name = sub_name
games_list.append(sub_name)
real_index += 1
if default_game_name is not None:
path_msg = ""
if len(games_list)>0:
path_msg = " (or game name listed above)"
for try_game_name in games_list:
print(" "+try_game_name)
minetestinfo.prepare_var("game_path",os.path.join(games_path,default_game_name),"game (your subgame) path"+path_msg)
if (not os.path.isdir(minetestinfo.get_var("game_path"))):
try_path = os.path.join(games_path,minetestinfo.get_var("game_path"))
if os.path.isdir(try_path):
minetestinfo.set_var("game_path",try_path)
mods_path = None
if minetestinfo.contains("game_path") and os.path.isdir(minetestinfo.get_var("game_path")):
mods_path = os.path.join(minetestinfo.get_var("game_path"), "mods")
folder_path = mods_path
for sub_name in os.listdir(folder_path):
sub_path = os.path.join(folder_path,sub_name)
if os.path.isdir(sub_path):
if (sub_name[:1]!="."):
if get_world_var("load_mod_"+sub_name) == True:
loaded_mod_list.append(sub_name)
else:
print("Could not find game folder '"+minetestinfo.get_var("game_path")+"'. Please fix game_path in '"+minetestinfo._config_path+"' to point to your subgame, so that game and mod management features will work.")
print("Mod list for current game: "+','.join(loaded_mod_list))
#mods_path
#mod_path = os.path.join(mods_path, mod_name)
#if os.path.isdir(mod_path):
#if default_game is None:
# game_names = os.listdir
world_mt_mapvars = None
world_mt_mapvars_world_path = None
def get_world_var(name):
result = None
global world_mt_mapvars_world_path
world_path = minetestinfo.get_var("world_path")
#world_mt_mapvars = None
global world_mt_mapvars
if world_mt_mapvars is None or (world_path != world_mt_mapvars_world_path):
world_mt_mapvars_world_path = world_path
if world_path is not None:
world_mt_mapvars = get_dict_from_conf_file(os.path.join(world_path, "world.mt"),"=")
if (world_mt_mapvars is not None) and name in world_mt_mapvars:
result = world_mt_mapvars[name]
return result
init_minetestinfo()

35
minetestmeta.py

@ -1,35 +0,0 @@
import os
from expertmm import *
class MinetestMetadata:
config = None
config_name = None
config_path = None
worldgen_mod_list = None
def __init__(self):
self.config_name = "minetestmeta.yml"
self.config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), self.config_name)
self.config = {}
self.config = get_dict_modified_by_conf_file(self.config, self.config_path, ":")
is_config_changed = False
if not os.path.isfile(self.config_path):
is_config_changed = True
print("Creating '"+self.config_path+"'")
self.worldgen_mod_list = list()
self.worldgen_mod_list.append("technic_worldgen")
self.worldgen_mod_list.append("mg") # this delays/prevents chunk generation and sometimes crashes in 0.4.13 release (tested on Windows 10)
self.worldgen_mod_list.append("moreores")
self.worldgen_mod_list.append("lapis")
self.worldgen_mod_list.append("sea")
self.worldgen_mod_list.append("moretrees")
self.worldgen_mod_list.append("caverealms")
#self.worldgen_mod_list.append("nature_classic") # NOTE: plantlife_modpack has this and other stuff, but detecting this could help since it is unique to the modpack
self.worldgen_mod_list.append("plantlife_modpack") #ok if installed as modpack instead of putting individual mods in mods folder
mtmeta = MinetestMetadata()

15
web/chunkymap.php

@ -328,7 +328,10 @@ function echo_chunkymap_canvas() {
if ($chunkymap_view_zoom_multiplier<$chunkymap_view_min_zoom) $chunkymap_view_zoom_multiplier = $chunkymap_view_min_zoom; if ($chunkymap_view_zoom_multiplier<$chunkymap_view_min_zoom) $chunkymap_view_zoom_multiplier = $chunkymap_view_min_zoom;
if ($chunkymap_view_zoom_multiplier>$chunkymap_view_max_zoom) $chunkymap_view_zoom_multiplier = $chunkymap_view_max_zoom; if ($chunkymap_view_zoom_multiplier>$chunkymap_view_max_zoom) $chunkymap_view_zoom_multiplier = $chunkymap_view_max_zoom;
$decachunks_per_page = 1.0/$chunkymap_view_zoom_multiplier; $decachunks_per_page = intval(1.0/$chunkymap_view_zoom_multiplier);
if ($decachunks_per_page<1) {
$decachunks_per_page = 1;
}
$view_w = (($decachunks_per_page*160.0)); $view_w = (($decachunks_per_page*160.0));
$view_h = (($decachunks_per_page*160.0)); $view_h = (($decachunks_per_page*160.0));
$view_left = (($chunkymap_view_x)) - (($view_w/2.0)); $view_left = (($chunkymap_view_x)) - (($view_w/2.0));
@ -362,13 +365,13 @@ function echo_chunkymap_canvas() {
echo '<table id="chunkymap_table" cellspacing="0" cellpadding="0" style="width:100%">'."\r\n"; echo '<table id="chunkymap_table" cellspacing="0" cellpadding="0" style="width:100%">'."\r\n";
echo ' <tr>'."\r\n"; echo ' <tr>'."\r\n";
echo ' <td style="width:5%">'."$td_placeholder_content".'</td>'."\r\n"; echo ' <td style="width:5%">'."$td_placeholder_content".'</td>'."\r\n";
echo " <td style=\"width:90%\"><a href=\"?$world_name=world_name&chunkymap_view_zoom_multiplier=$chunkymap_view_zoom_multiplier&chunkymap_view_x=$chunkymap_view_x&chunkymap_view_z=".($chunkymap_view_z+($view_h/2.0))."#chunkymap_top\">".'<img src="chunkymapdata/images/arrow-wide-up.png" style="width:100%"/>'.'</a></td>'."\r\n"; echo " <td style=\"width:95%\"><a href=\"?world_name=$world_name&chunkymap_view_zoom_multiplier=$chunkymap_view_zoom_multiplier&chunkymap_view_x=$chunkymap_view_x&chunkymap_view_z=".($chunkymap_view_z+($view_h/2.0))."#chunkymap_top\">".'<img src="chunkymapdata/images/arrow-wide-up.png" style="width:90%"/>'.'</a></td>'."\r\n";
echo ' <td style="width:5%">'."$td_placeholder_content".'</td>'."\r\n"; echo ' <td style="width:5%">'."$td_placeholder_content".'</td>'."\r\n";
echo ' </tr>'."\r\n"; echo ' </tr>'."\r\n";
$cell_perc=intval(round(100.0/$decachunky_count_x)); $cell_perc=intval(round(100.0/$decachunky_count_x));
echo ' <tr>'."\r\n"; echo ' <tr>'."\r\n";
echo " <td style=\"width:5%\"><a href=\"?$world_name=world_name&chunkymap_view_zoom_multiplier=$chunkymap_view_zoom_multiplier&chunkymap_view_x=".($chunkymap_view_x-($view_w/2.0))."&chunkymap_view_z=$chunkymap_view_z#chunkymap_top\">".'<img src="chunkymapdata/images/arrow-wide-left.png" style="width:100%"/>'.'</a></td>'."\r\n"; echo " <td style=\"width:5%\"><a href=\"?world_name=$world_name&chunkymap_view_zoom_multiplier=$chunkymap_view_zoom_multiplier&chunkymap_view_x=".($chunkymap_view_x-($view_w/2.0))."&chunkymap_view_z=$chunkymap_view_z#chunkymap_top\">".'<img src="chunkymapdata/images/arrow-wide-left.png" style="width:90%"/>'.'</a></td>'."\r\n";
echo ' <td style="width:90%">'."\r\n"; echo ' <td style="width:95%">'."\r\n";
echo ' <table id="decachunk_table" cellspacing="0" cellpadding="0" style="width:100%; background-color:black">'."\r\n"; echo ' <table id="decachunk_table" cellspacing="0" cellpadding="0" style="width:100%; background-color:black">'."\r\n";
while ($decachunky_z>=$decachunky_min_z) { while ($decachunky_z>=$decachunky_min_z) {
echo ' <tr>'."\r\n"; echo ' <tr>'."\r\n";
@ -398,11 +401,11 @@ function echo_chunkymap_canvas() {
} }
echo ' </table>'."\r\n"; echo ' </table>'."\r\n";
echo ' </td>'."\r\n"; echo ' </td>'."\r\n";
echo " <td style=\"width:5%\"><a href=\"?$world_name=world_name&chunkymap_view_zoom_multiplier=$chunkymap_view_zoom_multiplier&chunkymap_view_x=".($chunkymap_view_x+($view_w/2.0))."&chunkymap_view_z=$chunkymap_view_z#chunkymap_top\">".'<img src="chunkymapdata/images/arrow-wide-right.png" style="width:100%"/>'.'</a></td>'."\r\n"; echo " <td style=\"width:5%\"><a href=\"?world_name=$world_name&chunkymap_view_zoom_multiplier=$chunkymap_view_zoom_multiplier&chunkymap_view_x=".($chunkymap_view_x+($view_w/2.0))."&chunkymap_view_z=$chunkymap_view_z#chunkymap_top\">".'<img src="chunkymapdata/images/arrow-wide-right.png" style="width:100%"/>'.'</a></td>'."\r\n";
echo ' </tr>'."\r\n"; echo ' </tr>'."\r\n";
echo ' <tr>'."\r\n"; echo ' <tr>'."\r\n";
echo ' <td style="width:5%">'."$td_placeholder_content".'</td>'."\r\n"; echo ' <td style="width:5%">'."$td_placeholder_content".'</td>'."\r\n";
echo " <td style=\"width:90%\"><a href=\"?$world_name=world_name&chunkymap_view_zoom_multiplier=$chunkymap_view_zoom_multiplier&chunkymap_view_x=$chunkymap_view_x&chunkymap_view_z=".($chunkymap_view_z-($view_h/2.0))."#chunkymap_top\">".'<img src="chunkymapdata/images/arrow-wide-down.png" style="width:100%"/>'.'</a></td>'."\r\n"; echo " <td style=\"width:90%\"><a href=\"?world_name=$world_name&chunkymap_view_zoom_multiplier=$chunkymap_view_zoom_multiplier&chunkymap_view_x=$chunkymap_view_x&chunkymap_view_z=".($chunkymap_view_z-($view_h/2.0))."#chunkymap_top\">".'<img src="chunkymapdata/images/arrow-wide-down.png" style="width:100%"/>'.'</a></td>'."\r\n";
echo ' <td style="width:5%">'."$td_placeholder_content".'</td>'."\r\n"; echo ' <td style="width:5%">'."$td_placeholder_content".'</td>'."\r\n";
echo ' </tr>'."\r\n"; echo ' </tr>'."\r\n";
echo '</table>'."\r\n"; echo '</table>'."\r\n";

Loading…
Cancel
Save