Updates Loadout Backend (#18095)

* Updates Loadout Backend

Updates the loadout backend to store gear data in a separate table.

This might enable us to increase the number of loadout points available

* Bump MariaDB Version to 10.11

* Update editorconfig for SQL

* Fix update_character_gear

* tgui_alert and TOPIC_NOACTION

* Fix missing sur

* Actually, its user

* Adds a CHECK_TICK, just in case

---------

Co-authored-by: Werner <Arrow768@users.noreply.github.com>
This commit is contained in:
Werner
2024-01-02 21:54:49 +00:00
committed by GitHub
co-authored by Werner
parent 66f92c62ef
commit bff569a6da
12 changed files with 310 additions and 34 deletions
+3
View File
@@ -15,6 +15,9 @@ indent_size = 2
[*.py]
indent_style = space
[*.sql]
indent_style = space
[*.md]
trim_trailing_whitespace = false
+4 -4
View File
@@ -317,7 +317,7 @@ jobs:
services:
mariadb:
image: mariadb:10.4
image: mariadb:10.11
ports:
- 3306
env:
@@ -440,7 +440,7 @@ jobs:
services:
mariadb:
image: mariadb:10.4
image: mariadb:10.11
ports:
- 3306
env:
@@ -565,7 +565,7 @@ jobs:
services:
mariadb:
image: mariadb:10.4
image: mariadb:10.11
ports:
- 3306
env:
@@ -689,7 +689,7 @@ jobs:
services:
mariadb:
image: mariadb:10.4
image: mariadb:10.11
ports:
- 3306
env:
+179
View File
@@ -0,0 +1,179 @@
--
-- Overhauls the way the loadout is stored
--
-- --------------------------------------------------------
-- Server version: 10.11.5
-- Server OS: debian-linux-gnu
-- HeidiSQL Version: 12.3.0.6589
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-- Dumping structure for procedure convert_characters_gear
DELIMITER //
CREATE PROCEDURE `convert_characters_gear`(
IN `parameter_char_id` INT
)
MODIFIES SQL DATA
SQL SECURITY INVOKER
COMMENT 'Converts the character gear from the V1 (only one gear set) and V2 (multiple gear sets) storage formats to the ss13_characters_gear table'
BEGIN
DECLARE info_gear_version VARCHAR(50) DEFAULT NULL;
DECLARE value_fetch_geardata MEDIUMTEXT DEFAULT NULL;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
SELECT gear INTO value_fetch_geardata FROM ss13_characters WHERE id = parameter_char_id;
IF JSON_VALID(value_fetch_geardata) THEN
SET info_gear_version = "valid json";
IF JSON_CONTAINS_PATH(value_fetch_geardata, 'one', '$.1','$.2','$.3') THEN
SET info_gear_version = "version 2";
START TRANSACTION;
DELETE FROM ss13_characters_gear WHERE char_id = parameter_char_id;
IF JSON_CONTAINS_PATH(value_fetch_geardata, 'one', '$.1') THEN
CALL `convert_characters_gear_step2`(parameter_char_id, 1, JSON_EXTRACT(value_fetch_geardata, '$.1'));
END IF;
IF JSON_CONTAINS_PATH(value_fetch_geardata, 'one', '$.2') THEN
CALL `convert_characters_gear_step2`(parameter_char_id, 2, JSON_EXTRACT(value_fetch_geardata, '$.2'));
END IF;
IF JSON_CONTAINS_PATH(value_fetch_geardata, 'one', '$.3') THEN
CALL `convert_characters_gear_step2`(parameter_char_id, 3, JSON_EXTRACT(value_fetch_geardata, '$.3'));
END IF;
COMMIT;
ELSE
SET info_gear_version = "version 1";
START TRANSACTION;
DELETE FROM ss13_characters_gear WHERE char_id = parameter_char_id;
CALL `convert_characters_gear_step2`(parameter_char_id, 1, value_fetch_geardata);
COMMIT;
END IF;
ELSE
SET info_gear_version = "invalid";
END IF;
END//
DELIMITER ;
-- Dumping structure for procedure convert_characters_gear_all
DELIMITER //
CREATE PROCEDURE `convert_characters_gear_all`()
MODIFIES SQL DATA
SQL SECURITY INVOKER
COMMENT 'Runs convert_characters_gear for all characters'
BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE _id BIGINT UNSIGNED;
DECLARE cur CURSOR FOR SELECT id FROM ss13_characters;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE;
OPEN cur;
charLoop: LOOP
FETCH cur INTO _id;
IF done THEN
LEAVE charLoop;
END IF;
CALL convert_characters_gear(_id);
END LOOP charLoop;
CLOSE cur;
END//
DELIMITER ;
-- Dumping structure for procedure convert_characters_gear_step2
DELIMITER //
CREATE PROCEDURE `convert_characters_gear_step2`(
IN `parameter_char_id` INT,
IN `parameter_gear_slot_id` INT,
IN `parameter_gear_string` MEDIUMTEXT
)
MODIFIES SQL DATA
SQL SECURITY INVOKER
COMMENT 'Converts a characters gear to the new (v3) storage system. Requires a char id, the current gear_slot and and the gear string to convert'
BEGIN
INSERT INTO ss13_characters_gear (char_id, slot, NAME, tweaks)
SELECT
parameter_char_id AS char_id,
parameter_gear_slot_id AS gear_slot,
JSON_UNQUOTE(JSON_EXTRACT(JSON_KEYS(parameter_gear_string), CONCAT('$[', geardata.rowid-1, ']'))) AS gearname,
IF(STRCMP(geardata.geartweaks,'null')=0,NULL,geardata.geartweaks)
FROM JSON_TABLE(parameter_gear_string, '$.*' COLUMNS (
rowid FOR ORDINALITY,
geartweaks JSON PATH '$'
)) geardata;
END//
DELIMITER ;
-- Dumping structure for table ss13_characters_gear
CREATE TABLE IF NOT EXISTS `ss13_characters_gear` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`char_id` int(11) NOT NULL,
`slot` tinyint(4) NOT NULL,
`name` varchar(255) NOT NULL,
`tweaks` mediumtext DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `char_id_gear_slot` (`char_id`,`slot`) USING BTREE,
CONSTRAINT `fk_ss13_characters_char_id` FOREIGN KEY (`char_id`) REFERENCES `ss13_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `json_valid_tweaks` CHECK (json_valid(`tweaks`))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Data exporting was unselected.
-- Dumping structure for procedure update_character_gear
DELIMITER //
CREATE PROCEDURE `update_character_gear`(
IN `parameter_char_id` INT,
IN `parameter_gear_slot_id` INT,
IN `parameter_gear_string` MEDIUMTEXT
)
MODIFIES SQL DATA
SQL SECURITY INVOKER
COMMENT 'Updates a specific gear slot of a character to the supplied gear string'
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
START TRANSACTION;
DELETE FROM ss13_characters_gear WHERE char_id = parameter_char_id AND slot = parameter_gear_slot_id;
INSERT INTO ss13_characters_gear (char_id, slot, NAME, tweaks)
SELECT
parameter_char_id AS char_id,
parameter_gear_slot_id AS gear_slot,
JSON_UNQUOTE(JSON_EXTRACT(JSON_KEYS(parameter_gear_string), CONCAT('$[', geardata.rowid-1, ']'))) AS gearname,
IF(STRCMP(geardata.geartweaks,'null')=0,NULL,geardata.geartweaks)
FROM JSON_TABLE(parameter_gear_string, '$.*' COLUMNS (
rowid FOR ORDINALITY,
geartweaks JSON PATH '$'
)) geardata;
COMMIT;
END//
DELIMITER ;
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;
-2
View File
@@ -39,8 +39,6 @@
// Some arbitrary defines to be used by self-pruning global lists. (see master_controller)
#define PROCESS_KILL 26 // Used to trigger removal from a processing list.
#define MAX_GEAR_COST 15 // Used in chargen for accessory loadout limit.
// Preference toggles.
#define SOUND_ADMINHELP 0x1
#define SOUND_MIDI 0x2
+4
View File
@@ -225,6 +225,7 @@ GLOBAL_LIST_EMPTY(gamemode_cache)
var/character_slots = 10 // The number of available character slots
var/loadout_slots = 3 // The number of loadout slots per character
var/loadout_cost = 15 // The maximum cost of the loadout per slot
var/max_maint_drones = 5 //This many drones can spawn,
var/allow_drone_spawn = 1 //assuming the admin allow them to.
@@ -843,6 +844,9 @@ GENERAL_PROTECT_DATUM(/datum/configuration)
if("loadout_slots")
GLOB.config.loadout_slots = text2num(value)
if("loadout_cost")
GLOB.config.loadout_cost = text2num(value)
if("allow_drone_spawn")
GLOB.config.allow_drone_spawn = text2num(value)
@@ -115,7 +115,7 @@
"ckey" = PREF_CLIENT_CKEY
)
/datum/category_item/player_setup_item/general/basic/load_special()
/datum/category_item/player_setup_item/general/basic/load_character_special()
pref.can_edit_name = TRUE
pref.can_edit_ipc_tag = TRUE
@@ -134,7 +134,7 @@
if(GLOB.config.ipc_timelock_active)
pref.can_edit_ipc_tag = FALSE
else
log_world("ERROR: SQL CHARACTER LOAD: Logic error, general/basic/load_special() didn't return any rows when it should have. Character ID: [pref.current_character].")
log_world("ERROR: SQL CHARACTER LOAD: Logic error, general/basic/load_character_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()
if(!pref.species)
@@ -48,7 +48,7 @@
"ckey" = PREF_CLIENT_CKEY
)
/datum/category_item/player_setup_item/general/psionics/load_special(savefile/S)
/datum/category_item/player_setup_item/general/psionics/load_character_special(savefile/S)
if(!pref.psionics)
pref.psionics = "{}"
@@ -41,27 +41,58 @@ var/list/gear_datums = list()
/datum/category_item/player_setup_item/loadout/load_character(var/savefile/S)
S["gear"] >> pref.gear
S["gear_list"] >> pref.gear_list
if(pref.gear_list!=null && pref.gear_slot!=null)
pref.gear = pref.gear_list["[pref.gear_slot]"]
else
S["gear"] >> pref.gear
/datum/category_item/player_setup_item/loadout/load_character_special(var/savefile/S)
pref.gear_modified = FALSE
//Query the ss13_characters_gear table
if(GLOB.config.sql_saves && establish_db_connection(GLOB.dbcon))
var/fetched_gear = list()
var/DBQuery/character_gear_query = GLOB.dbcon.NewQuery("SELECT slot, name, tweaks FROM ss13_characters_gear WHERE char_id = :char_id:")
character_gear_query.Execute(list("char_id"=pref.current_character))
while(character_gear_query.NextRow())
CHECK_TICK
var/slot = character_gear_query.item[1]
var/name = character_gear_query.item[2]
if(!fetched_gear[slot])
fetched_gear[slot] = list()
var/tweaks = list()
if(character_gear_query.item[3])
tweaks = json_decode(character_gear_query.item[3])
fetched_gear[slot][name]=tweaks
pref.gear_list = fetched_gear
return
/datum/category_item/player_setup_item/loadout/save_character_special(var/savefile/S)
//Persist the loadout to the database
if(GLOB.config.sql_saves && establish_db_connection(GLOB.dbcon) && pref.gear_modified)
var/gear_string = json_encode(pref.gear_list["[pref.gear_slot]"])
var/DBQuery/character_gear_save_query = GLOB.dbcon.NewQuery("CALL `update_character_gear`(:char_id:, :slot_id:, :gear_data:)")
character_gear_save_query.Execute(list("char_id"=pref.current_character,"slot_id"=pref.gear_slot,"gear_data"=gear_string))
pref.gear_modified = FALSE
return
/datum/category_item/player_setup_item/loadout/save_character(var/savefile/S)
pref.gear_list["[pref.gear_slot]"] = pref.gear
to_file(S["gear_list"], pref.gear_list)
to_file(S["gear_slot"], pref.gear_slot)
/datum/category_item/player_setup_item/loadout/gather_load_query()
return list("ss13_characters" = list("vars" = list("gear_slot"), "args" = list("id")))
/datum/category_item/player_setup_item/loadout/gather_load_parameters()
return list("id" = pref.current_character)
/datum/category_item/player_setup_item/loadout/gather_load_query()
return list("ss13_characters" = list("vars" = list("gear" = "gear_list", "gear_slot"), "args" = list("id")))
/datum/category_item/player_setup_item/loadout/gather_save_query()
return list("ss13_characters" = list("gear", "gear_slot", "id" = 1, "ckey" = 1))
return list("ss13_characters" = list("gear_slot", "id" = 1, "ckey" = 1))
/datum/category_item/player_setup_item/loadout/gather_save_parameters()
return list("gear" = json_encode(pref.gear_list), "gear_slot" = pref.gear_slot, "id" = pref.current_character, "ckey" = PREF_CLIENT_CKEY)
return list("gear_slot" = pref.gear_slot, "id" = pref.current_character, "ckey" = PREF_CLIENT_CKEY)
/datum/category_item/player_setup_item/loadout/proc/valid_gear_choices(var/max_cost)
. = list()
@@ -131,7 +162,7 @@ var/list/gear_datums = list()
pref.gear -= gear_name
else
var/datum/gear/G = gear_datums[gear_name]
if(total_cost + G.cost > MAX_GEAR_COST)
if(total_cost + G.cost > GLOB.config.loadout_cost)
pref.gear -= gear_name
to_chat(preference_mob, "<span class='warning'>You cannot afford to take \the [gear_name]</span>")
else
@@ -146,13 +177,13 @@ var/list/gear_datums = list()
total_cost += G.cost
var/fcolor = "#3366CC"
if(total_cost < MAX_GEAR_COST)
if(total_cost < GLOB.config.loadout_cost)
fcolor = "#E67300"
. = list()
. += "<table align = 'center' width = 100%>"
if (gear_reset)
. += "<tr><td colspan=3><center><i>Your loadout failed to load and will be reset if you save this slot.</i></center></td></tr>"
. += "<tr><td colspan=3><center><a href='?src=\ref[src];prev_slot=1'>\<\<</a><b><font color = '[fcolor]'>\[[pref.gear_slot]\]</font> </b><a href='?src=\ref[src];next_slot=1'>\>\></a><b><font color = '[fcolor]'>[total_cost]/[MAX_GEAR_COST]</font> loadout points spent.</b> \[<a href='?src=\ref[src];clear_loadout=1'>Clear Loadout</a>\]</center></td></tr>"
. += "<tr><td colspan=3><center><a href='?src=\ref[src];prev_slot=1'>\<\<</a><b><font color = '[fcolor]'>\[[pref.gear_slot]\]</font> </b><a href='?src=\ref[src];next_slot=1'>\>\></a><b><font color = '[fcolor]'>[total_cost]/[GLOB.config.loadout_cost]</font> loadout points spent.</b> \[<a href='?src=\ref[src];clear_loadout=1'>Clear Loadout</a>\]</center></td></tr>"
. += "<tr><td colspan=3><center><b>"
var/firstcat = 1
@@ -307,6 +338,7 @@ var/list/gear_datums = list()
/datum/category_item/player_setup_item/loadout/OnTopic(href, href_list, user)
if(href_list["toggle_gear"])
pref.gear_modified = TRUE
var/datum/gear/TG = gear_datums[href_list["toggle_gear"]]
if(TG.display_name in pref.gear)
pref.gear -= TG.display_name
@@ -315,10 +347,11 @@ var/list/gear_datums = list()
for(var/gear_name in pref.gear)
var/datum/gear/G = gear_datums[gear_name]
if(istype(G)) total_cost += G.cost
if((total_cost+TG.cost) <= MAX_GEAR_COST)
if((total_cost+TG.cost) <= GLOB.config.loadout_cost)
pref.gear += TG.display_name
return TOPIC_REFRESH_UPDATE_PREVIEW
if(href_list["gear"] && href_list["tweak"])
pref.gear_modified = TRUE
var/datum/gear/gear = gear_datums[href_list["gear"]]
var/datum/gear_tweak/tweak = locate(href_list["tweak"])
if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks))
@@ -330,6 +363,9 @@ var/list/gear_datums = list()
return TOPIC_REFRESH_UPDATE_PREVIEW
if(href_list["next_slot"] || href_list["prev_slot"])
if(pref.gear_modified)
tgui_alert(user, "Gear has been Modified - Save First or Reload", "Gear Modified", list("OK"))
return TOPIC_NOACTION
//Set the current slot in the gear list to the currently selected gear
pref.gear_list["[pref.gear_slot]"] = pref.gear
@@ -357,12 +393,16 @@ var/list/gear_datums = list()
else if(href_list["select_category"])
current_tab = href_list["select_category"]
return TOPIC_REFRESH
else if(href_list["clear_loadout"])
pref.gear_modified = TRUE
pref.gear.Cut()
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["search_input_refresh"] != null) // empty str is false
search_input_value = sanitize(href_list["search_input_refresh"], 100)
return TOPIC_REFRESH_UPDATE_PREVIEW
return ..()
/datum/gear
@@ -2,7 +2,7 @@
name = "Incidents"
sort_order = 7
/datum/category_item/player_setup_item/other/incidents/load_special(var/savefile/S)
/datum/category_item/player_setup_item/other/incidents/load_character_special(var/savefile/S)
pref.incidents = list()
pref.ccia_actions = list()
@@ -153,7 +153,7 @@
handle_sql_loading(SQL_CHARACTER)
for(var/datum/category_item/player_setup_item/PI in items)
PI.load_special(S)
PI.load_character_special(S)
PI.sanitize_character(GLOB.config.sql_saves)
/datum/category_group/player_setup_category/proc/save_character(var/savefile/S)
@@ -161,10 +161,14 @@
for (var/datum/category_item/player_setup_item/PI in items)
PI.sanitize_character()
if (!GLOB.config.sql_saves || !establish_db_connection(GLOB.dbcon))
for (var/datum/category_item/player_setup_item/PI in items)
var/db_available = GLOB.config.sql_saves && establish_db_connection(GLOB.dbcon)
for (var/datum/category_item/player_setup_item/PI in items)
PI.save_character_special(S)
if(!db_available)
PI.save_character(S)
else if (modified)
if (db_available && modified)
// No save here, because this is only called from the menu and needs to save /everything/.
handle_sql_saving(SQL_CHARACTER)
modified = 0
@@ -238,17 +242,23 @@
return
/*
* Called no matter if sql safes are enabled or disabled
* Called no matter if sql saves are enabled or disabled (After load_character)
*/
/datum/category_item/player_setup_item/proc/load_special(var/savefile/S)
/datum/category_item/player_setup_item/proc/load_character_special(var/savefile/S)
return
/*
* Called when the item is asked to save per character settings
* Called when the item is asked to save per character settings - Only called when sql saves are disabled or unavailable
*/
/datum/category_item/player_setup_item/proc/save_character(var/savefile/S)
return
/*
* Called no matter if sql saves are enabled or disabled (Before save_character / handle_sql_saving)
*/
/datum/category_item/player_setup_item/proc/save_character_special(var/savefile/S)
return
/*
* Called when the item is asked to load user/global settings
*/
+5 -4
View File
@@ -85,9 +85,10 @@ var/list/preferences_datums = list()
var/list/alternate_languages = list() //Secondary language(s)
var/list/language_prefixes = list() // Language prefix keys
var/autohiss_setting = AUTOHISS_OFF
var/list/gear // Custom/fluff item loadout.
var/list/gear_list = list() //Custom/fluff item loadouts.
var/list/gear // The gear in the currently selected loadout item preset
var/list/gear_list = list() // The gear list holds all the different loadout item prests
var/gear_slot = 1 //The current gear save slot
var/gear_modified = FALSE
// IPC Stuff
var/machine_tag_status = TRUE
@@ -605,6 +606,8 @@ var/list/preferences_datums = list()
can_edit_name = 1
gear = list()
gear_list = list() //Dont copy the loadout
gear_modified = FALSE
//Reset the records when making a new char
med_record = ""
@@ -614,8 +617,6 @@ var/list/preferences_datums = list()
exploit_record = ""
ccia_record = ""
gear_list = list() //Dont copy the loadout
// Do we need to reinitialize a whole bunch more vars?
if (re_initialize)
be_special_role = list()
@@ -0,0 +1,41 @@
################################
# Example Changelog File
#
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
#
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
# When it is, any changes listed below will disappear.
#
# Valid Prefixes:
# bugfix
# wip (For works in progress)
# tweak
# soundadd
# sounddel
# rscadd (general adding of nice things)
# rscdel (general deleting of nice things)
# imageadd
# imagedel
# maptweak
# spellcheck (typo fixes)
# experiment
# balance
# admin
# backend
# security
# refactor
#################################
# Your name.
author: Arrow768
# 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:
- backend: "Updated the way the loadout is stored so we might be able to increase the available loadout points."