From f0cf4ad29dd9e9391d1996d714b86a37cb59843e Mon Sep 17 00:00:00 2001 From: ACCount Date: Fri, 9 Mar 2018 02:39:17 +0300 Subject: [PATCH] [READY] NTNet airlocks --- code/__DEFINES/subsystems.dm | 4 +- code/datums/components/ntnet_interface.dm | 65 +++++++++++++++++++ code/datums/wires/airlock.dm | 6 +- code/game/machinery/doors/airlock.dm | 54 ++++++++++++++- code/game/machinery/doors/door.dm | 5 -- code/game/mecha/mech_fabricator.dm | 11 ---- code/game/objects/items/control_wand.dm | 61 ++++++++--------- code/game/objects/objs.dm | 5 ++ code/modules/NTNet/netdata.dm | 25 +++++-- .../integrated_electronics/subtypes/access.dm | 37 +++++++++++ .../integrated_electronics/subtypes/input.dm | 5 +- code/modules/jobs/access.dm | 53 +++++---------- code/modules/jobs/job_types/job.dm | 1 + tgstation.dme | 1 + 14 files changed, 235 insertions(+), 98 deletions(-) create mode 100644 code/modules/integrated_electronics/subtypes/access.dm diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 770d3053c6..25e6a40934 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 3016e53c08..0f5d6d4d14 100644 --- a/code/datums/components/ntnet_interface.dm +++ b/code/datums/components/ntnet_interface.dm @@ -1,3 +1,4 @@ +<<<<<<< HEAD //Thing meant for allowing datums and objects to access a NTnet network datum. /datum/proc/ntnet_recieve(datum/netdata/data) return @@ -57,3 +58,67 @@ net.interface_disconnect(src) networks_connected_by_id -= net.network_id return TRUE +======= +//Thing meant for allowing datums and objects to access a NTnet network datum. +/datum/proc/ntnet_recieve(datum/netdata/data) + return + +/datum/proc/ntnet_send(datum/netdata/data, netid) + GET_COMPONENT(NIC, /datum/component/ntnet_interface) + if(!NIC) + return FALSE + return NIC.__network_send(data, netid) + +/datum/component/ntnet_interface + var/hardware_id //text + var/network_name = "" //text + var/list/networks_connected_by_id = list() //id = datum/ntnet + +/datum/component/ntnet_interface/Initialize(force_ID, force_name = "NTNet Device", autoconnect_station_network = TRUE) //Don't force ID unless you know what you're doing! + if(!force_ID) + hardware_id = "[SSnetworks.assignment_hardware_id++]" + else + hardware_id = force_ID + network_name = force_name + SSnetworks.register_interface(src) + if(autoconnect_station_network) + register_connection(SSnetworks.station_network) + +/datum/component/ntnet_interface/Destroy() + unregister_all_connections() + SSnetworks.unregister_interface(src) + return ..() + +/datum/component/ntnet_interface/proc/__network_recieve(datum/netdata/data) //Do not directly proccall! + parent.SendSignal(COMSIG_COMPONENT_NTNET_RECIEVE, data) + 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] + return net.process_data_transmit(src, data) + return FALSE + for(var/i in networks_connected_by_id) + var/datum/ntnet/net = networks_connected_by_id[i] + net.process_data_transmit(src, data) + return TRUE + +/datum/component/ntnet_interface/proc/register_connection(datum/ntnet/net) + if(net.interface_connect(src)) + networks_connected_by_id[net.network_id] = net + return TRUE + +/datum/component/ntnet_interface/proc/unregister_all_connections() + for(var/i in networks_connected_by_id) + unregister_connection(networks_connected_by_id[i]) + return TRUE + +/datum/component/ntnet_interface/proc/unregister_connection(datum/ntnet/net) + net.interface_disconnect(src) + networks_connected_by_id -= net.network_id + return TRUE +>>>>>>> 1f5b591... NTNet airlocks (#35947) diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index 5a01227b03..31156491f5 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 83b58fde65..438d754748 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() @@ -188,6 +189,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() @@ -197,6 +247,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() @@ -207,6 +258,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() @@ -320,7 +372,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 0a808ef3e6..e924472270 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 8a5044fdfa..c548e0b89d 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 ca51595124..ce674ceb94 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 fdd331a6f1..ae85b3b579 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 7d3d8f2b5d..d84ab43a6a 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 0000000000..0f0626057a --- /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 fa292c5644..c666f05d88 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 67bbd2fad9..f08bf5908e 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 077759e8b1..704722dc13 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 2129228566..fa6eea0c2a 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -1618,6 +1618,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"