mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
NTNet airlocks (#35947)
* Standardizes access checks * Makes SS Networks init before SS Atoms to prevent NTNet interfaces from initializing before them * Adds passkey support to NTNet packets * Adds NTNet support to airlocks, makes door remote use NTNet * Access levels given by jobs are now shuffled * Access code improvements * Adds IC card readers * Fixes a delay issue with opening/closing airlocks with NTNet or signalers * code review memes * Renames plaintext_passkey to encrypted_passkey * death by thousand nitpicks
This commit is contained in:
@@ -59,8 +59,8 @@
|
||||
#define INIT_ORDER_TRAITS 11
|
||||
#define INIT_ORDER_TICKER 10
|
||||
#define INIT_ORDER_MAPPING 9
|
||||
#define INIT_ORDER_ATOMS 8
|
||||
#define INIT_ORDER_NETWORKS 7
|
||||
#define INIT_ORDER_NETWORKS 8
|
||||
#define INIT_ORDER_ATOMS 7
|
||||
#define INIT_ORDER_LANGUAGE 6
|
||||
#define INIT_ORDER_MACHINES 5
|
||||
#define INIT_ORDER_CIRCUIT 4
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
parent.ntnet_recieve(data)
|
||||
|
||||
/datum/component/ntnet_interface/proc/__network_send(datum/netdata/data, netid) //Do not directly proccall!
|
||||
// Process data before sending it
|
||||
data.pre_send(src)
|
||||
|
||||
if(netid)
|
||||
if(networks_connected_by_id[netid])
|
||||
var/datum/ntnet/net = networks_connected_by_id[netid]
|
||||
|
||||
@@ -48,17 +48,15 @@
|
||||
return
|
||||
if(!A.requiresID() || A.check_access(null))
|
||||
if(A.density)
|
||||
A.open()
|
||||
INVOKE_ASYNC(A, /obj/machinery/door/airlock.proc/open)
|
||||
else
|
||||
A.close()
|
||||
INVOKE_ASYNC(A, /obj/machinery/door/airlock.proc/close)
|
||||
if(WIRE_BOLTS) // Pulse to toggle bolts (but only raise if power is on).
|
||||
if(!A.locked)
|
||||
A.bolt()
|
||||
A.audible_message("<span class='italics'>You hear a click from the bottom of the door.</span>", null, 1)
|
||||
else
|
||||
if(A.hasPower())
|
||||
A.unbolt()
|
||||
A.audible_message("<span class='italics'>You hear a click from the bottom of the door.</span>", null, 1)
|
||||
A.update_icon()
|
||||
if(WIRE_IDSCAN) // Pulse to disable emergency access and flash red lights.
|
||||
if(A.hasPower() && A.density)
|
||||
|
||||
@@ -153,6 +153,7 @@
|
||||
|
||||
/obj/machinery/door/airlock/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/ntnet_interface)
|
||||
AddComponent(/datum/component/rad_insulation, RAD_MEDIUM_INSULATION)
|
||||
|
||||
/obj/machinery/door/airlock/proc/update_other_id()
|
||||
@@ -189,6 +190,55 @@
|
||||
if ("cyclelinkeddir")
|
||||
cyclelinkairlock()
|
||||
|
||||
/obj/machinery/door/airlock/check_access_ntnet(datum/netdata/data)
|
||||
return !requiresID() || ..()
|
||||
|
||||
/obj/machinery/door/airlock/ntnet_recieve(datum/netdata/data)
|
||||
// Check if the airlock is powered and can accept control packets.
|
||||
if(!hasPower() || !canAIControl())
|
||||
return
|
||||
|
||||
// Check packet access level.
|
||||
if(!check_access_ntnet(data))
|
||||
return
|
||||
|
||||
// Handle recieved packet.
|
||||
var/command = lowertext(data.plaintext_data)
|
||||
var/command_value = lowertext(data.plaintext_data_secondary)
|
||||
switch(command)
|
||||
if("open")
|
||||
if(command_value == "on" && !density)
|
||||
return
|
||||
|
||||
if(command_value == "off" && density)
|
||||
return
|
||||
|
||||
if(density)
|
||||
INVOKE_ASYNC(src, .proc/open)
|
||||
else
|
||||
INVOKE_ASYNC(src, .proc/close)
|
||||
|
||||
if("bolt")
|
||||
if(command_value == "on" && locked)
|
||||
return
|
||||
|
||||
if(command_value == "off" && !locked)
|
||||
return
|
||||
|
||||
if(locked)
|
||||
unbolt()
|
||||
else
|
||||
bolt()
|
||||
|
||||
if("emergency")
|
||||
if(command_value == "on" && emergency)
|
||||
return
|
||||
|
||||
if(command_value == "off" && !emergency)
|
||||
return
|
||||
|
||||
emergency = !emergency
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/door/airlock/lock()
|
||||
bolt()
|
||||
@@ -198,6 +248,7 @@
|
||||
return
|
||||
locked = TRUE
|
||||
playsound(src,boltDown,30,0,3)
|
||||
audible_message("<span class='italics'>You hear a click from the bottom of the door.</span>", null, 1)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/door/airlock/unlock()
|
||||
@@ -208,6 +259,7 @@
|
||||
return
|
||||
locked = FALSE
|
||||
playsound(src,boltUp,30,0,3)
|
||||
audible_message("<span class='italics'>You hear a click from the bottom of the door.</span>", null, 1)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/door/airlock/narsie_act()
|
||||
@@ -321,7 +373,7 @@
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/door/airlock/proc/canAIControl(mob/user)
|
||||
return ((aiControlDisabled != 1) && (!isAllPowerCut()));
|
||||
return ((aiControlDisabled != 1) && !isAllPowerCut())
|
||||
|
||||
/obj/machinery/door/airlock/proc/canAIHack()
|
||||
return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerCut()));
|
||||
|
||||
@@ -45,11 +45,6 @@
|
||||
if(!poddoor)
|
||||
to_chat(user, "<span class='notice'>Its maintenance panel is <b>screwed</b> in place.</span>")
|
||||
|
||||
/obj/machinery/door/check_access(access)
|
||||
if(red_alert_access && GLOB.security_level >= SEC_LEVEL_RED)
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/obj/machinery/door/check_access_list(list/access_list)
|
||||
if(red_alert_access && GLOB.security_level >= SEC_LEVEL_RED)
|
||||
return TRUE
|
||||
|
||||
@@ -64,17 +64,6 @@
|
||||
time_coeff = round(initial(time_coeff) - (initial(time_coeff)*(T))/5,0.01)
|
||||
|
||||
|
||||
/obj/machinery/mecha_part_fabricator/check_access(obj/item/card/id/I)
|
||||
if(istype(I, /obj/item/device/pda))
|
||||
var/obj/item/device/pda/pda = I
|
||||
I = pda.id
|
||||
if(!istype(I) || !I.access) //not ID or no access
|
||||
return FALSE
|
||||
for(var/req in req_access)
|
||||
if(!(req in I.access)) //doesn't have this access
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/mecha_part_fabricator/emag_act()
|
||||
if(obj_flags & EMAGGED)
|
||||
return
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
var/mode = WAND_OPEN
|
||||
var/region_access = 1 //See access.dm
|
||||
var/obj/item/card/id/ID
|
||||
var/list/access_list
|
||||
|
||||
/obj/item/door_remote/New()
|
||||
..()
|
||||
ID = new /obj/item/card/id
|
||||
ID.access = get_region_accesses(region_access)
|
||||
/obj/item/door_remote/Initialize()
|
||||
. = ..()
|
||||
access_list = get_region_accesses(region_access)
|
||||
AddComponent(/datum/component/ntnet_interface)
|
||||
|
||||
/obj/item/door_remote/attack_self(mob/user)
|
||||
switch(mode)
|
||||
@@ -30,35 +30,30 @@
|
||||
mode = WAND_OPEN
|
||||
to_chat(user, "Now in mode: [mode].")
|
||||
|
||||
/obj/item/door_remote/afterattack(obj/machinery/door/airlock/D, mob/user)
|
||||
if(!istype(D))
|
||||
// Airlock remote works by sending NTNet packets to whatever it's pointed at.
|
||||
/obj/item/door_remote/afterattack(atom/A, mob/user)
|
||||
GET_COMPONENT_FROM(target_interface, /datum/component/ntnet_interface, A)
|
||||
|
||||
if(!target_interface)
|
||||
return
|
||||
if(!(D.hasPower()))
|
||||
to_chat(user, "<span class='danger'>[D] has no power!</span>")
|
||||
return
|
||||
if(!D.requiresID())
|
||||
to_chat(user, "<span class='danger'>[D]'s ID scan is disabled!</span>")
|
||||
return
|
||||
if(D.check_access(ID) && D.canAIControl(user))
|
||||
switch(mode)
|
||||
if(WAND_OPEN)
|
||||
if(D.density)
|
||||
D.open()
|
||||
else
|
||||
D.close()
|
||||
if(WAND_BOLT)
|
||||
if(D.locked)
|
||||
D.unbolt()
|
||||
else
|
||||
D.bolt()
|
||||
if(WAND_EMERGENCY)
|
||||
if(D.emergency)
|
||||
D.emergency = FALSE
|
||||
else
|
||||
D.emergency = TRUE
|
||||
D.update_icon()
|
||||
else
|
||||
to_chat(user, "<span class='danger'>[src] does not have access to this door.</span>")
|
||||
|
||||
// Generate a control packet.
|
||||
var/datum/netdata/data = new
|
||||
data.recipient_ids = list(target_interface.hardware_id)
|
||||
|
||||
switch(mode)
|
||||
if(WAND_OPEN)
|
||||
data.plaintext_data = "open"
|
||||
if(WAND_BOLT)
|
||||
data.plaintext_data = "bolt"
|
||||
if(WAND_EMERGENCY)
|
||||
data.plaintext_data = "emergency"
|
||||
|
||||
data.plaintext_data_secondary = "toggle"
|
||||
data.passkey = access_list
|
||||
|
||||
ntnet_send(data)
|
||||
|
||||
|
||||
/obj/item/door_remote/omni
|
||||
name = "omni door remote"
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
var/current_skin //Has the item been reskinned?
|
||||
var/list/unique_reskin //List of options to reskin.
|
||||
|
||||
// Access levels, used in modules\jobs\access.dm
|
||||
var/list/req_access
|
||||
var/req_access_txt = "0"
|
||||
var/list/req_one_access
|
||||
var/req_one_access_txt = "0"
|
||||
|
||||
|
||||
/obj/vv_edit_var(vname, vval)
|
||||
|
||||
@@ -6,7 +6,24 @@
|
||||
|
||||
var/plaintext_data
|
||||
var/plaintext_data_secondary
|
||||
var/plaintext_passkey
|
||||
var/encrypted_passkey
|
||||
|
||||
var/list/passkey
|
||||
|
||||
// Process data before sending it
|
||||
/datum/netdata/proc/pre_send(datum/component/ntnet_interface/interface)
|
||||
// Decrypt the passkey.
|
||||
if(encrypted_passkey && !passkey)
|
||||
passkey = json_decode(XorEncrypt(hextostr(encrypted_passkey, TRUE), SScircuit.cipherkey))
|
||||
|
||||
// Encrypt the passkey.
|
||||
if(!encrypted_passkey && passkey)
|
||||
encrypted_passkey = strtohex(XorEncrypt(json_encode(passkey), SScircuit.cipherkey))
|
||||
|
||||
// If there is no sender ID, set the default one.
|
||||
if(!sender_id && interface)
|
||||
sender_id = interface.hardware_id
|
||||
|
||||
|
||||
/datum/netdata/proc/json_list_generation_admin() //for admin logs and such.
|
||||
. = list()
|
||||
@@ -21,9 +38,9 @@
|
||||
. = list()
|
||||
.["recipient_ids"] = recipient_ids
|
||||
.["sender_id"] = sender_id
|
||||
.["plaintext_data"] = plaintext_data
|
||||
.["plaintext_data_secondary"] = plaintext_data_secondary
|
||||
.["plaintext_passkey"] = plaintext_passkey
|
||||
.["data"] = plaintext_data
|
||||
.["data_secondary"] = plaintext_data_secondary
|
||||
.["passkey"] = encrypted_passkey
|
||||
|
||||
/datum/netdata/proc/generate_netlog()
|
||||
return "[json_encode(json_list_generation_netlog())]"
|
||||
|
||||
37
code/modules/integrated_electronics/subtypes/access.dm
Normal file
37
code/modules/integrated_electronics/subtypes/access.dm
Normal file
@@ -0,0 +1,37 @@
|
||||
/obj/item/integrated_circuit/input/card_reader
|
||||
name = "card reader"
|
||||
desc = "A circuit that can read registred name, assignment and a PassKey string from an ID card."
|
||||
icon_state = "card_reader"
|
||||
|
||||
complexity = 4
|
||||
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
|
||||
outputs = list(
|
||||
"registered name" = IC_PINTYPE_STRING,
|
||||
"assignment" = IC_PINTYPE_STRING,
|
||||
"passkey" = IC_PINTYPE_STRING
|
||||
)
|
||||
activators = list(
|
||||
"on read" = IC_PINTYPE_PULSE_OUT
|
||||
)
|
||||
|
||||
/obj/item/integrated_circuit/input/card_reader/attackby_react(obj/item/I, mob/living/user, intent)
|
||||
var/obj/item/card/id/card = I.GetID()
|
||||
var/list/access = I.GetAccess()
|
||||
var/passkey = strtohex(XorEncrypt(json_encode(access), SScircuit.cipherkey))
|
||||
|
||||
if(card) // An ID card.
|
||||
set_pin_data(IC_OUTPUT, 1, card.registered_name)
|
||||
set_pin_data(IC_OUTPUT, 2, card.assignment)
|
||||
|
||||
else if(length(access)) // A non-card object that has access levels.
|
||||
set_pin_data(IC_OUTPUT, 1, null)
|
||||
set_pin_data(IC_OUTPUT, 2, null)
|
||||
|
||||
else
|
||||
return FALSE
|
||||
|
||||
set_pin_data(IC_OUTPUT, 3, passkey)
|
||||
|
||||
push_data()
|
||||
activate_pin(1)
|
||||
return TRUE
|
||||
@@ -629,17 +629,16 @@
|
||||
|
||||
var/datum/netdata/data = new
|
||||
data.recipient_ids = splittext(target_address, ";")
|
||||
data.sender_id = address
|
||||
data.plaintext_data = message
|
||||
data.plaintext_data_secondary = text
|
||||
data.plaintext_passkey = key
|
||||
data.encrypted_passkey = key
|
||||
ntnet_send(data)
|
||||
|
||||
/obj/item/integrated_circuit/input/ntnet_recieve(datum/netdata/data)
|
||||
set_pin_data(IC_OUTPUT, 1, data.sender_id)
|
||||
set_pin_data(IC_OUTPUT, 2, data.plaintext_data)
|
||||
set_pin_data(IC_OUTPUT, 3, data.plaintext_data_secondary)
|
||||
set_pin_data(IC_OUTPUT, 4, data.plaintext_passkey)
|
||||
set_pin_data(IC_OUTPUT, 4, data.encrypted_passkey)
|
||||
|
||||
push_data()
|
||||
activate_pin(2)
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
|
||||
/obj/var/list/req_access = null
|
||||
/obj/var/req_access_txt = "0" as text
|
||||
/obj/var/list/req_one_access = null
|
||||
/obj/var/req_one_access_txt = "0" as text
|
||||
|
||||
//returns TRUE if this mob has sufficient access to use this object
|
||||
/obj/proc/allowed(mob/M)
|
||||
//check if it doesn't require any access at all
|
||||
@@ -60,48 +55,36 @@
|
||||
for(var/b in text2access(req_one_access_txt))
|
||||
req_one_access += b
|
||||
|
||||
// Check if an item has access to this object
|
||||
/obj/proc/check_access(obj/item/I)
|
||||
return check_access_list(I ? I.GetAccess() : null)
|
||||
|
||||
|
||||
/obj/proc/check_access_list(list/access_list)
|
||||
gen_access()
|
||||
|
||||
if(!istype(src.req_access, /list)) //something's very wrong
|
||||
if(!islist(req_access)) //something's very wrong
|
||||
return TRUE
|
||||
|
||||
var/list/L = src.req_access
|
||||
if(!L.len && (!src.req_one_access || !src.req_one_access.len)) //no requirements
|
||||
if(!req_access.len && !length(req_one_access))
|
||||
return TRUE
|
||||
if(!I)
|
||||
|
||||
if(!length(access_list) || !islist(access_list))
|
||||
return FALSE
|
||||
for(var/req in src.req_access)
|
||||
if(!(req in I.GetAccess())) //doesn't have this access
|
||||
|
||||
for(var/req in req_access)
|
||||
if(!(req in access_list)) //doesn't have this access
|
||||
return FALSE
|
||||
if(src.req_one_access && src.req_one_access.len)
|
||||
for(var/req in src.req_one_access)
|
||||
if(req in I.GetAccess()) //has an access from the single access list
|
||||
|
||||
if(length(req_one_access))
|
||||
for(var/req in req_one_access)
|
||||
if(req in access_list) //has an access from the single access list
|
||||
return TRUE
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/proc/check_access_list(list/L)
|
||||
if(!src.req_access && !src.req_one_access)
|
||||
return TRUE
|
||||
if(!istype(src.req_access, /list))
|
||||
return TRUE
|
||||
if(!src.req_access.len && (!src.req_one_access || !src.req_one_access.len))
|
||||
return TRUE
|
||||
if(!L)
|
||||
return FALSE
|
||||
if(!istype(L, /list))
|
||||
return FALSE
|
||||
for(var/req in src.req_access)
|
||||
if(!(req in L)) //doesn't have this access
|
||||
return FALSE
|
||||
if(src.req_one_access && src.req_one_access.len)
|
||||
for(var/req in src.req_one_access)
|
||||
if(req in L) //has an access from the single access list
|
||||
return TRUE
|
||||
return FALSE
|
||||
return TRUE
|
||||
/obj/proc/check_access_ntnet(datum/netdata/data)
|
||||
return check_access_list(data.passkey)
|
||||
|
||||
/proc/get_centcom_access(job)
|
||||
switch(job)
|
||||
|
||||
@@ -182,6 +182,7 @@
|
||||
var/obj/item/card/id/C = H.wear_id
|
||||
if(istype(C))
|
||||
C.access = J.get_access()
|
||||
shuffle_inplace(C.access) // Shuffle access list to make NTNet passkeys less predictable
|
||||
C.registered_name = H.real_name
|
||||
C.assignment = J.title
|
||||
C.update_label()
|
||||
|
||||
@@ -1603,6 +1603,7 @@
|
||||
#include "code\modules\integrated_electronics\core\special_pins\string_pin.dm"
|
||||
#include "code\modules\integrated_electronics\passive\passive.dm"
|
||||
#include "code\modules\integrated_electronics\passive\power.dm"
|
||||
#include "code\modules\integrated_electronics\subtypes\access.dm"
|
||||
#include "code\modules\integrated_electronics\subtypes\arithmetic.dm"
|
||||
#include "code\modules\integrated_electronics\subtypes\converters.dm"
|
||||
#include "code\modules\integrated_electronics\subtypes\data_transfer.dm"
|
||||
|
||||
Reference in New Issue
Block a user