Browse Source

singleimage option and some canvas work

master
poikilos 9 years ago
committed by Jacob Gustafson
parent
commit
107b911e12
  1. 7
      README.md
  2. 192
      chunkymap-regen.py
  3. 1
      colors.txt
  4. BIN
      etc/canvas coords - converting from cartesian tile coords.png
  5. BIN
      etc/loading.xcf
  6. 4
      expertmm.py
  7. 152
      minetestinfo.py
  8. 189
      nocolors.txt
  9. 13
      pythoninfo.py
  10. 105
      singleimage.py
  11. 116
      web/chunkymap.php
  12. BIN
      web/chunkymapdata_default/images/loading.png
  13. 12
      web/viewchunkymap.php

7
README.md

@ -54,10 +54,15 @@ This should be hard to corrupt since id is used as the indexer for the players d
* games_path, mods_path, players_path, and other subdirectories of the major ones should not be stored in minetestmeta.yml, since otherwise the values may deviate from the parent directories when the parent directories change.
To avoid this problem, instead derive the paths from the parent paths using your favorite language such as in the following examples:
games_path = os.path.join(minetestinfo.get_var("shared_minetest_path"), "games")
#KEEP IN MIND: gameid in world.mt is the name of the game FOLDER (NOT the name variable in the folder's game.conf)
mods_path = os.path.join(minetestinfo.get_var("game_path"), "mods")
players_path = os.path.join(minetestinfo.get_var("primary_world_path"), "players")
world_path = None
world_name = None
if minetestinfo.contains("primary_world_path"):
world_path = minetestinfo.get_var("primary_world_path")
world_name = os.path.basename(world_path)
* Keep in mind that gameid (in game.conf in a subgame folder, and world.mt in a world folder) is NOT case-sensitive: for example, minetest_game has the gameid 'Minetest' (first letter capitalized) but the worlds generated by Minetest client have the gameid 'minetest' (lowercase) in their world.mt
Yet somehow for everything else, gameid in world.mt is the name of the game FOLDER (NOT the name variable in the folder's game.conf)
* the map update function is only able to detect new chunks, and only checks edge chunks if player is present in one
* The following are saved to chunkymap.yml if not already set:
www_minetest_path (such as /var/www/html/minetest)

192
chunkymap-regen.py

@ -15,9 +15,13 @@ import math
from minetestinfo import *
from expertmm import *
#python_exe_path is from:
from pythoninfo import *
from PIL import Image, ImageDraw, ImageFont, ImageColor
#mode_to_bpp dict is from Antti Haapala. <http://stackoverflow.com/questions/28913988/is-there-a-way-to-measure-the-memory-consumption-of-a-png-image>. 7 Mar 2015. 28 Feb 2016.
mode_to_bpp = {'1':1, 'L':8, 'P':8, 'RGB':24, 'RGBA':32, 'CMYK':32, 'YCbCr':24, 'I':32, 'F':32}
#best_timer = timeit.default_timer
#if sys.platform == "win32":
# on Windows, the best timer is time.clock()
@ -26,7 +30,8 @@ mode_to_bpp = {'1':1, 'L':8, 'P':8, 'RGB':24, 'RGBA':32, 'CMYK':32, 'YCbCr':24,
# on most other platforms, the best timer is time.time()
# best_timer = time.time
# REQUIRES: see README.md
# The way to do a full render is deleting all files from the folder www_minetest_path/chunkymapdata such as /var/www/html/minetest/chunkymapdata (or chunkymap in current directory on Windows)
# The way to do a full render is deleting all files from the world folder under chunkymapdata under your system's www_minetest_path such as /var/www/html/minetest/chunkymapdata/world
#minetestmapper-numpy.py calculates the region as follows:
#(XMIN','XMAX','ZMIN','ZMAX'), default = (-2000,2000,-2000,2000)
@ -52,150 +57,7 @@ mode_to_bpp = {'1':1, 'L':8, 'P':8, 'RGB':24, 'RGBA':32, 'CMYK':32, 'YCbCr':24,
#def size(self):
#return len(self.items)
class MTDecaChunk:
metadata = None
last_changed_utc_second = None
def __init__(self):
self.metadata = {}
self.metadata["last_saved_utc_second"] = None
self.metadata["luid_list"] = None # what chunks this decachunk contains (as saved to 160px image)
def load_yaml(self, yml_path):
self.metadata = get_dict_modified_by_conf_file(self.metadata,yml_path,":")
def save_yaml(self, yml_path):
save_conf_from_dict(yml_path, self.metadata, assignment_operator=":", save_nulls_enable=False)
class MTChunk:
#x = None
#z = None
metadata = None
is_fresh = None
#luid = None
def __init__(self):
# NOTE: variables that need to be saved (and only they) should be stored in dict
self.metadata = {}
self.is_fresh = False
self.metadata["is_empty"] = False # formerly is_marked_empty
self.metadata["is_marked"] = False
self.metadata["width"] = None
self.metadata["height"] = None
self.metadata["image_w"] = None
self.metadata["image_h"] = None
self.metadata["image_left"] = None
self.metadata["image_top"] = None
self.metadata["image_right"] = None
self.metadata["image_bottom"] = None
self.metadata["is_traversed"] = False
self.metadata["tags"] = None
def load_yaml(self, yml_path):
self.metadata = get_dict_modified_by_conf_file(self.metadata,yml_path,":")
def save_yaml(self, yml_path):
save_conf_from_dict(yml_path, self.metadata, assignment_operator=":", save_nulls_enable=False)
#requires output such as from minetestmapper-numpy.py
#returns whether save is needed (whether metadata was changed)
def set_from_genresult(self, this_genresult_path):
#this_genresult_path = mtchunks.get_chunk_genresult_path(chunk_luid)
participle = "getting copy of dict"
try:
is_changed = False
old_meta = get_dict_deepcopy(self.metadata)
if os.path.isfile(this_genresult_path):
#may have data such as:
#Result image (w=16 h=16) will be written to chunk_x0z0.png
#Unknown node names: meze:meze default:stone_with_iron air default:dirt_with_snow default:stone_with_copper default:snow
#Unknown node ids: 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7
#Drawing image
#Saving to: chunk_x0z0.png
#('PNG Region: ', [0, 64, 0, 64])
#('Pixels PerNode: ', 1)
#('border: ', 0)
self.metadata["is_marked"] = True
participle = "opening '"+this_genresult_path+"'"
ins = open(this_genresult_path, 'r')
line = True
counting_number = 1
while line:
participle = "reading line "+str(counting_number)
line = ins.readline()
if line:
line_strip = line.strip()
try:
if ("does not exist" in line_strip): # official minetestmapper.py says "World does not exist" but expertmm fork and minetestmapper-numpy.py say "data does not exist"
self.metadata["is_empty"] = True
break
elif "Result image" in line_strip:
oparen_index = line_strip.find("(")
if (oparen_index>-1):
cparen_index = line_strip.find(")", oparen_index+1)
if (cparen_index>-1):
operations_string = line_strip[oparen_index+1:cparen_index]
operation_list = operations_string.split(" ")
#if len(operation_list)==2:
for operation_string in operation_list:
if "=" in operation_string:
chunks = operation_string.split("=")
if len(chunks)==2:
if chunks[0].strip()=="w":
try:
self.metadata["image_w"]=int(chunks[1].strip())
except:
print("Bad value for image w:"+str(chunks[1]))
elif chunks[0].strip()=="h":
try:
self.metadata["image_h"]=int(chunks[1].strip())
except:
print("Bad value for image h:"+str(chunks[1]))
else:
print("Bad name for image variable so ignoring variable named '"+str(chunks[0])+"'")
else:
print("Bad assignment (not 2 sides) so ignoring command '"+operation_string+"'")
else:
print("Bad assignment (operator) so ignoring command '"+operation_string+"'")
#else:
# print("Bad assignment count so ignoring operations string '"+operations_string+"'")
elif "PNG Region" in line_strip:
obracket_index = line_strip.find("[")
if obracket_index>-1:
cbracket_index = line_strip.find("]", obracket_index+1)
if cbracket_index>-1:
rect_values_string = line_strip[obracket_index+1:cbracket_index]
rect_values_list = rect_values_string.split(",")
if len(rect_values_list)==4:
#pngregion=[pngminx, pngmaxx, pngminz, pngmaxz] #from minetestmapper-numpy.py
self.metadata["image_left"]=int(rect_values_list[0].strip())
self.metadata["image_right"]=int(rect_values_list[1].strip())
self.metadata["image_top"]=int(rect_values_list[2].strip())
self.metadata["image_bottom"]=int(rect_values_list[3].strip())
else:
print("Bad map rect, so ignoring: "+rect_values_string)
elif (len(line_strip)>5) and (line_strip[:5]=="xmin:"):
self.metadata["image_left"] = int(line_strip[5:].strip())
elif (len(line_strip)>5) and (line_strip[:5]=="xmax:"):
self.metadata["image_right"] = int(line_strip[5:].strip())
elif (len(line_strip)>5) and (line_strip[:5]=="zmin:"):
#(zmin is bottom since cartesian)
self.metadata["image_bottom"] = int(line_strip[5:].strip())
elif (len(line_strip)>5) and (line_strip[:5]=="zmax:"):
#(zmax is top since cartesian)
self.metadata["image_top"] = int(line_strip[5:].strip())
except:
print("#failed to parse line:"+str(line_strip))
counting_number += 1
ins.close()
participle = "checking for changes"
is_changed = is_dict_subset(self.metadata, old_meta, False)
except:
print("Could not finish "+participle+" in set_from_genresult:")
view_traceback()
return is_changed
class MTChunks:
chunkymap_data_path = None
@ -243,18 +105,17 @@ class MTChunks:
chunkymap_players_path = None
data_16px_path = None
data_160px_path = None
FLAG_EMPTY_HEXCOLOR = "#010000"
FLAG_COLORS_LIST = None
world_name = None
chunkymap_thisworld_data_path = None
genresult_name_opener_string = "chunk_"
genresult_name_closer_string = "_mapper_result.txt"
min_indent = None
def __init__(self): #formerly checkpaths() in global scope
#self.force_rerender_decachunks_enable = True
self.FLAG_COLORS_LIST = list()
self.FLAG_COLOR_CHANNELS = get_list_from_hex(self.FLAG_EMPTY_HEXCOLOR)
self.FLAG_COLOR_CHANNELS = get_list_from_hex(FLAG_EMPTY_HEXCOLOR)
self.FLAG_COLORS_LIST.append(self.FLAG_COLOR_CHANNELS)
self.FLAG_COLORS_LIST.append((255,255,255)) #for compatibility with maps generated by earlier versions ONLY
self.FLAG_COLORS_LIST.append((0,0,0)) #for compatibility with maps generated by earlier versions ONLY
@ -297,17 +158,6 @@ class MTChunks:
# print("(ERROR: missing, so please close immediately and update primary_world_path in '"+minetestinfo._config_path+"' before next run)")
#print("")
self.python_exe_path = "python"
if os_name=="windows":
try:
alt_path = "C:\\python27\python.exe"
if os.path.isfile(alt_path):
self.python_exe_path = alt_path
#else may be in path--assume installer worked
except:
pass # do nothing
worldmt_path = os.path.join(minetestinfo.get_var("primary_world_path"), "world.mt")
self.backend_string="sqlite3"
if (os.path.isfile(worldmt_path)):
@ -334,8 +184,8 @@ class MTChunks:
else:
print("WARNING: Database backend cannot be detected (unable to ensure image generator script will render map)")
#region the following is also in singleimage.py
self.minetestmapper_fast_sqlite_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmapper-numpy.py")
self.minetestmapper_custom_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmapper-expertmm.py")
self.minetestmapper_py_path = self.minetestmapper_fast_sqlite_path
if (self.backend_string!="sqlite3"):
@ -348,6 +198,7 @@ class MTChunks:
if not os.path.isfile(self.colors_path):
print("ERROR: missing '"+self.colors_path+"', so exiting "+__file__+".")
sys.exit(2)
#endregion the following is also in singleimage.py
self.chunkymap_data_path=os.path.join(minetestinfo.get_var("www_minetest_path"),"chunkymapdata")
@ -493,6 +344,7 @@ class MTChunks:
install_list.append(InstalledFile("start.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("target_start.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("compass-rose.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("loading.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("arrow-wide-up.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("arrow-wide-down.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("arrow-wide-left.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
@ -623,7 +475,7 @@ class MTChunks:
result = True
break
else:
raw_input("ERROR: FLAG_COLOR (obtained from FLAG_EMPTY_HEXCOLOR) has "+len(FLAG_COLOR)+" element(s) (3 or 4 expected)")
raw_input("ERROR: FLAG_COLOR (obtained from FLAG_EMPTY_HEXCOLOR in minetestinfo.py) has "+len(FLAG_COLOR)+" element(s) (3 or 4 expected)")
return result
def get_index_of_chunk_on_todo_list(self, chunky_pos, allow_current_chunk_enable=False):
@ -722,7 +574,7 @@ class MTChunks:
else:
print(" USING ("+str(len(chunky_coord_list))+") chunks (region "+str(chunky_min_x)+":"+str(chunky_max_x)+","+str(chunky_min_z)+":"+str(chunky_max_z)+")")
decachunk_global_coords = decachunky_x*160, decachunky_z*160
im = Image.new("RGB", (160, 160), self.FLAG_EMPTY_HEXCOLOR)
im = Image.new("RGB", (160, 160), FLAG_EMPTY_HEXCOLOR)
decachunk_yaml_path = self.get_decachunk_yaml_path_from_decachunk(decachunky_x, decachunky_z)
decachunk_image_path = self.get_decachunk_image_path_from_decachunk(decachunky_x, decachunky_z)
combined_count = 0
@ -848,10 +700,10 @@ class MTChunks:
def get_chunk_genresult_name(self, chunky_x, chunky_z):
chunk_luid = self.get_chunk_luid(chunky_x, chunky_z)
return self.genresult_name_opener_string+chunk_luid+self.genresult_name_closer_string
return self.genresult_name_opener_string+chunk_luid+genresult_name_closer_string
def get_chunk_luid_from_genresult_name(self, file_name):
return file_name[len(self.genresult_name_opener_string):-1*len(self.genresult_name_closer_string)]
return file_name[len(self.genresult_name_opener_string):-1*len(genresult_name_closer_string)]
def get_chunk_genresult_tmp_folder(self, chunky_x, chunky_z):
#coords = self.get_coords_from_luid(chunk_luid)
@ -995,7 +847,7 @@ class MTChunks:
cmd_suffix = ""
cmd_suffix = " > \""+genresult_path+"\""
#self.mapper_id = "minetestmapper-region"
cmd_no_out_string = self.python_exe_path + " \""+self.minetestmapper_py_path + "\" --region " + str(min_x) + " " + str(max_x) + " " + str(min_z) + " " + str(max_z) + " --maxheight "+str(self.mapvars["maxheight"])+" --minheight "+str(self.mapvars["minheight"])+" --pixelspernode "+str(self.mapvars["pixelspernode"])+" \""+minetestinfo.get_var("primary_world_path")+"\" \""+tmp_png_path+"\""
cmd_no_out_string = python_exe_path + " \""+self.minetestmapper_py_path + "\" --region " + str(min_x) + " " + str(max_x) + " " + str(min_z) + " " + str(max_z) + " --maxheight "+str(self.mapvars["maxheight"])+" --minheight "+str(self.mapvars["minheight"])+" --pixelspernode "+str(self.mapvars["pixelspernode"])+" \""+minetestinfo.get_var("primary_world_path")+"\" \""+tmp_png_path+"\""
cmd_string = cmd_no_out_string + cmd_suffix
if self.minetestmapper_py_path==self.minetestmapper_custom_path:#if self.backend_string!="sqlite3": #if self.mapper_id=="minetestmapper-region":
@ -1016,9 +868,15 @@ class MTChunks:
#if os.path.isfile(region_capable_script_path):
#script_path = region_capable_script_path
geometry_string = str(min_x)+":"+str(min_z)+"+"+str(int(max_x)-int(min_x)+1)+"+"+str(int(max_z)-int(min_z)+1) # +1 since max-min is exclusive and width must be inclusive for minetestmapper.py
geometry_param = " --geometry "+geometry_string
#expertmm_region_string = str(min_x) + ":" + str(max_x) + "," + str(min_z) + ":" + str(max_z)
#cmd_string="sudo python "+script_path+" --input \""+minetestinfo.get_var("primary_world_path")+"\" --geometry "+geometry_value_string+" --output \""+tmp_png_path+"\""+cmd_suffix
cmd_no_out_string = self.python_exe_path+" "+self.minetestmapper_py_path+" --bgcolor '"+self.FLAG_EMPTY_HEXCOLOR+"' --input \""+minetestinfo.get_var("primary_world_path")+"\" --geometry "+geometry_string+" --output \""+tmp_png_path+"\""
world_path = minetestinfo.get_var("primary_world_path")
io_string = " --input \""+world_path+"\" --output \""+tmp_png_path+"\""
#if "numpy" in self.minetestmapper_py_path:
# io_string = " \""+world_path+"\" \""+tmp_png_path+"\""
# geometry_param = " --region " + str(min_x) + " " + str(max_x) + " " + str(min_z) + " " + str(max_z)
cmd_no_out_string = python_exe_path+" "+self.minetestmapper_py_path+" --bgcolor '"+FLAG_EMPTY_HEXCOLOR+"'"+geometry_param+io_string
cmd_string = cmd_no_out_string + cmd_suffix
#sudo python /home/owner/minetest/util/minetestmapper.py --bgcolor '#010000' --input "/home/owner/.minetest/worlds/FCAGameAWorld" --output /var/www/html/minetest/chunkymapdata/entire.png > entire-mtmresult.txt
#sudo python /home/owner/minetest/util/chunkymap/minetestmapper.py --input "/home/owner/.minetest/worlds/FCAGameAWorld" --geometry 0:0+16+16 --output /var/www/html/minetest/chunkymapdata/chunk_x0z0.png > /home/owner/minetest/util/chunkymap-genresults/chunk_x0z0_mapper_result.txt
@ -1590,7 +1448,7 @@ class MTChunks:
player_position = None
#if (file_name[:len(badstart_string)]!=badstart_string):
if (file_name[:1]!="."):
if len(file_name)>=len(self.genresult_name_opener_string)+4+len(self.genresult_name_closer_string):
if len(file_name)>=len(self.genresult_name_opener_string)+4+len(genresult_name_closer_string):
chunk_luid = self.get_chunk_luid_from_genresult_name(file_name)
coords = self.get_coords_from_luid(chunk_luid)
if coords is not None:

1
colors.txt

@ -75,4 +75,3 @@ default:apple 200 0 0
default:desert_sand 210 180 50
default:desert_stone 150 100 30
default:dry_shrub 100 80 40

BIN
etc/canvas coords - converting from cartesian tile coords.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
etc/loading.xcf

Binary file not shown.

4
expertmm.py

@ -5,6 +5,10 @@ import copy
verbose_enable = False
os_name = "GNU/Linux"
if os.sep=="\\":
os_name = "windows"
print("Windows detected")
#formerly pcttext:
#uppercase_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

152
minetestinfo.py

@ -44,6 +44,7 @@ after_broken["default:stone_with_diamond"] = "default:diamond"
#after_broken[""] = ""
#after_broken[""] = ""
genresult_name_closer_string = "_mapper_result.txt"
loaded_mod_list = list()
@ -55,11 +56,154 @@ user_excluded_mod_count = 0
minetestinfo = ConfigManager(os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmeta.yml"), ":")
os_name="linux"
if (os.path.sep!="/"):
os_name="windows"
print("Windows detected")
game_path_from_gameid_dict = {}
FLAG_EMPTY_HEXCOLOR = "#010000"
class MTDecaChunk:
metadata = None
last_changed_utc_second = None
def __init__(self):
self.metadata = {}
self.metadata["last_saved_utc_second"] = None
self.metadata["luid_list"] = None # what chunks this decachunk contains (as saved to 160px image)
def load_yaml(self, yml_path):
self.metadata = get_dict_modified_by_conf_file(self.metadata,yml_path,":")
def save_yaml(self, yml_path):
save_conf_from_dict(yml_path, self.metadata, assignment_operator=":", save_nulls_enable=False)
class MTChunk:
#x = None
#z = None
metadata = None
is_fresh = None
#luid = None
def __init__(self):
# NOTE: variables that need to be saved (and only they) should be stored in dict
self.metadata = {}
self.is_fresh = False
self.metadata["is_empty"] = False # formerly is_marked_empty
self.metadata["is_marked"] = False
self.metadata["width"] = None
self.metadata["height"] = None
self.metadata["image_w"] = None
self.metadata["image_h"] = None
self.metadata["image_left"] = None
self.metadata["image_top"] = None
self.metadata["image_right"] = None
self.metadata["image_bottom"] = None
self.metadata["is_traversed"] = False
self.metadata["tags"] = None
def load_yaml(self, yml_path):
self.metadata = get_dict_modified_by_conf_file(self.metadata,yml_path,":")
def save_yaml(self, yml_path):
save_conf_from_dict(yml_path, self.metadata, assignment_operator=":", save_nulls_enable=False)
#requires output such as from minetestmapper-numpy.py
#returns whether save is needed (whether metadata was changed)
def set_from_genresult(self, this_genresult_path):
#this_genresult_path = mtchunks.get_chunk_genresult_path(chunk_luid)
participle = "getting copy of dict"
try:
is_changed = False
old_meta = get_dict_deepcopy(self.metadata)
if os.path.isfile(this_genresult_path):
#may have data such as:
#Result image (w=16 h=16) will be written to chunk_x0z0.png
#Unknown node names: meze:meze default:stone_with_iron air default:dirt_with_snow default:stone_with_copper default:snow
#Unknown node ids: 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7
#Drawing image
#Saving to: chunk_x0z0.png
#('PNG Region: ', [0, 64, 0, 64])
#('Pixels PerNode: ', 1)
#('border: ', 0)
self.metadata["is_marked"] = True
participle = "opening '"+this_genresult_path+"'"
ins = open(this_genresult_path, 'r')
line = True
counting_number = 1
while line:
participle = "reading line "+str(counting_number)
line = ins.readline()
if line:
line_strip = line.strip()
try:
if ("does not exist" in line_strip): # official minetestmapper.py says "World does not exist" but expertmm fork and minetestmapper-numpy.py say "data does not exist"
self.metadata["is_empty"] = True
break
elif "Result image" in line_strip:
oparen_index = line_strip.find("(")
if (oparen_index>-1):
cparen_index = line_strip.find(")", oparen_index+1)
if (cparen_index>-1):
operations_string = line_strip[oparen_index+1:cparen_index]
operation_list = operations_string.split(" ")
#if len(operation_list)==2:
for operation_string in operation_list:
if "=" in operation_string:
chunks = operation_string.split("=")
if len(chunks)==2:
if chunks[0].strip()=="w":
try:
self.metadata["image_w"]=int(chunks[1].strip())
except:
print("Bad value for image w:"+str(chunks[1]))
elif chunks[0].strip()=="h":
try:
self.metadata["image_h"]=int(chunks[1].strip())
except:
print("Bad value for image h:"+str(chunks[1]))
else:
print("Bad name for image variable so ignoring variable named '"+str(chunks[0])+"'")
else:
print("Bad assignment (not 2 sides) so ignoring command '"+operation_string+"'")
else:
print("Bad assignment (operator) so ignoring command '"+operation_string+"'")
#else:
# print("Bad assignment count so ignoring operations string '"+operations_string+"'")
elif "PNG Region" in line_strip:
obracket_index = line_strip.find("[")
if obracket_index>-1:
cbracket_index = line_strip.find("]", obracket_index+1)
if cbracket_index>-1:
rect_values_string = line_strip[obracket_index+1:cbracket_index]
rect_values_list = rect_values_string.split(",")
if len(rect_values_list)==4:
#pngregion=[pngminx, pngmaxx, pngminz, pngmaxz] #from minetestmapper-numpy.py
self.metadata["image_left"]=int(rect_values_list[0].strip())
self.metadata["image_right"]=int(rect_values_list[1].strip())
self.metadata["image_top"]=int(rect_values_list[2].strip())
self.metadata["image_bottom"]=int(rect_values_list[3].strip())
else:
print("Bad map rect, so ignoring: "+rect_values_string)
elif (len(line_strip)>5) and (line_strip[:5]=="xmin:"):
self.metadata["image_left"] = int(line_strip[5:].strip())
elif (len(line_strip)>5) and (line_strip[:5]=="xmax:"):
self.metadata["image_right"] = int(line_strip[5:].strip())
elif (len(line_strip)>5) and (line_strip[:5]=="zmin:"):
#(zmin is bottom since cartesian)
self.metadata["image_bottom"] = int(line_strip[5:].strip())
elif (len(line_strip)>5) and (line_strip[:5]=="zmax:"):
#(zmax is top since cartesian)
self.metadata["image_top"] = int(line_strip[5:].strip())
except:
print("#failed to parse line:"+str(line_strip))
counting_number += 1
ins.close()
participle = "checking for changes"
is_changed = is_dict_subset(self.metadata, old_meta, False)
except:
print("Could not finish "+participle+" in set_from_genresult:")
view_traceback()
return is_changed
def get_gameid_from_game_path(path):
result = None

189
nocolors.txt

@ -0,0 +1,189 @@
0
1
2
3
4
5
6
7
8
9
a
b
c
d
e
f
10
11
12
13
14
15
16
17
18
19
1a
1b
1c
1d
1e
1f
20
21
ethereal:willow_trunk
moretrees:oak_trunk
technic:mineral_lead
moretrees:apple_tree_trunk
ethereal:banana
bushes:BushLeaves1
bushes:BushLeaves2
flowers:dandelion_white
ethereal:bush
tsm_pyramids:deco_stone3
tsm_pyramids:deco_stone2
whiteshell:whiteshell
ethereal:onion_4
ethereal:quicksand2
bushes:youngtree2_bottom
default:stone_with_iron
moretrees:apple_tree_leaves
seacoral:seacoraldirtcyan
ethereal:gray_dirt
seacoral:seacoraldirtlime
youngtrees:youngtree_middle
vines:vine_end
seaplants:seaplantsdirtseagrassgreen
ferns:fern_trunk_big
noairblocks:water_sourcex
ethereal:mushroom_dirt
default:pine_tree
ignore
seacoral:seacoraldirtmagenta
flowers:mushroom_red
ethereal:sandy
ethereal:jungle_dirt
ethereal:mushroom
clams:dirtalgae
flowers:tulip
seacoral:seacoralsandcyan
ethereal:willow_twig
default:sandstonebrick
ferns:tree_fern_leaves_giant
ethereal:orange
moretrees:sequoia_leaves
trunks:twig_9
trunks:twig_7
trunks:twig_5
seacoral:seacoraldirtskyblue
moreores:mineral_silver
ethereal:banana_trunk
moretrees:pine_cone
seacoral:coralredviolet
moretrees:acorn
vines:vine_middle
technic:marble
seacoral:seacoralsandskyblue
ferns:fern_03
ferns:fern_02
ferns:fern_01
moretrees:palm_trunk
seacoral:seacoralsandmagenta
seacoral:seacoralsandlime
default:pine_needles
ferns:fern_trunk_big_top
seaplants:seaplantsdirtkelpbrown
tsm_pyramids:trap
ethereal:birch_trunk
ferns:horsetail_02
ferns:horsetail_03
bushes:bushbranches4
ferns:horsetail_01
bushes:bushbranches2
bushes:bushbranches3
ferns:horsetail_04
bushes:bushbranches1
clams:sandalgae
moretrees:oak_leaves
ethereal:mushroom_pore
ferns:tree_fern_leave_big
bakedclay:orange
seacoral:seacoraldirtaqua
moretrees:sequoia_trunk
bakedclay:red
seaplants:seaplantssandkelpgreen
stairs:stair_cobble
ethereal:snowygrass
woodsoils:dirt_with_leaves_1
moreores:mineral_tin
flowers:seaweed
ethereal:bamboo_leaves
ethereal:palm_trunk
seacoral:seacoralsandaqua
trunks:moss
seaplants:seaplantsdirtseagrassred
default:acacia_tree
default:jungleleaves
flowers:viola
seacoral:seacoralsandredviolet
flowers:rose
ethereal:bamboo
default:stone_with_copper
moretrees:jungletree_leaves_yellow
moretrees:coconut
ethereal:palmleaves
ethereal:green_dirt
seaplants:seaplantssandseagrassgreen
trunks:twig_8
trunks:moss_fungus
air
flowers:waterlily
ethereal:redwood_leaves
trunks:twig_10
trunks:twig_11
trunks:twig_12
trunks:twig_13
seacoral:corallime
technic:mineral_zinc
ethereal:strawberry_7
ethereal:prairie_dirt
default:grass_1
default:grass_3
default:grass_2
default:grass_5
default:grass_4
ethereal:coconut
ethereal:bamboo_dirt
cavestuff:desert_pebble_2
cavestuff:desert_pebble_1
flowers:geranium
ethereal:bananaleaves
moretrees:jungletree_trunk
flowers:mushroom_brown
bakedclay:grey
default:acacia_leaves
youngtrees:youngtree_bottom
ferns:tree_fern_leave_big_end
lapis:pyrite_ore
flowers:dandelion_yellow
flowers:seaweed_2
flowers:seaweed_3
flowers:seaweed_4
ethereal:orange_leaves
tsm_mines:dummy
moretrees:jungletree_leaves_red
seaplants:seaplantsdirtkelpgreen
ethereal:grove_dirt
tsm_pyramids:spawner_mummy
ethereal:mushroom_trunk
ethereal:fern
ethereal:birch_leaves
moretrees:palm_leaves
seacoral:coralcyan
ethereal:redwood_trunk
seaplants:seaplantssandkelpbrown
youngtrees:youngtree_top
seacoral:seacoraldirtredviolet
seaplants:seaplantssandseagrassred

13
pythoninfo.py

@ -0,0 +1,13 @@
import os
from expertmm import *
python_exe_path = "python"
if os_name=="windows":
try:
alt_path = "C:\\python27\python.exe"
if os.path.isfile(alt_path):
python_exe_path = alt_path
#else may be in path--assume installer worked
except:
pass # do nothing

105
singleimage.py

@ -0,0 +1,105 @@
import subprocess
import os
from minetestinfo import *
#python_exe_path is from:
from pythoninfo import *
class ChunkymapOfflineRenderer:
minetestmapper_fast_sqlite_path = None
minetestmapper_custom_path = None
minetestmapper_py_path = None
backend_string = None
world_path = None
world_name = None
def __init__(self):
self.backend_string = get_world_var("backend")
#region the following is also in singleimage.py
self.minetestmapper_fast_sqlite_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmapper-numpy.py")
self.minetestmapper_custom_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "minetestmapper-expertmm.py")
self.minetestmapper_py_path = self.minetestmapper_fast_sqlite_path
if (self.backend_string!="sqlite3"):
# Since minetestmapper-numpy has trouble with leveldb:
self.minetestmapper_py_path = self.minetestmapper_custom_path
print("Chose image generator script: "+self.minetestmapper_py_path)
if not os.path.isfile(self.minetestmapper_py_path):
print("ERROR: script does not exist, so exiting "+__file__+".")
sys.exit(2)
self.colors_path = os.path.join(os.path.dirname(os.path.abspath(self.minetestmapper_py_path)), "colors.txt")
if not os.path.isfile(self.colors_path):
print("ERROR: missing '"+self.colors_path+"', so exiting "+__file__+".")
sys.exit(2)
self.world_path = minetestinfo.get_var("primary_world_path")
if not os.path.isdir(self.world_path):
print("ERROR: missing world '"+self.world_path+"', so exiting "+__file__+".")
sys.exit(2)
else:
self.world_name = os.path.basename(self.world_path)
#endregion the following is also in singleimage.py
def RenderSingleImage(self):
genresults_folder_path = os.path.join( os.path.join(os.path.dirname(os.path.abspath(__file__)), "chunkymap-genresults"), self.world_name)
if not os.path.isdir(genresults_folder_path):
os.makedirs(genresults_folder_path)
genresult_path = os.path.join(genresults_folder_path, "singleimage"+genresult_name_closer_string)
cmd_suffix = " > \""+genresult_path+"\""
#blank since singleimage mode:
#geometry_string = str(min_x)+":"+str(min_z)+"+"+str(int(max_x)-int(min_x)+1)+"+"+str(int(max_z)-int(min_z)+1) # +1 since max-min is exclusive and width must be inclusive for minetestmapper.py
#geometry_param = " --geometry "+geometry_string
geometry_param = ""
#expertmm_region_string = str(min_x) + ":" + str(max_x) + "," + str(min_z) + ":" + str(max_z)
#cmd_no_out_string = python_exe_path+" "+self.minetestmapper_py_path+" --bgcolor '"+self.FLAG_EMPTY_HEXCOLOR+"' --input \""+minetestinfo.get_var("primary_world_path")+"\" --geometry "+geometry_string+" --output \""+tmp_png_path+"\""
png_name = "singleimage.png"
tmp_png_path = os.path.join(genresults_folder_path, png_name)
squote = ""
if os_name!="windows":
squote = "'"
io_string = " --input \""+self.world_path+"\" --output \""+tmp_png_path+"\""
if "numpy" in self.minetestmapper_py_path:
io_string = " \""+self.world_path+"\" \""+tmp_png_path+"\""
#geometry_param = " --region " + str(min_x) + " " + str(max_x) + " " + str(min_z) + " " + str(max_z)
cmd_no_out_string = python_exe_path+" "+self.minetestmapper_py_path+" --bgcolor "+squote+FLAG_EMPTY_HEXCOLOR+squote+io_string
cmd_string = cmd_no_out_string + cmd_suffix
print("")
print("")
print("Running")
print(" "+cmd_string)
print(" # (this may take a while...)")
if os.path.isfile(tmp_png_path):
os.remove(tmp_png_path)
subprocess.call(cmd_string, shell=True)
final_png_path = tmp_png_path
www_chunkymapdata_path = os.path.join(minetestinfo.get_var("www_minetest_path"), "chunkymapdata")
www_chunkymapdata_worlds_path = os.path.join(www_chunkymapdata_path, "worlds")
www_chunkymapdata_world_path = os.path.join(www_chunkymapdata_worlds_path, self.world_name)
if os.path.isfile(tmp_png_path):
if not os.path.isdir(www_chunkymapdata_world_path):
os.makedirs(www_chunkymapdata_world_path)
if minetestinfo.contains("www_minetest_path"):
dest_png_path = os.path.join(www_chunkymapdata_world_path, png_name)
if os.path.isfile(dest_png_path):
os.remove(dest_png_path)
os.rename(tmp_png_path, dest_png_path)
final_png_path = dest_png_path
print("Map image saved to:")
print(" "+final_png_path)
if os.path.isfile(genresult_path):
print("Results:")
print(" "+genresult_path)
mtchunk = MTChunk()
mtchunk.set_from_genresult(genresult_path)
mtchunk.metadata["is_traversed"] = True
dest_yaml_name = "singleimage.yml"
dest_yaml_path = os.path.join(www_chunkymapdata_world_path, dest_yaml_name)
mtchunk.save_yaml(dest_yaml_path)
else:
print("No image could be generated from '"+self.world_path+"'")
cmor = ChunkymapOfflineRenderer()
cmor.RenderSingleImage()

116
web/chunkymap.php

@ -55,7 +55,6 @@ $chunkymap_camera_pan_delta=.5;
$chunkymap_view_min_zoom=0.0173415299; //1.0/$chunk_dimension_min; //should be a number that would get to exactly 100 eventually if multiplied by chunkymap_zoom_delta repeatedly (such as 0.09765625 if chunkymap_zoom_delta were 2); 0.005 was avoided since tiles used to be 80x80 pixels
$chunkymap_view_max_zoom=16585998.48141; //13107200.0;
$decachunk_prefix_string="decachunk_";
$decachunk_prefix_then_x_string=$decachunk_prefix_string."x";
$chunk_prefix_string="chunk_";
@ -251,6 +250,18 @@ function get_javascript_bool_value($this_bool) {
return $result;
}
function get_javascript_int_value($this_val) {
$result = $this_val;
if ($this_val === null) {
$result = "null";
}
else {
$this_val = intval($this_val);
$result = "$this_val";
}
return $result;
}
//chunk_mode_enable: shows chunk png images instead of decachunk jpg images (slower)
//visual_debug_enable: draws colored rectangles based on yml files instead of drawing images
function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_mode_enable) {
@ -263,6 +274,7 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
global $chunkymap_change_zoom_multiplier;
global $chunkymap_camera_pan_delta;
global $world_name;
global $chunkymapdata_thisworld_path;
check_world();
@ -335,6 +347,43 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
$tile_x_count = $max_tiley_x-$min_tiley_x+1;
$tile_z_count = $max_tiley_z-$min_tiley_z+1;
$si_yml_path = "$chunkymapdata_thisworld_path/singleimage.yml";
$si_metadata = null;
$si_left = null;
$si_top = null;
$si_w = null;
$si_h = null;
$si_bottom = null;
$si_right = null;
if (is_file($si_yml_path)) {
$si_metadata = get_dict_from_conf($si_yml_path, ":");
if ($si_metadata!==null) {
if (isset($si_metadata["image_top"]) and isset($si_metadata["image_bottom"])) {
if ($si_metadata["image_top"]<$si_metadata["image_bottom"]) {
$cartesian_bottom = $si_metadata["image_top"];
$si_metadata["image_top"] = $si_metadata["image_bottom"];
$si_metadata["image_bottom"] = $cartesian_bottom;
}
}
if (isset($si_metadata["image_left"])) { $si_left=$si_metadata["image_left"]; }
if (isset($si_metadata["image_top"])) { $si_top=$si_metadata["image_top"]; }
if (isset($si_metadata["image_w"])) { $si_w=$si_metadata["image_w"]; }
if (isset($si_metadata["image_h"])) { $si_h=$si_metadata["image_h"]; }
if (isset($si_metadata["image_bottom"])) {
$si_bottom = $si_metadata["image_bottom"];
}
elseif (isset($si_metadata["image_top"]) and isset($si_metadata["image_h"])) {
$si_bottom=$si_top-$si_h; //minus since cartesian (which is assured above)
}
if (isset($si_metadata["image_right"])) {
$si_right = $si_metadata["image_right"];
}
elseif (isset($si_metadata["image_left"]) and isset($si_metadata["image_w"])) {
$si_right=$si_left+$si_w;
}
}
}
if ($html4_mode_enable!==true) {
echo '<canvas id="myCanvas"></canvas> ';
echo '<script>
@ -351,6 +400,12 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
var chunks_per_tile_z_count='.$chunks_per_tile_z_count.';
var tile_w='.$tile_w.';
var tile_h='.$tile_h.';
var si_left='.get_javascript_int_value($si_left).';
var si_top='.get_javascript_int_value($si_top).';
var si_w='.get_javascript_int_value($si_w).';
var si_h='.get_javascript_int_value($si_h).';
var si_bottom='.get_javascript_int_value($si_bottom).';
var si_right='.get_javascript_int_value($si_right).';
var EM_PER_WIDTH_COUNT = '.$EM_PER_WIDTH_COUNT.';
var size_1em_pixel_count = null;
var font_string = null;
@ -364,6 +419,10 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
var pen_x = null;
var world_camera_w = null; //calculated below
var world_camera_h = null; //calculated below
var world_camera_left = null; //calculated below
var world_camera_top = null; //calculated below
var world_camera_right = null; //calculated below
var world_camera_bottom = null; //calculated below
var current_w = null;
var current_h = null;
var current_ratio = null;
@ -371,6 +430,7 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
var zoom_out_button_index = null;
var zoom_out_label_index = null;
var location_label_index = null;
var debug_label = null;
function zoom_in() {
chunkymap_view_zoom*=chunkymap_zoom_delta;
@ -390,6 +450,10 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
world_camera_h = (800/tile_h) * (1.0/chunkymap_view_zoom);
world_camera_w = world_camera_h*current_ratio;
}
world_camera_left = chunkymap_view_x - (world_camera_w/2.0);
world_camera_top = chunkymap_view_z + (world_camera_h/2.0); //plus since cartesian
world_camera_right = world_camera_left+world_camera_w;
world_camera_bottom = world_camera_top - world_camera_h; //minus since cartesion
}
function process_resize(ctx) {
@ -522,13 +586,34 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
ctx.fillStyle = "black";
ctx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.fillStyle = "white";
ctx.rect(20,20,150,100);
ctx.stroke();
//ctx.fillStyle = "white";
//ctx.rect(20,20,150,100);
//ctx.stroke();
//size_1pt_pixel_count = ctx.canvas.height/600.0;
var zoomed_size_1pt_pixel_count = size_1pt_pixel_count*chunkymap_view_zoom;
var bw_index = 0;
var si = document.getElementById("singleimage");
if ((si!==null)&&(si_left!==null)&&(si_top!==null)&&(si_w!==null)&&(si_h!==null)) {
var si_canvas_x = (si_left-world_camera_left)*zoomed_size_1pt_pixel_count;
//var si_canvas_right = (si_right-world_camera_left)*zoomed_size_1pt_pixel_count;
//var si_canvas_w = si_canvas_right-si_canvas_x;
var si_canvas_w = si_w*zoomed_size_1pt_pixel_count;
var si_canvas_right = si_canvas_x+si_canvas_w;
//invert since cartesian:
var si_canvas_y = (world_camera_top-si_top)*zoomed_size_1pt_pixel_count;
//var si_canvas_bottom = (world_camera_bottom-si_top)*zoomed_size_1pt_pixel_count;
//var si_canvas_h = si_canvas_bottom-si_canvas_y;
var si_canvas_h = si_h*zoomed_size_1pt_pixel_count;
var si_canvas_bottom = si_canvas_y + si_canvas_h;
ctx.drawImage(si, si_canvas_x, si_canvas_y, si_canvas_w, si_canvas_h);
//debug_label["text"] = "--map "+si_canvas_x+":"+si_canvas_right+","+si_canvas_y+":"+si_canvas_bottom+" "+si_canvas_w+"x"+si_canvas_h+" --camera "+world_camera_left+":"+world_camera_right+","+world_camera_bottom+":"+world_camera_top;
debug_label["text"] = "--map "+Math.round(si_canvas_x)+":"+Math.round(si_canvas_right)+","+Math.round(si_canvas_y)+":"+Math.round(si_canvas_bottom)+" "+Math.round(si_canvas_w)+"x"+Math.round(si_canvas_h)+" --camera "+Math.round(world_camera_left)+":"+Math.round(world_camera_right)+","+Math.round(world_camera_bottom)+":"+Math.round(world_camera_top);
}
for (i=0; i<bawidgets.length; i++) {
this_widget = bawidgets[i];
this_img = this_widget["image"]
@ -610,9 +695,14 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
last_bawidget["image"]=zoom_out_img;
zoom_out_img.style.visibility="hidden";
document.getElementById("zoom_out_disabled").style.visibility="hidden";
pen_y += tmp_h+padding_h;
pen_y += tmp_h+size_1em_pixel_count+padding_h;
pen_x -= tmp_w;
//DEBUG LABEL (no click):
bw_index = add_bawidget(pen_x+compass_rose_w/4, pen_y, tmp_w, tmp_h, null, "debug_label");
debug_label = last_bawidget;
//done on each draw: last_bawidget["text"] =
pen_y += size_1em_pixel_count + padding_h;
draw_map();
}
@ -626,7 +716,19 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
$td_tile_placeholder_content = $td_chunk_placeholder_content;
}
else {
$td_tile_placeholder_content = $td_decachunk_placeholder_content
$td_tile_placeholder_content = $td_decachunk_placeholder_content;
}
$si_path = "$chunkymapdata_thisworld_path/singleimage.png";
$si_yml_path = "$chunkymapdata_thisworld_path/singleimage.yml";
if (is_file($si_path) and is_file($si_yml_path)) {
//$style_append="; visibility:hidden";
$style_append="";
echo '<table style="width:100%'."$style_append".'">'."\r\n";
echo ' <tr><td style="background-image:url(\'chunkymapdata/images/loading.png\'); background-repeat: no-repeat; background-size: 100% 100%">'."\r\n";
$style_append="";
echo ' <img id="singleimage" style="width:100%; $style_append" src="'."$si_path".'"/>'."\r\n";
echo ' </td></tr>'."\r\n";
echo '</table>'."\r\n";
}
echo '<img id="compass_rose" src="chunkymapdata/images/compass_rose.png"/>';
echo '<img id="zoom_in" src="chunkymapdata/images/zoom_in.png"/>';
@ -642,7 +744,7 @@ function echo_chunkymap_canvas($chunk_mode_enable, $visual_debug_enable, $html4_
echo " <td style=\"width:95%\"><a href=\"?world_name=$world_name&chunkymap_view_zoom=$chunkymap_view_zoom&chunkymap_view_x=$chunkymap_view_x&chunkymap_view_z=".($chunkymap_view_z+($world_camera_h*$chunkymap_camera_pan_delta))."#chunkymap_top\">".'<img src="chunkymapdata/images/arrow-wide-up.png" style="width:90%"/>'.'</a></td>'."\r\n";
echo ' <td style="width:5%">'."$td_tile_placeholder_content".'</td>'."\r\n";
echo ' </tr>'."\r\n";
$cell_perc=intval(round(100.0/$decachunky_count_x));
$cell_perc=intval(round(100.0/$tile_x_count));
echo ' <tr>'."\r\n";
echo " <td style=\"width:5%\"><a href=\"?world_name=$world_name&chunkymap_view_zoom=$chunkymap_view_zoom&chunkymap_view_x=".($chunkymap_view_x-($world_camera_w*$chunkymap_camera_pan_delta))."&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:95%">'."\r\n";

BIN
web/chunkymapdata_default/images/loading.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

12
web/viewchunkymap.php

@ -1,10 +1,16 @@
<html style="width:100%; height:100%">
<head>
<title>Chunkymap</title>
<meta http-equiv="refresh" content="30">
<?php
$html4_mode_enable=false; //if true, does not echo canvas nor client-side scripting
if ($html4_mode_enable===true) {
echo '<meta http-equiv="refresh" content="45">';
}
echo '
</head>
<body style="font-family:calibri,sans; width:100%; height:100%; margin:0; padding:0" topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">
<?php
';
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
@ -28,7 +34,7 @@ if (is_file('chunkymap.php')) {
//echo "<table><tr><td style=\"text-align:left\">";
$chunk_mode_enable=true; //(this should normally be false) if true, uses 16x16 png files instead of the 160x160 decachunks; it is slower but may have more of the map during times when new chunks are explored but before the render queue is done and the decachunk images are created from the chunk images.);
$visual_debug_enable=false; //if true, this renders colors based on yml files instead of drawing images (and does not echo images at all)
$html4_mode_enable=false; //if true, does not echo canvas nor client-side scripting
echo_chunkymap_canvas($chunk_mode_enable,$visual_debug_enable,$html4_mode_enable);
//echo_chunkymap_as_chunk_table(false);
//echo_decachunk_table();

Loading…
Cancel
Save