diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 75d81c9eb03..87fbb3cd1c5 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -606,3 +606,19 @@ CREATE TABLE `tickets` ( CONSTRAINT `all_responses` CHECK (json_valid(`all_responses`)), CONSTRAINT `awho` CHECK (json_valid(`awho`)) ) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB; + +-- +-- Table structure for table `json_datum_saves` +-- +DROP TABLE IF EXISTS `json_datum_saves`; +CREATE TABLE `json_datum_saves` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `ckey` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', + `slotname` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_general_ci', + `slotjson` LONGTEXT NOT NULL COLLATE 'utf8mb4_general_ci', + `created` DATETIME NOT NULL DEFAULT current_timestamp(), + `updated` DATETIME NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ckey_unique` (`ckey`, `slotname`) USING BTREE, + INDEX `ckey` (`ckey`) USING BTREE +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; diff --git a/SQL/updates/49-50.sql b/SQL/updates/49-50.sql new file mode 100644 index 00000000000..228827bc4ee --- /dev/null +++ b/SQL/updates/49-50.sql @@ -0,0 +1,13 @@ +# Updating SQL from 49 to 50 -AffectedArc07 +# Add new JSON datum saves table +CREATE TABLE `json_datum_saves` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `ckey` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', + `slotname` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_general_ci', + `slotjson` LONGTEXT NOT NULL COLLATE 'utf8mb4_general_ci', + `created` DATETIME NOT NULL DEFAULT current_timestamp(), + `updated` DATETIME NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ckey_unique` (`ckey`, `slotname`) USING BTREE, + INDEX `ckey` (`ckey`) USING BTREE +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; diff --git a/code/__DEFINES/misc_defines.dm b/code/__DEFINES/misc_defines.dm index d9328e682ff..387a43cadf1 100644 --- a/code/__DEFINES/misc_defines.dm +++ b/code/__DEFINES/misc_defines.dm @@ -375,7 +375,7 @@ #define INVESTIGATE_BOMB "bombs" // The SQL version required by this version of the code -#define SQL_VERSION 49 +#define SQL_VERSION 50 // Vending machine stuff #define CAT_NORMAL 1 diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index c1c181791c3..b21e5b725b2 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -117,7 +117,8 @@ GLOBAL_LIST_INIT(admin_verbs_spawn, list( /datum/admins/proc/spawn_atom, /*allows us to spawn instances*/ /client/proc/respawn_character, /client/proc/admin_deserialize, - /client/proc/create_crate + /client/proc/create_crate, + /client/proc/json_spawn_menu )) GLOBAL_LIST_INIT(admin_verbs_server, list( /client/proc/reload_admins, diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 7ba2806125f..9598439ac76 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -3360,6 +3360,73 @@ var/mob/about_to_be_banned = locateUID(href_list["adminalert"]) usr.client.cmd_admin_alert_message(about_to_be_banned) + else if(href_list["spawnjsondatum"]) + // Get the name and JSON to spawn + var/datum/db_query/dbq = SSdbcore.NewQuery("SELECT slotname, slotjson FROM json_datum_saves WHERE ckey=:ckey AND id=:id", list( + "ckey" = usr.ckey, + "id" = href_list["spawnjsondatum"] + )) + if(!dbq.warn_execute()) + qdel(dbq) + return + + var/slot_name = null + var/slot_json = null + + // Read it + while(dbq.NextRow()) + slot_name = dbq.item[1] + slot_json = dbq.item[2] + + qdel(dbq) + + // Double check + var/spawn_choice = alert(usr, "Are you sure you wish to spawn '[slot_name]' at your current location?", "Warning", "Yes", "No") + if(spawn_choice != "Yes") + return + + // Log this for gods sake + message_admins("[key_name_admin(usr)] spawned an atom from a JSON DB save.") + log_admin("[key_name(usr)] spawned an atom from a JSON DB save, JSON Text: [slot_json]") + json_to_object(slot_json, get_turf(usr)) + + else if(href_list["deletejsondatum"]) + // Get the name + var/datum/db_query/dbq = SSdbcore.NewQuery("SELECT slotname FROM json_datum_saves WHERE ckey=:ckey AND id=:id", list( + "ckey" = usr.ckey, + "id" = href_list["deletejsondatum"] + )) + if(!dbq.warn_execute()) + qdel(dbq) + return + + var/slot_name = null + + // Read it + while(dbq.NextRow()) + slot_name = dbq.item[1] + + qdel(dbq) + + // Double check + var/delete_choice = alert(usr, "Are you sure you wish to delete '[slot_name]'? This cannot be undone!", "Warning", "Yes", "No") + if(delete_choice != "Yes") + return + + var/datum/db_query/dbq2 = SSdbcore.NewQuery("DELETE FROM json_datum_saves WHERE ckey=:ckey AND id=:id", list( + "ckey" = usr.ckey, + "id" = href_list["deletejsondatum"] + )) + + if(!dbq2.warn_execute()) + qdel(dbq2) + return + + qdel(dbq2) + owner.json_spawn_menu() // Refresh their menu + to_chat(usr, "Slot [slot_name] deleted.") + + /client/proc/create_eventmob_for(mob/living/carbon/human/H, killthem = 0) if(!check_rights(R_EVENT)) return diff --git a/code/modules/admin/verbs/serialization.dm b/code/modules/admin/verbs/serialization.dm index 863d15287ce..c60a072b6f7 100644 --- a/code/modules/admin/verbs/serialization.dm +++ b/code/modules/admin/verbs/serialization.dm @@ -11,7 +11,75 @@ return var/atom/movable/AM = holder.marked_datum - to_chat(src, json_encode(AM.serialize())) + + var/json_data = json_encode(AM.serialize()) + + var/choice = alert(usr, "Would you like to store this on your PC or server side?", "Storage Location", "PC", "Server", "Cancel") + if(!choice || choice == "Cancel") + return + + if(choice == "PC") + to_chat(src, json_data) + + if(choice == "Server") + // Right, get their slot names + var/list/slots = list("--NEW--") + var/datum/db_query/dbq = SSdbcore.NewQuery("SELECT slotname FROM json_datum_saves WHERE ckey=:ckey", list("ckey" = usr.ckey)) + if(!dbq.warn_execute()) + qdel(dbq) + return + + while(dbq.NextRow()) + slots += dbq.item[1] + + qdel(dbq) + + var/slot_choice = input(usr, "Select a slot to update, or create a new one.", "Slot Selection") as null|anything in slots + + if(!slot_choice) + return + + if(slot_choice == "--NEW--") + // New slot, save to DB + var/chosen_slot_name = input(usr, "Name your slot", "Slot name") as null|text + if(!chosen_slot_name || length(chosen_slot_name) == 0) + return + + // Sanitize the name + var/clean_name = sanitize(copytext(chosen_slot_name, 1, 32)) // 32 chars is your max + + // And save + var/datum/db_query/dbq2 = SSdbcore.NewQuery("INSERT INTO json_datum_saves (ckey, slotname, slotjson) VALUES(:ckey, :slotname, :slotjson)", list( + "ckey" = usr.ckey, + "slotname" = clean_name, + "slotjson" = json_data + )) + if(!dbq2.warn_execute()) + qdel(dbq2) + return + + qdel(dbq2) + to_chat(usr, "Successfully saved [clean_name]. You can spawn it from Debug > Spawn Saved JSON Datum.") + + else + // Existing slot, warn first + var/confirmation = alert(usr, "Are you sure you want to update '[slot_choice]'? This cannot be undone!", "You sure?", "Yes", "No") + if(confirmation != "Yes") + return + + // Now update + var/datum/db_query/dbq2 = SSdbcore.NewQuery("UPDATE json_datum_saves SET slotjson=:slotjson WHERE slotname=:slotname AND ckey=:ckey", list( + "slotjson" = json_data, + "ckey" = usr.ckey, + "slotname" = slot_choice + )) + if(!dbq2.warn_execute()) + qdel(dbq2) + return + + qdel(dbq2) + to_chat(usr, "Successfully updated [slot_choice]. You can spawn it from Debug > Spawn Saved JSON Datum.") + /client/proc/admin_deserialize() set name = "Deserialize JSON datum" @@ -26,3 +94,41 @@ json_to_object(json_text, get_turf(usr)) message_admins("[key_name_admin(usr)] spawned an atom from a custom JSON object.") log_admin("[key_name(usr)] spawned an atom from a custom JSON object, JSON Text: [json_text]") + + +/client/proc/json_spawn_menu() + set name = "Spawn Saved JSON Datum" + set desc = "Spawns a JSON datums saved server side" + set category = "Debug" + + // This needs a holder to function + if(!check_rights(R_SPAWN) || !holder) + return + + // Right, get their slot names + var/list/slots = list() + var/datum/db_query/dbq = SSdbcore.NewQuery("SELECT slotname, id FROM json_datum_saves WHERE ckey=:ckey", list("ckey" = usr.ckey)) + if(!dbq.warn_execute()) + qdel(dbq) + return + + while(dbq.NextRow()) + slots[dbq.item[1]] += dbq.item[2] + qdel(dbq) + + + var/datum/browser/popup = new(usr, "jsonspawnmenu", "JSON Spawn Menu", 400, 300) + + // Cache this to reduce proc jumps + var/holder_uid = holder.UID() + + var/list/rows = list() + rows += "" + for(var/slotname in slots) + rows += "" + + rows += "
SlotActions
[slotname]Spawn  Delete
" + + popup.set_content(rows.Join("")) + popup.open(FALSE) + diff --git a/config/example/config.toml b/config/example/config.toml index e485a58436a..82b894458d7 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -144,7 +144,7 @@ ipc_screens = [ # Enable/disable the database on a whole sql_enabled = false # SQL version. If this is a mismatch, round start will be delayed -sql_version = 49 +sql_version = 50 # SQL server address. Can be an IP or DNS name sql_address = "127.0.0.1" # SQL server port