|
| 1 | +var/global/list/level_persistence_ref_map = list() |
| 2 | +/datum/level_data |
| 3 | + /// String pointing to a base directory on the filesystem for use in serde. |
| 4 | + /// If set, will automatically suffix map path and level name. |
| 5 | + /// Leave null to opt out of any persistence for this level. |
| 6 | + var/persistent_data_location |
| 7 | + /// Decl handler, mostly forcing myself to keep this general so it can be |
| 8 | + // optimized with a DB or something down the track. |
| 9 | + var/persistence_handler = /decl/level_persistence_handler/json |
| 10 | + |
| 11 | +/datum/level_data/proc/get_persistent_data() |
| 12 | + . = list() |
| 13 | + var/list/atoms_to_save = get_persistent_atoms() |
| 14 | + if(!length(atoms_to_save)) |
| 15 | + return |
| 16 | + for(var/atom/thing as anything in get_persistent_atoms()) |
| 17 | + var/serialized_atom = thing.Serialize() |
| 18 | + if(length(serialized_atom)) |
| 19 | + .["\ref[thing]"] = serialized_atom |
| 20 | + |
| 21 | +// Returns a linear list of atoms that we are interested in saving. |
| 22 | +/datum/level_data/proc/get_persistent_atoms() |
| 23 | + return |
| 24 | + |
| 25 | +// First load all the raw data into memory so every reference is populated. |
| 26 | +/datum/level_data/proc/preload_persistent_data() |
| 27 | + |
| 28 | + // Don't bother if we aren't configured for it at all. |
| 29 | + if(!persistence_handler || !persistent_data_location) |
| 30 | + return FALSE |
| 31 | + |
| 32 | + // Basic sanity check. |
| 33 | + if(!level_id) |
| 34 | + persistent_data_location = null |
| 35 | + PRINT_STACK_TRACE("Level data [type] tried to initialize persistent data but had no level_id.") |
| 36 | + return FALSE |
| 37 | + |
| 38 | + // Atoms on a map are expected to be returned as an associative list with some specific text keys. |
| 39 | + var/decl/level_persistence_handler/load_handler = GET_DECL(persistence_handler) |
| 40 | + var/list/loaded_data = load_handler?.load_data(persistent_data_location, global.using_map.path, ckey(level_id)) |
| 41 | + if(islist(loaded_data) && length(loaded_data)) |
| 42 | + var/list/atom_map = list() |
| 43 | + global.level_persistence_ref_map[level_id] = atom_map |
| 44 | + for(var/uid in loaded_data) |
| 45 | + atom_map[uid] = loaded_data[uid] |
| 46 | + return TRUE |
| 47 | + return FALSE |
| 48 | + |
| 49 | +// Now create the atoms and register them in the global map. Note that levels with no level_id |
| 50 | +// or no persistence handling set will not reach this proc. |
| 51 | +/datum/level_data/proc/load_persistent_data() |
| 52 | + var/load_count = 0 |
| 53 | + var/list/atom_map = global.level_persistence_ref_map[level_id] |
| 54 | + for(var/uid in atom_map) |
| 55 | + |
| 56 | + var/list/atom_data = atom_map[uid] |
| 57 | + |
| 58 | + // TODO: datum handling? Or expect atoms to create datums internally during serde? |
| 59 | + var/atom_type = text2path(atom_data["type"]) |
| 60 | + if(!ispath(atom_type, /atom)) |
| 61 | + error("[level_id]: attempted to load persistent atom with invalid or non-atom type.") |
| 62 | + continue |
| 63 | + |
| 64 | + // TODO: maybe we can hook DMMS for loading individual atoms? |
| 65 | + // TODO: if we can write our serde out as .TGM we won't need bespoke handling at all. |
| 66 | + |
| 67 | + var/atom/created_atom |
| 68 | + var/list/atom_coords = atom_data["location"] |
| 69 | + if(atom_coords) |
| 70 | + if(!islist(atom_coords) || length(atom_coords) < 2) |
| 71 | + error("[level_id]: attempted to load persistent atom with malformed coordinates.") |
| 72 | + continue |
| 73 | + var/turf/spawn_loc = locate(atom_coords[1], atom_coords[2], level_z) |
| 74 | + if(!istype(spawn_loc)) |
| 75 | + error("[level_id]: attempted to load persistent atom but could not find spawn loc.") |
| 76 | + continue |
| 77 | + if(ispath(atom_type, /turf)) |
| 78 | + created_atom = spawn_loc.ChangeTurf(atom_type) |
| 79 | + else if(ispath(atom_type, /area)) |
| 80 | + // VERY BAD, TODO |
| 81 | + ChangeArea(spawn_loc, new atom_type) |
| 82 | + created_atom = get_area(spawn_loc) |
| 83 | + else |
| 84 | + created_atom = new atom_type(spawn_loc) |
| 85 | + |
| 86 | + else if(ispath(atom_type, /turf) || ispath(atom_type, /area)) |
| 87 | + error("[level_id]: attempted to load persistent turf or area with no corresponding coordinate.") |
| 88 | + continue |
| 89 | + else |
| 90 | + created_atom = new atom_type |
| 91 | + |
| 92 | + load_count++ |
| 93 | + created_atom.__init_deserialization_payload = atom_data["data"] |
| 94 | + atom_map[uid] = created_atom |
| 95 | + SSatoms.deserialized_atoms += created_atom |
| 96 | + |
| 97 | + to_world_log("[level_id]/[name] loaded [load_count] persistent atom\s.") |
| 98 | + return !!load_count |
| 99 | + |
| 100 | +// Write any data out if we need to. |
| 101 | +/datum/level_data/proc/save_persistent_data() |
| 102 | + // TODO: block any changes to persistent data structures while save is running? |
| 103 | + if(persistence_handler && persistent_data_location && level_id) |
| 104 | + var/decl/level_persistence_handler/save_handler = GET_DECL(persistence_handler) |
| 105 | + save_handler?.save_data(src, persistent_data_location, global.using_map.path, ckey(level_id)) |
0 commit comments