Files
Bubberstation/code/modules/buildmode/submodes/map_export.dm
SkyratBot 782a04859a [MIRROR] Macro Optimizes Map Saving (100x) DO NOT CHURN STRINGS Edition (#26189)
* Macro Optimizes Map Saving (100x) DO NOT CHURN STRINGS Edition  (#80845)

## About The Pull Request

Yello!
This one is reasonably quick, tho I did some fixes too

This is the big one, fixes the buildmode tool sometimes locking disabled
for the whole round.
We do this by replacing the static var on buildmode with global var and
a global proc
This keeps a harddel on the buildmode datum from permalocking is_running
to TRUE

Also makes flipping the var BACK if something breaks significantly
easier for admins, so that's nice

Alright, smaller things now

Fixes lists of numbers failing to encoded improperly This was fixed on
shiptest, we failed to actually port their most recent revision
Fixes the shuttle flag not actually working because it used istype
instead of ispath
Changes obj_blacklist to a typecache for optimization's sake
Renames/moves some vars around to prevent weird double typing things
Removes a checktick in key gen, it's just costing more time then it
would save in overtime
Properly handles lists. We were only doing var encoding one layer deep,
need to do it alll the way down

Alright, now the optimizations

This proc is fucking HOT, and it's for really dumb reasons

This is a text gen proc, and it makes the mistake of generating text and
concatinating it with MORE text.
This is HORRIFICALLY EXPENSIVE because byond caches strings (can only be
one of each) and string churn fucks up that caching system something
fierce
Moving from strings to lists of strings we join at the end takes us from
like idk 100 seconds to save bare metastation to like 1.5
This is applied basically everywhere for obvious reasons

While I'm here, storing keys in a flat list and then using find to find
them, then using that index to lookup into another flat list is a bit
silly. Let's just make it an assoc list. Faster lookup, cleaner.

Oh also rather then iterating over all the vars on an object, let's
iterate over just the ones we care about yeah?

Let's see... no sense genning a key we'll never use, and having suffixes
be often non existent is silly just embrace the slight mess.

That's it I think, this takes us from 100 seconds to save metastation to
2.5 seconds to save ALL of metastation (I removed the vars limiter so I
could make sure var saving didn't fuck me up)

## Why It's Good For The Game

Cleans up some issues that we failed to port the fixes for, MASSIVELY
optimizes this (so it can finish in like 5/10 seconds and not 300!) and
ensures admins can always use the thing and don't risk dropping their
pet buildastation to the void.

Worth noting, this tool really should not be used for station mapping
outside an event context. It produces sorta buggy var edits, and WILL
fail to pull over context for shit. Please don't use it as such

Profiles (csv files I promise)

[Before](https://github.com/tgstation/tgstation/files/13853313/profiler.json)

[After](https://github.com/tgstation/tgstation/files/13853271/profiler.json)

I'd include my line by lines but I don't know how much you'd get out of
them. Here's an image tho

![image](https://github.com/tgstation/tgstation/assets/58055496/3f3148c5-8b1e-4bda-aa65-3983f9944a91)

## Changelog
🆑
fix: The map saving tool will no longer lock up and prevent all further
action at random
fix: Map saving now takes on the order of seconds, not minutes
fix: Fixes an issue with lists that caused strongdmm to report saved
maps as broken
/🆑

* Macro Optimizes Map Saving (100x) DO NOT CHURN STRINGS Edition

---------

Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
2024-01-20 23:39:52 +01:00

92 lines
3.9 KiB
Plaintext

/datum/buildmode_mode/map_export
key = "mapexport"
use_corner_selection = TRUE
/// Variable with the flag value to understand how to treat the shuttle zones.
var/shuttle_flag = SAVE_SHUTTLEAREA_DONTCARE
/// Variable with a flag value to indicate what should be saved (for example, only objects or only mobs).
var/save_flag = ALL
/datum/buildmode_mode/map_export/change_settings(client/builder)
var/static/list/options = list(
"Object Saving" = SAVE_OBJECTS,
"Mob Saving" = SAVE_MOBS,
"Turf Saving" = SAVE_TURFS,
"Area Saving" = SAVE_AREAS,
"Space Turf Saving" = SAVE_SPACE,
"Object Property Saving" = SAVE_OBJECT_PROPERTIES,
)
var/what_to_change = tgui_input_list(builder, "What export setting would you like to toggle?", "Map Exporter", options)
if (!what_to_change)
return
save_flag ^= options[what_to_change]
to_chat(builder, "<span class='notice'>[what_to_change] is now [save_flag & options[what_to_change] ? "ENABLED" : "DISABLED"].</span>")
/datum/buildmode_mode/map_export/show_help(client/builder)
to_chat(builder, span_purple(examine_block(
"[span_bold("Select corner")] -> Left Mouse Button on obj/turf/mob\n\
[span_bold("Set export options")] -> Right Mouse Button on buildmode button"))
)
/datum/buildmode_mode/map_export/handle_selected_area(client/builder, params)
var/list/listed_params = params2list(params)
var/left_click = listed_params.Find("left")
//Ensure the selection is actually done
if(!left_click)
to_chat(builder, span_warning("Invalid selection."))
return
//If someone somehow gets build mode, stop them from using this.
if(!check_rights(R_DEBUG))
message_admins("[ckey(builder)] tried to run the map save generator but was rejected due to insufficient perms.")
to_chat(builder, span_warning("You must have +ADMIN rights to use this."))
return
//Emergency check
if(get_dist(cornerA, cornerB) > 60 || cornerA.z != cornerB.z)
var/confirm = tgui_alert(builder, "Are you sure about this? Exporting large maps may take quite a while.", "Map Exporter", list("Yes", "No"))
if(confirm != "Yes")
return
if(cornerA == cornerB)
return
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(_save_map), cornerA, cornerB, save_flag, shuttle_flag)
/// A guard variable to prevent more than one map export process from occurring at the same time.
GLOBAL_VAR_INIT(map_writing_running, FALSE)
/// Hey bud don't call this directly, it exists so we can invoke async and prevent the buildmode datum being qdel'd from halting this proc
/proc/_save_map(turf/cornerA, turf/cornerB, save_flag, shuttle_flag)
if(!check_rights(R_DEBUG))
message_admins("[ckey(usr)] tried to run the map save generator but was rejected due to insufficient perms.")
to_chat(usr, span_warning("You must have +ADMIN rights to use this."))
return
if(GLOB.map_writing_running)
to_chat(usr, span_warning("Someone is already running the generator! Try again in a little bit."))
return
to_chat(usr, span_warning("Saving, please wait..."))
GLOB.map_writing_running = TRUE
//I put this before the actual saving of the map because it likely won't log if it crashes the fucking server
log_admin("Build Mode: [key_name(usr)] is exporting the map area from [AREACOORD(cornerA)] through [AREACOORD(cornerB)]")
//oversimplified for readability and understandibility
var/minx = min(cornerA.x, cornerB.x)
var/miny = min(cornerA.y, cornerB.y)
var/minz = min(cornerA.z, cornerB.z)
var/maxx = max(cornerA.x, cornerB.x)
var/maxy = max(cornerA.y, cornerB.y)
var/maxz = max(cornerA.z, cornerB.z)
//Step 1: Get the data (This can take a while)
var/dat = write_map(minx, miny, minz, maxx, maxy, maxz, save_flag, shuttle_flag)
//Step 2: Write the data to a file and give map to client
var/date = time2text(world.timeofday, "YYYY-MM-DD_hh-mm-ss")
var/file_name = sanitize_filename(tgui_input_text(usr, "Filename?", "Map Exporter", "exported_map_[date]"))
send_exported_map(usr, file_name, dat)
to_chat(usr, span_green("The map was successfully saved!"))
GLOB.map_writing_running = FALSE