diff --git a/code/__DEFINES/_helpers.dm b/code/__DEFINES/_helpers.dm
index 4560dac0d8e..3c6fb807310 100644
--- a/code/__DEFINES/_helpers.dm
+++ b/code/__DEFINES/_helpers.dm
@@ -49,3 +49,6 @@
/// A null statement to guard against EmptyBlock lint without necessitating the use of pass()
/// Used to avoid proc-call overhead. But use sparingly. Probably pointless in most places.
#define EMPTY_BLOCK_GUARD ;
+
+/// Abstraction over using mob.client to just check if there's a connected player.
+#define HAS_CONNECTED_PLAYER(mob) (mob.client)
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 4d2dd6da94d..0635a24da18 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -184,7 +184,7 @@
ghost_player.client.prefs.safe_transfer_prefs_to(new_character)
new_character.dna.update_dna_identity()
- new_character.key = ghost_player.key
+ new_character.PossessByPlayer(ghost_player.ckey)
return new_character
diff --git a/code/__HELPERS/logging/mob.dm b/code/__HELPERS/logging/mob.dm
index b6bebf74f68..f5f51256f32 100644
--- a/code/__HELPERS/logging/mob.dm
+++ b/code/__HELPERS/logging/mob.dm
@@ -18,11 +18,10 @@
// Cannot use the list as a map if the key is a number, so we stringify it (thank you BYOND)
var/smessage_type = num2text(message_type, MAX_BITFLAG_DIGITS)
- var/datum/player_details/client_details = client?.player_details
- if(!isnull(client_details))
- if(!islist(client_details.logging[smessage_type]))
- client_details.logging[smessage_type] = list()
+ if(HAS_CONNECTED_PLAYER(src))
+ if(!islist(persistent_client.logging[smessage_type]))
+ persistent_client.logging[smessage_type] = list()
if(!islist(logging[smessage_type]))
logging[smessage_type] = list()
@@ -51,7 +50,7 @@
logging[smessage_type] += timestamped_message
- if(client)
- client.player_details.logging[smessage_type] += timestamped_message
+ if(HAS_CONNECTED_PLAYER(src))
+ persistent_client.logging[smessage_type] += timestamped_message
..()
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 51b6f066d5e..3f846748dd3 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -661,7 +661,7 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
/datum/controller/subsystem/ticker/proc/give_show_report_button(client/C)
var/datum/action/report/R = new
- C.player_details.player_actions += R
+ C.persistent_client.player_actions += R
R.Grant(C.mob)
to_chat(C,span_infoplain("Show roundend report again"))
diff --git a/code/controllers/subsystem/achievements.dm b/code/controllers/subsystem/achievements.dm
index 3dad9ef824c..6a1cc5dd8bd 100644
--- a/code/controllers/subsystem/achievements.dm
+++ b/code/controllers/subsystem/achievements.dm
@@ -59,10 +59,9 @@ SUBSYSTEM_DEF(achievements)
most_unlocked_achievement = instance
qdel(query)
- for(var/i in GLOB.clients)
- var/client/C = i
- if(!C.player_details.achievements.initialized)
- C.player_details.achievements.InitializeData()
+ for(var/client/C in GLOB.clients)
+ if(!C.persistent_client.achievements.initialized)
+ C.persistent_client.achievements.InitializeData()
return SS_INIT_SUCCESS
@@ -71,11 +70,12 @@ SUBSYSTEM_DEF(achievements)
/datum/controller/subsystem/achievements/proc/save_achievements_to_db()
var/list/cheevos_to_save = list()
- for(var/ckey in GLOB.player_details)
- var/datum/player_details/PD = GLOB.player_details[ckey]
+ for(var/ckey in GLOB.persistent_clients_by_ckey)
+ var/datum/persistent_client/PD = GLOB.persistent_clients_by_ckey[ckey]
if(!PD || !PD.achievements)
continue
cheevos_to_save += PD.achievements.get_changed_data()
+
if(!length(cheevos_to_save))
return
SSdbcore.MassInsert(format_table_name("achievements"), cheevos_to_save, duplicate_key = TRUE)
diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm
index e869f21c615..628b192b72c 100644
--- a/code/controllers/subsystem/blackbox.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -86,9 +86,8 @@ SUBSYSTEM_DEF(blackbox)
if (MS.rc_msgs.len)
record_feedback("tally", "radio_usage", MS.rc_msgs.len, "request console")
- for(var/player_key in GLOB.player_details)
- var/datum/player_details/PD = GLOB.player_details[player_key]
- record_feedback("tally", "client_byond_version", 1, PD.full_byond_version())
+ for(var/datum/persistent_client/PC as anything in GLOB.persistent_clients)
+ record_feedback("tally", "client_byond_version", 1, PC.full_byond_version())
/datum/controller/subsystem/blackbox/Shutdown()
sealed = FALSE
diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
index f9ea045820f..49bc23a6c5d 100644
--- a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
@@ -538,7 +538,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/generate_ruleset_body(mob/applicant)
var/obj/vent = pick_n_take(vents)
var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc)
- new_xeno.key = applicant.key
+ new_xeno.PossessByPlayer(applicant.ckey)
new_xeno.move_into_vent(vent)
message_admins("[ADMIN_LOOKUPFLW(new_xeno)] has been made into an alien by the midround ruleset.")
log_dynamic("[key_name(new_xeno)] was spawned as an alien by the midround ruleset.")
@@ -696,7 +696,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/space_ninja/generate_ruleset_body(mob/applicant)
var/mob/living/carbon/human/ninja = create_space_ninja(pick(spawn_locs))
- ninja.key = applicant.key
+ ninja.PossessByPlayer(applicant.ckey)
ninja.mind.add_antag_datum(/datum/antagonist/ninja)
message_admins("[ADMIN_LOOKUPFLW(ninja)] has been made into a Space Ninja by the midround ruleset.")
@@ -769,7 +769,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/revenant/generate_ruleset_body(mob/applicant)
var/mob/living/basic/revenant/revenant = new(pick(spawn_locs))
- revenant.key = applicant.key
+ revenant.PossessByPlayer(applicant.ckey)
message_admins("[ADMIN_LOOKUPFLW(revenant)] has been made into a revenant by the midround ruleset.")
log_game("[key_name(revenant)] was spawned as a revenant by the midround ruleset.")
return revenant
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index 5f2134acb0a..84880a47948 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -240,7 +240,7 @@ SUBSYSTEM_DEF(vote)
voting_action.name = "Vote: [current_vote.override_question || current_vote.name]"
voting_action.Grant(new_voter.mob)
- new_voter.player_details.player_actions += voting_action
+ new_voter.persistent_client.player_actions += voting_action
generated_actions += voting_action
if(current_vote.vote_sound && new_voter.prefs.read_preference(/datum/preference/toggle/sound_announcements))
SEND_SOUND(new_voter, sound(current_vote.vote_sound))
@@ -472,12 +472,12 @@ SUBSYSTEM_DEF(vote)
// We also need to remove our action from the player actions when we're cleaning up.
/datum/action/vote/Remove(mob/removed_from)
- if(removed_from.client)
- removed_from.client?.player_details.player_actions -= src
+ if(removed_from.persistent_client)
+ removed_from.persistent_client.player_actions -= src
else if(removed_from.ckey)
- var/datum/player_details/associated_details = GLOB.player_details[removed_from.ckey]
- associated_details?.player_actions -= src
+ var/datum/persistent_client/persistent_client = GLOB.persistent_clients_by_ckey[removed_from.ckey]
+ persistent_client?.player_actions -= src
return ..()
diff --git a/code/datums/achievements/_achievement_data.dm b/code/datums/achievements/_achievement_data.dm
index 62d6f6061b0..eeaa5b6edee 100644
--- a/code/datums/achievements/_achievement_data.dm
+++ b/code/datums/achievements/_achievement_data.dm
@@ -131,4 +131,4 @@
set name = "Check achievements"
set desc = "See all of your achievements!"
- player_details.achievements.ui_interact(usr)
+ persistent_client.achievements.ui_interact(usr)
diff --git a/code/datums/achievements/misc_scores.dm b/code/datums/achievements/misc_scores.dm
index 292a74e768a..63d8835e613 100644
--- a/code/datums/achievements/misc_scores.dm
+++ b/code/datums/achievements/misc_scores.dm
@@ -37,14 +37,16 @@
/datum/award/score/progress/fish/proc/validate_early_joiners(datum/source)
for(var/client/client as anything in GLOB.clients)
- var/datum/achievement_data/holder = client.player_details.achievements
+ var/datum/achievement_data/holder = client.persistent_client.achievements
if(!holder?.initialized)
continue
+
var/list/entries = holder.data[/datum/award/score/progress/fish]
var/list_copied = FALSE
for(var/fish_id in entries)
if(SSfishing.catchable_fish[fish_id])
continue
+
//make a new list, unbound from the cached awards data, so that the score can be updated at the end of the round.
if(!list_copied)
entries = entries.Copy()
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index 6945648a734..a2ce01fc55d 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -69,7 +69,7 @@
qdel(src)
return
- friend.key = ghost.key
+ friend.PossessByPlayer(ghost.ckey)
friend.attach_to_owner(owner)
friend.setup_appearance()
friend_initialized = TRUE
diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm
index bed12417218..62075149b24 100644
--- a/code/datums/brain_damage/split_personality.dm
+++ b/code/datums/brain_damage/split_personality.dm
@@ -56,7 +56,7 @@
qdel(src)
return
- stranger_backseat.key = ghost.key
+ stranger_backseat.PossessByPlayer(ghost.ckey)
stranger_backseat.log_message("became [key_name(owner)]'s split personality.", LOG_GAME)
message_admins("[ADMIN_LOOKUPFLW(stranger_backseat)] became [ADMIN_LOOKUPFLW(owner)]'s split personality.")
@@ -222,7 +222,7 @@
set waitfor = FALSE
var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_danger("[owner.real_name]'s")] brainwashed mind?", poll_time = 7.5 SECONDS, checked_target = stranger_backseat, alert_pic = owner, role_name_text = "brainwashed mind")
if(chosen_one)
- stranger_backseat.key = chosen_one.key
+ stranger_backseat.PossessByPlayer(chosen_one.ckey)
else
qdel(src)
diff --git a/code/datums/components/ghost_direct_control.dm b/code/datums/components/ghost_direct_control.dm
index fb3a2c06e83..8d9e632b059 100644
--- a/code/datums/components/ghost_direct_control.dm
+++ b/code/datums/components/ghost_direct_control.dm
@@ -145,7 +145,7 @@
harbinger.log_message("took control of [new_body].", LOG_GAME)
// doesn't transfer mind because that transfers antag datum as well
- new_body.key = harbinger.key
+ new_body.PossessByPlayer(harbinger.ckey)
// Already qdels due to below proc but just in case
qdel(src)
diff --git a/code/datums/components/ghostrole_on_revive.dm b/code/datums/components/ghostrole_on_revive.dm
index fd5ed5ae1e2..5c85754e9fb 100644
--- a/code/datums/components/ghostrole_on_revive.dm
+++ b/code/datums/components/ghostrole_on_revive.dm
@@ -101,7 +101,7 @@
aliver.death()
aliver.visible_message(span_deadsay("[aliver.name]'s soul is struggling to return!"))
else
- aliver.key = chosen_one.key
+ aliver.PossessByPlayer(chosen_one.ckey)
on_successful_revive?.Invoke(aliver)
qdel(src)
diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm
index 4a359ca1f2d..5c11528b686 100644
--- a/code/datums/diseases/transformation.dm
+++ b/code/datums/diseases/transformation.dm
@@ -74,7 +74,7 @@
if(affected_mob.mind)
affected_mob.mind.transfer_to(new_mob)
else
- new_mob.key = affected_mob.key
+ new_mob.PossessByPlayer(affected_mob.ckey)
if(transformed_antag_datum)
new_mob.mind.add_antag_datum(transformed_antag_datum)
new_mob.name = affected_mob.real_name
@@ -89,14 +89,13 @@
to_chat(affected_mob, span_userdanger("Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!"))
message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.")
affected_mob.ghostize(FALSE)
- affected_mob.key = chosen_one.key
+ affected_mob.PossessByPlayer(chosen_one.ckey)
else
to_chat(new_mob, span_userdanger("Your mob has been claimed by death! Appeal your job ban if you want to avoid this in the future!"))
new_mob.investigate_log("has been killed because there was no one to replace them as a job-banned player.", INVESTIGATE_DEATHS)
new_mob.death()
if (!QDELETED(new_mob))
new_mob.ghostize(can_reenter_corpse = FALSE)
- new_mob.key = null
/datum/disease/transformation/jungle_flu
name = "Jungle Flu"
diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm
index dccd0876c82..f202af400e2 100644
--- a/code/datums/mind/_mind.dm
+++ b/code/datums/mind/_mind.dm
@@ -205,7 +205,7 @@
RegisterSignal(new_character, COMSIG_LIVING_DEATH, PROC_REF(set_death_time))
if(active || force_key_move)
- new_character.key = key //now transfer the key to link the client to our new body
+ new_character.PossessByPlayer(key) //now transfer the key to link the client to our new body
if(new_character.client)
LAZYCLEARLIST(new_character.client.recent_examines)
new_character.client.init_verbs() // re-initialize character specific verbs
diff --git a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm
index e1684a6df6c..96f9c1245df 100644
--- a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm
+++ b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm
@@ -183,7 +183,7 @@
var/mob/living/basic/ghost/swarm/new_ghost = new(get_turf(src))
ghosts_spawned += new_ghost
new_ghost.ghostize(FALSE)
- new_ghost.key = candidate_ghost.key
+ new_ghost.PossessByPlayer(candidate_ghost.key)
var/policy = get_policy(ROLE_ANOMALY_GHOST)
if(policy)
to_chat(new_ghost, policy)
diff --git a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
index db8b44beb04..7c0d1873bf8 100644
--- a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
+++ b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
@@ -53,7 +53,7 @@
// Failed to find a player, or somehow detonated prematurely
if(isnull(chosen_one))
return
- pyro.key = chosen_one.key
+ pyro.PossessByPlayer(chosen_one.key)
pyro.mind.special_role = ROLE_PYROCLASTIC_SLIME
pyro.mind.add_antag_datum(/datum/antagonist/pyro_slime)
pyro.log_message("was made into a slime by pyroclastic anomaly", LOG_GAME)
diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm
index ef1e4ebdee4..e65b4bada08 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -432,7 +432,7 @@
var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_danger("[user.real_name]'s")] [span_notice("Servant")]?", check_jobban = ROLE_WIZARD, role = ROLE_WIZARD, poll_time = 5 SECONDS, checked_target = human_servant, alert_pic = user, role_name_text = "dice servant")
if(chosen_one)
message_admins("[ADMIN_LOOKUPFLW(chosen_one)] was spawned as Dice Servant")
- human_servant.key = chosen_one.key
+ human_servant.PossessByPlayer(chosen_one.key)
human_servant.equipOutfit(/datum/outfit/butler)
var/datum/mind/servant_mind = new /datum/mind()
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 986a1c2a5a0..0f6dd5b68ef 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -286,7 +286,7 @@ ADMIN_VERB(create_or_modify_area, R_DEBUG, "Create Or Modify Area", "Create of m
log_admin("[key_name(usr)] stuffed [frommob.key] into [tomob.name].")
BLACKBOX_LOG_ADMIN_VERB("Ghost Drag Control")
- tomob.key = frommob.key
+ tomob.PossessByPlayer(frommob.key)
tomob.client?.init_verbs()
qdel(frommob)
diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm
index efda097db6c..3e8d212d071 100644
--- a/code/modules/admin/fun_balloon.dm
+++ b/code/modules/admin/fun_balloon.dm
@@ -112,7 +112,7 @@
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(body)])")
body.ghostize(FALSE)
- body.key = C.key
+ body.PossessByPlayer(C.key)
if (make_antag)
body.mind.add_antag_datum(antag_type)
continue
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index ddb02b78b8a..36b6a5b2c4a 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -283,9 +283,8 @@
M_rname_as_key = null
var/previous_names_string = ""
- var/datum/player_details/readable = GLOB.player_details[M.ckey]
- if(readable)
- previous_names_string = readable.get_played_names()
+ if(M.persistent_client)
+ previous_names_string = M.persistent_client.get_played_names()
//output for each mob
dat += {"
diff --git a/code/modules/admin/verbs/admingame.dm b/code/modules/admin/verbs/admingame.dm
index 8e7fd97cc1f..16303302b20 100644
--- a/code/modules/admin/verbs/admingame.dm
+++ b/code/modules/admin/verbs/admingame.dm
@@ -62,11 +62,10 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_ADMIN, "Show Player Panel", mo
body += "Mob type = [player.type]
"
- if(player.client)
+ if(HAS_CONNECTED_PLAYER(player))
body += "Old names: "
- 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 += "None?!"
body += "
"
@@ -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.
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index d71522cc722..fb50c1d9834 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -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)
diff --git a/code/modules/admin/verbs/ert.dm b/code/modules/admin/verbs/ert.dm
index 09903e76429..7ee51a895ab 100644
--- a/code/modules/admin/verbs/ert.dm
+++ b/code/modules/admin/verbs/ert.dm
@@ -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)
diff --git a/code/modules/admin/verbs/individual_logging.dm b/code/modules/admin/verbs/individual_logging.dm
index eb665d35f65..ede3c1fc42d 100644
--- a/code/modules/admin/verbs/individual_logging.dm
+++ b/code/modules/admin/verbs/individual_logging.dm
@@ -48,10 +48,9 @@
dat += "
"
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 += "[entry]
[all_the_entrys[entry]]"
+
if(length(concatenated_logs))
sortTim(concatenated_logs, cmp = GLOBAL_PROC_REF(cmp_text_dsc)) //Sort by timestamp.
dat += ""
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index 4d7fbe16be8..446b5d72219 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -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
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 1cc84228258..9de40566814 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -306,7 +306,7 @@ GLOBAL_LIST_EMPTY(antagonists)
message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(owner)]) to replace antagonist banned player.")
log_game("[key_name(chosen_one)] has taken control of ([key_name(owner)]) to replace antagonist banned player.")
owner.current.ghostize(FALSE)
- owner.current.key = chosen_one.key
+ owner.current.PossessByPlayer(chosen_one.key)
else
log_game("Couldn't find antagonist ban replacement for ([key_name(owner)]).")
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index 574e08f9753..588d54e049c 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -76,7 +76,7 @@
new /obj/effect/particle_effect/fluid/smoke(T)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
C.prefs.safe_transfer_prefs_to(M, is_antag = TRUE)
- M.key = C.key
+ M.PossessByPlayer(C.key)
var/datum/mind/app_mind = M.mind
var/datum/antagonist/wizard/apprentice/app = new()
@@ -247,7 +247,7 @@
borg.mmi.brainmob.name = brainopsname
borg.real_name = borg.name
- borg.key = C.key
+ borg.PossessByPlayer(C.key)
borg.mind.add_antag_datum(antag_datum, creator_op ? creator_op.get_team() : null)
borg.mind.special_role = special_role_name
@@ -290,7 +290,7 @@
var/mob/living/basic/demon/spawned = new demon_type(T)
new /obj/effect/dummy/phased_mob(T, spawned)
- spawned.key = C.key
+ spawned.PossessByPlayer(C.key)
/obj/item/antag_spawner/slaughter_demon/laughter
name = "vial of tickles"
diff --git a/code/modules/antagonists/changeling/headslug_eggs.dm b/code/modules/antagonists/changeling/headslug_eggs.dm
index 9006f938d10..529a27ee575 100644
--- a/code/modules/antagonists/changeling/headslug_eggs.dm
+++ b/code/modules/antagonists/changeling/headslug_eggs.dm
@@ -33,7 +33,7 @@
if(origin && (origin.current ? (origin.current.stat == DEAD) : origin.get_ghost()))
origin.transfer_to(spawned_monkey)
- spawned_monkey.key = origin.key
+ spawned_monkey.PossessByPlayer(origin.key)
var/datum/antagonist/changeling/changeling_datum = origin.has_antag_datum(/datum/antagonist/changeling)
if(!changeling_datum)
changeling_datum = origin.add_antag_datum(/datum/antagonist/changeling/headslug)
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index a6290679b11..2c7f409619b 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -371,7 +371,7 @@ Striking a noncultist, however, will tear their flesh."}
// Get the heretic's new body and antag datum.
trapped_entity = trapped_mind?.current
- trapped_entity.key = trapped_mind?.key
+ trapped_entity.PossessByPlayer(trapped_mind?.key)
var/datum/antagonist/heretic/heretic_holder = GET_HERETIC(trapped_entity)
if(!heretic_holder)
stack_trace("[soul_to_bind] in but not a heretic on the heretic soul blade.")
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index 079f9243da6..b8a18782c3d 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -771,7 +771,7 @@ GLOBAL_VAR_INIT(narsie_summon_count, 0)
to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.")
message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.")
mob_to_revive.ghostize(FALSE)
- mob_to_revive.key = chosen_one.key
+ mob_to_revive.PossessByPlayer(chosen_one.key)
else
fail_invoke()
return
@@ -1035,7 +1035,7 @@ GLOBAL_VAR_INIT(narsie_summon_count, 0)
old_mind = ghost_to_spawn.mind, \
old_body = ghost_to_spawn.mind.current, \
)
- new_human.key = ghost_to_spawn.key
+ new_human.PossessByPlayer(ghost_to_spawn.key)
var/datum/antagonist/cult/created_cultist = new_human.mind?.add_antag_datum(/datum/antagonist/cult)
created_cultist?.silent = TRUE
to_chat(new_human, span_cult_italic("You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar'Sie, and you are to serve them at all costs."))
diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm
index 566f8a2230a..95c6925dea7 100644
--- a/code/modules/antagonists/heretic/heretic_knowledge.dm
+++ b/code/modules/antagonists/heretic/heretic_knowledge.dm
@@ -424,7 +424,7 @@
summoned.move_resist = initial(summoned.move_resist)
summoned.ghostize(FALSE)
- summoned.key = chosen_one.key
+ summoned.PossessByPlayer(chosen_one.key)
user.log_message("created a [summoned.name], controlled by [key_name(chosen_one)].", LOG_GAME)
message_admins("[ADMIN_LOOKUPFLW(user)] created a [summoned.name], [ADMIN_LOOKUPFLW(summoned)].")
diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
index cfcd6ea9ced..aa8bbaa5039 100644
--- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
@@ -167,7 +167,7 @@
return FALSE
message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(soon_to_be_ghoul)]) to replace an AFK player.")
soon_to_be_ghoul.ghostize(FALSE)
- soon_to_be_ghoul.key = chosen_one.key
+ soon_to_be_ghoul.PossessByPlayer(chosen_one.key)
selected_atoms -= soon_to_be_ghoul
make_ghoul(user, soon_to_be_ghoul)
diff --git a/code/modules/antagonists/heretic/structures/lock_final.dm b/code/modules/antagonists/heretic/structures/lock_final.dm
index cd187641f73..5a49c0a0790 100644
--- a/code/modules/antagonists/heretic/structures/lock_final.dm
+++ b/code/modules/antagonists/heretic/structures/lock_final.dm
@@ -73,7 +73,7 @@
return FALSE
var/monster_type = pick(monster_types)
var/mob/living/monster = new monster_type(loc)
- monster.key = user.key
+ monster.PossessByPlayer(user.key)
monster.set_name()
var/datum/antagonist/heretic_monster/woohoo_free_antag = new(src)
monster.mind.add_antag_datum(woohoo_free_antag)
diff --git a/code/modules/antagonists/nukeop/datums/operative_team.dm b/code/modules/antagonists/nukeop/datums/operative_team.dm
index ac65a17dc2d..7a7e3dfa647 100644
--- a/code/modules/antagonists/nukeop/datums/operative_team.dm
+++ b/code/modules/antagonists/nukeop/datums/operative_team.dm
@@ -194,7 +194,7 @@
var/mob/living/carbon/human/nukie = new(spawn_loc)
chosen_one.client.prefs.safe_transfer_prefs_to(nukie, is_antag = TRUE)
- nukie.key = chosen_one.key
+ nukie.PossessByPlayer(chosen_one.key)
var/datum/antagonist/nukeop/antag_datum = new()
antag_datum.send_to_spawnpoint = FALSE
diff --git a/code/modules/antagonists/space_dragon/carp_rift.dm b/code/modules/antagonists/space_dragon/carp_rift.dm
index 6299bde9275..05d712254e8 100644
--- a/code/modules/antagonists/space_dragon/carp_rift.dm
+++ b/code/modules/antagonists/space_dragon/carp_rift.dm
@@ -265,7 +265,7 @@
if(!is_listed)
ckey_list += user.ckey
- newcarp.key = user.key
+ newcarp.PossessByPlayer(user.key)
newcarp.set_name()
var/datum/antagonist/space_carp/carp_antag = new(src)
newcarp.mind.add_antag_datum(carp_antag)
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index 52cd80e7155..11e58529399 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -392,7 +392,7 @@
soulstone_spirit.AddComponent(/datum/component/soulstoned, src)
soulstone_spirit.name = "Shade of [victim.real_name]"
soulstone_spirit.real_name = "Shade of [victim.real_name]"
- soulstone_spirit.key = shade_controller.key
+ soulstone_spirit.PossessByPlayer(shade_controller.key)
soulstone_spirit.copy_languages(victim, LANGUAGE_MIND)//Copies the old mobs languages into the new mob holder.
if(user)
soulstone_spirit.copy_languages(user, LANGUAGE_MASTER)
@@ -516,7 +516,7 @@
seek_master.Grant(newstruct)
if (isnull(target.mind))
- newstruct.key = target.key
+ newstruct.PossessByPlayer(target.key)
else
target.mind.transfer_to(newstruct, force_key_move = TRUE)
var/atom/movable/screen/alert/bloodsense/sense_alert
diff --git a/code/modules/basketball/controller.dm b/code/modules/basketball/controller.dm
index 20b3b0ecdaf..d7f58f11903 100644
--- a/code/modules/basketball/controller.dm
+++ b/code/modules/basketball/controller.dm
@@ -198,7 +198,7 @@ GLOBAL_VAR(basketball_game)
old_mind = player_client.mob.mind, \
old_body = player_client.mob.mind.current, \
)
- baller.key = player_key
+ baller.PossessByPlayer(player_key)
SEND_SOUND(baller, sound('sound/items/whistle/whistle.ogg', volume=30))
if(is_player_referee)
diff --git a/code/modules/bitrunning/components/avatar_connection.dm b/code/modules/bitrunning/components/avatar_connection.dm
index cefaeef91f3..296fb233cf4 100644
--- a/code/modules/bitrunning/components/avatar_connection.dm
+++ b/code/modules/bitrunning/components/avatar_connection.dm
@@ -32,7 +32,7 @@
server_ref = WEAKREF(server)
server.avatar_connection_refs.Add(WEAKREF(src))
- avatar.key = old_body.key
+ avatar.PossessByPlayer(old_body.key)
ADD_TRAIT(avatar, TRAIT_NO_MINDSWAP, REF(src)) // do not remove this one
ADD_TRAIT(old_body, TRAIT_MIND_TEMPORARILY_GONE, REF(src))
diff --git a/code/modules/bitrunning/server/threats.dm b/code/modules/bitrunning/server/threats.dm
index 60bf6220839..3e1d6252f5c 100644
--- a/code/modules/bitrunning/server/threats.dm
+++ b/code/modules/bitrunning/server/threats.dm
@@ -111,7 +111,7 @@
mutation_target.gib(DROP_ALL_REMAINS)
var/datum/mind/ghost_mind = ghost.mind
- new_mob.key = ghost.key
+ new_mob.PossessByPlayer(ghost.key)
if(ghost_mind)
new_mob.AddComponent(/datum/component/temporary_body, ghost_mind, ghost_mind.current, TRUE)
diff --git a/code/modules/buildmode/buildmode.dm b/code/modules/buildmode/buildmode.dm
index 80f5ae1ad27..78e68d3e6b4 100644
--- a/code/modules/buildmode/buildmode.dm
+++ b/code/modules/buildmode/buildmode.dm
@@ -30,7 +30,7 @@
holder = c
buttons = list()
li_cb = CALLBACK(src, PROC_REF(post_login))
- holder.player_details.post_login_callbacks += li_cb
+ holder.persistent_client.post_login_callbacks += li_cb
holder.show_popup_menus = FALSE
create_buttons()
holder.screen += buttons
@@ -47,7 +47,7 @@
/datum/buildmode/Destroy()
close_switchstates()
close_preview()
- holder.player_details.post_login_callbacks -= li_cb
+ holder.persistent_client.post_login_callbacks -= li_cb
li_cb = null
holder = null
modebutton = null
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index c8e9e9a64c2..99b22e27fa5 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -138,7 +138,7 @@
var/list/credits
///these persist between logins/logouts during the same round.
- var/datum/player_details/player_details
+ var/datum/persistent_client/persistent_client
///Should only be a key-value list of north/south/east/west = atom/movable/screen.
var/list/char_render_holders
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index fb40674b31f..d0696b6e0ee 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -249,6 +249,17 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
GLOB.clients += src
GLOB.directory[ckey] = src
+ var/reconnecting = FALSE
+ if(GLOB.persistent_clients_by_ckey[ckey])
+ reconnecting = TRUE
+ persistent_client = GLOB.persistent_clients_by_ckey[ckey]
+ persistent_client.byond_build = byond_build
+ persistent_client.byond_version = byond_version
+ else
+ persistent_client = new(ckey)
+ persistent_client.byond_build = byond_build
+ persistent_client.byond_version = byond_version
+
if(byond_version >= 516)
winset(src, null, list("browser-options" = "find,refresh,byondstorage"))
@@ -328,18 +339,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
else
message_admins(span_danger("[message_type]: Connecting player [key_name_admin(src)] has the same [matches] as [joined_player_ckey](no longer logged in)[in_round]. "))
log_admin_private("[message_type]: Connecting player [key_name(src)] has the same [matches] as [joined_player_ckey](no longer logged in)[in_round].")
- var/reconnecting = FALSE
- if(GLOB.player_details[ckey])
- reconnecting = TRUE
- player_details = GLOB.player_details[ckey]
- player_details.byond_version = byond_version
- player_details.byond_build = byond_build
- else
- player_details = new(ckey)
- player_details.byond_version = byond_version
- player_details.byond_build = byond_build
- GLOB.player_details[ckey] = player_details
-
. = ..() //calls mob.Login()
@@ -586,6 +585,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
GLOB.clients -= src
GLOB.directory -= ckey
+ persistent_client.client = null
+
log_access("Logout: [key_name(src)]")
GLOB.ahelp_tickets.ClientLogout(src)
GLOB.interviews.client_logout(src)
@@ -1040,11 +1041,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
///Redirect proc that makes it easier to call the unlock achievement proc. Achievement type is the typepath to the award, user is the mob getting the award, and value is an optional variable used for leaderboard value increments
/client/proc/give_award(achievement_type, mob/user, value = 1)
- return player_details.achievements.unlock(achievement_type, user, value)
+ return persistent_client.achievements.unlock(achievement_type, user, value)
///Redirect proc that makes it easier to get the status of an achievement. Achievement type is the typepath to the award.
/client/proc/get_award_status(achievement_type, mob/user, value = 1)
- return player_details.achievements.get_achievement_status(achievement_type)
+ return persistent_client.achievements.get_achievement_status(achievement_type)
///Gives someone hearted status for OOC, from behavior commendations
/client/proc/adjust_heart(duration = 24 HOURS)
diff --git a/code/modules/client/player_details.dm b/code/modules/client/persistent_client.dm
similarity index 60%
rename from code/modules/client/player_details.dm
rename to code/modules/client/persistent_client.dm
index d0982a0c312..ac791b846a2 100644
--- a/code/modules/client/player_details.dm
+++ b/code/modules/client/persistent_client.dm
@@ -1,55 +1,86 @@
-///assoc list of ckey -> /datum/player_details
-GLOBAL_LIST_EMPTY_TYPED(player_details, /datum/player_details)
+///assoc list of ckey -> /datum/persistent_client
+GLOBAL_LIST_EMPTY_TYPED(persistent_clients_by_ckey, /datum/persistent_client)
+/// A flat list of all persistent clients, for her looping pleasure.
+GLOBAL_LIST_EMPTY_TYPED(persistent_clients, /datum/persistent_client)
/// Tracks information about a client between log in and log outs
-/datum/player_details
+/datum/persistent_client
+ /// The true client
+ var/client/client
+ /// The mob this persistent client is currently bound to.
+ var/mob/mob
+
+ /// Major version of BYOND this client is using.
+ var/byond_version
+ /// Build number of BYOND this client is using.
+ var/byond_build
+
/// Action datums assigned to this player
var/list/datum/action/player_actions = list()
/// Tracks client action logging
var/list/logging = list()
+
/// Callbacks invoked when this client logs in again
var/list/post_login_callbacks = list()
/// Callbacks invoked when this client logs out
var/list/post_logout_callbacks = list()
+
/// List of names this key played under this round
/// assoc list of name -> mob tag
var/list/played_names = list()
/// Lazylist of preference slots this client has joined the round under
/// Numbers are stored as strings
var/list/joined_as_slots
- /// Major version of BYOND this client is using.
- var/byond_version
- /// Build number of BYOND this client is using.
- var/byond_build
+
/// Tracks achievements they have earned
var/datum/achievement_data/achievements
+
/// World.time this player last died
var/time_of_death = 0
-/datum/player_details/New(key)
- achievements = new(key)
+/datum/persistent_client/New(ckey, client)
+ src.client = client
+ achievements = new(ckey)
+ GLOB.persistent_clients_by_ckey[ckey] = src
+ GLOB.persistent_clients += src
+
+/datum/persistent_client/Destroy(force)
+ SHOULD_CALL_PARENT(FALSE)
+ . = QDEL_HINT_LETMELIVE
+ CRASH("Who the FUCK tried to delete a persistent client? Get your head checked you leadskull.")
+
+/// Setter for the mob var, handles both references.
+/datum/persistent_client/proc/set_mob(mob/new_mob)
+ if(mob == new_mob)
+ return
+
+ mob?.persistent_client = null
+ new_mob?.persistent_client?.set_mob(null)
+
+ mob = new_mob
+ new_mob?.persistent_client = src
/// Writes all of the `played_names` into an HTML-escaped string.
-/datum/player_details/proc/get_played_names()
+/datum/persistent_client/proc/get_played_names()
var/list/previous_names = list()
for(var/previous_name in played_names)
previous_names += html_encode("[previous_name] ([played_names[previous_name]])")
return previous_names.Join("; ")
/// Returns the full version string (i.e 515.1642) of the BYOND version and build.
-/datum/player_details/proc/full_byond_version()
+/datum/persistent_client/proc/full_byond_version()
if(!byond_version)
return "Unknown"
return "[byond_version].[byond_build || "xxx"]"
-/// Adds the new names to the player's played_names list on their /datum/player_details for use of admins.
+/// Adds the new names to the player's played_names list on their /datum/persistent_client for use of admins.
/// `ckey` should be their ckey, and `data` should be an associative list with the keys being the names they played under and the values being the unique mob ID tied to that name.
/proc/log_played_names(ckey, data)
if(!ckey)
return
- var/datum/player_details/writable = GLOB.player_details[ckey]
+ var/datum/persistent_client/writable = GLOB.persistent_clients_by_ckey[ckey]
if(isnull(writable))
return
diff --git a/code/modules/clothing/head/mind_monkey_helmet.dm b/code/modules/clothing/head/mind_monkey_helmet.dm
index 602f64eefb2..4aafaa4dfc1 100644
--- a/code/modules/clothing/head/mind_monkey_helmet.dm
+++ b/code/modules/clothing/head/mind_monkey_helmet.dm
@@ -94,7 +94,7 @@
user.dropItemToGround(src)
return
- magnification.key = chosen_one.key
+ magnification.PossessByPlayer(chosen_one.key)
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 100, FALSE)
to_chat(magnification, span_notice("You're a mind magnified monkey! Protect your helmet with your life- if you lose it, your sentience goes with it!"))
var/policy = get_policy(ROLE_MONKEY_HELMET)
diff --git a/code/modules/deathmatch/deathmatch_lobby.dm b/code/modules/deathmatch/deathmatch_lobby.dm
index a99f4fabfe1..3c607089528 100644
--- a/code/modules/deathmatch/deathmatch_lobby.dm
+++ b/code/modules/deathmatch/deathmatch_lobby.dm
@@ -148,7 +148,7 @@
old_body = observer.mind.current, \
)
new_player.equipOutfit(loadout) // Loadout
- new_player.key = ckey
+ new_player.PossessByPlayer(ckey)
players_info["mob"] = new_player
for(var/datum/deathmatch_modifier/modifier as anything in modifiers)
diff --git a/code/modules/events/ghost_role/alien_infestation.dm b/code/modules/events/ghost_role/alien_infestation.dm
index 1c0c938ce89..d02b0055f65 100644
--- a/code/modules/events/ghost_role/alien_infestation.dm
+++ b/code/modules/events/ghost_role/alien_infestation.dm
@@ -71,7 +71,7 @@
var/obj/vent = pick_n_take(vents)
var/mob/dead/observer/selected = pick_n_take(candidates)
var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc)
- new_xeno.key = selected.key
+ new_xeno.PossessByPlayer(selected.key)
new_xeno.move_into_vent(vent)
spawncount--
diff --git a/code/modules/events/ghost_role/revenant_event.dm b/code/modules/events/ghost_role/revenant_event.dm
index 6f615f729c2..7b18a868e62 100644
--- a/code/modules/events/ghost_role/revenant_event.dm
+++ b/code/modules/events/ghost_role/revenant_event.dm
@@ -51,7 +51,7 @@
return MAP_ERROR
var/mob/living/basic/revenant/revvie = new(pick(spawn_locs))
- revvie.key = chosen_one.key
+ revvie.PossessByPlayer(chosen_one.key)
message_admins("[ADMIN_LOOKUPFLW(revvie)] has been made into a revenant by an event.")
revvie.log_message("was spawned as a revenant by an event.", LOG_GAME)
spawned_mobs += revvie
diff --git a/code/modules/events/ghost_role/sentience.dm b/code/modules/events/ghost_role/sentience.dm
index 60b43216ab2..4507e9e2790 100644
--- a/code/modules/events/ghost_role/sentience.dm
+++ b/code/modules/events/ghost_role/sentience.dm
@@ -87,7 +87,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list(
spawned_animals++
- selected.key = picked_candidate.key
+ selected.PossessByPlayer(picked_candidate.key)
selected.grant_all_languages(UNDERSTOOD_LANGUAGE, grant_omnitongue = FALSE, source = LANGUAGE_ATOM)
diff --git a/code/modules/events/ghost_role/space_dragon.dm b/code/modules/events/ghost_role/space_dragon.dm
index 56d82ff33c7..6fac4fe1edf 100644
--- a/code/modules/events/ghost_role/space_dragon.dm
+++ b/code/modules/events/ghost_role/space_dragon.dm
@@ -26,7 +26,7 @@
if(isnull(spawn_location))
return MAP_ERROR
var/mob/living/basic/space_dragon/dragon = new(spawn_location)
- dragon.key = chosen_one.key
+ dragon.PossessByPlayer(chosen_one.key)
dragon.mind.add_antag_datum(/datum/antagonist/space_dragon)
playsound(dragon, 'sound/effects/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(dragon)] has been made into a Space Dragon by an event.")
diff --git a/code/modules/events/ghost_role/space_ninja.dm b/code/modules/events/ghost_role/space_ninja.dm
index eaccbe3de39..9d93697ce31 100644
--- a/code/modules/events/ghost_role/space_ninja.dm
+++ b/code/modules/events/ghost_role/space_ninja.dm
@@ -24,7 +24,7 @@
return NOT_ENOUGH_PLAYERS
//spawn the ninja and assign the candidate
var/mob/living/carbon/human/ninja = create_space_ninja(spawn_location)
- ninja.key = chosen_one.key
+ ninja.PossessByPlayer(chosen_one.key)
ninja.mind.add_antag_datum(/datum/antagonist/ninja)
spawned_mobs += ninja
message_admins("[ADMIN_LOOKUPFLW(ninja)] has been made into a space ninja by an event.")
diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm
index 7a119ee92bd..84a56b43a0a 100644
--- a/code/modules/events/holiday/xmas.dm
+++ b/code/modules/events/holiday/xmas.dm
@@ -89,6 +89,6 @@
if(isnull(chosen_one))
return NOT_ENOUGH_PLAYERS
santa = new /mob/living/carbon/human(pick(GLOB.blobstart))
- santa.key = chosen_one.key
+ santa.PossessByPlayer(chosen_one.key)
var/datum/antagonist/santa/A = new
santa.mind.add_antag_datum(A)
diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm
index c2fb5472bdd..110f6afa5f6 100644
--- a/code/modules/events/wizard/imposter.dm
+++ b/code/modules/events/wizard/imposter.dm
@@ -23,7 +23,7 @@
I.name = I.dna.real_name
I.updateappearance(mutcolor_update=1)
I.domutcheck()
- I.key = chosen_one.key
+ I.PossessByPlayer(chosen_one.key)
var/datum/antagonist/wizard/master = M.has_antag_datum(/datum/antagonist/wizard)
if(!master.wiz_team)
master.create_wiz_team()
diff --git a/code/modules/fishing/fish/types/ruins.dm b/code/modules/fishing/fish/types/ruins.dm
index 10e921a2b25..92dc9d0e699 100644
--- a/code/modules/fishing/fish/types/ruins.dm
+++ b/code/modules/fishing/fish/types/ruins.dm
@@ -104,7 +104,7 @@
/obj/item/fish/soul/proc/good_ending(mob/living/user)
var/mob/living/basic/spaceman/soulman = new(get_turf(user))
if(prob(80)) // the percentage is important.
- soulman.ckey = user.ckey
+ soulman.PossessByPlayer(user.ckey)
to_chat(soulman, span_notice("You finally feel at peace."))
user.gib()
qdel(src)
diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm
index a84d4d82ff7..492012ad4b7 100644
--- a/code/modules/hydroponics/grown/replicapod.dm
+++ b/code/modules/hydroponics/grown/replicapod.dm
@@ -181,9 +181,9 @@
podman.real_name = "Pod Person ([rand(1,999)])"
mind.transfer_to(podman)
if(ckey)
- podman.ckey = ckey
+ podman.PossessByPlayer(ckey)
else
- podman.ckey = ckey_holder
+ podman.PossessByPlayer(ckey_holder)
podman.gender = blood_gender
podman.faction |= factions
if(!features["mcolor"])
diff --git a/code/modules/mafia/roles/roles.dm b/code/modules/mafia/roles/roles.dm
index 77f46cddac7..85d1170d1a9 100644
--- a/code/modules/mafia/roles/roles.dm
+++ b/code/modules/mafia/roles/roles.dm
@@ -122,7 +122,7 @@
old_mind = player.mob.mind, \
old_body = player.mob.mind.current, \
)
- body.key = player.key
+ body.PossessByPlayer(player.key)
/**
* Tests kill immunities, if nothing prevents the kill, kills this role.
diff --git a/code/modules/mining/lavaland/megafauna_loot.dm b/code/modules/mining/lavaland/megafauna_loot.dm
index 83c7eda9a46..4a181b17f4a 100644
--- a/code/modules/mining/lavaland/megafauna_loot.dm
+++ b/code/modules/mining/lavaland/megafauna_loot.dm
@@ -456,7 +456,7 @@
using = FALSE
return
- soul.ckey = ghost.ckey
+ soul.PossessByPlayer(ghost.ckey)
soul.copy_languages(master, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the master.
soul.faction = list("[REF(master)]")
balloon_alert(master, "the scythe glows")
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 8d59e663643..5e902493401 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -90,14 +90,16 @@
else
to_chat(src, span_notice("Teleporting failed. Ahelp an admin please"))
stack_trace("There's no freaking observer landmark available on this map or you're making observers before the map is initialised")
- observer.key = key
+
+ observer.PossessByPlayer(key)
observer.client = client
observer.set_ghost_appearance()
if(observer.client && observer.client.prefs)
observer.real_name = observer.client.prefs.read_preference(/datum/preference/name/real_name)
observer.name = observer.real_name
observer.client.init_verbs()
- observer.client.player_details.time_of_death = world.time
+ observer.persistent_client.time_of_death = world.time
+
observer.update_appearance()
observer.stop_sound_channel(CHANNEL_LOBBYMUSIC)
deadchat_broadcast(" has observed.", "[observer.real_name]", follow_target = observer, turf_target = get_turf(observer), message_type = DEADCHAT_DEATHRATTLE)
@@ -151,7 +153,7 @@
/mob/dead/new_player/proc/AttemptLateSpawn(rank)
// Check that they're picking someone new for new character respawning
if(CONFIG_GET(flag/allow_respawn) == RESPAWN_FLAG_NEW_CHARACTER)
- if("[client.prefs.default_slot]" in client.player_details.joined_as_slots)
+ if("[client.prefs.default_slot]" in persistent_client.joined_as_slots)
tgui_alert(usr, "You already have played this character in this round!")
return FALSE
@@ -263,8 +265,9 @@
mind.active = FALSE //we wish to transfer the key manually
var/mob/living/spawning_mob = mind.assigned_role.get_spawn_mob(client, destination)
- if(QDELETED(src) || !client)
+ if(QDELETED(src) || !HAS_CONNECTED_PLAYER(src))
return // Disconnected while checking for the appearance ban.
+
if(!isAI(spawning_mob)) // Unfortunately there's still snowflake AI code out there.
// transfer_to sets mind to null
var/datum/mind/preserved_mind = mind
@@ -272,7 +275,7 @@
preserved_mind.transfer_to(spawning_mob) //won't transfer key since the mind is not active
preserved_mind.set_original_character(spawning_mob)
- LAZYADD(client.player_details.joined_as_slots, "[client.prefs.default_slot]")
+ LAZYADD(persistent_client.joined_as_slots, "[client.prefs.default_slot]")
client.init_verbs()
. = spawning_mob
new_character = .
@@ -282,7 +285,7 @@
. = new_character
if(!.)
return
- new_character.key = key //Manually transfer the key to log them in,
+ new_character.PossessByPlayer(key) //Manually transfer the key to log them in,
new_character.stop_sound_channel(CHANNEL_LOBBYMUSIC)
var/area/joined_area = get_area(new_character.loc)
if(joined_area)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 820f14edeba..f93af197b1a 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -283,7 +283,7 @@ Works together with spawning an observer, noted above.
var/mob/dead/observer/ghost = new(src) // Transfer safety to observer spawning proc.
SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
ghost.can_reenter_corpse = can_reenter_corpse
- ghost.key = key
+ ghost.PossessByPlayer(key)
ghost.client?.init_verbs()
if(!can_reenter_corpse)// Disassociates observer mind from the body mind
ghost.mind = null
@@ -293,7 +293,7 @@ Works together with spawning an observer, noted above.
if(isliving(former_mob))
recordable_time = former_mob.timeofdeath
- ghost.client?.player_details.time_of_death = recordable_time
+ ghost.persistent_client?.time_of_death = recordable_time
SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZED)
return ghost
@@ -386,7 +386,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
SStgui.on_transfer(src, mind.current) // Transfer NanoUIs.
if(mind.current.stat == DEAD && SSlag_switch.measures[DISABLE_DEAD_KEYLOOP])
to_chat(src, span_warning("To leave your body again use the Ghost verb."))
- mind.current.key = key
+ mind.current.PossessByPlayer(key)
mind.current.client.init_verbs()
return TRUE
@@ -680,7 +680,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(src, span_warning("Someone has taken this body while you were choosing!"))
return FALSE
- target.key = key
+ target.PossessByPlayer(key)
target.faction = list(FACTION_NEUTRAL)
return TRUE
diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm
index 5272d64add5..eb8683e7a87 100644
--- a/code/modules/mob/living/basic/bots/_bots.dm
+++ b/code/modules/mob/living/basic/bots/_bots.dm
@@ -719,7 +719,7 @@ GLOBAL_LIST_INIT(command_strings, list(
if(isnull(mind))
mind.transfer_to(paicard.pai)
else
- paicard.pai.key = key
+ paicard.pai.PossessByPlayer(key)
else
ghostize(FALSE) // The pAI card that just got ejected was dead.
diff --git a/code/modules/mob/living/basic/guardian/guardian_creator.dm b/code/modules/mob/living/basic/guardian/guardian_creator.dm
index 08a28041cc8..5667d77d616 100644
--- a/code/modules/mob/living/basic/guardian/guardian_creator.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_creator.dm
@@ -117,7 +117,7 @@ GLOBAL_LIST_INIT(guardian_radial_images, setup_guardian_radial())
var/datum/guardian_fluff/guardian_theme = GLOB.guardian_themes[theme]
var/mob/living/basic/guardian/summoned_guardian = new guardian_path(user, guardian_theme)
summoned_guardian.set_summoner(user, different_person = TRUE)
- summoned_guardian.key = candidate.key
+ summoned_guardian.PossessByPlayer(candidate.key)
user.log_message("has summoned [key_name(summoned_guardian)], a [summoned_guardian.creator_name] holoparasite.", LOG_GAME)
summoned_guardian.log_message("was summoned as a [summoned_guardian.creator_name] holoparasite.", LOG_GAME)
to_chat(user, guardian_theme.get_fluff_string(summoned_guardian.guardian_type))
diff --git a/code/modules/mob/living/basic/guardian/guardian_verbs.dm b/code/modules/mob/living/basic/guardian/guardian_verbs.dm
index b69fac87820..c63dffd4316 100644
--- a/code/modules/mob/living/basic/guardian/guardian_verbs.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_verbs.dm
@@ -180,7 +180,7 @@
to_chat(owner, span_boldholoparasite("The personality of [chosen_guardian.theme.name] has been successfully reset."))
message_admins("[key_name_admin(chosen_one)] has taken control of ([ADMIN_LOOKUPFLW(chosen_guardian)])")
chosen_guardian.ghostize(FALSE)
- chosen_guardian.key = chosen_one.key
+ chosen_guardian.PossessByPlayer(chosen_one.key)
COOLDOWN_START(chosen_guardian, resetting_cooldown, 5 MINUTES)
chosen_guardian.guardian_rename() //give it a new color and name, to show it's a new person
chosen_guardian.guardian_recolour()
diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm
index f825788decd..93308852a52 100644
--- a/code/modules/mob/living/basic/pets/parrot/poly.dm
+++ b/code/modules/mob/living/basic/pets/parrot/poly.dm
@@ -67,7 +67,7 @@
if(mind)
mind.transfer_to(specter)
else
- specter.key = key
+ specter.PossessByPlayer(key)
return ..()
/mob/living/basic/parrot/poly/get_static_list_of_phrases() // there's only one poly, so there should only be one ongoing list of phrases. i guess
diff --git a/code/modules/mob/living/basic/slime/actions.dm b/code/modules/mob/living/basic/slime/actions.dm
index a3168d81437..2b399e3128c 100644
--- a/code/modules/mob/living/basic/slime/actions.dm
+++ b/code/modules/mob/living/basic/slime/actions.dm
@@ -149,7 +149,7 @@
new_slime.set_combat_mode(TRUE)
if(isnull(mind))
- new_slime.key = key
+ new_slime.PossessByPlayer(key)
else
mind.transfer_to(new_slime)
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm
index f8a3db0202a..f691521b076 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm
@@ -71,7 +71,7 @@
var/user_name = old_ckey
if(isnull(revenant.client))
var/mob/potential_user = get_new_user()
- revenant.key = potential_user.key
+ revenant.PossessByPlayer(potential_user.key)
user_name = potential_user.ckey
qdel(potential_user)
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 54939f85961..a912f01b0bc 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -77,7 +77,7 @@
if(brainmob.mind)
brainmob.mind.transfer_to(brain_owner)
else
- brain_owner.key = brainmob.key
+ brain_owner.PossessByPlayer(brainmob.key)
brain_owner.set_suicide(HAS_TRAIT(brainmob, TRAIT_SUICIDED))
diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm
index 6557d0d1da8..bb677d86f9e 100644
--- a/code/modules/mob/living/brain/posibrain.dm
+++ b/code/modules/mob/living/brain/posibrain.dm
@@ -155,7 +155,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
if(candidate.mind && !isobserver(candidate))
candidate.mind.transfer_to(brainmob)
else
- brainmob.ckey = candidate.ckey
+ brainmob.PossessByPlayer(candidate.ckey)
name = "[initial(name)] ([brainmob.name])"
var/policy = get_policy(ROLE_POSIBRAIN)
if(policy)
diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
index ccb8a22a2fa..ef3b242cbb3 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -119,7 +119,7 @@
var/atom/xeno_loc = get_turf(owner)
var/mob/living/carbon/alien/larva/new_xeno = new(xeno_loc)
- new_xeno.key = ghost.key
+ new_xeno.PossessByPlayer(ghost.key)
SEND_SOUND(new_xeno, sound('sound/mobs/non-humanoids/hiss/hiss5.ogg',0,0,0,100)) //To get the player's attention
new_xeno.add_traits(list(TRAIT_HANDS_BLOCKED, TRAIT_IMMOBILIZED, TRAIT_NO_TRANSFORM), type) //so we don't move during the bursting animation
new_xeno.SetInvisibility(INVISIBILITY_MAXIMUM, id=type)
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index 7dc6de58cfc..a97dd352568 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -193,6 +193,7 @@
if (client)
client.move_delay = initial(client.move_delay)
- client.player_details.time_of_death = timeofdeath
+
+ persistent_client?.time_of_death = timeofdeath
return TRUE
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 0143fbad542..5a16c56eb71 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1781,7 +1781,7 @@
// Well, no mmind, guess we should try to move a key over
else if(key)
- new_mob.key = key
+ new_mob.PossessByPlayer(key)
/mob/living/proc/unfry_mob() //Callback proc to tone down spam from multiple sizzling frying oil dipping.
REMOVE_TRAIT(src, TRAIT_OIL_FRIED, "cooking_oil_react")
@@ -2932,7 +2932,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
summoned_guardian.fully_replace_character_name(null, picked_name)
if(picked_color)
summoned_guardian.set_guardian_colour(picked_color)
- summoned_guardian.key = guardian_client?.key
+ summoned_guardian.PossessByPlayer(guardian_client?.key)
guardian_client?.init_verbs()
if(del_mob)
qdel(old_mob)
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 6753a34b5a3..4798752969a 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -1097,7 +1097,7 @@ Pass a positive integer as an argument to override a bot's default speed.
if(mind && paicard.pai)
mind.transfer_to(paicard.pai)
else if(paicard.pai)
- paicard.pai.key = key
+ paicard.pai.PossessByPlayer(key)
else
ghostize(FALSE) // The pAI card that just got ejected was dead.
key = null
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index d921b5f5878..8ff5914c4f1 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -468,7 +468,7 @@
var/be_helper = tgui_alert(usr, "Become a Lightgeist? (Warning, You can no longer be revived!)", "Lightgeist Deployment", list("Yes", "No"))
if((be_helper == "Yes") && !QDELETED(src) && isobserver(user))
var/mob/living/basic/lightgeist/deployable = new(get_turf(loc))
- deployable.key = user.key
+ deployable.PossessByPlayer(user.key)
/obj/machinery/anomalous_crystal/possessor //Allows you to bodyjack small animals, then exit them at your leisure, but you can only do this once per activation. Because they blow up. Also, if the bodyjacked animal dies, SO DO YOU.
observer_desc = "When activated, this crystal allows you to take over small animals, and then exit them at the possessors leisure. Exiting the animal kills it, and if you die while possessing the animal, you die as well."
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index 1cf118d5876..8cc6fc3e02f 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -206,7 +206,7 @@ While using this makes the system rely on OnFire, it still gives options for tim
visible_message(span_boldwarning("[mychild] emerges from [src]!"))
playsound(loc,'sound/effects/phasein.ogg', 200, 0, 50, TRUE, TRUE)
if(boosted)
- mychild.key = elitemind.key
+ mychild.PossessByPlayer(elitemind.key)
mychild.sentience_act()
notify_ghosts(
"\A [mychild] has been awakened in \the [get_area(src)]!",
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 7d2e111083f..cd661869463 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -31,6 +31,8 @@
return FALSE
canon_client = client
+ client.persistent_client.set_mob(src)
+
add_to_player_list()
lastKnownIP = client.address
computer_id = client.computer_id
@@ -108,13 +110,12 @@
else
client.change_view(getScreenSize(client.prefs.read_preference(/datum/preference/toggle/widescreen)))
- if(client.player_details.player_actions.len)
- for(var/datum/action/A in client.player_details.player_actions)
- A.Grant(src)
+ for(var/datum/action/A as anything in persistent_client.player_actions)
+ A.Grant(src)
- for(var/foo in client.player_details.post_login_callbacks)
- var/datum/callback/CB = foo
+ for(var/datum/callback/CB as anything in persistent_client.post_login_callbacks)
CB.Invoke()
+
log_played_names(
client.ckey,
list(
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index c5b70411f25..97f92018200 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -29,6 +29,8 @@
else if(ckey)
stack_trace("Mob without client but with associated ckey, [ckey], has been deleted.")
+ persistent_client?.set_mob(null)
+
remove_from_mob_list()
remove_from_dead_mob_list()
remove_from_alive_mob_list()
@@ -107,6 +109,17 @@
. = ..()
tag = "mob_[next_mob_id++]"
+/// Assigns a (c)key to this mob.
+/mob/proc/PossessByPlayer(ckey)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ if(isnull(ckey))
+ return
+
+ if(!istext(ckey))
+ CRASH("Tried to assign a mob a non-text ckey, wtf?!")
+
+ src.ckey = ckey(ckey)
+
/mob/serialize_list(list/options, list/semvers)
. = ..()
@@ -780,14 +793,14 @@
qdel(M)
return
- M.key = key
+ M.PossessByPlayer(key)
/// Checks if the mob can respawn yet according to the respawn delay
/mob/proc/check_respawn_delay(override_delay = 0)
if(!override_delay && !CONFIG_GET(number/respawn_delay))
return TRUE
- var/death_time = world.time - client.player_details.time_of_death
+ var/death_time = world.time - persistent_client.time_of_death
var/required_delay = override_delay || CONFIG_GET(number/respawn_delay)
@@ -1535,8 +1548,7 @@
if(!canon_client)
return
- for(var/foo in canon_client.player_details.post_logout_callbacks)
- var/datum/callback/CB = foo
+ for(var/datum/callback/CB as anything in persistent_client.post_logout_callbacks)
CB.Invoke()
if(canon_client?.movingmob)
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 28062fd0669..7ea6966b84d 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -27,6 +27,8 @@
/// We also need to clear this var/do other cleanup in client/Destroy, since that happens before logout
/// HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
var/client/canon_client
+ /// It's like a client, but persists! Persistent clients will stick to a mob until the client in question is logged into a different mob.
+ var/datum/persistent_client/persistent_client
var/shift_to_open_context_menu = TRUE
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index ffc20194084..73b59d9f295 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -376,7 +376,7 @@
to_chat(M, "Your mob has been taken over by a ghost!")
message_admins("[key_name_admin(chosen_one)] has taken control of ([ADMIN_LOOKUPFLW(M)])")
M.ghostize(FALSE)
- M.key = chosen_one.key
+ M.PossessByPlayer(chosen_one.key)
M.client?.init_verbs()
return TRUE
else
diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm
index 56cd102a59b..fd2280dba72 100644
--- a/code/modules/mob/mob_transformation_simple.dm
+++ b/code/modules/mob/mob_transformation_simple.dm
@@ -67,7 +67,7 @@
mind.transfer_to(desired_mob, 1) // second argument to force key move to new mob
else
- desired_mob.key = key
+ desired_mob.PossessByPlayer(key)
SEND_SIGNAL(src, COMSIG_MOB_CHANGED_TYPE, desired_mob)
if(delete_old_mob)
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index df33c71305e..2ac1dc56c1a 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -155,7 +155,7 @@
mind.active = FALSE
mind.transfer_to(new_borg, TRUE)
else if(transfer_after)
- new_borg.key = key
+ new_borg.PossessByPlayer(key)
if(new_borg.mmi)
new_borg.mmi.name = "[initial(new_borg.mmi.name)]: [real_name]"
@@ -222,7 +222,7 @@
new_xeno = new /mob/living/carbon/alien/adult/drone(loc)
new_xeno.set_combat_mode(TRUE)
- new_xeno.key = key
+ new_xeno.PossessByPlayer(key)
to_chat(new_xeno, span_boldnotice("You are now an alien."))
qdel(src)
@@ -254,7 +254,7 @@
else
new_slime = new /mob/living/basic/slime(loc)
new_slime.set_combat_mode(TRUE)
- new_slime.key = key
+ new_slime.PossessByPlayer(key)
to_chat(new_slime, span_boldnotice("You are now a slime. Skreee!"))
qdel(src)
@@ -262,7 +262,7 @@
/mob/proc/become_overmind(starting_points = OVERMIND_STARTING_POINTS)
var/mob/eye/blob/B = new /mob/eye/blob(get_turf(src), starting_points)
- B.key = key
+ B.PossessByPlayer(key)
. = B
qdel(src)
@@ -282,7 +282,7 @@
var/mob/living/basic/pet/dog/corgi/new_corgi = new /mob/living/basic/pet/dog/corgi (loc)
new_corgi.set_combat_mode(TRUE)
- new_corgi.key = key
+ new_corgi.PossessByPlayer(key)
to_chat(new_corgi, span_boldnotice("You are now a Corgi. Yap Yap!"))
qdel(src)
@@ -333,7 +333,7 @@
if(mind)
mind.transfer_to(new_gorilla)
else
- new_gorilla.key = key
+ new_gorilla.PossessByPlayer(key)
to_chat(new_gorilla, span_boldnotice("You are now a gorilla. Ooga ooga!"))
qdel(src)
return new_gorilla
@@ -365,7 +365,7 @@
var/mob/living/new_mob = new mobpath(src.loc)
- new_mob.key = key
+ new_mob.PossessByPlayer(key)
new_mob.set_combat_mode(TRUE)
to_chat(new_mob, span_boldnotice("You suddenly feel more... animalistic."))
@@ -384,7 +384,7 @@
var/mob/living/new_mob = new mobpath(src.loc)
- new_mob.key = key
+ new_mob.PossessByPlayer(key)
new_mob.set_combat_mode(TRUE)
to_chat(new_mob, span_boldnotice("You feel more... animalistic."))
diff --git a/code/modules/mob_spawn/mob_spawn.dm b/code/modules/mob_spawn/mob_spawn.dm
index b4ff1c48b6c..166f3132a13 100644
--- a/code/modules/mob_spawn/mob_spawn.dm
+++ b/code/modules/mob_spawn/mob_spawn.dm
@@ -242,7 +242,7 @@
if(mob_possessor.mind)
mob_possessor.mind.transfer_to(spawned_mob, force_key_move = TRUE)
else
- spawned_mob.key = mob_possessor.key
+ spawned_mob.PossessByPlayer(mob_possessor.key)
var/datum/mind/spawned_mind = spawned_mob.mind
if(spawned_mind)
spawned_mob.mind.set_assigned_role_with_greeting(SSjob.get_job_type(spawner_job_path))
diff --git a/code/modules/pai/card.dm b/code/modules/pai/card.dm
index 40e1aaf6c05..769dc58aa59 100644
--- a/code/modules/pai/card.dm
+++ b/code/modules/pai/card.dm
@@ -212,7 +212,7 @@
var/mob/living/silicon/pai/new_pai = new(src)
new_pai.name = candidate.name || pick(GLOB.ninja_names)
new_pai.real_name = new_pai.name
- new_pai.key = candidate.ckey
+ new_pai.PossessByPlayer(candidate.ckey)
set_personality(new_pai)
SSpai.candidates -= ckey
return TRUE
diff --git a/code/modules/pai/debug.dm b/code/modules/pai/debug.dm
index 089dcedfabb..1cc0115c747 100644
--- a/code/modules/pai/debug.dm
+++ b/code/modules/pai/debug.dm
@@ -24,7 +24,7 @@
pai.name = chosen_name
pai.real_name = pai.name
- pai.key = choice.key
+ pai.PossessByPlayer(choice.key)
card.set_personality(pai)
if(SSpai.candidates[key])
SSpai.candidates -= key
@@ -38,7 +38,7 @@
/mob/proc/make_pai(delete_old)
var/obj/item/pai_card/card = new(src)
var/mob/living/silicon/pai/pai = new(card)
- pai.key = key
+ pai.PossessByPlayer(key)
pai.name = name
card.set_personality(pai)
if(delete_old)
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index c7f457ee427..02238021e33 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -378,7 +378,7 @@
if(chosen_one)
to_chat(target, span_boldnotice("You have been noticed by a ghost and it has possessed you!"))
var/mob/dead/observer/ghosted_target = target.ghostize(FALSE)
- target.key = chosen_one.key
+ target.PossessByPlayer(chosen_one.key)
trauma.add_friend(ghosted_target)
else
to_chat(target, span_notice("Your mind has managed to go unnoticed in the spirit world."))
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 9ef5ac84d2a..71e8f04b4b5 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -736,7 +736,7 @@
being_used = FALSE
return
- dumb_mob.key = ghost.key
+ dumb_mob.PossessByPlayer(ghost.key)
dumb_mob.mind.enslave_mind_to_creator(user)
SEND_SIGNAL(dumb_mob, COMSIG_SIMPLEMOB_SENTIENCEPOTION, user)
diff --git a/code/modules/spells/spell_types/pointed/mind_transfer.dm b/code/modules/spells/spell_types/pointed/mind_transfer.dm
index 0a800385c53..0d5fece02d1 100644
--- a/code/modules/spells/spell_types/pointed/mind_transfer.dm
+++ b/code/modules/spells/spell_types/pointed/mind_transfer.dm
@@ -129,7 +129,7 @@
// Just in case the swappee's key wasn't grabbed by transfer_to...
if(to_swap_key)
- caster.key = to_swap_key
+ caster.PossessByPlayer(to_swap_key)
// MIND TRANSFER END
diff --git a/tgstation.dme b/tgstation.dme
index 5a4a9c08382..da054778db5 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3797,7 +3797,7 @@
#include "code\modules\client\client_defines.dm"
#include "code\modules\client\client_procs.dm"
#include "code\modules\client\message.dm"
-#include "code\modules\client\player_details.dm"
+#include "code\modules\client\persistent_client.dm"
#include "code\modules\client\preferences.dm"
#include "code\modules\client\preferences_menu.dm"
#include "code\modules\client\preferences_savefile.dm"