import subprocess
import os
import shutil
from minetestinfo import *
#python_exe_path is from:
from pythoninfo import *
#from PIL import Image, ImageDraw, ImageFont, ImageColor
from PIL import Image
class ChunkymapOfflineRenderer :
minetestmapper_numpy_path = None
minetestmapper_custom_path = None
minetestmapper_py_path = None
backend_string = None
world_path = None
world_name = None
boundary_x_min = None
boundary_x_max = None
boundary_z_min = None
boundary_z_max = None
def __init__ ( self ) :
self . boundary_x_min = - 4096 #formerly -10000
self . boundary_x_max = 4096 #formerly 10000
self . boundary_z_min = - 4096 #formerly -10000
self . boundary_z_max = 4096 #formerly 10000
#NOTE: 6144*2 = 12288
#NOTE: a 16464x16384 or 12288x12288 image fails to load in browser, but 6112x6592 works
self . backend_string = get_world_var ( " backend " )
#region the following is also in singleimage.py
self . minetestmapper_numpy_path = os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " minetestmapper-numpy.py " )
self . minetestmapper_custom_path = os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " minetestmapper-expertmm.py " )
self . minetestmapper_py_path = self . minetestmapper_numpy_path
#region useful if version of minetestmapper.py from expertmm fork of minetest is used
#profile_path = None
#if 'USERPROFILE' in os.environ:
# profile_path = os.environ['USERPROFILE']
#elif 'HOME' in os.environ:
# profile_path = os.environ['HOME']
#minetest_program_path = os.path.join(profile_path, "minetest")
#minetest_util_path = os.path.join(minetest_program_path,"util")
#minetest_minetestmapper_path = os.path.join(minetest_util_path,"minetestmapper.py")
#if not os.path.isfile(self.minetestmapper_py_path):
# self.minetestmapper_py_path = minetest_minetestmapper_path
#endregion useful if version of minetestmapper.py from expertmm fork of minetest is used
#if (self.backend_string!="sqlite3"):
# minetestmapper-numpy had trouble with leveldb but this fork has it fixed so use numpy always always instead of running the following line
# self.minetestmapper_py_path = self.minetestmapper_custom_path
print ( " Chose image generator script: " + self . minetestmapper_py_path )
if not os . path . isfile ( self . minetestmapper_py_path ) :
print ( " ERROR: script does not exist, so exiting " + __file__ + " . " )
sys . exit ( 2 )
self . colors_path = os . path . join ( os . path . dirname ( os . path . abspath ( self . minetestmapper_py_path ) ) , " colors.txt " )
if not os . path . isfile ( self . colors_path ) :
print ( " ERROR: missing ' " + self . colors_path + " ' , so exiting " + __file__ + " . " )
sys . exit ( 2 )
self . world_path = minetestinfo . get_var ( " primary_world_path " )
if not os . path . isdir ( self . world_path ) :
print ( " ERROR: missing world ' " + self . world_path + " ' , so exiting " + __file__ + " . " )
sys . exit ( 2 )
else :
self . world_name = os . path . basename ( self . world_path )
#endregion the following is also in singleimage.py
def RenderSingleImage ( self ) :
genresults_folder_path = os . path . join ( os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " chunkymap-genresults " ) , self . world_name )
if not os . path . isdir ( genresults_folder_path ) :
os . makedirs ( genresults_folder_path )
genresult_path = os . path . join ( genresults_folder_path , " singleimage " + genresult_name_closer_string )
gen_error_path = os . path . join ( genresults_folder_path , " singleimage " + gen_error_name_closer_string )
cmd_suffix = " 1> \" " + genresult_path + " \" "
cmd_suffix + = " 2> \" " + gen_error_path + " \" "
#if self.boundary_x_min is None:
# print("ERROR: boundary_x_min is None")
#if self.boundary_x_max is None:
# print("ERROR: boundary_x_max is None")
#if self.boundary_z_min is None:
# print("ERROR: boundary_z_min is None")
#if self.boundary_z_max is None:
# print("ERROR: boundary_z_max is None")
geometry_string = str ( self . boundary_x_min ) + " : " + str ( self . boundary_z_min ) + " + " + str ( self . boundary_x_max - self . boundary_x_min ) + " + " + str ( self . boundary_z_max - self . boundary_z_min ) # "-10000:-10000+20000+20000" #2nd two params are sizes
#VERY BIG since singleimage mode (if no geometry param, minetestmapper-numpy reverts to its default which is -2000 2000 -2000 2000):
region_string = str ( self . boundary_x_min ) + " " + str ( self . boundary_x_max ) + " " + str ( self . boundary_z_min ) + " " + str ( self . boundary_z_max ) # "-10000 10000 -10000 10000"
#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
region_param = " --region " + region_string # minetestmapper-numpy.py --region xmin xmax zmin zmax
geometry_param = " --geometry " + geometry_string # " --geometry -10000:-10000+20000+20000" # minetestmapper-expertmm.py --geometry <xmin>:<zmin>+<width>+<height>
limit_param = geometry_param
#expertmm_region_string = str(min_x) + ":" + str(max_x) + "," + str(min_z) + ":" + str(max_z)
#cmd_no_out_string = python_exe_path+" "+self.minetestmapper_py_path+" --bgcolor '"+self.FLAG_EMPTY_HEXCOLOR+"' --input \""+minetestinfo.get_var("primary_world_path")+"\" --geometry "+geometry_string+" --output \""+tmp_png_path+"\""
png_name = " singleimage.png "
tmp_png_path = os . path . join ( genresults_folder_path , png_name )
squote = " "
if os_name != " windows " :
squote = " ' "
io_string = " --input \" " + self . world_path + " \" --output \" " + tmp_png_path + " \" "
if " numpy " in self . minetestmapper_py_path :
limit_param = region_param
io_string = " \" " + self . world_path + " \" \" " + tmp_png_path + " \" "
#geometry_param = " --region " + str(min_x) + " " + str(max_x) + " " + str(min_z) + " " + str(max_z)
#print("Using numpy style parameters.")
#print(" since using "+self.minetestmapper_py_path)
#print()
cmd_no_out_string = python_exe_path + " " + self . minetestmapper_py_path + " --bgcolor " + squote + FLAG_EMPTY_HEXCOLOR + squote + io_string + limit_param
cmd_string = cmd_no_out_string + cmd_suffix
print ( " " )
print ( " " )
print ( " Running " )
print ( " " + cmd_string )
print ( " mapper_path: " + self . minetestmapper_py_path )
print ( " backend: " + self . backend_string )
print ( " # (this may take a while...) " )
if os . path . isfile ( tmp_png_path ) :
os . remove ( tmp_png_path )
subprocess . call ( cmd_string , shell = True )
final_png_path = tmp_png_path
www_chunkymapdata_path = os . path . join ( minetestinfo . get_var ( " www_minetest_path " ) , " chunkymapdata " )
www_chunkymapdata_worlds_path = os . path . join ( www_chunkymapdata_path , " worlds " )
www_chunkymapdata_world_path = os . path . join ( www_chunkymapdata_worlds_path , self . world_name )
is_locked = False
err_count = 0
if os . path . isfile ( gen_error_path ) :
ins = open ( gen_error_path , ' r ' )
line = True
while line :
line = ins . readline ( )
if line :
if len ( line . strip ( ) ) > 0 :
err_count + = 1
line_lower = line . lower ( )
if ( " lock " in line_lower ) or ( " /lock " in line_lower ) :
is_locked = True
lock_line = line
result = None
break
ins . close ( )
if err_count < 1 :
os . remove ( gen_error_path )
if os . path . isfile ( tmp_png_path ) :
if not os . path . isdir ( www_chunkymapdata_world_path ) :
os . makedirs ( www_chunkymapdata_world_path )
if minetestinfo . contains ( " www_minetest_path " ) :
dest_png_path = os . path . join ( www_chunkymapdata_world_path , png_name )
if os . path . isfile ( dest_png_path ) :
os . remove ( dest_png_path )
print ( " Moving temp image from " + tmp_png_path + " to " + dest_png_path + " ... " )
#move_cmd_string = "mv"
#if os_name=="windows":
# move_cmd_string= "move"
#this_move_cmd_string = move_cmd_string+" \""+tmp_png_path+"\" to \""+dest_png_path+"\"..."
#subprocess.call(this_move_cmd_string, shell=True)
shutil . move ( tmp_png_path , dest_png_path ) #avoids error below according to
# os.rename(tmp_png_path, dest_png_path) # fails with the following output:
# Moving temp image from /home/owner/chunkymap/chunkymap-genresults/FCAGameAWorld/singleimage.png to /var/www/html/minetest/chunkymapdata/worlds/FCAGameAWorld/singleimage.png...
# Traceback (most recent call last):
# File "chunkymap/singleimage.py", line 157, in <module>
# cmor.RenderSingleImage()
# File "chunkymap/singleimage.py", line 118, in RenderSingleImage
# os.rename(tmp_png_path, dest_png_path)
# OSError: [Errno 18] Invalid cross-device link
final_png_path = dest_png_path
print ( " Png image saved to: " )
print ( " " + final_png_path )
print ( " Converting to jpg... " )
pngim = Image . open ( final_png_path )
#jpgim = Image.new('RGB', pngim.size, (0, 0, 0))
#jpgim.paste(pngim.convert("RGB"), (0,0,pngim.size[0],pngim.size[0]))
jpg_name = " singleimage.jpg "
dest_jpg_path = os . path . join ( www_chunkymapdata_world_path , jpg_name )
if os . path . isfile ( dest_jpg_path ) :
os . remove ( dest_jpg_path )
if not os . path . isfile ( dest_jpg_path ) :
print ( " removed old ' " + dest_jpg_path + " ' " )
else :
print ( " failed to remove ' " + dest_jpg_path + " ' " )
#jpgim.save(dest_jpg_path)
pngim . save ( dest_jpg_path , ' JPEG ' )
if os . path . isfile ( dest_jpg_path ) :
print ( " jpg image saved to: " )
print ( " " + dest_jpg_path )
os . remove ( final_png_path )
print ( " removed temporary file " + final_png_path )
else :
print ( " Could not write ' " + dest_jpg_path + " ' " )
if os . path . isfile ( genresult_path ) :
print ( " Results: " )
print ( " " + genresult_path )
mtchunk = MTChunk ( )
mtchunk . set_from_genresult ( genresult_path )
mtchunk . metadata [ " is_traversed " ] = True
dest_yaml_name = " singleimage.yml "
dest_yaml_path = os . path . join ( www_chunkymapdata_world_path , dest_yaml_name )
mtchunk . save_yaml ( dest_yaml_path )
else :
print ( " No image could be generated from ' " + self . world_path + " ' " )
if is_locked :
print ( " (database is locked--shutdown server first or try generator.py to render chunks individually). " )
cmor = ChunkymapOfflineRenderer ( )
cmor . RenderSingleImage ( )