SQL Saves - Season 2 Episode 2 - The Fixening (#1286)

Update the schema
 Modify player_preferences and character related tables
 Readd the incidents + missing CCIAA tables to it
 Fix loading
 Fix saving
 Make it impossible to edit character name after a while
 Sanity check so you cannot enter without a valid (saved) character
 Fix New Character button
 Remove debug messages

Fixes #600 
Fixes #588
This commit is contained in:
skull132
2016-12-25 00:56:36 +02:00
committed by GitHub
parent 7c8cf8408e
commit 24258106da
19 changed files with 363 additions and 147 deletions

View File

@@ -112,41 +112,43 @@ CREATE TABLE `ss13_player` (
CREATE TABLE `ss13_characters` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) NOT NULL,
`name` varchar(128) NOT NULL,
`metadata` varchar(512) DEFAULT NULL,
`random_name` tinyint(1) DEFAULT '0',
`gender` varchar(32) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`species` varchar(32) DEFAULT NULL,
`language` varchar(128) DEFAULT NULL,
`hair_colour` varchar(7) DEFAULT NULL,
`facial_colour` varchar(7) DEFAULT NULL,
`skin_tone` int(11) DEFAULT NULL,
`skin_colour` varchar(7) DEFAULT NULL,
`hair_style` varchar(32) DEFAULT NULL,
`facial_style` varchar(32) DEFAULT NULL,
`eyes_colour` varchar(7) DEFAULT NULL,
`underwear` varchar(32) DEFAULT NULL,
`undershirt` varchar(32) DEFAULT NULL,
`backbag` int(11) DEFAULT NULL,
`b_type` varchar(32) DEFAULT NULL,
`spawnpoint` varchar(32) DEFAULT NULL,
`jobs` text,
`name` varchar(128) NULL DEFAULT NULL,
`metadata` varchar(512) NULL DEFAULT NULL,
`be_special_role` text NULL DEFAULT NULL,
`gender` varchar(32) NULL DEFAULT NULL,
`age` int(11) NULL DEFAULT NULL,
`species` varchar(32) NULL DEFAULT NULL,
`language` text NULL DEFAULT NULL,
`hair_colour` varchar(7) NULL DEFAULT NULL,
`facial_colour` varchar(7) NULL DEFAULT NULL,
`skin_tone` int(11) NULL DEFAULT NULL,
`skin_colour` varchar(7) NULL DEFAULT NULL,
`hair_style` varchar(32) NULL DEFAULT NULL,
`facial_style` varchar(32) NULL DEFAULT NULL,
`eyes_colour` varchar(7) NULL DEFAULT NULL,
`underwear` varchar(32) NULL DEFAULT NULL,
`undershirt` varchar(32) NULL DEFAULT NULL,
`socks` varchar(32) NULL DEFAULT NULL,
`backbag` int(11) NULL DEFAULT NULL,
`b_type` varchar(32) NULL DEFAULT NULL,
`spawnpoint` varchar(32) NULL DEFAULT NULL,
`jobs` text NULL DEFAULT NULL,
`alternate_option` tinyint(1) DEFAULT NULL,
`alternate_titles` text,
`alternate_titles` text NULL DEFAULT NULL,
`disabilities` int(11) DEFAULT '0',
`skills` text,
`skills_specialization` text,
`home_system` text,
`citizenship` text,
`faction` text,
`religion` text,
`nt_relation` text,
`uplink_location` text,
`organs_data` text,
`organs_robotic` text,
`gear` text,
`deleted_at` datetime DEFAULT NULL,
`skills` text NULL DEFAULT NULL,
`skill_specialization` text NULL DEFAULT NULL,
`home_system` text NULL DEFAULT NULL,
`citizenship` text NULL DEFAULT NULL,
`faction` text NULL DEFAULT NULL,
`religion` text NULL DEFAULT NULL,
`nt_relation` text NULL DEFAULT NULL,
`uplink_location` text NULL DEFAULT NULL,
`organs_data` text NULL DEFAULT NULL,
`organs_robotic` text NULL DEFAULT NULL,
`gear` text NULL DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`deleted_at` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ss13_characters_ckey` (`ckey`),
KEY `ss13_characteres_name` (`name`),
@@ -155,36 +157,47 @@ CREATE TABLE `ss13_characters` (
CREATE TABLE `ss13_characters_flavour` (
`char_id` int(11) NOT NULL,
`records_employment` text,
`records_medical` text,
`records_security` text,
`records_exploit` text,
`records_ccia` text,
`flavour_general` text,
`flavour_head` text,
`flavour_face` text,
`flavour_eyes` text,
`flavour_torso` text,
`flavour_arms` text,
`flavour_hands` text,
`flavour_legs` text,
`flavour_feet` text,
`robot_default` text,
`robot_standard` text,
`robot_engineering` text,
`robot_construction` text,
`robot_medical` text,
`robot_rescue` text,
`robot_miner` text,
`robot_custodial` text,
`robot_service` text,
`robot_clerical` text,
`robot_security` text,
`robot_research` text,
`records_employment` text NULL DEFAULT NULL,
`records_medical` text NULL DEFAULT NULL,
`records_security` text NULL DEFAULT NULL,
`records_exploit` text NULL DEFAULT NULL,
`records_ccia` text NULL DEFAULT NULL,
`flavour_general` text NULL DEFAULT NULL,
`flavour_head` text NULL DEFAULT NULL,
`flavour_face` text NULL DEFAULT NULL,
`flavour_eyes` text NULL DEFAULT NULL,
`flavour_torso` text NULL DEFAULT NULL,
`flavour_arms` text NULL DEFAULT NULL,
`flavour_hands` text NULL DEFAULT NULL,
`flavour_legs` text NULL DEFAULT NULL,
`flavour_feet` text NULL DEFAULT NULL,
`robot_default` text NULL DEFAULT NULL,
`robot_standard` text NULL DEFAULT NULL,
`robot_engineering` text NULL DEFAULT NULL,
`robot_construction` text NULL DEFAULT NULL,
`robot_medical` text NULL DEFAULT NULL,
`robot_rescue` text NULL DEFAULT NULL,
`robot_miner` text NULL DEFAULT NULL,
`robot_custodial` text NULL DEFAULT NULL,
`robot_service` text NULL DEFAULT NULL,
`robot_clerical` text NULL DEFAULT NULL,
`robot_security` text NULL DEFAULT NULL,
`robot_research` text NULL DEFAULT NULL,
PRIMARY KEY (`char_id`),
CONSTRAINT `ss13_flavour_fk_char_id` FOREIGN KEY (`char_id`) REFERENCES `ss13_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
CREATE TABLE `ss13_characters_log` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`char_id` INT(11) NOT NULL,
`game_id` VARCHAR(50) NOT NULL,
`datetime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`job_name` VARCHAR(32) NOT NULL,
`special_role` VARCHAR(32) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `ss13_charlog_fk_char_id` FOREIGN KEY (`char_id`) REFERENCES `ss13_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ss13_connection_log` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) NOT NULL,
@@ -325,17 +338,17 @@ CREATE TABLE `ss13_player_linking` (
CREATE TABLE `ss13_player_preferences` (
`ckey` varchar(32) NOT NULL,
`ooccolor` text NOT NULL,
`lastchangelog` text NOT NULL,
`UI_style` text NOT NULL,
`current_character` int(11) NOT NULL,
`toggles` int(11) NOT NULL,
`UI_style_color` text NOT NULL,
`UI_style_alpha` int(11) NOT NULL,
`be_special` int(11) NOT NULL,
`asfx_togs` int(11) NOT NULL,
`lastmotd` text NOT NULL,
`lastmemo` text NOT NULL,
`ooccolor` text NULL DEFAULT NULL,
`lastchangelog` text NULL DEFAULT NULL,
`UI_style` text NULL DEFAULT NULL,
`current_character` int(11) NULL DEFAULT NULL,
`toggles` int(11) DEFAULT '0',
`UI_style_color` text NULL DEFAULT NULL,
`UI_style_alpha` int(11) NULL DEFAULT NULL,
`asfx_togs` int(11) DEFAULT '0',
`lastmotd` text NULL DEFAULT NULL,
`lastmemo` text NULL DEFAULT NULL,
`language_prefixes` text NULL DEFAULT NULL,
PRIMARY KEY (`ckey`),
CONSTRAINT `player_preferences_fk_ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
@@ -518,5 +531,45 @@ CREATE TABLE `ss13_character_incidents` (
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `UID_char_id` (`char_id`, `UID`)
COLLATE='utf8_bin' ENGINE=InnoDB;
UNIQUE INDEX `UID_char_id` (`char_id`, `UID`),
) COLLATE='utf8_bin' ENGINE=InnoDB;
CREATE TABLE `discord_channels` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`channel_group` varchar(32) NOT NULL,
`channel_id` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ss13_ccia_actions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` text COLLATE utf8_unicode_ci NOT NULL,
`type` enum('injunction','suspension','warning','other') COLLATE utf8_unicode_ci NOT NULL,
`issuedby` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`details` text COLLATE utf8_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`expires_at` date DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `ss13_ccia_action_char` (
`action_id` int(10) unsigned NOT NULL,
`char_id` int(11) NOT NULL,
PRIMARY KEY (`action_id`,`char_id`),
KEY `ccia_action_char_char_id_foreign` (`char_id`),
CONSTRAINT `ccia_action_char_action_id_foreign` FOREIGN KEY (`action_id`) REFERENCES `ss13_ccia_actions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `ccia_action_char_char_id_foreign` FOREIGN KEY (`char_id`) REFERENCES `ss13_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `ss13_ccia_general_notice_list` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`message` text COLLATE utf8_unicode_ci NOT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

View File

@@ -15,10 +15,10 @@
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))
return list("ss13_characters" = list("be_special_role", "id" = 1, "ckey" = 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)
return list(":be_special_role" = list2params(pref.be_special_role), ":id" = pref.current_character, ":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/antagonism/candidacy/sanitize_character(var/sql_load = 0)
if (sql_load)

View File

@@ -21,10 +21,10 @@ var/global/list/uplink_locations = list("PDA", "Headset", "None")
/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))
"ss13_characters" = list("uplink_location", "id" = 1, "ckey" = 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)
return list(":records_exploit" = pref.exploit_record, ":char_id" = pref.current_character, ":uplink_location" = pref.uplinklocation, ":id" = pref.current_character, ":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/antagonism/basic/sanitize_character()
pref.uplinklocation = sanitize_inlist(pref.uplinklocation, uplink_locations, initial(pref.uplinklocation))

View File

@@ -5,7 +5,6 @@
/datum/category_item/player_setup_item/general/basic/load_character(var/savefile/S)
S["real_name"] >> pref.real_name
S["name_is_always_random"] >> pref.be_random_name
S["gender"] >> pref.gender
S["age"] >> pref.age
S["spawnpoint"] >> pref.spawnpoint
@@ -13,7 +12,6 @@
/datum/category_item/player_setup_item/general/basic/save_character(var/savefile/S)
S["real_name"] << pref.real_name
S["name_is_always_random"] << pref.be_random_name
S["gender"] << pref.gender
S["age"] << pref.age
S["spawnpoint"] << pref.spawnpoint
@@ -21,7 +19,6 @@
/datum/category_item/player_setup_item/general/basic/gather_load_query()
return list("ss13_characters" = list("vars" = list("name" = "real_name",
"random_name" = "be_random_name",
"gender",
"age",
"metadata",
@@ -33,21 +30,40 @@
/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))
"id" = 1,
"ckey" = 1))
/datum/category_item/player_setup_item/general/basic/gather_save_parameters()
return 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)
":id" = pref.current_character,
":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/general/basic/load_special()
pref.can_edit_name = 1
if (config.sql_saves && pref.current_character)
if (!establish_db_connection(dbcon))
return
// Called /after/ loading and /before/ sanitization.
// So we have pref.current_character. It's just in text format.
var/DBQuery/query = dbcon.NewQuery("SELECT DATEDIFF(NOW(), created_at) AS DiffDate FROM ss13_characters WHERE id = :id")
query.Execute(list(":id" = text2num(pref.current_character)))
if (query.NextRow())
if (text2num(query.item[1]) > 5)
pref.can_edit_name = 0
else
error("SQL CHARACTER LOAD: Logic error, general/basic/load_special() didn't return any rows when it should have.")
log_debug("SQL CHARACTER LOAD: Logic error, general/basic/load_special() didn't return any rows when it should have. Character 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))
@@ -56,13 +72,15 @@
if(!pref.real_name)
pref.real_name = random_name(pref.gender, pref.species)
pref.spawnpoint = sanitize_inlist(pref.spawnpoint, spawntypes, initial(pref.spawnpoint))
pref.be_random_name = sanitize_integer(text2num(pref.be_random_name), 0, 1, initial(pref.be_random_name))
/datum/category_item/player_setup_item/general/basic/content()
. = "<b>Name:</b> "
. += "<a href='?src=\ref[src];rename=1'><b>[pref.real_name]</b></a><br>"
. += "(<a href='?src=\ref[src];random_name=1'>Random Name</A>) "
. += "(<a href='?src=\ref[src];always_random_name=1'>Always Random Name: [pref.be_random_name ? "Yes" : "No"]</a>)"
if (pref.can_edit_name)
. += "<a href='?src=\ref[src];rename=1'><b>[pref.real_name]</b></a><br>"
else
. += "<b>[pref.real_name]</b><br> (<a href='?src=\ref[src];namehelp=1'>?</a>)"
if (pref.can_edit_name)
. += "(<a href='?src=\ref[src];random_name=1'>Random Name</A>)"
. += "<br>"
. += "<b>Gender:</b> <a href='?src=\ref[src];gender=1'><b>[capitalize(lowertext(pref.gender))]</b></a><br>"
. += "<b>Age:</b> <a href='?src=\ref[src];age=1'>[pref.age]</a><br>"
@@ -72,6 +90,10 @@
/datum/category_item/player_setup_item/general/basic/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["rename"])
if (!pref.can_edit_name)
alert(user, "You can no longer edit the name of your character.<br><br>If there is a legitimate need, please contact an administrator regarding the matter.")
return TOPIC_NOACTION
var/raw_name = input(user, "Choose your character's name:", "Character Name") as text|null
if (!isnull(raw_name) && CanUseTopic(user))
var/new_name = sanitize_name(raw_name, pref.species)
@@ -82,12 +104,16 @@
user << "<span class='warning'>Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .</span>"
return TOPIC_NOACTION
else if(href_list["random_name"])
pref.real_name = random_name(pref.gender, pref.species)
return TOPIC_REFRESH
else if(href_list["namehelp"])
alert(user, "Due to game mechanics, you are no longer able to edit the name for this character. The grace period offered is 5 days since the character's initial save.<br><br>If you have a need to change the character's name, or further questions regarding this policy, please contact an administrator.")
return TOPIC_NOACTION
else if(href_list["always_random_name"])
pref.be_random_name = !pref.be_random_name
else if(href_list["random_name"])
if (!pref.can_edit_name)
alert(user, "You can no longer edit the name of your character.<br><br>If there is a legitimate need, please contact an administrator regarding the matter.")
return TOPIC_NOACTION
pref.real_name = random_name(pref.gender, pref.species)
return TOPIC_REFRESH
else if(href_list["gender"])

View File

@@ -15,12 +15,14 @@
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))
return list("ss13_characters" = list("id" = 1,
"ckey" = 1,
"language"))
/datum/category_item/player_setup_item/general/language/gather_save_parameters()
return list(":language" = list2params(pref.alternate_languages),
":id" = pref.current_character)
":id" = pref.current_character,
":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/general/language/sanitize_character(var/sql_load = 0)
if (sql_load)

View File

@@ -79,7 +79,8 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
"disabilities",
"organs_data",
"organs_robotic",
"id" = 1))
"id" = 1,
"ckey" = 1))
/datum/category_item/player_setup_item/general/body/gather_save_parameters()
return list(":species" = pref.species,
@@ -94,7 +95,8 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
":disabilities" = pref.disabilities,
":organs_data" = list2params(pref.organ_data),
":organs_robotic"= list2params(pref.rlimb_data),
":id" = pref.current_character)
":id" = pref.current_character,
":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/general/body/sanitize_character(var/sql_load = 0)
if(!pref.species || !(pref.species in playable_species))

View File

@@ -23,10 +23,10 @@
return list(":id" = pref.current_character)
/datum/category_item/player_setup_item/general/equipment/gather_save_query()
return list("ss13_characters" = list("underwear", "undershirt", "socks", "backbag", "gear", "id" = 1))
return list("ss13_characters" = list("underwear", "undershirt", "socks", "backbag", "gear", "id" = 1, "ckey" = 1))
/datum/category_item/player_setup_item/general/equipment/gather_save_parameters()
return list(":underwear" = pref.underwear, ":undershirt" = pref.undershirt, ":socks" = pref.socks, ":backbag" = pref.backbag, ":gear" = list2params(pref.gear), ":id" = pref.current_character)
return list(":underwear" = pref.underwear, ":undershirt" = pref.undershirt, ":socks" = pref.socks, ":backbag" = pref.backbag, ":gear" = list2params(pref.gear), ":id" = pref.current_character, ":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/general/equipment/sanitize_character(var/sql_load = 0)
if (sql_load)
@@ -89,7 +89,7 @@
/datum/category_item/player_setup_item/general/equipment/proc/get_undies()
return pref.gender == MALE ? underwear_m : underwear_f
/datum/category_item/player_setup_item/general/equipment/proc/get_gender_socks()
return pref.gender == MALE ? socks_m : socks_f

View File

@@ -38,7 +38,7 @@
"records_medical",
"records_security",
"char_id" = 1),
"ss13_characters" = list("home_system", "citizenship", "faction", "religion", "id" = 1))
"ss13_characters" = list("home_system", "citizenship", "faction", "religion", "id" = 1, "ckey" = 1))
/datum/category_item/player_setup_item/general/background/gather_save_parameters()
return list(":records_employment" = pref.gen_record,
@@ -49,7 +49,8 @@
":citizenship" = pref.citizenship,
":faction" = pref.faction,
":religion" = pref.religion,
":id" = pref.current_character)
":id" = pref.current_character,
":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/general/background/sanitize_character()
if(!pref.home_system)

View File

@@ -73,6 +73,7 @@
/datum/category_item/player_setup_item/general/flavor/gather_save_parameters()
var/list/var_list = list(":char_id" = pref.current_character,
":flavour_general" = pref.flavor_texts["general"],
":flavour_head" = pref.flavor_texts["head"],
":flavour_face" = pref.flavor_texts["face"],
":flavour_eyes" = pref.flavor_texts["eyes"],

View File

@@ -19,13 +19,13 @@
S["memo_hash"] << pref.memo_hash
/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_togs" = "asfx_toggles", "motd_hash", "memo_hash"), "args" = list("ckey")))
return list("ss13_player_preferences" = list("vars" = list("lastchangelog", "current_character", "toggles", "asfx_togs", "lastmotd" = "motd_hash", "lastmemo" = "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_togs", "motd_hash", "memo_hash", "ckey" = 1))
return list("ss13_player_preferences" = list("lastchangelog", "current_character", "toggles", "asfx_togs", "lastmotd", "lastmemo", "ckey" = 1))
/datum/category_item/player_setup_item/player_global/settings/gather_save_parameters()
return list(":ckey" = pref.client.ckey,
@@ -33,8 +33,8 @@
":current_character" = pref.current_character,
":toggles" = pref.toggles,
":asfx_togs" = pref.asfx_togs,
":motd_hash" = pref.motd_hash,
":memo_hash" = pref.memo_hash)
":lastmotd" = pref.motd_hash,
":lastmemo" = pref.memo_hash)
/datum/category_item/player_setup_item/player_global/settings/sanitize_preferences(var/sql_load = 0)
if (sql_load)

View File

@@ -40,7 +40,7 @@
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))
return list("ss13_characters" = list("jobs", "alternate_option", "alternate_titles", "id" = 1, "ckey" = 1))
/datum/category_item/player_setup_item/occupation/gather_save_parameters()
var/list/compiled_jobs = list("job_civilian_high" = pref.job_civilian_high,
@@ -53,11 +53,12 @@
"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)
return list(":jobs" = list2params(compiled_jobs), ":alternate_option" = pref.alternate_option, ":alternate_titles" = list2params(pref.player_alt_titles), ":id" = pref.current_character, ":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/occupation/sanitize_character(var/sql_load = 0)
if (sql_load)
pref.alternate_option = text2num(pref.alternate_option)
pref.player_alt_titles = params2list(pref.player_alt_titles)
var/list/jobs = params2list(pref.unsanitized_jobs)

View File

@@ -62,8 +62,8 @@
pref.incidents.Add(infraction)
log_debug("Added infraction with [infraction.UID]")
/datum/category_item/player_setup_item/other/incidents/content(var/mob/user)
pref.incidents = list()
. += "<b>Incident Information</b><br>"
. += "The following incidents are on file for your character<br>"
for (var/datum/char_infraction/I in pref.incidents)

View File

@@ -157,7 +157,8 @@
for (var/datum/category_item/player_setup_item/PI in items)
PI.load_preferences(S)
else
handle_sql_loading(SQL_PREFERENCES)
if (sql_role == SQL_PREFERENCES)
handle_sql_loading(SQL_PREFERENCES)
for (var/datum/category_item/player_setup_item/PI in items)
PI.sanitize_preferences(config.sql_saves)
@@ -170,7 +171,8 @@
for (var/datum/category_item/player_setup_item/PI in items)
PI.save_preferences(S)
else
handle_sql_saving(SQL_PREFERENCES)
if (sql_role == SQL_PREFERENCES)
handle_sql_saving(SQL_PREFERENCES)
/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)

View File

@@ -84,7 +84,11 @@
var/DBQuery/query = dbcon.NewQuery(query_text)
query.Execute(arg_list, 1)
if (query.ErrorMsg())
error("Error loading character from SQL: [query.ErrorMsg()]")
error("SQL CHARACTER LOAD: SQL query error: [query.ErrorMsg()]")
log_debug("SQL CHARACTER LOAD: SQL query error: [query.ErrorMsg()]")
log_debug("SQL CHARACTER LOAD: query args: [json_encode(arg_list)]")
continue
// Each query should only return exactly 1 row.
var/list/var_names = query_cache[type][query_text]
@@ -97,9 +101,9 @@
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]")
error("SQL CHARACTER LOAD: bad variable name: [e.name]")
log_debug("SQL CHARACTER LOAD: bad variable name: [e.name]")
log_debug("SQL CHARACTER LOAD: var name: [var_names[i]]")
/datum/category_group/player_setup_category/proc/gather_load_parameters()
var/list/arg_list = list()
@@ -162,11 +166,15 @@
for (var/variable in var_names)
if (isnull(var_names[variable]))
query += " [variable] = [arg_names[i]]"
if (i < var_names.len)
query += ", "
if (i < var_names.len)
query += ","
i++
// Remove any potentially damaging commas from the end.
query = replacetext(query, ",", "", length(query) - 1)
// Save it.
query_cache[type] += query
@@ -180,7 +188,11 @@
query.Execute(arg_list, 1)
if (query.ErrorMsg())
error("Error saving character to SQL: [query.ErrorMsg()]")
error("SQL CHARACTER SAVE: SQL query error: [query.ErrorMsg()]")
log_debug("SQL CHARACTER SAVE: SQL query error: [query.ErrorMsg()]")
log_debug("SQL CHARACTER SAVE: query args: [json_encode(arg_list)]")
continue
if (role_type == SQL_CHARACTER && !cc.preferences.current_character)
// No current character, means we're doing insert queries.
@@ -193,9 +205,11 @@
arg_list[":char_id"] = text2num(query.item[1])
cc.preferences.current_character = text2num(query.item[1])
else
error("Error inserting character to SQL: New ID was not recovered.")
error("SQL CHARACTER SAVE: New ID was not recovered.")
log_debug("SQL CHARACTER SAVE: New ID was not recovered.")
if (query.ErrorMsg())
error("Error retreiving new character ID: [query.ErrorMsg()]")
error("SQL CHARACTER SAVE: SQL query error from last_insert_id: [query.ErrorMsg()]")
log_debug("SQL CHARACTER SAVE: SQL query error from last_insert_id: [query.ErrorMsg()]")
/datum/category_group/player_setup_category/proc/gather_save_parameters()
var/list/arg_list = list()

View File

@@ -19,10 +19,10 @@
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))
return list("ss13_characters" = list("skills", "skill_specialization", "id" = 1, "ckey" = 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)
return list(":skills" = list2params(pref.skills), ":skill_specialization" = pref.skill_specialization, ":id" = pref.current_character, ":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/skills/sanitize_character(var/sql_load = 0)
if (SKILLS == null)
@@ -31,6 +31,11 @@
pref.skills = list()
if (sql_load)
pref.skills = params2list(pref.skills)
for (var/skill in pref.skills)
pref.skills[skill] = text2num(pref.skills[skill])
pref.CalculateSkillPoints()
if (!pref.skills.len)
pref.ZeroSkills()
if (pref.used_skillpoints < 0)

View File

@@ -31,7 +31,7 @@ datum/preferences
//character preferences
var/real_name //our character's name
var/be_random_name = 0 //whether we are a random name every round
var/can_edit_name = 1 //Whether or not a character's name can be edited. Used with SQL saving.
var/gender = MALE //gender of character (well duh)
var/age = 30 //age of character
var/spawnpoint = "Arrivals Shuttle" //where this character will spawn (0-2).
@@ -131,12 +131,7 @@ datum/preferences
var/datum/category_collection/player_setup_collection/player_setup
/datum/preferences/New(client/C)
player_setup = new(src)
gender = pick(MALE, FEMALE)
real_name = random_name(gender,species)
b_type = pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
gear = list()
new_setup()
if(istype(C))
client = C
@@ -145,16 +140,6 @@ datum/preferences
load_preferences()
load_and_update_character()
//Reset the records when making a new char
med_record = ""
sec_record = ""
incidents = list()
gen_record = ""
exploit_record = ""
ccia_record = ""
ZeroSkills(1)
/datum/preferences/proc/load_and_update_character(var/slot)
load_character(slot)
if(update_setup(loaded_preferences, loaded_character))
@@ -289,6 +274,12 @@ datum/preferences
else if(href_list["changeslot"])
load_character(text2num(href_list["changeslot"]))
close_load_dialog(usr)
else if(href_list["new_character_sql"])
new_setup(1)
usr << "<span class='notice'>Your setup has been refreshed.</span>"
close_load_dialog(usr)
else if(href_list["close_load_dialog"])
close_load_dialog(usr)
else
return 0
@@ -298,8 +289,6 @@ datum/preferences
/datum/preferences/proc/copy_to(mob/living/carbon/human/character, safety = 0)
// Sanitizing rather than saving as someone might still be editing when copy_to occurs.
player_setup.sanitize_setup()
if(be_random_name)
real_name = random_name(gender,species)
if(config.humans_need_surnames)
var/firstspace = findtext(real_name, " ")
@@ -440,12 +429,12 @@ datum/preferences
dat += "<hr>"
dat += "<b>[query.RowCount()]/[config.character_slots] slots used</b><br>"
if (query.RowCount() < config.character_slots)
dat += "<a href='byond://?src=\ref[src];preference=new_character_sql;'>New Character</a>"
dat += "<a href='?src=\ref[src];new_character_sql=1'>New Character</a>"
else
dat += "<strike>New Character</strike>"
dat += "<hr>"
dat += "<a href='byond://?src=\ref[user];preference=close_load_dialog'>Close</a><br>"
dat += "<a href='?src=\ref[src];close_load_dialog=1'>Close</a><br>"
dat += "</center></tt>"
user << browse(dat, "window=saves;size=300x390")
@@ -472,3 +461,92 @@ datum/preferences
/datum/preferences/proc/close_load_dialog(mob/user)
user << browse(null, "window=saves")
// Logs a character to the database. For statistics.
/datum/preferences/proc/log_character(var/mob/living/carbon/human/H)
if (!config.sql_saves || !config.sql_stats || !establish_db_connection(dbcon) || !H)
return
var/DBQuery/query = dbcon.NewQuery("INSERT INTO ss13_characters_log (char_id, game_id, datetime, job_name, special_role) VALUES (:char_id, :game_id, NOW(), :job, :special_role)")
query.Execute(list(":char_id" = current_character, ":game_id" = game_id, ":job" = H.mind.assigned_role, ":special_role" = H.mind.special_role))
// Turned into a proc so we could reuse it for SQL shenanigans.
/datum/preferences/proc/new_setup(var/re_initialize = 0)
if (player_setup)
qdel(player_setup)
player_setup = null
player_setup = new(src)
gender = pick(MALE, FEMALE)
real_name = random_name(gender,species)
b_type = pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
current_character = 0
can_edit_name = 1
gear = list()
//Reset the records when making a new char
med_record = ""
sec_record = ""
incidents = list()
gen_record = ""
exploit_record = ""
ccia_record = ""
ZeroSkills(1)
// Do we need to reinitialize a whole bunch more vars?
if (re_initialize)
be_special_role = list()
uplinklocation = initial(uplinklocation)
r_hair = 0
g_hair = 0
b_hair = 0
r_facial = 0
g_facial = 0
b_facial = 0
r_skin = 0
g_skin = 0
b_skin = 0
r_eyes = 0
g_eyes = 0
b_eyes = 0
species = "Human"
home_system = "Unset"
citizenship = "None"
faction = "None"
religion = "None"
species = "Human"
job_civilian_high = 0
job_civilian_med = 0
job_civilian_low = 0
job_medsci_high = 0
job_medsci_med = 0
job_medsci_low = 0
job_engsec_high = 0
job_engsec_med = 0
job_engsec_low = 0
alternate_option = 0
metadata = ""
organ_data = list()
rlimb_data = list()
player_alt_titles = new()
flavor_texts = list()
flavour_texts_robot = list()
ccia_actions = list()
disabilities = 0
nanotrasen_relation = "Neutral"
update_preview_icon()

View File

@@ -65,12 +65,15 @@
S["default_slot"] << slot
S.cd = "/character[slot]"
else
current_character = slot
if (slot)
current_character = slot
player_setup.load_character(S)
if (!config.sql_saves)
loaded_character = S
else
save_preferences()
return 1

View File

@@ -95,6 +95,11 @@
if(href_list["ready"])
if(!ticker || ticker.current_state <= GAME_STATE_PREGAME) // Make sure we don't ready up after the round has started
// Cannot join without a saved character, if we're on SQL saves.
if (config.sql_saves && !client.prefs.current_character)
alert(src, "You have not saved your character yet. Please do so before readying up.")
return
ready = text2num(href_list["ready"])
else
ready = 0
@@ -127,8 +132,6 @@
observer.icon = client.prefs.preview_icon
observer.alpha = 127
if(client.prefs.be_random_name)
client.prefs.real_name = random_name(client.prefs.gender)
observer.real_name = client.prefs.real_name
observer.name = observer.real_name
if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed.
@@ -145,6 +148,11 @@
usr << "\red The round is either not ready, or has already finished..."
return
// Cannot join without a saved character, if we're on SQL saves.
if (config.sql_saves && !client.prefs.current_character)
alert(src, "You have not saved your character yet. Please do so before attempting to join.")
return
if(!check_rights(R_ADMIN, 0))
var/datum/species/S = all_species[client.prefs.species]
if((S.spawn_flags & IS_WHITELISTED) && !is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist)
@@ -298,6 +306,9 @@
if(!config.enter_allowed)
usr << "<span class='notice'>There is an administrative lock on entering the game!</span>"
return 0
if(config.sql_saves && !client.prefs.current_character)
alert(src, "You have not saved your character yet. Please do so before attempting to join.")
return 0
if(!IsJobAvailable(rank))
src << alert("[rank] is not available. Please try another.")
return 0
@@ -361,7 +372,7 @@
global_announcer.autosay("A new[rank ? " [rank]" : " visitor" ] [join_message ? join_message : "has arrived on the station"].", "Arrivals Announcement Computer")
proc/LateChoices()
var/name = client.prefs.be_random_name ? "friend" : client.prefs.real_name
var/name = client.prefs.real_name
var/dat = "<html><body><center>"
dat += "<b>Welcome, [name].<br></b>"
@@ -455,6 +466,8 @@
new_character.update_eyes()
new_character.regenerate_icons()
client.prefs.log_character(new_character)
new_character.key = key //Manually transfer the key to log them in
return new_character

View File

@@ -0,0 +1,15 @@
author: Skull132
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
# Any changes you've made. See valid prefix list above.
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
# SCREW THIS UP AND IT WON'T WORK.
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
changes:
- tweak: "Rewrote the entire SQL saving and loading system. On top of the backend changes, it is important to note that a character's name cannot be edited after 5 days since the creation of the character."
- rscdel: "Removed the ability to play a character with a random name."
- bugfix: "Fixed old character data not being wiped if you press the New Character button."
- bugfix: "Fixed the skill level not being recalculated properly upon loading a character from SQL."