Skip to content
This repository was archived by the owner on May 22, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions code/modules/admin/admin.dm
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,23 @@
set name = "Create or modify area"
create_area(usr)

/datum/admins/proc/observe_follow(atom/movable/AM)
if(!isobserver(owner.mob) && !check_rights(R_ADMIN))
return
var/can_ghost = TRUE
if(!isobserver(owner.mob))
can_ghost = owner.admin_ghost()

if(!can_ghost)
return
var/mob/dead/observer/A = owner.mob
var/mob/living/silicon/ai/I = AM //yogs start - adminfollow now follows AI eyes instead of the core
if(istype(I) && I.eyeobj)
A.ManualFollow(I.eyeobj)
else
A.ManualFollow(AM) //yogs stop - adminfollow now follows AI eyes instead of the core


//
//
//ALL DONE
Expand Down
3 changes: 2 additions & 1 deletion code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ GLOBAL_PROTECT(admin_verbs_admin)
/datum/admins/proc/cmd_create_centcom,
/datum/admins/proc/cmd_admin_fuckrads,
/client/proc/admincryo,
/client/proc/cmd_admin_dress
/client/proc/cmd_admin_dress,
/client/proc/disconnect_panel
)
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/ban_panel, /client/proc/stickybanpanel))
GLOBAL_PROTECT(admin_verbs_ban)
Expand Down
18 changes: 1 addition & 17 deletions code/modules/admin/topic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1242,24 +1242,8 @@
show_player_panel(M)

else if(href_list["adminplayerobservefollow"])
if(!isobserver(usr) && !check_rights(R_ADMIN))
return

var/atom/movable/AM = locate(href_list["adminplayerobservefollow"])

var/client/C = usr.client
var/can_ghost = TRUE
if(!isobserver(usr))
can_ghost = C.admin_ghost()

if(!can_ghost)
return
var/mob/dead/observer/A = C.mob
var/mob/living/silicon/ai/I = AM //yogs start - adminfollow now follows AI eyes instead of the core
if(istype(I) && I.eyeobj)
A.ManualFollow(I.eyeobj)
else
A.ManualFollow(AM) //yogs stop - adminfollow now follows AI eyes instead of the core
observe_follow(AM)

else if(href_list["admingetmovable"])
if(!check_rights(R_ADMIN))
Expand Down
124 changes: 124 additions & 0 deletions code/modules/admin/verbs/disconnectpanel.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
GLOBAL_LIST_EMPTY(connection_logs)

/datum/connection_log
var/datum/connection_entry/last_data_point
var/first_login
var/list/datum/connection_entry/data_points = list()

/datum/connection_log/New()
first_login = world.time

/datum/connection_log/proc/logout(mob/C)
var/datum/connection_entry/CE = new()
CE.disconnected = world.time
CE.disconnect_type = C.type
CE.living = isliving(C)
CE.job = C.mind?.assigned_role || "Ghost"
last_data_point = CE
data_points |= CE

/datum/connection_log/proc/login()
if(last_data_point)
last_data_point.connected = world.time

/datum/connection_entry
var/disconnected
var/connected
var/disconnect_type
var/living = FALSE
var/job

/client/proc/disconnect_panel()
set name = "Disconnect Panel"
set desc = "Panel showing information about the currently disconnected players"
set category = "Admin"
if(!check_rights(R_ADMIN))
return
new /datum/disconnect_panel(usr)

/datum/disconnect_panel
var/client/holder

/datum/disconnect_panel/New(user)
if(user)
setup(user)

/datum/disconnect_panel/proc/setup(user)
if(istype(user,/client))
holder = user
else
var/mob/user_mob = user
holder = user_mob.client

ui_interact(holder.mob)

/datum/disconnect_panel/ui_state(mob/user)
return GLOB.admin_state

/datum/disconnect_panel/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "DisconnectPanel")
ui.open()

/datum/disconnect_panel/ui_data(mob/user)
. = list()
.["world_time"] = world.time
.["users"] = list()
for(var/ckey in GLOB.connection_logs)
var/datum/connection_log/CL = GLOB.connection_logs[ckey]
if(!CL.last_data_point)
continue

var/ckey_data = list()
ckey_data["ckey"] = ckey
ckey_data["connected"] = !!CL.last_data_point.connected
ckey_data["last"] = entry2ui(CL.last_data_point)
ckey_data["history"] = list()

for(var/datum/connection_entry/entry in CL.data_points)
ckey_data["history"] += list(entry2ui(entry))

.["users"] += list(ckey_data)

/datum/disconnect_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/ckey = params["ckey"]
switch(action)
if("follow")
var/mob/M = ckey2mob(ckey)
if(!M)
return
usr.client.holder.observe_follow(M)
if("player-panel")
var/mob/M = ckey2mob(ckey)
if(!M)
return
usr.client.holder.show_player_panel(M)
if("pm")
usr.client.cmd_admin_pm(ckey)
if("notes")
if(!check_rights(R_ADMIN))
return
browse_messages(target_ckey = ckey, agegate = TRUE)
if("cryo")
var/mob/M = ckey2mob(ckey)
if(!M)
return
usr.client.admincryo(M)

/datum/disconnect_panel/proc/ckey2mob(ckey)
var/client/C = GLOB.directory[ckey]
if(C)
return C.mob
for(var/mob/M in GLOB.mob_list)
if(M.ckey == ckey)
return M

/datum/disconnect_panel/proc/entry2ui(datum/connection_entry/entry)
. = list()
.["disconnect"] = entry.disconnected
.["connect"] = entry.connected
.["type"] = entry.disconnect_type
.["living"] = entry.living
.["job"] = entry.job
11 changes: 11 additions & 0 deletions code/modules/client/client_procs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(params["discordlink"])
do_discord_link(params["discordlink"])

var/datum/connection_log/CL = GLOB.connection_logs[ckey]
if(CL)
CL.login()
else
GLOB.connection_logs[ckey] = new/datum/connection_log()

//////////////
//DISCONNECT//
Expand Down Expand Up @@ -593,13 +598,19 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
GLOB.ahelp_tickets.ClientLogout(src)
GLOB.directory -= ckey
GLOB.clients -= src

var/datum/connection_log/CL = GLOB.connection_logs[ckey]
if(CL)
CL.logout(mob)

QDEL_LIST_ASSOC_VAL(char_render_holders)
if(movingmob != null)
movingmob.client_mobs_in_contents -= mob
UNSETEMPTY(movingmob.client_mobs_in_contents)
seen_messages = null
Master.UpdateTickRate()
world.sync_logout_with_db(connection_number) // yogs - logout logging

return ..()

/client/proc/set_client_age_from_db(connectiontopic)
Expand Down
71 changes: 71 additions & 0 deletions tgui/packages/tgui/interfaces/DisconnectPanel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useBackend } from '../backend';
import { Section, Collapsible, Button } from '../components';
import { Window } from '../layouts';

const sec2time = function (seconds) {
let h = Math.floor(seconds / 3600);
let m = Math.floor(seconds % 3600 / 60).toString().padStart(2, "0");
let s = Math.floor(seconds % 3600 % 60).toString().padStart(2, "0");
return h + ":" + m + ":" + s;
};

export const DisconnectPanel = (props, context) => {
const { act, data } = useBackend(context);

data.users.sort((a, b) => {
if (a.connected !== b.connected) {
return a.connected - b.connected;
}
if (a.connected) {
return a.last.connect - b.last.connect;
}
return a.last.disconnect - b.last.disconnect;
});

return (
<Window
title="Disconnect Panel"
width={700}
height={700}
resizable>
<Window.Content scrollable>
<Section>
{data.users.map(user => (
<Collapsible
key={user.ckey}
color={user.connected ? 'green' : (user.last.living ? 'red' : 'yellow')}
title={user.ckey + " - "
+ (user.connected ? "Connected" : "Disconnected") + " - "
+ user.last.job + " - "
+ sec2time((data.world_time
- (user.connected ? user.last.connect : user.last.disconnect))/10)}>
<Section>
<Button onClick={() => act('follow', { ckey: user.ckey })}>Follow</Button>
<Button onClick={() => act('player-panel', { ckey: user.ckey })}>Player Panel</Button>
<Button onClick={() => act('pm', { ckey: user.ckey })}>Private Message</Button>
<Button onClick={() => act('notes', { ckey: user.ckey })}>Notes</Button>
<Button
onClick={() => act('acryo', { ckey: user.ckey })}
disabled={user.connected}>
Admin Cryo
</Button>
<Collapsible title="History">
{user.history.reverse().map(datapoint => (
<Section
key={user.ckey}
color={datapoint.living ? 'yellow' : 'green'}>
{sec2time(datapoint.disconnect/10)}-{sec2time(datapoint.connect/10)}
({sec2time(((datapoint.connect || data.world_time)
- datapoint.disconnect)/10)})
As {datapoint.job} ({datapoint.type})
</Section>
))}
</Collapsible>
</Section>
</Collapsible>
))}
</Section>
</Window.Content>
</Window>
);
};
1 change: 1 addition & 0 deletions yogstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,7 @@
#include "code\modules\admin\verbs\deadsay.dm"
#include "code\modules\admin\verbs\debug.dm"
#include "code\modules\admin\verbs\diagnostics.dm"
#include "code\modules\admin\verbs\disconnectpanel.dm"
#include "code\modules\admin\verbs\fps.dm"
#include "code\modules\admin\verbs\getlogs.dm"
#include "code\modules\admin\verbs\ghost_pool_protection.dm"
Expand Down