diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index cdfb757179f..40f6ae29946 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -265,27 +265,44 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
log_access("Login: [key_name(src)] from [address ? address : "localhost"]-[computer_id] || BYOND v[full_version]")
var/alert_mob_dupe_login = FALSE
+ var/alert_admin_multikey = FALSE
if(CONFIG_GET(flag/log_access))
- for(var/I in GLOB.clients)
- if(!I || I == src)
+ var/list/joined_players = list()
+ for(var/player_ckey in GLOB.joined_player_list)
+ joined_players[player_ckey] = 1
+
+ for(var/joined_player_ckey in (GLOB.directory | joined_players))
+ if (!joined_player_ckey || joined_player_ckey == ckey)
continue
- var/client/C = I
- if(C.key && (C.key != key) )
- var/matches
- if( (C.address == address) )
- matches += "IP ([address])"
- if( (C.computer_id == computer_id) )
- if(matches)
- matches += " and "
- matches += "ID ([computer_id])"
- alert_mob_dupe_login = TRUE
+
+ var/datum/preferences/joined_player_preferences = GLOB.preferences_datums[joined_player_ckey]
+ if(!joined_player_preferences)
+ continue //this shouldn't happen.
+
+ var/client/C = GLOB.directory[joined_player_ckey]
+ var/in_round = ""
+ if (joined_players[joined_player_ckey])
+ in_round = " who has played in the current round"
+ var/message_type = "Notice"
+
+ var/matches
+ if(joined_player_preferences.last_ip == address)
+ matches += "IP ([address])"
+ if(joined_player_preferences.last_id == computer_id)
if(matches)
- if(C)
- message_admins(span_danger("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)]."))
- log_admin_private("Notice: [key_name(src)] has the same [matches] as [key_name(C)].")
- else
- message_admins(span_danger("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)] (no longer logged in). "))
- log_admin_private("Notice: [key_name(src)] has the same [matches] as [key_name(C)] (no longer logged in).")
+ matches = "BOTH [matches] and "
+ alert_admin_multikey = TRUE
+ message_type = "MULTIKEY"
+ matches += "Computer ID ([computer_id])"
+ alert_mob_dupe_login = TRUE
+
+ if(matches)
+ if(C)
+ message_admins(span_danger("[message_type]: Connecting player [key_name_admin(src)] has the same [matches] as [key_name_admin(C)][in_round]."))
+ log_admin_private("[message_type]: Connecting player [key_name(src)] has the same [matches] as [key_name(C)][in_round].")
+ 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
@@ -330,7 +347,15 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
tgui_panel.initialize()
if(alert_mob_dupe_login && !holder)
- INVOKE_ASYNC(GLOBAL_PROC, /proc/tgui_alert, mob, "You have logged in already with another key this round, please log out of this one NOW or risk being banned!")
+ var/dupe_login_message = "Your ComputerID has already logged in with another key this round, please log out of this one NOW or risk being banned!"
+ if (alert_admin_multikey)
+ dupe_login_message += "\nAdmins have been informed."
+ message_admins(span_danger("MULTIKEYING: [key_name_admin(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned."))
+ log_admin_private("MULTIKEYING: [key_name(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.")
+ spawn(0.5 SECONDS) //needs to run during world init, do not convert to add timer
+ alert(mob, dupe_login_message) //players get banned if they don't see this message, do not convert to tgui_alert (or even tg_alert) please.
+ to_chat(mob, span_danger(dupe_login_message))
+
connection_time = world.time
connection_realtime = world.realtime