Merge branch 'master' into upstream-merge-30056

This commit is contained in:
LetterJay
2017-09-13 06:08:59 -05:00
committed by GitHub
545 changed files with 23622 additions and 13702 deletions
+51 -7
View File
@@ -5,6 +5,10 @@
#define SECURITY_HAS_MAINT_ACCESS 2
#define EVERYONE_HAS_MAINT_ACCESS 4
GLOBAL_VAR_INIT(config_dir, "config/")
GLOBAL_PROTECT(config_dir)
/datum/configuration/can_vv_get(var_name)
var/static/list/banned_gets = list("autoadmin", "autoadmin_rank")
if (var_name in banned_gets)
@@ -12,7 +16,7 @@
return ..()
/datum/configuration/vv_edit_var(var_name, var_value)
var/static/list/banned_edits = list("cross_address", "cross_allowed", "autoadmin", "autoadmin_rank")
var/static/list/banned_edits = list("cross_address", "cross_allowed", "autoadmin", "autoadmin_rank", "invoke_youtubedl")
if(var_name in banned_edits)
return FALSE
return ..()
@@ -90,6 +94,8 @@
var/panic_server_name
var/panic_address //Reconnect a player this linked server if this server isn't accepting new players
var/invoke_youtubedl
//IP Intel vars
var/ipintel_email
var/ipintel_rating_bad = 1
@@ -102,6 +108,15 @@
var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database
var/use_account_age_for_jobs = 0 //Uses the time they made the account for the job restriction stuff. New player joining alerts should be unaffected.
var/see_own_notes = 0 //Can players see their own admin notes (read-only)? Config option in config.txt
var/note_fresh_days
var/note_stale_days
var/use_exp_tracking = FALSE
var/use_exp_restrictions_heads = FALSE
var/use_exp_restrictions_heads_hours = 0
var/use_exp_restrictions_heads_department = FALSE
var/use_exp_restrictions_other = FALSE
var/use_exp_restrictions_admin_bypass = FALSE
//Population cap vars
var/soft_popcap = 0
@@ -168,6 +183,7 @@
var/rename_cyborg = 0
var/ooc_during_round = 0
var/emojis = 0
var/no_credits_round_end = FALSE
//Used for modifying movement speed for mobs.
//Unversal modifiers
@@ -266,6 +282,8 @@
var/list/policies = list()
var/debug_admin_hrefs = FALSE //turns off admin href token protection for debugging purposes
/datum/configuration/New()
gamemode_cache = typecacheof(/datum/game_mode,TRUE)
for(var/T in gamemode_cache)
@@ -287,18 +305,20 @@
Reload()
/datum/configuration/proc/Reload()
load("config/config.txt")
load("config/comms.txt", "comms")
load("config/game_options.txt","game_options")
load("config/policies.txt", "policies")
loadsql("config/dbconfig.txt")
load("config.txt")
load("comms.txt", "comms")
load("game_options.txt","game_options")
load("policies.txt", "policies")
loadsql("dbconfig.txt")
reload_custom_roundstart_items_list()
if (maprotation)
loadmaplist("config/maps.txt")
loadmaplist("maps.txt")
// apply some settings from config..
GLOB.abandon_allowed = respawn
/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist
filename = "[GLOB.config_dir][filename]"
var/list/Lines = world.file2list(filename)
for(var/t in Lines)
@@ -336,6 +356,18 @@
use_age_restriction_for_jobs = 1
if("use_account_age_for_jobs")
use_account_age_for_jobs = 1
if("use_exp_tracking")
use_exp_tracking = TRUE
if("use_exp_restrictions_heads")
use_exp_restrictions_heads = TRUE
if("use_exp_restrictions_heads_hours")
use_exp_restrictions_heads_hours = text2num(value)
if("use_exp_restrictions_heads_department")
use_exp_restrictions_heads_department = TRUE
if("use_exp_restrictions_other")
use_exp_restrictions_other = TRUE
if("use_exp_restrictions_admin_bypass")
use_exp_restrictions_admin_bypass = TRUE
if("lobby_countdown")
lobby_countdown = text2num(value)
if("round_end_countdown")
@@ -450,10 +482,16 @@
if("panic_server_address")
if(value != "byond://address:port")
panic_address = value
if("invoke_youtubedl")
invoke_youtubedl = value
if("show_irc_name")
showircname = 1
if("see_own_notes")
see_own_notes = 1
if("note_fresh_days")
note_fresh_days = text2num(value)
if("note_stale_days")
note_stale_days = text2num(value)
if("soft_popcap")
soft_popcap = text2num(value)
if("hard_popcap")
@@ -533,6 +571,8 @@
error_msg_delay = text2num(value)
if("irc_announce_new_game")
irc_announce_new_game = TRUE
if("debug_admin_hrefs")
debug_admin_hrefs = TRUE
else
#if DM_VERSION > 511
#error Replace the line below with WRITE_FILE(GLOB.config_error_log, "Unknown setting in configuration: '[name]'")
@@ -556,6 +596,8 @@
ooc_during_round = 1
if("emojis")
emojis = 1
if("no_credits_round_end")
no_credits_round_end = TRUE
if("run_delay")
run_speed = text2num(value)
if("walk_delay")
@@ -799,6 +841,7 @@
WRITE_FILE(GLOB.config_error_log, "Unknown setting in configuration: '[name]'")
/datum/configuration/proc/loadmaplist(filename)
filename = "[GLOB.config_dir][filename]"
var/list/Lines = world.file2list(filename)
var/datum/map_config/currentmap = null
@@ -851,6 +894,7 @@
/datum/configuration/proc/loadsql(filename)
filename = "[GLOB.config_dir][filename]"
var/list/Lines = world.file2list(filename)
for(var/t in Lines)
if(!t)
+24
View File
@@ -0,0 +1,24 @@
diff a/code/controllers/configuration.dm b/code/controllers/configuration.dm (rejected hunks)
@@ -337,17 +337,17 @@
if("use_account_age_for_jobs")
use_account_age_for_jobs = 1
if("use_exp_tracking")
- use_exp_tracking = 1
+ use_exp_tracking = TRUE
if("use_exp_restrictions_heads")
- use_exp_restrictions_heads = 1
+ use_exp_restrictions_heads = TRUE
if("use_exp_restrictions_heads_hours")
use_exp_restrictions_heads_hours = text2num(value)
if("use_exp_restrictions_heads_department")
- use_exp_restrictions_heads_department = 1
+ use_exp_restrictions_heads_department = TRUE
if("use_exp_restrictions_other")
- use_exp_restrictions_other = 1
+ use_exp_restrictions_other = TRUE
if("use_exp_restrictions_admin_bypass")
- use_exp_restrictions_admin_bypass = 1
+ use_exp_restrictions_admin_bypass = TRUE
if("lobby_countdown")
lobby_countdown = text2num(value)
if("round_end_countdown")
+102 -102
View File
@@ -1,102 +1,102 @@
/**
* Failsafe
*
* Pretty much pokes the MC to make sure it's still alive.
**/
GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
name = "Failsafe"
// The length of time to check on the MC (in deciseconds).
// Set to 0 to disable.
var/processing_interval = 20
// The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
var/defcon = 5
//the world.time of the last check, so the mc can restart US if we hang.
// (Real friends look out for *eachother*)
var/lasttick = 0
// Track the MC iteration to make sure its still on track.
var/master_iteration = 0
var/running = TRUE
/datum/controller/failsafe/New()
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
if(Failsafe != src)
if(istype(Failsafe))
qdel(Failsafe)
Failsafe = src
Initialize()
/datum/controller/failsafe/Initialize()
set waitfor = 0
Failsafe.Loop()
if(!QDELETED(src))
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
/datum/controller/failsafe/Destroy()
running = FALSE
..()
return QDEL_HINT_HARDDEL_NOW
/datum/controller/failsafe/proc/Loop()
while(running)
lasttick = world.time
if(!Master)
// Replace the missing Master! This should never, ever happen.
new /datum/controller/master()
// Only poke it if overrides are not in effect.
if(processing_interval > 0)
if(Master.processing && Master.iteration)
// Check if processing is done yet.
if(Master.iteration == master_iteration)
switch(defcon)
if(4,5)
--defcon
if(3)
to_chat(GLOB.admins, "<span class='adminnotice'>Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.")
--defcon
if(2)
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.</span>")
--defcon
if(1)
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...</span>")
--defcon
var/rtn = Recreate_MC()
if(rtn > 0)
defcon = 4
master_iteration = 0
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
else if(rtn < 0)
log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
to_chat(GLOB.admins, "<span class='boldannounce'>ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.</span>")
//if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
//no need to handle that specially when defcon 0 can handle it
if(0) //DEFCON 0! (mc failed to restart)
var/rtn = Recreate_MC()
if(rtn > 0)
defcon = 4
master_iteration = 0
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
else
defcon = min(defcon + 1,5)
master_iteration = Master.iteration
if (defcon <= 1)
sleep(processing_interval*2)
else
sleep(processing_interval)
else
defcon = 5
sleep(initial(processing_interval))
/datum/controller/failsafe/proc/defcon_pretty()
return defcon
/datum/controller/failsafe/stat_entry()
if(!statclick)
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
/**
* Failsafe
*
* Pretty much pokes the MC to make sure it's still alive.
**/
GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
name = "Failsafe"
// The length of time to check on the MC (in deciseconds).
// Set to 0 to disable.
var/processing_interval = 20
// The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
var/defcon = 5
//the world.time of the last check, so the mc can restart US if we hang.
// (Real friends look out for *eachother*)
var/lasttick = 0
// Track the MC iteration to make sure its still on track.
var/master_iteration = 0
var/running = TRUE
/datum/controller/failsafe/New()
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
if(Failsafe != src)
if(istype(Failsafe))
qdel(Failsafe)
Failsafe = src
Initialize()
/datum/controller/failsafe/Initialize()
set waitfor = 0
Failsafe.Loop()
if(!QDELETED(src))
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
/datum/controller/failsafe/Destroy()
running = FALSE
..()
return QDEL_HINT_HARDDEL_NOW
/datum/controller/failsafe/proc/Loop()
while(running)
lasttick = world.time
if(!Master)
// Replace the missing Master! This should never, ever happen.
new /datum/controller/master()
// Only poke it if overrides are not in effect.
if(processing_interval > 0)
if(Master.processing && Master.iteration)
// Check if processing is done yet.
if(Master.iteration == master_iteration)
switch(defcon)
if(4,5)
--defcon
if(3)
message_admins("<span class='adminnotice'>Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.</span>")
--defcon
if(2)
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.</span>")
--defcon
if(1)
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...</span>")
--defcon
var/rtn = Recreate_MC()
if(rtn > 0)
defcon = 4
master_iteration = 0
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
else if(rtn < 0)
log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
to_chat(GLOB.admins, "<span class='boldannounce'>ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.</span>")
//if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
//no need to handle that specially when defcon 0 can handle it
if(0) //DEFCON 0! (mc failed to restart)
var/rtn = Recreate_MC()
if(rtn > 0)
defcon = 4
master_iteration = 0
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
else
defcon = min(defcon + 1,5)
master_iteration = Master.iteration
if (defcon <= 1)
sleep(processing_interval*2)
else
sleep(processing_interval)
else
defcon = 5
sleep(initial(processing_interval))
/datum/controller/failsafe/proc/defcon_pretty()
return defcon
/datum/controller/failsafe/stat_entry()
if(!statclick)
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
+275 -263
View File
@@ -1,269 +1,281 @@
SUBSYSTEM_DEF(blackbox)
name = "Blackbox"
wait = 6000
flags = SS_NO_TICK_CHECK | SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
init_order = INIT_ORDER_BLACKBOX
var/list/msg_common = list()
var/list/msg_science = list()
var/list/msg_command = list()
var/list/msg_medical = list()
var/list/msg_engineering = list()
var/list/msg_security = list()
var/list/msg_deathsquad = list()
var/list/msg_syndicate = list()
var/list/msg_service = list()
var/list/msg_cargo = list()
var/list/msg_other = list()
var/list/feedback = list() //list of datum/feedback_variable
var/sealed = FALSE //time to stop tracking stats?
//poll population
/datum/controller/subsystem/blackbox/fire()
if(!SSdbcore.Connect())
return
var/playercount = 0
for(var/mob/M in GLOB.player_list)
if(M.client)
playercount += 1
var/admincount = GLOB.admins.len
var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]')")
query_record_playercount.Execute()
/datum/controller/subsystem/blackbox/Recover()
msg_common = SSblackbox.msg_common
msg_science = SSblackbox.msg_science
msg_command = SSblackbox.msg_command
msg_medical = SSblackbox.msg_medical
msg_engineering = SSblackbox.msg_engineering
msg_security = SSblackbox.msg_security
msg_deathsquad = SSblackbox.msg_deathsquad
msg_syndicate = SSblackbox.msg_syndicate
msg_service = SSblackbox.msg_service
msg_cargo = SSblackbox.msg_cargo
msg_other = SSblackbox.msg_other
feedback = SSblackbox.feedback
sealed = SSblackbox.sealed
//no touchie
/datum/controller/subsystem/blackbox/can_vv_get(var_name)
if(var_name == "feedback")
return FALSE
return ..()
/datum/controller/subsystem/blackbox/vv_edit_var(var_name, var_value)
return FALSE
/datum/controller/subsystem/blackbox/Shutdown()
sealed = FALSE
set_val("ahelp_unresolved", GLOB.ahelp_tickets.active_tickets.len)
var/pda_msg_amt = 0
var/rc_msg_amt = 0
for (var/obj/machinery/message_server/MS in GLOB.message_servers)
if (MS.pda_msgs.len > pda_msg_amt)
pda_msg_amt = MS.pda_msgs.len
if (MS.rc_msgs.len > rc_msg_amt)
rc_msg_amt = MS.rc_msgs.len
set_details("radio_usage","")
add_details("radio_usage","COM-[msg_common.len]")
add_details("radio_usage","SCI-[msg_science.len]")
add_details("radio_usage","HEA-[msg_command.len]")
add_details("radio_usage","MED-[msg_medical.len]")
add_details("radio_usage","ENG-[msg_engineering.len]")
add_details("radio_usage","SEC-[msg_security.len]")
add_details("radio_usage","DTH-[msg_deathsquad.len]")
add_details("radio_usage","SYN-[msg_syndicate.len]")
add_details("radio_usage","SRV-[msg_service.len]")
add_details("radio_usage","CAR-[msg_cargo.len]")
add_details("radio_usage","OTH-[msg_other.len]")
add_details("radio_usage","PDA-[pda_msg_amt]")
add_details("radio_usage","RC-[rc_msg_amt]")
if (!SSdbcore.Connect())
return
var/list/sqlrowlist = list()
for (var/datum/feedback_variable/FV in feedback)
sqlrowlist += list(list("time" = "Now()", "round_id" = GLOB.round_id, "var_name" = "'[sanitizeSQL(FV.get_variable())]'", "var_value" = FV.get_value(), "details" = "'[sanitizeSQL(FV.get_details())]'"))
if (!length(sqlrowlist))
return
SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)
/datum/controller/subsystem/blackbox/proc/LogBroadcast(blackbox_msg, freq)
if(sealed)
return
switch(freq)
if(1459)
msg_common += blackbox_msg
if(1351)
msg_science += blackbox_msg
if(1353)
msg_command += blackbox_msg
if(1355)
msg_medical += blackbox_msg
if(1357)
msg_engineering += blackbox_msg
if(1359)
msg_security += blackbox_msg
if(1441)
msg_deathsquad += blackbox_msg
if(1213)
msg_syndicate += blackbox_msg
if(1349)
msg_service += blackbox_msg
if(1347)
msg_cargo += blackbox_msg
else
msg_other += blackbox_msg
/datum/controller/subsystem/blackbox/proc/find_feedback_datum(variable)
for(var/datum/feedback_variable/FV in feedback)
if(FV.get_variable() == variable)
return FV
var/datum/feedback_variable/FV = new(variable)
feedback += FV
return FV
/datum/controller/subsystem/blackbox/proc/set_val(variable, value)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_value(value)
/datum/controller/subsystem/blackbox/proc/inc(variable, value)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.inc(value)
/datum/controller/subsystem/blackbox/proc/dec(variable,value)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.dec(value)
/datum/controller/subsystem/blackbox/proc/set_details(variable,details)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_details(details)
/datum/controller/subsystem/blackbox/proc/add_details(variable,details)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.add_details(details)
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
if(sealed)
return
if(!SSdbcore.Connect())
return
if(!L || !L.key || !L.mind)
return
var/turf/T = get_turf(L)
var/area/placeofdeath = get_area(T.loc)
var/sqlname = sanitizeSQL(L.real_name)
var/sqlkey = sanitizeSQL(L.ckey)
var/sqljob = sanitizeSQL(L.mind.assigned_role)
var/sqlspecial = sanitizeSQL(L.mind.special_role)
var/sqlpod = sanitizeSQL(placeofdeath.name)
var/laname
var/lakey
if(L.lastattacker && ismob(L.lastattacker))
var/mob/LA = L.lastattacker
laname = sanitizeSQL(LA.real_name)
lakey = sanitizeSQL(LA.key)
var/sqlbrute = sanitizeSQL(L.getBruteLoss())
var/sqlfire = sanitizeSQL(L.getFireLoss())
var/sqlbrain = sanitizeSQL(L.getBrainLoss())
var/sqloxy = sanitizeSQL(L.getOxyLoss())
var/sqltox = sanitizeSQL(L.getToxLoss())
var/sqlclone = sanitizeSQL(L.getCloneLoss())
var/sqlstamina = sanitizeSQL(L.getStaminaLoss())
var/x_coord = sanitizeSQL(L.x)
var/y_coord = sanitizeSQL(L.y)
var/z_coord = sanitizeSQL(L.z)
var/map = sanitizeSQL(SSmapping.config.map_name)
var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina])")
query_report_death.Execute()
/datum/controller/subsystem/blackbox/proc/Seal()
if(sealed)
return
if(IsAdminAdvancedProcCall())
var/msg = "[key_name_admin(usr)] sealed the blackbox!"
message_admins(msg)
log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].")
sealed = TRUE
//feedback variable datum, for storing all kinds of data
/datum/feedback_variable
var/variable
var/value
var/details
/datum/feedback_variable/New(param_variable, param_value = 0)
variable = param_variable
value = param_value
/datum/feedback_variable/proc/inc(num = 1)
if (isnum(value))
value += num
else
value = text2num(value)
if (isnum(value))
value += num
else
value = num
/datum/feedback_variable/proc/dec(num = 1)
if (isnum(value))
value -= num
else
value = text2num(value)
if (isnum(value))
value -= num
else
value = -num
/datum/feedback_variable/proc/set_value(num)
if (isnum(num))
value = num
/datum/feedback_variable/proc/get_value()
if (!isnum(value))
return 0
return value
/datum/feedback_variable/proc/get_variable()
return variable
SUBSYSTEM_DEF(blackbox)
name = "Blackbox"
wait = 6000
flags = SS_NO_TICK_CHECK
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
init_order = INIT_ORDER_BLACKBOX
var/list/msg_common = list()
var/list/msg_science = list()
var/list/msg_command = list()
var/list/msg_medical = list()
var/list/msg_engineering = list()
var/list/msg_security = list()
var/list/msg_deathsquad = list()
var/list/msg_syndicate = list()
var/list/msg_service = list()
var/list/msg_cargo = list()
var/list/msg_other = list()
var/list/feedback = list() //list of datum/feedback_variable
var/triggertime = 0
var/sealed = FALSE //time to stop tracking stats?
/datum/controller/subsystem/blackbox/Initialize()
triggertime = world.time
. = ..()
//poll population
/datum/controller/subsystem/blackbox/fire()
if(!SSdbcore.Connect())
return
var/playercount = 0
for(var/mob/M in GLOB.player_list)
if(M.client)
playercount += 1
var/admincount = GLOB.admins.len
var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port, round_id) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]')")
query_record_playercount.Execute()
if(config.use_exp_tracking)
if((triggertime < 0) || (world.time > (triggertime +3000))) //subsystem fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire. The <0 is midnight rollover check
update_exp(10,FALSE)
/datum/controller/subsystem/blackbox/Recover()
msg_common = SSblackbox.msg_common
msg_science = SSblackbox.msg_science
msg_command = SSblackbox.msg_command
msg_medical = SSblackbox.msg_medical
msg_engineering = SSblackbox.msg_engineering
msg_security = SSblackbox.msg_security
msg_deathsquad = SSblackbox.msg_deathsquad
msg_syndicate = SSblackbox.msg_syndicate
msg_service = SSblackbox.msg_service
msg_cargo = SSblackbox.msg_cargo
msg_other = SSblackbox.msg_other
feedback = SSblackbox.feedback
sealed = SSblackbox.sealed
//no touchie
/datum/controller/subsystem/blackbox/can_vv_get(var_name)
if(var_name == "feedback")
return FALSE
return ..()
/datum/controller/subsystem/blackbox/vv_edit_var(var_name, var_value)
return FALSE
/datum/controller/subsystem/blackbox/Shutdown()
sealed = FALSE
set_val("ahelp_unresolved", GLOB.ahelp_tickets.active_tickets.len)
var/pda_msg_amt = 0
var/rc_msg_amt = 0
for (var/obj/machinery/message_server/MS in GLOB.message_servers)
if (MS.pda_msgs.len > pda_msg_amt)
pda_msg_amt = MS.pda_msgs.len
if (MS.rc_msgs.len > rc_msg_amt)
rc_msg_amt = MS.rc_msgs.len
set_details("radio_usage","")
add_details("radio_usage","COM-[msg_common.len]")
add_details("radio_usage","SCI-[msg_science.len]")
add_details("radio_usage","HEA-[msg_command.len]")
add_details("radio_usage","MED-[msg_medical.len]")
add_details("radio_usage","ENG-[msg_engineering.len]")
add_details("radio_usage","SEC-[msg_security.len]")
add_details("radio_usage","DTH-[msg_deathsquad.len]")
add_details("radio_usage","SYN-[msg_syndicate.len]")
add_details("radio_usage","SRV-[msg_service.len]")
add_details("radio_usage","CAR-[msg_cargo.len]")
add_details("radio_usage","OTH-[msg_other.len]")
add_details("radio_usage","PDA-[pda_msg_amt]")
add_details("radio_usage","RC-[rc_msg_amt]")
if (!SSdbcore.Connect())
return
var/list/sqlrowlist = list()
for (var/datum/feedback_variable/FV in feedback)
sqlrowlist += list(list("time" = "Now()", "round_id" = GLOB.round_id, "var_name" = "'[sanitizeSQL(FV.get_variable())]'", "var_value" = FV.get_value(), "details" = "'[sanitizeSQL(FV.get_details())]'"))
if (!length(sqlrowlist))
return
SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)
/datum/controller/subsystem/blackbox/proc/LogBroadcast(blackbox_msg, freq)
if(sealed)
return
switch(freq)
if(1459)
msg_common += blackbox_msg
if(1351)
msg_science += blackbox_msg
if(1353)
msg_command += blackbox_msg
if(1355)
msg_medical += blackbox_msg
if(1357)
msg_engineering += blackbox_msg
if(1359)
msg_security += blackbox_msg
if(1441)
msg_deathsquad += blackbox_msg
if(1213)
msg_syndicate += blackbox_msg
if(1349)
msg_service += blackbox_msg
if(1347)
msg_cargo += blackbox_msg
else
msg_other += blackbox_msg
/datum/controller/subsystem/blackbox/proc/find_feedback_datum(variable)
for(var/datum/feedback_variable/FV in feedback)
if(FV.get_variable() == variable)
return FV
var/datum/feedback_variable/FV = new(variable)
feedback += FV
return FV
/datum/controller/subsystem/blackbox/proc/set_val(variable, value)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_value(value)
/datum/controller/subsystem/blackbox/proc/inc(variable, value)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.inc(value)
/datum/controller/subsystem/blackbox/proc/dec(variable,value)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.dec(value)
/datum/controller/subsystem/blackbox/proc/set_details(variable,details)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_details(details)
/datum/controller/subsystem/blackbox/proc/add_details(variable,details)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.add_details(details)
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
if(sealed)
return
if(!SSdbcore.Connect())
return
if(!L || !L.key || !L.mind)
return
var/turf/T = get_turf(L)
var/area/placeofdeath = get_area(T.loc)
var/sqlname = sanitizeSQL(L.real_name)
var/sqlkey = sanitizeSQL(L.ckey)
var/sqljob = sanitizeSQL(L.mind.assigned_role)
var/sqlspecial = sanitizeSQL(L.mind.special_role)
var/sqlpod = sanitizeSQL(placeofdeath.name)
var/laname
var/lakey
if(L.lastattacker && ismob(L.lastattacker))
var/mob/LA = L.lastattacker
laname = sanitizeSQL(LA.real_name)
lakey = sanitizeSQL(LA.key)
var/sqlbrute = sanitizeSQL(L.getBruteLoss())
var/sqlfire = sanitizeSQL(L.getFireLoss())
var/sqlbrain = sanitizeSQL(L.getBrainLoss())
var/sqloxy = sanitizeSQL(L.getOxyLoss())
var/sqltox = sanitizeSQL(L.getToxLoss())
var/sqlclone = sanitizeSQL(L.getCloneLoss())
var/sqlstamina = sanitizeSQL(L.getStaminaLoss())
var/x_coord = sanitizeSQL(L.x)
var/y_coord = sanitizeSQL(L.y)
var/z_coord = sanitizeSQL(L.z)
var/last_words = sanitizeSQL(L.last_words)
var/suicide = sanitizeSQL(L.suiciding)
var/map = sanitizeSQL(SSmapping.config.map_name)
var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, last_words, suicide) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina], '[last_words]', [suicide])")
query_report_death.Execute()
/datum/controller/subsystem/blackbox/proc/Seal()
if(sealed)
return
if(IsAdminAdvancedProcCall())
var/msg = "[key_name_admin(usr)] sealed the blackbox!"
message_admins(msg)
log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].")
sealed = TRUE
//feedback variable datum, for storing all kinds of data
/datum/feedback_variable
var/variable
var/value
var/list/details
/datum/feedback_variable/New(param_variable, param_value = 0)
variable = param_variable
value = param_value
/datum/feedback_variable/proc/inc(num = 1)
if (isnum(value))
value += num
else
value = text2num(value)
if (isnum(value))
value += num
else
value = num
/datum/feedback_variable/proc/dec(num = 1)
if (isnum(value))
value -= num
else
value = text2num(value)
if (isnum(value))
value -= num
else
value = -num
/datum/feedback_variable/proc/set_value(num)
if (isnum(num))
value = num
/datum/feedback_variable/proc/get_value()
if (!isnum(value))
return 0
return value
/datum/feedback_variable/proc/get_variable()
return variable
/datum/feedback_variable/proc/set_details(deets)
details = "\"[deets]\""
details = list("\"[deets]\"")
/datum/feedback_variable/proc/add_details(deets)
if (!details)
set_details(deets)
else
details += " | \"[deets]\""
/datum/feedback_variable/proc/get_details()
return details
/datum/feedback_variable/proc/get_parsed()
return list(variable,value,details)
details += "\"[deets]\""
/datum/feedback_variable/proc/get_details()
return details.Join(" | ")
/datum/feedback_variable/proc/get_parsed()
return list(variable,value,details.Join(" | "))
@@ -0,0 +1,10 @@
diff a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm (rejected hunks)
@@ -40,7 +40,7 @@ SUBSYSTEM_DEF(blackbox)
if(config.use_exp_tracking)
if((triggertime < 0) || (world.time > (triggertime +3000))) //subsystem fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire. The <0 is midnight rollover check
- SSblackbox.update_exp(10,FALSE)
+ update_exp(10,FALSE)
/datum/controller/subsystem/blackbox/Recover()
+7
View File
@@ -14,3 +14,10 @@ SUBSYSTEM_DEF(disease)
/datum/controller/subsystem/disease/stat_entry(msg)
..("P:[active_diseases.len]")
/datum/controller/subsystem/disease/proc/get_disease_name(id)
var/datum/disease/advance/A = archive_diseases[id]
if(A.name)
return A.name
else
return "Unknown"
+3 -1
View File
@@ -116,6 +116,8 @@ SUBSYSTEM_DEF(events)
//allows a client to trigger an event
//aka Badmin Central
// > Not in modules/admin
// REEEEEEEEE
/client/proc/forceEvent()
set name = "Trigger Event"
set category = "Fun"
@@ -131,7 +133,7 @@ SUBSYSTEM_DEF(events)
var/magic = ""
var/holiday = ""
for(var/datum/round_event_control/E in SSevents.control)
dat = "<BR><A href='?src=\ref[src];forceevent=\ref[E]'>[E]</A>"
dat = "<BR><A href='?src=\ref[src];[HrefToken()];forceevent=\ref[E]'>[E]</A>"
if(E.holidayID)
holiday += dat
else if(E.wizardevent)
+208 -142
View File
@@ -1,40 +1,44 @@
SUBSYSTEM_DEF(garbage)
name = "Garbage"
priority = 15
wait = 20
wait = 2 SECONDS
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
var/delslasttick = 0 // number of del()'s we've done this tick
var/gcedlasttick = 0 // number of things that gc'ed last tick
var/list/collection_timeout = list(0, 2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
//Stat tracking
var/delslasttick = 0 // number of del()'s we've done this tick
var/gcedlasttick = 0 // number of things that gc'ed last tick
var/totaldels = 0
var/totalgcs = 0
var/highest_del_time = 0
var/highest_del_tickusage = 0
var/list/queue = list() // list of refID's of things that should be garbage collected
// refID's are associated with the time at which they time out and need to be manually del()
// we do this so we aren't constantly locating them and preventing them from being gc'd
var/list/pass_counts
var/list/fail_counts
var/list/tobequeued = list() //We store the references of things to be added to the queue separately so we can spread out GC overhead over a few ticks
var/list/items = list() // Holds our qdel_item statistics datums
var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened.
// the types are stored as strings
var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call
//Queue
var/list/queues
var/list/noqdelhint = list()// list of all types that do not return a QDEL_HINT
// all types that did not respect qdel(A, force=TRUE) and returned one
// of the immortality qdel hints
var/list/noforcerespect = list()
#ifdef TESTING
var/list/qdel_list = list() // list of all types that have been qdel()eted
#endif
/datum/controller/subsystem/garbage/PreInit()
queues = new(GC_QUEUE_COUNT)
pass_counts = new(GC_QUEUE_COUNT)
fail_counts = new(GC_QUEUE_COUNT)
for(var/i in 1 to GC_QUEUE_COUNT)
queues[i] = list()
pass_counts[i] = 0
fail_counts[i] = 0
/datum/controller/subsystem/garbage/stat_entry(msg)
msg += "Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|"
var/list/counts = list()
for (var/list/L in queues)
counts += length(L)
msg += "Q:[counts.Join(",")]|D:[delslasttick]|G:[gcedlasttick]|"
msg += "GR:"
if (!(delslasttick+gcedlasttick))
msg += "n/a|"
@@ -46,116 +50,179 @@ SUBSYSTEM_DEF(garbage)
msg += "n/a|"
else
msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%"
msg += " P:[pass_counts.Join(",")]"
msg += "|F:[fail_counts.Join(",")]"
..(msg)
/datum/controller/subsystem/garbage/Shutdown()
//Adds the del() log to world.log in a format condensable by the runtime condenser found in tools
if(didntgc.len || sleptDestroy.len)
var/list/dellog = list()
for(var/path in didntgc)
dellog += "Path : [path] \n"
dellog += "Failures : [didntgc[path]] \n"
if(path in sleptDestroy)
dellog += "Sleeps : [sleptDestroy[path]] \n"
sleptDestroy -= path
for(var/path in sleptDestroy)
dellog += "Path : [path] \n"
dellog += "Sleeps : [sleptDestroy[path]] \n"
text2file(dellog.Join(), "[GLOB.log_directory]/qdel.log")
//Adds the del() log to the qdel log file
var/list/dellog = list()
//sort by how long it's wasted hard deleting
sortTim(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
for(var/path in items)
var/datum/qdel_item/I = items[path]
dellog += "Path: [path]"
if (I.failures)
dellog += "\tFailures: [I.failures]"
dellog += "\tqdel() Count: [I.qdels]"
dellog += "\tDestroy() Cost: [I.destroy_time]ms"
if (I.hard_deletes)
dellog += "\tTotal Hard Deletes [I.hard_deletes]"
dellog += "\tTime Spent Hard Deleting: [I.hard_delete_time]ms"
if (I.slept_destroy)
dellog += "\tSleeps: [I.slept_destroy]"
if (I.no_respect_force)
dellog += "\tIgnored force: [I.no_respect_force] times"
if (I.no_hint)
dellog += "\tNo hint: [I.no_hint] times"
log_qdel(dellog.Join("\n"))
/datum/controller/subsystem/garbage/fire()
HandleToBeQueued()
if(state == SS_RUNNING)
HandleQueue()
//the fact that this resets its processing each fire (rather then resume where it left off) is intentional.
var/queue = GC_QUEUE_PREQUEUE
while (state == SS_RUNNING)
switch (queue)
if (GC_QUEUE_PREQUEUE)
HandlePreQueue()
queue = GC_QUEUE_PREQUEUE+1
if (GC_QUEUE_CHECK)
HandleQueue(GC_QUEUE_CHECK)
queue = GC_QUEUE_CHECK+1
if (GC_QUEUE_HARDDELETE)
HandleQueue(GC_QUEUE_HARDDELETE)
break
if (state == SS_PAUSED) //make us wait again before the next run.
state = SS_RUNNING
state = SS_RUNNING
//If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond.
//Don't attempt to optimize, not worth the effort.
/datum/controller/subsystem/garbage/proc/HandleToBeQueued()
var/list/tobequeued = src.tobequeued
var/starttime = world.time
var/starttimeofday = world.timeofday
while(tobequeued.len && starttime == world.time && starttimeofday == world.timeofday)
if (MC_TICK_CHECK)
break
var/ref = tobequeued[1]
Queue(ref)
tobequeued.Cut(1, 2)
/datum/controller/subsystem/garbage/proc/HandlePreQueue()
var/list/tobequeued = queues[GC_QUEUE_PREQUEUE]
var/static/count = 0
if (count)
var/c = count
count = 0 //so if we runtime on the Cut, we don't try again.
tobequeued.Cut(1,c+1)
/datum/controller/subsystem/garbage/proc/HandleQueue()
delslasttick = 0
gcedlasttick = 0
var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
var/list/queue = src.queue
var/starttime = world.time
var/starttimeofday = world.timeofday
while(queue.len && starttime == world.time && starttimeofday == world.timeofday)
for (var/ref in tobequeued)
count++
Queue(ref, GC_QUEUE_PREQUEUE+1)
if (MC_TICK_CHECK)
break
var/refID = queue[1]
if (count)
tobequeued.Cut(1,count+1)
count = 0
/datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_CHECK)
if (level == GC_QUEUE_CHECK)
delslasttick = 0
gcedlasttick = 0
var/cut_off_time = world.time - collection_timeout[level] //ignore entries newer then this
var/list/queue = queues[level]
var/static/lastlevel
var/static/count = 0
if (count) //runtime last run before we could do this.
var/c = count
count = 0 //so if we runtime on the Cut, we don't try again.
var/list/lastqueue = queues[lastlevel]
lastqueue.Cut(1, c+1)
lastlevel = level
for (var/refID in queue)
if (!refID)
queue.Cut(1, 2)
count++
if (MC_TICK_CHECK)
break
continue
var/GCd_at_time = queue[refID]
if(GCd_at_time > time_to_kill)
if(GCd_at_time > cut_off_time)
break // Everything else is newer, skip them
queue.Cut(1, 2)
var/datum/A
A = locate(refID)
if (A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
#ifdef GC_FAILURE_HARD_LOOKUP
A.find_references()
#endif
count++
// Something's still referring to the qdel'd object. Kill it.
var/type = A.type
testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --")
didntgc["[type]"]++
HardDelete(A)
var/datum/D
D = locate(refID)
++delslasttick
++totaldels
else
if (!D || D.gc_destroyed != GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
++gcedlasttick
++totalgcs
pass_counts[level]++
if (MC_TICK_CHECK)
break
continue
/datum/controller/subsystem/garbage/proc/QueueForQueuing(datum/A)
if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
tobequeued += A
A.gc_destroyed = GC_QUEUED_FOR_QUEUING
// Something's still referring to the qdel'd object.
fail_counts[level]++
switch (level)
if (GC_QUEUE_CHECK)
#ifdef GC_FAILURE_HARD_LOOKUP
D.find_references()
#endif
var/type = D.type
var/datum/qdel_item/I = items[type]
testing("GC: -- \ref[D] | [type] was unable to be GC'd --")
I.failures++
if (GC_QUEUE_HARDDELETE)
HardDelete(D)
if (MC_TICK_CHECK)
break
continue
/datum/controller/subsystem/garbage/proc/Queue(datum/A)
if (isnull(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0))
Queue(D, level+1)
if (MC_TICK_CHECK)
break
if (count)
queue.Cut(1,count+1)
count = 0
/datum/controller/subsystem/garbage/proc/PreQueue(datum/D)
if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
queues[GC_QUEUE_PREQUEUE] += D
D.gc_destroyed = GC_QUEUED_FOR_QUEUING
/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_CHECK)
if (isnull(D))
return
if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL)
HardDelete(A)
if (D.gc_destroyed == GC_QUEUED_FOR_HARD_DEL)
level = GC_QUEUE_HARDDELETE
if (level > GC_QUEUE_COUNT)
HardDelete(D)
return
var/gctime = world.time
var/refid = "\ref[A]"
A.gc_destroyed = gctime
var/refid = "\ref[D]"
D.gc_destroyed = gctime
var/list/queue = queues[level]
if (queue[refid])
queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list.
queue[refid] = gctime
//this is purely to separate things profile wise.
/datum/controller/subsystem/garbage/proc/HardDelete(datum/A)
//this is mainly to separate things profile wise.
/datum/controller/subsystem/garbage/proc/HardDelete(datum/D)
var/time = world.timeofday
var/tick = TICK_USAGE
var/ticktime = world.time
var/type = A.type
var/refID = "\ref[A]"
del(A)
++delslasttick
++totaldels
var/type = D.type
var/refID = "\ref[D]"
del(D)
tick = (TICK_USAGE-tick+((world.time-ticktime)/world.tick_lag*100))
var/datum/qdel_item/I = items[type]
I.hard_deletes++
I.hard_delete_time += TICK_DELTA_TO_MS(tick)
if (tick > highest_del_tickusage)
highest_del_tickusage = tick
time = world.timeofday - time
@@ -166,18 +233,33 @@ SUBSYSTEM_DEF(garbage)
if (time > 10)
log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)")
message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).")
postpone(time/5)
/datum/controller/subsystem/garbage/proc/HardQueue(datum/A)
if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
tobequeued += A
A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
postpone(time)
/datum/controller/subsystem/garbage/proc/HardQueue(datum/D)
if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
queues[GC_QUEUE_PREQUEUE] += D
D.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
/datum/controller/subsystem/garbage/Recover()
if (istype(SSgarbage.queue))
queue |= SSgarbage.queue
if (istype(SSgarbage.tobequeued))
tobequeued |= SSgarbage.tobequeued
if (istype(SSgarbage.queues))
for (var/i in 1 to SSgarbage.queues.len)
queues[i] |= SSgarbage.queues[i]
/datum/qdel_item
var/name = ""
var/qdels = 0 //Total number of times it's passed thru qdel.
var/destroy_time = 0 //Total amount of milliseconds spent processing this type's Destroy()
var/failures = 0 //Times it was queued for soft deletion but failed to soft delete.
var/hard_deletes = 0 //Different from failures because it also includes QDEL_HINT_HARDDEL deletions
var/hard_delete_time = 0//Total amount of milliseconds spent hard deleting this type.
var/no_respect_force = 0//Number of times it's not respected force=TRUE
var/no_hint = 0 //Number of times it's not even bother to give a qdel hint
var/slept_destroy = 0 //Number of times it's slept in its destroy
/datum/qdel_item/New(mytype)
name = "[mytype]"
// Should be treated as a replacement for the 'del' keyword.
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
@@ -185,21 +267,27 @@ SUBSYSTEM_DEF(garbage)
if(!istype(D))
del(D)
return
#ifdef TESTING
SSgarbage.qdel_list += "[D.type]"
#endif
var/datum/qdel_item/I = SSgarbage.items[D.type]
if (!I)
I = SSgarbage.items[D.type] = new /datum/qdel_item(D.type)
I.qdels++
if(isnull(D.gc_destroyed))
D.SendSignal(COMSIG_PARENT_QDELETED)
D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED
var/start_time = world.time
var/start_tick = world.tick_usage
var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up.
if(world.time != start_time)
SSgarbage.sleptDestroy["[D.type]"]++
I.slept_destroy++
else
I.destroy_time += TICK_USAGE_TO_MS(start_tick)
if(!D)
return
switch(hint)
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
SSgarbage.QueueForQueuing(D)
SSgarbage.PreQueue(D)
if (QDEL_HINT_IWILLGC)
D.gc_destroyed = world.time
return
@@ -209,28 +297,33 @@ SUBSYSTEM_DEF(garbage)
return
// Returning LETMELIVE after being told to force destroy
// indicates the objects Destroy() does not respect force
if(!SSgarbage.noforcerespect["[D.type]"])
SSgarbage.noforcerespect["[D.type]"] = "[D.type]"
#ifdef TESTING
if(!I.no_respect_force)
testing("WARNING: [D.type] has been force deleted, but is \
returning an immortal QDEL_HINT, indicating it does \
not respect the force flag for qdel(). It has been \
placed in the queue, further instances of this type \
will also be queued.")
SSgarbage.QueueForQueuing(D)
#endif
I.no_respect_force++
SSgarbage.PreQueue(D)
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate()
SSgarbage.HardQueue(D)
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
SSgarbage.HardDelete(D)
if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion.
SSgarbage.QueueForQueuing(D)
SSgarbage.PreQueue(D)
#ifdef TESTING
D.find_references()
#endif
else
if(!SSgarbage.noqdelhint["[D.type]"])
SSgarbage.noqdelhint["[D.type]"] = "[D.type]"
#ifdef TESTING
if(!I.no_hint)
testing("WARNING: [D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.")
SSgarbage.QueueForQueuing(D)
#endif
I.no_hint++
SSgarbage.PreQueue(D)
else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic")
@@ -281,15 +374,6 @@ SUBSYSTEM_DEF(garbage)
SSgarbage.can_fire = 1
SSgarbage.next_fire = world.time + world.tick_lag
/client/verb/purge_all_destroyed_objects()
set category = "Debug"
while(SSgarbage.queue.len)
var/datum/o = locate(SSgarbage.queue[1])
if(istype(o) && o.gc_destroyed)
del(o)
SSgarbage.totaldels++
SSgarbage.queue.Cut(1, 2)
/datum/verb/qdel_then_find_references()
set category = "Debug"
set name = "qdel() then Find References"
@@ -300,24 +384,6 @@ SUBSYSTEM_DEF(garbage)
if(!running_find_references)
find_references(TRUE)
/client/verb/show_qdeleted()
set category = "Debug"
set name = "Show qdel() Log"
set desc = "Render the qdel() log and display it"
var/dat = "<B>List of things that have been qdel()eted this round</B><BR><BR>"
var/tmplist = list()
for(var/elem in SSgarbage.qdel_list)
if(!(elem in tmplist))
tmplist[elem] = 0
tmplist[elem]++
for(var/path in tmplist)
dat += "[path] - [tmplist[path]] times<BR>"
usr << browse(dat, "window=qdeletedlog")
/datum/proc/DoSearchVar(X, Xname)
if(usr && usr.client && !usr.client.running_find_references) return
if(istype(X, /datum))
+17 -1
View File
@@ -73,6 +73,8 @@ SUBSYSTEM_DEF(job)
return 0
if(!job.player_old_enough(player.client))
return 0
if(job.required_playtime_remaining(player.client))
return 0
var/position_limit = job.total_positions
if(!latejoin)
position_limit = job.spawn_positions
@@ -95,6 +97,9 @@ SUBSYSTEM_DEF(job)
if(!job.player_old_enough(player.client))
Debug("FOC player not old enough, Player: [player]")
continue
if(job.required_playtime_remaining(player.client))
Debug("FOC player not enough xp, Player: [player]")
continue
if(flag && (!(flag in player.client.prefs.be_special)))
Debug("FOC flag failed, Player: [player], Flag: [flag], ")
continue
@@ -130,6 +135,10 @@ SUBSYSTEM_DEF(job)
Debug("GRJ player not old enough, Player: [player]")
continue
if(job.required_playtime_remaining(player.client))
Debug("GRJ player not enough xp, Player: [player]")
continue
if(player.mind && job.title in player.mind.restricted_roles)
Debug("GRJ incompatible with antagonist role, Player: [player], Job: [job.title]")
continue
@@ -300,6 +309,10 @@ SUBSYSTEM_DEF(job)
Debug("DO player not old enough, Player: [player], Job:[job.title]")
continue
if(job.required_playtime_remaining(player.client))
Debug("DO player not enough xp, Player: [player], Job:[job.title]")
continue
if(player.mind && job.title in player.mind.restricted_roles)
Debug("DO incompatible with antagonist role, Player: [player], Job:[job.title]")
continue
@@ -407,7 +420,7 @@ SUBSYSTEM_DEF(job)
if(job && H)
job.after_spawn(H, M)
handle_roundstart_items(H, M.ckey, H.mind.assigned_role, H.mind.special_role)
return H
@@ -463,6 +476,9 @@ SUBSYSTEM_DEF(job)
if(!job.player_old_enough(player.client))
level6++
continue
if(job.required_playtime_remaining(player.client))
level6++
continue
if(player.client.prefs.GetJobDepartment(job, 1) & job.flag)
level1++
else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag)
+60 -64
View File
@@ -2,15 +2,15 @@ SUBSYSTEM_DEF(persistence)
name = "Persistence"
init_order = INIT_ORDER_PERSISTENCE
flags = SS_NO_FIRE
var/savefile/secret_satchels
var/secret_satchels
var/list/satchel_blacklist = list() //this is a typecache
var/list/new_secret_satchels = list() //these are objects
var/old_secret_satchels = ""
var/list/old_secret_satchels = ""
var/list/obj/structure/chisel_message/chisel_messages = list()
var/list/saved_messages = list()
var/savefile/trophy_sav
var/trophy_sav
var/list/saved_trophies = list()
/datum/controller/subsystem/persistence/Initialize()
@@ -21,67 +21,53 @@ SUBSYSTEM_DEF(persistence)
..()
/datum/controller/subsystem/persistence/proc/LoadSatchels()
secret_satchels = new /savefile("data/npc_saves/SecretSatchels.sav")
secret_satchels = file("data/npc_saves/SecretSatchels[SSmapping.config.map_name].json")
if(!fexists(secret_satchels))
return
satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/crowbar))
secret_satchels[SSmapping.config.map_name] >> old_secret_satchels
var/list/expanded_old_satchels = list()
var/placed_satchels = 0
if(!isnull(old_secret_satchels))
expanded_old_satchels = splittext(old_secret_satchels,"#")
if(PlaceSecretSatchel(expanded_old_satchels))
placed_satchels++
else
expanded_old_satchels.len = 0
var/list/json = list()
json = json_decode(file2text(secret_satchels))
old_secret_satchels = json["data"]
var/placed_satchel = 0
if(old_secret_satchels.len)
if(old_secret_satchels.len >= 20) //guards against low drop pools assuring that one player cannot reliably find his own gear.
var/pos = rand(1, old_secret_satchels.len)
old_secret_satchels.Cut(pos, pos+1)
var/obj/item/storage/backpack/satchel/flat/F = new()
F.x = old_secret_satchels[pos]["x"]
F.y = old_secret_satchels[pos]["y"]
F.z = ZLEVEL_STATION
var/path = text2path(old_secret_satchels[pos]["saved_obj"])
if(!ispath(path))
return
if(isfloorturf(F.loc) && !istype(F.loc, /turf/open/floor/plating/))
F.hide(1)
new path(F)
placed_satchel++
var/list/free_satchels = list()
for(var/turf/T in shuffle(block(locate(TRANSITIONEDGE,TRANSITIONEDGE,ZLEVEL_STATION), locate(world.maxx-TRANSITIONEDGE,world.maxy-TRANSITIONEDGE,ZLEVEL_STATION)))) //Nontrivially expensive but it's roundstart only
if(isfloorturf(T) && !istype(T, /turf/open/floor/plating/))
free_satchels += new /obj/item/storage/backpack/satchel/flat/secret(T)
if(!isemptylist(free_satchels) && ((free_satchels.len + placed_satchels) >= (50 - expanded_old_satchels.len) * 0.1)) //up to six tiles, more than enough to kill anything that moves
if(!isemptylist(free_satchels) && ((free_satchels.len + placed_satchel) >= (50 - old_secret_satchels.len) * 0.1)) //up to six tiles, more than enough to kill anything that moves
break
/datum/controller/subsystem/persistence/proc/PlaceSecretSatchel(list/expanded_old_satchels)
var/satchel_string
if(expanded_old_satchels.len >= 20) //guards against low drop pools assuring that one player cannot reliably find his own gear.
satchel_string = pick_n_take(expanded_old_satchels)
old_secret_satchels = jointext(expanded_old_satchels,"#")
WRITE_FILE(secret_satchels[SSmapping.config.map_name], old_secret_satchels)
var/list/chosen_satchel = splittext(satchel_string,"|")
if(!chosen_satchel || isemptylist(chosen_satchel) || chosen_satchel.len != 3) //Malformed
return 0
var/path = text2path(chosen_satchel[3]) //If the item no longer exist, this returns null
if(!path)
return 0
var/obj/item/storage/backpack/satchel/flat/F = new()
F.x = text2num(chosen_satchel[1])
F.y = text2num(chosen_satchel[2])
F.z = ZLEVEL_STATION
if(isfloorturf(F.loc) && !istype(F.loc, /turf/open/floor/plating/))
F.hide(1)
new path(F)
return 1
/datum/controller/subsystem/persistence/proc/LoadPoly()
for(var/mob/living/simple_animal/parrot/Poly/P in GLOB.living_mob_list)
twitterize(P.speech_buffer, "polytalk")
break //Who's been duping the bird?!
/datum/controller/subsystem/persistence/proc/LoadChiselMessages()
var/savefile/chisel_messages_sav = new /savefile("data/npc_saves/ChiselMessages.sav")
var/saved_json
chisel_messages_sav[SSmapping.config.map_name] >> saved_json
var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json")
if(!fexists(json_file))
return
var/list/json
json = json_decode(file2text(json_file))
if(!saved_json)
if(!json)
return
var/list/saved_messages = json_decode(saved_json)
var/list/saved_messages = json["data"]
for(var/item in saved_messages)
if(!islist(item))
@@ -109,19 +95,16 @@ SUBSYSTEM_DEF(persistence)
log_world("Loaded [saved_messages.len] engraved messages on map [SSmapping.config.map_name]")
/datum/controller/subsystem/persistence/proc/LoadTrophies()
trophy_sav = new /savefile("data/npc_saves/TrophyItems.sav")
var/saved_json
trophy_sav >> saved_json
trophy_sav = file("data/npc_saves/TrophyItems.json")
if(!fexists(trophy_sav))
return
var/list/json = list()
json = json_decode(file2text(trophy_sav))
if(!saved_json)
if(!json)
return
var/decoded_json = json_decode(saved_json)
if(!islist(decoded_json))
return
saved_trophies = decoded_json
saved_trophies = json["data"]
SetUpTrophies(saved_trophies.Copy())
@@ -156,6 +139,7 @@ SUBSYSTEM_DEF(persistence)
CollectTrophies()
/datum/controller/subsystem/persistence/proc/CollectSecretSatchels()
var/list/satchels = list()
for(var/A in new_secret_satchels)
var/obj/item/storage/backpack/satchel/flat/F = A
if(QDELETED(F) || F.z != ZLEVEL_STATION || F.invisibility != INVISIBILITY_MAXIMUM)
@@ -170,25 +154,37 @@ SUBSYSTEM_DEF(persistence)
savable_obj += O.type
if(isemptylist(savable_obj))
continue
old_secret_satchels += "[F.x]|[F.y]|[pick(savable_obj)]#"
WRITE_FILE(secret_satchels[SSmapping.config.map_name], old_secret_satchels)
var/list/data = list()
data["x"] = F.x
data["y"] = F.y
data["saved_obj"] = pick(savable_obj)
satchels += list(data)
var/list/file_data = list()
file_data["data"] = satchels
fdel(secret_satchels)
WRITE_FILE(secret_satchels, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectChiselMessages()
var/savefile/chisel_messages_sav = new /savefile("data/npc_saves/ChiselMessages.sav")
var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json")
for(var/obj/structure/chisel_message/M in chisel_messages)
saved_messages += list(M.pack())
log_world("Saved [saved_messages.len] engraved messages on map [SSmapping.config.map_name]")
WRITE_FILE(chisel_messages_sav[SSmapping.config.map_name], json_encode(saved_messages))
var/list/file_data = list()
file_data["data"] = saved_messages
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/SaveChiselMessage(obj/structure/chisel_message/M)
saved_messages += list(M.pack()) // dm eats one list
/datum/controller/subsystem/persistence/proc/CollectTrophies()
WRITE_FILE(trophy_sav, json_encode(saved_trophies))
var/list/file_data = list()
file_data["data"] = saved_trophies
fdel(trophy_sav)
WRITE_FILE(trophy_sav, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/SaveTrophy(obj/structure/displaycase/trophy/T)
if(!T.added_roundstart && T.showpiece)
@@ -196,4 +192,4 @@ SUBSYSTEM_DEF(persistence)
data["path"] = T.showpiece.type
data["message"] = T.trophy_message
data["placer_key"] = T.placer_key
saved_trophies += list(data)
saved_trophies += list(data)
@@ -0,0 +1,30 @@
diff a/code/controllers/subsystem/server_maint.dm b/code/controllers/subsystem/server_maint.dm (rejected hunks)
@@ -6,18 +6,16 @@ SUBSYSTEM_DEF(server_maint)
flags = SS_POST_FIRE_TIMING|SS_FIRE_IN_LOBBY
priority = 10
var/list/currentrun
- var/triggertime = null
/datum/controller/subsystem/server_maint/Initialize(timeofday)
if (config.hub)
world.visibility = 1
- triggertime = REALTIMEOFDAY
..()
/datum/controller/subsystem/server_maint/fire(resumed = FALSE)
if(!resumed)
src.currentrun = GLOB.clients.Copy()
-
+
var/list/currentrun = src.currentrun
var/round_started = SSticker.HasRoundStarted()
@@ -39,8 +37,3 @@ SUBSYSTEM_DEF(server_maint)
return
#undef PING_BUFFER_TIME
- if(config.sql_enabled)
- sql_poll_population()
- if(config.use_exp_tracking)
- if(REALTIMEOFDAY > (triggertime +3000)) //server maint fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire
- update_exp(10,0)
+2 -2
View File
@@ -341,7 +341,7 @@ SUBSYSTEM_DEF(shuttle)
if(M.request(getDock(destination)))
return 2
else
if(M.dock(getDock(destination)))
if(M.dock(getDock(destination)) != DOCKING_SUCCESS)
return 2
return 0 //dock successful
@@ -356,7 +356,7 @@ SUBSYSTEM_DEF(shuttle)
if(M.request(D))
return 2
else
if(M.dock(D))
if(M.dock(D) != DOCKING_SUCCESS)
return 2
return 0 //dock successful
+8 -2
View File
@@ -465,6 +465,12 @@ SUBSYSTEM_DEF(ticker)
to_chat(world, "<BR><BR><BR><FONT size=3><B>The round has ended.</B></FONT>")
/* var/nocredits = config.no_credits_round_end
for(var/client/C in GLOB.clients)
if(!C.credits && !nocredits)
C.RollCredits()
C.playtitlemusic(40)*/
//Player status report
for(var/mob/Player in GLOB.mob_list)
if(Player.mind && !isnewplayer(Player))
@@ -513,7 +519,7 @@ SUBSYSTEM_DEF(ticker)
//Silicon laws report
for (var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list)
if (aiPlayer.stat != 2 && aiPlayer.mind)
if (aiPlayer.stat != DEAD && aiPlayer.mind)
to_chat(world, "<b>[aiPlayer.name] (Played by: [aiPlayer.mind.key])'s laws at the end of the round were:</b>")
aiPlayer.show_laws(1)
else if (aiPlayer.mind) //if the dead ai has a mind, use its key instead
@@ -533,7 +539,7 @@ SUBSYSTEM_DEF(ticker)
for (var/mob/living/silicon/robot/robo in GLOB.mob_list)
if (!robo.connected_ai && robo.mind)
if (robo.stat != 2)
if (robo.stat != DEAD)
to_chat(world, "<b>[robo.name] (Played by: [robo.mind.key]) survived as an AI-less borg! Its laws were:</b>")
else
to_chat(world, "<b>[robo.name] (Played by: [robo.mind.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:</b>")
+10 -5
View File
@@ -69,7 +69,7 @@ SUBSYSTEM_DEF(timer)
if (ctime_timer.timeToRun <= REALTIMEOFDAY)
--clienttime_timers.len
var/datum/callback/callBack = ctime_timer.callBack
ctime_timer.spent = TRUE
ctime_timer.spent = REALTIMEOFDAY
callBack.InvokeAsync()
qdel(ctime_timer)
else
@@ -105,11 +105,11 @@ SUBSYSTEM_DEF(timer)
if (!callBack)
qdel(timer)
bucket_resolution = null //force bucket recreation
CRASH("Invalid timer: [timer] timer.timeToRun=[timer.timeToRun]||QDELETED(timer)=[QDELETED(timer)]||world.time=[world.time]||head_offset=[head_offset]||practical_offset=[practical_offset]||timer.spent=[timer.spent]")
CRASH("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
if (!timer.spent)
spent += timer
timer.spent = TRUE
timer.spent = world.time
callBack.InvokeAsync()
last_invoke_tick = world.time
@@ -135,9 +135,11 @@ SUBSYSTEM_DEF(timer)
. = "Timer: [TE]"
. += "Prev: [TE.prev ? TE.prev : "NULL"], Next: [TE.next ? TE.next : "NULL"]"
if(TE.spent)
. += ", SPENT"
. += ", SPENT([TE.spent])"
if(QDELETED(TE))
. += ", QDELETED"
if(!TE.callBack)
. += ", NO CALLBACK"
/datum/controller/subsystem/timer/proc/shift_buckets()
var/list/bucket_list = src.bucket_list
@@ -216,7 +218,7 @@ SUBSYSTEM_DEF(timer)
var/timeToRun
var/hash
var/list/flags
var/spent = FALSE //set to true right before running.
var/spent = 0 //time we ran the timer.
var/name //for easy debugging.
//cicular doublely linked list
var/datum/timedevent/next
@@ -243,6 +245,9 @@ SUBSYSTEM_DEF(timer)
name = "Timer: " + num2text(id, 8) + ", TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT")), ", ")], callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])"
if (spent)
CRASH("HOLY JESUS. WHAT IS THAT? WHAT THE FUCK IS THAT?")
if (callBack.object != GLOBAL_PROC)
LAZYADD(callBack.object.active_timers, src)