mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-06-23 07:05:13 +01:00
546dc7fdfa
## About The Pull Request [cameras should actually show all of their view](https://github.com/tgstation/tgstation/commit/a8ef0f3bd7f16e590b9fae193f645c6cb6255f8f) Melb fucked up luminosity a bit (cries) [Implements camera range as the deciding factor for who can see a chunk](https://github.com/tgstation/tgstation/commit/bf4f9776dfce1c15e756ae1933a96ff0cff2dbf0) This allows us to safely modify chunk size (which I do, down to 8, which increases the actual cost involved in doing a full z update, but decreases the cost per chunk significantly) I use the range to macro optimize chunk New, we can safely use a bounding box instead of multiple urange calls and get about 1/3 the performance hit, even with a lower range (and thus 4x as many chunks) Standard caching and such applies [Fixes cameras not looking down correctly](https://github.com/tgstation/tgstation/commit/99b23f25203f34f04c0f92bb7d191699b91a439a) [Implements yielding support for individual chunks](https://github.com/tgstation/tgstation/commit/bdd2d01106465b8498307c16f83e9dd90706652a) This should functionally remove lagspikes from the cameras subsystem, which just leaves the problem of ais flying around. Much better tick obedience: https://github.com/user-attachments/assets/607d61a4-ad96-450c-acdf-10da967ab49b I tested this pr with #94522 to confirm that halving chunk sizes would have a benefit, see below: <img width="364" height="176" alt="image" src="https://github.com/user-attachments/assets/19f3ae7f-408b-4da5-8e38-a691030138fe" /> <img width="462" height="195" alt="image" src="https://github.com/user-attachments/assets/492b93bf-93ae-4227-9c6d-c5b2f0be9711" /> <img width="469" height="229" alt="image" src="https://github.com/user-attachments/assets/dba92982-09e4-4c3a-96e2-15e1a299676b" /> <img width="444" height="233" alt="image" src="https://github.com/user-attachments/assets/40b1e0dc-c03a-4acd-bba9-f1e4c68bffad" /> Things to note: Mean cost of finding turfs goes from 2ms to 1ms Max cost of finding turfs goes down by somewhere between 30% and 50%. I'd call this worthwhile, even if total cost of updating has gone up slightly. [Properly manages the camera refs in chunks](https://github.com/tgstation/tgstation/pull/94530/commits/00c97183ca83a91d95b9afcde70814c36a8f9a5e) [00c9718](https://github.com/tgstation/tgstation/pull/94530/commits/00c97183ca83a91d95b9afcde70814c36a8f9a5e) Turns out our solution for moving cameras in chunk code was to just... NOT CARE!! This means any moving camera is going to cause hard deletes. This was an issue during unit tests because of a stripping unit test. I suspect it only showed up here because I reduced the chunk size so we actually enter/leave a new set of chunks. I fixed this by reworking things to use a "leaving/joining" pattern. There's some other stuff here, mostly cleaning up old copypasta'd cruft in the code adjacent to moving cameras. I also changed the cameras and processing cameras alists back into lists, mostly for the sake of vv ability this didn't actually break anything. The harddel wasn't showing up because it was a number keyed alist, and reference tracking isn't built to find those (yet, pr up) ## Why It's Good For The Game I want AIs flying around to not cause massive lag spikes. That + the camera subsystem are one of like 4 things that's causing reliable tidi on live and it makes me upset. ## Changelog 🆑 fix: Cameras should work a little better (won't fail in the dark, will display multiz correctly refactor: Changed how static turf generation thinks about cameras. Shouldn't break anything but it MIGHT so keep an eye out for that. The chunks we use to divvy up the world for them are also 1/4th the area now, which should make flying around as ai less lagsikey /🆑 --------- Co-authored-by: Lucy <lucy@absolucy.moe>
242 lines
8.0 KiB
Plaintext
242 lines
8.0 KiB
Plaintext
#define DEFAULT_MAP_SIZE 15
|
|
|
|
/datum/computer_file/program/secureye
|
|
filename = "secureye"
|
|
filedesc = "SecurEye"
|
|
downloader_category = PROGRAM_CATEGORY_SECURITY
|
|
ui_header = "borg_mon.gif"
|
|
program_open_overlay = "generic"
|
|
extended_desc = "This program allows access to standard security camera networks."
|
|
program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
|
|
download_access = list(ACCESS_SECURITY)
|
|
can_run_on_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
|
|
size = 5
|
|
tgui_id = "NtosSecurEye"
|
|
program_icon = "eye"
|
|
always_update_ui = TRUE
|
|
|
|
///Boolean on whether or not the app will make noise when flipping around the channels.
|
|
var/spying = FALSE
|
|
|
|
var/list/network = list(CAMERANET_NETWORK_SS13)
|
|
///List of weakrefs of all users watching the program.
|
|
var/list/concurrent_users = list()
|
|
|
|
/// Weakref to the active camera
|
|
var/datum/weakref/camera_ref
|
|
/// The turf where the camera was last updated.
|
|
var/turf/last_camera_turf
|
|
|
|
// Stuff needed to render the map
|
|
var/atom/movable/screen/map_view/camera/cam_screen
|
|
|
|
///Internal tracker used to find a specific person and keep them on cameras, only used if this is a 'spying' console.
|
|
var/datum/trackable/internal_tracker
|
|
|
|
///Syndicate subtype that has no access restrictions and is available on Syndinet
|
|
/datum/computer_file/program/secureye/syndicate
|
|
filename = "syndeye"
|
|
filedesc = "SyndEye"
|
|
extended_desc = "This program allows for illegal access to security camera networks."
|
|
download_access = null
|
|
can_run_on_flags = PROGRAM_ALL
|
|
program_flags = PROGRAM_ON_SYNDINET_STORE | PROGRAM_UNIQUE_COPY
|
|
|
|
network = list(
|
|
CAMERANET_NETWORK_SS13,
|
|
CAMERANET_NETWORK_MINE,
|
|
CAMERANET_NETWORK_RD,
|
|
CAMERANET_NETWORK_LABOR,
|
|
CAMERANET_NETWORK_ORDNANCE,
|
|
CAMERANET_NETWORK_MINISAT,
|
|
)
|
|
spying = TRUE
|
|
|
|
///Human AI subtype that has access to most networks on the station and can't be copied.
|
|
/datum/computer_file/program/secureye/human_ai
|
|
filename = "Overseer"
|
|
filedesc = "OverSeer"
|
|
run_access = list(ACCESS_MINISAT)
|
|
can_run_on_flags = PROGRAM_PDA
|
|
program_flags = PROGRAM_UNIQUE_COPY
|
|
network = list(
|
|
CAMERANET_NETWORK_SS13,
|
|
CAMERANET_NETWORK_MINE,
|
|
CAMERANET_NETWORK_RD,
|
|
CAMERANET_NETWORK_LABOR,
|
|
CAMERANET_NETWORK_ORDNANCE,
|
|
CAMERANET_NETWORK_MINISAT,
|
|
)
|
|
spying = TRUE
|
|
|
|
/datum/computer_file/program/secureye/on_install(datum/computer_file/source, obj/item/modular_computer/computer_installing, mob/user)
|
|
. = ..()
|
|
// Map name has to start and end with an A-Z character,
|
|
// and definitely NOT with a square bracket or even a number.
|
|
var/map_name = "camera_console_[REF(src)]_map"
|
|
// Convert networks to lowercase
|
|
for(var/i in network)
|
|
network -= i
|
|
network += LOWER_TEXT(i)
|
|
// Initialize map objects
|
|
cam_screen = new
|
|
cam_screen.generate_view(map_name)
|
|
|
|
/datum/computer_file/program/secureye/Destroy()
|
|
QDEL_NULL(cam_screen)
|
|
QDEL_NULL(internal_tracker)
|
|
last_camera_turf = null
|
|
return ..()
|
|
|
|
/datum/computer_file/program/secureye/kill_program(mob/user)
|
|
if(user)
|
|
ui_close(user)
|
|
return ..()
|
|
|
|
/datum/computer_file/program/secureye/ui_interact(mob/user, datum/tgui/ui)
|
|
// Update the camera, showing static if necessary and updating data if the location has moved.
|
|
update_active_camera_screen()
|
|
|
|
var/user_ref = REF(user)
|
|
var/is_living = isliving(user)
|
|
// Ghosts shouldn't count towards concurrent users, which produces
|
|
// an audible terminal_on click.
|
|
if(is_living)
|
|
concurrent_users += user_ref
|
|
// Register map objects
|
|
cam_screen.display_to(user, ui.window)
|
|
|
|
/datum/computer_file/program/secureye/ui_status(mob/user, datum/ui_state/state)
|
|
. = ..()
|
|
if(. == UI_DISABLED)
|
|
return UI_CLOSE
|
|
return .
|
|
|
|
/datum/computer_file/program/secureye/ui_data()
|
|
var/list/data = list()
|
|
data["activeCamera"] = null
|
|
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
|
|
if(active_camera)
|
|
data["activeCamera"] = list(
|
|
name = active_camera.c_tag,
|
|
ref = REF(active_camera),
|
|
status = active_camera.camera_enabled,
|
|
)
|
|
return data
|
|
|
|
/datum/computer_file/program/secureye/ui_static_data(mob/user)
|
|
var/list/data = list()
|
|
data["network"] = network
|
|
data["mapRef"] = cam_screen.assigned_map
|
|
data["can_spy"] = !!spying
|
|
data["cameras"] = SScameras.get_available_cameras_data(network)
|
|
return data
|
|
|
|
/datum/computer_file/program/secureye/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
switch(action)
|
|
if("switch_camera")
|
|
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
|
|
if(!spying && active_camera)
|
|
active_camera.on_stop_watching(src)
|
|
|
|
if(!spying)
|
|
playsound(computer, SFX_TERMINAL_TYPE, 25, FALSE)
|
|
|
|
var/obj/machinery/camera/selected_camera = locate(params["camera"]) in SScameras.cameras
|
|
if(selected_camera)
|
|
camera_ref = WEAKREF(selected_camera)
|
|
else
|
|
camera_ref = null
|
|
return TRUE
|
|
if(!spying)
|
|
selected_camera.on_start_watching(src)
|
|
if(internal_tracker)
|
|
internal_tracker.reset_tracking()
|
|
|
|
update_active_camera_screen()
|
|
return TRUE
|
|
|
|
if("start_tracking")
|
|
if(!internal_tracker)
|
|
internal_tracker = new(src)
|
|
RegisterSignal(internal_tracker, COMSIG_TRACKABLE_TRACKING_TARGET, PROC_REF(on_track_target))
|
|
internal_tracker.track_input(usr)
|
|
return TRUE
|
|
|
|
/datum/computer_file/program/secureye/proc/on_track_target(datum/trackable/source, mob/living/target)
|
|
SIGNAL_HANDLER
|
|
var/target_turf = get_turf(target)
|
|
var/datum/camerachunk/target_camerachunk = SScameras.get_turf_camera_chunk(target_turf)
|
|
if(!target_camerachunk)
|
|
CRASH("[src] was able to track [target] through /datum/trackable, but was not on a visible turf to cameras.")
|
|
for(var/obj/machinery/camera/cameras as anything in target_camerachunk.cameras["[target.z]"])
|
|
// We need to find a particular camera that can see this turf
|
|
if(length(cameras.can_see() & list(target_turf)))
|
|
continue
|
|
var/new_camera = WEAKREF(cameras)
|
|
if(camera_ref == new_camera)
|
|
return
|
|
camera_ref = new_camera
|
|
update_active_camera_screen()
|
|
return
|
|
|
|
/datum/computer_file/program/secureye/ui_close(mob/user)
|
|
. = ..()
|
|
//don't track anyone while we're shutting off.
|
|
if(internal_tracker)
|
|
internal_tracker.reset_tracking()
|
|
var/user_ref = REF(user)
|
|
var/is_living = isliving(user)
|
|
// Living creature or not, we remove you anyway.
|
|
concurrent_users -= user_ref
|
|
// Unregister map objects
|
|
cam_screen.hide_from(user)
|
|
// Turn off the console
|
|
if(length(concurrent_users) == 0 && is_living)
|
|
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
|
|
if(!spying && active_camera)
|
|
active_camera.on_stop_watching(src)
|
|
camera_ref = null
|
|
last_camera_turf = null
|
|
if(!spying)
|
|
playsound(computer, 'sound/machines/terminal/terminal_off.ogg', 25, FALSE)
|
|
|
|
/datum/computer_file/program/secureye/proc/update_active_camera_screen()
|
|
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
|
|
// Show static if can't use the camera
|
|
if(!active_camera?.can_use())
|
|
cam_screen.show_camera_static()
|
|
return
|
|
|
|
var/list/visible_turfs = list()
|
|
|
|
// Get the camera's turf to correctly gather what's visible from its turf, in case it's located in a moving object (borgs / mechs)
|
|
var/new_cam_turf = get_turf(active_camera)
|
|
|
|
// If we're not forcing an update for some reason and the cameras are in the same location,
|
|
// we don't need to update anything.
|
|
// Most security cameras will end here as they're not moving.
|
|
if(last_camera_turf == new_cam_turf)
|
|
return
|
|
|
|
// Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs.
|
|
last_camera_turf = new_cam_turf
|
|
|
|
//Here we gather what's visible from the camera's POV based on its view_range and xray modifier if present
|
|
var/list/visible_things = active_camera.isXRay(ignore_malf_upgrades = TRUE) ? range(active_camera.view_range, new_cam_turf) : view(active_camera.view_range, new_cam_turf)
|
|
|
|
for(var/turf/visible_turf in visible_things)
|
|
visible_turfs += visible_turf
|
|
|
|
//Get coordinates for a rectangle area that contains the turfs we see so we can then clear away the static in the resulting rectangle area
|
|
var/list/bbox = get_bbox_of_atoms(visible_turfs)
|
|
var/size_x = bbox[3] - bbox[1] + 1
|
|
var/size_y = bbox[4] - bbox[2] + 1
|
|
|
|
cam_screen.show_camera(visible_turfs, size_x, size_y)
|
|
|
|
#undef DEFAULT_MAP_SIZE
|