Base implementation of /datum/persistent_client (#89449)

## About The Pull Request
Converts `/datum/player_details` into `/datum/persistent_client`.
Persistent Clients persist across connections. The only time a mob's
persistent client will change is if the ckey it's bound to logs into a
different mob, or the mob is deleted (duh).

Also adds PossessByPlayer() so that transfering mob control is cleaner
and makes more immediate sense if you don't know byond-fu.

## Why It's Good For The Game
Clients are an abstract representation of a connection that can be
dropped at almost any moment so putting things that should be stable to
access at any time onto an undying object is ideal. This allows for
future expansions like abstracting away client.screen and managing
everything cleanly.
This commit is contained in:
Kapu1178
2025-02-25 13:52:24 -06:00
committed by GitHub
parent 575972e3f8
commit a0e862d575
87 changed files with 226 additions and 173 deletions
+5 -6
View File
@@ -62,11 +62,10 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_ADMIN, "Show Player Panel", mo
body += "<b>Mob type</b> = [player.type]<br><br>"
if(player.client)
if(HAS_CONNECTED_PLAYER(player))
body += "<b>Old names:</b> "
var/datum/player_details/deets = GLOB.player_details[player.ckey]
if(deets)
body += deets.get_played_names()
if(player.persistent_client)
body += player.persistent_client.get_played_names()
else
body += "<i>None?!</i>"
body += "<br><br>"
@@ -196,7 +195,7 @@ ADMIN_VERB(respawn_character, R_ADMIN, "Respawn Character", "Respawn a player th
var/mob/living/carbon/human/species/monkey/new_monkey = new
SSjob.send_to_late_join(new_monkey)
G_found.mind.transfer_to(new_monkey) //be careful when doing stuff like this! I've already checked the mind isn't in use
new_monkey.key = G_found.key
new_monkey.PossessByPlayer(G_found.key)
to_chat(new_monkey, "You have been fully respawned. Enjoy the game.", confidential = TRUE)
var/msg = span_adminnotice("[key_name_admin(user)] has respawned [new_monkey.key] as a filthy monkey.")
message_admins(msg)
@@ -231,7 +230,7 @@ ADMIN_VERB(respawn_character, R_ADMIN, "Respawn Character", "Respawn a player th
if(is_unassigned_job(new_character.mind.assigned_role))
new_character.mind.set_assigned_role(SSjob.get_job_type(SSjob.overflow_role))
new_character.key = G_found.key
new_character.PossessByPlayer(G_found.key)
/*
The code below functions with the assumption that the mob is already a traitor if they have a special role.
+3 -1
View File
@@ -167,7 +167,9 @@ ADMIN_VERB(cmd_assume_direct_control, R_ADMIN, "Assume Direct Control", "Assume
var/mob/adminmob = user.mob
if(M.ckey)
M.ghostize(FALSE)
M.key = user.key
M.PossessByPlayer(user.key)
user.init_verbs()
if(isobserver(adminmob))
qdel(adminmob)
+2 -2
View File
@@ -174,7 +174,7 @@
var/chosen_outfit = usr.client?.prefs?.read_preference(/datum/preference/choiced/brief_outfit)
usr.client.prefs.safe_transfer_prefs_to(admin_officer, is_antag = TRUE)
admin_officer.equipOutfit(chosen_outfit)
admin_officer.key = usr.key
admin_officer.PossessByPlayer(usr.key)
else
to_chat(usr, span_warning("Could not spawn you in as briefing officer as you are not a ghost!"))
@@ -232,7 +232,7 @@
else
ert_operative = new /mob/living/carbon/human(spawnloc)
chosen_candidate.client.prefs.safe_transfer_prefs_to(ert_operative, is_antag = TRUE)
ert_operative.key = chosen_candidate.key
ert_operative.PossessByPlayer(chosen_candidate.key)
if(ertemplate.enforce_human || !(ert_operative.dna.species.changesource_flags & ERT_SPAWN))
ert_operative.set_species(/datum/species/human)
@@ -48,10 +48,9 @@
dat += "<hr style='background:#000000; border:0; height:1px'>"
var/log_source = M.logging
if(source == LOGSRC_CKEY && M.ckey)
var/datum/player_details/details = GLOB.player_details[M.ckey]
if(details) //we dont want to runtime if an admin aghosted
log_source = details.logging
if(source == LOGSRC_CKEY && M.persistent_client)
log_source = M.persistent_client.logging
var/list/concatenated_logs = list()
for(var/log_type in log_source)
var/nlog_type = text2num(log_type)
@@ -59,6 +58,7 @@
var/list/all_the_entrys = log_source[log_type]
for(var/entry in all_the_entrys)
concatenated_logs += "<b>[entry]</b><br>[all_the_entrys[entry]]"
if(length(concatenated_logs))
sortTim(concatenated_logs, cmp = GLOBAL_PROC_REF(cmp_text_dsc)) //Sort by timestamp.
dat += "<font size=2px>"
+2 -2
View File
@@ -639,7 +639,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w
chosen_candidate = pick(candidates)
candidates -= chosen_candidate
nerd = new /mob/living/basic/drone/classic(spawnpoint)
nerd.key = chosen_candidate.key
nerd.PossessByPlayer(chosen_candidate.key)
nerd.log_message("has been selected as a Nanotrasen emergency response drone.", LOG_GAME)
teamsize--
@@ -716,7 +716,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w
var/mob/chosen = players[1]
if (chosen.client)
chosen.client.prefs.safe_transfer_prefs_to(spawnedMob, is_antag = TRUE)
spawnedMob.key = chosen.key
spawnedMob.PossessByPlayer(chosen.key)
players -= chosen
if (ishuman(spawnedMob) && ispath(humanoutfit, /datum/outfit))
var/mob/living/carbon/human/H = spawnedMob