Files
2025-07-07 18:53:24 +02:00

154 lines
4.0 KiB
Plaintext

//* This file is explicitly licensed under the MIT license. *//
//* Copyright (c) 2023 Citadel Station developers. *//
GLOBAL_LIST_EMPTY(client_data)
/proc/resolve_client_data(ckey, key)
ckey = ckey(ckey) // just in case
if(!islist(GLOB.client_data))
// we CANNOT RUNTIME
GLOB.client_data = list()
if(!istype(GLOB.client_data[ckey], /datum/client_data))
GLOB.client_data[ckey] = new /datum/client_data(ckey, key)
return GLOB.client_data[ckey]
/**
* client data datums, to hold
* round-based data that we don't want wiped
* by a disconnect.
*/
/datum/client_data
/// owner ckey
var/ckey
/// owner key
var/key
/// absolutely, positively annihilated
var/ligma = FALSE
/// byond account join date; null = not loaded
var/account_join
/// byond account age; null = not loaded
var/account_age
/// is guest
var/is_guest
//* externally managed data *//
/// playtime - role string to number of minutes.
var/list/playtime
/// playtime was loaded
var/playtime_loaded = FALSE
/// playtime is loading or flushing
var/playtime_mutex = FALSE
/// playtime - queued for addition
var/list/playtime_queued = list()
/// last REALTIMEOFDAY we did queuing
var/playtime_last
/datum/client_data/New(ckey, key)
src.ckey = ckey
src.key = key
src.playtime_last = REALTIMEOFDAY
is_guest = IsGuestKey(key)
spawn(0)
load_account_age()
var/list/the_cheese_touch = CONFIG_GET(keyed_list/shadowban)
var/client/C = GLOB.directory[src.ckey]
var/why
if("[C.ckey]" in the_cheese_touch)
why = "ckey"
src.ligma = TRUE
else if("[C.computer_id]" in the_cheese_touch)
why = "computerid"
src.ligma = TRUE
else if("[C.address]" in the_cheese_touch)
why = "IP address"
src.ligma = TRUE
if(src.ligma)
log_shadowban("[ckey] autobanned based on [why].")
message_admins(SPAN_DANGER("Automatically shadowbanning [ckey] based on configuration (matched on [why]). Varedit client.persistent.ligma to change this."))
/datum/client_data/proc/block_on_account_age_loaded(timeout = 10 SECONDS)
var/timed_out = world.time + timeout
UNTIL(!isnull(account_age) || world.time > timed_out)
return account_age
/datum/client_data/proc/load_account_age()
if(is_guest)
account_age = 0
return
var/list/http = world.Export("http://byond.com/members/[ckey]?format=text")
if(!http)
log_world("Failed to connect to byond age check for [ckey]")
return
var/F = file2text(http["CONTENT"])
. = null
if(F)
// year-month-day
var/regex/R = regex("joined = \"(\\d{4}-\\d{2}-\\d{2})\"")
if(R.Find(F))
var/str = R.group[1]
account_join = str
if(!SSdbcore.Connect())
account_age = null
return
var/datum/db_query/query = SSdbcore.RunQuery(
"SELECT DATEDIFF(Now(), :date)",
list(
"date" = str,
)
)
if(query.NextRow())
. = text2num(query.item[1])
else
CRASH("Age check regex failed for [src.ckey]")
account_age = .
//* External - Playtime *//
/datum/client_data/proc/load_playtime()
set waitfor = FALSE
if(playtime_loaded)
return
// no args, injection proof; release proccall guard
var/old_usr = usr
usr = null
load_playtime_impl()
usr = old_usr
/datum/client_data/proc/load_playtime_impl()
PRIVATE_PROC(TRUE)
if(playtime_mutex)
return
playtime_mutex = TRUE
playtime = list()
var/player_id
var/client/client = GLOB.directory[ckey]
if(isnull(client))
playtime_mutex = FALSE
return
client.player.block_on_available()
// clients can be deleted at any time.
player_id = client?.player?.player_id
if(isnull(player_id))
playtime_mutex = FALSE
return
var/datum/db_query/query = SSdbcore.NewQuery(
"SELECT `roleid`, `minutes` FROM [DB_PREFIX_TABLE_NAME("playtime")] WHERE player = :player",
list(
"player" = player_id,
)
)
query.Execute()
while(query.NextRow())
playtime[query.item[1]] = text2num(query.item[2])
playtime_loaded = TRUE
playtime_mutex = FALSE
/datum/client_data/proc/block_on_playtime_loaded(timeout = 10 SECONDS)
var/timed_out = world.time + timeout
load_playtime()
UNTIL(playtime_loaded || world.time > timed_out)
return playtime_loaded