@ -3,7 +3,18 @@ import os
import subprocess
import traceback
import argparse
import time
import sys
import timeit
from timeit import default_timer as best_timer
#best_timer = timeit.default_timer
#if sys.platform == "win32":
# on Windows, the best timer is time.clock()
# best_timer = time.clock
#else:
# on most other platforms, the best timer is time.time()
# best_timer = time.time
# REQUIRES: see README.md
# The way to do a full render is deleting all files from the folder self.chunkymap_data_path such as /var/www/html/minetest/chunkymapdata (or chunkymap in current directory on Windows)
@ -15,9 +26,30 @@ import argparse
#region server-specific options
def get_dict_from_conf_file ( path , assignment_operator = " = " ) :
results = None
results = get_dict_modified_by_conf_file ( results , path , assignment_operator )
return results
def RepresentsInt ( s ) :
try :
int ( s )
return True
except ValueError :
return False
def RepresentsFloat ( s ) :
try :
float ( s )
return True
except ValueError :
return False
def get_dict_modified_by_conf_file ( this_dict , path , assignment_operator = " = " ) :
results = None
#print ("Checking "+str(path)+" for settings...")
if os . path . isfile ( path ) :
results = this_dict
if ( results is None ) or ( type ( results ) is not dict ) :
results = { }
ins = open ( path , ' r ' )
line = True
@ -32,11 +64,35 @@ def get_dict_from_conf_file(path,assignment_operator="="):
if ao_index < len ( line_strip ) - 1 : # skip yaml implicit nulls or yaml objects
result_name = line_strip [ : ao_index ] . strip ( )
result_value = line_strip [ ao_index + 1 : ] . strip ( )
result_lower = result_value . lower ( )
if result_value == " None " or result_value == " null " or result_value == " ~ " or result_value == " NULL " :
result_value = None
elif result_lower == " true " :
result_value = True
elif result_lower == " false " :
result_value = False
elif RepresentsInt ( result_value ) :
result_value = int ( result_value )
elif RepresentsFloat ( result_value ) :
result_value = float ( result_value )
#print (" CHECKING... "+result_name+":"+result_value)
results [ result_name ] = result_value
ins . close ( )
return results
def save_conf_from_dict ( path , this_dict , assignment_operator = " = " , save_nulls_enable = True ) :
try :
outs = open ( path , ' w ' )
for this_key in this_dict . keys ( ) :
if save_nulls_enable or ( this_dict [ this_key ] is not None ) :
if this_dict [ this_key ] is None :
outs . write ( this_key + assignment_operator + " null \n " )
else :
outs . write ( this_key + assignment_operator + str ( this_dict [ this_key ] ) + " \n " )
outs . close ( )
except :
print ( " Could not finish saving chunk metadata to ' " + str ( yml_path ) + " ' : " + str ( traceback . format_exc ( ) ) )
def get_tuple_from_notation ( line , debug_src_name = " <unknown object> " ) :
result = None
if line is not None :
@ -69,49 +125,138 @@ def is_same_fvec3(list_a, list_b):
class MTChunk :
x = None
z = None
chunk_dict = None
is_player_in_this_chunk = None
is_fresh = None
width = None
height = None
is_marked = None
is_marked_empty = None
image_w = None
image_h = None
image_left = None
image_top = None
image_right = None
image_bottom = None
#width = None
#height = None
#is_marked = None
#is_marked_empty = None
#image_w = None
#image_h = None
#image_left = None
#image_top = None
#image_right = None
#image_bottom = None
def __init__ ( self ) :
# NOTE: variables that need to be saved (and only they) should be stored in dict
self . chunk_dict = { }
self . is_player_in_this_chunk = False
self . is_fresh = False
self . is_marked = False
self . is_marked_empty = False
self . chunk_dict [ " is_marked_empty " ] = False
self . chunk_dict [ " is_marked " ] = False
self . chunk_dict [ " width " ] = None
self . chunk_dict [ " height " ] = None
self . chunk_dict [ " image_w " ] = None
self . chunk_dict [ " image_h " ] = None
self . chunk_dict [ " image_left " ] = None
self . chunk_dict [ " image_top " ] = None
self . chunk_dict [ " image_right " ] = None
self . chunk_dict [ " image_bottom " ] = None
def load_yaml ( self , yml_path ) :
self . chunk_dict = get_dict_modified_by_conf_file ( self . chunk_dict , yml_path , " : " )
def save_yaml ( self , yml_path ) :
save_conf_from_dict ( yml_path , self . chunk_dict , assignment_operator = " : " , save_nulls_enable = False )
#try:
#outs = open(yml_path, 'w')
#outs.write("is_marked_empty:"+str(self.is_marked_empty)+"\n")
#outs.write("is_marked:"+str(self.is_marked)+"\n")
#if self.width is not None:
#outs.write("width:"+str(self.width)+"\n")
#if self.height is not None:
#outs.write("height:"+str(self.height)+"\n")
#if self.image_w is not None:
#outs.write("image_w:"+str(self.image_w)+"\n")
#if self.image_h is not None:
#outs.write("image_h:"+str(self.image_h)+"\n")
#if self.image_left is not None:
#outs.write("image_left:"+str(self.image_left)+"\n")
#if self.image_top is not None:
#outs.write("image_top:"+str(self.image_top)+"\n")
#if self.image_right is not None:
#outs.write("image_right:"+str(self.image_right)+"\n")
#if self.image_bottom is not None:
#outs.write("image_bottom:"+str(self.image_bottom)+"\n")
#outs.close()
#except:
#print("Could not finish saving chunk metadata to '"+str(yml_path)+"': "+str(traceback.format_exc()))
#requires output such as from minetestmapper-numpy.py
def set_from_genresult ( self , this_genresult_path ) :
#this_genresult_path = mtchunks.get_chunk_genresult_path(chunk_luid)
result = False
if os . path . isfile ( this_genresult_path ) :
#may have data such as:
#Result image (w=16 h=16) will be written to chunk_x0z0.png
#Unknown node names: meze:meze default:stone_with_iron air default:dirt_with_snow default:stone_with_copper default:snow
#Unknown node ids: 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7
#Drawing image
#Saving to: chunk_x0z0.png
#('PNG Region: ', [0, 64, 0, 64])
#('Pixels PerNode: ', 1)
#('border: ', 0)
self . chunk_dict [ " is_marked " ] = True
ins = open ( this_genresult_path , ' r ' )
line = True
while line :
line = ins . readline ( )
if line :
line_strip = line . strip ( )
if " data does not exist " in line_strip :
self . chunk_dict [ " is_marked_empty " ] = True
break
elif " Result image " in line_strip :
oparen_index = line_strip . find ( " ( " )
if ( oparen_index > - 1 ) :
cparen_index = line_strip . find ( " ) " , oparen_index + 1 )
if ( cparen_index > - 1 ) :
operations_string = line_strip [ oparen_index + 1 : cparen_index ]
operation_list = operations_string . split ( " " )
#if len(operation_list)==2:
for operation_string in operation_list :
if " = " in operation_string :
chunks = operation_string . split ( " = " )
if len ( chunks ) == 2 :
if chunks [ 0 ] . strip ( ) == " w " :
try :
outs = open ( yml_path , ' w ' )
outs . write ( " is_marked_empty: " + str ( self . is_marked_empty ) + " \n " )
outs . write ( " is_marked: " + str ( self . is_marked ) + " \n " )
if self . width is not None :
outs . write ( " width: " + str ( self . width ) + " \n " )
if self . height is not None :
outs . write ( " height: " + str ( self . height ) + " \n " )
if self . image_w is not None :
outs . write ( " image_w: " + str ( self . image_w ) + " \n " )
if self . image_h is not None :
outs . write ( " image_h: " + str ( self . image_h ) + " \n " )
if self . image_left is not None :
outs . write ( " image_left: " + str ( self . image_left ) + " \n " )
if self . image_top is not None :
outs . write ( " image_top: " + str ( self . image_top ) + " \n " )
if self . image_right is not None :
outs . write ( " image_right: " + str ( self . image_right ) + " \n " )
if self . image_bottom is not None :
outs . write ( " image_bottom: " + str ( self . image_bottom ) + " \n " )
outs . close ( )
self . chunk_dict [ " image_w " ] = int ( chunks [ 1 ] . strip ( ) )
except :
print ( " Could not finish saving chunk metadata to ' " + str ( yml_path ) + " ' : " + str ( traceback . format_exc ( ) ) )
print ( " Bad value for image w: " + str ( chunks [ 1 ] ) )
elif chunks [ 0 ] . strip ( ) == " h " :
try :
self . chunk_dict [ " image_h " ] = int ( chunks [ 1 ] . strip ( ) )
except :
print ( " Bad value for image h: " + str ( chunks [ 1 ] ) )
else :
print ( " Bad name for image variable so ignoring variable named ' " + str ( chunks [ 0 ] ) + " ' " )
else :
print ( " Bad assignment (not 2 sides) so ignoring command ' " + operation_string + " ' " )
else :
print ( " Bad assignment (operator) so ignoring command ' " + operation_string + " ' " )
#else:
# print("Bad assignment count so ignoring operations string '"+operations_string+"'")
elif " PNG Region " in line_strip :
obracket_index = line_strip . find ( " [ " )
if obracket_index > - 1 :
cbracket_index = line_strip . find ( " ] " , obracket_index + 1 )
if cbracket_index > - 1 :
rect_values_string = line_strip [ obracket_index + 1 : cbracket_index ]
rect_values_list = rect_values_string . split ( " , " )
if len ( rect_values_list ) == 4 :
#pngregion=[pngminx, pngmaxx, pngminz, pngmaxz] #from minetestmapper-numpy.py
self . chunk_dict [ " image_left " ] = int ( rect_values_list [ 0 ] . strip ( ) )
self . chunk_dict [ " image_right " ] = int ( rect_values_list [ 1 ] . strip ( ) )
self . chunk_dict [ " image_top " ] = int ( rect_values_list [ 2 ] . strip ( ) )
self . chunk_dict [ " image_bottom " ] = int ( rect_values_list [ 3 ] . strip ( ) )
else :
print ( " Bad map rect, so ignoring: " + rect_values_string )
ins . close ( )
class MTChunks :
@ -142,24 +287,35 @@ class MTChunks:
pixelspernode = 1
refresh_map_enable = None
refresh_players_enable = None
refresh_map_seconds = None
refresh_players_seconds = None
last_players_refresh_second = None
last_map_refresh_second = None
#ALSO save to YAML:
#total_generated_count = 0
#endregion values to save to YAML
loop_enable = None
is_verbose = None
world_blacklist = None
def __init__ ( self ) : #formerly checkpaths() in global scope
self . is_verbose = True
self . loop_enable = True
self . refresh_map_enable = True
self . refresh_players_enable = True
self . chunks = { }
self . username = " owner "
self . website_root = " /var/www/html/minetest "
self . world_name = " FCAGameAWorld "
os_name = " linux "
self . os_name = " linux "
self . refresh_map_seconds = 10
self . refresh_players_seconds = 3
input_string = " "
if ( os . path . sep != " / " ) :
os_name = " windows "
self . os_name = " windows "
#input_string = input("Which self.username contains minetest/util/minetestmapper-numpy.py (minetest not .minetest) ["+self.username+"]?")
if ( len ( input_string ) > 0 ) :
self . username = input_string
@ -174,13 +330,13 @@ class MTChunks:
self . world_name = input_string
#region server-specific options
self . profiles_path = " /home "
if os_name == " windows " :
if self . os_name == " windows " :
self . profiles_path = " C: \\ Users "
self . profile_path = os . path . join ( self . profiles_path , self . username )
#if (not os.path.isdir(self.profile_path)):
# self.profile_path = os.path.join(self.profiles_path, "jgustafson")
self . dotminetest_path = os . path . join ( self . profile_path , " .minetest " )
if ( os_name == " windows " ) : self . dotminetest_path = " C: \\ games \\ Minetest "
if ( self . os_name == " windows " ) : self . dotminetest_path = " C: \\ games \\ Minetest "
self . worlds_path = os . path . join ( self . dotminetest_path , " worlds " )
self . world_path = os . path . join ( self . worlds_path , self . world_name )
auto_chosen_world = False
@ -211,22 +367,23 @@ class MTChunks:
break
self . python_exe_path = " python "
self . is_save_output_ok = True # this is probably required to avoid minutely writes
self . is_save_output_ok = True # Keeping output after analyzing it is no longer necessary since results are saved to YAML, but keeping output provides debug info since is the output of minetestmapper-numpy.py
try :
alt_path = " C: \\ python27 \ python.exe "
if os . path . isfile ( alt_path ) :
self . python_exe_path = alt_path
#else may be in path--assume installer worked
except :
pass # do nothing, probably linux
self . mtmn_path = os . path . join ( self . profile_path , " minetest/util/minetestmapper-numpy.py " )
self . colors_path = os . path . join ( self . profile_path , " minetest/util/colors.txt " )
if os_name == " windows " :
self . mtmn_path = os . path . join ( os . path . dirname ( __file__ ) , " minetestmapper-numpy.py " )
self . colors_path = os . path . join ( os . path . dirname ( __file__ ) , " colors.txt " )
self . website_root = os . path . dirname ( __file__ )
if self . os_name == " windows " :
self . mtmn_path = os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " minetestmapper-numpy.py " )
self . colors_path = os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " colors.txt " )
self . website_root = os . path . dirname ( os . path . abspath ( __file__ ) )
self . chunkx_min = 0
self . chunkz_min = 0
@ -238,76 +395,6 @@ class MTChunks:
self . minheight = - 32
self . pixelspernode = 1
def set_from_genresult ( self , mtchunk , chunk_luid ) :
dest_genresult_path = self . get_chunk_genresult_path ( chunk_luid )
result = False
if os . path . isfile ( dest_genresult_path ) :
#may have data such as:
#Result image (w=16 h=16) will be written to chunk_x0z0.png
#Unknown node names: meze:meze default:stone_with_iron air default:dirt_with_snow default:stone_with_copper default:snow
#Unknown node ids: 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7
#Drawing image
#Saving to: chunk_x0z0.png
#('PNG Region: ', [0, 64, 0, 64])
#('Pixels PerNode: ', 1)
#('border: ', 0)
mtchunk . is_marked = True
ins = open ( dest_genresult_path , ' r ' )
line = True
while line :
line = ins . readline ( )
if line :
line_strip = line . strip ( )
if " data does not exist " in line_strip :
mtchunk . is_marked_empty = True
break
elif " Result image " in line_strip :
oparen_index = line_strip . find ( " ( " )
if ( oparen_index > - 1 ) :
cparen_index = line_strip . find ( " ) " , oparen_index + 1 )
if ( cparen_index > - 1 ) :
operations_string = line_strip [ oparen_index + 1 : cparen_index ]
operation_list = operations_string . split ( " " )
#if len(operation_list)==2:
for operation_string in operation_list :
if " = " in operation_string :
chunks = operation_string . split ( " = " )
if len ( chunks ) == 2 :
if chunks [ 0 ] . strip ( ) == " w " :
try :
mtchunk . image_w = int ( chunks [ 1 ] . strip ( ) )
except :
print ( " Bad value for image w: " + str ( chunks [ 1 ] ) )
elif chunks [ 0 ] . strip ( ) == " h " :
try :
mtchunk . image_h = int ( chunks [ 1 ] . strip ( ) )
except :
print ( " Bad value for image h: " + str ( chunks [ 1 ] ) )
else :
print ( " Bad name for image variable so ignoring variable named ' " + str ( chunks [ 0 ] ) + " ' " )
else :
print ( " Bad assignment (not 2 sides) so ignoring command ' " + operation_string + " ' " )
else :
print ( " Bad assignment (operator) so ignoring command ' " + operation_string + " ' " )
#else:
# print("Bad assignment count so ignoring operations string '"+operations_string+"'")
elif " PNG Region " in line_strip :
obracket_index = line_strip . find ( " [ " )
if obracket_index > - 1 :
cbracket_index = line_strip . find ( " ] " , obracket_index + 1 )
if cbracket_index > - 1 :
rect_values_string = line_strip [ obracket_index + 1 : cbracket_index ]
rect_values_list = rect_values_string . split ( " , " )
if len ( rect_values_list ) == 4 :
#pngregion=[pngminx, pngmaxx, pngminz, pngmaxz] #from minetestmapper-numpy.py
mtchunk . image_left = int ( rect_values_list [ 0 ] . strip ( ) )
mtchunk . image_right = int ( rect_values_list [ 1 ] . strip ( ) )
mtchunk . image_top = int ( rect_values_list [ 2 ] . strip ( ) )
mtchunk . image_bottom = int ( rect_values_list [ 3 ] . strip ( ) )
else :
print ( " Bad map rect, so ignoring: " + rect_values_string )
ins . close ( )
@ -335,7 +422,13 @@ class MTChunks:
return " chunk_ " + chunk_luid + " .png "
def get_chunk_image_tmp_path ( self , chunk_luid ) :
return os . path . join ( os . path . dirname ( __file__ ) , self . get_chunk_image_name ( chunk_luid ) )
return os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , self . get_chunk_image_name ( chunk_luid ) )
def get_signal_name ( self ) :
return " chunkymap-signals.txt "
def get_signal_path ( self ) :
return os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , self . get_signal_name ( ) )
def get_chunk_image_path ( self , chunk_luid ) :
return os . path . join ( self . chunkymap_data_path , self . get_chunk_image_name ( chunk_luid ) )
@ -343,8 +436,11 @@ class MTChunks:
def get_chunk_genresult_name ( self , chunk_luid ) :
return " chunk_ " + chunk_luid + " _mapper_result.txt "
def get_chunk_genresults_tmp_folder ( self , chunk_luid ) :
return os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " chunkymap-genresults " )
def get_chunk_genresult_tmp_path ( self , chunk_luid ) :
return os . path . join ( os . path . dirname ( __file__ ) , self . get_chunk_genresult_name ( chunk_luid ) )
return os . path . join ( self . get_chunk_genresults_tmp_folder ( chunk_luid ) , self . get_chunk_genresult_name ( chunk_luid ) )
def get_chunk_yaml_name ( self , chunk_luid ) :
return " chunk_ " + chunk_luid + " .yml "
@ -389,43 +485,6 @@ class MTChunks:
ins . close ( )
return result
def get_chunk_genresult_path ( self , chunk_luid ) :
return os . path . join ( self . chunkymap_data_path , self . get_chunk_genresult_name ( chunk_luid ) )
#deprecated
def is_genresult_marked ( self , chunk_luid ) :
result = False
dest_genresult_path = self . get_chunk_genresult_path ( chunk_luid )
if os . path . isfile ( dest_genresult_path ) :
result = True
return result
#deprecated
def is_genresult_marked_empty ( self , chunk_luid ) :
dest_genresult_path = self . get_chunk_genresult_path ( chunk_luid )
result = False
if os . path . isfile ( dest_genresult_path ) :
ins = open ( dest_genresult_path , ' r ' )
line = True
while line :
line = ins . readline ( )
if line :
line_strip = line . strip ( )
if " data does not exist " in line_strip :
result = True
break
ins . close ( )
return result
def remove_genresult ( self , chunk_luid ) :
result = False
dest_genresult_path = self . get_chunk_genresult_path ( chunk_luid )
if os . path . isfile ( dest_genresult_path ) :
result = True
os . remove ( dest_genresult_path )
return result
def remove_chunk_image ( self , chunk_luid ) :
result = False
tmp_png_path = self . get_chunk_image_path ( chunk_luid )
@ -436,49 +495,54 @@ class MTChunks:
def remove_chunk ( self , chunk_luid ) :
result = False
out_path = self . get_chunk_genresult_path ( chunk_luid )
out_path = self . get_chunk_genresult_tmp_ path ( chunk_luid )
tmp_png_path = self . get_chunk_image_path ( chunk_luid )
if os . path . isfile ( out_path ) :
os . remove ( out_path )
result = True
yml_path = self . get_chunk_yaml_path ( chunk_luid )
if os . path . isfile ( tmp_png_path ) :
os . remove ( tmp_png_path )
result = True
if os . path . isfile ( yml_path ) :
os . remove ( yml_path )
result = True
if os . path . isfile ( out_path ) :
os . remove ( out_path )
result = True
return result
def is_chunk_rendered_on_dest ( self , chunk_luid ) : #formerly is_chunk_empty_on_dest (reversed)
is_rendered = False
#is_chunk_out_empty = self.is_chunk_yaml_marked_empty(chunk_luid)
#dest_genresult_path = self.get_chunk_genresult_path(chunk_luid)
dest_png_path = self . get_chunk_image_path ( chunk_luid )
if os . path . isfile ( dest_png_path ) :
#os.remove(dest_genresult_path)
is_rendered = True
return is_rendered
def prepare_chunk_meta ( self , chunk_luid ) :
if chunk_luid not in self . chunks . keys ( ) :
self . chunks [ chunk_luid ] = MTChunk ( )
yaml_path = self . get_chunk_yaml_path ( chunk_luid )
if os . path . isfile ( yaml_path ) :
self . chunks [ chunk_luid ] . load_yaml ( yaml_path )
def render_chunk ( self , x , z ) :
result = False
chunk_luid = self . get_chunk_luid ( x , z )
png_name = self . get_chunk_image_name ( chunk_luid )
tmp_png_path = self . get_chunk_image_tmp_path ( chunk_luid )
cmd_suffix = " "
genresult_name = self . get_chunk_genresult_name ( chunk_luid )
genresult_tmp_folder_path = self . get_chunk_genresults_tmp_folder ( chunk_luid )
if not os . path . isdir ( genresult_tmp_folder_path ) :
os . makedirs ( genresult_tmp_folder_path )
genresult_path = self . get_chunk_genresult_tmp_path ( chunk_luid )
if self . is_save_output_ok :
cmd_suffix = " > \" " + genresult_path + " \" "
x_min = x * self . chunk_size
x_max = x * self . chunk_size + self . chunk_size - 1
z_min = z * self . chunk_size
z_max = z * self . chunk_size + self . chunk_size - 1
#print ("generating x = " + str(x_min) + " to " + str(x_max) + " , z = " + str(z_min) + " to " + str(z_max))
cmd_suffix = " "
cmd_suffix = " > \" " + genresult_path + " \" "
cmd_string = self . python_exe_path + " \" " + self . mtmn_path + " \" --region " + str ( x_min ) + " " + str ( x_max ) + " " + str ( z_min ) + " " + str ( z_max ) + " --maxheight " + str ( self . maxheight ) + " --minheight " + str ( self . minheight ) + " --pixelspernode " + str ( self . pixelspernode ) + " \" " + self . world_path + " \" \" " + tmp_png_path + " \" " + cmd_suffix
dest_png_path = self . get_chunk_image_path ( chunk_luid )
dest_genresult_path = self . get_chunk_genresult_path ( chunk_luid )
#is_empty_chunk = is_chunk_yaml_marked(chunk_luid) and is_chunk_yaml_marked_empty(chunk_luid)
print ( cmd_string )
subprocess . call ( cmd_string , shell = True ) # TODO: remember not to allow arbitrary command execution, which could happen if input contains ';' when using shell=True
@ -492,27 +556,23 @@ class MTChunks:
try :
os . rename ( tmp_png_path , dest_png_path )
print ( " (moved to ' " + dest_png_path + " ' ) " )
self . prepare_chunk_meta ( chunk_luid )
self . prepare_chunk_meta ( chunk_luid ) # DOES load existing yml if exists
self . chunks [ chunk_luid ] . is_fresh = True
except :
print ( " Could not finish moving ' " + tmp_png_path + " ' to ' " + dest_png_path + " ' " )
try :
if ( os . path . isfile ( dest_genresult_path ) ) :
os . remove ( dest_genresult_path )
if self . is_save_output_ok :
#os.rename(genresult_path, dest_genresult_path)
#print("(moved to '"+dest_genresult_path+"')")
tmp_chunk = MTChunk ( )
self . set_from_genresult ( tmp_chunk , chunk_luid )
self . prepare_chunk_meta ( chunk_luid ) # DOES load existing yml if exists
this_chunk = self . chunks [ chunk_luid ]
#this_chunk = MTChunk()
this_chunk . set_from_genresult ( genresult_path )
chunk_yaml_path = self . get_chunk_yaml_path ( chunk_luid )
tmp_chunk . save_yaml ( chunk_yaml_path )
this_chunk . save_yaml ( chunk_yaml_path )
this_chunk . save_yaml ( chunk_yaml_path )
print ( " (saved yaml to ' " + chunk_yaml_path + " ' ) " )
os . remove ( genresult_path )
else :
if not self . is_save_output_ok :
if os . path . isfile ( genresult_path ) :
os . remove ( genresult_path )
except :
print ( " Could not finish deleting/moving output " )
@ -523,6 +583,7 @@ class MTChunks:
def check_players ( self ) :
# NOT NEEDED: if os.path.isfile(self.mtmn_path) and os.path.isfile(self.colors_path):
print ( " PROCESSING PLAYERS " )
self . chunkymap_data_path = os . path . join ( self . website_root , " chunkymapdata " )
chunkymap_players_name = " players "
chunkymap_players_path = os . path . join ( self . chunkymap_data_path , chunkymap_players_name )
@ -535,6 +596,9 @@ class MTChunks:
players_path = os . path . join ( self . world_path , " players " )
player_count = 0
player_written_count = 0
players_moved_count = 0
players_didntmove_count = 0
players_saved_count = 0
for dirname , dirnames , filenames in os . walk ( players_path ) :
for filename in filenames :
file_fullname = os . path . join ( players_path , filename )
@ -582,7 +646,7 @@ class MTChunks:
chunk_y = int ( ( float ( player_y ) / self . chunk_size ) )
chunk_z = int ( ( float ( player_z ) / self . chunk_size ) )
chunk_luid = self . get_chunk_luid ( chunk_x , chunk_z )
self . prepare_chunk_meta ( chunk_luid )
self . prepare_chunk_meta ( chunk_luid ) # DOES load existing yml if exists
self . chunks [ chunk_luid ] . is_player_in_this_chunk = True
#if is_enough_data:
@ -601,15 +665,18 @@ class MTChunks:
if " z " in map_player_dict . keys ( ) :
saved_player_z = float ( map_player_dict [ " z " ] )
#if (map_player_dict is None) or not is_same_fvec3( map_player_position_tuple, player_position_tuple):
if ( map_player_dict is None ) or ( saved_player_x is None ) or ( saved_player_z is None ) or ( int ( saved_player_x ) != int ( player_x ) ) or ( int ( saved_player_z ) != int ( player_z ) ) :
if ( map_player_dict is None ) or ( saved_player_x is None ) or ( saved_player_z is None ) or ( int ( saved_player_x ) != int ( player_x ) ) or ( int ( saved_player_y ) != int ( player_y ) ) or ( int ( saved_player_ z ) != int ( player_z ) ) :
# don't check y since y is elevation in minetest, don't use float since subblock position doesn't matter to map
if map_player_dict is not None and saved_player_x is not None and saved_player_z is not None :
if map_player_dict is not None and saved_player_x is not None and saved_player_y is not None and saved_player_ z is not None :
#print("PLAYER MOVED: "+str(player_name)+" moved from "+str(map_player_position_tuple)+" to "+str(player_position_tuple))
if self . is_verbose :
print ( " PLAYER MOVED: " + str ( player_name ) + " moved from " + str ( saved_player_x ) + " , " + str ( saved_player_y ) + " , " + str ( saved_player_z ) + " to " + str ( player_x ) + " , " + str ( player_y ) + " , " + str ( player_z ) )
players_moved_count + = 1
else :
if self . is_verbose :
print ( " SAVING YAML for player ' " + str ( player_name ) + " ' " )
players_saved_count + = 1
outs = open ( player_dest_path , ' w ' )
if player_name is not None :
outs . write ( " name: " + player_name + " \n " ) # python automatically uses correct newline for your os when you put "\n"
@ -625,9 +692,14 @@ class MTChunks:
outs . close ( )
player_written_count + = 1
else :
if self . is_verbose :
print ( " DIDN ' T MOVE: " + str ( player_name ) )
players_didntmove_count + = 1
player_count + = 1
if not self . is_verbose :
print ( " PLAYERS: " )
print ( " saved: " + str ( player_written_count ) + " (moved: " + str ( players_moved_count ) + " ; new: " + str ( players_saved_count ) + " ) " )
print ( " didn ' t move: " + str ( player_name ) )
def is_player_at_luid ( self , chunk_luid ) :
result = False
@ -643,7 +715,7 @@ class MTChunks:
def check_map ( self ) :
if os . path . isfile ( self . mtmn_path ) and os . path . isfile ( self . colors_path ) :
rendered_count = 0
self . chunkymap_data_path = os . path . join ( self . website_root , " chunkymapdata " )
yaml_name = " generated.yml "
world_yaml_path = os . path . join ( self . chunkymap_data_path , yaml_name )
@ -657,12 +729,13 @@ class MTChunks:
self . deny_http_access ( self . chunkymap_data_path )
mapvars = get_dict_from_conf_file ( world_yaml_path , " : " )
#is_testonly == (os_name=="windows")
#is_testonly == (self. os_name=="windows")
if mapvars is not None and set ( [ ' world_name ' ] ) . issubset ( mapvars ) :
#print (" (FOUND self.world_name)")
#if self.is_verbose:
# print (" (FOUND self.world_name)")
if mapvars [ " world_name " ] != self . world_name :
print ( " REMOVING data since from different world (map ' " + str ( mapvars [ " world_name " ] ) + " ' is not ' " + str ( self . world_name ) + " ' )... " )
print ( " Removing ALL map data since from WORLD NAME is different (map ' " + str ( mapvars [ " world_name " ] ) + " ' is not ' " + str ( self . world_name ) + " ' )... " )
for dirname , dirnames , filenames in os . walk ( self . chunkymap_data_path ) :
#index = 0
#for j in range(0,len(filenames)):
@ -673,6 +746,7 @@ class MTChunks:
for filename in filenames :
if filename [ 0 ] != " . " :
file_fullname = os . path . join ( self . chunkymap_data_path , filename )
if self . is_verbose :
print ( " EXAMINING " + filename )
badstart_string = " chunk "
if ( len ( filename ) > = len ( badstart_string ) ) and ( filename [ : len ( badstart_string ) ] == badstart_string ) :
@ -683,11 +757,13 @@ class MTChunks:
#for j in range(0,len(filenames)):
# i = len(filenames) - 0 - 1
# if filenames[i][0] == ".":
# if self.is_verbose:
# print (" SKIPPING "+filenames[i])
# filenames.remove_at(i)
for filename in filenames :
if filename [ 0 ] != " . " :
file_fullname = os . path . join ( self . chunkymap_data_path , filename )
if self . is_verbose :
print ( " EXAMINING " + filename )
badend_string = " .yml "
if ( len ( filename ) > = len ( badend_string ) ) and ( filename [ len ( filename ) - len ( badend_string ) : ] == badend_string ) :
@ -705,7 +781,7 @@ class MTChunks:
#if str(self.world_name) != str(mapvars["world_name"]):
# is_different_world = True
# print("FULL RENDER since chosen world name '"+self.world_name+"' does not match previously rendered world name '"+mapvars["world_name"]+"'")
print ( " PROCESSING MAP DATA " )
while outline_generates_count > 0 :
outline_generates_count = 0
for z in range ( self . chunkz_min , self . chunkz_max + 1 ) :
@ -730,19 +806,33 @@ class MTChunks:
if self . is_chunk_yaml_marked ( chunk_luid ) :
if self . is_chunk_yaml_marked_empty ( chunk_luid ) :
is_render_needed = True
if self . is_verbose :
print ( chunk_luid + " : RENDERING nonfresh previously marked empty (player in it) " )
else :
sys . stdout . write ( ' . ' )
else :
if self . is_verbose :
print ( chunk_luid + " : SKIPPING nonfresh previously marked (player in it) " )
#else:
#sys.stdout.write('.')
else :
is_render_needed = True
if self . is_verbose :
print ( chunk_luid + " : RENDERING nonfresh unmarked (player in it) " )
else :
sys . stdout . write ( ' . ' )
else :
if ( not self . is_chunk_yaml_marked ( chunk_luid ) ) :
is_render_needed = True
if self . is_verbose :
print ( chunk_luid + " : RENDERING nonfresh unmarked (simple check since has no player) " )
else :
sys . stdout . write ( ' . ' )
else :
if self . is_verbose :
print ( chunk_luid + " : SKIPPING nonfresh previously marked (simple check since has no player) " )
else :
if self . is_verbose :
print ( chunk_luid + " : SKIPPING fresh chunk " )
#if (not self.is_chunk_yaml_marked(chunk_luid)):
#is_render_needed = True
@ -750,13 +840,15 @@ class MTChunks:
# This should never happen since keeping the output of minetestmapper-numpy.py (after analyzing that output) is deprecated:
#if self.is_genresult_marked(chunk_luid) and not self.is_chunk_yaml_present(chunk_luid):
# tmp_chunk = MTChunk()
# self.set_from_genresult(tmp_chunk,chunk_luid)
# genresult_path = self.get_chunk_genresult_tmp_path(chunk_luid)
# tmp_chunk.set_from_genresult(genresult_path)
# chunk_yaml_path = self.get_chunk_yaml_path(chunk_luid)
# tmp_chunk.save_yaml(chunk_yaml_path)
# print("(saved yaml to '"+chunk_yaml_path+"')")
if is_render_needed :
rendered_count + = 1
if ( self . render_chunk ( x , z ) ) :
total_generated_count + = 1
outline_generates_count + = 1
@ -765,11 +857,13 @@ class MTChunks:
total_generated_count + = 1
outline_generates_count + = 1
tmp_png_path = self . get_chunk_image_path ( chunk_luid )
if self . is_verbose :
print ( chunk_luid + " : Skipping existing map tile file " + tmp_png_path + " (delete it to re-render) " )
#elif is_empty_chunk:
#print("Skipping empty chunk " + chunk_luid)
#else:
#print(chunk_luid+": Not rendered on dest.")
if self . is_verbose :
print ( " " ) # blank line before next z so output is more readable
self . chunkx_min - = 1
self . chunkz_min - = 1
@ -820,9 +914,96 @@ class MTChunks:
outs . write ( " total_generated_count: " + str ( total_generated_count ) + " \n " )
outs . close ( )
else :
print ( " (Not saving ' " + world_yaml_path + " ' since same value of each current variable is already in file as loaded) " )
if self . is_verbose :
print ( " (Not saving ' " + world_yaml_path + " ' since same value of each current variable is already in file as loaded) " )
if not self . is_verbose :
print ( " rendered: " + str ( rendered_count ) + " (only checks for new chunks) " )
else :
print ( " MAP ERROR: failed since this folder must contain colors.txt and minetestmapper-numpy.py " )
def read_then_remove_signals ( self ) :
signal_path = self . get_signal_path ( )
if os . path . isfile ( signal_path ) :
signals = get_dict_from_conf_file ( signal_path )
if signals is not None :
for this_key in signals . keys :
if this_key == " loop_enable " :
if not signals [ this_key ] :
self . loop_enable = False
else :
print ( " WARNING: Got signal to change loop_enable to True, so doing nothing " )
elif this_key == " refresh_players_enable " :
if type ( signals [ this_key ] ) is bool :
self . refresh_players_enable = signals [ this_key ]
else :
print ( " ERROR: expected bool for " + this_key )
elif this_key == " refresh_map_seconds " :
if ( type ( signals [ this_key ] ) is float ) or ( type ( signals [ this_key ] ) is int ) :
if float ( signals [ this_key ] ) > = 1.0 :
self . refresh_map_seconds = float ( signals [ this_key ] )
else :
print ( " ERROR: expected >=1 seconds for refresh_map_seconds (int or float) " )
else :
print ( " ERROR: expected int for " + this_key )
elif this_key == " refresh_players_seconds " :
if ( type ( signals [ this_key ] ) is float ) or ( type ( signals [ this_key ] ) is int ) :
if float ( signals [ this_key ] ) > = 1.0 :
self . refresh_players_seconds = float ( signals [ this_key ] )
else :
print ( " failed since this folder must contain colors.txt and minetestmapper-numpy.py " )
print ( " ERROR: expected >=1 seconds for refresh_players_seconds (int or float) " )
else :
print ( " ERROR: expected int for " + this_key )
elif this_key == " refresh_map_enable " :
if type ( signals [ this_key ] ) is bool :
self . refresh_map_enable = signals [ this_key ]
else :
print ( " ERROR: expected bool for " + this_key )
else :
print ( " ERROR: unknown signal ' " + this_key + " ' " )
else :
print ( " WARNING: blank ' " + signal_path + " ' " )
try :
os . remove ( )
except :
print ( " FATAL ERROR: " + __file__ + " must have permission to remove ' " + signal_path + " ' so exiting to avoid inability to avoid repeating commands at next launch. " )
self . loop_enable = False
def run_loop ( self ) :
#self.last_run_second = best_timer()
self . loop_enable = True
self . is_verbose = False
while self . loop_enable :
before_second = best_timer ( )
run_wait_seconds = self . refresh_map_seconds
if self . refresh_players_seconds < run_wait_seconds :
run_wait_seconds = self . refresh_players_seconds
print ( " " )
self . read_then_remove_signals ( )
if self . loop_enable :
if self . refresh_players_enable :
if self . last_players_refresh_second is None or ( best_timer ( ) - self . last_players_refresh_second > self . refresh_players_seconds ) :
last_players_refresh_second = best_timer ( )
self . check_players ( )
else :
print ( " waiting before doing player update " )
else :
print ( " player update is not enabled " )
if self . refresh_map_enable :
if self . last_map_refresh_second is None or ( best_timer ( ) - self . last_map_refresh_second > self . refresh_map_seconds ) :
last_map_refresh_second = best_timer ( )
self . check_map ( )
else :
print ( " waiting before doing map update " )
else :
print ( " map update is not enabled " )
else :
self . is_verbose = True
run_wait_seconds - = ( best_timer ( ) - before_second )
if ( int ( float ( run_wait_seconds ) + .5 ) > 0.0 ) :
time . sleep ( run_wait_seconds )
def run ( self ) :
if self . refresh_players_enable :
@ -831,17 +1012,17 @@ class MTChunks:
self . check_map ( )
if __name__ == ' __main__ ' :
mtchunks = MTChunks ( )
signal_path = mtchunks . get_signal_path ( )
stop_line = " loop_enable:False "
parser = argparse . ArgumentParser ( description = ' A mapper for minetest ' )
parser . add_argument ( ' --skip-map ' , type = bool , metavar = ( ' skip_map ' ) , default = False , help = ' draw map tiles and save YAML files for chunkymap.php to use ' )
#parser.add_argument('--skip-map',action='store_const', const = True, default = False, help = 'Do not draw map tiles (which would save PNG and YAML files for chunkymap.php to use)')
parser . add_argument ( ' --skip-players ' , type = bool , metavar = ( ' skip_players ' ) , default = False , help = ' update player YAML files for chunkymap.php to use ' )
#parser.add_argument('--skip-players',action='store_const', const = True, default = False, help = 'Do not update players (which would save YAML files for chunkymap.php to use)')
#parser = argparse.ArgumentParser(description='Process some integers.')
parser . add_argument ( ' --loop ' , type = bool , metavar = ( ' loop ' ) , default = False , help = ' keep running until " ' + signal_path + ' " contains the line ' + stop_line )
args = parser . parse_args ( )
mtchunks = MTChunks ( )
if not args . skip_players :
if not args . skip_map :
mtchunks . refresh_players_enable = False
print ( " Drawing players and map " )
else :
mtchunks . refresh_map_enable = False
@ -851,10 +1032,12 @@ if __name__ == '__main__':
mtchunks . refresh_players_enable = False
print ( " Drawing map only " )
else :
mtchunks . refresh_players_enable = False
mtchunks . refresh_map_enable = False
print ( " Nothing to do since " + str ( args ) )
#input("press enter to exit...")
if mtchunks . refresh_players_enable or mtchunks . refresh_map_enable :
if args . loop :
print ( " To stop chunkymap-regen loop, save a line ' " + stop_line + " ' to ' " + signal_path + " ' " )
mtchunks . run_loop ( )
else :
mtchunks . run ( )