diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 909cba565c5..aa156cf17a1 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -806,8 +806,21 @@ //NTnet -///called on an object by its NTNET connection component on receive. (sending_id(number), sending_netname(text), data(datum/netdata)) +///called on an object by its NTNET connection component on receive. (data(datum/netdata)) #define COMSIG_COMPONENT_NTNET_RECEIVE "ntnet_receive" +///called on an object by its NTNET connection component on a port update (hardware_id, port)) +#define COMSIG_COMPONENT_NTNET_PORT_UPDATE "ntnet_port_update" +/// called when packet was accepted by the target (datum/netdata, error_code) +#define COMSIG_COMPONENT_NTNET_ACK "ntnet_ack" +/// called when packet was not acknoledged by the target (datum/netdata, error_code) +#define COMSIG_COMPONENT_NTNET_NAK "ntnet_nack" + +// Some internal NTnet signals used on ports +///called on an object by its NTNET connection component on a port distruction (port, list/data)) +#define COMSIG_COMPONENT_NTNET_PORT_DESTROYED "ntnet_port_destroyed" +///called on an object by its NTNET connection component on a port distruction (port, list/data)) +#define COMSIG_COMPONENT_NTNET_PORT_UPDATED "ntnet_port_updated" + //Nanites diff --git a/code/__DEFINES/networks.dm b/code/__DEFINES/networks.dm index 115c1653493..6f1d9ae0e6e 100644 --- a/code/__DEFINES/networks.dm +++ b/code/__DEFINES/networks.dm @@ -1,3 +1,94 @@ #define HID_RESTRICTED_END 101 //the first nonrestricted ID, automatically assigned on connection creation. #define NETWORK_BROADCAST_ID "ALL" + +/// To debug networks use this to check +//#define DEBUG_NETWORKS 1 + + +/// We do some macro magic to make sure the strings are created at compile time rather than runtime +/// We do it this way so that if someone changes any of the names of networks we don't have to hunt down +/// all the constants though all the files for them. hurrah! + +/// Ugh, couldn't get recursive stringafy to work in byond for some reason +#define NETWORK_NAME_COMBINE(L,R) ((L) + "." + (R)) + +/// Station network names. Used as the root networks for main parts of the station +#define __STATION_NETWORK_ROOT "SS13" +#define __CENTCOM_NETWORK_ROOT "CENTCOM" +#define __SYNDICATE_NETWORK_ROOT "SYNDI" +#define __LIMBO_NETWORK_ROOT "LIMBO" // Limbo is a dead network + +/// various sub networks pieces +#define __NETWORK_LIMBO "LIMBO" +#define __NETWORK_TOOLS "TOOLS" +#define __NETWORK_REMOTES "REMOTES" +#define __NETWORK_AIRLOCKS "AIRLOCKS" +#define __NETWORK_DOORS "DOORS" +#define __NETWORK_ATMOS "ATMOS" +#define __NETWORK_SCUBBERS "AIRLOCKS" +#define __NETWORK_AIRALARMS "AIRALARMS" +#define __NETWORK_CONTROL "CONTROL" +#define __NETWORK_STORAGE "STORAGE" +#define __NETWORK_CARGO "CARGO" +#define __NETWORK_BOTS "BOTS" +#define __NETWORK_COMPUTER "COMPUTER" +#define __NETWORK_CARDS "CARDS" + +/// Various combined subnetworks +#define NETWORK_DOOR_REMOTES NETWORK_NAME_COMBINE(__NETWORK_DOORS, __NETWORK_REMOTES) +#define NETWORK_DOOR_AIRLOCKS NETWORK_NAME_COMBINE(__NETWORK_DOORS, __NETWORK_AIRLOCKS) +#define NETWORK_ATMOS_AIRALARMS NETWORK_NAME_COMBINE(__NETWORK_ATMOS, __NETWORK_AIRALARMS) +#define NETWORK_ATMOS_SCUBBERS NETWORK_NAME_COMBINE(__NETWORK_ATMOS, __NETWORK_SCUBBERS) +#define NETWORK_CARDS NETWORK_NAME_COMBINE(__NETWORK_COMPUTER, __NETWORK_CARDS) +#define NETWORK_BOTS_CARGO NETWORK_NAME_COMBINE(__NETWORK_CARGO, __NETWORK_BOTS) + + +// Finally turn eveything into strings +#define STATION_NETWORK_ROOT __STATION_NETWORK_ROOT +#define CENTCOM_NETWORK_ROOT __CENTCOM_NETWORK_ROOT +#define SYNDICATE_NETWORK_ROOT __SYNDICATE_NETWORK_ROOT +#define LIMBO_NETWORK_ROOT __LIMBO_NETWORK_ROOT + + + +/// Network name should be all caps and no punctuation except for _ and . between domains +/// This does a quick an dirty fix to a network name to make sure it works +/proc/simple_network_name_fix(name) + // can't make this as a define, some reason findtext(name,@"^[^\. ]+[A-Z0-9_\.]+[^\. ]+$") dosn't work + var/static/regex/check_regex = new(@"[ \-]{1}","g") + return check_regex.Replace(uppertext(name),"_") +/* + * Helper that verifies a network name is valid. + * + * A valid network name (ie, SS13.ATMOS.SCRUBBERS) is all caps, no spaces with periods between + * branches. Returns false if it doesn't meat this requirement + * + * Arguments: + * * name - network text name to check +*/ +/proc/verify_network_name(name) + // can't make this as a define, some reason findtext(name,@"^[^\. ]+[A-Z0-9_\.]+[^\. ]+$") dosn't work + var/static/regex/check_regex = new(@"^(?=[^\. ]+)[A-Z0-9_\.]+[^\. ]+$") + return istext(name) && check_regex.Find(name) + + + +/// Port protocol. A port is just a list with a few vars that are used to send signals +/// that something is refreshed or updated. These macros make it faster rather than +/// calling procs +#define NETWORK_PORT_DISCONNECTED(LIST) (!LIST || LIST["_disconnected"]) +#define NETWORK_PORT_UPDATED(LIST) (LIST && !LIST["_disconnected"] && LIST["_updated"]) +#define NETWORK_PORT_UPDATE(LIST) if(LIST) { LIST["_updated"] = TRUE } +#define NETWORK_PORT_CLEAR_UPDATE(LIST) if(LIST) { LIST["_updated"] = FALSE } +#define NETWORK_PORT_SET_UPDATE(LIST) if(LIST) { LIST["_updated"] = TRUE } +#define NETWORK_PORT_DISCONNECT(LIST) if(LIST) { LIST["_disconnected"] = TRUE } + +/// Error codes +#define NETWORK_ERROR_OK null +#define NETWORK_ERROR_NOT_ON_NETWORK "network_error_not_on_network" +#define NETWORK_ERROR_BAD_NETWORK "network_error_bad_network" +#define NETWORK_ERROR_BAD_RECEIVER_ID "network_error_bad_receiver_id" +#define NETWORK_ERROR_UNAUTHORIZED "network_error_bad_unauthorized" +#define NETWORK_ERROR_BAD_TARGET_ID "network_error_bad_target_id" + diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index a688ea08c8d..8d37b37d83c 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -430,13 +430,14 @@ GLOBAL_LIST_EMPTY(colored_images) GLOB.colored_images += shiny /datum/controller/subsystem/air/proc/setup_template_machinery(list/atmos_machines) - for(var/A in atmos_machines) - var/obj/machinery/atmospherics/AM = A + var/obj/machinery/atmospherics/AM + for(var/A in 1 to atmos_machines.len) + AM = atmos_machines[A] AM.atmosinit() CHECK_TICK - for(var/A in atmos_machines) - var/obj/machinery/atmospherics/AM = A + for(var/A in 1 to atmos_machines.len) + AM = atmos_machines[A] AM.build_network() CHECK_TICK diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index d0f485849d3..02aad6dec36 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -34,12 +34,13 @@ SUBSYSTEM_DEF(atoms) var/count var/list/mapload_arg = list(TRUE) + if(atoms) count = atoms.len - for(var/I in atoms) - var/atom/A = I + for(var/I in 1 to count) + var/atom/A = atoms[I] if(!(A.flags_1 & INITIALIZED_1)) - InitAtom(I, mapload_arg) + InitAtom(A, mapload_arg) CHECK_TICK else count = 0 @@ -55,8 +56,8 @@ SUBSYSTEM_DEF(atoms) initialized = old_initialized if(late_loaders.len) - for(var/I in late_loaders) - var/atom/A = I + for(var/I in 1 to late_loaders.len) + var/atom/A = late_loaders[I] A.LateInitialize() testing("Late initialized [late_loaders.len] atoms") late_loaders.Cut() diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm index 23190574d8e..dd17b86f633 100644 --- a/code/controllers/subsystem/machines.dm +++ b/code/controllers/subsystem/machines.dm @@ -51,8 +51,9 @@ SUBSYSTEM_DEF(machines) return /datum/controller/subsystem/machines/proc/setup_template_powernets(list/cables) - for(var/A in cables) - var/obj/structure/cable/PC = A + var/obj/structure/cable/PC + for(var/A in 1 to cables.len) + PC = cables[A] if(!PC.powernet) var/datum/powernet/NewPN = new() NewPN.add_cable(PC) diff --git a/code/controllers/subsystem/processing/networks.dm b/code/controllers/subsystem/processing/networks.dm index f7f16538a62..ec8208998ea 100644 --- a/code/controllers/subsystem/processing/networks.dm +++ b/code/controllers/subsystem/processing/networks.dm @@ -1,51 +1,523 @@ -PROCESSING_SUBSYSTEM_DEF(networks) +SUBSYSTEM_DEF(networks) name = "Networks" priority = FIRE_PRIORITY_NETWORKS - wait = 1 - stat_tag = "NET" + wait = 5 flags = SS_KEEP_TIMING init_order = INIT_ORDER_NETWORKS - var/datum/ntnet/station/station_network - var/assignment_hardware_id = HID_RESTRICTED_END - var/list/networks_by_id = list() //id = network - var/list/interfaces_by_id = list() //hardware id = component interface - var/resolve_collisions = TRUE -/datum/controller/subsystem/processing/networks/Initialize() - station_network = new - station_network.register_map_supremecy() - . = ..() + var/list/relays = list() + /// Legacy ntnet lookup for software. Should be changed latter so don't rely on this + /// being here. + var/datum/ntnet/station_root/station_network + var/datum/ntnet/station_root/syndie_network + var/list/network_initialize_queue = list() + /// all interfaces by their hardware address. + /// Do NOT use to verify a reciver_id is valid, use the network.root_devices for that + var/list/interfaces_by_hardware_id = list() + /// Area to network, network to area list + /// This is an associated list to quickly find an area using either its id or network + /// Used mainly to make sure all area id's are unique even if a mapper uses the same + /// area many times + var/list/area_network_lookup = list() -/datum/controller/subsystem/processing/networks/proc/register_network(datum/ntnet/network) - if(!networks_by_id[network.network_id]) - networks_by_id[network.network_id] = network - return TRUE + /// List of networks using their fully qualified network name. Used for quick lookups + /// of networks for sending packets + var/list/networks = list() + /// List of the root networks starting at their root names. Used to find and/or build + /// network tress + var/list/root_networks = list() + + + + // Why not list? Because its a Copy() every time we add a packet, and thats stupid. + var/datum/netdata/first = null // start of the queue. Pulled off in fire. + var/datum/netdata/last = null // end of the queue. pushed on by transmit + var/packet_count = 0 + // packet stats + var/count_broadcasts_packets = 0 // count of broadcast packets sent + var/count_failed_packets = 0 // count of message fails + var/count_good_packets = 0 + // Logs moved here + // Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly. + // High values make displaying logs much laggier. + var/setting_maxlogcount = 100 + var/list/logs = list() + + /// Random name search to make sure we have unique names. + /// DO NOT REMOVE NAMES HERE UNLESS YOU KNOW WHAT YOUR DOING + var/list/used_names = list() + + +/// You shouldn't need to do this. But mapping is async and there is no guarantee that Initialize +/// will run before these networks are dynamically created. So its here. +/datum/controller/subsystem/networks/PreInit() + /// Limbo network needs to be made at boot up for all error devices + new/datum/ntnet(LIMBO_NETWORK_ROOT) + station_network = new(STATION_NETWORK_ROOT) + syndie_network = new(SYNDICATE_NETWORK_ROOT) + /// As well as the station network incase something funny goes during startup + new/datum/ntnet(CENTCOM_NETWORK_ROOT) + + +/datum/controller/subsystem/networks/stat_entry(msg) + msg = "NET: QUEUE([packet_count]) FAILS([count_failed_packets]) BROADCAST([count_broadcasts_packets])" + return ..() + +/datum/controller/subsystem/networks/Initialize() + station_network.register_map_supremecy() // sigh + assign_areas_root_ids(GLOB.sortedAreas) // setup area names before Initialize + station_network.build_software_lists() + syndie_network.build_software_lists() + + // At round start, fix the network_id's so the station root is on them + initialized = TRUE + // Now when the objects Initialize they will join the right network + return ..() + +/* + * Process incoming queued packet and return NAK/ACK signals + * + * This should only be called when you want the target object to process the NAK/ACK signal, usually + * during fire. At this point data.receiver_id has already been converted if it was a broadcast but + * is undefined in this function. + * Arguments: + * * receiver_id - text hardware id for the target device + * * data - packet to be sent + */ + +/datum/controller/subsystem/networks/proc/_process_packet(receiver_id, datum/netdata/data) + /// Used only for sending NAK/ACK and error reply's + var/datum/component/ntnet_interface/sending_interface = interfaces_by_hardware_id[data.sender_id] + + /// Check if the network_id is valid and if not send an error and return + var/datum/ntnet/target_network = networks[data.network_id] + if(!target_network) + count_failed_packets++ + add_log("Bad target network '[data.network_id]'", null, data.sender_id) + if(!QDELETED(sending_interface)) + SEND_SIGNAL(sending_interface.parent, COMSIG_COMPONENT_NTNET_NAK, data , NETWORK_ERROR_BAD_NETWORK) + return + + /// Check if the receiver_id is in the network. If not send an error and return + var/datum/component/ntnet_interface/target_interface = target_network.root_devices[receiver_id] + if(QDELETED(target_interface)) + count_failed_packets++ + add_log("Bad target device '[receiver_id]'", target_network, data.sender_id) + if(!QDELETED(sending_interface)) + SEND_SIGNAL(sending_interface.parent, COMSIG_COMPONENT_NTNET_NAK, data, NETWORK_ERROR_BAD_RECEIVER_ID) + return + + // Check if we care about permissions. If we do check if we are allowed the message to be processed + if(data.passkey) // got to check permissions + var/obj/O = target_interface.parent + if(O) + if(!O.check_access_ntnet(data.passkey)) + count_failed_packets++ + add_log("Access denied to ([receiver_id]) from ([data.network_id])", target_network, data.sender_id) + if(!QDELETED(sending_interface)) + SEND_SIGNAL(sending_interface.parent, COMSIG_COMPONENT_NTNET_NAK, data, NETWORK_ERROR_UNAUTHORIZED) + return + else + add_log("A access key message was sent to a non-device", target_network, data.sender_id) + if(!QDELETED(sending_interface)) + SEND_SIGNAL(sending_interface.parent, COMSIG_COMPONENT_NTNET_NAK, data, NETWORK_ERROR_UNAUTHORIZED) + + + SEND_SIGNAL(target_interface.parent, COMSIG_COMPONENT_NTNET_RECEIVE, data) + // All is good, send the packet then send an ACK to the sender + if(!QDELETED(sending_interface)) + SEND_SIGNAL(sending_interface.parent, COMSIG_COMPONENT_NTNET_ACK, data) + count_good_packets++ + +/// Helper define to make sure we pop the packet and qdel it +#define POP_PACKET(CURRENT) first = CURRENT.next; packet_count--; if(!first) { last = null; packet_count = 0; }; qdel(CURRENT); + +/datum/controller/subsystem/networks/fire(resumed = 0) + var/datum/netdata/current + var/datum/component/ntnet_interface/target_interface + while(first) + current = first + /// Check if we are a list. If so process the list + if(islist(current.receiver_id)) // are we a broadcast list + var/list/receivers = current.receiver_id + var/receiver_id = receivers[receivers.len--] // pop it + _process_packet(receiver_id, current) + if(receivers.len == 0) // pop it if done + count_broadcasts_packets++ + POP_PACKET(current) + else // else set up a broadcast or send a single targete + // check if we are sending to a network or to a single target + target_interface = interfaces_by_hardware_id[current.receiver_id] + if(target_interface) // a single sender id + _process_packet(current.receiver_id, current) // single target + POP_PACKET(current) + else // ok so lets find the network to send it too + var/datum/ntnet/net = networks[current.network_id] // get the sending network + net = net?.networks[current.receiver_id] // find the target network to broadcast + if(net) // we found it + current.receiver_id = net.collect_interfaces() // make a list of all the sending targets + else + // We got an error, the network is bad so send a NAK + target_interface = interfaces_by_hardware_id[current.sender_id] + if(!QDELETED(target_interface)) + SEND_SIGNAL(target_interface.parent, COMSIG_COMPONENT_NTNET_NAK, current , NETWORK_ERROR_BAD_NETWORK) + POP_PACKET(current) // and get rid of it + if (MC_TICK_CHECK) + return + +#undef POP_PACKET + +/* + * Main function to queue a packet. As long as we have valid receiver_id and network_id we will take it + * + * Main queuing function for any message sent. if the data.receiver_id is null, then it will be broadcasted + * error checking is only done during the process this just throws it on the queue. + * Arguments: + * * data - packet to be sent + */ +/datum/controller/subsystem/networks/proc/transmit(datum/netdata/data) + data.next = null // sanity check + + if(!last) + first = last = data + else + last.next = data + last = data + packet_count++ + // We do error checking when the packet is sent + return NETWORK_ERROR_OK + + +/datum/controller/subsystem/networks/proc/check_relay_operation(zlevel=0) //can be expanded later but right now it's true/false. + for(var/i in relays) + var/obj/machinery/ntnet_relay/n = i + if(zlevel && n.z != zlevel) + continue + if(n.is_operational) + return TRUE return FALSE -/datum/controller/subsystem/processing/networks/proc/unregister_network(datum/ntnet/network) - networks_by_id -= network.network_id - return TRUE +/datum/controller/subsystem/networks/proc/log_data_transfer( datum/netdata/data) + logs += "[station_time_timestamp()] - [data.generate_netlog()]" + if(logs.len > setting_maxlogcount) + logs = logs.Copy(logs.len - setting_maxlogcount, 0) -/datum/controller/subsystem/processing/networks/proc/register_interface(datum/component/ntnet_interface/D) - if(!interfaces_by_id[D.hardware_id]) - interfaces_by_id[D.hardware_id] = D - return TRUE - return FALSE +/** + * Records a message into the station logging system for the network + * + * This CAN be read in station by personal so do not use it for game debugging + * during fire. At this point data.receiver_id has already been converted if it was a broadcast but + * is undefined in this function. It is also dumped to normal logs but remember players can read/intercept + * these messages + * Arguments: + * * log_string - message to log + * * network - optional, It can be a ntnet or just the text equivalent + * * hardware_id = optional, text, will look it up and return with the parent.name as well + */ +/datum/controller/subsystem/networks/proc/add_log(log_string, network = null , hardware_id = null) + set waitfor = FALSE // so process keeps running + var/list/log_text = list() + log_text += "\[[station_time_timestamp()]\]" + if(network) + var/datum/ntnet/net = network + if(!net) + net = networks[network] + if(net) // bad network? + log_text += "{[net.network_id]}" + else // bad network? + log_text += "{[network] *BAD*}" -/datum/controller/subsystem/processing/networks/proc/unregister_interface(datum/component/ntnet_interface/D) - interfaces_by_id -= D.hardware_id - return TRUE + if(hardware_id) + var/datum/component/ntnet_interface/conn = interfaces_by_hardware_id[hardware_id] + if(conn) + log_text += " ([hardware_id])[conn.parent]" + else + log_text += " ([hardware_id])*BAD ID*" + else + log_text += "*SYSTEM*" + log_text += " - " + log_text += log_string + log_string = log_text.Join() -/datum/controller/subsystem/processing/networks/proc/get_next_HID() - var/string = "[num2text(assignment_hardware_id++, 12)]" - return make_address(string) + logs.Add(log_string) + //log_telecomms("NetLog: [log_string]") // causes runtime on startup humm -/datum/controller/subsystem/processing/networks/proc/make_address(string) - if(!string) - return resolve_collisions? make_address("[num2text(rand(HID_RESTRICTED_END, 999999999), 12)]"):null - var/hex = md5(string) - if(!hex) - return //errored - . = "[copytext_char(hex, 1, 9)]" //16 ^ 8 possibilities I think. - if(interfaces_by_id[.]) - return resolve_collisions? make_address("[num2text(rand(HID_RESTRICTED_END, 999999999), 12)]"):null + // We have too many logs, remove the oldest entries until we get into the limit + if(logs.len > setting_maxlogcount) + logs = logs.Copy(logs.len-setting_maxlogcount,0) + + +/** + * Removes all station logs for the current game + */ +/datum/controller/subsystem/networks/proc/purge_logs() + logs = list() + add_log("-!- LOGS DELETED BY SYSTEM OPERATOR -!-") + + + +/** + * Updates the maximum amount of logs and purges those that go beyond that number + * + * Shouldn't been needed to be run by players but maybe admins need it? + * Arguments: + * * lognumber - new setting_maxlogcount count + */ +/datum/controller/subsystem/networks/proc/update_max_log_count(lognumber) + if(!lognumber) + return FALSE + // Trim the value if necessary + lognumber = max(MIN_NTNET_LOGS, min(lognumber, MAX_NTNET_LOGS)) + setting_maxlogcount = lognumber + add_log("Configuration Updated. Now keeping [setting_maxlogcount] logs in system memory.") + + + +/** + * Gives an area a root and a network_area_id + * + * When a device is added to the network on map load, it needs to know where it is. + * So that it is added to that ruins/base's network instead of the general station network + * This way people on the station cannot just hack Charlie's doors and visa versa. All area's + * "should" have this information and if not one is created from existing map tags or + * ruin template id's. This SHOULD run before the Initialize of a atom, or the root will not + * be put in the object.area + * + * An example on what the area.network_root_id does/ + * Before Init: obj.network_id = "ATMOS.SCRUBBER" area.network_root_id="SS13_STATION" area.network_area_id = "BRIDGE" + * After Init: obj.network_id = "SS13_STATION.ATMOS.SCRUBBER" also obj.network_id = "SS13_STATION.AREA.BRIDGE" + * + * Arguments: + * * area - Area to modify the root id. + * * template - optional, map_template of that area + */ +/datum/controller/subsystem/networks/proc/lookup_area_root_id(area/A, datum/map_template/M=null) + /// Check if the area is valid and if it doesn't have a network root id. + if(!istype(A) || A.network_root_id != null) + return + + /// If we are a ruin or a shuttle, we get our own network + if(M) + /// if we have a template, try to get the network id from the template + if(M.station_id && M.station_id != LIMBO_NETWORK_ROOT) // check if the template specifies it + A.network_root_id = simple_network_name_fix(M.station_id) + else if(istype(M, /datum/map_template/shuttle)) // if not, then check if its a shuttle type + var/datum/map_template/shuttle/T = M // we are a shuttle so use shuttle id + A.network_root_id = simple_network_name_fix(T.shuttle_id) + else if(istype(M,/datum/map_template/ruin)) // if not again, check if its a ruin type + var/datum/map_template/ruin/R = M + A.network_root_id = simple_network_name_fix(R.id) + + if(!A.network_root_id) // not assigned? Then lets use some defaults + // Anything in Centcom is completely isolated + // Special case for holodecks. + if(istype(A,/area/holodeck)) + A.network_root_id = "HOLODECK" // isolated from the station network + else if(SSmapping.level_trait(A.z, ZTRAIT_CENTCOM)) + A.network_root_id = CENTCOM_NETWORK_ROOT + // Otherwise the default is the station + else + A.network_root_id = STATION_NETWORK_ROOT + +/datum/controller/subsystem/networks/proc/assign_area_network_id(area/A, datum/map_template/M=null) + if(!istype(A)) + return + if(!A.network_root_id) + lookup_area_root_id(A, M) + // finally set the network area id, bit copy paste from area Initialize + // This is done in case we have more than one area type, each area instance has its own network name + if(!A.network_area_id) + A.network_area_id = A.network_root_id + ".AREA." + simple_network_name_fix(A.name) // Make the string + if(!(A.area_flags & UNIQUE_AREA)) // if we aren't a unique area, make sure our name is different + A.network_area_id = SSnetworks.assign_random_name(5, A.network_area_id + "_") // tack on some garbage incase there are two area types + +/datum/controller/subsystem/networks/proc/assign_areas_root_ids(list/areas, datum/map_template/M=null) + for(var/area/A in areas) + assign_area_network_id(A, M) + +/** + * Converts a list of string's into a full network_id + * + * Converts a list of individual branches into a proper network id. Validates + * individual parts to make sure they are clean. + * + * ex. list("A","B","C") -> A.B.C + * + * Arguments: + * * tree - List of strings + */ +/datum/controller/subsystem/networks/proc/network_list_to_string(list/tree) +#ifdef DEBUG_NETWORKS + ASSERT(tree && tree.len > 0) // this should be obvious but JUST in case. + for(var/part in tree) + if(!verify_network_name(part) || findtext(name,".")!=0) // and no stray dots + stack_trace("network_list_to_string: Cannot create network with ([part]) of ([tree.Join(".")])") + break +#endif + return tree.Join(".") + +/** + * Converts string into a list of network branches + * + * Converts a a proper network id into a list of the individual branches + * + * ex. A.B.C -> list("A","B","C") + * + * Arguments: + * * tree - List of strings + */ +/datum/controller/subsystem/networks/proc/network_string_to_list(name) +#ifdef DEBUG_NETWORKS + if(!verify_network_name(name)) + stack_trace("network_string_to_list: [name] IS INVALID") +#endif + return splittext(name,".") // should we do a splittext_char? I doubt we really need unicode in network names + + +/** + * Hard creates a network. Helper function for create_network_simple and create_network + * + * Hard creates a using a list of branches and returns. No error checking as it should + * of been done before this call + * + * Arguments: + * * network_tree - list,text List of branches of network + */ +/datum/controller/subsystem/networks/proc/_hard_create_network(list/network_tree) + var/network_name_part = network_tree[1] + var/network_id = network_name_part + var/datum/ntnet/parent = root_networks[network_name_part] + var/datum/ntnet/network + if(!parent) // we have no network root? Must be mapload of a ruin or such + parent = new(network_name_part) + + // go up the branches, creating nodes + for(var/i in 2 to network_tree.len) + network_name_part = network_tree[i] + network = parent.children[network_name_part] + network_id += "." + network_name_part + if(!network) + network = new(network_id, network_name_part, parent) + + parent = network + return network + + +/** + * Creates or finds a network anywhere in the world using a fully qualified name + * + * This is the simple case finding of a network in the world. It must take a full + * qualified network name and it will either return an existing network or build + * a new one from scratch. We must be able to create names on the fly as there is + * no way for the map loader to tell us ahead of time what networks to create or + * use for any maps or templates. So this thing will throw silent mapping errors + * and log them, but will always return a network for something. + * + * Arguments: + * * network_id - text, Fully qualified network name + */ +/datum/controller/subsystem/networks/proc/create_network_simple(network_id) + + var/datum/ntnet/network = networks[network_id] + if(network!=null) + return network // don't worry about it + + /// Checks to make sure the network is valid. We log BOTH to mapping and telecoms + /// so if your checking for network errors you can find it in mapping to (because its their fault!) + if(!verify_network_name(network_id)) + log_mapping("create_network_simple: [network_id] IS INVALID, replacing with LIMBO") + log_telecomms("create_network_simple: [network_id] IS INVALID, replacing with LIMBO") + return networks[LIMBO_NETWORK_ROOT] + + var/list/network_tree = network_string_to_list(network_id) + if(!network_tree || network_tree.len == 0) + log_mapping("create_network_simple: [network_id] IS INVALID, replacing with LIMBO") + log_telecomms("create_network_simple: [network_id] IS INVALID, replacing with LIMBO") + return networks[LIMBO_NETWORK_ROOT] + + network = _hard_create_network(network_tree) +#ifdef DEBUG_NETWORKS + if(!network) + CRASH("NETWORK CANNOT BE NULL") +#endif + log_telecomms("create_network_simple: created final [network.network_id]") + return network // and we are done! + + +/** + * Creates or finds a network anywhere in the world using bits of text + * + * This works the same as create_network_simple however it allows the addition + * of qualified network names. So you can call it with a root_id and a sub + * network. However this function WILL return null if it cannot be created + * so it should be used with error checking is involved. + * + * ex. create_network("ROOT_NETWORK", "ATMOS.SCRUBBERS") -> ROOT_NETWORK.ATMOS.SCRUBBERS + * + * Arguments: + * * tree - List of string + */ +/datum/controller/subsystem/networks/proc/create_network(...) + var/list/network_tree = list() + + for(var/i in 1 to args.len) + var/part = args[i] +#ifdef DEBUG_NETWORKS + if(!part || !istext(part)) + /// stack trace here because this is a bad error + stack_trace("create_network: We only take text on [part] index [i]") + return null +#endif + network_tree += network_string_to_list(part) + + var/datum/ntnet/network = _hard_create_network(network_tree) + log_telecomms("create_network: created final [network.network_id]") + return network + +/** + * Generate a hardware id for devices. + * + * Creates a 32 bit hardware id for network devices. This is random so masking + * the number won't make routing "easier" (Think Ethernet) It does check if + * an existing device has the number but will NOT assign it as thats + * up to the collar + * + * Returns (string) The generated name + */ +/datum/controller/subsystem/networks/proc/get_next_HID() + do + var/string = md5("[num2text(rand(HID_RESTRICTED_END, 999999999), 12)]") + if(!string) + log_runtime("Could not generagea m5 hash from address, problem with md5?") + return //errored + . = "[copytext_char(string, 1, 9)]" //16 ^ 8 possibilities I think. + while(interfaces_by_hardware_id[.]) + +/** + * Generate a name devices + * + * Creates a randomly generated tag or name for devices or anything really + * it keeps track of a special list that makes sure no name is used more than + * once + * + * args: + * * len (int)(Optional) Default=5 The length of the name + * * prefix (string)(Optional) static text in front of the random name + * * postfix (string)(Optional) static text in back of the random name + * Returns (string) The generated name + */ +/datum/controller/subsystem/networks/proc/assign_random_name(len=5, prefix="", postfix="") + var/static/valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + var/list/new_name = list() + var/text + // machine id's should be fun random chars hinting at a larger world + do + new_name.Cut() + new_name += prefix + for(var/i = 1 to len) + new_name += valid_chars[rand(1,length(valid_chars))] + new_name += postfix + text = new_name.Join() + while(used_names[text]) + used_names[text] = TRUE + return text diff --git a/code/datums/components/ntnet_interface.dm b/code/datums/components/ntnet_interface.dm index 19fae545eb9..8585a39b921 100644 --- a/code/datums/components/ntnet_interface.dm +++ b/code/datums/components/ntnet_interface.dm @@ -1,66 +1,143 @@ -//Thing meant for allowing datums and objects to access an NTnet network datum. -/datum/proc/ntnet_receive(datum/netdata/data) - return -/datum/proc/ntnet_receive_broadcast(datum/netdata/data) - return - -/datum/proc/ntnet_send(datum/netdata/data, netid) +/* + * Helper function that does 90% of the work in sending a packet + * + * This function gets the component and builds a packet so the sending + * person doesn't have to lift a finger. Just create a netdata datum or even + * just a list and it will send it on its merry way. + * + * Arguments: + * * packet_data - Either a list() or a /datum/netdata. If its netdata, the other args are ignored + * * target_id - Target hardware id or network_id for this packet. If we are a network id, then its + broadcasted to that network. + * * passkey - Authentication for the packet. If the target doesn't authenticate the packet is dropped + */ +/datum/proc/ntnet_send(packet_data, target_id = null, passkey = null) + var/datum/netdata/data = packet_data + if(!data) // check for easy case + if(!islist(packet_data) || target_id == null) + stack_trace("ntnet_send: Bad packet creation") // hard fail as its runtime fault + return + data = new(packet_data) + data.receiver_id = target_id + data.passkey = passkey + if(data.receiver_id == null) + return NETWORK_ERROR_BAD_TARGET_ID var/datum/component/ntnet_interface/NIC = GetComponent(/datum/component/ntnet_interface) if(!NIC) - return FALSE - return NIC.__network_send(data, netid) + return NETWORK_ERROR_NOT_ON_NETWORK + data.sender_id = NIC.hardware_id + data.network_id = NIC.network.network_id + return SSnetworks.transmit(data) +/* + * # /datum/component/ntnet_interface + * + * This component connects a obj/datum to the station network. + * + * Anything can be connected to the station network. Any obj can auto connect as long as its network_id + * var is set before the parent new is called. This allows map objects to be connected. Technically the + * component only handles getting you on the network while SSnetwork and datum/ntnet does all the real work. + * There are quite a few stack_traces in here. This is because error checking should be done before this component is + * added. Also, there never should be a component that has no network. If it needs a network assign it to LIMBO + * + */ /datum/component/ntnet_interface - var/hardware_id //text. this is the true ID. do not change this. stuff like ID forgery can be done manually. - var/network_name = "" //text - var/list/networks_connected_by_id = list() //id = datum/ntnet - var/differentiate_broadcast = TRUE //If false, broadcasts go to ntnet_receive. NOT RECOMMENDED. + var/hardware_id = null // text. this is the true ID. do not change this. stuff like ID forgery can be done manually. + var/id_tag = null // named tag, mainly used to look up mapping objects + var/datum/ntnet/network = null // network we are on, we MUST be on a network or there is no point in this component + var/list/registered_sockets = list()// list of ports opened up on devices + var/list/alias = list() // if we live in more than one network branch + +/** + * Initialize for the interface + * + * Assigns a hardware id and gets your object onto the network + * + * Arguments: + * * network_name - Fully qualified network id of the network we are joining + * * network_tag - The objects id_tag. Used for finding the device at mapload time + */ +/datum/component/ntnet_interface/Initialize(network_name, network_tag = null) + if(network_name == null || !istext(network_name)) + log_telecomms("ntnet_interface/Initialize: Bad network '[network_name]' for '[parent]', going to limbo it") + network_name = LIMBO_NETWORK_ROOT + // Tags cannot be numbers and must be unique over the world + if(network_tag != null && !istext(network_tag)) + // numbers are not allowed as lookups for interfaces + log_telecomms("Tag cannot be a number? '[network_name]' for '[parent]', going to limbo it") + network_tag = "BADTAG_" + network_tag + + hardware_id = SSnetworks.get_next_HID() + id_tag = network_tag + SSnetworks.interfaces_by_hardware_id[hardware_id] = src + + network = SSnetworks.create_network_simple(network_name) + + network.add_interface(src) + + +/** + * Create a port for this interface + * + * A port is basicity a shared associated list() with some values that + * indicated its been updated. (see _DEFINES/network.dm). By using a shared + * we don't have to worry about qdeling this object if it goes out of scope. + * + * Once a port is created any number of devices can use the port, however only + * the creating interface can disconnect it. + * + * Arguments: + * * port - text, Name of the port installed on this interface + * * data - list, shared list of data. Don't put objects in this + */ +/datum/component/ntnet_interface/proc/register_port(port, list/data) + if(!port || !length(data)) + stack_trace("port is null or data is empty") + return + if(registered_sockets[port]) + stack_trace("port already regestered") + return + data["_updated"] = FALSE + registered_sockets[port] = data + +/** + * Disconnects an existing port in the interface + * + * Removes a port from this interface and marks it that its + * has been disconnected + * + * Arguments: + * * port - text, Name of the port installed on this interface + * * data - list, shared list of data. Don't put objects in this + */ +/datum/component/ntnet_interface/proc/deregister_port(port) + if(registered_sockets[port]) // should I runtime if this isn't in here? + var/list/datalink = registered_sockets[port] + NETWORK_PORT_DISCONNECT(datalink) + // this should remove all outstanding ports + registered_sockets.Remove(port) + + +/** + * Connect to a port on this interface + * + * Returns the shared list that this interface uses to send + * data though a port. + * + * Arguments: + * * port - text, Name of the port installed on this interface + */ +/datum/component/ntnet_interface/proc/connect_port(port) + return registered_sockets[port] + -/datum/component/ntnet_interface/Initialize(force_name = "NTNet Device", autoconnect_station_network = TRUE) //Don't force ID unless you know what you're doing! - hardware_id = "[SSnetworks.get_next_HID()]" - network_name = force_name - if(!SSnetworks.register_interface(src)) - . = COMPONENT_INCOMPATIBLE - CRASH("Unable to register NTNet interface. Interface deleted.") - if(autoconnect_station_network) - register_connection(SSnetworks.station_network) /datum/component/ntnet_interface/Destroy() - unregister_all_connections() - SSnetworks.unregister_interface(src) + network.remove_interface(src, TRUE) + SSnetworks.interfaces_by_hardware_id.Remove(hardware_id) + for(var/port in registered_sockets) + var/list/datalink = registered_sockets[port] + NETWORK_PORT_DISCONNECT(datalink) + registered_sockets.Cut() return ..() - -/datum/component/ntnet_interface/proc/__network_receive(datum/netdata/data) //Do not directly proccall! - SEND_SIGNAL(parent, COMSIG_COMPONENT_NTNET_RECEIVE, data) - if(differentiate_broadcast && data.broadcast) - parent.ntnet_receive_broadcast(data) - else - parent.ntnet_receive(data) - -/datum/component/ntnet_interface/proc/__network_send(datum/netdata/data, netid) //Do not directly proccall! - - 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 diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm index 071cb0269cf..639a3da3852 100644 --- a/code/game/area/Space_Station_13_areas.dm +++ b/code/game/area/Space_Station_13_areas.dm @@ -1321,6 +1321,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg', 'sound/ambience/ambitech.ogg',\ 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg', 'sound/ambience/ambimystery.ogg') airlock_wires = /datum/wires/airlock/engineering + network_root_id = STATION_NETWORK_ROOT // They should of unpluged the router before they left /area/tcommsat/computer name = "Telecomms Control Room" diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 1e96f3dc52c..231f1088d58 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -71,10 +71,14 @@ ///This datum, if set, allows terrain generation behavior to be ran on Initialize() var/datum/map_generator/map_generator + /// Default network root for this area aka station, lavaland, etc + var/network_root_id = null + /// Area network id when you want to find all devices hooked up to this area + var/network_area_id = null + ///Used to decide what kind of reverb the area makes sound have var/sound_environment = SOUND_ENVIRONMENT_NONE - /** * A list of teleport locations * @@ -120,7 +124,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) power_usage = new /list(AREA_USAGE_LEN) // Some atoms would like to use power in Initialize() return ..() -/** +/* * Initalize this area * * intializes the dynamic area lighting and also registers the area with the z level via @@ -128,7 +132,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) * * returns INITIALIZE_HINT_LATELOAD */ -/area/Initialize() +/area/Initialize(mapload) icon_state = "" if(requires_power) @@ -146,6 +150,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) if(dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT) dynamic_lighting = CONFIG_GET(flag/starlight) ? DYNAMIC_LIGHTING_ENABLED : DYNAMIC_LIGHTING_DISABLED + . = ..() blend_mode = BLEND_MULTIPLY // Putting this in the constructor so that it stops the icons being screwed up in the map editor. @@ -155,6 +160,11 @@ GLOBAL_LIST_EMPTY(teleportlocs) reg_in_areas_in_z() + if(!mapload) + if(!network_root_id) + network_root_id = STATION_NETWORK_ROOT // default to station root because this might be created with a blueprint + SSnetworks.assign_area_network_id(src) + return INITIALIZE_HINT_LATELOAD /** diff --git a/code/game/area/areas/away_content.dm b/code/game/area/areas/away_content.dm index 53ccc590c72..83f098ef1f1 100644 --- a/code/game/area/areas/away_content.dm +++ b/code/game/area/areas/away_content.dm @@ -31,3 +31,4 @@ Unused icons for new areas are "awaycontent1" ~ "awaycontent30" dynamic_lighting = DYNAMIC_LIGHTING_DISABLED var/pacifist = TRUE // if when you enter this zone, you become a pacifist or not var/death = FALSE // if when you enter this zone, you die + network_root_id = "VR" diff --git a/code/game/area/areas/centcom.dm b/code/game/area/areas/centcom.dm index fce13a4040e..cfe8bc85203 100644 --- a/code/game/area/areas/centcom.dm +++ b/code/game/area/areas/centcom.dm @@ -1,6 +1,10 @@ // CENTCOM +// Side note, be sure to change the network_root_id of any areas that are not a part of centcom +// and just using the z space as safe harbor. It shouldn't matter much as centcom z is isolated +// from everything anyway + /area/centcom name = "CentCom" icon_state = "centcom" @@ -47,7 +51,7 @@ var/loading_id = "" /area/centcom/supplypod/loading/Initialize() - . = ..() + . = ..() if(!loading_id) CRASH("[type] created without a loading_id") if(GLOB.supplypod_loading_bays[loading_id]) @@ -121,6 +125,7 @@ has_gravity = STANDARD_GRAVITY area_flags = VALID_TERRITORY | UNIQUE_AREA | NOTELEPORT flags_1 = NONE + network_root_id = "MAGIC_NET" //Abductors /area/abductor_ship @@ -130,6 +135,7 @@ area_flags = VALID_TERRITORY | UNIQUE_AREA | NOTELEPORT has_gravity = STANDARD_GRAVITY flags_1 = NONE + network_root_id = "ALIENS" //Syndicates /area/syndicate_mothership @@ -140,16 +146,18 @@ area_flags = VALID_TERRITORY | UNIQUE_AREA | NOTELEPORT flags_1 = NONE ambientsounds = HIGHSEC + network_root_id = SYNDICATE_NETWORK_ROOT /area/syndicate_mothership/control name = "Syndicate Control Room" icon_state = "syndie-control" dynamic_lighting = DYNAMIC_LIGHTING_FORCED + network_root_id = SYNDICATE_NETWORK_ROOT /area/syndicate_mothership/elite_squad name = "Syndicate Elite Squad" icon_state = "syndie-elite" - + network_root_id = SYNDICATE_NETWORK_ROOT //CAPTURE THE FLAG /area/ctf diff --git a/code/game/area/areas/holodeck.dm b/code/game/area/areas/holodeck.dm index 4d1c561f4a1..f0c9b4cea96 100644 --- a/code/game/area/areas/holodeck.dm +++ b/code/game/area/areas/holodeck.dm @@ -8,7 +8,7 @@ var/obj/machinery/computer/holodeck/linked var/restricted = FALSE // if true, program goes on emag list - + network_root_id = "HOLODECK" /* Power tracking: Use the holodeck computer's power grid Asserts are to avoid the inevitable infinite loops diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 5a676786ca3..93ed0a62383 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -718,28 +718,6 @@ return TRUE return ..() -/** - * Generate a name devices - * - * Creates a randomly generated tag or name for devices5 - * The length of the generated name can be set by passing in an int - * args: - * * len (int)(Optional) Default=5 The length of the name - * Returns (string) The generated name - */ -/obj/machinery/proc/assign_random_name(len=5) - var/list/new_name = list() - // machine id's should be fun random chars hinting at a larger world - for(var/i = 1 to len) - switch(rand(1,3)) - if(1) - new_name += ascii2text(rand(65, 90)) // A - Z - if(2) - new_name += ascii2text(rand(97,122)) // a - z - if(3) - new_name += ascii2text(rand(48, 57)) // 0 - 9 - return new_name.Join() - /** * Alerts the AI that a hack is in progress. * @@ -750,3 +728,4 @@ var/alertstr = "Network Alert: Hacking attempt detected[get_area(src)?" in [get_area_name(src, TRUE)]":". Unable to pinpoint location"]." for(var/mob/living/silicon/ai/AI in GLOB.player_list) to_chat(AI, alertstr) + diff --git a/code/game/machinery/airlock_control.dm b/code/game/machinery/airlock_control.dm index 4d7e59c32b3..9d32c050fd2 100644 --- a/code/game/machinery/airlock_control.dm +++ b/code/game/machinery/airlock_control.dm @@ -2,7 +2,6 @@ // This code allows for airlocks to be controlled externally by setting an id_tag and comm frequency (disables ID access) /obj/machinery/door/airlock - var/id_tag var/frequency var/datum/radio_frequency/radio_connection @@ -92,7 +91,6 @@ power_channel = AREA_USAGE_ENVIRON - var/id_tag var/master_tag var/frequency = FREQ_AIRLOCK_CONTROL diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index 736b8228bdb..e0f1137c4ff 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -10,7 +10,6 @@ var/on = TRUE - var/id_tag var/frequency = FREQ_ATMOS_STORAGE var/datum/radio_frequency/radio_connection diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 34a4e29621b..7675b843202 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -15,6 +15,20 @@ shock - has a chance of electrocuting its target. */ +/// Overlay cache. Why isn't this just in /obj/machinery/door/airlock? Because its used just a +/// tiny bit in door_assembly.dm Refactored so you don't have to make a null copy of airlock +/// to get to the damn thing +/// Someone, for the love of god, profile this. Is there a reason to cache mutable_appearance +/// if so, why are we JUST doing the airlocks when we can put this in mutable_appearance.dm for +/// everything +/proc/get_airlock_overlay(icon_state, icon_file) + var/static/list/airlock_overlays = list() + var/iconkey = "[icon_state][icon_file]" + if((!(. = airlock_overlays[iconkey]))) + . = airlock_overlays[iconkey] = mutable_appearance(icon_file, icon_state) +// Before you say this is a bad implmentation, look at what it was before then ask yourself +// "Would this be better with a global var" + // Wires for the airlock are located in the datum folder, inside the wires datum folder. #define AIRLOCK_CLOSED 1 @@ -101,7 +115,7 @@ flags_1 = RAD_PROTECT_CONTENTS_1 | RAD_NO_CONTAMINATE_1 rad_insulation = RAD_MEDIUM_INSULATION - var/static/list/airlock_overlays = list() + network_id = NETWORK_DOOR_AIRLOCKS /obj/machinery/door/airlock/Initialize() . = ..() @@ -127,6 +141,7 @@ diag_hud_set_electrified() RegisterSignal(src, COMSIG_MACHINERY_BROKEN, .proc/on_break) + RegisterSignal(src, COMSIG_COMPONENT_NTNET_RECEIVE, .proc/ntnet_receive) return INITIALIZE_HINT_LATELOAD @@ -159,9 +174,6 @@ wires.cut(WIRE_AI) update_icon() -/obj/machinery/door/airlock/ComponentInitialize() - . = ..() - AddComponent(/datum/component/ntnet_interface) /obj/machinery/door/airlock/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock) if(id_tag) @@ -204,18 +216,14 @@ /obj/machinery/door/airlock/check_access_ntnet(datum/netdata/data) return !requiresID() || ..() -/obj/machinery/door/airlock/ntnet_receive(datum/netdata/data) +/obj/machinery/door/airlock/proc/ntnet_receive(datum/source, 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 received packet. - var/command = lowertext(data.data["data"]) - var/command_value = lowertext(data.data["data_secondary"]) + var/command = data.data["data"] + var/command_value = data.data["data_secondary"] switch(command) if("open") if(command_value == "on" && !density) @@ -243,6 +251,7 @@ if("emergency") if(command_value == "on" && emergency) + return if(command_value == "off" && !emergency) @@ -586,14 +595,6 @@ check_unres() */ -/proc/get_airlock_overlay(icon_state, icon_file) - var/obj/machinery/door/airlock/A - pass(A) //suppress unused warning - var/list/airlock_overlays = A.airlock_overlays - var/iconkey = "[icon_state][icon_file]" - if((!(. = airlock_overlays[iconkey]))) - . = airlock_overlays[iconkey] = mutable_appearance(icon_file, icon_state) - //SKYRAT EDIT CHANGE BEGIN - AESTHETICS /obj/machinery/door/airlock/proc/check_unres() //unrestricted sides. This overlay indicates which directions the player can access even without an ID if(hasPower() && unres_sides) @@ -615,6 +616,8 @@ add_overlay(I) /* - SKYRAT ORIGINAL +======= +>>>>>>> 56345975ba6 (The Great Radio Rework: NTNET Part 1 of many. (#54462)) /obj/machinery/door/airlock/proc/check_unres() //unrestricted sides. This overlay indicates which directions the player can access even without an ID if(hasPower() && unres_sides) if(unres_sides & NORTH) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 3d639d05c5e..5948cea73a1 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -16,6 +16,7 @@ pass_flags_self = PASSGLASS CanAtmosPass = ATMOS_PASS_PROC interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN + network_id = NETWORK_DOOR_AIRLOCKS var/obj/item/electronics/airlock/electronics = null var/reinf = 0 var/shards = 2 @@ -38,9 +39,7 @@ if(cable) debris += new /obj/item/stack/cable_coil(src, cable) -/obj/machinery/door/window/ComponentInitialize() - . = ..() - AddComponent(/datum/component/ntnet_interface) + RegisterSignal(src, COMSIG_COMPONENT_NTNET_RECEIVE, .proc/ntnet_receive) /obj/machinery/door/window/Destroy() density = FALSE @@ -309,18 +308,14 @@ /obj/machinery/door/window/check_access_ntnet(datum/netdata/data) return !requiresID() || ..() -/obj/machinery/door/window/ntnet_receive(datum/netdata/data) +/obj/machinery/door/window/proc/ntnet_receive(datum/source, datum/netdata/data) // Check if the airlock is powered. if(!hasPower()) return - // Check packet access level. - if(!check_access_ntnet(data)) - return - // Handle received packet. - var/command = lowertext(data.data["data"]) - var/command_value = lowertext(data.data["data_secondary"]) + var/command = data.data["data"] + var/command_value = data.data["data_secondary"] switch(command) if("open") if(command_value == "on" && !density) diff --git a/code/game/machinery/embedded_controller/airlock_controller.dm b/code/game/machinery/embedded_controller/airlock_controller.dm index c028beb3d69..bc609a079b8 100644 --- a/code/game/machinery/embedded_controller/airlock_controller.dm +++ b/code/game/machinery/embedded_controller/airlock_controller.dm @@ -204,7 +204,6 @@ power_channel = AREA_USAGE_ENVIRON // Setup parameters only - var/id_tag var/exterior_door_tag var/interior_door_tag var/airpump_tag diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm index e5f2ce63d4b..928050f0a37 100644 --- a/code/game/objects/items/control_wand.dm +++ b/code/game/objects/items/control_wand.dm @@ -1,6 +1,6 @@ -#define WAND_OPEN "Open Door" -#define WAND_BOLT "Toggle Bolts" -#define WAND_EMERGENCY "Toggle Emergency Access" +#define WAND_OPEN "open" +#define WAND_BOLT "bolt" +#define WAND_EMERGENCY "emergency" /obj/item/door_remote icon_state = "gangtool-white" @@ -14,13 +14,31 @@ var/mode = WAND_OPEN var/region_access = 1 //See access.dm var/list/access_list + network_id = NETWORK_DOOR_REMOTES /obj/item/door_remote/Initialize() . = ..() access_list = get_region_accesses(region_access) - AddComponent(/datum/component/ntnet_interface) + RegisterSignal(src, COMSIG_COMPONENT_NTNET_NAK, .proc/bad_signal) + RegisterSignal(src, COMSIG_COMPONENT_NTNET_ACK, .proc/good_signal) + +/obj/item/door_remote/proc/bad_signal(datum/source, datum/netdata/data, error_code) + if(QDELETED(data.user)) + return // can't send a message to a missing user + if(error_code == NETWORK_ERROR_UNAUTHORIZED) + to_chat(data.user, "This remote is not authorized to modify this door.") + else + to_chat(data.user, "Error: [error_code]") + + +/obj/item/door_remote/proc/good_signal(datum/source, datum/netdata/data, error_code) + if(QDELETED(data.user)) + return + var/toggled = data.data["data"] + to_chat(data.user, "Door [toggled] toggled") /obj/item/door_remote/attack_self(mob/user) + var/static/list/desc = list(WAND_OPEN = "Open Door", WAND_BOLT = "Toggle Bolts", WAND_EMERGENCY = "Toggle Emergency Access") switch(mode) if(WAND_OPEN) mode = WAND_BOLT @@ -28,7 +46,7 @@ mode = WAND_EMERGENCY if(WAND_EMERGENCY) mode = WAND_OPEN - to_chat(user, "Now in mode: [mode].") + to_chat(user, "Now in mode: [desc[mode]].") // Airlock remote works by sending NTNet packets to whatever it's pointed at. /obj/item/door_remote/afterattack(atom/A, mob/user) @@ -38,20 +56,12 @@ if(!target_interface) return + user.set_machine(user) // Generate a control packet. - var/datum/netdata/data = new - data.recipient_ids = list(target_interface.hardware_id) - - switch(mode) - if(WAND_OPEN) - data.data["data"] = "open" - if(WAND_BOLT) - data.data["data"] = "bolt" - if(WAND_EMERGENCY) - data.data["data"] = "emergency" - - data.data["data_secondary"] = "toggle" + var/datum/netdata/data = new(list("data" = mode,"data_secondary" = "toggle")) + data.receiver_id = target_interface.hardware_id data.passkey = access_list + data.user = user // for responce message ntnet_send(data) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 5aaeb562a6d..6051f942b12 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -39,13 +39,20 @@ vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace. + /// Map tag for something. Tired of it being used on snowflake items. Moved here for some semblance of a standard. + /// Next pr after the network fix will have me refactor door interactions, so help me god. + var/id_tag = null + /// Network id. If set it can be found by either its hardware id or by the id tag if thats set. It can also be + /// broadcasted to as long as the other guys network is on the same branch or above. + var/network_id = null + /obj/vv_edit_var(vname, vval) if(vname == NAMEOF(src, obj_flags)) if ((obj_flags & DANGEROUS_POSSESSION) && !(vval & DANGEROUS_POSSESSION)) return FALSE return ..() -/obj/Initialize() +/obj/Initialize(mapload) if (islist(armor)) armor = getArmor(arglist(armor)) else if (!armor) @@ -70,6 +77,20 @@ var/turf/T = loc T.add_blueprints_preround(src) + if(network_id) + var/area/A = get_area(src) + if(A) + if(!A.network_root_id) + log_telecomms("Area '[A.name]([REF(A)])' has no network network_root_id, force assigning in object [src]([REF(src)])") + SSnetworks.lookup_area_root_id(A) + network_id = NETWORK_NAME_COMBINE(A.network_root_id, network_id) // I regret nothing!! + else + log_telecomms("Created [src]([REF(src)] in nullspace, assuming network to be in station") + network_id = NETWORK_NAME_COMBINE(STATION_NETWORK_ROOT, network_id) // I regret nothing!! + AddComponent(/datum/component/ntnet_interface, network_id, id_tag) + /// Needs to run before as ComponentInitialize runs after this statement...why do we have ComponentInitialize again? + + /obj/Destroy(force=FALSE) if(!ismachinery(src)) STOP_PROCESSING(SSobj, src) // TODO: Have a processing bitflag to reduce on unnecessary loops through the processing lists diff --git a/code/modules/NTNet/netdata.dm b/code/modules/NTNet/netdata.dm index 2a3a85f7064..8df266ac1d5 100644 --- a/code/modules/NTNet/netdata.dm +++ b/code/modules/NTNet/netdata.dm @@ -1,20 +1,102 @@ -/datum/netdata //this requires some thought later on but for now it's fine. - var/network_id +// This netlink class shares a list to two devices +// This allows skipping of sending many many packets +// just to update simple data +/datum/netlink + var/server_id + var/server_network + var/port + var/passkey = null // sends auth data used to check if we can connect or send data to a device + var/list/data - var/autopasskey = TRUE +/datum/netlink/New(datum/component/ntnet_interface/conn, port) + data = conn.registered_sockets[port] + ASSERT(data != null) + server_id = conn.hardware_id + server_network = conn.network.network_id + src.port = port + RegisterSignal(conn, COMSIG_COMPONENT_NTNET_PORT_DESTROYED, .proc/_server_disconnected) + ..() - var/list/recipient_ids = list() +/datum/netlink/proc/_server_disconnected(datum/component/com) + SIGNAL_HANDLER + data = null + +/datum/netlink/Destroy() + passkey = null + data = null + return ..() + +// If you don't want to use this fine, but this just shows how the system works + +// I hate you all. I want to operator overload [] +// So fuck you all, Do NOT access data directly you freaks +// or this breaks god knows what. +// FINE, you don't have to use get, is dirty or even clean +// proc overhead is WAY more important than fucking clarity or +// sealed classes. But for the LOVE OF GOD make sure _updated +// is set if your going to do this. +/datum/netlink/proc/get(idx) + return data ? data[idx] : null + +/datum/netlink/proc/put(idx, V) + // is it posable to do this async without worry about racing conditions? + if(data && data[idx] != V) + data["_updated"] = TRUE + data[idx] = V + + +/datum/netlink/proc/is_dirty() + return data && data["_updated"] + +/datum/netlink/proc/clean() + if(data) + data["_updated"] = FALSE + +/datum/netlink/proc/is_connected() + return data != null + + + +/datum/netdata + //this requires some thought later on but for now it's fine. (WarlockD) ARRRRG + // Packets are kind of shaped like IPX. IPX had a network, a node (aka id) and a port. + // Special case with receiver_id == null, that wil broadcast to the network_id + // Also, if the network id is not the same for both sender and receiver the packet is dropped. var/sender_id - var/broadcast = FALSE //Whether this is a broadcast packet. - + var/receiver_id + var/network_id + var/passkey = null // sends auth data used to check if we can connect or send data to a device var/list/data = list() + // Used for packet queuing + var/datum/netdata/next = null + var/mob/user = null // used for sending error messages - var/list/passkey +/datum/netdata/New(list/data = null) + if(!data) + data = list() + src.data = data -/datum/netdata/proc/standard_format_data(primary, secondary, passkey) - data["data"] = primary - data["data_secondary"] = secondary - data["encrypted_passkey"] = passkey +/datum/netdata/Destroy() + data = null + passkey = null + next = null + user = null + return ..() + +/datum/netdata/proc/clone(deep_copy=FALSE) + var/datum/netdata/C = new + C.sender_id = sender_id + C.receiver_id = receiver_id + C.network_id = network_id + C.passkey = passkey + C.user = user + C.next = null + if(deep_copy) + C.data = deepCopyList(data) + else + C.data = data + return C + /datum/netdata/proc/json_to_data(json) data = json_decode(json) @@ -32,12 +114,12 @@ /datum/netdata/proc/json_list_generation() . = list() . |= json_list_generation_netlog() - .["network_id"] = network_id /datum/netdata/proc/json_list_generation_netlog() . = list() - .["recipient_ids"] = recipient_ids + .["network_id"] = network_id .["sender_id"] = sender_id + .["receiver_id"] = receiver_id .["data_list"] = data /datum/netdata/proc/generate_netlog() diff --git a/code/modules/NTNet/network.dm b/code/modules/NTNet/network.dm index 7f771d34c17..6878ef09313 100644 --- a/code/modules/NTNet/network.dm +++ b/code/modules/NTNet/network.dm @@ -1,21 +1,247 @@ +/* + * # /datum/ntnet + * + * This class defines each network of the world. Each root network is accessible by any device + * on the same network but NOT accessible to any other "root" networks. All normal devices only have + * one network and one network_id. + * + * This thing replaces radio. Think of wifi but better, bigger and bolder! The idea is that any device + * on a network can reach any other device on that same network if it knows the hardware_id. You can also + * search or broadcast to devices if you know what branch you wish. That is to say you can broadcast to all + * devices on "SS13.ATMOS.SCRUBBERS" to change the settings of all the scrubbers on the station or to + * "SS13.AREA.FRED_HOME.SCRUBBERS" to all the scrubbers at one area. However devices CANNOT communicate cross + * networks normality. + * + */ + /datum/ntnet - var/network_id = "Network" - var/list/connected_interfaces_by_id = list() //id = datum/component/ntnet_interface + /// The full network name for this network ex. SS13.ATMOS.SCRUBBERS + var/network_id + /// The network name part of this leaf ex ATMOS + var/network_node_id + /// All devices on this network. ALL devices on this network, not just this branch. + /// This list is shared between all leaf networks so we don't have to keep going to the + /// parents on lookups. It is an associated list of hardware_id AND tag_id's + var/list/root_devices + /// This lists has all the networks in this node. Each name is fully qualified + /// ie. SS13.ATMOS.SCRUBBERS, SS13.ATMOS.VENTS, etc + var/list/networks + /// All the devices on this branch of the network + var/list/linked_devices + /// Network children. Associated list using the network_node_id of the child as the key + var/list/children + /// Parrnt of the network. If this is null, we are a oot network + var/datum/ntnet/parent + + +/* + * Creates a new network + * + * Used for /datum/controller/subsystem/networks/proc/create_network so do not + * call yourself as new doesn't do any checking itself + * + * Arguments: + * * net_id - Fully qualified network id for this network + * * net_part_id - sub part of a network if this is a child of P + * * P - Parent network, this will be attached to that network. + */ +/datum/ntnet/New(net_id, net_part_id, datum/ntnet/P = null) + linked_devices = list() + children = list() + network_id = net_id + + if(P) + network_node_id = net_part_id + parent = P + parent.children[network_node_id] = src + root_devices = parent.root_devices + networks = parent.networks + else + network_node_id = net_id + parent = null + networks = list() + root_devices = list() + SSnetworks.root_networks[network_id] = src + + SSnetworks.networks[network_id] = src + + SSnetworks.add_log("Network was created: [network_id]") + + return ..() + +/// A network should NEVER be deleted. If you don't want to show it exists just check if its +/// empty +/datum/ntnet/Destroy() + if(children.len > 0 || linked_devices.len > 0) + CRASH("Trying to delete a network with devices still in them") + + if(parent) + parent.children.Remove(network_id) + parent = null + else + SSnetworks.root_networks.Remove(network_id) + + SSnetworks.networks.Remove(network_id) + + root_devices = null + networks = null + network_node_id = null + SSnetworks.add_log("Network was destroyed: [network_id]") + network_id = null + + return ..() + +/* + * Collects all the devices on this branch of the network and maybe its + * children + * + * Used for broadcasting, this will collect all the interfaces on this + * network and by default everything below this branch. Will return an + * empty list if no devices were found + * + * Arguments: + * * include_children - Include the children of all branches below this + */ +/datum/ntnet/proc/collect_interfaces(include_children=TRUE) + if(!include_children || children.len == 0) + return linked_devices.Copy() + else + /// Please no recursion. Byond hates recursion + var/list/devices = list() + var/list/queue = list(src) // add ourselves + while(queue.len) + var/datum/ntnet/net = queue[queue.len--] + if(net.children.len > 0) + for(var/net_id in net.children) + queue += networks[net_id] + devices += net.linked_devices + return devices + + +/* + * Find if an interface exists on this network + * + * Used to look up a device on the network. + * + * Arguments: + * * tag_or_hid - Ither the hardware id or the id_tag + */ +/datum/ntnet/proc/find_interface(tag_or_hid) + return root_devices[tag_or_hid] + +/** + * Add this interface to this branch of the network. + * + * This will add a network interface to this branch of the network. + * If the interface already exists on the network it will add it and + * give the alias list in the interface this branch name. If the interface + * has an id_tag it will add that name to the root_devices for map lookup + * + * Arguments: + * * interface - ntnet component of the device to add to the network + */ +/datum/ntnet/proc/add_interface(datum/component/ntnet_interface/interface) + if(interface.network) + if(!networks[interface.network.network_id]) + /// If we are doing a hard jump to a new network, log it + log_telecomms("The device {[interface.hardware_id]} is jumping networks from '[interface.network.network_id]' to '[network_id]'") + interface.network.remove_interface(interface, TRUE) + else // we are on the same network so we just want to be aliased to another branch + if(!interface.alias[network_id]) + interface.alias[network_id] = src // add to the alias list + linked_devices[interface.hardware_id] = interface + else + log_telecomms("The device {[interface.hardware_id]} is trying to join '[network_id]' for a second time!") + return + // we have no network + interface.network = src // now we do! + interface.alias[network_id] = src // add to the alias just to make removing easier. + linked_devices[interface.hardware_id] = interface + root_devices[interface.hardware_id] = interface + if(interface.id_tag != null) // could be a type, never know + root_devices[interface.id_tag] = interface + +/* + * Remove this interface from the network + * + * This will remove an interface from this network and null the network field on the + * interface. Be sure that add_interface is run as soon as posable as an interface MUST + * have a network + * + * Arguments: + * * interface - ntnet component of the device to remove to the network + * * remove_all_alias - remove ALL references to this device on this network + */ +/datum/ntnet/proc/remove_interface(datum/component/ntnet_interface/interface, remove_all_alias=FALSE) + if(!interface.alias[network_id]) + log_telecomms("The device {[interface.hardware_id]} is trying to leave a '[network_id]'' when its on '[interface.network.network_id]'") + return + // just cashing it + var/hardware_id = interface.hardware_id + // Handle the quick case + interface.alias.Remove(network_id) + linked_devices.Remove(hardware_id) + if(remove_all_alias) + var/datum/ntnet/net + for(var/id in interface.alias) + net = interface.alias[id] + net.linked_devices.Remove(hardware_id) + + // Now check if there are more than meets the eye + if(interface.network == src || remove_all_alias) + // Ok, so we got to remove this network, but if we have an alias we are still "on" the network + // so we need to shift down to one of the other networks on the alias list. If the alias list + // is empty, fuck it and remove it from the network. + if(interface.alias.len > 0) + interface.network = interface.alias[1] // ... whatever is there. + else + // ok, hard remove from everything then + root_devices.Remove(interface.hardware_id) + if(interface.id_tag != null) // could be a type, never know + root_devices.Remove(interface.id_tag) + interface.network = null + +/* + * Move interface to another branch of the network + * + * This function is a lightweight way of moving an interface from one branch to another like a gps + * device going from one area to another. Target network MUST be this network or it will fail + * + * Arguments: + * * interface - ntnet component of the device to move + * * target_network - qualified network id to move to + * * original_network - qualified network id from the original network if not this one + */ +/datum/ntnet/proc/move_interface(datum/component/ntnet_interface/interface, target_network, original_network = null) + var/datum/ntnet/net = original_network == null ? src : networks[original_network] + var/datum/ntnet/target = networks[target_network] + if(!target || !net) + log_telecomms("The device {[interface.hardware_id]} is trying to move to a network ([target_network]) that is not on ([network_id])") + return + if(target.linked_devices[interface.hardware_id]) + log_telecomms("The device {[interface.hardware_id]} is trying to move to a network ([target_network]) it is already on.") + return + if(!net.linked_devices[interface.hardware_id]) + log_telecomms("The device {[interface.hardware_id]} is trying to move to a network ([target_network]) but its not on ([net.network_id]) ") + return + net.linked_devices.Remove(interface.hardware_id) + target.linked_devices[interface.hardware_id] = interface + interface.alias.Remove(net.network_id) + interface.alias[target.network_id] = target + + + +/datum/ntnet/station_root var/list/services_by_path = list() //type = datum/ntnet_service var/list/services_by_id = list() //id = datum/ntnet_service var/list/autoinit_service_paths = list() //typepaths - var/list/relays = list() - var/list/logs = list() var/list/available_station_software = list() var/list/available_antag_software = list() var/list/chat_channels = list() var/list/fileservers = list() - // Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly. - // High values make displaying logs much laggier. - var/setting_maxlogcount = 100 // These only affect wireless. LAN (consoles) are unaffected since it would be possible to create scenario where someone turns off NTNet, and is unable to turn it back on since it refuses connections var/setting_softwaredownload = TRUE @@ -28,44 +254,28 @@ var/intrusion_detection_alarm = FALSE // Set when there is an IDS warning due to malicious (antag) software. // If new NTNet datum is spawned, it replaces the old one. -/datum/ntnet/New(_netid) - build_software_lists() - add_log("NTNet logging system activated.") - if(_netid) - network_id = _netid - if(!SSnetworks.register_network(src)) - stack_trace("Network [type] with ID [network_id] failed to register and has been deleted.") - qdel(src) +/datum/ntnet/station_root/New(root_name) + . = ..() + SSnetworks.add_log("NTNet logging system activated for [root_name]") -/datum/ntnet/Destroy() - for(var/i in connected_interfaces_by_id) - var/datum/component/ntnet_interface/I = i - I.unregister_connection(src) + + + +#ifdef NTNET_SERVICE +/datum/ntnet/station_root/Destroy() for(var/i in services_by_id) - var/datum/ntnet_service/S = i + var/S = i S.disconnect(src, TRUE) return ..() -/datum/ntnet/proc/interface_connect(datum/component/ntnet_interface/I) - if(connected_interfaces_by_id[I.hardware_id]) - return FALSE - connected_interfaces_by_id[I.hardware_id] = I - return TRUE -/datum/ntnet/proc/interface_disconnect(datum/component/ntnet_interface/I) - connected_interfaces_by_id -= I.hardware_id - return TRUE - -/datum/ntnet/proc/find_interface_id(id) - return connected_interfaces_by_id[id] - -/datum/ntnet/proc/find_service_id(id) +/datum/ntnet/station_root/proc/find_service_id(id) return services_by_id[id] -/datum/ntnet/proc/find_service_path(path) +/datum/ntnet/station_root/proc/find_service_path(path) return services_by_path[path] -/datum/ntnet/proc/register_service(datum/ntnet_service/S) +/datum/ntnet/station_root/proc/register_service(datum/ntnet_service/S) if(!istype(S)) return FALSE if(services_by_path[S.type] || services_by_id[S.id]) @@ -74,14 +284,14 @@ services_by_id[S.id] = S return TRUE -/datum/ntnet/proc/unregister_service(datum/ntnet_service/S) +/datum/ntnet/station_root/proc/unregister_service(datum/ntnet_service/S) if(!istype(S)) return FALSE services_by_path -= S.type services_by_id -= S.id return TRUE -/datum/ntnet/proc/create_service(type) +/datum/ntnet/station_root/proc/create_service(type) var/datum/ntnet_service/S = new type if(!istype(S)) return FALSE @@ -89,7 +299,7 @@ if(!.) qdel(S) -/datum/ntnet/proc/destroy_service(type) +/datum/ntnet/station_root/proc/destroy_service(type) var/datum/ntnet_service/S = find_service_path(type) if(!istype(S)) return FALSE @@ -97,69 +307,21 @@ if(.) qdel(src) -/datum/ntnet/proc/process_data_transmit(datum/component/ntnet_interface/sender, datum/netdata/data) - if(!check_relay_operation()) - return FALSE - data.network_id = src - log_data_transfer(data) - var/list/datum/component/ntnet_interface/receiving = list() - if((length(data.recipient_ids == 1) && data.recipient_ids[1] == NETWORK_BROADCAST_ID) || data.recipient_ids == NETWORK_BROADCAST_ID) - data.broadcast = TRUE - for(var/i in connected_interfaces_by_id) - receiving |= connected_interfaces_by_id[i] - else - for(var/i in data.recipient_ids) - var/datum/component/ntnet_interface/receiver = find_interface_id(i) - receiving |= receiver - - for(var/i in receiving) - var/datum/component/ntnet_interface/receiver = i - if(receiver) - receiver.__network_receive(data) - - for(var/i in services_by_id) - var/datum/ntnet_service/serv = services_by_id[i] - serv.ntnet_intercept(data, src, sender) - - return TRUE - -/datum/ntnet/proc/check_relay_operation(zlevel) //can be expanded later but right now it's true/false. - for(var/i in relays) - var/obj/machinery/ntnet_relay/n = i - if(zlevel && n.z != zlevel) - continue - if(n.is_operational) - return TRUE - return FALSE - -/datum/ntnet/proc/log_data_transfer(datum/netdata/data) - logs += "[station_time_timestamp()] - [data.generate_netlog()]" - if(logs.len > setting_maxlogcount) - logs = logs.Copy(logs.len - setting_maxlogcount, 0) - return - -// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional. -/datum/ntnet/proc/add_log(log_string, obj/item/computer_hardware/network_card/source = null) - var/log_text = "[station_time_timestamp()] - " - if(source) - log_text += "[source.get_network_tag()] - " - else - log_text += "*SYSTEM* - " - log_text += log_string - logs.Add(log_text) - - // We have too many logs, remove the oldest entries until we get into the limit - if(logs.len > setting_maxlogcount) - logs = logs.Copy(logs.len-setting_maxlogcount,0) - +/datum/ntnet/station_root/proc/process_data_transmit(datum/component/ntnet_interface/sender, datum/netdata/data) + if(..()) + for(var/i in services_by_id) + var/datum/ntnet_service/serv = services_by_id[i] + serv.ntnet_intercept(data, src, sender) + return TRUE +#endif // Checks whether NTNet operates. If parameter is passed checks whether specific function is enabled. -/datum/ntnet/proc/check_function(specific_action = 0) - if(!relays || !relays.len) // No relays found. NTNet is down +/datum/ntnet/station_root/proc/check_function(specific_action = 0) + if(!SSnetworks.relays || !SSnetworks.relays.len) // No relays found. NTNet is down return FALSE // Check all relays. If we have at least one working relay, network is up. - if(!check_relay_operation()) + if(!SSnetworks.check_relay_operation()) return FALSE if(setting_disabled) @@ -177,7 +339,7 @@ return TRUE // Builds lists that contain downloadable software. -/datum/ntnet/proc/build_software_lists() +/datum/ntnet/station_root/proc/build_software_lists() available_station_software = list() available_antag_software = list() for(var/F in typesof(/datum/computer_file/program)) @@ -192,7 +354,7 @@ available_antag_software.Add(prog) // Attempts to find a downloadable file according to filename var -/datum/ntnet/proc/find_ntnet_file_by_name(filename) +/datum/ntnet/station_root/proc/find_ntnet_file_by_name(filename) for(var/N in available_station_software) var/datum/computer_file/program/P = N if(filename == P.filename) @@ -202,55 +364,41 @@ if(filename == P.filename) return P -/datum/ntnet/proc/get_chat_channel_by_id(id) +/datum/ntnet/station_root/proc/get_chat_channel_by_id(id) for(var/datum/ntnet_conversation/chan in chat_channels) if(chan.id == id) return chan // Resets the IDS alarm -/datum/ntnet/proc/resetIDS() +/datum/ntnet/station_root/proc/resetIDS() intrusion_detection_alarm = FALSE -/datum/ntnet/proc/toggleIDS() +/datum/ntnet/station_root/proc/toggleIDS() resetIDS() intrusion_detection_enabled = !intrusion_detection_enabled -// Removes all logs -/datum/ntnet/proc/purge_logs() - logs = list() - add_log("-!- LOGS DELETED BY SYSTEM OPERATOR -!-") -// Updates maximal amount of stored logs. Use this instead of setting the number, it performs required checks. -/datum/ntnet/proc/update_max_log_count(lognumber) - if(!lognumber) - return FALSE - // Trim the value if necessary - lognumber = max(MIN_NTNET_LOGS, min(lognumber, MAX_NTNET_LOGS)) - setting_maxlogcount = lognumber - add_log("Configuration Updated. Now keeping [setting_maxlogcount] logs in system memory.") - -/datum/ntnet/proc/toggle_function(function) +/datum/ntnet/station_root/proc/toggle_function(function) if(!function) return function = text2num(function) switch(function) if(NTNET_SOFTWAREDOWNLOAD) setting_softwaredownload = !setting_softwaredownload - add_log("Configuration Updated. Wireless network firewall now [setting_softwaredownload ? "allows" : "disallows"] connection to software repositories.") + SSnetworks.add_log("Configuration Updated. Wireless network firewall now [setting_softwaredownload ? "allows" : "disallows"] connection to software repositories.") if(NTNET_PEERTOPEER) setting_peertopeer = !setting_peertopeer - add_log("Configuration Updated. Wireless network firewall now [setting_peertopeer ? "allows" : "disallows"] peer to peer network traffic.") + SSnetworks.add_log("Configuration Updated. Wireless network firewall now [setting_peertopeer ? "allows" : "disallows"] peer to peer network traffic.") if(NTNET_COMMUNICATION) setting_communication = !setting_communication - add_log("Configuration Updated. Wireless network firewall now [setting_communication ? "allows" : "disallows"] instant messaging and similar communication services.") + SSnetworks.add_log("Configuration Updated. Wireless network firewall now [setting_communication ? "allows" : "disallows"] instant messaging and similar communication services.") if(NTNET_SYSTEMCONTROL) setting_systemcontrol = !setting_systemcontrol - add_log("Configuration Updated. Wireless network firewall now [setting_systemcontrol ? "allows" : "disallows"] remote control of station's systems.") + SSnetworks.add_log("Configuration Updated. Wireless network firewall now [setting_systemcontrol ? "allows" : "disallows"] remote control of station's systems.") -/datum/ntnet/station - network_id = "SS13-NTNET" -/datum/ntnet/station/proc/register_map_supremecy() //called at map init to make this what station networks use. + +/datum/ntnet/station_root/proc/register_map_supremecy() //called at map init to make this what station networks use. for(var/obj/machinery/ntnet_relay/R in GLOB.machines) - relays.Add(R) + SSnetworks.relays.Add(R) R.NTNet = src diff --git a/code/modules/NTNet/relays.dm b/code/modules/NTNet/relays.dm index 0c31dc599ac..e5bd8d4238a 100644 --- a/code/modules/NTNet/relays.dm +++ b/code/modules/NTNet/relays.dm @@ -79,12 +79,12 @@ if((dos_overload > dos_capacity) && !dos_failure) set_dos_failure(TRUE) update_icon() - SSnetworks.station_network.add_log("Quantum relay switched from normal operation mode to overload recovery mode.") + SSnetworks.add_log("Quantum relay switched from normal operation mode to overload recovery mode.") // If the DoS buffer reaches 0 again, restart. if((dos_overload == 0) && dos_failure) set_dos_failure(FALSE) update_icon() - SSnetworks.station_network.add_log("Quantum relay switched from overload recovery mode to normal operation mode.") + SSnetworks.add_log("Quantum relay switched from overload recovery mode to normal operation mode.") ..() /obj/machinery/ntnet_relay/ui_interact(mob/user, datum/tgui/ui) @@ -110,11 +110,11 @@ dos_overload = 0 set_dos_failure(FALSE) update_icon() - SSnetworks.station_network.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.") + SSnetworks.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.") return TRUE if("toggle") set_relay_enabled(!relay_enabled) - SSnetworks.station_network.add_log("Quantum relay manually [relay_enabled ? "enabled" : "disabled"].") + SSnetworks.add_log("Quantum relay manually [relay_enabled ? "enabled" : "disabled"].") update_icon() return TRUE @@ -123,15 +123,15 @@ component_parts = list() if(SSnetworks.station_network) - SSnetworks.station_network.relays.Add(src) + SSnetworks.relays.Add(src) NTNet = SSnetworks.station_network - SSnetworks.station_network.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]") + SSnetworks.add_log("New quantum relay activated. Current amount of linked relays: [SSnetworks.relays.len]") . = ..() /obj/machinery/ntnet_relay/Destroy() if(SSnetworks.station_network) - SSnetworks.station_network.relays.Remove(src) - SSnetworks.station_network.add_log("Quantum relay connection severed. Current amount of linked relays: [NTNet.relays.len]") + SSnetworks.relays.Remove(src) + SSnetworks.add_log("Quantum relay connection severed. Current amount of linked relays: [SSnetworks.relays.len]") NTNet = null for(var/datum/computer_file/program/ntnet_dos/D in dos_sources) diff --git a/code/modules/NTNet/services/_service.dm b/code/modules/NTNet/services/_service.dm deleted file mode 100644 index 75059d9992b..00000000000 --- a/code/modules/NTNet/services/_service.dm +++ /dev/null @@ -1,38 +0,0 @@ -/datum/ntnet_service - var/name = "Unidentified Network Service" - var/id - var/list/networks_by_id = list() //Yes we support multinetwork services! - -/datum/ntnet_service/New() - var/datum/component/ntnet_interface/N = AddComponent(/datum/component/ntnet_interface, id, name, FALSE) - id = N.hardware_id - -/datum/ntnet_service/Destroy() - for(var/i in networks_by_id) - var/datum/ntnet/N = i - disconnect(N, TRUE) - networks_by_id = null - return ..() - -/datum/ntnet_service/proc/connect(datum/ntnet/net) - if(!istype(net)) - return FALSE - var/datum/component/ntnet_interface/interface = GetComponent(/datum/component/ntnet_interface) - if(!interface.register_connection(net)) - return FALSE - if(!net.register_service(src)) - interface.unregister_connection(net) - return FALSE - networks_by_id[net.network_id] = net - return TRUE - -/datum/ntnet_service/proc/disconnect(datum/ntnet/net, force = FALSE) - if(!istype(net) || (!net.unregister_service(src) && !force)) - return FALSE - var/datum/component/ntnet_interface/interface = GetComponent(/datum/component/ntnet_interface) - interface.unregister_connection(net) - networks_by_id -= net.network_id - return TRUE - -/datum/ntnet_service/proc/ntnet_intercept(datum/netdata/data, datum/ntnet/net, datum/component/ntnet_interface/sender) - return diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm index 29d47348b26..6a18731171c 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm @@ -18,7 +18,6 @@ hide = TRUE shift_underlay_only = FALSE - var/id_tag = null var/pump_direction = RELEASING var/pressure_checks = EXT_BOUND @@ -36,9 +35,9 @@ pipe_state = "uvent" /obj/machinery/atmospherics/components/unary/vent_pump/New() - ..() if(!id_tag) - id_tag = assign_uid_vents() + id_tag = SSnetworks.assign_random_name() + . = ..() /obj/machinery/atmospherics/components/unary/vent_pump/Destroy() var/area/vent_area = get_area(src) @@ -164,7 +163,7 @@ if(!GLOB.air_vent_names[id_tag]) // If we do not have a name, assign one. // Produces names like "Port Quarter Solar vent pump hZ2l6". - name = "\proper [vent_area.name] vent pump [assign_random_name()]" + name = "\proper [vent_area.name] vent pump [id_tag]" GLOB.air_vent_names[id_tag] = name vent_area.air_vent_info[id_tag] = signal.data diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm index 2001629b948..609c6c43b9e 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm @@ -15,7 +15,6 @@ hide = TRUE shift_underlay_only = FALSE - var/id_tag = null var/scrubbing = SCRUBBING //0 = siphoning, 1 = scrubbing var/filter_types = list(/datum/gas/carbon_dioxide) @@ -31,10 +30,9 @@ pipe_state = "scrubber" /obj/machinery/atmospherics/components/unary/vent_scrubber/New() - ..() if(!id_tag) - id_tag = assign_uid_vents() - + id_tag = SSnetworks.assign_random_name() + . = ..() for(var/f in filter_types) if(istext(f)) filter_types -= f @@ -120,7 +118,7 @@ var/area/scrub_area = get_area(src) if(!GLOB.air_scrub_names[id_tag]) // If we do not have a name, assign one - name = "\proper [scrub_area.name] air scrubber [assign_random_name()]" + name = "\proper [scrub_area.name] air scrubber [id_tag]" GLOB.air_scrub_names[id_tag] = name scrub_area.air_scrub_info[id_tag] = signal.data diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index 6a0918343bc..c36468b6b2d 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -12,7 +12,6 @@ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 0, BIO = 100, RAD = 100, FIRE = 40, ACID = 0) var/frequency = 0 var/atom/target - var/id_tag var/target_layer = PIPING_LAYER_DEFAULT /obj/machinery/meter/atmos diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm index 9f06ea93b19..3fab13fdb19 100644 --- a/code/modules/jobs/access.dm +++ b/code/modules/jobs/access.dm @@ -90,8 +90,17 @@ return FALSE return TRUE -/obj/proc/check_access_ntnet(datum/netdata/data) - return check_access_list(data.passkey) +/* + * Checks if this packet can access this device + * + * Normally just checks the access list however you can override it for + * hacking proposes or if wires are cut + * + * Arguments: + * * passkey - passkey from the datum/netdata packet + */ +/obj/proc/check_access_ntnet(list/passkey) + return check_access_list(passkey) /proc/get_centcom_access(job) switch(job) diff --git a/code/modules/mapping/map_template.dm b/code/modules/mapping/map_template.dm index 1f3472c7ba5..2dff816029f 100644 --- a/code/modules/mapping/map_template.dm +++ b/code/modules/mapping/map_template.dm @@ -6,6 +6,7 @@ var/loaded = 0 // Times loaded this round var/datum/parsed_map/cached_map var/keep_cached_map = FALSE + var/station_id = null // used to override the root id when generating /datum/map_template/New(path = null, rename = null, cache = FALSE) if(path) @@ -25,7 +26,9 @@ cached_map = parsed return bounds -/datum/parsed_map/proc/initTemplateBounds() +/datum/parsed_map/proc/initTemplateBounds(datum/map_template/template) + + var/list/obj/machinery/atmospherics/atmos_machines = list() var/list/obj/structure/cable/cables = list() var/list/atom/atoms = list() @@ -45,7 +48,11 @@ ) for(var/L in turfs) var/turf/B = L - areas |= B.loc + var/area/G = B.loc + areas |= G + if(!SSatoms.initialized) + continue + for(var/A in B) atoms += A if(istype(A, /obj/structure/cable)) @@ -54,7 +61,17 @@ if(istype(A, /obj/machinery/atmospherics)) atmos_machines += A + // Not sure if there is some importance here to make sure the area is in z + // first or not. Its defined In Initialize yet its run first in templates + // BEFORE so... hummm SSmapping.reg_in_areas_in_z(areas) + // We have to do this hack here because its the ONLY place we can get the + // meta data from the template so we can properly set up the area + SSnetworks.assign_areas_root_ids(areas, template) + // If the world is starting up stop here and the world will do the rest + if(!SSatoms.initialized) + return + SSatoms.InitializeAtoms(areas + turfs + atoms) // NOTE, now that Initialize and LateInitialize run correctly, do we really // need these two below? @@ -92,7 +109,7 @@ repopulate_sorted_areas() //initialize things that are normally initialized after map load - parsed.initTemplateBounds() + parsed.initTemplateBounds(src) smooth_zlevel(world.maxz) log_game("Z-level [name] loaded at [x],[y],[world.maxz]") @@ -129,7 +146,7 @@ repopulate_sorted_areas() //initialize things that are normally initialized after map load - parsed.initTemplateBounds() + parsed.initTemplateBounds(src) log_game("[name] loaded at [T.x],[T.y],[T.z]") return bounds diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 2c11592ced4..99454a745e9 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -32,6 +32,7 @@ bot_core_type = /obj/machinery/bot_core/mulebot hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_PATH_HUD = HUD_LIST_LIST) //Diagnostic HUD views + var/network_id = NETWORK_BOTS_CARGO /// unique identifier in case there are multiple mulebots. var/id @@ -55,6 +56,8 @@ var/bloodiness = 0 ///If we've run over a mob, how many tiles will we leave tracks on while moving var/num_steps = 0 ///The amount of steps we should take until we rest for a time. + + /mob/living/simple_animal/bot/mulebot/Initialize(mapload) . = ..() if(prob(0.666) && mapload) @@ -73,9 +76,9 @@ AddElement(/datum/element/ridable, /datum/component/riding/creature/mulebot) diag_hud_set_mulebotcell() -/mob/living/simple_animal/bot/mulebot/ComponentInitialize() - . = ..() - AddComponent(/datum/component/ntnet_interface) + if(network_id) + AddComponent(/datum/component/ntnet_interface, network_id) + /mob/living/simple_animal/bot/mulebot/handle_atom_del(atom/A) if(A == load) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 41f843ddfdd..a13492ab9d4 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -367,7 +367,8 @@ if(!get_ntnet_status()) return FALSE var/obj/item/computer_hardware/network_card/network_card = all_components[MC_NET] - return SSnetworks.station_network.add_log(text, network_card) + + return SSnetworks.add_log(text, network_card.network_id, network_card.hardware_id) /obj/item/modular_computer/proc/shutdown_computer(loud = 1) kill_program(forced = TRUE) diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index fb7df4dafcc..6bab999facd 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -45,7 +45,7 @@ return switch(action) if("PRG_target_relay") - for(var/obj/machinery/ntnet_relay/R in SSnetworks.station_network.relays) + for(var/obj/machinery/ntnet_relay/R in SSnetworks.relays) if("[R.uid]" == params["targid"]) target = R break @@ -63,7 +63,7 @@ target.dos_sources.Add(src) if(SSnetworks.station_network.intrusion_detection_enabled) var/obj/item/computer_hardware/network_card/network_card = computer.all_components[MC_NET] - SSnetworks.station_network.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [network_card.get_network_tag()]") + SSnetworks.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [network_card.get_network_tag()]") SSnetworks.station_network.intrusion_detection_alarm = TRUE return TRUE @@ -83,7 +83,7 @@ else data["target"] = FALSE data["relays"] = list() - for(var/obj/machinery/ntnet_relay/R in SSnetworks.station_network.relays) + for(var/obj/machinery/ntnet_relay/R in SSnetworks.relays) data["relays"] += list(list("id" = R.uid)) data["focus"] = target ? target.uid : null diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm index 63f0b18a747..b39fcad572d 100644 --- a/code/modules/modular_computers/file_system/programs/ntmonitor.dm +++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm @@ -36,12 +36,12 @@ return TRUE if("purgelogs") if(SSnetworks.station_network) - SSnetworks.station_network.purge_logs() + SSnetworks.purge_logs() return TRUE if("updatemaxlogs") var/logcount = params["new_number"] if(SSnetworks.station_network) - SSnetworks.station_network.update_max_log_count(logcount) + SSnetworks.update_max_log_count(logcount) return TRUE if("toggle_function") if(!SSnetworks.station_network) @@ -55,7 +55,7 @@ var/list/data = get_header_data() data["ntnetstatus"] = SSnetworks.station_network.check_function() - data["ntnetrelays"] = SSnetworks.station_network.relays.len + data["ntnetrelays"] = SSnetworks.relays.len data["idsstatus"] = SSnetworks.station_network.intrusion_detection_enabled data["idsalarm"] = SSnetworks.station_network.intrusion_detection_alarm @@ -68,8 +68,8 @@ data["minlogs"] = MIN_NTNET_LOGS data["maxlogs"] = MAX_NTNET_LOGS - for(var/i in SSnetworks.station_network.logs) + for(var/i in SSnetworks.logs) data["ntnetlogs"] += list(list("entry" = i)) - data["ntnetmaxlogs"] = SSnetworks.station_network.setting_maxlogcount + data["ntnetmaxlogs"] = SSnetworks.setting_maxlogcount return data diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index 5a65184a274..bba19831619 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -3,17 +3,18 @@ desc = "A basic wireless network card for usage with standard NTNet frequencies." power_usage = 50 icon_state = "radio_mini" - var/identification_id = null // Identification ID. Technically MAC address of this device. Can't be changed by user. + network_id = NETWORK_CARDS // Network we are on + var/hardware_id = null // Identification ID. Technically MAC address of this device. Can't be changed by user. var/identification_string = "" // Identification string, technically nickname seen in the network. Can be set by user. var/long_range = 0 var/ethernet = 0 // Hard-wired, therefore always on, ignores NTNet wireless checks. malfunction_probability = 1 device_type = MC_NET - var/static/ntnet_card_uid = 1 + /obj/item/computer_hardware/network_card/diagnostics(mob/user) ..() - to_chat(user, "NIX Unique ID: [identification_id]") + to_chat(user, "NIX Unique ID: [hardware_id]") to_chat(user, "NIX User Tag: [identification_string]") to_chat(user, "Supported protocols:") to_chat(user, "511.m SFS (Subspace) - Standard Frequency Spread") @@ -22,13 +23,10 @@ if(ethernet) to_chat(user, "OpenEth (Physical Connection) - Physical network connection port") -/obj/item/computer_hardware/network_card/New(l) - ..() - identification_id = ntnet_card_uid++ // Returns a string identifier of this network card /obj/item/computer_hardware/network_card/proc/get_network_tag() - return "[identification_string] (NID [identification_id])" + return "[identification_string] (NID [hardware_id])" // 0 - No signal, 1 - Low signal, 2 - High signal. 3 - Wired Connection /obj/item/computer_hardware/network_card/proc/get_signal(specific_action = 0) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index df69991f7b9..d943bc22e35 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -50,6 +50,7 @@ #include "medical_wounds.dm" #include "merge_type.dm" #include "metabolizing.dm" +#include "ntnetwork_tests.dm" #include "outfit_sanity.dm" #include "pills.dm" #include "plantgrowth_tests.dm" diff --git a/code/modules/unit_tests/ntnetwork_tests.dm b/code/modules/unit_tests/ntnetwork_tests.dm new file mode 100644 index 00000000000..9ae9efe0287 --- /dev/null +++ b/code/modules/unit_tests/ntnetwork_tests.dm @@ -0,0 +1,68 @@ +/datum/unit_test/ntnetwork + var/list/valid_network_names = list("SS13.ATMOS.SCRUBBERS.SM", "DEEPSPACE.HYDRO.PLANT", "SINDIE.STINKS.BUTT") + + var/list/invalid_network_names = list(".SS13.BOB", "SS13.OHMAN.", "SS13.HAS A SPACE" ) + + var/list/valid_network_trees = list(list("SS13","ATMOS","SCRUBBERS","SM"),list("DEEPSPACE","HYDRO","PLANT"), list("SINDIE","STINKS","BUTT")) + + var/list/network_roots = list( + __STATION_NETWORK_ROOT, + __CENTCOM_NETWORK_ROOT, + __SYNDICATE_NETWORK_ROOT, + __LIMBO_NETWORK_ROOT) + + var/list/random_words_for_testing = list( + __NETWORK_TOOLS, __NETWORK_REMOTES, __NETWORK_AIRLOCKS, + __NETWORK_DOORS, __NETWORK_ATMOS, __NETWORK_SCUBBERS, + __NETWORK_AIRALARMS, __NETWORK_CONTROL, __NETWORK_STORAGE, + __NETWORK_CARGO, __NETWORK_BOTS, __NETWORK_COMPUTER, + __NETWORK_CARDS) + + var/number_of_names_to_test = 50 + var/length_of_test_network = 5 + +/datum/unit_test/proc/mangle_word(word) + var/len = length(word) + var/space_pos = round(len/2) + word = copytext(word,1,space_pos) + " " + copytext(word,space_pos,len) + return word + +/datum/unit_test/ntnetwork/Run() + // First check if name checks work + for(var/name in valid_network_names) + TEST_ASSERT(verify_network_name(name), "Network name ([name]) marked as invalid but supposed to valid") + + for(var/name in invalid_network_names) + TEST_ASSERT(!verify_network_name(name), "Network name ([name]) marked as valid but supposed to be invalid") + + // Next check if we can pack and unpack network names + for(var/i in 1 to valid_network_names.len) + var/name = valid_network_names[i] + var/list/name_list = SSnetworks.network_string_to_list(name) + TEST_ASSERT(compare_list(name_list, valid_network_trees[i]), "Network name ([name]) did not unpack into a proper list") + for(var/i in 1 to valid_network_trees.len) + var/list/name_list = valid_network_trees[i] + var/name = SSnetworks.network_list_to_string(name_list) + TEST_ASSERT_EQUAL(name, valid_network_names[i], "Network name ([name]) did not pack into a proper string") + + // Ok, we know we can verify network names now, and that we can pack and unpack. Lets try making some random good names + var/list/generated_network_names = list() + var/test_string + for(var/i in 1 to number_of_names_to_test) + var/list/builder = list() + builder += pick(network_roots) + for(var/j in 1 to length_of_test_network) + builder += pick(random_words_for_testing) + test_string = SSnetworks.network_list_to_string(builder) + var/name_fix = simple_network_name_fix(test_string) + TEST_ASSERT_EQUAL(name_fix, test_string, "Network name ([test_string]) was not fixed correctly to ([name_fix])") + generated_network_names += name_fix // save for future + // test badly generated names + for(var/i in 1 to number_of_names_to_test) + var/list/builder = list() + builder += mangle_word(pick(network_roots)) + for(var/j in 1 to length_of_test_network) + builder += mangle_word(pick(random_words_for_testing)) + test_string = builder.Join(".") + var/name_fix = simple_network_name_fix(test_string) + TEST_ASSERT(verify_network_name(name_fix), "Network name ([test_string]) was not fixed correctly ([name_fix]) with bad name") diff --git a/tgstation.dme b/tgstation.dme index 269f33be355..4de143eaa58 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2690,7 +2690,6 @@ #include "code\modules\NTNet\netdata.dm" #include "code\modules\NTNet\network.dm" #include "code\modules\NTNet\relays.dm" -#include "code\modules\NTNet\services\_service.dm" #include "code\modules\paperwork\carbonpaper.dm" #include "code\modules\paperwork\clipboard.dm" #include "code\modules\paperwork\contract.dm"