mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-26 21:48:21 +01:00
Batches EXP updates for incredible efficiency (#15275)
* Batches EXP updates for incredible efficiency * Review tweaks
This commit is contained in:
@@ -265,15 +265,17 @@ SUBSYSTEM_DEF(dbcore)
|
||||
* * warn - Boolean to warn on query failure
|
||||
* * qdel - Boolean to enable auto qdel of queries
|
||||
* * assoc - Boolean to enable support for an associative list of queries
|
||||
* * log - Do we want to generate logs for these queries
|
||||
*/
|
||||
/datum/controller/subsystem/dbcore/proc/MassExecute(list/querys, warn = FALSE, qdel = FALSE, assoc = FALSE)
|
||||
/datum/controller/subsystem/dbcore/proc/MassExecute(list/querys, warn = FALSE, qdel = FALSE, assoc = FALSE, log = TRUE)
|
||||
if(!islist(querys))
|
||||
if(!istype(querys, /datum/db_query))
|
||||
CRASH("Invalid query passed to MassExecute: [querys]")
|
||||
querys = list(querys)
|
||||
|
||||
var/start_time = start_watch()
|
||||
log_debug("Mass executing [length(querys)] queries...")
|
||||
if(log)
|
||||
log_debug("Mass executing [length(querys)] queries...")
|
||||
|
||||
for(var/thing in querys)
|
||||
var/datum/db_query/query
|
||||
@@ -296,7 +298,8 @@ SUBSYSTEM_DEF(dbcore)
|
||||
if(qdel)
|
||||
qdel(query)
|
||||
|
||||
log_debug("Executed [length(querys)] queries in [stop_watch(start_time)]s")
|
||||
if(log)
|
||||
log_debug("Executed [length(querys)] queries in [stop_watch(start_time)]s")
|
||||
|
||||
/**
|
||||
* # db_query
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
SUBSYSTEM_DEF(jobs)
|
||||
name = "Jobs"
|
||||
init_order = INIT_ORDER_JOBS // 12
|
||||
wait = 3000 // 5 minutes (Deciseconds)
|
||||
wait = 5 MINUTES // Dont ever make this a super low value since EXP updates are calculated from this value
|
||||
runlevels = RUNLEVEL_GAME
|
||||
offline_implications = "Job playtime hours will no longer be logged. No immediate action is needed."
|
||||
|
||||
@@ -25,9 +25,9 @@ SUBSYSTEM_DEF(jobs)
|
||||
|
||||
// Only fires every 5 minutes
|
||||
/datum/controller/subsystem/jobs/fire()
|
||||
if(!config.sql_enabled || !config.use_exp_tracking)
|
||||
if(!SSdbcore.IsConnected() || !config.use_exp_tracking)
|
||||
return
|
||||
INVOKE_ASYNC(GLOBAL_PROC, /.proc/update_exp, 5, 0)
|
||||
batch_update_player_exp(announce = FALSE) // Set this to true if you ever want to inform players about their EXP gains
|
||||
|
||||
/datum/controller/subsystem/jobs/proc/SetupOccupations(var/list/faction = list("Station"))
|
||||
occupations = list()
|
||||
@@ -726,3 +726,139 @@ SUBSYSTEM_DEF(jobs)
|
||||
new_id_change_records["[id_change_counter]"] = thisrecord
|
||||
id_change_counter++
|
||||
id_change_records = new_id_change_records
|
||||
|
||||
// This proc will update all players EXP at once. It will calculate amount of time to add dynamically based on the SS fire time.
|
||||
/datum/controller/subsystem/jobs/proc/batch_update_player_exp(announce = FALSE)
|
||||
// Right off the bat
|
||||
var/start_time = start_watch()
|
||||
// First calculate minutes
|
||||
var/divider = 10 // By default, 10 deciseconds in 1 second
|
||||
if(flags & SS_TICKER)
|
||||
divider = 20 // If this SS ever gets made into a ticker SS, account for that
|
||||
|
||||
var/minutes = (wait / divider) / 60 // Calculate minutes based on the SS wait time (How often this proc fires)
|
||||
|
||||
// Step 1: Get us a list of clients to process
|
||||
var/list/client/clients_to_process = GLOB.clients.Copy() // This is copied so that clients joining in the middle of this dont break things
|
||||
Debug("Starting EXP update for [length(clients_to_process)] clients. (Adding [minutes] minutes)")
|
||||
|
||||
var/list/datum/db_query/select_queries = list() // List of SELECT queries to mass grab EXP.
|
||||
|
||||
for(var/i in clients_to_process)
|
||||
var/client/C = i
|
||||
if(!C)
|
||||
continue // If a client logs out in the middle of this
|
||||
|
||||
var/datum/db_query/exp_read = SSdbcore.NewQuery(
|
||||
"SELECT exp FROM [format_table_name("player")] WHERE ckey=:ckey",
|
||||
list("ckey" = C.ckey)
|
||||
)
|
||||
|
||||
select_queries[C.ckey] = exp_read
|
||||
|
||||
var/list/read_records = list()
|
||||
// Explanation for parameters:
|
||||
// TRUE: We want warnings if these fail
|
||||
// FALSE: Do NOT qdel() queries here, otherwise they wont be read. At all.
|
||||
// TRUE: This is an assoc list, so it needs to prepare for that
|
||||
// FALSE: We dont want to logspam
|
||||
SSdbcore.MassExecute(select_queries, TRUE, FALSE, TRUE, FALSE) // Batch execute so we can take advantage of async magic
|
||||
|
||||
for(var/i in clients_to_process)
|
||||
var/client/C = i
|
||||
if(!C)
|
||||
continue // If a client logs out in the middle of this
|
||||
|
||||
if(select_queries[C.ckey]) // This check should not be necessary, but I am paranoid
|
||||
while(select_queries[C.ckey].NextRow())
|
||||
read_records[C.ckey] = params2list(select_queries[C.ckey].item[1])
|
||||
|
||||
QDEL_LIST_ASSOC_VAL(select_queries) // Clean stuff up
|
||||
|
||||
var/list/play_records = list()
|
||||
|
||||
var/list/datum/db_query/player_update_queries = list() // List of queries to update player EXP
|
||||
var/list/datum/db_query/playtime_history_update_queries = list() // List of queries to update the playtime history table
|
||||
|
||||
for(var/i in clients_to_process)
|
||||
var/client/C = i
|
||||
if(!C)
|
||||
continue // If a client logs out in the middle of this
|
||||
// Get us a container
|
||||
play_records[C.ckey] = list()
|
||||
for(var/rtype in GLOB.exp_jobsmap)
|
||||
if(text2num(read_records[C.ckey][rtype]))
|
||||
play_records[C.ckey][rtype] = text2num(read_records[C.ckey][rtype])
|
||||
else
|
||||
play_records[C.ckey][rtype] = 0
|
||||
|
||||
|
||||
var/myrole
|
||||
if(C.mob.mind)
|
||||
if(C.mob.mind.playtime_role)
|
||||
myrole = C.mob.mind.playtime_role
|
||||
else if(C.mob.mind.assigned_role)
|
||||
myrole = C.mob.mind.assigned_role
|
||||
|
||||
var/added_living = 0
|
||||
var/added_ghost = 0
|
||||
if(C.mob.stat == CONSCIOUS && myrole)
|
||||
play_records[C.ckey][EXP_TYPE_LIVING] += minutes
|
||||
added_living += minutes
|
||||
|
||||
if(announce)
|
||||
to_chat(C.mob, "<span class='notice'>You got: [minutes] Living EXP!</span>")
|
||||
|
||||
for(var/category in GLOB.exp_jobsmap)
|
||||
if(GLOB.exp_jobsmap[category]["titles"])
|
||||
if(myrole in GLOB.exp_jobsmap[category]["titles"])
|
||||
play_records[C.ckey][category] += minutes
|
||||
if(announce)
|
||||
to_chat(C.mob, "<span class='notice'>You got: [minutes] [category] EXP!</span>")
|
||||
|
||||
if(C.mob.mind.special_role)
|
||||
play_records[C.ckey][EXP_TYPE_SPECIAL] += minutes
|
||||
if(announce)
|
||||
to_chat(C.mob, "<span class='notice'>You got: [minutes] Special EXP!</span>")
|
||||
|
||||
else if(isobserver(C.mob))
|
||||
play_records[C.ckey][EXP_TYPE_GHOST] += minutes
|
||||
added_ghost += minutes
|
||||
if(announce)
|
||||
to_chat(C.mob, "<span class='notice'>You got: [minutes] Ghost EXP!</span>")
|
||||
else
|
||||
continue
|
||||
|
||||
var/new_exp = list2params(play_records[C.ckey])
|
||||
|
||||
C.prefs.exp = new_exp
|
||||
|
||||
var/datum/db_query/update_query = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("player")] SET exp =:newexp, lastseen=NOW() WHERE ckey=:ckey",
|
||||
list(
|
||||
"newexp" = new_exp,
|
||||
"ckey" = C.ckey
|
||||
)
|
||||
)
|
||||
|
||||
player_update_queries += update_query
|
||||
|
||||
var/datum/db_query/update_query_history = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("playtime_history")] (ckey, date, time_living, time_ghost)
|
||||
VALUES (:ckey, CURDATE(), :addedliving, :addedghost)
|
||||
ON DUPLICATE KEY UPDATE time_living=time_living + VALUES(time_living), time_ghost=time_ghost + VALUES(time_ghost)"},
|
||||
list(
|
||||
"ckey" = C.ckey,
|
||||
"addedliving" = added_living,
|
||||
"addedghost" = added_ghost
|
||||
)
|
||||
)
|
||||
|
||||
playtime_history_update_queries += update_query_history
|
||||
|
||||
|
||||
// warn=TRUE, qdel=TRUE, assoc=FALSE, log=FALSE
|
||||
SSdbcore.MassExecute(player_update_queries, TRUE, TRUE, FALSE, FALSE) // Batch execute so we can take advantage of async magic
|
||||
SSdbcore.MassExecute(playtime_history_update_queries, TRUE, TRUE, FALSE, FALSE)
|
||||
|
||||
Debug("Successfully updated all EXP data in [stop_watch(start_time)]s")
|
||||
|
||||
Reference in New Issue
Block a user