From 471a5ecca1cb2ce823f3ac9e9003666670949cc0 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Wed, 3 Aug 2016 13:26:22 -0700 Subject: [PATCH 01/11] First pass at a functioning map writer --- .gitignore | 1 + code/game/dna/dna2.dm | 6 +- code/modules/admin/buildmode.dm | 46 ++++++++- code/modules/awaymissions/maploader/reader.dm | 19 +++- code/modules/awaymissions/maploader/writer.dm | 88 ++++++++++++------ code/modules/persistence/persistence.dm | 26 +++++- icons/misc/buildmode.dmi | Bin 1540 -> 1640 bytes 7 files changed, 141 insertions(+), 45 deletions(-) diff --git a/.gitignore b/.gitignore index 02f0c8cf790..9a9bbe0aafd 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ data/ /_maps/map_files/backup/* /_maps/map_files/**/backup/* /_maps/map_files/**/*.dmm.backup +/_maps/quicksave/* /nano/debug.html *.db stddef.dm diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index a8a33784263..4224ad1aa5e 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -421,7 +421,7 @@ var/global/list/bad_blocks[0] reg_dna[unique_enzymes] = character.real_name // Hmm, I wonder how to go about this without a huge convention break -/datum/dna/proc/serialize() +/datum/dna/serialize() var/data = list() data["UE"] = unique_enzymes data["SE"] = SE.Copy() // This is probably too lazy for my own good @@ -432,7 +432,7 @@ var/global/list/bad_blocks[0] data["real_name"] = real_name return data -/datum/dna/proc/deserialize(data) +/datum/dna/deserialize(data) unique_enzymes = data["UE"] // The de-serializer is unlikely to tamper with the lists SE = data["SE"] @@ -441,4 +441,4 @@ var/global/list/bad_blocks[0] UpdateSE() species = data["species"] b_type = data["b_type"] - real_name = data["real_name"] \ No newline at end of file + real_name = data["real_name"] diff --git a/code/modules/admin/buildmode.dm b/code/modules/admin/buildmode.dm index ed48b2dc6d0..f08121634d3 100644 --- a/code/modules/admin/buildmode.dm +++ b/code/modules/admin/buildmode.dm @@ -8,7 +8,8 @@ #define FILL_BUILDMODE 8 #define LINK_BUILDMODE 9 #define BOOM_BUILDMODE 10 -#define NUM_BUILDMODES 10 +#define SAVE_BUILDMODE 11 +#define NUM_BUILDMODES 11 /obj/screen/buildmode icon = 'icons/misc/buildmode.dmi' @@ -81,8 +82,11 @@ /obj/effect/buildmode_reticule/New(var/turf/t, var/client/c) loc = t I = image('icons/mob/blob.dmi', t, "marker",19.0,2) // Sprite reuse wooo - cl = c - cl.images += I + if(c) + cl = c + cl.images += I + else + log_debug("Buildmode reticule created without a client!") /obj/effect/buildmode_reticule/proc/deselect() qdel(src) @@ -174,6 +178,8 @@ var/light = -1 var/flash = -1 var/flames = -1 + // Saving mode + var/use_json = TRUE /datum/click_intercept/buildmode/New(client/c) ..() @@ -261,6 +267,13 @@ to_chat(user, "***********************************************************") to_chat(user, "Mouse Button on obj = Kaboom") to_chat(user, "***********************************************************") + if(SAVE_BUILDMODE) + to_chat(user, "***********************************************************") + to_chat(user, "Left Mouse Button on turf/obj/mob = Select corner") + to_chat(user, "***********************************************************") + + + /datum/click_intercept/buildmode/proc/change_settings(mob/user) switch(mode) @@ -345,6 +358,9 @@ var/flames = input("Range of flames. -1 to none", text("Input")) as num|null if(flames == null) flames = -1 + if(SAVE_BUILDMODE) + use_json = (alert("Would you like to use json (Default is \"Yes\")?",,"Yes","No") == "Yes") + /datum/click_intercept/buildmode/proc/change_dir() switch(build_dir) if(NORTH) @@ -625,3 +641,27 @@ link_lines += L2 if(BOOM_BUILDMODE) explosion(object, devastation, heavy, light, flash, null, null,flames) + if(SAVE_BUILDMODE) + if(!cornerA) + cornerA = select_tile(get_turf(object)) + return + if(!cornerB) + cornerB = select_tile(get_turf(object)) + if(left_click) + if(cornerA && cornerB) + var/turf/A = get_turf(cornerA) + var/turf/B = get_turf(cornerB) + deselect_region() // So we don't read our own reticules + var/map_name = input(holder, "Please select a name for your map", "Buildmode", "") + if(map_name == "") + return + var/map_flags = 0 + if(use_json) + map_flags = 32 // Magic number defined in `writer.dm` that I can't use directly + // because #defines are for some reason our coding standard + var/our_map = maploader.save_map(A,B,map_name,map_flags) + holder << ftp(our_map) // send the map they've made! Or are stealing, whatever + to_chat(holder, "Map saving complete! [our_map]") + return + + deselect_region() diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index ea59dd1d518..ff26e4071ac 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -373,6 +373,7 @@ var/global/dmm_suite/preloader/_preloader = new var/trim_left = trim_text(copytext(text,old_position,(equal_position ? equal_position : position)),1)//the name of the variable, must trim quotes to build a BYOND compliant associatives list old_position = position + 1 + log_debug(trim_left) if(equal_position)//associative var, so do the association var/trim_right = trim_text(copytext(text,equal_position+1,position))//the content of the variable @@ -401,6 +402,7 @@ var/global/dmm_suite/preloader/_preloader = new else if(ispath(text2path(trim_right))) trim_right = text2path(trim_right) + log_debug(trim_right) to_return[trim_left] = trim_right else//simple var @@ -430,19 +432,26 @@ var/global/dmm_suite/preloader/_preloader = new parent_type = /datum var/list/attributes var/target_path + var/json_ready = 0 /dmm_suite/preloader/proc/setup(list/the_attributes, path) if(the_attributes.len) + if("map_json_data" in the_attributes) + json_ready = 1 use_preloader = TRUE attributes = the_attributes target_path = path /dmm_suite/preloader/proc/load(atom/what) - for(var/attribute in attributes) - var/value = attributes[attribute] - if(islist(value)) - value = deepCopyList(value) - what.vars[attribute] = value + if(json_ready) + log_debug(attributes["map_json_data"]) + what.deserialize(json_decode(attributes["map_json_data"])) + else + 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 diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index 208d0935a32..c3c0a1f8739 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -4,6 +4,7 @@ #define DMM_IGNORE_NPCS 8 #define DMM_IGNORE_PLAYERS 16 #define DMM_IGNORE_MOBS 24 +#define DMM_USE_JSON 32 dmm_suite{ var{ quote = "\"" @@ -31,11 +32,13 @@ dmm_suite{ 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" var/file_text = write_map(t1,t2,flags) - if(fexists("[map_name].dmm")){ - fdel("[map_name].dmm") + if(fexists(map_path)){ + fdel(map_path) } - var/saved_map = file("[map_name].dmm") + var/saved_map = file(map_path) saved_map << file_text return saved_map } @@ -44,17 +47,22 @@ dmm_suite{ if(!isturf(t1) || !isturf(t2)){ CRASH("Invalid arguments supplied to proc write_map, arguments were not turfs.") } - var/turf/nw = locate(min(t1.x,t2.x),max(t1.y,t2.y),min(t1.z,t2.z)) - var/turf/se = locate(max(t1.x,t2.x),min(t1.y,t2.y),max(t1.z,t2.z)) + 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/dmm_text = {""} - for(var/pos_z in nw.z to se.z){ - for(var/pos_y in nw.y to se.y){ - for(var/pos_x in nw.x to se.x){ + for(var/pos_z in sw.z to ne.z){ + // log_debug("z: [pos_z]") + for(var/pos_y in sw.y to ne.y){ + // log_debug("y: [pos_y]") + for(var/pos_x in sw.x to ne.x){ + // log_debug("x: [pos_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) + // log_debug("Template number: [template_number]") + // log_debug("Template string: [test_template]") if(!template_number){ templates.Add(test_template) template_number = templates.len @@ -65,6 +73,10 @@ dmm_suite{ } template_buffer += "." } + // log_debug("[template_buffer]") + // log_debug("We are done with the loop, doing log") + 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){ @@ -98,57 +110,71 @@ 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/obj_template = "" var/mob_template = "" var/turf_template = "" if(!(flags & DMM_IGNORE_TURFS)){ - turf_template = "[model.type][check_attributes(model)]," + 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){ - obj_template += "[O.type][check_attributes(O)]," + obj_template += "[O.type][check_attributes(O,use_json=use_json)]," } } for(var/mob/M in model.contents){ if(M.client){ if(!(flags & DMM_IGNORE_PLAYERS)){ - mob_template += "[M.type][check_attributes(M)]," + mob_template += "[M.type][check_attributes(M,use_json=use_json)]," } } else{ if(!(flags & DMM_IGNORE_NPCS)){ - mob_template += "[M.type][check_attributes(M)]," + 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)]" + 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){ + check_attributes(var/atom/A,use_json=0){ var/attributes_text = {"{"} - 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]]""} + 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 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" + var/json_stuff = json_encode(yeah) + if(yeah.len) { + log_debug(json_stuff) + attributes_text += {"map_json_data = "[json_stuff]""} } } if(attributes_text=={"{"}){ @@ -171,4 +197,4 @@ dmm_suite{ return key } } - } \ No newline at end of file + } diff --git a/code/modules/persistence/persistence.dm b/code/modules/persistence/persistence.dm index cc5a7684947..8e05ef2432e 100644 --- a/code/modules/persistence/persistence.dm +++ b/code/modules/persistence/persistence.dm @@ -5,8 +5,9 @@ * If we want to store this info, we can pass it to `json_encode` or some other * interface that suits our fancy, to make it into an easily-handled string */ -/atom/movable/proc/serialize() - return list("type" = "[type]") +/datum/proc/serialize() + var/data = list("type" = "[type]") + return data /* * This is given the byond list from above, to bring this atom to the state @@ -17,9 +18,28 @@ * Also, this should only be called by `json_to_object` in persistence.dm - at least * with current plans - that way it can actually initialize the type from the list */ -/atom/movable/proc/deserialize(var/data) +/datum/proc/deserialize(var/list/data) return + +/atom/movable + // This is so specific atoms can override these, and ignore certain ones + var/list/vars_to_save = list("dir","name","color","icon","icon_state") +/atom/movable/serialize() + var/list/data = ..() + for(var/thing in vars_to_save) + if(vars[thing] != initial(vars[thing])) + data[thing] = vars[thing] + return data + + +/atom/movable/deserialize(var/list/data) + for(var/thing in vars_to_save) + if(thing in data) + vars[thing] = data[thing] + ..() + + /proc/json_to_object(var/json_data, var/loc) var/data = json_decode(json_data) return list_to_object(data, loc) diff --git a/icons/misc/buildmode.dmi b/icons/misc/buildmode.dmi index 062d5b404c8d9583d6a54c6c07db584fb414758e..cc54e5aec0f3eb56aae5642f0ac167280a1674c1 100644 GIT binary patch delta 1446 zcmV;X1zGxp4CoAy7Y?8V0{{R3dEt5<0000XktHG+1u_#|q8zsX0004WQchCstDg%v1p@%}c+35s7CRCE00lTnL_t(&f$f@2j@&p5g`FE@ zYSO@LYj#=Y2muUeJ)6u4#=3wau5qA8_4Ig_Z}52D)zjn8xi~&O@9OFC_GL+b$AAAkqqbh_`uR#*0MB}Qd??@H z^D}Ayt?TEt1~<8kXI*nlJFo8`S9Zi`SoK6 zb4=|rgb>$3e!ZK=5DKC#R%{a!>k{D{*BcjCY^q|sT69;rO5?n-psL=T7^+Vg3Vj(9 zEZq0>cd#O(&C+l?_wt9M{9*QYeVW$i&6zWQ)&8za2eCJ^rrO{2Xq@_xRvRpM)&f<& zNLc#29*wklbqY1l(RcVQ6Doh#p=ouokO)IX4nKj=#+su1U5Dmy5+c`WA&fOe`@6P| zf)Fg>$@^@`r0Lo0E)Wok$IM15sKyymwxs1;SeoVs@!@}RS^^)^a@{qo)?4-26YwHyu z%mETyBGmq_tv3imka4P%gvsBv^@?zI=o>Pj_IDk+CIk#&G0NKCb!e4$n=5wd?|O7O zGd5<5_IEvcK5%PO?eDtuLDyu*!M5~&Q4C-H@Q3@uZ(9HL@`t^g4-_FG(ne-G4~f;}8iF+OkX-?P5f+~y z_y(BgRQ2jn!)V&PFYz|uB32^gs24HLeQ&OMZbl@Di2)AKF5yOuQ)iy*vJDZisqU@` zDQf6oWK_{6*fI;!&Wd+ZXitoTofxi&*bwTyU+#*$ArHmGFKU#_y`9d(@B+YB6nj4} zo;!LQ=Xm$l+evcdw#vPq>L*hHdCaRmHRO0BRu_cVf88chsg;zLq)@M?T@zzV^bm32rR&VUb-Az_fxD* z_xZ~Hp;SEp0}kCvFQGUcA>_^wKz0g7-t-g;Q&VJq)= z)WMkD6+0zjryr!ewA-su?#|KQE)1E=UlQWbt_at?mp{B>mWJDLxz#W39xLSA7p(o= z?F-ia?)C-iOuv1>I@9kHR;$H}@CqUL!Gvx!p{L%U^#2AB0(ag9jV!Z07*qoM6N<$f`^vW AH~;_u delta 1346 zcmV-I1-<&{41^4j7Y={~0{{R3eocQU0000UktHG(mOuuJ00001bW%=J06^y0W&i*H zu907&e*l+SWd&D17jUWs0J_w_NaFY$h5!Hr+DSw~RA_2@za;C8>Nr`yN5IKJJl>go3Kx}@XRk9*YCe`{Tzue1g5tf$+j@)N$_qXy8rKCd;n zc{V^g5H7`mpSa{tOu9a=wPoQ^ZxI@IfRL}9%?@FVVIQHD>-``?C(4W=wE22JgwSN^ z#t{OHBg89#p0hbfXjyxGy^o2A|Mn9az{63@_+=(E#^CkNxq`4JdeHP!05!kL=e@NS zf3Nr67lf^twFq&DcL`TVbu)$w&~rIJ*vdm*o!mjfojl~jk0a#wk0H!4waXAfTnG96 zZXQD@h_+aS7@ghKd~i1VS5Yit=|In!`zmT&IOF))ejU+BynCTqV^0uB}DL zs0T0|L+$U{I_6v^gmBj4BaX86cWo_z62pf))c&rm3&52sIMx2HtqZ^nLgnw;f4Tsa znL??Fk4!kvnrKIJO6IwYX9GW`VTxhl@7j9Fc`12F4=Xz^j-{~{_vmsf5Sgoe}4MI&HmwOz10;tpiTBZ(c370cVb5mqT+g} z{oM&-5dWCGP3fLa?3V=*q^2l;cP6Hch_3M@VenZ1WXPglga{~A&mD}RbMYPPU1II; zC??@msA1saCS3Nf)lElOS2WXRUCB~^U&vn^`2-sA29|U_!>vB{A_BdXrd?7@GJiJ{U@e>lL-)fZgVZ{QEVmLG6pP^$O;Fa8j3GYy+5 z(#y(y81xaI{yMSx6#c{G1e~FwVY>FmYlN|>kZJ@LU@u*cuKOw0f2R9STVvYAZ&s9ANx+dfssAubPHOe;K zn+2cpaJi6&M()kVyR+PA+jC!SLXabNl4w(q+!w+6#cmRYuM!3t9j4@A`hk*%+QCYH zaC4MtYMJ0LB@g!rfBh2sgS(@Q*=xmpJrDw#<~(yWp~IUMM3r_@OqhO)z-z*qpe7^@ zIGrlObO+pM!a7DoL=(J-DHxNo0B9HCGGUd6t-NDmG~!$9VU>riyyH;^V>Vapl!%>v zkn+;5uSU5$N58u;WG=rZ#Gzdgu6s{^c*ZP^wsU_0mC{~q5U?cZn*aa+07*qoM6N<$ Ef>jike*gdg From f0a7d192405c543f523238f77b04218c945d6086 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Wed, 3 Aug 2016 14:55:29 -0700 Subject: [PATCH 02/11] Map writer works! --- code/modules/awaymissions/maploader/reader.dm | 1 + code/modules/awaymissions/maploader/writer.dm | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index ff26e4071ac..1cd81bd214d 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -436,6 +436,7 @@ var/global/dmm_suite/preloader/_preloader = new /dmm_suite/preloader/proc/setup(list/the_attributes, path) if(the_attributes.len) + json_ready = 0 if("map_json_data" in the_attributes) json_ready = 1 use_preloader = TRUE diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index c3c0a1f8739..1727f9d9088 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -54,7 +54,7 @@ dmm_suite{ var/dmm_text = {""} for(var/pos_z in sw.z to ne.z){ // log_debug("z: [pos_z]") - for(var/pos_y in sw.y to ne.y){ + for(var/pos_y = ne.y, pos_y >= sw.y, pos_y--){ // We're reversing this because the map format is silly // log_debug("y: [pos_y]") for(var/pos_x in sw.x to ne.x){ // log_debug("x: [pos_x]") From 87cde9f61cd78013dceb596436d7b2408f69decb Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Wed, 3 Aug 2016 15:38:08 -0700 Subject: [PATCH 03/11] The map writer now works on small chunks of the station --- code/game/machinery/navbeacon.dm | 17 ++++++++++++++--- code/modules/awaymissions/maploader/reader.dm | 8 ++++++-- code/modules/persistence/persistence.dm | 6 +++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 5b6cfb5e73d..3d980e769e7 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -26,11 +26,13 @@ var/turf/T = loc hide(T.intact) - if(codes["patrol"]) + if(!codes || !codes.len) + log_debug("Empty codes datum at ([x],[y],[z])") + if("patrol" in codes) if(!navbeacons["[z]"]) navbeacons["[z]"] = list() navbeacons["[z]"] += src //Register with the patrol list! - if(codes["delivery"]) + if("delivery" in codes) deliverybeacons += src deliverybeacontags += location @@ -39,6 +41,15 @@ deliverybeacons -= src return ..() +/obj/machinery/navbeacon/serialize() + var/list/data = ..() + data["codes"] = codes + return data + +/obj/machinery/navbeacon/deserialize(list/data) + codes = data["codes"] + ..() + // set the transponder codes assoc list from codes_txt /obj/machinery/navbeacon/proc/set_codes() if(!codes_txt) @@ -201,4 +212,4 @@ Transponder Codes:
    "} codes[newkey] = newval - updateDialog() \ No newline at end of file + updateDialog() diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index 1cd81bd214d..3dc187f388a 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -379,8 +379,13 @@ var/global/dmm_suite/preloader/_preloader = new var/trim_right = trim_text(copytext(text,equal_position+1,position))//the content of the variable //Check for string + // Make it read to the next delimiter, instead of the quote if(findtext(trim_right,quote,1,2)) - trim_right = copytext(trim_right,2,findtext(trim_right,quote,3,0)) + log_debug(trim_right) + var/endquote = findtext(trim_right,quote,-1) + if(!endquote) + log_debug("Terminating quote not found!") + trim_right = copytext(trim_right,2,endquote) //Check for number else if(isnum(text2num(trim_right))) @@ -402,7 +407,6 @@ var/global/dmm_suite/preloader/_preloader = new else if(ispath(text2path(trim_right))) trim_right = text2path(trim_right) - log_debug(trim_right) to_return[trim_left] = trim_right else//simple var diff --git a/code/modules/persistence/persistence.dm b/code/modules/persistence/persistence.dm index 8e05ef2432e..aa2784cd071 100644 --- a/code/modules/persistence/persistence.dm +++ b/code/modules/persistence/persistence.dm @@ -22,10 +22,10 @@ return -/atom/movable +/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") -/atom/movable/serialize() +/atom/serialize() var/list/data = ..() for(var/thing in vars_to_save) if(vars[thing] != initial(vars[thing])) @@ -33,7 +33,7 @@ return data -/atom/movable/deserialize(var/list/data) +/atom/deserialize(var/list/data) for(var/thing in vars_to_save) if(thing in data) vars[thing] = data[thing] From 3fbb00d5f7e683cbdd15936a8fe95957f8ddf268 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Wed, 3 Aug 2016 18:58:41 -0700 Subject: [PATCH 04/11] Can now save and load the cyberiad --- code/__HELPERS/text.dm | 37 ++++++++++++- code/modules/awaymissions/maploader/reader.dm | 15 ++++-- code/modules/awaymissions/maploader/writer.dm | 53 +++++++++++-------- code/modules/persistence/persistence.dm | 2 +- 4 files changed, 79 insertions(+), 28 deletions(-) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index b17cda4e560..19ed2c06383 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -389,4 +389,39 @@ proc/checkhtml(var/t) /proc/macro2html(text) var/static/regex/text_macro = new("(\\xFF.)(.*)$") - return text_macro.Replace(text, /proc/replace_text_macro) \ No newline at end of file + return text_macro.Replace(text, /proc/replace_text_macro) + +/proc/dmm_encode(text) + // First, go through and nix out any of our escape sequences so we don't leave ourselves open to some escape sequence attack + // Some coder will probably despise me for this, years down the line + + var/list/repl_chars = list(""", "{", "}") + for(var/char in repl_chars) + var/index = findtext(text, char) + var/keylength = length(char) + while(index) + log_debug("Bad string given to dmm encoder! [text]") + text = copytext(text, 1, index) + copytext(text, index+keylength) + index = findtext(text, char) + + // Then, replace characters as normal + var/list/repl_chars_2 = list("\"" = """, "{" = "{", "}" = "}") + for(var/char in repl_chars_2) + var/index = findtext(text, char) + var/keylength = length(char) + while(index) + text = copytext(text, 1, index) + repl_chars_2[char] + copytext(text, index+keylength) + index = findtext(text, char) + return text + + +/proc/dmm_decode(text) + // Replace what we extracted above + var/list/repl_chars = list(""" = "\"", "{" = "{", "}" = "}") + for(var/char in repl_chars) + var/index = findtext(text, char) + var/keylength = length(char) + while(index) + text = copytext(text, 1, index) + repl_chars[char] + copytext(text, index+keylength) + index = findtext(text, char) + return text diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index 3dc187f388a..55125186da9 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -228,10 +228,12 @@ var/global/dmm_suite/preloader/_preloader = new var/full_def = trim_text(copytext(model, old_position, dpos)) //full definition, e.g : /obj/foo/bar{variables=derp} var/variables_start = findtext(full_def, "{") - var/atom_def = text2path(trim_text(copytext(full_def, 1, variables_start))) //path definition, e.g /obj/foo/bar + var/atom_text = trim_text(copytext(full_def, 1, variables_start)) + var/atom_def = text2path(atom_text) //path definition, e.g /obj/foo/bar old_position = dpos + 1 if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers! + log_debug("Bad path: [atom_text]") continue members.Add(atom_def) @@ -373,7 +375,6 @@ var/global/dmm_suite/preloader/_preloader = new var/trim_left = trim_text(copytext(text,old_position,(equal_position ? equal_position : position)),1)//the name of the variable, must trim quotes to build a BYOND compliant associatives list old_position = position + 1 - log_debug(trim_left) if(equal_position)//associative var, so do the association var/trim_right = trim_text(copytext(text,equal_position+1,position))//the content of the variable @@ -381,7 +382,6 @@ var/global/dmm_suite/preloader/_preloader = new //Check for string // Make it read to the next delimiter, instead of the quote if(findtext(trim_right,quote,1,2)) - log_debug(trim_right) var/endquote = findtext(trim_right,quote,-1) if(!endquote) log_debug("Terminating quote not found!") @@ -449,8 +449,13 @@ var/global/dmm_suite/preloader/_preloader = new /dmm_suite/preloader/proc/load(atom/what) if(json_ready) - log_debug(attributes["map_json_data"]) - what.deserialize(json_decode(attributes["map_json_data"])) + var/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] diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index 1727f9d9088..a2141194119 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -34,15 +34,16 @@ dmm_suite{ } var/map_prefix = "_maps/quicksave/" var/map_path = "[map_prefix][map_name].dmm" - var/file_text = write_map(t1,t2,flags) if(fexists(map_path)){ fdel(map_path) } - var/saved_map = file(map_path) - saved_map << file_text + 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){ + 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.") @@ -51,30 +52,41 @@ dmm_suite{ 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 = {""} + + + // // Don't write more than 1000 maps at once ;) + // var/static/cache_index = 0 + // if(cache_index > 1000) + // cache_index = 0 + // cache_index++ + // var/cache_prefix = "data/map_cache/" + // var/cache_name = "mapcache[cache_index].txt" + // var/cache_path = "[cache_prefix][cache_name]" + // if(fexists(cache_path)) + // fdel(cache_path) + // // Here's hoping file writes are less pricey than repeated string appends + // var/template_buffer_cache = file(cache_path) + for(var/pos_z in sw.z to ne.z){ - // log_debug("z: [pos_z]") for(var/pos_y = ne.y, pos_y >= sw.y, pos_y--){ // We're reversing this because the map format is silly - // log_debug("y: [pos_y]") for(var/pos_x in sw.x to ne.x){ - // log_debug("x: [pos_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) - // log_debug("Template number: [template_number]") - // log_debug("Template string: [test_template]") if(!template_number){ templates.Add(test_template) template_number = templates.len } - template_buffer += "[template_number]," + buffer_line += "[template_number]," + CHECK_TICK } - template_buffer += ";" + template_buffer += "[buffer_line];" + buffer_line = "" } template_buffer += "." } - // log_debug("[template_buffer]") - // log_debug("We are done with the loop, doing log") if(templates.len == 0) CRASH("No templates found!") var/key_length = round/*floor*/(log(letter_digits.len,templates.len-1)+1) @@ -86,7 +98,7 @@ dmm_suite{ 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"}} + 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){ @@ -97,14 +109,13 @@ dmm_suite{ 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] - dmm_text += temp_key - sleep(-1) + buffer_line += temp_key + CHECK_TICK } - dmm_text += {"\n"} - sleep(-1) + dmm_text += {"[buffer_line]\n"} + buffer_line = "" } dmm_text += {"\"}"} - sleep(-1) } return dmm_text } @@ -171,9 +182,9 @@ dmm_suite{ var/list/yeah = A.serialize() // Remove useless info yeah -= "type" - var/json_stuff = json_encode(yeah) if(yeah.len) { - log_debug(json_stuff) + var/json_stuff = json_encode(yeah) + json_stuff = dmm_encode(json_stuff) attributes_text += {"map_json_data = "[json_stuff]""} } } diff --git a/code/modules/persistence/persistence.dm b/code/modules/persistence/persistence.dm index aa2784cd071..a803d6b2eb4 100644 --- a/code/modules/persistence/persistence.dm +++ b/code/modules/persistence/persistence.dm @@ -24,7 +24,7 @@ /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") + var/list/vars_to_save = list("dir","name","color","icon","icon_state", "pixel_x", "pixel_y") /atom/serialize() var/list/data = ..() for(var/thing in vars_to_save) From 05a95086bde1d736eefc9aedeb322acbab980d1d Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Wed, 3 Aug 2016 19:07:23 -0700 Subject: [PATCH 05/11] Comments bgone --- code/modules/awaymissions/maploader/writer.dm | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index a2141194119..9846cdb4f8d 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -55,20 +55,6 @@ dmm_suite{ var/buffer_line = {""} var/dmm_text = {""} - - // // Don't write more than 1000 maps at once ;) - // var/static/cache_index = 0 - // if(cache_index > 1000) - // cache_index = 0 - // cache_index++ - // var/cache_prefix = "data/map_cache/" - // var/cache_name = "mapcache[cache_index].txt" - // var/cache_path = "[cache_prefix][cache_name]" - // if(fexists(cache_path)) - // fdel(cache_path) - // // Here's hoping file writes are less pricey than repeated string appends - // var/template_buffer_cache = file(cache_path) - 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){ From 2f3e529db071284a20b4a88fe5c0025c0b722ae2 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Wed, 3 Aug 2016 19:44:47 -0700 Subject: [PATCH 06/11] No more lighting overlays --- code/game/atoms.dm | 1 + code/modules/awaymissions/maploader/writer.dm | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index e4332ac78c0..6513f811aa0 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -13,6 +13,7 @@ var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. var/simulated = 1 //filter for actions - used by lighting overlays var/atom_say_verb = "says" + var/dont_save = 0 // For atoms that are temporary by necessity - like lighting overlays ///Chemistry. var/datum/reagents/reagents = null diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index 9846cdb4f8d..dee2eb0b7db 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -120,10 +120,14 @@ dmm_suite{ 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(O.dont_save) + continue if(M.client){ if(!(flags & DMM_IGNORE_PLAYERS)){ mob_template += "[M.type][check_attributes(M,use_json=use_json)]," From 40fe120067dc6cba41f1d92223fc4d8814067ee2 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Wed, 3 Aug 2016 20:04:14 -0700 Subject: [PATCH 07/11] Whoops --- code/modules/awaymissions/maploader/writer.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index dee2eb0b7db..3de76ccefa7 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -126,7 +126,7 @@ dmm_suite{ } } for(var/mob/M in model.contents){ - if(O.dont_save) + if(M.dont_save) continue if(M.client){ if(!(flags & DMM_IGNORE_PLAYERS)){ From a9f3d0464e21ddb89e8a14a0dea7caea7f6319b0 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Sat, 6 Aug 2016 17:40:33 -0700 Subject: [PATCH 08/11] Makes the json-maploader legible in DM --- code/__HELPERS/text.dm | 4 +- code/modules/awaymissions/maploader/reader.dm | 17 +- code/modules/awaymissions/maploader/writer.dm | 370 +++++++++--------- code/modules/persistence/persistence.dm | 27 +- 4 files changed, 225 insertions(+), 193 deletions(-) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 19ed2c06383..3fe80b245e9 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -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 "{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 diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index 55125186da9..9cb75d4a22b 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -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 diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index 3de76ccefa7..c5cac261c46 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -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 "" diff --git a/code/modules/persistence/persistence.dm b/code/modules/persistence/persistence.dm index a803d6b2eb4..e6de3f5d138 100644 --- a/code/modules/persistence/persistence.dm +++ b/code/modules/persistence/persistence.dm @@ -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] ..() From ffd47933e5178ed347f9ae379246969e378f76f9 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Sun, 7 Aug 2016 12:02:43 -0700 Subject: [PATCH 09/11] Map writer is now legible in DM, saves 19x faster --- code/__HELPERS/text.dm | 6 +- code/modules/awaymissions/maploader/writer.dm | 64 +++++++++++-------- code/modules/persistence/persistence.dm | 4 +- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 3fe80b245e9..e56ed3a4310 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -395,7 +395,7 @@ proc/checkhtml(var/t) // First, go through and nix out any of our escape sequences so we don't leave ourselves open to some escape sequence attack // Some coder will probably despise me for this, years down the line - var/list/repl_chars = list(""", "{", "}") + var/list/repl_chars = list("#?qt;", "#?lbr;", "#?rbr;") for(var/char in repl_chars) var/index = findtext(text, char) var/keylength = length(char) @@ -407,7 +407,7 @@ proc/checkhtml(var/t) index = findtext(text, char) // Then, replace characters as normal - var/list/repl_chars_2 = list("\"" = """, "{" = "{", "}" = "}") + var/list/repl_chars_2 = list("\"" = "#?qt;", "{" = "#?lbr;", "}" = "#?rbr;") for(var/char in repl_chars_2) var/index = findtext(text, char) var/keylength = length(char) @@ -419,7 +419,7 @@ proc/checkhtml(var/t) /proc/dmm_decode(text) // Replace what we extracted above - var/list/repl_chars = list(""" = "\"", "{" = "{", "}" = "}") + var/list/repl_chars = list("#?qt;" = "\"", "#?lbr;" = "{", "#?rbr;" = "}") for(var/char in repl_chars) var/index = findtext(text, char) var/keylength = length(char) diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index c5cac261c46..9b7fdf1bedc 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -47,7 +47,8 @@ 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/list/template_buffer = list() + var/template_buffer_text var/buffer_line = "" var/dmm_text = "" @@ -56,7 +57,7 @@ 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_y in ne.y to sw.y step -1) // 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) @@ -64,13 +65,13 @@ if(!template_number) templates.Add(test_template) template_number = templates.len - buffer_line += "[template_number]," + template_buffer += "[template_number]," CHECK_TICK template_buffer += "[buffer_line];" - buffer_line = "" template_buffer += "." + template_buffer_text = jointext(template_buffer,"") log_debug("Reading turfs took [stop_watch(timer)]s.") if(templates.len == 0) @@ -80,20 +81,24 @@ // Write the list of key/model pairs to the file timer = start_watch() log_debug("Writing out key/model pairs to file header...") + var/list/key_models = list() 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" + key_models += "\"[keys[key_pos]]\" = ([templates[key_pos]])\n" + CHECK_TICK + dmm_text += jointext(key_models,"") 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)) + var/list/key_map = list() + for(var/z_pos=1;TRUE;z_pos=findtext(template_buffer_text,".",z_pos)+1) + if(z_pos>=length(template_buffer_text)) break + if(z_level) key_map += "\n" + key_map += "\n(1,1,[++z_level]) = {\"\n" + var/z_block = copytext(template_buffer_text,z_pos,findtext(template_buffer_text,".",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)) @@ -103,11 +108,11 @@ 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 + key_map += temp_key CHECK_TICK - dmm_text += "[buffer_line]\n" - buffer_line = "" - dmm_text += "\"}" + key_map += "[buffer_line]\n" + key_map += "\"}" + dmm_text += jointext(key_map,"") log_debug("Writing key map complete, took [stop_watch(timer)]s.") log_debug("TOTAL TIME: [stop_watch(total_timer)]s.") return dmm_text @@ -122,6 +127,8 @@ var/mob_template = "" var/area_template = "" + + // Turf if(!(flags & DMM_IGNORE_TURFS)) turf_template = "[model.type][check_attributes(model,use_json=use_json)]," @@ -156,11 +163,13 @@ /dmm_suite/proc/check_attributes(var/atom/A,use_json=0) var/attributes_text = "{" + var/list/attributes = list() if(!use_json) for(var/V in A.vars) - sleep(-1) + CHECK_TICK if((!issaved(A.vars[V])) || (A.vars[V]==initial(A.vars[V]))) continue - attributes_text += var_to_dmm(A.vars[V], V) + + attributes += 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 @@ -170,42 +179,45 @@ // 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) + attributes += 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 == "{") + attributes += var_to_dmm(json_stuff, "map_json_data") + if(attributes.len == 0) 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 += "}" + attributes_text = "{[jointext(attributes,"; ")]}" return attributes_text /dmm_suite/proc/get_model_key(var/which as num, var/key_length as num) - var/key = "" + var/list/key = list() 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 + key += letter_digits[place_value+1] + return jointext(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)]\"; " + return "[name] = \"[dmm_encode(attr)]\"" else if(isnum(attr)||ispath(attr)) - return "[name] = [attr]; " + return "[name] = [attr]" else if(isicon(attr)||isfile(attr)) - return "[name] = '[attr]'; " + if(length("[attr]") == 0) + // The DM map reader is unable to read files that have a '' file/icon entry + return + return "[name] = '[attr]'" else return "" diff --git a/code/modules/persistence/persistence.dm b/code/modules/persistence/persistence.dm index e6de3f5d138..4c1b40eaa98 100644 --- a/code/modules/persistence/persistence.dm +++ b/code/modules/persistence/persistence.dm @@ -28,11 +28,11 @@ // 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") + return list("color","dir","icon","icon_state","name","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") + return list("color","dir","icon","icon_state","layer","name","pixel_x","pixel_y") /area/map_important_vars() // Keep the area default icons, to keep things nice and legible From 360f073609639756aa7ef5432f78ed0425c196c4 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Sun, 7 Aug 2016 13:36:18 -0700 Subject: [PATCH 10/11] Removes buffer_line, makes model saving for cluttered tiles quick --- code/modules/awaymissions/maploader/writer.dm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index 9b7fdf1bedc..88cb42afcdd 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -49,7 +49,6 @@ var/list/templates[0] var/list/template_buffer = list() var/template_buffer_text - var/buffer_line = "" var/dmm_text = "" var/total_timer = start_watch() @@ -110,7 +109,7 @@ var/temp_key = keys[key_number] key_map += temp_key CHECK_TICK - key_map += "[buffer_line]\n" + key_map += "\n" key_map += "\"}" dmm_text += jointext(key_map,"") log_debug("Writing key map complete, took [stop_watch(timer)]s.") @@ -123,8 +122,8 @@ use_json = 1 var/template = "" var/turf_template = "" - var/obj_template = "" - var/mob_template = "" + var/list/obj_template = list() + var/list/mob_template = list() var/area_template = "" @@ -158,7 +157,7 @@ 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]" + template = "[jointext(obj_template,"")][jointext(mob_template,"")][turf_template][area_template]" return template /dmm_suite/proc/check_attributes(var/atom/A,use_json=0) From 342fcad5d50c377dbb55351f387027ff10740602 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Sun, 7 Aug 2016 13:43:15 -0700 Subject: [PATCH 11/11] All is ablaze --- code/modules/awaymissions/maploader/writer.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index 88cb42afcdd..842a51fbce5 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -67,7 +67,7 @@ template_buffer += "[template_number]," CHECK_TICK - template_buffer += "[buffer_line];" + template_buffer += ";" template_buffer += "." template_buffer_text = jointext(template_buffer,"")