diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 232da0d2cacd..7925edb76a18 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -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 diff --git a/code/datums/components/ntnet_interface.dm b/code/datums/components/ntnet_interface.dm index 14705691bbc9..c346279b3258 100644 --- a/code/datums/components/ntnet_interface.dm +++ b/code/datums/components/ntnet_interface.dm @@ -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] diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index 5a01227b0336..31156491f503 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -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("You hear a click from the bottom of the door.", null, 1) else if(A.hasPower()) A.unbolt() - A.audible_message("You hear a click from the bottom of the door.", null, 1) A.update_icon() if(WIRE_IDSCAN) // Pulse to disable emergency access and flash red lights. if(A.hasPower() && A.density) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 609da46c52ae..b1075a41a056 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -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("You hear a click from the bottom of the door.", null, 1) update_icon() /obj/machinery/door/airlock/unlock() @@ -208,6 +259,7 @@ return locked = FALSE playsound(src,boltUp,30,0,3) + audible_message("You hear a click from the bottom of the door.", 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())); diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 0a808ef3e61e..e9244722705d 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -45,11 +45,6 @@ if(!poddoor) to_chat(user, "Its maintenance panel is screwed in place.") -/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 diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 8a5044fdfa21..c548e0b89da6 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -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 diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm index ca51595124c9..ce674ceb9464 100644 --- a/code/game/objects/items/control_wand.dm +++ b/code/game/objects/items/control_wand.dm @@ -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, "[D] has no power!") - return - if(!D.requiresID()) - to_chat(user, "[D]'s ID scan is disabled!") - 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, "[src] does not have access to this door.") + + // 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" diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index fdd331a6f12d..ae85b3b57949 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -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) diff --git a/code/modules/NTNet/netdata.dm b/code/modules/NTNet/netdata.dm index 7d3d8f2b5df0..d84ab43a6ab4 100644 --- a/code/modules/NTNet/netdata.dm +++ b/code/modules/NTNet/netdata.dm @@ -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())]" diff --git a/code/modules/integrated_electronics/subtypes/access.dm b/code/modules/integrated_electronics/subtypes/access.dm new file mode 100644 index 000000000000..0f0626057a45 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/access.dm @@ -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 diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index fa292c56449b..c666f05d88d4 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -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) diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm index 67bbd2fad97f..f08bf5908eca 100644 --- a/code/modules/jobs/access.dm +++ b/code/modules/jobs/access.dm @@ -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) diff --git a/code/modules/jobs/job_types/job.dm b/code/modules/jobs/job_types/job.dm index 077759e8b10d..704722dc13b7 100644 --- a/code/modules/jobs/job_types/job.dm +++ b/code/modules/jobs/job_types/job.dm @@ -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() diff --git a/tgstation.dme b/tgstation.dme index 651cffcd5729..421f49f360a9 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -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"