DB stickies

This commit is contained in:
Letter N
2020-12-23 16:50:48 +08:00
parent 61c8f69be8
commit 47a3afff54
7 changed files with 572 additions and 69 deletions

View File

@@ -91,3 +91,6 @@
#define SPAM_TRIGGER_WARNING 5 //Number of identical messages required before the spam-prevention will warn you to stfu
#define SPAM_TRIGGER_AUTOMUTE 10 //Number of identical messages required before the spam-prevention will automute you
#define STICKYBAN_DB_CACHE_TIME 10 SECONDS
#define STICKYBAN_ROGUE_CHECK_TIME 5

12
code/_globalvars/admin.dm Normal file
View File

@@ -0,0 +1,12 @@
GLOBAL_LIST_EMPTY(stickybanadminexemptions) //stores a list of ckeys exempted from a stickyban (workaround for a bug)
GLOBAL_LIST_EMPTY(stickybanadmintexts) //stores the entire stickyban list temporarily
GLOBAL_VAR(stickbanadminexemptiontimerid) //stores the timerid of the callback that restores all stickybans after an admin joins
// /proc/init_smites() //todo: add on the second wave
// var/list/smites = list()
// for (var/_smite_path in subtypesof(/datum/smite))
// var/datum/smite/smite_path = _smite_path
// smites[initial(smite_path.name)] = smite_path
// return smites
// GLOBAL_LIST_INIT_TYPED(smites, /datum/smite, init_smites())

View File

@@ -177,6 +177,25 @@ SUBSYSTEM_DEF(dbcore)
return FALSE
return new /datum/DBQuery(sql_query, connection)
/datum/controller/subsystem/dbcore/proc/QuerySelect(list/querys, warn = FALSE, qdel = FALSE)
if (!islist(querys))
if (!istype(querys, /datum/DBQuery))
CRASH("Invalid query passed to QuerySelect: [querys]")
querys = list(querys)
for (var/thing in querys)
var/datum/DBQuery/query = thing
if (warn)
INVOKE_ASYNC(query, /datum/DBQuery.proc/warn_execute)
else
INVOKE_ASYNC(query, /datum/DBQuery.proc/Execute)
for (var/thing in querys)
var/datum/DBQuery/query = thing
UNTIL(!query.in_progress)
if (qdel)
qdel(query)
/*
Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query.
Rows missing columns present in other rows will resolve to SQL NULL
@@ -361,5 +380,5 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table
//strip sensitive stuff
if(findtext(message, ": CreateConnection("))
message = "CreateConnection CENSORED"
log_sql("BSQL_DEBUG: [message]")

View File

@@ -1,34 +1,231 @@
SUBSYSTEM_DEF(stickyban)
name = "Sticky Ban"
name = "PRISM"
init_order = INIT_ORDER_STICKY_BAN
flags = SS_NO_FIRE
var/list/cache = list()
var/list/dbcache = list()
var/list/confirmed_exempt = list()
var/dbcacheexpire = 0
/datum/controller/subsystem/stickyban/Initialize(timeofday)
var/list/bannedkeys = world.GetConfig("ban")
if (length(GLOB.stickybanadminexemptions))
restore_stickybans()
var/list/bannedkeys = sticky_banned_ckeys()
//sanitize the sticky ban list
//delete db bans that no longer exist in the database and add new legacy bans to the database
if (SSdbcore.Connect() || length(SSstickyban.dbcache))
if (length(GLOB.stickybanadminexemptions))
restore_stickybans()
for (var/oldban in (world.GetConfig("ban") - bannedkeys))
var/ckey = ckey(oldban)
if (ckey != oldban && (ckey in bannedkeys))
continue
var/list/ban = params2list(world.GetConfig("ban", oldban))
if (ban && !ban["fromdb"])
if (!import_raw_stickyban_to_db(ckey, ban))
log_world("Could not import stickyban on [oldban] into the database. Ignoring")
continue
dbcacheexpire = 0
bannedkeys += ckey
world.SetConfig("ban", oldban, null)
if (length(GLOB.stickybanadminexemptions)) //the previous loop can sleep
restore_stickybans()
for (var/bannedkey in bannedkeys)
var/ckey = ckey(bannedkey)
var/list/ban = stickyban2list(world.GetConfig("ban", bannedkey))
var/list/ban = get_stickyban_from_ckey(bannedkey)
//byond stores sticky bans by key, that can end up confusing things
//i also remove it here so that if any stickybans cause a runtime, they just stop existing
world.SetConfig("ban", bannedkey, null)
//byond stores sticky bans by key, that's lame
if (ckey != bannedkey)
world.SetConfig("ban", bannedkey, null)
if (!ban["ckey"])
ban["ckey"] = ckey
//storing these can break things and isn't needed for sticky ban tracking
ban -= "IP"
ban -= "computer_id"
ban["matches_this_round"] = list()
ban["existing_user_matches_this_round"] = list()
ban["admin_matches_this_round"] = list()
ban["pending_matches_this_round"] = list()
cache[ckey] = ban
for (var/bannedckey in cache)
world.SetConfig("ban", bannedckey, list2stickyban(cache[bannedckey]))
world.SetConfig("ban", ckey, list2stickyban(ban))
return ..()
/datum/controller/subsystem/stickyban/proc/Populatedbcache()
var/newdbcache = list() //so if we runtime or the db connection dies we don't kill the existing cache
// var/datum/db_query/query_stickybans = SSdbcore.NewQuery("SELECT ckey, reason, banning_admin, datetime FROM [format_table_name("stickyban")] ORDER BY ckey")
// var/datum/db_query/query_ckey_matches = SSdbcore.NewQuery("SELECT stickyban, matched_ckey, first_matched, last_matched, exempt FROM [format_table_name("stickyban_matched_ckey")] ORDER BY first_matched")
// var/datum/db_query/query_cid_matches = SSdbcore.NewQuery("SELECT stickyban, matched_cid, first_matched, last_matched FROM [format_table_name("stickyban_matched_cid")] ORDER BY first_matched")
// var/datum/db_query/query_ip_matches = SSdbcore.NewQuery("SELECT stickyban, INET_NTOA(matched_ip), first_matched, last_matched FROM [format_table_name("stickyban_matched_ip")] ORDER BY first_matched")
var/datum/DBQuery/query_stickybans = SSdbcore.NewQuery("SELECT ckey, reason, banning_admin, datetime FROM [format_table_name("stickyban")] ORDER BY ckey")
var/datum/DBQuery/query_ckey_matches = SSdbcore.NewQuery("SELECT stickyban, matched_ckey, first_matched, last_matched, exempt FROM [format_table_name("stickyban_matched_ckey")] ORDER BY first_matched")
var/datum/DBQuery/query_cid_matches = SSdbcore.NewQuery("SELECT stickyban, matched_cid, first_matched, last_matched FROM [format_table_name("stickyban_matched_cid")] ORDER BY first_matched")
var/datum/DBQuery/query_ip_matches = SSdbcore.NewQuery("SELECT stickyban, INET_NTOA(matched_ip), first_matched, last_matched FROM [format_table_name("stickyban_matched_ip")] ORDER BY first_matched")
SSdbcore.QuerySelect(list(query_stickybans, query_ckey_matches, query_cid_matches, query_ip_matches))
if (query_stickybans.last_error)
qdel(query_stickybans)
qdel(query_ckey_matches)
qdel(query_cid_matches)
qdel(query_ip_matches)
return
while (query_stickybans.NextRow())
var/list/ban = list()
ban["ckey"] = query_stickybans.item[1]
ban["message"] = query_stickybans.item[2]
ban["reason"] = "(InGameBan)([query_stickybans.item[3]])"
ban["admin"] = query_stickybans.item[3]
ban["datetime"] = query_stickybans.item[4]
ban["type"] = list("sticky")
newdbcache["[query_stickybans.item[1]]"] = ban
if (!query_ckey_matches.last_error)
while (query_ckey_matches.NextRow())
var/list/match = list()
match["stickyban"] = query_ckey_matches.item[1]
match["matched_ckey"] = query_ckey_matches.item[2]
match["first_matched"] = query_ckey_matches.item[3]
match["last_matched"] = query_ckey_matches.item[4]
match["exempt"] = text2num(query_ckey_matches.item[5])
var/ban = newdbcache[query_ckey_matches.item[1]]
if (!ban)
continue
var/keys = ban[text2num(query_ckey_matches.item[5]) ? "whitelist" : "keys"]
if (!keys)
keys = ban[text2num(query_ckey_matches.item[5]) ? "whitelist" : "keys"] = list()
keys[query_ckey_matches.item[2]] = match
if (!query_cid_matches.last_error)
while (query_cid_matches.NextRow())
var/list/match = list()
match["stickyban"] = query_cid_matches.item[1]
match["matched_cid"] = query_cid_matches.item[2]
match["first_matched"] = query_cid_matches.item[3]
match["last_matched"] = query_cid_matches.item[4]
var/ban = newdbcache[query_cid_matches.item[1]]
if (!ban)
continue
var/computer_ids = ban["computer_id"]
if (!computer_ids)
computer_ids = ban["computer_id"] = list()
computer_ids[query_cid_matches.item[2]] = match
if (!query_ip_matches.last_error)
while (query_ip_matches.NextRow())
var/list/match = list()
match["stickyban"] = query_ip_matches.item[1]
match["matched_ip"] = query_ip_matches.item[2]
match["first_matched"] = query_ip_matches.item[3]
match["last_matched"] = query_ip_matches.item[4]
var/ban = newdbcache[query_ip_matches.item[1]]
if (!ban)
continue
var/IPs = ban["IP"]
if (!IPs)
IPs = ban["IP"] = list()
IPs[query_ip_matches.item[2]] = match
dbcache = newdbcache
dbcacheexpire = world.time+STICKYBAN_DB_CACHE_TIME
qdel(query_stickybans)
qdel(query_ckey_matches)
qdel(query_cid_matches)
qdel(query_ip_matches)
/datum/controller/subsystem/stickyban/proc/import_raw_stickyban_to_db(ckey, list/ban)
. = FALSE
if (!ban["admin"])
ban["admin"] = "LEGACY"
if (!ban["message"])
ban["message"] = "Evasion"
// TODO: USE NEW DB IMPLEMENTATION
var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery(
"INSERT IGNORE INTO [format_table_name("stickyban")] (ckey, reason, banning_admin) VALUES ([ckey], [ban["message"]], ban["admin"]))"
)
if (query_create_stickyban.warn_execute())
qdel(query_create_stickyban)
return
qdel(query_create_stickyban)
// var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery(
// "INSERT IGNORE INTO [format_table_name("stickyban")] (ckey, reason, banning_admin) VALUES (:ckey, :message, :admin)",
// list("ckey" = ckey, "message" = ban["message"], "admin" = ban["admin"])
// )
// if (!query_create_stickyban.warn_execute())
// qdel(query_create_stickyban)
// return
// qdel(query_create_stickyban)
var/list/sqlckeys = list()
var/list/sqlcids = list()
var/list/sqlips = list()
if (ban["keys"])
var/list/keys = splittext(ban["keys"], ",")
for (var/key in keys)
var/list/sqlckey = list()
sqlckey["stickyban"] = ckey
sqlckey["matched_ckey"] = ckey(key)
sqlckey["exempt"] = FALSE
sqlckeys[++sqlckeys.len] = sqlckey
if (ban["whitelist"])
var/list/keys = splittext(ban["whitelist"], ",")
for (var/key in keys)
var/list/sqlckey = list()
sqlckey["stickyban"] = ckey
sqlckey["matched_ckey"] = ckey(key)
sqlckey["exempt"] = TRUE
sqlckeys[++sqlckeys.len] = sqlckey
if (ban["computer_id"])
var/list/cids = splittext(ban["computer_id"], ",")
for (var/cid in cids)
var/list/sqlcid = list()
sqlcid["stickyban"] = ckey
sqlcid["matched_cid"] = cid
sqlcids[++sqlcids.len] = sqlcid
if (ban["IP"])
var/list/ips = splittext(ban["IP"], ",")
for (var/ip in ips)
var/list/sqlip = list()
sqlip["stickyban"] = ckey
sqlip["matched_ip"] = ip
sqlips[++sqlips.len] = sqlip
if (length(sqlckeys))
SSdbcore.MassInsert(format_table_name("stickyban_matched_ckey"), sqlckeys, ignore_errors = TRUE)
if (length(sqlcids))
SSdbcore.MassInsert(format_table_name("stickyban_matched_cid"), sqlcids, ignore_errors = TRUE)
if (length(sqlips))
SSdbcore.MassInsert(format_table_name("stickyban_matched_ip"), sqlips, ignore_errors = TRUE)
return TRUE

View File

@@ -226,6 +226,14 @@
key_cache[key] = 0
return .
/proc/restore_stickybans()
for (var/banned_ckey in GLOB.stickybanadmintexts)
world.SetConfig("ban", banned_ckey, GLOB.stickybanadmintexts[banned_ckey])
GLOB.stickybanadminexemptions = list()
GLOB.stickybanadmintexts = list()
if (GLOB.stickbanadminexemptiontimerid)
deltimer(GLOB.stickbanadminexemptiontimerid)
GLOB.stickbanadminexemptiontimerid = null
#undef STICKYBAN_MAX_MATCHES
#undef STICKYBAN_MAX_EXISTING_USER_MATCHES

View File

@@ -7,7 +7,7 @@
if ("add")
var/list/ban = list()
var/ckey
ban["admin"] = usr.key
ban["admin"] = usr.ckey
ban["type"] = list("sticky")
ban["reason"] = "(InGameBan)([usr.key])" //this will be displayed in dd only
@@ -21,7 +21,8 @@
ban["ckey"] = ckey
if (get_stickyban_from_ckey(ckey))
to_chat(usr, "<span class='adminnotice'>Error: Can not add a stickyban: User already has a current sticky ban</span>")
to_chat(usr, "<span class='adminnotice'>Error: Can not add a stickyban: User already has a current sticky ban</span>", confidential = TRUE)
return
if (data["reason"])
ban["message"] = data["reason"]
@@ -31,7 +32,26 @@
return
ban["message"] = "[reason]"
if(SSdbcore.Connect()) // todo: second wave
// var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery({"
// INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
// VALUES (:ckey, :message, :banning_admin)
// "}, list("ckey" = ckey, "message" = ban["message"], "banning_admin" = usr.ckey))
var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
VALUES ([ckey], [ban["message"]], [usr.ckey])
"})
if (query_create_stickyban.warn_execute())
ban["fromdb"] = TRUE
qdel(query_create_stickyban)
world.SetConfig("ban",ckey,list2stickyban(ban))
ban = stickyban2list(list2stickyban(ban))
ban["matches_this_round"] = list()
ban["existing_user_matches_this_round"] = list()
ban["admin_matches_this_round"] = list()
ban["pending_matches_this_round"] = list()
SSstickyban.cache[ckey] = ban
log_admin_private("[key_name(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has stickybanned [ckey].\nReason: [ban["message"]]</span>")
@@ -43,14 +63,29 @@
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
if (alert("Are you sure you want to remove the sticky ban on [ckey]?","Are you sure","Yes","No") == "No")
return
if (!get_stickyban_from_ckey(ckey))
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
world.SetConfig("ban",ckey, null)
SSstickyban.cache -= ckey
if (SSdbcore.Connect())
// SSdbcore.QuerySelect(list(
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = :ckey", list("ckey" = ckey)),
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey", list("ckey" = ckey)),
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = :ckey", list("ckey" = ckey)),
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = :ckey", list("ckey" = ckey))
// ), warn = TRUE, qdel = TRUE)
SSdbcore.QuerySelect(list(
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = [ckey]"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey]"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = [ckey]"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = [ckey]")
), warn = TRUE, qdel = TRUE)
log_admin_private("[key_name(usr)] removed [ckey]'s stickyban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed [ckey]'s stickyban</span>")
@@ -64,42 +99,45 @@
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/found = 0
//we have to do it this way because byond keeps the case in its sticky ban matches WHY!!!
for (var/key in ban["keys"])
if (ckey(key) == alt)
found = 1
break
if (!found)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>")
var/key = LAZYACCESS(ban["keys"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>", confidential = TRUE)
return
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them","Are you sure","Yes","No") == "No")
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them, Use \[E] to exempt them","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
found = 0
for (var/key in ban["keys"])
if (ckey(key) == alt)
ban["keys"] -= key
found = 1
break
key = LAZYACCESS(ban["keys"], alt)
if (!found)
to_chat(usr, "<span class='adminnotice'>Error: [alt] link to [ckey]'s sticky ban disappeared.</span>")
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] link to [ckey]'s sticky ban disappeared.</span>", confidential = TRUE)
return
LAZYREMOVE(ban["keys"], alt)
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_remove_stickyban_alt = SSdbcore.NewQuery(
// "DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey AND matched_ckey = :alt",
// list("ckey" = ckey, "alt" = alt)
// )
var/datum/DBQuery/query_remove_stickyban_alt = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey] AND matched_ckey = [alt]"
)
query_remove_stickyban_alt.warn_execute()
qdel(query_remove_stickyban_alt)
log_admin_private("[key_name(usr)] has disassociated [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has disassociated [alt] from [ckey]'s sticky ban</span>")
@@ -109,7 +147,7 @@
var/ckey = data["ckey"]
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/oldreason = ban["message"]
var/reason = input(usr,"Reason","Reason","[ban["message"]]") as text|null
@@ -118,28 +156,203 @@
//we have to do this again incase something changed while we waited for input
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
ban["message"] = "[reason]"
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_edit_stickyban = SSdbcore.NewQuery(
// "UPDATE [format_table_name("stickyban")] SET reason = :reason WHERE ckey = :ckey",
// list("reason" = reason, "ckey" = ckey)
// )
var/datum/DBQuery/query_edit_stickyban = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban")] SET reason = [reason] WHERE ckey = [ckey]"
)
query_edit_stickyban.warn_execute()
qdel(query_edit_stickyban)
log_admin_private("[key_name(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]</span>")
if ("exempt")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (!data["alt"])
return
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/key = LAZYACCESS(ban["keys"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>", confidential = TRUE)
return
if (alert("Are you sure you want to exempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
key = LAZYACCESS(ban["keys"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt]'s link to [ckey]'s sticky ban disappeared.</span>", confidential = TRUE)
return
LAZYREMOVE(ban["keys"], alt)
key["exempt"] = TRUE
LAZYSET(ban["whitelist"], alt, key)
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_exempt_stickyban_alt = SSdbcore.NewQuery(
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = :ckey AND matched_ckey = :alt",
// list("ckey" = ckey, "alt" = alt)
// )
var/datum/DBQuery/query_exempt_stickyban_alt = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
)
query_exempt_stickyban_alt.warn_execute()
qdel(query_exempt_stickyban_alt)
log_admin_private("[key_name(usr)] has exempted [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has exempted [alt] from [ckey]'s sticky ban</span>")
if ("unexempt")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (!data["alt"])
return
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/key = LAZYACCESS(ban["whitelist"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not exempt from [ckey]'s sticky ban!</span>", confidential = TRUE)
return
if (alert("Are you sure you want to unexempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
key = LAZYACCESS(ban["whitelist"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt]'s exemption from [ckey]'s sticky ban disappeared.</span>", confidential = TRUE)
return
LAZYREMOVE(ban["whitelist"], alt)
key["exempt"] = FALSE
LAZYSET(ban["keys"], alt, key)
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = :ckey AND matched_ckey = :alt",
// list("ckey" = ckey, "alt" = alt)
// )
var/datum/DBQuery/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
)
query_unexempt_stickyban_alt.warn_execute()
qdel(query_unexempt_stickyban_alt)
log_admin_private("[key_name(usr)] has unexempted [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has unexempted [alt] from [ckey]'s sticky ban</span>")
if ("timeout")
if (!data["ckey"])
return
if (!SSdbcore.Connect())
to_chat(usr, "<span class='adminnotice'>No database connection!</span>", confidential = TRUE)
return
var/ckey = data["ckey"]
if (alert("Are you sure you want to put [ckey]'s stickyban on timeout until next round (or removed)?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
ban["timeout"] = TRUE
world.SetConfig("ban", ckey, null)
var/cachedban = SSstickyban.cache[ckey]
if (cachedban)
cachedban["timeout"] = TRUE
log_admin_private("[key_name(usr)] has put [ckey]'s sticky ban on timeout.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has put [ckey]'s sticky ban on timeout.</span>")
if ("untimeout")
if (!data["ckey"])
return
if (!SSdbcore.Connect())
to_chat(usr, "<span class='adminnotice'>No database connection!</span>", confidential = TRUE)
return
var/ckey = data["ckey"]
if (alert("Are you sure you want to lift the timeout on [ckey]'s stickyban?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
var/cachedban = SSstickyban.cache[ckey]
if (cachedban)
cachedban["timeout"] = FALSE
if (!ban)
if (!cachedban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
ban = cachedban
ban["timeout"] = FALSE
world.SetConfig("ban",ckey,list2stickyban(ban))
log_admin_private("[key_name(usr)] has taken [ckey]'s sticky ban off of timeout.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has taken [ckey]'s sticky ban off of timeout.</span>")
if ("revert")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start?","Are you sure","Yes","No") == "No")
if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start (or last edit)?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/cached_ban = SSstickyban.cache[ckey]
if (!cached_ban)
to_chat(usr, "<span class='adminnotice'>Error: No cached sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No cached sticky ban for [ckey] found!</span>", confidential = TRUE)
world.SetConfig("ban",ckey,null)
log_admin_private("[key_name(usr)] has reverted [ckey]'s sticky ban to its state at round start.")
@@ -150,14 +363,22 @@
world.SetConfig("ban",ckey,list2stickyban(cached_ban))
/datum/admins/proc/stickyban_gethtml(ckey, ban)
. = {"
/datum/admins/proc/stickyban_gethtml(ckey)
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
return
var/timeout
if (SSdbcore.Connect())
timeout = "<a href='?_src_=holder;[HrefToken()];stickyban=[(ban["timeout"] ? "untimeout" : "timeout")]&ckey=[ckey]'>\[[(ban["timeout"] ? "untimeout" : "timeout" )]\]</a>"
else
timeout = "<a href='?_src_=holder;[HrefToken()];stickyban=revert&ckey=[ckey]'>\[revert\]</a>"
. = list({"
<a href='?_src_=holder;[HrefToken()];stickyban=remove&ckey=[ckey]'>\[-\]</a>
<a href='?_src_=holder;[HrefToken()];stickyban=revert&ckey=[ckey]'>\[revert\]</a>
[timeout]
<b>[ckey]</b>
<br />"
[ban["message"]] <b><a href='?_src_=holder;[HrefToken()];stickyban=edit&ckey=[ckey]'>\[Edit\]</a></b><br />
"}
"})
if (ban["admin"])
. += "[ban["admin"]]<br />"
else
@@ -166,19 +387,24 @@
for (var/key in ban["keys"])
if (ckey(key) == ckey)
continue
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]</li>"
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]<a href='?_src_=holder;[HrefToken()];stickyban=exempt&ckey=[ckey]&alt=[ckey(key)]'>\[E\]</a></li>"
for (var/key in ban["whitelist"])
if (ckey(key) == ckey)
continue
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]<a href='?_src_=holder;[HrefToken()];stickyban=unexempt&ckey=[ckey]&alt=[ckey(key)]'>\[UE\]</a></li>"
. += "</ol>\n"
/datum/admins/proc/stickyban_show()
if(!check_rights(R_BAN))
return
var/list/bans = sortList(world.GetConfig("ban"))
var/banhtml = ""
var/list/bans = sticky_banned_ckeys()
var/list/banhtml = list()
for(var/key in bans)
var/ckey = ckey(key)
var/ban = stickyban2list(world.GetConfig("ban",key))
banhtml += "<br /><hr />\n"
banhtml += stickyban_gethtml(ckey,ban)
banhtml += stickyban_gethtml(ckey)
var/html = {"
<head>
@@ -186,22 +412,49 @@
</head>
<body>
<h2>All Sticky Bans:</h2> <a href='?_src_=holder;[HrefToken()];stickyban=add'>\[+\]</a><br>
[banhtml]
[banhtml.Join("")]
</body>
"}
usr << browse(html,"window=stickybans;size=700x400")
/proc/get_stickyban_from_ckey(var/ckey)
/proc/sticky_banned_ckeys()
if (SSdbcore.Connect() || length(SSstickyban.dbcache))
if (SSstickyban.dbcacheexpire < world.time)
SSstickyban.Populatedbcache()
if (SSstickyban.dbcacheexpire)
return SSstickyban.dbcache.Copy()
return sortList(world.GetConfig("ban"))
/proc/get_stickyban_from_ckey(ckey)
. = list()
if (!ckey)
return null
ckey = ckey(ckey)
. = null
for (var/key in world.GetConfig("ban"))
if (ckey(key) == ckey)
. = stickyban2list(world.GetConfig("ban",key))
break
if (SSdbcore.Connect() || length(SSstickyban.dbcache))
if (SSstickyban.dbcacheexpire < world.time)
SSstickyban.Populatedbcache()
if (SSstickyban.dbcacheexpire)
. = SSstickyban.dbcache[ckey]
//reset the cache incase its a newer ban (but only if we didn't update the cache recently)
if (!. && SSstickyban.dbcacheexpire != world.time+STICKYBAN_DB_CACHE_TIME)
SSstickyban.dbcacheexpire = 1
SSstickyban.Populatedbcache()
. = SSstickyban.dbcache[ckey]
if (.)
var/list/cachedban = SSstickyban.cache["[ckey]"]
if (cachedban)
.["timeout"] = cachedban["timeout"]
/proc/stickyban2list(var/ban)
.["fromdb"] = TRUE
return
. = stickyban2list(world.GetConfig("ban", ckey)) || stickyban2list(world.GetConfig("ban", ckey(ckey))) || list()
if (!length(.))
return null
/proc/stickyban2list(ban, strictdb = TRUE)
if (!ban)
return null
. = params2list(ban)
@@ -212,30 +465,40 @@
var/ckey = ckey(key)
ckeys[ckey] = ckey //to make searching faster.
.["keys"] = ckeys
if (.["whitelist"])
var/keys = splittext(.["whitelist"], ",")
var/ckeys = list()
for (var/key in keys)
var/ckey = ckey(key)
ckeys[ckey] = ckey //to make searching faster.
.["whitelist"] = ckeys
.["type"] = splittext(.["type"], ",")
.["IP"] = splittext(.["IP"], ",")
.["computer_id"] = splittext(.["computer_id"], ",")
. -= "fromdb"
/proc/list2stickyban(var/list/ban)
/proc/list2stickyban(list/ban)
if (!ban || !islist(ban))
return null
. = ban.Copy()
if (.["keys"])
.["keys"] = jointext(.["keys"], ",")
if (.["IP"])
.["IP"] = jointext(.["IP"], ",")
if (.["computer_id"])
.["computer_id"] = jointext(.["computer_id"], ",")
if (.["whitelist"])
.["whitelist"] = jointext(.["whitelist"], ",")
if (.["type"])
.["type"] = jointext(.["type"], ",")
//internal tracking only, shouldn't be stored
. -= "reverting"
. -= "matches_this_round"
. -= "existing_user_matches_this_round"
. -= "admin_matches_this_round"
. -= "matches_this_round"
. -= "reverting"
. -= "pending_matches_this_round"
//storing these can sometimes cause sticky bans to start matching everybody
// and isn't even needed for sticky ban matching, as the hub tracks these separately
. -= "IP"
. -= "computer_id"
. = list2params(.)

View File

@@ -202,6 +202,7 @@
#include "code\__HELPERS\sorts\InsertSort.dm"
#include "code\__HELPERS\sorts\MergeSort.dm"
#include "code\__HELPERS\sorts\TimSort.dm"
#include "code\_globalvars\admin.dm"
#include "code\_globalvars\bitfields.dm"
#include "code\_globalvars\configuration.dm"
#include "code\_globalvars\game_modes.dm"