diff --git a/code/datums/extensions/network_lockable.dm b/code/datums/extensions/network_lockable.dm
new file mode 100644
index 00000000000..430ae0b0ed3
--- /dev/null
+++ b/code/datums/extensions/network_lockable.dm
@@ -0,0 +1,39 @@
+// Generic method to copy network lock access onto an atom without requiring a network connection
+/datum/extension/network_lockable
+ base_type = /datum/extension/network_lockable
+ expected_type = /obj
+ flags = EXTENSION_FLAG_IMMEDIATE
+
+// Returns TRUE on interaction, even on failure.
+/datum/extension/network_lockable/proc/process_interact(obj/item/I, mob/user)
+ var/obj/o_holder = holder
+ if(!istype(o_holder))
+ return FALSE
+
+ if(!length(o_holder.req_access))
+ if(istype(I, /obj/item/stock_parts/network_receiver/network_lock))
+ . = TRUE
+ var/obj/item/stock_parts/network_receiver/network_lock/lock = I
+ var/list/copy_access = lock.get_req_access()
+ if(!length(copy_access))
+ to_chat(user, SPAN_WARNING("You must set up the access on \the [lock] first!"))
+ return
+
+ to_chat(user, SPAN_NOTICE("You begin copying the access codes from \the [lock] into \the [o_holder]..."))
+ if(do_after(user, 2 SECONDS, o_holder))
+ playsound(o_holder, 'sound/machines/ping.ogg', 10, 0)
+ to_chat(user, SPAN_NOTICE("\The [o_holder] pings as it successfully copies its access codes from \the [lock]."))
+ o_holder.req_access = copy_access.Copy()
+ return
+ else if(IS_MULTITOOL(I)) // Resetting access.
+ . = TRUE
+ if(!o_holder.allowed(user))
+ to_chat(user, SPAN_WARNING("You lack access to reset the access codes on \the [o_holder]!"))
+ return
+ to_chat(user, SPAN_NOTICE("You begin resetting the access codes on \the [o_holder]..."))
+ if(do_after(user, 8 SECONDS, o_holder))
+ playsound(o_holder, 'sound/machines/ping.ogg', 10, 0)
+ to_chat(user, SPAN_NOTICE("\The [o_holder] pings as it resets its access codes."))
+ o_holder.req_access = null
+ return
+
diff --git a/code/game/machinery/_machines_base/machinery_components.dm b/code/game/machinery/_machines_base/machinery_components.dm
index 7d7dd280131..9a0346c17ed 100644
--- a/code/game/machinery/_machines_base/machinery_components.dm
+++ b/code/game/machinery/_machines_base/machinery_components.dm
@@ -37,7 +37,7 @@ var/global/list/machine_path_to_circuit_type
for(var/access_list in initial_access)
// Each part is an AND component.
var/obj/item/stock_parts/network_receiver/network_lock/lock = install_component(/obj/item/stock_parts/network_receiver/network_lock/buildable, refresh_parts = FALSE)
- lock.groups = islist(access_list) ? access_list : list(access_list)
+ lock.access_keys = islist(access_list) ? access_list : list(access_list)
// Create the parts we are supposed to have. If not full_populate, this is only hard-baked parts, and more will be added later.
for(var/component_path in uncreated_component_parts)
diff --git a/code/game/machinery/_machines_base/stock_parts/network_lock.dm b/code/game/machinery/_machines_base/stock_parts/network_lock.dm
index ca74dc09444..a5f8b4c1608 100644
--- a/code/game/machinery/_machines_base/stock_parts/network_lock.dm
+++ b/code/game/machinery/_machines_base/stock_parts/network_lock.dm
@@ -7,14 +7,17 @@
part_flags = PART_FLAG_QDEL
base_type = /obj/item/stock_parts/network_receiver/network_lock
- var/auto_deny_all // Set this to TRUE to deny all access attempts if network connection is lost.
+ var/requires_network = FALSE // Set this to TRUE to deny all access attempts if network connection is lost.
var/initial_network_id // The address to the network
var/initial_network_key // network KEY
var/selected_parent_group // Current selected parent_group for access assignment.
- var/list/groups // List of lists of groups. In order to access the device, users must have membership in at least one
- // of the groups in each list.
- var/selected_pattern // Index of the group pattern selected.
+ var/list/access_keys // List of lists of groups and account logins.
+ // In order to access the device, users must have membership in at least one
+ // of the groups or accounts in each list. Account logins are denoted with an @ at the end.
+
+ var/selected_pattern // Index of the access key pattern selected.
+ var/viewing_accounts = FALSE
var/emagged // Whether or not this has been emagged.
var/error
@@ -40,36 +43,40 @@
if(!is_functional())
return list()
- . = get_default_access()
var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device)
- var/datum/computer_network/network = D.get_network(NET_FEATURE_ACCESS)
- if(!network)
- return
+ var/datum/computer_network/network = D?.get_network(NET_FEATURE_ACCESS)
+ var/datum/extension/network_device/acl/access_controller = network?.access_controller
- var/datum/extension/network_device/acl/access_controller = network.access_controller
- if(!access_controller)
- return
+ var/list/all_groups = access_controller?.get_all_groups()
+ var/list/all_logins = network?.get_all_logins()
+
+ if(requires_network)
+ // Network locks require no access if the network is down and requires_network is toggled on
+ if(!network || !access_controller)
+ return list()
- LAZYINITLIST(groups)
+ LAZYINITLIST(access_keys)
var/list/resulting_access = list()
- for(var/list/pattern in groups)
+ for(var/list/pattern in access_keys)
var/list/resulting_pattern = list()
- for(var/group in pattern)
- if(!(group in access_controller.get_all_groups()))
- pattern -= group // This group doesn't exist anymore - delete it.
- continue
- resulting_pattern |= "[group].[D.network_id]"
+ for(var/access_key in pattern)
+ // This is an account login.
+ if(text_ends_with(access_key, "@"))
+ if(all_logins && !(copytext(access_key, 1, -1) in all_logins)) // This account doesn't exist anymore - delete it
+ pattern -= access_key
+ continue
+ resulting_pattern |= "[access_key][D.network_id]" // The @ is included in the pattern.
+ else
+ if(all_groups && !(access_key in all_groups)) // Ditto for groups
+ pattern -= access_key
+ continue
+ resulting_pattern |= "[access_key].[D.network_id]"
if(resulting_pattern.len)
resulting_access += list(resulting_pattern)
if(!length(resulting_access))
return
return resulting_access
-/obj/item/stock_parts/network_receiver/network_lock/proc/get_default_access()
- if(auto_deny_all)
- return list("NO_PERMISSIONS_DENY_ALL")
- return list()
-
/obj/item/stock_parts/network_receiver/network_lock/examine(mob/user)
. = ..()
if(emagged && user.skill_check_multiple(list(SKILL_FORENSICS = SKILL_EXPERT, SKILL_COMPUTER = SKILL_EXPERT)))
@@ -86,8 +93,8 @@
/obj/item/stock_parts/network_receiver/network_lock/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
if(istype(target, /obj/item/stock_parts/network_receiver/network_lock))
var/obj/item/stock_parts/network_receiver/network_lock/other_lock = target
- if(length(other_lock.groups)) // Prevent mistakingly copying from a blank lock instead of vice versa.
- groups = other_lock.groups.Copy()
+ if(length(other_lock.access_keys)) // Prevent mistakingly copying from a blank lock instead of vice versa.
+ access_keys = other_lock.access_keys.Copy()
playsound(src, 'sound/machines/ping.ogg', 20, 0)
to_chat(user, SPAN_NOTICE("\The [src] pings as it successfully copies its access requirements from the other network lock."))
@@ -112,47 +119,59 @@
data["connected"] = FALSE
return
data["connected"] = TRUE
- data["default_state"] = auto_deny_all
+ data["req_network"] = requires_network
if(!network.access_controller)
return
data["patterns"] = list()
var/pattern_index = 0
- for(var/list/pattern in groups)
+ for(var/list/pattern in access_keys)
pattern_index++
data["patterns"].Add(list(list(
"index" = "[pattern_index]",
- "groups" = english_list(pattern, "No groups assigned!", and_text = " or ")
+ "access" = english_list(pattern, "No access assigned!", and_text = " or ")
)))
- var/list/group_dictionary = network.access_controller.get_group_dict()
var/list/parent_groups_data
var/list/child_groups_data
-
- var/list/pattern = LAZYACCESS(groups, selected_pattern)
+ var/list/account_data
+ var/list/pattern = LAZYACCESS(access_keys, selected_pattern)
if(pattern)
- if(selected_parent_group)
- if(!(selected_parent_group in group_dictionary))
- selected_parent_group = null
- else
- var/list/child_groups = group_dictionary[selected_parent_group]
- if(child_groups)
- child_groups_data = list()
- for(var/child_group in child_groups)
- child_groups_data.Add(list(list(
- "child_group" = child_group,
- "assigned" = (LAZYISIN(pattern, child_group))
- )))
- if(!selected_parent_group) // Check again in case we ended up with a non-existent selected parent group instead of breaking the UI.
- parent_groups_data = list()
- for(var/parent_group in group_dictionary)
- parent_groups_data.Add(list(list(
- "parent_group" = parent_group,
- "assigned" = (LAZYISIN(pattern, parent_group))
- )))
+ if(viewing_accounts)
+ data["viewing_accounts"] = TRUE
+ var/list/login_list = network.get_all_logins()
+ if(length(login_list))
+ account_data = list()
+ for(var/login in login_list)
+ account_data.Add(list(list(
+ "login" = login,
+ "assigned" = (LAZYISIN(pattern, (login + "@")))
+ )))
+ else
+ var/list/group_dictionary = network.access_controller.get_group_dict()
+ if(selected_parent_group)
+ if(!(selected_parent_group in group_dictionary))
+ selected_parent_group = null
+ else
+ var/list/child_groups = group_dictionary[selected_parent_group]
+ if(length(child_groups))
+ child_groups_data = list()
+ for(var/child_group in child_groups)
+ child_groups_data.Add(list(list(
+ "child_group" = child_group,
+ "assigned" = (LAZYISIN(pattern, child_group))
+ )))
+ if(!selected_parent_group) // Check again in case we ended up with a non-existent selected parent group instead of breaking the UI.
+ parent_groups_data = list()
+ for(var/parent_group in group_dictionary)
+ parent_groups_data.Add(list(list(
+ "parent_group" = parent_group,
+ "assigned" = (LAZYISIN(pattern, parent_group))
+ )))
data["parent_groups"] = parent_groups_data
data["child_groups"] = child_groups_data
+ data["accounts"] = account_data
data["selected_parent_group"] = selected_parent_group
data["selected_pattern"] = "[selected_pattern]"
@@ -165,6 +184,12 @@
if(href_list["refresh"])
error = null
return TOPIC_REFRESH
+
+ if(href_list["back"])
+ viewing_accounts = FALSE
+ selected_parent_group = null
+ return TOPIC_REFRESH
+
var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device)
if(!D)
return
@@ -173,24 +198,20 @@
D.ui_interact(user)
return TOPIC_REFRESH
- if(href_list["allow_all"])
- auto_deny_all = FALSE
- return TOPIC_REFRESH
-
- if(href_list["deny_all"])
- auto_deny_all = TRUE
+ if(href_list["req_network"])
+ requires_network = !requires_network
return TOPIC_REFRESH
if(href_list["add_pattern"])
- if(length(groups) >= MAX_PATTERNS)
+ if(length(access_keys) >= MAX_PATTERNS)
to_chat(usr, SPAN_WARNING("You cannot add more than [MAX_PATTERNS] patterns to \the [src]!"))
return TOPIC_HANDLED
- LAZYADD(groups, list(list()))
+ LAZYADD(access_keys, list(list()))
return TOPIC_REFRESH
if(href_list["remove_pattern"])
var/pattern_index = text2num(href_list["remove_pattern"])
- LAZYREMOVE(groups, list(LAZYACCESS(groups, pattern_index))) // We have to encapsulate the pattern in another list to actually delete it.
+ LAZYREMOVE(access_keys, list(LAZYACCESS(access_keys, pattern_index))) // We have to encapsulate the pattern in another list to actually delete it.
if(selected_pattern == pattern_index)
selected_pattern = null
else if(selected_pattern > pattern_index)
@@ -199,11 +220,15 @@
if(href_list["select_pattern"])
var/pattern_index = text2num(href_list["select_pattern"])
- if(pattern_index > LAZYLEN(groups))
+ if(pattern_index > LAZYLEN(access_keys))
return TOPIC_HANDLED
selected_pattern = pattern_index
return TOPIC_REFRESH
+ if(href_list["view_accounts"])
+ viewing_accounts = TRUE
+ return TOPIC_REFRESH
+
if(href_list["select_parent_group"])
selected_parent_group = href_list["select_parent_group"]
return TOPIC_REFRESH
@@ -211,15 +236,32 @@
if(href_list["info"])
switch(href_list["info"])
if("pattern")
- to_chat(user, SPAN_NOTICE("In order to access the device, users must be a member of at least one group in each access pattern."))
+ to_chat(user, SPAN_NOTICE("In order to access the device, users must be a member of at least one group or account in each access pattern."))
if("parent_groups")
to_chat(user, SPAN_NOTICE("Assigning a parent group to an access pattern will permit any member of its child groups access to the pattern."))
+ if("req_network")
+ to_chat(user, SPAN_NOTICE("If toggled on, the network lock will automatically allow all access attempts if the network goes down. This should rarely be used."))
+
+ return TOPIC_HANDLED
- var/list/pattern_list = LAZYACCESS(groups, selected_pattern)
+ var/list/pattern_list = LAZYACCESS(access_keys, selected_pattern)
if(!pattern_list)
return TOPIC_REFRESH
+
+ var/datum/computer_network/network = D.get_network()
+ if(!network)
+ return TOPIC_REFRESH
+
+ var/datum/extension/network_device/acl/acl = network.access_controller
+ if(!acl)
+ return TOPIC_REFRESH
+
+
if(href_list["assign_group"])
+ var/list/valid_groups = acl.get_all_groups()
+ if(!(href_list["assign_group"] in valid_groups))
+ return TOPIC_REFRESH
pattern_list |= href_list["assign_group"]
return TOPIC_REFRESH
@@ -227,6 +269,17 @@
pattern_list -= href_list["remove_group"]
return TOPIC_REFRESH
+ if(href_list["assign_account"])
+ var/list/valid_accounts = network.get_all_logins()
+ if(!(href_list["assign_account"] in valid_accounts))
+ return TOPIC_REFRESH
+ pattern_list |= (href_list["assign_account"] + "@")
+ return TOPIC_REFRESH
+
+ if(href_list["remove_account"])
+ pattern_list -= (href_list["remove_account"] + "@")
+ return TOPIC_REFRESH
+
/obj/item/stock_parts/network_receiver/network_lock/ui_interact(mob/user, ui_key, datum/nanoui/ui, force_open, datum/nanoui/master_ui, datum/topic_state/state)
var/data = ui_data(user)
ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 04426a9e287..48bc5d875c0 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -340,9 +340,6 @@
/decl/stock_part_preset/network_lock/camera
expected_part_type = /obj/item/stock_parts/network_receiver/network_lock
-/decl/stock_part_preset/network_lock/camera/do_apply(obj/machinery/camera/machine, obj/item/stock_parts/network_receiver/network_lock/part)
- part.auto_deny_all = TRUE
-
/obj/machinery/camera
public_methods = list(
/decl/public_access/public_method/toggle_camera
diff --git a/code/game/objects/structures/crates_lockers/closets/_closet_appearance_definitions.dm b/code/game/objects/structures/crates_lockers/closets/_closet_appearance_definitions.dm
index 1e13e508629..92e3ef289e1 100644
--- a/code/game/objects/structures/crates_lockers/closets/_closet_appearance_definitions.dm
+++ b/code/game/objects/structures/crates_lockers/closets/_closet_appearance_definitions.dm
@@ -501,6 +501,15 @@
/decl/closet_appearance/secure_closet/sol/two/dark
color = COLOR_DARK_BLUE_GRAY
+/decl/closet_appearance/secure_closet/network
+ color = COLOR_OFF_WHITE
+ decals = list(
+ "lower_side_vent"
+ )
+ extra_decals = list(
+ "stripe_vertical_mid_full" = COLOR_CYAN_BLUE
+ )
+
// Crates.
/decl/closet_appearance/crate
decals = null
@@ -598,6 +607,13 @@
"crate_stripe_right" = COLOR_YELLOW_GRAY
)
+/decl/closet_appearance/crate/secure/network
+ color = COLOR_OFF_WHITE
+ extra_decals = list(
+ "crate_stripe_left" = COLOR_CYAN_BLUE,
+ "crate_stripe_right" = COLOR_CYAN_BLUE
+ )
+
// Large crates.
/decl/closet_appearance/large_crate
base_icon = 'icons/obj/closets/bases/large_crate.dmi'
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/network.dm b/code/game/objects/structures/crates_lockers/closets/secure/network.dm
new file mode 100644
index 00000000000..10b0aaa996d
--- /dev/null
+++ b/code/game/objects/structures/crates_lockers/closets/secure/network.dm
@@ -0,0 +1,22 @@
+/obj/structure/closet/secure_closet/network
+ name = "network secured locker"
+ desc = "A storage locker capable of storing access codes from a functioning network lock."
+
+ reinf_material = /decl/material/solid/organic/plastic
+ closet_appearance = /decl/closet_appearance/secure_closet/network
+ locked = FALSE
+
+/obj/structure/closet/secure_closet/Initialize()
+ . = ..()
+ set_extension(src, /datum/extension/network_lockable)
+
+/obj/structure/closet/secure_closet/network/attackby(obj/item/W, mob/user)
+ if(!opened)
+ var/datum/extension/network_lockable/lockable = get_extension(src, /datum/extension/network_lockable)
+ if(lockable.process_interact(W, user))
+ return TRUE
+
+ . = ..()
+
+/obj/structure/closet/secure_closet/network/get_mechanics_info()
+ return "[..()]
Can have its access set when closed with a functioning network lock.
Can have its access reset with a multitool."
\ No newline at end of file
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 695f17f89a6..1d1506e3ab7 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -293,4 +293,27 @@
closet_appearance = /decl/closet_appearance/crate/radiation
/obj/structure/closet/crate/uranium/WillContain()
- return list(/obj/item/stack/material/puck/mapped/uranium/ten = 5)
\ No newline at end of file
+ return list(/obj/item/stack/material/puck/mapped/uranium/ten = 5)
+
+/obj/structure/closet/crate/secure/network
+ name = "network secured crate"
+ desc = "A storage crate capable of storing access codes from a functioning network lock."
+ closet_appearance = /decl/closet_appearance/crate/secure/network
+
+ reinf_material = /decl/material/solid/organic/plastic
+ locked = FALSE
+
+/obj/structure/closet/crate/secure/network/Initialize()
+ . = ..()
+ set_extension(src, /datum/extension/network_lockable)
+
+/obj/structure/closet/crate/secure/network/attackby(obj/item/W, mob/user)
+ if(!opened)
+ var/datum/extension/network_lockable/lockable = get_extension(src, /datum/extension/network_lockable)
+ if(lockable.process_interact(W, user))
+ return TRUE
+
+ . = ..()
+
+/obj/structure/closet/crate/secure/network/get_mechanics_info()
+ return "[..()]
Can have its access set when closed with a functioning network lock.
Can have its access reset with a multitool."
\ No newline at end of file
diff --git a/code/modules/fabrication/designs/industrial/designs_structure.dm b/code/modules/fabrication/designs/industrial/designs_structure.dm
new file mode 100644
index 00000000000..417bff901c5
--- /dev/null
+++ b/code/modules/fabrication/designs/industrial/designs_structure.dm
@@ -0,0 +1,8 @@
+/datum/fabricator_recipe/industrial/structure
+ category = "Structures"
+
+/datum/fabricator_recipe/industrial/structure/network_closet
+ path = /obj/structure/closet/secure_closet/network
+
+/datum/fabricator_recipe/industrial/structure/network_crate
+ path = /obj/structure/closet/crate/secure/network
\ No newline at end of file
diff --git a/code/modules/modular_computers/networking/accounts/_network_accounts.dm b/code/modules/modular_computers/networking/accounts/_network_accounts.dm
index ecd642c5bc8..4f8b7a7a0be 100644
--- a/code/modules/modular_computers/networking/accounts/_network_accounts.dm
+++ b/code/modules/modular_computers/networking/accounts/_network_accounts.dm
@@ -16,6 +16,15 @@
result |= E
return result
+/datum/computer_network/proc/get_all_logins()
+ var/list/result = list()
+ for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(MF_ROLE_ACCOUNT_SERVER))
+ for(var/datum/computer_file/data/account/E in M.get_all_files())
+ if(E.backup)
+ continue
+ ADD_SORTED(result, E.login, /proc/cmp_text_asc)
+ return result
+
// Return account backups keyed by account login
/datum/computer_network/proc/get_account_backups(accesses)
var/list/result = list()
diff --git a/code/modules/modular_computers/networking/accounts/id_card.dm b/code/modules/modular_computers/networking/accounts/id_card.dm
index 87591829b5b..161447c432c 100644
--- a/code/modules/modular_computers/networking/accounts/id_card.dm
+++ b/code/modules/modular_computers/networking/accounts/id_card.dm
@@ -1,8 +1,14 @@
+#define CACHE_TIME 30 MINUTES
/obj/item/card/id/network
+ name = "network identification card"
+ desc = "A card used to provide ID and determine access. It connects remotely to a network to find its access codes."
var/weakref/current_account
color = COLOR_GRAY80
detail_color = COLOR_SKY_BLUE
+ var/list/cached_network_access = list()
+ var/last_cached // The real time of day the network access was last cached
+
/obj/item/card/id/network/Initialize()
set_extension(src, /datum/extension/network_device/id_card)
return ..()
@@ -12,14 +18,24 @@
var/datum/computer_file/data/account/access_account = resolve_account()
var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device)
var/datum/computer_network/network = D.get_network(NET_FEATURE_ACCESS)
- if(network && access_account && access_account.login != ignore_account)
- var/location = "[network.network_id]"
- if(access_account)
- . += "[access_account.login]@[location]" // User access uses '@'
+ if(network)
+ var/list/net_access = list()
+ if(access_account && access_account.login != ignore_account)
+ var/location = "[network.network_id]"
+ net_access += "[access_account.login]@[location]" // User access uses '@'
for(var/group in access_account.groups)
- . += "[group].[location]" // Group access uses '.'
+ net_access += "[group].[location]" // Group access uses '.'
for(var/group in access_account.parent_groups) // Membership in a child group grants access to anything with an access requirement set to the parent group.
- . += "[group].[location]"
+ net_access += "[group].[location]"
+
+ . += net_access
+ cached_network_access = net_access
+ last_cached = REALTIMEOFDAY
+ else if(REALTIMEOFDAY <= (last_cached + CACHE_TIME))
+ . += cached_network_access
+ else if(length(cached_network_access))
+ cached_network_access.Cut()
+ visible_message(SPAN_SUBTLE("\The [src] beeps, indicating that it cleared its cached access."), range = 1)
/obj/item/card/id/network/proc/resolve_account(net_feature = NET_FEATURE_ACCESS)
if(!current_account)
@@ -114,6 +130,9 @@
current_account = weakref(check_account)
return TRUE
+ // Cache the access post login.
+ GetAccess()
+
/obj/item/card/id/network/proc/get_network_id()
var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device)
var/datum/computer_network/network = D?.get_network()
@@ -128,3 +147,5 @@
"You flash your ID card: [html_icon(src)] [src.name]. The assignment on the card: '[src.assignment]'.")
src.add_fingerprint(usr)
+
+#undef CACHE_TIME
\ No newline at end of file
diff --git a/code/modules/modular_computers/networking/finance/money_accounts.dm b/code/modules/modular_computers/networking/finance/money_accounts.dm
index fc1f68fd107..f81eed45091 100644
--- a/code/modules/modular_computers/networking/finance/money_accounts.dm
+++ b/code/modules/modular_computers/networking/finance/money_accounts.dm
@@ -85,7 +85,7 @@
. = ..()
/datum/money_account/child/network/Destroy(force)
- var/datum/computer_file/data/account/attached_account = network_account.resolve()
+ var/datum/computer_file/data/account/attached_account = network_account?.resolve()
if(attached_account)
attached_account.money_account = null
. = ..()
diff --git a/mods/persistence/game/machinery/_machines_base/stock_parts/network_lock.dm b/mods/persistence/game/machinery/_machines_base/stock_parts/network_lock.dm
index 72ee15d0c76..a40b2fda065 100644
--- a/mods/persistence/game/machinery/_machines_base/stock_parts/network_lock.dm
+++ b/mods/persistence/game/machinery/_machines_base/stock_parts/network_lock.dm
@@ -9,7 +9,7 @@
initial_network_id = null
initial_network_key = null
-SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, groups)
+SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, access_keys)
SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, initial_network_id)
SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, initial_network_key)
SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, emagged)
\ No newline at end of file
diff --git a/nano/templates/network_lock.tmpl b/nano/templates/network_lock.tmpl
index 319618960fc..70a0dbadb1d 100644
--- a/nano/templates/network_lock.tmpl
+++ b/nano/templates/network_lock.tmpl
@@ -13,10 +13,11 @@
Disconnected from network.
{{else}}
-
Device Default State:
+
Network Requirement:
- {{:helper.link('ALLOW ALL', null, { "allow_all" : 1 }, !data.default_state ? 'disabled' : null)}}
- {{:helper.link('DENY ALL', null, { "deny_all" : 1 }, data.default_state ? 'disabled' : null)}}
+ {{:helper.link('On', null, { "req_network" : 1 }, data.req_network ? 'selected' : null)}}
+ {{:helper.link('Off', null, { "req_network" : 1 }, !data.req_network ? 'selected' : null)}}
+ {{:helper.link('?', null, { "info" : "req_network" }, null)}}
@@ -26,7 +27,7 @@
Operations
{{for data.patterns}}
| {{:helper.link("Pattern " + value.index, null, { "select_pattern" : value.index}, (data.selected_pattern == value.index) ? 'selected' : null)}}
- | {{:value.groups}}
+ | {{:value.access}}
| {{:helper.link('Delete pattern', null, { "remove_pattern" : value.index})}}
{{/for}}
@@ -34,23 +35,7 @@
{{:helper.link('Add pattern', null, { "add_pattern" : 1}, null)}}
{{:helper.link('?', null, { "info" : "pattern" }, null)}}
- {{if data.parent_groups}}
- Parent Groups:
- {{:helper.link('?', null, { "info" : "parent_groups" }, null)}}
-
- Group
- | Operations
- {{for data.parent_groups}}
- |
---|
{{:helper.link(value.parent_group, null, { "select_parent_group" : value.parent_group })}}
- |
- {{if value.assigned}}
- {{:helper.link('REMOVE', null, { "remove_group" : value.parent_group })}}
- {{else}}
- {{:helper.link('ASSIGN', null, { "assign_group" : value.parent_group })}}
- {{/if}}
- {{/for}}
- |
- {{else data.selected_parent_group}}
+ {{if data.selected_parent_group}}
Viewing Child Groups for: {{:data.selected_parent_group}}
{{if data.child_groups}}
Child Groups:
@@ -71,12 +56,50 @@
No child groups found!
{{/if}}
- {{:helper.link('Back to parent group listing', null, { "select_parent_group" : null })}}
+ {{:helper.link('Back to access listings', null, { "back" : 1 })}}
- {{else}}
- {{if data.selected_pattern}}
- No groups found on network!
+ {{else data.viewing_accounts}}
+ Viewing accounts:
+ {{if data.accounts}}
+
+ Account
+ | Operations
+ {{for data.accounts}}
+ |
---|
{{:value.login}}
+ |
+ {{if value.assigned}}
+ {{:helper.link('REMOVE', null, { "remove_account" : value.login })}}
+ {{else}}
+ {{:helper.link('ASSIGN', null, { "assign_account" : value.login })}}
+ {{/if}}
+ {{/for}}
+ |
+ {{else}}
+ No accounts found on network!
{{/if}}
+ {{:helper.link('Back to access listings', null, { "back" : 1 })}}
+ {{else data.selected_pattern}}
+ Parent Groups:
+ {{if data.parent_groups}}
+ {{:helper.link('?', null, { "info" : "parent_groups" }, null)}}
+
+ Group
+ | Operations
+ {{for data.parent_groups}}
+ |
---|
{{:helper.link(value.parent_group, null, { "select_parent_group" : value.parent_group })}}
+ |
+ {{if value.assigned}}
+ {{:helper.link('REMOVE', null, { "remove_group" : value.parent_group })}}
+ {{else}}
+ {{:helper.link('ASSIGN', null, { "assign_group" : value.parent_group })}}
+ {{/if}}
+ {{/for}}
+ |
+ {{else}}
+ No groups found on network!
+ {{/if}}
+
+ {{:helper.link('View accounts', null, { "view_accounts" : "1" }, null)}}
{{/if}}
{{/if}}
{{/if}}
\ No newline at end of file
diff --git a/nebula.dme b/nebula.dme
index 5098fb8015a..9d04ce19bc5 100644
--- a/nebula.dme
+++ b/nebula.dme
@@ -330,6 +330,7 @@
#include "code\datums\extensions\label.dm"
#include "code\datums\extensions\local_network.dm"
#include "code\datums\extensions\lockable.dm"
+#include "code\datums\extensions\network_lockable.dm"
#include "code\datums\extensions\parts_stash.dm"
#include "code\datums\extensions\penetration.dm"
#include "code\datums\extensions\radio_provider.dm"
@@ -1360,6 +1361,7 @@
#include "code\game\objects\structures\crates_lockers\closets\secure\guncabinet.dm"
#include "code\game\objects\structures\crates_lockers\closets\secure\hydroponics.dm"
#include "code\game\objects\structures\crates_lockers\closets\secure\medical.dm"
+#include "code\game\objects\structures\crates_lockers\closets\secure\network.dm"
#include "code\game\objects\structures\crates_lockers\closets\secure\nuke.dm"
#include "code\game\objects\structures\crates_lockers\closets\secure\personal.dm"
#include "code\game\objects\structures\crates_lockers\closets\secure\scientist.dm"
@@ -2029,6 +2031,7 @@
#include "code\modules\fabrication\designs\industrial\_designs_industrial.dm"
#include "code\modules\fabrication\designs\industrial\designs_armour.dm"
#include "code\modules\fabrication\designs\industrial\designs_exosuit_components.dm"
+#include "code\modules\fabrication\designs\industrial\designs_structure.dm"
#include "code\modules\fabrication\designs\meat\_designs_meat.dm"
#include "code\modules\fabrication\designs\meat\designs_organs.dm"
#include "code\modules\fabrication\designs\micro\designs_cutlery.dm"
|