Improved antag fishing busting (#19542)

This commit is contained in:
AffectedArc07
2022-11-03 18:05:43 +00:00
committed by GitHub
parent 23c3a6c252
commit 21109f3be2
7 changed files with 192 additions and 30 deletions
+3
View File
@@ -73,6 +73,9 @@
/// Note "ckey" for CID info tracking. Do not EVER update this.
#define CIDTRACKING_PSUEDO_CKEY "ALICE-CIDTRACKING"
/// Note "ckey" for roundstart antag rolling tracking. Do not EVER update this.
#define ANTAGTRACKING_PSUEDO_CKEY "ALICE-ANTAGTRACKING"
// Connection types. These match enums in the SQL DB. Dont change them
/// Client was let into the server
#define CONNECTION_TYPE_ESTABLISHED "ESTABLISHED"
+4 -4
View File
@@ -276,12 +276,12 @@ SUBSYSTEM_DEF(jobs)
//Get the players who are ready
for(var/mob/new_player/player in GLOB.player_list)
if(player.ready && player.has_valid_preferences() && player.mind && !player.mind.assigned_role)
if(player.ready && player.mind && !player.mind.assigned_role)
unassigned += player
Debug("DO, Len: [unassigned.len]")
if(unassigned.len == 0)
return 0
if(!length(unassigned))
return FALSE
//Shuffle players and jobs
unassigned = shuffle(unassigned)
@@ -392,7 +392,7 @@ SUBSYSTEM_DEF(jobs)
unassigned -= player
log_debug("Dividing Occupations took [stop_watch(watch)]s")
return 1
return TRUE
/datum/controller/subsystem/jobs/proc/AssignRank(mob/living/carbon/human/H, rank, joined_late = FALSE, log_to_db = TRUE)
if(!H)
+80 -3
View File
@@ -65,6 +65,8 @@ SUBSYSTEM_DEF(ticker)
var/real_reboot_time = 0
/// Datum used to generate the end of round scoreboard.
var/datum/scoreboard/score = null
/// List of ckeys who had antag rolling issues flagged
var/list/flagged_antag_rollers = list()
/datum/controller/subsystem/ticker/Initialize()
login_music = pick(\
@@ -197,13 +199,35 @@ SUBSYSTEM_DEF(ticker)
if(player.client.prefs.toggles2 & PREFTOGGLE_2_RANDOMSLOT)
player.client.prefs.load_random_character_slot(player.client)
// Lets check if people who ready should or shouldnt be
for(var/mob/new_player/P in GLOB.player_list)
// Not logged in
if(!P.client)
continue
// Not ready
if(!P.ready)
continue
// Not set to return if nothing available
if(P.client.prefs.active_character.alternate_option != RETURN_TO_LOBBY)
continue
var/has_antags = (length(P.client.prefs.be_special) > 0)
if(!P.client.prefs.active_character.check_any_job())
to_chat(P, "<span class='danger'>You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.</span>")
if(has_antags)
// We add these to a list so we can deal with them as a batch later
// A lot of DB tracking stuff needs doing, so we may as well async it
flagged_antag_rollers |= P.ckey
P.ready = FALSE
//Configure mode and assign player to special mode stuff
mode.pre_pre_setup()
var/can_continue
can_continue = mode.pre_setup() //Setup special modes
var/can_continue = FALSE
can_continue = mode.pre_setup() //Setup special modes. This also does the antag fishing checks.
SSjobs.DivideOccupations() //Distribute jobs
if(!can_continue)
qdel(mode)
QDEL_NULL(mode)
to_chat(world, "<B>Error setting up [GLOB.master_mode].</B> Reverting to pre-game lobby.")
current_state = GAME_STATE_PREGAME
force_start = FALSE
@@ -307,6 +331,9 @@ SUBSYSTEM_DEF(ticker)
#ifdef UNIT_TESTS
RunUnitTests()
#endif
// Do this 10 second after roundstart because of roundstart lag, and make it more visible
addtimer(CALLBACK(src, .proc/handle_antagfishing_reporting), 10 SECONDS)
return TRUE
@@ -653,3 +680,53 @@ SUBSYSTEM_DEF(ticker)
sleep(sound_length)
world.Reboot()
// Timers invoke this async
/datum/controller/subsystem/ticker/proc/handle_antagfishing_reporting()
// This needs the DB
if(!SSdbcore.IsConnected())
return
// Dont need to do anything
if(!length(flagged_antag_rollers))
return
// Records themselves
var/list/datum/antag_record/records = list()
// Queries to load data (executed as async batch)
var/list/datum/db_query/load_queries = list()
// Queries to save data (executed as async batch)
var/list/datum/db_query/save_queries = list()
for(var/ckey in flagged_antag_rollers)
var/datum/antag_record/AR = new /datum/antag_record(ckey)
records[ckey] = AR
load_queries[ckey] = AR.get_load_query()
// 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
SSdbcore.MassExecute(load_queries, TRUE, FALSE, TRUE)
// Report on things
var/list/log_text = list("The following players attempted to roll antag with no jobs (total infractions listed)")
for(var/ckey in flagged_antag_rollers)
var/datum/antag_record/AR = records[ckey]
AR.handle_data(load_queries[ckey])
save_queries[ckey] = AR.get_save_query()
log_text += "<small>- <a href='?priv_msg=[ckey]'>[ckey]</a>: [AR.infraction_count]</small>"
log_text += "Investigation advised if there are a high number of infractions"
message_admins(log_text.Join("<br>"))
// Now do a ton of saves
SSdbcore.MassExecute(save_queries, TRUE, TRUE, TRUE)
// And cleanup
QDEL_LIST_ASSOC_VAL(load_queries)
records.Cut()
flagged_antag_rollers.Cut()
+1 -1
View File
@@ -240,7 +240,7 @@
// Assemble a list of active players without jobbans.
for(var/mob/new_player/player in GLOB.player_list)
if(player.client && player.ready && player.has_valid_preferences())
if(player.client && player.ready)
if(!jobban_isbanned(player, ROLE_SYNDICATE) && !jobban_isbanned(player, roletext))
if(player_old_enough_antag(player.client,role))
players += player
+103
View File
@@ -0,0 +1,103 @@
//////////////////////////////////////////////////////////////////////////////////////
// This file houses all the code for antag tracking datum serialization and deserialization
//
// If you are going to modify ANYTHING in here, please test it THOROUGHLY
// The serialization and deserialization here is so complicated that you WILL break something here
// PLEASE test things properly if you modify this file. -aa07
//
//////////////////////////////////////////////////////////////////////////////////////
// All these defines are integral to the workings and mesh together with the database.
// DO NOT EDIT THESE UNDER ANY CIRCUMSTANCES EVER
#define ANTAGRECORD_FIRST_INFRACTION "First infraction: "
#define ANTAGRECORD_LAST_INFRACTION "Last infraction: "
#define ANTAGRECORD_TOTAL_INFRACTIONS "Total infractions: "
/datum/antag_record
var/holder_ckey
var/first_infraction_date
var/last_infraction_date
var/infraction_count
var/has_note = FALSE
/datum/antag_record/New(_holder_ckey)
holder_ckey = _holder_ckey
/datum/antag_record/proc/handle_data(datum/db_query/Q)
var/notetxt = null
while(Q.NextRow())
notetxt = Q.item[1]
if(notetxt)
has_note = TRUE
deserialize_and_load(notetxt)
infraction_count++
else
// Sane defaults
first_infraction_date = SQLtime()
infraction_count = 1
last_infraction_date = SQLtime()
// Seems pointless but it allows code to flow smoother
// Also I grew too dependant on languages that have nice syntax stuff
/datum/antag_record/proc/get_load_query()
return SSdbcore.NewQuery("SELECT notetext FROM notes WHERE ckey=:ckey AND adminckey=:ackey LIMIT 1", list(
"ckey" = holder_ckey,
"ackey" = ANTAGTRACKING_PSUEDO_CKEY
))
/*
Expected output below. These are parsed from raw_text by splitting by <br>
[1] ANTAGRECORD_FIRST_INFRACTION
[2] ANTAGRECORD_LAST_INFRACTION
[3] ANTAGRECORD_TOTAL_INFRACTIONS
*/
/datum/antag_record/proc/deserialize_and_load(raw_text)
var/list/lines = splittext(raw_text, "<br>")
// Text
first_infraction_date = splittext(lines[1], ANTAGRECORD_FIRST_INFRACTION)[2]
last_infraction_date = splittext(lines[2], ANTAGRECORD_LAST_INFRACTION)[2]
// Number
infraction_count = text2num(splittext(lines[3], ANTAGRECORD_TOTAL_INFRACTIONS)[2]) // Make sure its a number
/datum/antag_record/proc/get_save_query()
var/serialized_text
var/list/serialized_list = list()
serialized_list.len = 3 // Make it 3 off the bat
serialized_list[1] = "[ANTAGRECORD_FIRST_INFRACTION][first_infraction_date]"
serialized_list[2] = "[ANTAGRECORD_LAST_INFRACTION][last_infraction_date]"
serialized_list[3] = "[ANTAGRECORD_TOTAL_INFRACTIONS][infraction_count]"
serialized_text = serialized_list.Join("<br>")
if(has_note) // They have a note. Update.
var/datum/db_query/update_existing_note = SSdbcore.NewQuery("UPDATE notes SET notetext=:nt, timestamp=NOW(), round_id=:rid WHERE ckey=:ckey AND adminckey=:ackey", list(
"nt" = serialized_text,
"rid" = GLOB.round_id,
"ckey" = holder_ckey,
"ackey" = ANTAGTRACKING_PSUEDO_CKEY
))
return update_existing_note
else
// They dont have a note. Make an insert query.
// To the person who asks "why dont you juse use add_note()"
// By making a query batch, we can use async magic to make it go faster
var/datum/db_query/query_noteadd = SSdbcore.NewQuery({"
INSERT INTO notes (ckey, timestamp, notetext, adminckey, server, round_id, automated)
VALUES (:targetckey, NOW(), :notetext, :adminkey, :server, :roundid, 1)
"}, list(
"targetckey" = holder_ckey,
"notetext" = serialized_text,
"adminkey" = ANTAGTRACKING_PSUEDO_CKEY,
"server" = GLOB.configuration.system.instance_id,
"roundid" = GLOB.round_id,
"automated" = TRUE
))
return query_noteadd
-22
View File
@@ -730,28 +730,6 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM
// Cast to 1/0
return !!(client.prefs.toggles & toggleflag)
// Used to make sure that a player has a valid job preference setup, used to knock players out of eligibility for anything if their prefs don't make sense.
// A "valid job preference setup" in this situation means at least having one job set to low, or not having "return to lobby" enabled
// Prevents "antag rolling" by setting antag prefs on, all jobs to never, and "return to lobby if preferences not availible"
// Doing so would previously allow you to roll for antag, then send you back to lobby if you didn't get an antag role
// This also does some admin notification and logging as well
/mob/proc/has_valid_preferences()
if(!client)
return FALSE //Not sure how this would get run without the mob having a client, but let's just be safe.
if(client.prefs.active_character.alternate_option != RETURN_TO_LOBBY)
return TRUE
// If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so.
var/has_antags = FALSE
if(client.prefs.be_special.len > 0)
has_antags = TRUE
if(!client.prefs.active_character.check_any_job())
to_chat(src, "<span class='danger'>You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.</span>")
if(has_antags)
log_admin("[src.ckey] just got booted back to lobby with no jobs, but antags enabled.")
message_admins("[src.ckey] just got booted back to lobby with no jobs enabled, but antag rolling enabled. Likely antag rolling abuse.")
return FALSE //This is the only case someone should actually be completely blocked from antag rolling as well
return TRUE
/**
* Helper proc to determine if a mob can use emotes that make sound or not.
*/