Browse Source

fixing decachunk completeness check

master
poikilos 9 years ago
committed by Jacob Gustafson
parent
commit
97d3953412
  1. 6
      README.md
  2. 170
      chunkymap-regen.py
  3. 0
      chunkymap-signals example - verbose_enable True.txt
  4. 24
      expertmm.py
  5. 3
      minetestinfo.py
  6. 4
      update-chunkymap-on-ubuntu-from-web.sh
  7. 4
      web/chunkymap.php
  8. 2
      web/update wamp www.bat
  9. 0
      web/viewchunkymap.php

6
README.md

@ -38,7 +38,7 @@ This program comes without any warranty, to the extent permitted by applicable l
#where 1 is number of seconds: #where 1 is number of seconds:
refresh_players_seconds:1 refresh_players_seconds:1
* Has static html version of map (echo_chunkymap_table() php function) -- see example.php * Can show a static html version of map (echo_chunkymap_table() php function) -- see viewchunkymap.php
* Zoom in and out * Zoom in and out
* optionally echo name of world that was detected by the scheduled py file * optionally echo name of world that was detected by the scheduled py file
* shows player location (can optionally show only first characters of name, for privacy; there is no saved setting yet, so to adjust, you must change the value of $nonprivate_name_beginning_char_count in chunkymap.php) * shows player location (can optionally show only first characters of name, for privacy; there is no saved setting yet, so to adjust, you must change the value of $nonprivate_name_beginning_char_count in chunkymap.php)
@ -78,11 +78,11 @@ world_path
(if you are not using /var/www/html/minetest/chunkymapdata, edit chunkymap-cronjob script to use the correct directory, then) (if you are not using /var/www/html/minetest/chunkymapdata, edit chunkymap-cronjob script to use the correct directory, then)
`chmod +x set-minutely-crontab-job.sh && ./set-minutely-crontab-job.sh` `chmod +x set-minutely-crontab-job.sh && ./set-minutely-crontab-job.sh`
* IF you are using Linux * IF you are using Linux
* Either copy your code to example.php and use it, or just rename it to map.php (or anything you want) then link to it. * Rename viewchunkymap.php so it won't be overwritten on update if you want to modify it (or anything you want) then make a link to it on your website or share the link some other way.
# The commands below will work if you are using the web installer, or have done mv minetest-chunkymap-master "$HOME/Downloads/minetest-chunkymap" (and if you are using /var/www/html/minetest -- otherwise change that below) # The commands below will work if you are using the web installer, or have done mv minetest-chunkymap-master "$HOME/Downloads/minetest-chunkymap" (and if you are using /var/www/html/minetest -- otherwise change that below)
MT_MY_WEBSITE_PATH=/var/www/html/minetest MT_MY_WEBSITE_PATH=/var/www/html/minetest
sudo cp -f "$HOME/Downloads/minetest-chunkymap/web/chunkymap.php" "$MT_MY_WEBSITE_PATH/chunkymap.php" sudo cp -f "$HOME/Downloads/minetest-chunkymap/web/chunkymap.php" "$MT_MY_WEBSITE_PATH/chunkymap.php"
sudo cp --no-clobber "$HOME/Downloads/minetest-chunkymap/web/example.php" "$MT_MY_WEBSITE_PATH/viewchunkymap.php" sudo cp -f "$HOME/Downloads/minetest-chunkymap/web/viewchunkymap.php" "$MT_MY_WEBSITE_PATH/viewchunkymap.php"
sudo cp -R --no-clobber "$HOME/Downloads/minetest-chunkymap/web/images/*" "$MT_MY_WEBSITE_PATH/images/" sudo cp -R --no-clobber "$HOME/Downloads/minetest-chunkymap/web/images/*" "$MT_MY_WEBSITE_PATH/images/"
#--no-clobber: do not overwrite existing #--no-clobber: do not overwrite existing
# after you do this, the update script will do it for you if you are using /var/www/html/minetest, otherwise edit the update script before using it to get these things updated # after you do this, the update script will do it for you if you are using /var/www/html/minetest, otherwise edit the update script before using it to get these things updated

170
chunkymap-regen.py

@ -478,7 +478,7 @@ class MTChunks:
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))
install_list.append(InstalledFile("chunkymap.php",source_web_path,dest_web_path)) install_list.append(InstalledFile("chunkymap.php",source_web_path,dest_web_path))
install_list.append(InstalledFile("example.php",source_web_path,dest_web_path)) install_list.append(InstalledFile("viewchunkymap.php",source_web_path,dest_web_path))
install_list.append(InstalledFile("zoom-in.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) install_list.append(InstalledFile("zoom-in.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("zoom-out.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) install_list.append(InstalledFile("zoom-out.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
install_list.append(InstalledFile("zoom-in_disabled.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path)) install_list.append(InstalledFile("zoom-in_disabled.png", source_web_chunkymapdata_images_path, dest_web_chunkymapdata_images_path))
@ -585,6 +585,47 @@ class MTChunks:
#-1 becomes -10 #-1 becomes -10
return int(decachunky_x*10) return int(decachunky_x*10)
def is_worldborder_chunk(self, chunky_x, chunky_z):
result = False
image_path = get_chunk_image_path(chunky_x, chunky_z)
if os.path.isfile(image_path):
original_im = Image.open(image_path)
im = original_im
if im.bits<24:
im = original_im.convert('RGB')
width, height = im.size
pixel_count = width*height
pixel_count_f = float(pixel_count)
border_count = 0
FLAG_COLORS_LIST = list()
FLAG_COLOR_CHANNELS = get_list_from_hex(self.FLAG_EMPTY_HEXCOLOR)
FLAG_COLORS_LIST.append(FLAG_COLOR_CHANNELS)
FLAG_COLORS_LIST.append((255,255,255)) #for compatibility with maps generated by earlier versions ONLY
FLAG_COLORS_LIST.append((0,0,0)) #for compatibility with maps generated by earlier versions ONLY
for FLAG_COLOR in FLAG_COLORS_LIST:
if len(FLAG_COLOR)==3 or len(FLAG_COLOR)==4:
for y in range(0,height):
for x in range(0,width):
r, g, b = im.getpixel((x, y))
if r==FLAG_COLOR[0] and g==FLAG_COLOR[1] and b==FLAG_COLOR[2]:
border_pixel_count += 1
if float(border_pixel_count)/pixel_count_f >=.51:
result = True
break
else:
raw_input("ERROR: FLAG_COLOR (obtained from FLAG_EMPTY_HEXCOLOR) has "+len(FLAG_COLOR)+" element(s) (3 or 4 expected)")
return result
def is_chunk_on_todo_list(self, chunky_pos):
result = -1
if 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], chunky_pos):
result = index
break
return result
def check_decachunk_containing_chunk(self, chunky_x, chunky_z): def check_decachunk_containing_chunk(self, chunky_x, chunky_z):
chunky_coord_list = list() chunky_coord_list = list()
decachunky_x = self.get_decachunky_coord_from_chunky_coord(chunky_x) decachunky_x = self.get_decachunky_coord_from_chunky_coord(chunky_x)
@ -601,30 +642,61 @@ class MTChunks:
chunky_offset_z = 0 chunky_offset_z = 0
chunky_z = chunky_min_z chunky_z = chunky_min_z
queued_index = None queued_index = None
is_chunk_complete = False
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
while chunky_x <= chunky_max_x: while chunky_x <= chunky_max_x:
coords = (chunky_x, chunky_z) coords = (chunky_x, chunky_z)
chunky_coord_list.append( coords ) chunky_coord_list.append( coords )
if self.todo_index<len(self.todo_positions): is_any_part_queued = is_chunk_on_todo_list(coords)
for index in range(self.todo_index,len(self.todo_positions)): if is_any_part_queued:
if ivec2_equals(self.todo_positions[index], coords):
is_any_part_queued = True
if queued_chunk_coords is None: if queued_chunk_coords is None:
queued_chunk_coords = list() queued_chunk_coords = list()
queued_chunk_coords.append(coords) queued_chunk_coords.append(coords)
queued_index = index
break
if is_any_part_queued:
break break
chunky_x += 1 chunky_x += 1
if is_any_part_queued: if is_any_part_queued:
break break
chunky_z += 1 chunky_z += 1
chunky_offset_z += 1 chunky_offset_z += 1
if not is_any_part_queued:
is_chunk_complete = True
if is_chunk_complete:
### NOTE: a chunk is incomplete if any rendered nonworldborder chunk touches a nonrendered chunk
for chunky_pos in chunky_coord_list:
this_chunky_x, this_chunky_z = chunky_pos
if not self.is_chunk_rendered_on_dest(this_chunky_x, this_chunky_z):
outline_coords_list = self.get_outline_coords_list(this_chunky_x, this_chunky_z, True)
if outline_coords_list is not None:
for nearby_chunky_pos in outline_coords_list:
nearby_chunky_x, nearby_chunky_z = nearby_chunky_pos
nearby_chunk_luid = self.get_chunk_luid(nearby_chunky_x, nearby_chunky_z)
if (nearby_chunk_luid in self.chunks and self.chunks[nearby_chunk_luid].is_fresh) or self.is_chunk_rendered_on_dest(nearby_chunky_x, nearby_chunky_z):
this_is_worldborder_chunk = False
if (nearby_chunk_luid in self.chunks and "is_worldborder" in self.chunks[nearby_chunk_luid].metadata and self.chunks[nearby_chunk_luid].metadata["is_worldborder"]):
this_is_worldborder_chunk = True
elif self.is_worldborder_chunk(nearby_chunky_x, nearby_chunky_z):
this_is_worldborder_chunk = True
self.prepare_chunk_meta(nearby_chunky_x, nearby_chunky_z)
if "is_worldborder" not in self.chunks[nearby_chunk_luid].metadata or (self.chunks[nearby_chunk_luid].metadata["is_worldborder"] != True)
self.chunks[nearby_chunk_luid].metadata["is_worldborder"] = True
self.save_chunk_meta(nearby_chunky_x, nearby_chunky_z)
if not this_is_worldborder_chunk:
#empty chunk would not touch NON-worldborder chunk if decachunk was complete
is_chunk_complete = False
break
else:
print(self.min_indent+"ERROR in check_decachunk_containing_chunk: no outline of chunks could be found around "+str(chunky_pos))
if not is_chunk_complete:
break
#if not is_any_part_queued: #if not is_any_part_queued:
if queued_chunk_coords is None: #if queued_chunk_coords is None:
if is_chunk_complete and not is_any_part_queued:
print("") print("")
print("") print("")
print(" Rendering 160px decachunk "+str((decachunky_x, decachunky_z))) print(" Rendering 160px decachunk "+str((decachunky_x, decachunky_z)))
@ -1002,6 +1074,11 @@ class MTChunks:
print("ERROR: chunk changed from nonempty to empty (may happen if output of mapper was not recognized)") print("ERROR: chunk changed from nonempty to empty (may happen if output of mapper was not recognized)")
elif this_chunk.metadata["is_empty"] and os.path.isfile(dest_png_path): elif this_chunk.metadata["is_empty"] and os.path.isfile(dest_png_path):
print("ERROR: chunk marked empty though has data (may happen if output of mapper was not recognized)") print("ERROR: chunk marked empty though has data (may happen if output of mapper was not recognized)")
this_is_worldborder_chunk = self.is_worldborder_chunk(chunky_x, chunky_z)
if ("is_worldborder" not in self.chunks[chunk_luid].metadata) or this_is_worldborder_chunk != self.chunks[chunk_luid].metadata["is_worldborder"]:
self.chunks[chunk_luid].metadata["is_worldborder"] = this_is_worldborder_chunk
is_changed = True
#chunk_yaml_path = self.get_chunk_yaml_path(chunky_x, chunky_z) #chunk_yaml_path = self.get_chunk_yaml_path(chunky_x, chunky_z)
#self.create_chunk_folder(chunky_x, chunky_z) #self.create_chunk_folder(chunky_x, chunky_z)
#this_chunk.save_yaml(chunk_yaml_path) #this_chunk.save_yaml(chunk_yaml_path)
@ -1317,16 +1394,21 @@ class MTChunks:
#must check_decachunk_containing_chunk AFTER _check_map_pseudorecursion_branchfrom so check_decachunk_containing_chunk can see if there are more to do before rendering superchunk #must check_decachunk_containing_chunk AFTER _check_map_pseudorecursion_branchfrom so check_decachunk_containing_chunk can see if there are more to do before rendering superchunk
#always check since already checks queue and doesn't render decachunk on last rendered chunk, but instead on last queued chunk in decachunk #always check since already checks queue and doesn't render decachunk on last rendered chunk, but instead on last queued chunk in decachunk
#if self.rendered_this_session_count>prev_rendered_this_session_count or self.force_rerender_decachunks_enable: #if self.rendered_this_session_count>prev_rendered_this_session_count or self.force_rerender_decachunks_enable:
self.check_decachunk_containing_chunk(chunky_x, chunky_z) #self.check_decachunk_containing_chunk(chunky_x, chunky_z)
if self.verbose_enable: if self.verbose_enable:
print(min_indent+"["+str(self.todo_index)+"] branching from "+str((chunky_x, chunky_z))+" (added "+str(len(self.todo_positions)-prev_len)+")") print(min_indent+"["+str(self.todo_index)+"] branching from "+str((chunky_x, chunky_z))+" (added "+str(len(self.todo_positions)-prev_len)+")")
else: else:
#self.check_decachunk_containing_chunk(chunky_x, chunky_z)
if self.verbose_enable: if self.verbose_enable:
print(min_indent+"["+str(self.todo_index)+"] not branching from "+str((chunky_x, chunky_z))) print(min_indent+"["+str(self.todo_index)+"] not branching from "+str((chunky_x, chunky_z)))
self.todo_index += 1 self.todo_index += 1
#check_decachunk_containing_chunk AFTER incrementing todo_index so that self being queued doesn't prevent decachunk render:
self.check_decachunk_containing_chunk(chunky_x, chunky_z)
if self.todo_index>=len(self.todo_positions): # check again since may have branched above, making this untrue if self.todo_index>=len(self.todo_positions): # check again since may have branched above, making this untrue
self.save_mapvars_if_changed() self.save_mapvars_if_changed()
self.todo_index = -1 self.todo_index = -1
self.todo_positions = list()
else: else:
if self.verbose_enable: if self.verbose_enable:
print(min_indent+"(no branches)") print(min_indent+"(no branches)")
@ -1417,6 +1499,74 @@ class MTChunks:
print("") print("")
print("") print("")
def get_cross_coords_list(x_int, y_int, restrict_to_decachunk_enable=False):
results = None
if x_int is not None and y_int is not None:
tmp = list()
# North, East, South, West (cartesian):
tmp.append((x_int,y_int+1))
tmp.append((x_int+1,y_int))
tmp.append((x_int,y_int-1))
tmp.append((x_int-1,y_int))
if restrict_to_decachunk_enable:
results = list()
starting_decachunk_luid = self.get_decachunk_luid_from_chunk(x_int, y_int)
for result in tmp:
this_x, this_y = result
if self.get_decachunk_luid_from_chunk(this_x, this_y) == starting_decachunk_luid:
results.append(result)
else:
results = tmp
return results
def get_outline_coords_list(x_int, y_int, restrict_to_decachunk_enable=False):
results = None
if x_int is not None and y_int is not None:
tmp = list()
# North, NE, East, SE, South, SW, West, NW (cartesian):
tmp.append((x_int,y_int+1)) # N
tmp.append((x_int+1,y_int+1)) # NE
tmp.append((x_int+1,y_int)) # E
tmp.append((x_int+1,y_int-1)) # SE
tmp.append((x_int,y_int-1)) # S
tmp.append((x_int-1,y_int-1)) # SW
tmp.append((x_int-1,y_int)) # W
tmp.append((x_int-1,y_int+1)) # NW
if restrict_to_decachunk_enable:
results = list()
starting_decachunk_luid = self.get_decachunk_luid_from_chunk(x_int, y_int)
for result in tmp:
this_x, this_y = result
if self.get_decachunk_luid_from_chunk(this_x, this_y) == starting_decachunk_luid:
results.append(result)
else:
results = tmp
return results
def is_worldborder_count_gt_or_eq(chunky_coords_list, min_count):
result = False
count = 0
for chunky_pos in chunky_coords_list:
if is_worldborder_chunk(chunky_pos[0], chunky_pos[1]):
count += 1
if count >= min_count:
result = True
break
return result
def is_nonworldborder_isrendered_count_gt_or_eq(chunky_coords_list, min_count):
result = False
count = 0
if chunky_coords_list is not None:
for chunky_pos in chunky_coords_list:
chunky_x, chunky_z = chunky_pos
if is_chunk_rendered_on_dest(chunky_x, chunky_z) and not is_worldborder_chunk(chunky_x, chunky_z):
count += 1
if count >= min_count:
result = True
break
return result
def check_map_pseudorecursion_start(self): def check_map_pseudorecursion_start(self):
if self.todo_index<0: if self.todo_index<0:

0
chunkymap-signals example - turn on verbose.txt → chunkymap-signals example - verbose_enable True.txt

24
expertmm.py

@ -32,6 +32,8 @@ 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 ConfigManager: class ConfigManager:
#config_name = None #config_name = None
_config_path = None _config_path = None
@ -138,7 +140,7 @@ 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): def vec2_not_in(this_vec, this_list):
result = False result = False
if this_list is not None and this_vec is not None: if this_list is not None and this_vec is not None:
for try_vec in this_list: for try_vec in this_list:
@ -262,6 +264,26 @@ def save_conf_from_dict(path, this_dict, assignment_operator="=", save_nulls_ena
except: except:
pass pass
def get_list_from_hex(hex_string):
results = None
if hex_string is not None:
if len(hex_string)>=2:
if hex_string[:2]=="0x":
hex_string = hex_string[2:]
elif hex_string[:1]=="#":
hex_string = hex_string[1:]
if len(hex_string)>0 and hex_string[len(hex_string)-1:]=="h":
hex_string = hex_string[:len(hex_string)-1]
index = 0
while index<len(hex_string):
if results is None:
results = list()
if len(hex_string)-index >= 2:
results.append(int(hex_string[index:index+2], 16))
index += 2
return results
def get_tuple_from_notation(line, debug_src_name="<unknown object>"): def get_tuple_from_notation(line, debug_src_name="<unknown object>"):
result = None result = None
if line is not None: if line is not None:

3
minetestinfo.py

@ -163,6 +163,9 @@ def load_world_and_mod_data():
this_primary_world_path = try_path this_primary_world_path = try_path
minetestinfo._data["primary_world_path"] = this_primary_world_path minetestinfo._data["primary_world_path"] = this_primary_world_path
auto_chosen_world = False auto_chosen_world = False
else:
if default_world_path is not None:
minetestinfo._data["primary_world_path"] = default_world_path
minetestinfo.save_yaml() minetestinfo.save_yaml()
if get_world_var("gameid") != minetestinfo.get_var("game_path"): if get_world_var("gameid") != minetestinfo.get_var("game_path"):

4
update-chunkymap-on-ubuntu-from-web.sh

@ -24,8 +24,8 @@ MT_MY_WEBSITE_PATH=/var/www/html/minetest
if [ -f "$HOME/Downloads/minetest-chunkymap/web/chunkymap.php" ]; then if [ -f "$HOME/Downloads/minetest-chunkymap/web/chunkymap.php" ]; then
if [ -f "$MT_MY_WEBSITE_PATH/chunkymap.php" ]; then if [ -f "$MT_MY_WEBSITE_PATH/chunkymap.php" ]; then
sudo cp -f "$HOME/Downloads/minetest-chunkymap/web/chunkymap.php" "$MT_MY_WEBSITE_PATH/" sudo cp -f "$HOME/Downloads/minetest-chunkymap/web/chunkymap.php" "$MT_MY_WEBSITE_PATH/"
#sudo cp --no-clobber "$HOME/Downloads/minetest-chunkymap/web/example.php" "$MT_MY_WEBSITE_PATH/viewchunkymap.php" #sudo cp --no-clobber "$HOME/Downloads/minetest-chunkymap/web/viewchunkymap.php" "$MT_MY_WEBSITE_PATH/viewchunkymap.php"
sudo cp -f "$HOME/Downloads/minetest-chunkymap/web/example.php" "$MT_MY_WEBSITE_PATH/example.php" sudo cp -f "$HOME/Downloads/minetest-chunkymap/web/viewchunkymap.php" "$MT_MY_WEBSITE_PATH/viewchunkymap.php"
# cannot put wildcard in quotes on unix # cannot put wildcard in quotes on unix
#sudo cp -R --no-clobber $HOME/Downloads/minetest-chunkymap/web/images/* "$MT_MY_WEBSITE_PATH/images/" #sudo cp -R --no-clobber $HOME/Downloads/minetest-chunkymap/web/images/* "$MT_MY_WEBSITE_PATH/images/"
#--no-clobber: do not overwrite existing #--no-clobber: do not overwrite existing

4
web/chunkymap.php

@ -1,8 +1,10 @@
<?php <?php
#this is the backend--don't call it directly. instead do include_once('chunkymap.php'); for further info, see example.php
ini_set('display_errors', 1); ini_set('display_errors', 1);
ini_set('display_startup_errors', 1); ini_set('display_startup_errors', 1);
error_reporting(E_ALL); error_reporting(E_ALL);
if (($_SERVER['PHP_SELF'] == "chunkymap.php") or endsWith($_SERVER['PHP_SELF'],"/chunkymap.php")) {
echo "<html><body style=\"font-family:calibri,arial,helvetica,sans\">This is the backend--don't call it directly. instead do include_once('chunkymap.php'); To use the map, go to <a href=\"viewchunkymap.php\">viewchunkymap.php</a> instead.</body></html>";
}
//NOTE: for parse errors, MUST add the following line to php.ini (such as /etc/php5/apache2/php.ini): display_errors = on //NOTE: for parse errors, MUST add the following line to php.ini (such as /etc/php5/apache2/php.ini): display_errors = on

2
web/update wamp www.bat

@ -3,7 +3,7 @@ SET SRC_MT_WEB_PATH=%USERPROFILE%\Documents\GitHub\minetest-chunkymap\web
SET DEST_MT_WEBSITE_PATH=C:\wamp\www SET DEST_MT_WEBSITE_PATH=C:\wamp\www
copy /y "%SRC_MT_WEB_PATH%\browser.php" "%DEST_MT_WEBSITE_PATH%\" copy /y "%SRC_MT_WEB_PATH%\browser.php" "%DEST_MT_WEBSITE_PATH%\"
copy /y "%SRC_MT_WEB_PATH%\chunkymap.php" "%DEST_MT_WEBSITE_PATH%\" copy /y "%SRC_MT_WEB_PATH%\chunkymap.php" "%DEST_MT_WEBSITE_PATH%\"
copy /y "%SRC_MT_WEB_PATH%\example.php" "%DEST_MT_WEBSITE_PATH%\" copy /y "%SRC_MT_WEB_PATH%\viewchunkymap.php" "%DEST_MT_WEBSITE_PATH%\"
SET DEST_MT_WEBSITE_CHUNKYMAP_IMAGES_PATH=%DEST_MT_WEBSITE_PATH%\chunkymapdata\images SET DEST_MT_WEBSITE_CHUNKYMAP_IMAGES_PATH=%DEST_MT_WEBSITE_PATH%\chunkymapdata\images
copy /y "%SRC_MT_WEB_PATH%\chunkymapdata_default\images\*" "%DEST_MT_WEBSITE_CHUNKYMAP_IMAGES_PATH%" copy /y "%SRC_MT_WEB_PATH%\chunkymapdata_default\images\*" "%DEST_MT_WEBSITE_CHUNKYMAP_IMAGES_PATH%"

0
web/example.php → web/viewchunkymap.php

Loading…
Cancel
Save