-
Notifications
You must be signed in to change notification settings - Fork 514
Preferences
Preferences on Aurora are primarily geared towards utilizing the MySQL database. All tables prefixes with ss13_preferences
and ss13_characters_
hold data pertaining to either global player preferences, which are unaffected by the currently loaded character, or to specific characters, respectively.
The API for preferences suggests the creation of list datums per every visible page in the character setup window.
Each category is defined by a new /datum/category_group/player_setup_category
class. These are all declared in the code/modules/client/preference_setup/preference_setup.dm
file. There are four variables per prototype that you should pay attention to:
-
name
- self-explanatory, a simple name for the category. -
sort_order
- the order in which to algorithmically sort the category. This affects both load and save order, so it should be paid attention to if one category depends on the loading of another! -
category_item_type
- the parent class for all item subclasses that'll belong to this group. General naming convention is to call the type the same as you called your newplayer_setup_category
class. -
sql_role
- defines whether or not the MySQL loader should interpret this category as global preferences or character specific preferences. Do not use both flags! By default, the value isSQL_CHARACTER
. The differences are:-
SQL_PREFERENCES
are loaded first, before character data, and are saved whenever global preferences are edited. -
SQL_CHARACTER
are loaded after preferences. They're also only saved whenever the player presses thesave
button in character setup, and loaded whenever a new character is loaded.
-
Category items is where the "magic happens". It's where you handle specific data and generate the UI for editing it. Each item also manages its loading and data validation, allowing for special conditions and backend logic.
Each category item is a child of /datum/category_item/player_setup_item/category/
class. You don't need to declare the parent of it, simply start creating children. Much like with category groups, for each item class you'll have to define the name
and sort_order
variables. Their functions are the same.
The general idea is that category items will describe how variables owned by either the player's preferences object, accessible via src.pref
, or client variables, src.pref.client
, are handled and edited. All variables where you wish to store data to should thus be declared to either the /datum/preferences
class or in the /client
class, and not to a specific item.
To set up a category item properly, you will need to override a set of procs.
/datum/category_item/player_setup_item/proc/save_character(var/savefile/S)
/datum/category_item/player_setup_item/proc/load_character(var/savefile/S)
These two are old file system saving and loading procs. They're simple enough, and define where in the savefile to save and load the data from. Definitions are as simple as:
/datum/category_item/player_setup_item/general/body/load_character(var/savefile/S)
S["hair_red"] >> pref.r_hair
S["hair_green"] >> pref.g_hair
S["hair_blue"] >> pref.b_hair
/datum/category_item/player_setup_item/general/body/save_character(var/savefile/S)
S["hair_red"] << pref.r_hair
S["hair_green"] << pref.g_hair
S["hair_blue"] << pref.b_hair
The MySQL API is a bit more complex and more powerful. There are four procs that you should be aware of:
/datum/category_item/player_setup_item/proc/gather_load_query()
/datum/category_item/player_setup_item/proc/gather_save_query()
/datum/category_item/player_setup_item/proc/gather_load_parameters()
/datum/category_item/player_setup_item/proc/gather_save_parameters()
All of these must return an instance of type /list
, even if said list empty.
gather_load_query
is responsible for describing what data is getting pulled from what tables, and based on what arguments the query is executed. It also describes into which variables the data will be stored right after loading. The list has the following key-value structure:
list(
"table_name" = list(
"vars" = list(
"column_name1",
"column_name2" = "var_name"
),
"args" = list("id")
)
)
"table_name"
can be able table name from which the data is being pulled from. You can return a list describing multiple tables, though note you should never duplicate these key values in one return value.
"vars"
and "args"
are keywords and need to always be present. They must also always be associated with an instance of type /list
.
The list attached to "vars"
is an optionally associated list containing SQL column names -> preferences variable names
. If only the column name is present, then the variable name is implicitly associated with a variable of exactly the same name. Data from the described column is thus loaded into the variable specified. The variable name mark-up also has support for associated lists, by the following style: variable_name/key_value
. So describing "robot_generic" = "flavourtext_robot/generic"
is equal to saving the contents of the robot_generic
column to preferences.flavourtext_robot["generic"]
.
The list attached to "args"
describes what is written into the WHERE
clause of the category's query. The args are populated each query by gather_load_parameters
. So whatever values are written into the "args"
list must be populated by gather_load_parameters
of the same class.
This proc populates the arguments for the WHERE
clause in the load query. As described earlier, all arguments referenced in the queries of gather_load_query
must be populated here. Though this list is not table dependent.
Note that all arguments are properly sanitized and escaped automatically. Do not add data sanitization here unless you know exactly what you're doing.
The list returned from this proc is a very simple key -> value list of "argument_name" = argument_value.
A classic argument to use is the character ID:
/datum/category_item/player_setup_item/general/body/gather_load_parameters()
return list("id" = pref.current_character)
Similarly to gather_load_query
, this describes what data to save into which columns. Though the key list structure is simpler, as it does not require the specification of arguments explicitly. Nor does it require the specification of variable names. Just like with gather_load_query
, multiple tables can be specified.
An example is as follows:
list(
"ss13_characters" = list(
"hair_colour",
"id" = 1,
"ckey" = 1
)
)
The optional value 1
describes whether or not the data field is to be updated on duplicate key value. If 1
, then the data field is left untouched whenever a duplicate key is found. Generally speaking, you want to specify this for "id"
and "ckey"
: data that is immutable after the entry is created.
In this instance, it is important to know what the end query is and how it works in MySQL. This is the query generated by the example above:
INSERT INTO ss13_characters (hair_colour, id, ckey) VALUES (:hair_colour:, :id:, :ckey:) ON DUPLICATE KEY UPDATE hair_colour = :hair_colour:;
Effectively, id
and ckey
take the role of arguments with this query. And as such, they should not be updated on duplicate key entry. To minimize potential errors.
Functionally the same as gather_load_parameters
, this is a table agnostic function for gathering values to all of the arguments referenced in gather_save_query
. Everything defined there must be given a value here. The list is a simple key -> value list, containing argument name and its value.
Once again, variables are sanitized before being uploaded, so be careful with avoiding double sanitization.
An example list returned is as follows:
list(
"hair_colour" = rgb(pref.r_hair, pref.g_hair, pref.b_hair),
"id" = pref.current_character,
"ckey" = pref.client.ckey
)
A collection of standards and guidelines applied to the codebase.
Documentation regarding common APIs which speed up feature implementation and should be known by all coders.
- Atom Initialization
- Garbage, Queued Deletion, and Destroy
- Callbacks
- Timers
- Lazy Lists
- Overlays
- Processing APIs
- Common Helpers
- Global Listeners
- Singletons
Documentation for less used APIs that are not often needed.
Documentation regarding our implementation of StonedMC (SMC).
Decrepit or unused systems.
- Dynamic Maps (Not to be confused with the newer away mission implementation.)