diff --git a/baystation12.dme b/baystation12.dme index e4c660ca7e1..884ad691a88 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1013,6 +1013,7 @@ #include "code\modules\client\preferences_toggles.dm" #include "code\modules\client\ui_style.dm" #include "code\modules\client\preference_setup\preference_setup.dm" +#include "code\modules\client\preference_setup\preferences_sql.dm" #include "code\modules\client\preference_setup\antagonism\01_candidacy.dm" #include "code\modules\client\preference_setup\antagonism\02_setup.dm" #include "code\modules\client\preference_setup\general\01_basic.dm" diff --git a/code/datums/server_greeting.dm b/code/datums/server_greeting.dm index 70c9409ef95..0a9511d018f 100644 --- a/code/datums/server_greeting.dm +++ b/code/datums/server_greeting.dm @@ -207,7 +207,7 @@ user << output(JS_SANITIZE(data), "greeting.browser:AddContent") if (save_prefs) - user.prefs.handle_preferences_save(user) + user.prefs.save_preferences() /* * Basically the Topic proc for the greeting datum. diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 732421ab782..56be3304b8e 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -551,7 +551,7 @@ var/list/admin_verbs_cciaa = list( prefs.ooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color else if(response == "Reset to default") prefs.ooccolor = initial(prefs.ooccolor) - prefs.handle_preferences_save(src) + prefs.save_preferences() feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return @@ -920,7 +920,7 @@ var/list/admin_verbs_cciaa = list( usr << "You now will get attack log messages" else usr << "You now won't get attack log messages" - prefs.handle_preferences_save(src) + prefs.save_preferences() /client/proc/toggleghostwriters() diff --git a/code/modules/client/preference_setup/antagonism/01_candidacy.dm b/code/modules/client/preference_setup/antagonism/01_candidacy.dm index 1bb96e80224..8909069f3d4 100644 --- a/code/modules/client/preference_setup/antagonism/01_candidacy.dm +++ b/code/modules/client/preference_setup/antagonism/01_candidacy.dm @@ -11,6 +11,15 @@ /datum/category_item/player_setup_item/antagonism/candidacy/gather_load_query() return list("ss13_characters" = list("vars" = list("be_special_role"), "args" = list("id"))) +/datum/category_item/player_setup_item/antagonism/candidacy/gather_load_parameters() + return list(":id" = pref.current_character) + +/datum/category_item/player_setup_item/antagonism/candidacy/gather_save_query() + return list("ss13_characters" = list("be_special_role", "id" = 1)) + +/datum/category_item/player_setup_item/antagonism/candidacy/gather_save_parameters() + return list(":be_special_role" = list2params(pref.be_special_role), ":id" = pref.current_character) + /datum/category_item/player_setup_item/antagonism/candidacy/sanitize_character(var/sql_load = 0) if (sql_load) if (pref.be_special_role) diff --git a/code/modules/client/preference_setup/antagonism/02_setup.dm b/code/modules/client/preference_setup/antagonism/02_setup.dm index c84c2a6781f..59e06d2b3dc 100644 --- a/code/modules/client/preference_setup/antagonism/02_setup.dm +++ b/code/modules/client/preference_setup/antagonism/02_setup.dm @@ -16,6 +16,16 @@ var/global/list/uplink_locations = list("PDA", "Headset", "None") return list("ss13_characters_flavour" = list("vars" = list("records_exploit" = "exploit_record"), "args" = list("char_id")), "ss13_characters" = list("vars" = list("uplink_location" = "uplinklocation"), "args" = list("id"))) +/datum/category_item/player_setup_item/antagonism/basic/gather_load_parameters() + return list(":char_id" = pref.current_character, ":id" = pref.current_character) + +/datum/category_item/player_setup_item/antagonism/basic/gather_save_query() + return list("ss13_characters_flavour" = list("records_exploit", "char_id" = 1), + "ss13_characters" = list("uplink_location", "id" = 1)) + +/datum/category_item/player_setup_item/antagonism/basic/gather_save_parameters() + return list(":records_exploit" = pref.exploit_record, ":char_id" = pref.current_character, ":uplink_location" = pref.uplinklocation, ":id" = pref.current_character) + /datum/category_item/player_setup_item/antagonism/basic/sanitize_character() pref.uplinklocation = sanitize_inlist(pref.uplinklocation, uplink_locations, initial(pref.uplinklocation)) diff --git a/code/modules/client/preference_setup/general/01_basic.dm b/code/modules/client/preference_setup/general/01_basic.dm index 4665e87d82a..651c7ad331b 100644 --- a/code/modules/client/preference_setup/general/01_basic.dm +++ b/code/modules/client/preference_setup/general/01_basic.dm @@ -1,7 +1,7 @@ /datum/category_item/player_setup_item/general/basic name = "Basic" sort_order = 1 - var/list/valid_player_genders = list(MALE, FEMALE) + var/static/list/valid_player_genders = list(MALE, FEMALE) /datum/category_item/player_setup_item/general/basic/load_character(var/savefile/S) S["real_name"] >> pref.real_name @@ -28,6 +28,27 @@ "spawnpoint",), "args" = list("id"))) +/datum/category_item/player_setup_item/general/basic/gather_load_parameters() + return list(":id" = pref.current_character) + +/datum/category_item/player_setup_item/general/basic/gather_save_query() + return list("ss13_characters" = list("name", + "random_name", + "gender", + "age", + "metadata", + "spawnpoint", + "id" = 1)) + +/datum/category_item/player_setup_item/general/basic/gather_save_parameters() + return list("ss13_characters" = list(":name" = pref.real_name, + ":random_name" = pref.be_random_name, + ":gender" = pref.gender, + ":age" = pref.age, + ":metadata" = pref.metadata, + ":spawnpoint" = pref.spawnpoint, + ":id" = pref.current_character)) + /datum/category_item/player_setup_item/general/basic/sanitize_character() pref.age = sanitize_integer(text2num(pref.age), AGE_MIN, AGE_MAX, initial(pref.age)) pref.gender = sanitize_inlist(pref.gender, valid_player_genders, pick(valid_player_genders)) diff --git a/code/modules/client/preference_setup/general/02_language.dm b/code/modules/client/preference_setup/general/02_language.dm index 101695cc0c4..61857ffbf48 100644 --- a/code/modules/client/preference_setup/general/02_language.dm +++ b/code/modules/client/preference_setup/general/02_language.dm @@ -11,6 +11,17 @@ /datum/category_item/player_setup_item/general/language/gather_load_query() return list("ss13_characters" = list("vars" = list("language" = "alternate_languages"), "args" = list("id"))) +/datum/category_item/player_setup_item/general/language/gather_load_parameters() + return list(":id" = pref.current_character) + +/datum/category_item/player_setup_item/general/language/gather_save_query() + return list("ss13_characters" = list("language", + "id" = 1)) + +/datum/category_item/player_setup_item/general/language/gather_save_parameters() + return list("ss13_characters" = list(":language" = list2params(pref.alternate_languages), + ":id" = pref.current_character)) + /datum/category_item/player_setup_item/general/language/sanitize_character(var/sql_load = 0) if (sql_load) pref.alternate_languages = params2list(pref.alternate_languages) diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 81868b07db6..eb68f418c98 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -63,6 +63,39 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O "organs_robotic" = "rlimb_data"), "args" = list("id"))) +/datum/category_item/player_setup_item/general/body/gather_load_parameters() + return list(":id" = pref.current_character) + +/datum/category_item/player_setup_item/general/body/gather_save_query() + return list("ss13_characters" = list("species", + "hair_colour", + "facial_colour", + "skin_tone", + "skin_colour", + "hair_style", + "facial_style", + "eyes_colour", + "b_type", + "disabilities", + "organs_data", + "organs_robotic", + "id" = 1)) + +/datum/category_item/player_setup_item/general/body/gather_save_parameters() + return list("ss13_characters" = list(":species" = pref.species, + ":hair_colour" = "#" + num2hex(pref.r_hair) + num2hex(pref.g_hair) + num2hex(pref.b_hair), + ":facial_colour" = "#" + num2hex(pref.r_facial) + num2hex(pref.g_facial) + num2hex(pref.b_facial), + ":skin_tone" = pref.s_tone, + ":skin_colour" = "#" + num2hex(pref.r_skin) + num2hex(pref.g_skin) + num2hex(pref.b_skin), + ":hair_style" = pref.h_style, + ":facial_style" = pref.f_style, + ":eyes_colour" = "#" + num2hex(pref.r_eyes) + num2hex(pref.g_eyes) + num2hex(pref.b_eyes), + ":b_type" = pref.b_type, + ":disabilities" = pref.disabilities, + ":organs_data" = list2params(pref.organ_data), + ":organs_robotic"= list2params(pref.rlimb_data), + ":id" = pref.current_character)) + /datum/category_item/player_setup_item/general/body/sanitize_character(var/sql_load = 0) if(!pref.species || !(pref.species in playable_species)) pref.species = "Human" diff --git a/code/modules/client/preference_setup/general/04_equipment.dm b/code/modules/client/preference_setup/general/04_equipment.dm index 8d53fb4c60a..a4297812471 100644 --- a/code/modules/client/preference_setup/general/04_equipment.dm +++ b/code/modules/client/preference_setup/general/04_equipment.dm @@ -17,6 +17,15 @@ /datum/category_item/player_setup_item/general/equipment/gather_load_query() return list("ss13_characters" = list("vars" = list("underwear", "undershirt", "backbag", "gear"), "args" = list("id"))) +/datum/category_item/player_setup_item/general/equipment/gather_load_parameters() + return list(":id" = pref.current_character) + +/datum/category_item/player_setup_item/general/equipment/gather_save_query() + return list("ss13_characters" = list("underwear", "undershirt", "backbag", "gear", "id" = 1)) + +/datum/category_item/player_setup_item/general/equipment/gather_save_parameters() + return list(":underwear" = pref.underwear, ":undershirt" = pref.undershirt, ":backbag" = pref.backbag, ":gear" = list2params(pref.gear), ":id" = pref.current_character) + /datum/category_item/player_setup_item/general/equipment/sanitize_character(var/sql_load = 0) if (sql_load) pref.backbag = text2num(pref.backbag) diff --git a/code/modules/client/preference_setup/general/05_background.dm b/code/modules/client/preference_setup/general/05_background.dm index f110bb5df38..7fe7a7be13e 100644 --- a/code/modules/client/preference_setup/general/05_background.dm +++ b/code/modules/client/preference_setup/general/05_background.dm @@ -29,6 +29,27 @@ "args" = list("char_id")), "ss13_characters" = list("vars" = list("home_system", "citizenship", "faction", "religion"), "args" = list("id"))) +/datum/category_item/player_setup_item/general/background/gather_load_parameters() + return list(":id" = pref.current_character, ":char_id" = pref.current_character) + +/datum/category_item/player_setup_item/general/background/gather_save_query() + return list("ss13_characters_flavour" = list("records_employment", + "records_medical", + "records_security", + "char_id" = 1), + "ss13_characters" = list("home_system", "citizenship", "faction", "religion", "id" = 1)) + +/datum/category_item/player_setup_item/general/background/gather_save_parameters() + return list(":records_employment" = pref.gen_record, + ":records_medical" = pref.med_record, + ":records_security" = pref.sec_record, + ":char_id" = pref.current_character, + ":home_system" = pref.home_system, + ":citizenship" = pref.citizenship, + ":faction" = pref.faction, + ":religion" = pref.religion, + ":id" = pref.current_character) + /datum/category_item/player_setup_item/general/background/sanitize_character() if(!pref.home_system) pref.home_system = "Unset" diff --git a/code/modules/client/preference_setup/general/06_flavor.dm b/code/modules/client/preference_setup/general/06_flavor.dm index 19873858e61..f4cbdbe1b43 100644 --- a/code/modules/client/preference_setup/general/06_flavor.dm +++ b/code/modules/client/preference_setup/general/06_flavor.dm @@ -35,14 +35,14 @@ /datum/category_item/player_setup_item/general/flavor/gather_load_query() var/list/var_list = list("flavour_general" = "flavor_texts/general", - "flavour_head" = "flavour_texts/head", - "flavour_face" = "flavour_texts/face", - "flavour_eyes" = "flavour_texts/eyes", - "flavour_torso" = "flavour_texts/torso", - "flavour_arms" = "flavour_texts/arms", - "flavour_hands" = "flavour_texts/hands", - "flavour_legs" = "flavour_texts/legs", - "flavour_feet" = "flavour_texts/feet", + "flavour_head" = "flavor_texts/head", + "flavour_face" = "flavor_texts/face", + "flavour_eyes" = "flavor_texts/eyes", + "flavour_torso" = "flavor_texts/torso", + "flavour_arms" = "flavor_texts/arms", + "flavour_hands" = "flavor_texts/hands", + "flavour_legs" = "flavor_texts/legs", + "flavour_feet" = "flavor_texts/feet", "robot_default" = "flavour_texts_robot/default") for (var/module in robot_module_types) @@ -50,6 +50,44 @@ return list("ss13_characters_flavour" = list("vars" = var_list, "args" = list("char_id"))) +/datum/category_item/player_setup_item/general/flavor/gather_load_parameters() + return list(":char_id" = pref.current_character) + +/datum/category_item/player_setup_item/general/flavor/gather_save_query() + var/list/var_list = list("flavour_general", + "flavour_head", + "flavour_face", + "flavour_eyes", + "flavour_torso", + "flavour_arms", + "flavour_hands", + "flavour_legs", + "flavour_feet", + "robot_default", + "char_id" = 1) + + for (var/module in robot_module_types) + var_list += "robot_[module]" + + return list("ss13_characters_flavour" = var_list) + +/datum/category_item/player_setup_item/general/flavor/gather_save_parameters() + var/list/var_list = list(":char_id" = pref.current_character, + ":flavour_head" = pref.flavor_texts["head"], + ":flavour_face" = pref.flavor_texts["face"], + ":flavour_eyes" = pref.flavor_texts["eyes"], + ":flavour_torso" = pref.flavor_texts["torso"], + ":flavour_arms" = pref.flavor_texts["arms"], + ":flavour_hands" = pref.flavor_texts["hands"], + ":flavour_legs" = pref.flavor_texts["legs"], + ":flavour_feet" = pref.flavor_texts["feet"], + ":robot_default" = pref.flavour_texts_robot["default"]) + + for (var/module in robot_module_types) + var_list[":robot_[module]"] += pref.flavour_texts_robot[module] + + return list("ss13_characters_flavour" = var_list) + /datum/category_item/player_setup_item/general/flavor/sanitize_character() return diff --git a/code/modules/client/preference_setup/global/01_ui.dm b/code/modules/client/preference_setup/global/01_ui.dm index d69de5b8ea8..0585039cad7 100644 --- a/code/modules/client/preference_setup/global/01_ui.dm +++ b/code/modules/client/preference_setup/global/01_ui.dm @@ -17,6 +17,15 @@ /datum/category_item/player_setup_item/player_global/ui/gather_load_query() return list("ss13_player_preferences" = list("vars" = list("UI_style", "UI_style_color", "UI_style_alpha", "ooccolor"), "args" = list("ckey"))) +/datum/category_item/player_setup_item/player_global/ui/gather_load_parameters() + return list(":ckey" = pref.client.ckey) + +/datum/category_item/player_setup_item/player_global/ui/gather_save_query() + return list("ss13_player_preferences" = list("UI_style", "UI_style_color", "UI_style_alpha", "ooccolor", "ckey" = 1)) + +/datum/category_item/player_setup_item/player_global/ui/gather_save_parameters() + return list(":ckey" = pref.client.ckey, ":UI_style" = pref.UI_style, ":UI_style_alpha" = pref.UI_style_alpha, ":UI_style_color" = pref.UI_style_color, ":ooccolor" = pref.ooccolor) + /datum/category_item/player_setup_item/player_global/ui/sanitize_preferences() pref.UI_style = sanitize_inlist(pref.UI_style, all_ui_styles, initial(pref.UI_style)) pref.UI_style_color = sanitize_hexcolor(pref.UI_style_color, initial(pref.UI_style_color)) diff --git a/code/modules/client/preference_setup/global/02_settings.dm b/code/modules/client/preference_setup/global/02_settings.dm index 33c1b13b117..332e6bdd0a5 100644 --- a/code/modules/client/preference_setup/global/02_settings.dm +++ b/code/modules/client/preference_setup/global/02_settings.dm @@ -21,6 +21,21 @@ /datum/category_item/player_setup_item/player_global/settings/gather_load_query() return list("ss13_player_preferences" = list("vars" = list("lastchangelog", "current_character", "toggles", "asfx_toggles" = "asfx_togs", "motd_hash", "memo_hash"), "args" = list("ckey"))) +/datum/category_item/player_setup_item/player_global/settings/gather_load_parameters() + return list(":ckey" = pref.client.ckey) + +/datum/category_item/player_setup_item/player_global/settings/gather_save_query() + return list("ss13_player_preferences" = list("lastchangelog", "current_character", "toggles", "asfx_toggles", "motd_hash", "memo_hash", "ckey" = 1)) + +/datum/category_item/player_setup_item/player_global/settings/gather_save_parameters() + return list(":ckey" = pref.client.ckey, + ":lastchangelog" = pref.lastchangelog, + ":current_character" = pref.current_character, + ":toggles" = pref.toggles, + ":asfx_toggles" = pref.asfx_togs, + ":motd_hash" = pref.motd_hash, + ":memo_hash" = pref.memo_hash) + /datum/category_item/player_setup_item/player_global/settings/sanitize_preferences(var/sql_load = 0) if (sql_load) pref.current_character = text2num(pref.current_character) @@ -28,6 +43,9 @@ pref.lastchangelog = sanitize_text(pref.lastchangelog, initial(pref.lastchangelog)) pref.default_slot = sanitize_integer(text2num(pref.default_slot), 1, config.character_slots, initial(pref.default_slot)) pref.toggles = sanitize_integer(text2num(pref.toggles), 0, 65535, initial(pref.toggles)) + pref.asfx_togs = sanitize_integer(text2num(pref.asfx_togs), 0, 65535, initial(pref.toggles)) + pref.motd_hash = sanitize_text(pref.motd_hash, initial(pref.motd_hash)) + pref.memo_hash = sanitize_text(pref.memo_hash, initial(pref.memo_hash)) /datum/category_item/player_setup_item/player_global/settings/content(var/mob/user) . += "Play admin midis: [(pref.toggles & SOUND_MIDI) ? "Yes" : "No"]
" diff --git a/code/modules/client/preference_setup/global/03_language.dm b/code/modules/client/preference_setup/global/03_language.dm index 740806cef7d..c5f8a7d1164 100644 --- a/code/modules/client/preference_setup/global/03_language.dm +++ b/code/modules/client/preference_setup/global/03_language.dm @@ -11,6 +11,15 @@ /datum/category_item/player_setup_item/player_global/language/gather_load_query() return list("ss13_player_preferences" = list("vars" = list("language_prefixes"), "args" = list("ckey"))) +/datum/category_item/player_setup_item/player_global/language/gather_load_parameters() + return list(":ckey" = pref.client.ckey) + +/datum/category_item/player_setup_item/player_global/ui/gather_save_query() + return list("ss13_player_preferences" = list("language_prefixes", "ckey" = 1)) + +/datum/category_item/player_setup_item/player_global/language/gather_save_parameters() + return list(":ckey" = pref.client.ckey, ":language_prefixes" = list2params(pref.language_prefixes)) + /datum/category_item/player_setup_item/player_global/language/sanitize_preferences(var/sql_load = 0) if (sql_load && pref.language_prefixes) pref.language_prefixes = params2list(pref.language_prefixes) diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index 08d771c98d1..0a9fa071a61 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -36,9 +36,28 @@ /datum/category_item/player_setup_item/occupation/gather_load_query() return list("ss13_characters" = list("vars" = list("jobs" = "unsanitized_jobs", "alternate_option", "alternate_titles" = "player_alt_titles"), "args" = list("id"))) +/datum/category_item/player_setup_item/occupation/gather_load_parameters() + return list(":id" = pref.current_character) + +/datum/category_item/player_setup_item/occupation/gather_save_query() + return list("ss13_characters" = list("jobs", "alternate_option", "alternate_titles", "id" = 1)) + +/datum/category_item/player_setup_item/occupation/gather_save_parameters() + var/list/compiled_jobs = list("job_civilian_high" = pref.job_civilian_high, + "job_civilian_med" = pref.job_civilian_med, + "job_civilian_low" = pref.job_civilian_low, + "job_medsci_high" = pref.job_medsci_high, + "job_medsci_med" = pref.job_medsci_med, + "job_medsci_low" = pref.job_medsci_low, + "job_engsec_high" = pref.job_engsec_high, + "job_engsec_med" = pref.job_engsec_med, + "job_engsec_low" = pref.job_engsec_low) + + return list(":jobs" = list2params(compiled_jobs), ":alternate_option" = pref.alternate_option, ":alternate_titles" = list2params(pref.player_alt_titles), ":id" = pref.current_character) + /datum/category_item/player_setup_item/occupation/sanitize_character(var/sql_load = 0) if (sql_load) - pref.alternate_option = text2num(alternate_option) + pref.alternate_option = text2num(pref.alternate_option) var/list/jobs = params2list(pref.unsanitized_jobs) diff --git a/code/modules/client/preference_setup/preference_setup.dm b/code/modules/client/preference_setup/preference_setup.dm index da59832595d..6d80bb3f315 100644 --- a/code/modules/client/preference_setup/preference_setup.dm +++ b/code/modules/client/preference_setup/preference_setup.dm @@ -109,6 +109,7 @@ /datum/category_group/player_setup_category var/sort_order = 0 var/sql_role = SQL_CHARACTER + var/modified = 0 /datum/category_group/player_setup_category/dd_SortValue() return sort_order @@ -132,131 +133,40 @@ for(var/datum/category_item/player_setup_item/PI in items) PI.sanitize_character(config.sql_saves) -/* - * A proc for dynamically loading a character from the database. - * See the category items for procs on what pieces are given. - * - * Each query is cached as well, after generation. Only things recompiled are the arguments and the finalized query text (with arguments in it). - */ -/datum/category_group/player_setup_category/proc/handle_sql_loading(var/load_type) - var/static/list/query_cache - - // We aren't loading this category. Bye. - if (sql_role != load_type) - return - - if (isnull(query_cache)) - query_cache = list() - - // This handles the generation of queries. One per category, total of 5 UNLESS one category wants to query more than one table. - // It uses like, 3+ levels of lists. But it works, and is only run once per category in the global sense. So technically, efficient! - // :D - Skull132 - if (!query_cache.len || isnull(query_cache[type])) - query_cache[type] = list() - var/list/tables = list() - - // First, consolidate the different lists into one. - for (var/datum/category_item/player_setup_item/PI in items) - var/list/pi_tables = PI.gather_load_query() - // Expected layout: list("table A name" = list("vars" = list("column name" = "var name"), "args" = list()), - // "table B name" = list("vars" = list(), "args" = list())) - - for (var/table in pi_tables) - var/list/A = pi_tables[table] - if (isnull(tables[table])) - tables[table] = list("vars" = list(), "args" = list()) - - tables[table]["vars"] |= A["vars"] - tables[table]["args"] |= A["args"] - - // Second, create the queries and save them. - for (var/table in tables) - var/query = "SELECT " - var/list/var_names = tables[table]["vars"] - var/count = var_names.len - - // Process the variables and rows we want. - var/i = 1 - for (var/name in var_names) - query += name - - if (!isnull(var_names[name])) - var/new_name = var_names[name] - var_names.Remove(name) - var_names.Insert(i, new_name) - - if (i != count) - query += ", " - else - query += " " - - i++ - - query += "FROM [table] WHERE " - - // Process the args. - var/list/arg_names = tables[table]["args"] - count = arg_names.len - for (i = 1, i <= count, i++) - query += "[arg_names[i]] = :[arg_names[i]]" - arg_names[i] = ":[arg_names[i]]" - - if (i != count) - query += " AND " - else - query += ";" - - // Save it. - query_cache[type][query] = var_names - - // Need to typecast due to reasons. - var/datum/category_collection/player_setup_collection/cc = collection - - // Actually utilize the queries. - var/list/arg_list = gather_load_parameters() - for (var/query_text in query_cache[type]) - var/DBQuery/query = dbcon.NewQuery(query_text) - query.Execute(arg_list, 1) - - // Each query should only return exactly 1 row. - var/list/var_names = query_cache[type][query_text] - if (query.NextRow()) - for (var/i = 1, i <= var_names.len, i++) - var/list/layers = splittext(var_names[i], "/") - if (layers.len == 1) - cc.preferences.vars[var_names[i]] = query.item[i] - else - cc.preferences.vars[layers[1]][layers[2]] = query.item[i] - -/datum/category_group/player_setup_category/proc/gather_load_parameters() - var/list/arg_list = list() - for (var/datum/category_item/player_setup_item/PI in items) - arg_list |= PI.gather_load_parameters() - - return arg_list - /datum/category_group/player_setup_category/proc/save_character(var/savefile/S) // Sanitize all data, then save it - for(var/datum/category_item/player_setup_item/PI in items) + for (var/datum/category_item/player_setup_item/PI in items) PI.sanitize_character() - for(var/datum/category_item/player_setup_item/PI in items) - PI.save_character(S) + + if (!config.sql_saves || !establish_db_connection(dbcon)) + for (var/datum/category_item/player_setup_item/PI in items) + PI.save_character(S) + else + if (modified && sql_role == SQL_CHARACTER) + handle_sql_saving(SQL_CHARACTER) + modified = 0 /datum/category_group/player_setup_category/proc/load_preferences(var/savefile/S) if (!config.sql_saves || !establish_db_connection(dbcon)) - for(var/datum/category_item/player_setup_item/PI in items) + for (var/datum/category_item/player_setup_item/PI in items) PI.load_preferences(S) else handle_sql_loading(SQL_PREFERENCES) - for(var/datum/category_item/player_setup_item/PI in items) + for (var/datum/category_item/player_setup_item/PI in items) PI.sanitize_preferences(config.sql_saves) /datum/category_group/player_setup_category/proc/save_preferences(var/savefile/S) - for(var/datum/category_item/player_setup_item/PI in items) + for (var/datum/category_item/player_setup_item/PI in items) PI.sanitize_preferences() - for(var/datum/category_item/player_setup_item/PI in items) - PI.save_preferences(S) + + if (!config.sql_saves || !establish_db_connection(dbcon)) + for (var/datum/category_item/player_setup_item/PI in items) + PI.save_preferences(S) + else + if (modified && sql_role == SQL_PREFERENCES) + handle_sql_saving(SQL_PREFERENCES) + modified = 0 /datum/category_group/player_setup_category/proc/update_setup(var/savefile/preferences, var/savefile/character) for(var/datum/category_item/player_setup_item/PI in items) @@ -332,12 +242,24 @@ /datum/category_item/player_setup_item/proc/gather_load_query() return list() +/* +* Called when the owner category is composing its insert query +*/ +/datum/category_item/player_setup_item/proc/gather_save_query() + return list() + /* * Called when the owner category is composing its query parameters for loading. */ /datum/category_item/player_setup_item/proc/gather_load_parameters() return list() +/* +* Called when the owner category is composing its query parameters for inserting a new record. +*/ +/datum/category_item/player_setup_item/proc/gather_save_parameters() + return list() + /datum/category_item/player_setup_item/proc/content() return @@ -351,11 +273,14 @@ if(..()) return 1 var/mob/user = usr - if(!user.client) + if (!user.client) return 1 . = OnTopic(href, href_list, user) - if(. == TOPIC_REFRESH) + if (. != TOPIC_NOACTION) + var/datum/category_group/player_setup_category/cat = category + cat.modified = 1 + if (. == TOPIC_REFRESH) user.client.prefs.ShowChoices(user) /datum/category_item/player_setup_item/CanUseTopic(var/mob/user) @@ -367,6 +292,3 @@ /datum/category_item/player_setup_item/proc/preference_mob() if(pref && pref.client && pref.client.mob) return pref.client.mob - -#undef SQL_CHARACTER -#undef SQL_PREFERENCES diff --git a/code/modules/client/preference_setup/preferences_sql.dm b/code/modules/client/preference_setup/preferences_sql.dm new file mode 100644 index 00000000000..369dbcbac21 --- /dev/null +++ b/code/modules/client/preference_setup/preferences_sql.dm @@ -0,0 +1,197 @@ +/* + * A proc for dynamically loading a character from the database. + * See the category items for procs on what pieces are given. + * + * Each query is cached as well, after generation. Only things recompiled are the arguments and the finalized query text (with arguments in it). + */ +/datum/category_group/player_setup_category/proc/handle_sql_loading(var/role_type) + var/static/list/query_cache + + // We aren't loading this category. Bye. + if (role_type && sql_role != role_type) + return + + if (isnull(query_cache)) + query_cache = list() + + // This handles the generation of queries. One per category, total of 5 UNLESS one category wants to query more than one table. + // It uses like, 3+ levels of lists. But it works, and is only run once per category in the global sense. So technically, efficient! + // :D - Skull132 + if (!query_cache.len || isnull(query_cache[type])) + query_cache[type] = list() + var/list/tables = list() + + // First, consolidate the different lists into one. + for (var/datum/category_item/player_setup_item/PI in items) + var/list/pi_tables = PI.gather_load_query() + // Expected layout: list("table A name" = list("vars" = list("column name" = "var name"), "args" = list()), + // "table B name" = list("vars" = list(), "args" = list())) + + for (var/table in pi_tables) + var/list/A = pi_tables[table] + if (isnull(tables[table])) + tables[table] = list("vars" = list(), "args" = list()) + + tables[table]["vars"] |= A["vars"] + tables[table]["args"] |= A["args"] + + // Second, create the queries and save them. + for (var/table in tables) + var/query = "SELECT " + var/list/var_names = tables[table]["vars"] + var/count = var_names.len + + // Process the variables and rows we want. + var/i = 1 + for (var/name in var_names) + query += name + + if (!isnull(var_names[name])) + var/new_name = var_names[name] + var_names.Remove(name) + var_names.Insert(i, new_name) + + if (i != count) + query += ", " + else + query += " " + + i++ + + query += "FROM [table] WHERE " + + // Process the args. + var/list/arg_names = tables[table]["args"] + count = arg_names.len + for (i = 1, i <= count, i++) + query += "[arg_names[i]] = :[arg_names[i]]" + arg_names[i] = ":[arg_names[i]]" + + if (i != count) + query += " AND " + else + query += ";" + + // Save it. + query_cache[type][query] = var_names + + // Need to typecast due to reasons. + var/datum/category_collection/player_setup_collection/cc = collection + + // Actually utilize the queries. + var/list/arg_list = gather_load_parameters() + for (var/query_text in query_cache[type]) + var/DBQuery/query = dbcon.NewQuery(query_text) + query.Execute(arg_list, 1) + if (query.ErrorMsg()) + error("Error loading character from SQL: [query.ErrorMsg()]") + + // Each query should only return exactly 1 row. + var/list/var_names = query_cache[type][query_text] + if (query.NextRow()) + for (var/i = 1, i <= var_names.len, i++) + var/list/layers = splittext(var_names[i], "/") + try + if (layers.len == 1) + cc.preferences.vars[var_names[i]] = query.item[i] + else + cc.preferences.vars[layers[1]][layers[2]] = query.item[i] + catch(var/exception/e) + error("Error loading character from SQL: [e.name]") + log_debug("SQL Saves: [e.name]") + log_debug("SQL Saves: [e.desc]") + +/datum/category_group/player_setup_category/proc/gather_load_parameters() + var/list/arg_list = list() + for (var/datum/category_item/player_setup_item/PI in items) + arg_list |= PI.gather_load_parameters() + + return arg_list + +/* + * A proc for dynamically inserting new character and preference records into the database. + * See the category items for procs on what pieces are given. + * + * Each query is cached as well, after generation. Only things recompiled are the arguments and the finalized query text (with arguments in it). + */ +/datum/category_group/player_setup_category/proc/handle_sql_saving(var/role_type) + var/static/list/query_cache + + // We aren't loading this category. Bye. + if (role_type && sql_role != role_type) + return + + if (isnull(query_cache)) + query_cache = list() + + // This handles the generation of queries. One per category, total of 5 UNLESS one category wants to query more than one table. + // It uses like, 3+ levels of lists. But it works, and is only run once per category in the global sense. So technically, efficient! + // :D - Skull132 + if (!query_cache.len || isnull(query_cache[type])) + query_cache[type] = list() + var/list/tables = list() + + // First, consolidate the different lists into one. + for (var/datum/category_item/player_setup_item/PI in items) + var/list/pi_tables = PI.gather_save_query() + // Expected layout: list("table A name" = list("var1" = 1, "var2" = 0, "var3" = 1), + // "table B name" = list("var4" = 0, "var5" = 0, "var6" = 0)) + + for (var/table in pi_tables) + var/list/A = pi_tables[table] + if (isnull(tables[table])) + tables[table] = list() + + tables[table] |= A + + // Second, create the queries and save them. + for (var/table in tables) + var/query = "INSERT INTO [table] (" + var/list/var_names = tables[table] + + query += "[jointext(var_names, ", ")]) VALUES (" + + // Process the args. + var/list/arg_names = list() + for (var/variable in var_names) + arg_names += ":[variable]" + + query += "[jointext(arg_names, ", ")]) ON DUPLICATE KEY UPDATE" + + var/i = 1 + for (var/variable in var_names) + if (isnull(var_names[variable])) + query += " [variable] = [arg_names[i]]" + + i++ + + // Save it. + query_cache[type] += query + + // Actually utilize the queries. + var/list/arg_list = gather_save_parameters() + for (var/query_text in query_cache[type]) + var/DBQuery/query = dbcon.NewQuery(query_text) + query.Execute(arg_list, 1) + + if (query.ErrorMsg()) + error("Error saving character to SQL: [query.ErrorMsg()]") + + // Get the character ID if we inserted a new character. + if (role_type == SQL_CHARACTER) + // Need to typecast due to reasons. + var/datum/category_collection/player_setup_collection/cc = collection + + if (!cc.preferences.current_character) + var/DBQuery/query = dbcon.NewQuery("SELECT id FROM ss13_characters WHERE ckey = :ckey ORDER BY id DESC LIMIT 1") + query.Execute(list(":ckey" = cc.preferences.client.ckey)) + + if (query.NextRow()) + cc.preferences.current_character = text2num(query.item[1]) + +/datum/category_group/player_setup_category/proc/gather_save_parameters() + var/list/arg_list = list() + for (var/datum/category_item/player_setup_item/PI in items) + arg_list |= PI.gather_save_parameters() + + return arg_list diff --git a/code/modules/client/preference_setup/skills/skills.dm b/code/modules/client/preference_setup/skills/skills.dm index 8960673f0e8..1cb25641828 100644 --- a/code/modules/client/preference_setup/skills/skills.dm +++ b/code/modules/client/preference_setup/skills/skills.dm @@ -15,6 +15,15 @@ /datum/category_item/player_setup_item/skills/gather_load_query() return list("ss13_characters" = list("vars" = list("skills", "skill_specialization"), "args" = list("id"))) +/datum/category_item/player_setup_item/skills/gather_load_parameters() + return list(":id" = pref.current_character) + +/datum/category_item/player_setup_item/skills/gather_save_query() + return list("ss13_characters" = list("skills", "skill_specialization", "id" = 1)) + +/datum/category_item/player_setup_item/skills/gather_save_parameters() + return list(":skills" = list2params(pref.skills), ":skill_specialization" = pref.skill_specialization, ":id" = pref.current_character) + /datum/category_item/player_setup_item/skills/sanitize_character(var/sql_load = 0) if (SKILLS == null) setup_skills() diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 6ea1f057db3..22467520d60 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -88,6 +88,9 @@ datum/preferences var/job_engsec_med = 0 var/job_engsec_low = 0 + // A text blob which temporarily houses data from the SQL. + var/unsanitized_jobs = "" + //Keeps track of preferrence for not getting any wanted jobs var/alternate_option = 0 @@ -126,22 +129,6 @@ datum/preferences var/datum/category_collection/player_setup_collection/player_setup /datum/preferences/New(client/C) - // #TODO-MERGE: Review SQL saving and migration - if (config.sql_saves && C.need_saves_migrated) - var/dat = "
The server attempted to automatically migrate your saves from the old file-system onto an SQL database.
" - dat += "This operation was " - if (!handle_saves_migration(C)) - error("Failed migrating saves from [C.ckey] to the database!") - dat += "unsuccessful!

" - dat += "There was most likely an error in one of the SQL commands used to handle this migration. Do not worry: your saves are still safe in the old system and retreivable. Please contact Skull132 immediately in order to have the issue resolved. Until such a time, you will have to play without your old characters.
" - else - dat += "successful!

" - dat += "All of your character and preference data should be now neatly housed on the SQL database. Please look over it to see if anything is wrong or otherwise messed up. If you find something like that, please contact Skull132 immediately. Your old saves are still completely safe on the old system, should anything have gone wrong!

" - dat += "You should now immediately load a character. Otherwise you risk overriding one of your characters! Once that is done, everything shuold be fine." - - C << browse(dat, "window=migrate_status") - update_migrate_status(C) - player_setup = new(src) gender = pick(MALE, FEMALE) real_name = random_name(gender,species) @@ -153,16 +140,16 @@ datum/preferences client = C if(!IsGuestKey(C.key)) load_path(C.ckey) - handle_preferences_load(C) + load_preferences() load_and_update_character() ZeroSkills(1) /datum/preferences/proc/load_and_update_character(var/slot) - handle_character_load(get_default_slot(), client) + load_character(slot) if(update_setup(loaded_preferences, loaded_character)) - handle_preferences_save(client) - handle_character_save(client) + save_preferences() + save_character() /datum/preferences/proc/getMinAge(var/age_min) if(species == "Vaurca" || species == "Machine" || species == "Diona") @@ -284,7 +271,10 @@ datum/preferences load_character() else if(href_list["load"]) if(!IsGuestKey(usr.key)) - open_load_dialog(usr) + if (config.sql_saves) + open_load_dialog_sql(usr) + else + open_load_dialog_file(usr) return 1 else if(href_list["changeslot"]) load_character(text2num(href_list["changeslot"])) diff --git a/code/modules/client/preferences_ambience.dm b/code/modules/client/preferences_ambience.dm index 6f081f8512c..7b68efa4cfd 100644 --- a/code/modules/client/preferences_ambience.dm +++ b/code/modules/client/preferences_ambience.dm @@ -18,7 +18,7 @@ set category = "Preferences" set desc = "Toggles hearing ambient sound effects" prefs.asfx_togs ^= ASFX_AMBIENCE - prefs.handle_preferences_save(src) + prefs.save_preferences() if(prefs.asfx_togs & ASFX_AMBIENCE) src << "You will now hear ambient sounds." else @@ -33,7 +33,7 @@ set desc = "Toggles hearing footstep sound effects" prefs.asfx_togs ^= ASFX_FOOTSTEPS - prefs.handle_preferences_save(src) + prefs.save_preferences() if(prefs.asfx_togs & ASFX_FOOTSTEPS) src << "You will now hear footstep sounds." else @@ -45,7 +45,7 @@ set category = "SoundFx Prefs" set desc = "Toggles hearing of the vote alarm" prefs.asfx_togs ^= ASFX_VOTE - prefs.handle_preferences_save(src) + prefs.save_preferences() if(prefs.asfx_togs & ASFX_VOTE) src << "You will now hear the vote alarm." else diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 0da4e42815c..b2d0958dbd5 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -5,7 +5,7 @@ set desc = ".Toggle Between seeing all mob speech, and only speech of nearby mobs" prefs.toggles ^= CHAT_GHOSTEARS src << "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTEARS) ? "see all speech in the world" : "only see speech from nearby mobs"]." - prefs.handle_preferences_save(src) + prefs.save_preferences() feedback_add_details("admin_verb","TGE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/verb/toggle_ghost_sight() @@ -14,7 +14,7 @@ set desc = ".Toggle Between seeing all mob emotes, and only emotes of nearby mobs" prefs.toggles ^= CHAT_GHOSTSIGHT src << "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTSIGHT) ? "see all emotes in the world" : "only see emotes from nearby mobs"]." - prefs.handle_preferences_save(src) + prefs.save_preferences() feedback_add_details("admin_verb","TGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/verb/toggle_ghost_radio() @@ -23,7 +23,7 @@ set desc = ".Toggle between hearing all radio chatter, or only from nearby speakers" prefs.toggles ^= CHAT_GHOSTRADIO src << "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTRADIO) ? "hear all radio chat in the world" : "only hear from nearby speakers"]." - prefs.handle_preferences_save(src) + prefs.save_preferences() feedback_add_details("admin_verb","TGR") /client/proc/toggle_hear_radio() @@ -32,7 +32,7 @@ set desc = "Toggle seeing radiochatter from radios and speakers" if(!holder) return prefs.toggles ^= CHAT_RADIO - prefs.handle_preferences_save(src) + prefs.save_preferences() usr << "You will [(prefs.toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers" feedback_add_details("admin_verb","THR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -42,7 +42,7 @@ set desc = "Toggle hearing a notification when admin PMs are recieved" if(!holder) return prefs.toggles ^= SOUND_ADMINHELP - prefs.handle_preferences_save(src) + prefs.save_preferences() usr << "You will [(prefs.toggles & SOUND_ADMINHELP) ? "now" : "no longer"] hear a sound when adminhelps arrive." feedback_add_details("admin_verb","AHS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -51,7 +51,7 @@ set category = "Preferences" set desc ="Toggles seeing deadchat" prefs.toggles ^= CHAT_DEAD - prefs.handle_preferences_save(src) + prefs.save_preferences() if(src.holder) src << "You will [(prefs.toggles & CHAT_DEAD) ? "now" : "no longer"] see deadchat." @@ -65,7 +65,7 @@ set category = "Preferences" set desc = "Toggles seeing prayers" prefs.toggles ^= CHAT_PRAYER - prefs.handle_preferences_save(src) + prefs.save_preferences() src << "You will [(prefs.toggles & CHAT_PRAYER) ? "now" : "no longer"] see prayerchat." feedback_add_details("admin_verb","TP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -74,7 +74,7 @@ set category = "Preferences" set desc = "Toggles hearing the GameLobby music" prefs.toggles ^= SOUND_LOBBY - prefs.handle_preferences_save(src) + prefs.save_preferences() if(prefs.toggles & SOUND_LOBBY) src << "You will now hear music in the game lobby." if(istype(mob, /mob/new_player)) @@ -90,7 +90,7 @@ set category = "Preferences" set desc = "Toggles hearing sounds uploaded by admins" prefs.toggles ^= SOUND_MIDI - prefs.handle_preferences_save(src) + prefs.save_preferences() if(prefs.toggles & SOUND_MIDI) src << "You will now hear any sounds uploaded by admins." var/sound/break_sound = sound(null, repeat = 0, wait = 0, channel = 777) @@ -105,7 +105,7 @@ set category = "Preferences" set desc = "Toggles seeing OutOfCharacter chat" prefs.toggles ^= CHAT_OOC - prefs.handle_preferences_save(src) + prefs.save_preferences() src << "You will [(prefs.toggles & CHAT_OOC) ? "now" : "no longer"] see messages on the OOC channel." feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -115,7 +115,7 @@ set category = "Preferences" set desc = "Toggles seeing Local OutOfCharacter chat" prefs.toggles ^= CHAT_LOOC - prefs.handle_preferences_save(src) + prefs.save_preferences() src << "You will [(prefs.toggles & CHAT_LOOC) ? "now" : "no longer"] see messages on the LOOC channel." feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -126,7 +126,7 @@ set category = "Preferences" set desc = "Toggles seeing chat tags/icons" prefs.toggles ^= CHAT_NOICONS - prefs.handle_preferences_save(src) + prefs.save_preferences() src << "You will [!(prefs.toggles & CHAT_NOICONS) ? "now" : "no longer"] see chat tag icons." feedback_add_details("admin_verb","TCTAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -137,7 +137,7 @@ set category = "Preferences" set desc = "Toggles hearing ambient sound effects" prefs.toggles ^= SOUND_AMBIENCE - prefs.handle_preferences_save(src) + prefs.save_preferences() if(prefs.toggles & SOUND_AMBIENCE) src << "You will now hear ambient sounds." else diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 38423925bfa..95002223f49 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -512,7 +512,7 @@ src << browse('html/changelog.html', "window=changes;size=675x650") if(prefs.lastchangelog != changelog_hash) prefs.lastchangelog = changelog_hash - prefs.handle_preferences_save(src) + prefs.save_preferences() winset(src, "rpane.changelog", "background-color=none;font-style=;") /mob/verb/observe() diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm index 53d68def64f..b6179c544ce 100644 --- a/code/modules/mob/typing_indicator.dm +++ b/code/modules/mob/typing_indicator.dm @@ -78,7 +78,7 @@ mob/var/obj/effect/decal/typing_indicator set category = "Preferences" set desc = "Toggles showing an indicator when you are typing emote or say message." prefs.toggles ^= SHOW_TYPING - prefs.handle_preferences_save(src) + prefs.save_preferences() src << "You will [(prefs.toggles & SHOW_TYPING) ? "no longer" : "now"] display a typing indicator." // Clear out any existing typing indicator.