Makes the json-maploader legible in DM

This commit is contained in:
Crazylemon64
2016-08-06 17:40:33 -07:00
parent 40fe120067
commit a9f3d0464e
4 changed files with 225 additions and 193 deletions
+3 -1
View File
@@ -401,7 +401,9 @@ proc/checkhtml(var/t)
var/keylength = length(char)
while(index)
log_debug("Bad string given to dmm encoder! [text]")
text = copytext(text, 1, index) + copytext(text, index+keylength)
// Replace w/ underscore to prevent "&#3{4;" from cheesing the radar
// Should probably also use canon text replacing procs
text = copytext(text, 1, index) + "_" + copytext(text, index+keylength)
index = findtext(text, char)
// Then, replace characters as normal
+10 -7
View File
@@ -385,7 +385,10 @@ var/global/dmm_suite/preloader/_preloader = new
var/endquote = findtext(trim_right,quote,-1)
if(!endquote)
log_debug("Terminating quote not found!")
trim_right = copytext(trim_right,2,endquote)
// Our map writer escapes quotes and curly brackets to avoid
// letting our simple parser choke on meanly-crafted names/etc
// - so we decode it here so it's back to good ol' legibility
trim_right = dmm_decode(copytext(trim_right,2,endquote))
//Check for number
else if(isnum(text2num(trim_right)))
@@ -450,18 +453,18 @@ var/global/dmm_suite/preloader/_preloader = new
/dmm_suite/preloader/proc/load(atom/what)
if(json_ready)
var/json_data = attributes["map_json_data"]
attributes -= "map_json_data"
json_data = dmm_decode(json_data)
try
what.deserialize(json_decode(json_data))
catch(var/exception/e)
log_debug("Bad json data: '[json_data]'")
throw e
else
for(var/attribute in attributes)
var/value = attributes[attribute]
if(islist(value))
value = deepCopyList(value)
what.vars[attribute] = value
for(var/attribute in attributes)
var/value = attributes[attribute]
if(islist(value))
value = deepCopyList(value)
what.vars[attribute] = value
use_preloader = FALSE
// If the map loader fails, make this safe
+190 -180
View File
@@ -5,10 +5,9 @@
#define DMM_IGNORE_PLAYERS 16
#define DMM_IGNORE_MOBS 24
#define DMM_USE_JSON 32
dmm_suite{
var{
quote = "\""
list/letter_digits = list(
/dmm_suite
var/quote = "\""
var/list/letter_digits = list(
"a","b","c","d","e",
"f","g","h","i","j",
"k","l","m","n","o",
@@ -22,180 +21,191 @@ dmm_suite{
"U","V","W","X","Y",
"Z"
)
}
save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num){
//Check for illegal characters in file name... in a cheap way.
if(!((ckeyEx(map_name)==map_name) && ckeyEx(map_name))){
CRASH("Invalid text supplied to proc save_map, invalid characters or empty string.")
}
//Check for valid turfs.
if(!isturf(t1) || !isturf(t2)){
CRASH("Invalid arguments supplied to proc save_map, arguments were not turfs.")
}
var/map_prefix = "_maps/quicksave/"
var/map_path = "[map_prefix][map_name].dmm"
if(fexists(map_path)){
fdel(map_path)
}
var/saved_map = file(map_path) // We give the map writer the file directly
// Because repeated string appending is super murder for performance
var/map_text = write_map(t1,t2,flags,saved_map)
saved_map << map_text
return saved_map
}
write_map(var/turf/t1 as turf, var/turf/t2 as turf, var/flags as num, var/map_file as file){
//Check for valid turfs.
if(!isturf(t1) || !isturf(t2)){
CRASH("Invalid arguments supplied to proc write_map, arguments were not turfs.")
}
var/turf/ne = locate(max(t1.x,t2.x),max(t1.y,t2.y),max(t1.z,t2.z)) // Outer corner
var/turf/sw = locate(min(t1.x,t2.x),min(t1.y,t2.y),min(t1.z,t2.z)) // Inner corner
var/list/templates[0]
var/template_buffer = {""}
var/buffer_line = {""}
var/dmm_text = {""}
for(var/pos_z in sw.z to ne.z){
for(var/pos_y = ne.y, pos_y >= sw.y, pos_y--){ // We're reversing this because the map format is silly
for(var/pos_x in sw.x to ne.x){
var/turf/test_turf = locate(pos_x,pos_y,pos_z)
var/test_template = make_template(test_turf, flags)
var/template_number = templates.Find(test_template)
if(!template_number){
templates.Add(test_template)
template_number = templates.len
}
buffer_line += "[template_number],"
CHECK_TICK
}
template_buffer += "[buffer_line];"
buffer_line = ""
}
template_buffer += "."
}
if(templates.len == 0)
CRASH("No templates found!")
var/key_length = round/*floor*/(log(letter_digits.len,templates.len-1)+1)
var/list/keys[templates.len]
for(var/key_pos in 1 to templates.len){
keys[key_pos] = get_model_key(key_pos,key_length)
dmm_text += {""[keys[key_pos]]" = ([templates[key_pos]])\n"}
}
var/z_level = 0
for(var/z_pos=1;TRUE;z_pos=findtext(template_buffer,".",z_pos)+1){
if(z_pos>=length(template_buffer)){break}
if(z_level){dmm_text += {"\n"}}
dmm_text += {"\n(1,1,[++z_level]) = {"\n"}
var/z_block = copytext(template_buffer,z_pos,findtext(template_buffer,".",z_pos))
for(var/y_pos=1;TRUE;y_pos=findtext(z_block,";",y_pos)+1){
if(y_pos>=length(z_block)){break}
var/y_block = copytext(z_block,y_pos,findtext(z_block,";",y_pos))
for(var/x_pos=1;TRUE;x_pos=findtext(y_block,",",x_pos)+1){
if(x_pos>=length(y_block)){break}
var/x_block = copytext(y_block,x_pos,findtext(y_block,",",x_pos))
var/key_number = text2num(x_block)
var/temp_key = keys[key_number]
buffer_line += temp_key
CHECK_TICK
}
dmm_text += {"[buffer_line]\n"}
buffer_line = ""
}
dmm_text += {"\"}"}
}
return dmm_text
}
proc{
make_template(var/turf/model as turf, var/flags as num){
var/use_json = 0
if(flags & DMM_USE_JSON)
use_json = 1
var/template = ""
var/obj_template = ""
var/mob_template = ""
var/turf_template = ""
if(!(flags & DMM_IGNORE_TURFS)){
turf_template = "[model.type][check_attributes(model,use_json=use_json)],"
} else{ turf_template = "[world.turf],"}
var/area_template = ""
if(!(flags & DMM_IGNORE_OBJS)){
for(var/obj/O in model.contents){
if(O.dont_save)
continue
obj_template += "[O.type][check_attributes(O,use_json=use_json)],"
}
}
for(var/mob/M in model.contents){
if(M.dont_save)
continue
if(M.client){
if(!(flags & DMM_IGNORE_PLAYERS)){
mob_template += "[M.type][check_attributes(M,use_json=use_json)],"
}
}
else{
if(!(flags & DMM_IGNORE_NPCS)){
mob_template += "[M.type][check_attributes(M,use_json=use_json)],"
}
}
}
if(!(flags & DMM_IGNORE_AREAS)){
var/area/m_area = model.loc
area_template = "[m_area.type][check_attributes(m_area,use_json=use_json)]"
} else{ area_template = "[world.area]"}
template = "[obj_template][mob_template][turf_template][area_template]"
return template
}
check_attributes(var/atom/A,use_json=0){
var/attributes_text = {"{"}
if(!use_json){
for(var/V in A.vars){
sleep(-1)
if((!issaved(A.vars[V])) || (A.vars[V]==initial(A.vars[V]))){continue}
if(istext(A.vars[V])){
attributes_text += {"[V] = "[A.vars[V]]""}
}
else if(isnum(A.vars[V])||ispath(A.vars[V])){
attributes_text += {"[V] = [A.vars[V]]"}
}
else if(isicon(A.vars[V])||isfile(A.vars[V])){
attributes_text += {"[V] = '[A.vars[V]]'"}
}
else{
continue
}
if(attributes_text != {"{"}){
attributes_text+={"; "}
}
}
} else {
var/list/yeah = A.serialize()
// Remove useless info
yeah -= "type"
if(yeah.len) {
var/json_stuff = json_encode(yeah)
json_stuff = dmm_encode(json_stuff)
attributes_text += {"map_json_data = "[json_stuff]""}
}
}
if(attributes_text=={"{"}){
return
}
if(copytext(attributes_text, length(attributes_text)-1, 0) == {"; "}){
attributes_text = copytext(attributes_text, 1, length(attributes_text)-1)
}
attributes_text += {"}"}
return attributes_text
}
get_model_key(var/which as num, var/key_length as num){
var/key = ""
var/working_digit = which-1
for(var/digit_pos in key_length to 1 step -1){
var/place_value = round/*floor*/(working_digit/(letter_digits.len**(digit_pos-1)))
working_digit-=place_value*(letter_digits.len**(digit_pos-1))
key = "[key][letter_digits[place_value+1]]"
}
return key
}
}
}
/dmm_suite/save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num)
//Check for illegal characters in file name... in a cheap way.
if(!((ckeyEx(map_name)==map_name) && ckeyEx(map_name)))
CRASH("Invalid text supplied to proc save_map, invalid characters or empty string.")
//Check for valid turfs.
if(!isturf(t1) || !isturf(t2))
CRASH("Invalid arguments supplied to proc save_map, arguments were not turfs.")
var/map_prefix = "_maps/quicksave/"
var/map_path = "[map_prefix][map_name].dmm"
if(fexists(map_path))
fdel(map_path)
var/saved_map = file(map_path)
var/map_text = write_map(t1,t2,flags,saved_map)
saved_map << map_text
return saved_map
/dmm_suite/write_map(var/turf/t1 as turf, var/turf/t2 as turf, var/flags as num)
//Check for valid turfs.
if(!isturf(t1) || !isturf(t2))
CRASH("Invalid arguments supplied to proc write_map, arguments were not turfs.")
var/turf/ne = locate(max(t1.x,t2.x),max(t1.y,t2.y),max(t1.z,t2.z)) // Outer corner
var/turf/sw = locate(min(t1.x,t2.x),min(t1.y,t2.y),min(t1.z,t2.z)) // Inner corner
var/list/templates[0]
var/template_buffer = ""
var/buffer_line = ""
var/dmm_text = ""
var/total_timer = start_watch()
var/timer = start_watch()
log_debug("Reading turfs...")
// Read the contents of all the turfs we were given
for(var/pos_z in sw.z to ne.z)
for(var/pos_y = ne.y, pos_y >= sw.y, pos_y--) // We're reversing this because the map format is silly
for(var/pos_x in sw.x to ne.x)
var/turf/test_turf = locate(pos_x,pos_y,pos_z)
var/test_template = make_template(test_turf, flags)
var/template_number = templates.Find(test_template)
if(!template_number)
templates.Add(test_template)
template_number = templates.len
buffer_line += "[template_number],"
CHECK_TICK
template_buffer += "[buffer_line];"
buffer_line = ""
template_buffer += "."
log_debug("Reading turfs took [stop_watch(timer)]s.")
if(templates.len == 0)
CRASH("No templates found!")
var/key_length = round/*floor*/(log(letter_digits.len,templates.len-1)+1)
var/list/keys[templates.len]
// Write the list of key/model pairs to the file
timer = start_watch()
log_debug("Writing out key/model pairs to file header...")
for(var/key_pos in 1 to templates.len)
keys[key_pos] = get_model_key(key_pos,key_length)
dmm_text += "\"[keys[key_pos]]\" = ([templates[key_pos]])\n"
log_debug("Writing key/model pairs complete, took [stop_watch(timer)]s.")
var/z_level = 0
// Loop over all z in our zone
timer = start_watch()
log_debug("Writing out key map...")
for(var/z_pos=1;TRUE;z_pos=findtext(template_buffer,".",z_pos)+1)
if(z_pos>=length(template_buffer)) break
if(z_level) dmm_text += "\n"
dmm_text += "\n(1,1,[++z_level]) = {\"\n"
var/z_block = copytext(template_buffer,z_pos,findtext(template_buffer,".",z_pos))
for(var/y_pos=1;TRUE;y_pos=findtext(z_block,";",y_pos)+1)
if(y_pos>=length(z_block)) break
var/y_block = copytext(z_block,y_pos,findtext(z_block,";",y_pos))
// A row of keys
for(var/x_pos=1;TRUE;x_pos=findtext(y_block,",",x_pos)+1)
if(x_pos>=length(y_block)) break
var/x_block = copytext(y_block,x_pos,findtext(y_block,",",x_pos))
var/key_number = text2num(x_block)
var/temp_key = keys[key_number]
buffer_line += temp_key
CHECK_TICK
dmm_text += "[buffer_line]\n"
buffer_line = ""
dmm_text += "\"}"
log_debug("Writing key map complete, took [stop_watch(timer)]s.")
log_debug("TOTAL TIME: [stop_watch(total_timer)]s.")
return dmm_text
/dmm_suite/proc/make_template(var/turf/model as turf, var/flags as num)
var/use_json = 0
if(flags & DMM_USE_JSON)
use_json = 1
var/template = ""
var/turf_template = ""
var/obj_template = ""
var/mob_template = ""
var/area_template = ""
// Turf
if(!(flags & DMM_IGNORE_TURFS))
turf_template = "[model.type][check_attributes(model,use_json=use_json)],"
else turf_template = "[world.turf],"
// Objects loop
if(!(flags & DMM_IGNORE_OBJS))
for(var/obj/O in model.contents)
if(O.dont_save || !isnull(O.gcDestroyed))
continue
obj_template += "[O.type][check_attributes(O,use_json=use_json)],"
// Mobs Loop
for(var/mob/M in model.contents)
if(M.dont_save || !isnull(M.gcDestroyed))
continue
if(M.client)
if(!(flags & DMM_IGNORE_PLAYERS))
mob_template += "[M.type][check_attributes(M,use_json=use_json)],"
else
if(!(flags & DMM_IGNORE_NPCS))
mob_template += "[M.type][check_attributes(M,use_json=use_json)],"
// Area
if(!(flags & DMM_IGNORE_AREAS))
var/area/m_area = model.loc
area_template = "[m_area.type][check_attributes(m_area,use_json=use_json)]"
else area_template = "[world.area]"
template = "[obj_template][mob_template][turf_template][area_template]"
return template
/dmm_suite/proc/check_attributes(var/atom/A,use_json=0)
var/attributes_text = "{"
if(!use_json)
for(var/V in A.vars)
sleep(-1)
if((!issaved(A.vars[V])) || (A.vars[V]==initial(A.vars[V]))) continue
attributes_text += var_to_dmm(A.vars[V], V)
else
var/list/yeah = A.serialize()
// We'll want to write out vars that are important to the editor
// So that the map is legible as before
for(var/thing in A.map_important_vars())
// Save vars that are important for the map editor, so that
// json-encoded maps are legible for standard editors
if(A.vars[thing] != initial(A.vars[thing]))
yeah -= thing
attributes_text += var_to_dmm(A.vars[thing],thing)
// Remove useless info
yeah -= "type"
if(yeah.len)
var/json_stuff = json_encode(yeah)
attributes_text += var_to_dmm(json_stuff, "map_json_data")
if(attributes_text == "{")
return
// Trim a trailing semicolon - `var_to_dmm` always appends a semicolon,
// so the last one will be trailing.
if(copytext(attributes_text, length(attributes_text)-1, 0) == "; ")
attributes_text = copytext(attributes_text, 1, length(attributes_text)-1)
attributes_text += "}"
return attributes_text
/dmm_suite/proc/get_model_key(var/which as num, var/key_length as num)
var/key = ""
var/working_digit = which-1
for(var/digit_pos in key_length to 1 step -1)
var/place_value = round/*floor*/(working_digit/(letter_digits.len**(digit_pos-1)))
working_digit-=place_value*(letter_digits.len**(digit_pos-1))
key = "[key][letter_digits[place_value+1]]"
return key
/dmm_suite/proc/var_to_dmm(attr, name)
if(istext(attr))
// dmm_encode will strip out characters that would be capable of disrupting
// parsing - namely, quotes and curly braces
return "[name] = \"[dmm_encode(attr)]\"; "
else if(isnum(attr)||ispath(attr))
return "[name] = [attr]; "
else if(isicon(attr)||isfile(attr))
return "[name] = '[attr]'; "
else
return ""
+22 -5
View File
@@ -21,20 +21,37 @@
/datum/proc/deserialize(var/list/data)
return
/atom
// This is so specific atoms can override these, and ignore certain ones
var/list/vars_to_save = list("dir","name","color","icon","icon_state", "pixel_x", "pixel_y")
// This var isn't actually used for anything, but is present so that
// DM's map reader doesn't forfeit on reading a JSON-serialized map
var/map_json_data
// This is so specific atoms can override these, and ignore certain ones
/atom/proc/vars_to_save()
return list("dir","name","color","icon","icon_state", "pixel_x", "pixel_y")
/atom/proc/map_important_vars()
// A list of important things to save in the map editor
return list("dir","name","color","icon","icon_state", "pixel_x", "pixel_y")
/area/map_important_vars()
// Keep the area default icons, to keep things nice and legible
return list("name")
// No need to save any state of an area by default
/area/vars_to_save()
return list("name")
/atom/serialize()
var/list/data = ..()
for(var/thing in vars_to_save)
for(var/thing in vars_to_save())
if(vars[thing] != initial(vars[thing]))
data[thing] = vars[thing]
return data
/atom/deserialize(var/list/data)
for(var/thing in vars_to_save)
for(var/thing in vars_to_save())
if(thing in data)
vars[thing] = data[thing]
..()