diff --git a/WorkInProgress/francinum/centralized_phones/callstation.dm b/WorkInProgress/francinum/centralized_phones/callstation.dm
index 986a768a652f..ed5508693a75 100644
--- a/WorkInProgress/francinum/centralized_phones/callstation.dm
+++ b/WorkInProgress/francinum/centralized_phones/callstation.dm
@@ -44,7 +44,7 @@ Phone registration flow (Post-Roundstart):
return
switch(lowertext(signal.data["command"]))
if("ping_reply")// A reply to our ping!
- if(signal.data[PACKET_NETCLASS] != "PNET_SIPSERVER") //Not who we care about!
+ if(signal.data[LEGACY_PACKET_NETCLASS] != "PNET_SIPSERVER") //Not who we care about!
return
register_with_server(signal.data["s_addr"])// It's a server, link!
if("sip_adopt")
diff --git a/code/__DEFINES/computer4_defines.dm b/code/__DEFINES/computer4_defines.dm
index 661abb171df5..c348e2fd9574 100644
--- a/code/__DEFINES/computer4_defines.dm
+++ b/code/__DEFINES/computer4_defines.dm
@@ -4,8 +4,15 @@
// See proc/peripheral_input
#define PERIPHERAL_CMD_RECEIVE_PACKET "receive_packet"
+#define PERIPHERAL_CMD_RECEIVE_PDP_PACKET "receive_pdp_packet"
#define PERIPHERAL_CMD_SCAN_CARD "scan_card"
+// Shell command macros
+#define SHELL_CMD_HELP_ERROR 1 //! Could not find a command to display information about
+#define SHELL_CMD_HELP_GENERIC 2 //! Generated generic help information
+#define SHELL_CMD_HELP_COMMAND 3 //! Generated information about a command
+
+
// MedTrak menus
#define MEDTRAK_MENU_HOME 1
#define MEDTRAK_MENU_INDEX 2
@@ -20,6 +27,11 @@
// ITS THREE IN THE MOOOOORNIN' AND YOOUUU'RE EATING ALONE
#define DIRECTMAN_MENU_NEW_DIRECTIVE 5
+// packMAN shit
+#define PACKMAN_MODE_UNDEFINED 0
+#define PACKMAN_MODE_CLIENT 1
+#define PACKMAN_MODE_SERVER 2
+
// Wireless card incoming filter modes
#define WIRELESS_FILTER_PROMISC 0 //! Forward all packets
#define WIRELESS_FILTER_NETADDR 1 //! Forward only bcast/unicast matched GPRS packets
@@ -27,6 +39,11 @@
#define WIRELESS_FILTER_MODEMAX 2 //! Max of WIRELESS_FILTER_* Defines.
+// PDP BindPort return values
+
+#define PDP_BIND_FAILED_CATASTROPHIC 1 //! What the fuck did you do???
+#define PDP_BIND_FAILED_CONFLICT 2 //! Port already bound?
+
// ThinkDOS //
// Constants
#define THINKDOS_MAX_COMMANDS 3 //! The maximum amount of commands
diff --git a/code/__DEFINES/packetnet.dm b/code/__DEFINES/packetnet.dm
index 0bce0df1d9a8..9a5bbe6ad5bb 100644
--- a/code/__DEFINES/packetnet.dm
+++ b/code/__DEFINES/packetnet.dm
@@ -32,14 +32,33 @@
// not honestly thrilled with having these be defines but kapu wants it that way
// I believe every coder is empowered with a right to footgun by our lord Dennis Ritchie
+//* HEADER FIELDS *//
+/// The version of the packet.
+#define PKT_HEAD_VERSION "version"
/// Source (sender) address of a packet
-#define PACKET_SOURCE_ADDRESS "s_addr"
+#define PKT_HEAD_SOURCE_ADDRESS "s_addr"
/// Destination (receiver) address of a packet
-#define PACKET_DESTINATION_ADDRESS "d_addr"
-/// Command (type) of a packet
-#define PACKET_CMD "command"
+#define PKT_HEAD_DEST_ADDRESS "d_addr"
/// Network Class of a device, used as part of ping replies.
-#define PACKET_NETCLASS "netclass"
+#define PKT_HEAD_NETCLASS "netclass"
+
+#define PKT_HEAD_SOURCE_PORT "sourceport"
+#define PKT_HEAD_DEST_PORT "destport"
+
+#define PKT_HEAD_PROTOCOL "proto"
+ #define PKT_PROTOCOL_PDP "pdp"
+
+#define PKT_PAYLOAD "payload"
+
+//* PAYLOAD FIELDS*//
+/// Command (type) of a packet
+#define PKT_ARG_CMD "command"
+
+// Legacy fields. Do not use.
+#define LEGACY_PACKET_SOURCE_ADDRESS PKT_HEAD_SOURCE_ADDRESS
+#define LEGACY_PACKET_DESTINATION_ADDRESS PKT_HEAD_DEST_ADDRESS
+#define LEGACY_PACKET_NETCLASS PKT_HEAD_NETCLASS
+#define LEGACY_PACKET_COMMAND PKT_ARG_CMD
// Pagers
/// Packet arg for pager types
@@ -104,3 +123,17 @@
#define MAGIC_DATA_INVIOLABLE ALL
#define PACKET_STRING_FILE "packetnet.json"
+
+
+// -----
+// PDP Port Allocations
+
+// Kapu insisted these be defines "to not be confusing"
+#define PDP_BIND_EPHEMERAL_PORT -1
+#define PDP_FREE_ALL_PORTS -1
+
+#define PDP_MAX_PORT 65535 //! Maximum PDP Port number
+
+#define PDP_EPHEMERAL_START 49152 //! Start of PDP Ephemeral Range, used for outgoing client connections.
+
+#define PDP_PORT_NETTEST 28910
diff --git a/code/__HELPERS/packetnet.dm b/code/__HELPERS/packetnet.dm
new file mode 100644
index 000000000000..de64ba58951e
--- /dev/null
+++ b/code/__HELPERS/packetnet.dm
@@ -0,0 +1,12 @@
+/// Returns the data field for a packet.
+/proc/packetv2(source_addr, dest_addr, source_port, dest_port, netclass, protocol, list/payload) as /list
+ . = list(
+ PKT_HEAD_VERSION = 2,
+ PKT_HEAD_SOURCE_ADDRESS = source_addr,
+ PKT_HEAD_DEST_ADDRESS = dest_addr,
+ PKT_HEAD_SOURCE_PORT = source_port,
+ PKT_HEAD_DEST_PORT = dest_port,
+ PKT_HEAD_NETCLASS = netclass,
+ PKT_HEAD_PROTOCOL = protocol,
+ PKT_PAYLOAD = payload,
+ )
diff --git a/code/game/communications.dm b/code/game/communications.dm
index a27666573518..52886a86afa2 100644
--- a/code/game/communications.dm
+++ b/code/game/communications.dm
@@ -149,7 +149,7 @@ GLOBAL_LIST_INIT(freq2icon, list(
//If range is null, or 0, signal is TRULY global (skips z_level checks) (Be careful with this.)
//If range > 0, only post to devices on the same z_level and within range
//Use range = -1, to restrain to the same z_level without limiting range
-/datum/radio_frequency/proc/post_signal(datum/signal/signal, filter = null as text|null, range = null as num|null)
+/datum/radio_frequency/post_signal(datum/signal/signal, filter = null as text|null, range = null as num|null)
if(!istype(signal))
CRASH("LEGACY POST SIGNAL SHIT")
@@ -194,6 +194,11 @@ GLOBAL_LIST_INIT(freq2icon, list(
//SHOULD_CALL_PARENT(TRUE)
. = TRUE
+
+/datum/proc/post_signal(datum/signal/signal)
+ CRASH("Invoked base datum post_signal handler. Did something call parent?")
+
+
/datum/signal
/// The author/sender of this packet.
/// This atom will be skipped during packet send.
diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm
index b3ce1a8b03fd..24bf637914b8 100644
--- a/code/game/machinery/announcement_system.dm
+++ b/code/game/machinery/announcement_system.dm
@@ -224,7 +224,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
for(var/next_addr in targets)
var/datum/signal/outgoing = create_signal(next_addr, list(
- PACKET_CMD = NETCMD_PDAMESSAGE,
+ LEGACY_PACKET_COMMAND = NETCMD_PDAMESSAGE,
"name" = "Automated Announcement System",
"job" = "Security Department Update",
"message" = "Officer [officer.real_name] has been assigned to your department, [department].",
@@ -244,7 +244,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
break //No RF card to send to.
var/message = "You have been fined [fine_amount] marks for '[cite_message]'. Fines may be paid at security."
var/datum/signal/outgoing = create_signal(rfcard.hardware_id, list(
- PACKET_CMD = NETCMD_PDAMESSAGE,
+ LEGACY_PACKET_COMMAND = NETCMD_PDAMESSAGE,
"name" = "Automated Announcement System",
"job" = "Security Citation",
"message" = message,
@@ -257,7 +257,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/obj/machinery/announcement_system/proc/mass_pda_message(list/recipients, message, reason)
for(var/next_addr in recipients)
var/datum/signal/outgoing = create_signal(next_addr, list(
- PACKET_CMD = NETCMD_PDAMESSAGE,
+ LEGACY_PACKET_COMMAND = NETCMD_PDAMESSAGE,
"name" = "Automated Announcement System",
"job" = reason,
"message" = message,
@@ -270,7 +270,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/// A helper proc for sending an announcement to a single PDA.
/obj/machinery/announcement_system/proc/pda_message(recipient, message, reason)
var/datum/signal/outgoing = create_signal(recipient, list(
- PACKET_CMD = NETCMD_PDAMESSAGE,
+ LEGACY_PACKET_COMMAND = NETCMD_PDAMESSAGE,
"name" = "Automated Announcement System",
"job" = reason,
"message" = message,
diff --git a/code/game/machinery/buttons_radio.dm b/code/game/machinery/buttons_radio.dm
index 4332005a3566..62ca4df29dbd 100644
--- a/code/game/machinery/buttons_radio.dm
+++ b/code/game/machinery/buttons_radio.dm
@@ -13,8 +13,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/button/radio, 24)
var/datum/radio_frequency/radio_connection = SSpackets.return_frequency(frequency)
var/datum/signal/signal = new(src, list(
- PACKET_SOURCE_ADDRESS = net_id,
- PACKET_NETCLASS = NETCLASS_BUTTON,
+ LEGACY_PACKET_SOURCE_ADDRESS = net_id,
+ LEGACY_PACKET_NETCLASS = NETCLASS_BUTTON,
"tag" = id_tag,
))
radio_connection.post_signal(signal)
diff --git a/code/game/machinery/datanet/_networked.dm b/code/game/machinery/datanet/_networked.dm
index 961ccaabddff..6ff1fc55d40d 100644
--- a/code/game/machinery/datanet/_networked.dm
+++ b/code/game/machinery/datanet/_networked.dm
@@ -7,8 +7,8 @@
if(!datagram || !destination_id)
return //Unfortunately /dev/null isn't network-scale.
var/list/sig_data = datagram.Copy()
- sig_data[PACKET_SOURCE_ADDRESS] = src.net_id
- sig_data[PACKET_DESTINATION_ADDRESS] = destination_id
+ sig_data[PKT_HEAD_SOURCE_ADDRESS] = src.net_id
+ sig_data[PKT_HEAD_DEST_ADDRESS] = destination_id
return new /datum/signal(src, sig_data, TRANSMISSION_WIRE)
@@ -16,11 +16,11 @@
/// If you're sending a forged source address (You should have a good reason for this...) set `preserve_s_addr = TRUE
///
/// NONE OF THE ABOVE IS TRUE IF YOU ARE `machinery/power`, AS THEY DEAL DIRECTLY WITH SSPACKETS INSTEAD OF ABSTRACTED TERMINALS
-/obj/machinery/proc/post_signal(datum/signal/sending_signal, preserve_s_addr = FALSE)
+/obj/machinery/post_signal(datum/signal/sending_signal, preserve_s_addr = FALSE)
if(isnull(netjack) || isnull(sending_signal)) //nullcheck for sanic speed
return //You need a pipe and something to send down it, though.
if(!preserve_s_addr)
- sending_signal.data[PACKET_SOURCE_ADDRESS] = src.net_id
+ sending_signal.data[PKT_HEAD_SOURCE_ADDRESS] = src.net_id
sending_signal.transmission_method = TRANSMISSION_WIRE
sending_signal.author = WEAKREF(src) // Override the sending signal author.
src.netjack.post_signal(sending_signal)
@@ -31,16 +31,16 @@
if(isnull(signal))
return
var/sigdat = signal.data //cache for sanic speed this joke is getting old.
- if(sigdat[PACKET_DESTINATION_ADDRESS] != src.net_id)//This packet doesn't belong to us directly
- if(sigdat[PACKET_DESTINATION_ADDRESS] == NET_ADDRESS_PING)// But it could be a ping, if so, reply
+ if(sigdat[PKT_HEAD_DEST_ADDRESS] != src.net_id)//This packet doesn't belong to us directly
+ if(sigdat[PKT_HEAD_DEST_ADDRESS] == NET_ADDRESS_PING)// But it could be a ping, if so, reply
var/tmp_filter = sigdat["filter"]
if(!isnull(tmp_filter) && tmp_filter != net_class)
return RECEIVE_SIGNAL_FINISHED
//Blame kapu for how stupid this looks :3
- var/payload = list(PACKET_CMD=NET_COMMAND_PING_REPLY,PACKET_NETCLASS=src.net_class)
+ var/payload = list(PKT_ARG_CMD=NET_COMMAND_PING_REPLY,PKT_HEAD_NETCLASS=src.net_class)
if(ping_addition)
payload += ping_addition
- post_signal(create_signal(sigdat[PACKET_SOURCE_ADDRESS],payload))
+ post_signal(create_signal(sigdat[PKT_HEAD_SOURCE_ADDRESS],payload))
return RECEIVE_SIGNAL_FINISHED//regardless, return 1 so that machines don't process packets not intended for them.
return RECEIVE_SIGNAL_CONTINUE // We are the designated recipient of this packet, we need to handle it.
diff --git a/code/game/machinery/datanet/p2p_phones/_p2p_phones.dm b/code/game/machinery/datanet/p2p_phones/_p2p_phones.dm
index 8d017a440f60..14286bd19b39 100644
--- a/code/game/machinery/datanet/p2p_phones/_p2p_phones.dm
+++ b/code/game/machinery/datanet/p2p_phones/_p2p_phones.dm
@@ -231,27 +231,27 @@
if(. == RECEIVE_SIGNAL_FINISHED)//Handled by default.
return
//Ping response handled in parent.
- switch(signal.data[PACKET_CMD])
+ switch(signal.data[LEGACY_PACKET_COMMAND])
if(NET_COMMAND_PING_REPLY)//Add new phone to database
- if(signal.data[PACKET_NETCLASS] == NETCLASS_P2P_PHONE) //Another phone!
- discovered_phones[signal.data[PACKET_SOURCE_ADDRESS]]=signal.data["user_id"]
+ if(signal.data[LEGACY_PACKET_NETCLASS] == NETCLASS_P2P_PHONE) //Another phone!
+ discovered_phones[signal.data[LEGACY_PACKET_SOURCE_ADDRESS]]=signal.data["user_id"]
return RECEIVE_SIGNAL_FINISHED
if("tel_ring")//Incoming ring
if(active_caller || handset_state == HANDSET_OFFHOOK)//We're either calling, or about to call, Just tell them to fuck off.
- post_signal(create_signal(signal.data[PACKET_SOURCE_ADDRESS],list(PACKET_CMD="tel_busy"))) //Busy signal, Reject call.
+ post_signal(create_signal(signal.data[LEGACY_PACKET_SOURCE_ADDRESS],list(LEGACY_PACKET_COMMAND="tel_busy"))) //Busy signal, Reject call.
return RECEIVE_SIGNAL_FINISHED
- receive_call(list(signal.data[PACKET_SOURCE_ADDRESS],signal.data["caller_id"]))
+ receive_call(list(signal.data[LEGACY_PACKET_SOURCE_ADDRESS],signal.data["caller_id"]))
return RECEIVE_SIGNAL_FINISHED
if("tel_ready")//Remote side pickup
- if(active_caller && signal.data[PACKET_SOURCE_ADDRESS] == active_caller[CALLER_NETID])// Ensure the packet is sensible
+ if(active_caller && signal.data[LEGACY_PACKET_SOURCE_ADDRESS] == active_caller[CALLER_NETID])// Ensure the packet is sensible
call_connected()
return RECEIVE_SIGNAL_FINISHED
if("tel_busy")//Answering station busy
- if(active_caller && signal.data[PACKET_SOURCE_ADDRESS] == active_caller[CALLER_NETID])// Ensure the packet is sensible
+ if(active_caller && signal.data[LEGACY_PACKET_SOURCE_ADDRESS] == active_caller[CALLER_NETID])// Ensure the packet is sensible
fuck_off_im_busy()
return RECEIVE_SIGNAL_FINISHED
if("tel_hup")//Remote side hangup
- if(active_caller && signal.data[PACKET_SOURCE_ADDRESS] == active_caller[CALLER_NETID])// Ensure the packet is sensible
+ if(active_caller && signal.data[LEGACY_PACKET_SOURCE_ADDRESS] == active_caller[CALLER_NETID])// Ensure the packet is sensible
switch(state)
if(STATE_ANSWER)
drop_call()// Call never connected, just reset.
@@ -588,8 +588,8 @@
//Bundle up what we care about.
var/datum/signal/v_signal = new(src, null, TRANSMISSION_WIRE)
v_signal.has_magic_data = MAGIC_DATA_INVIOLABLE //We're sending a virtual speaker. This packet MUST be discarded.
- v_signal.data[PACKET_SOURCE_ADDRESS] = null //(Set by post_signal), Just setting it to null means it's always first in the list.
- v_signal.data[PACKET_DESTINATION_ADDRESS] = callstation.active_caller[CALLER_NETID]
+ v_signal.data[LEGACY_PACKET_SOURCE_ADDRESS] = null //(Set by post_signal), Just setting it to null means it's always first in the list.
+ v_signal.data[LEGACY_PACKET_DESTINATION_ADDRESS] = callstation.active_caller[CALLER_NETID]
v_signal.data["command"] = "tel_voicedata"
v_signal.data["virtualspeaker"] = v_speaker //This is a REAL REFERENCE. Packet MUST be discarded.
v_signal.data["message"] = message
diff --git a/code/game/machinery/datanet/README.md b/code/game/machinery/datanet/packetv1.md
similarity index 67%
rename from code/game/machinery/datanet/README.md
rename to code/game/machinery/datanet/packetv1.md
index e8e8dde7eb63..131076271d60 100644
--- a/code/game/machinery/datanet/README.md
+++ b/code/game/machinery/datanet/packetv1.md
@@ -1,9 +1,9 @@
# Data Networks, or: Why the OSI Model is for Pussies
-
## Important Variables
### Data
+
These are the basic entries in a signal's data list, which are required for standard network transport.
`s_addr` - Source Address, the ID of the machine that transmitted the packet.
@@ -14,8 +14,9 @@ Example:
```json
{
- "s_addr":00000001, // We are being sent by device 1
- "d_addr":00000002, // intended for device 2
- "command":"do_thing", // and we want them to "do_thing"
- "other_fields":"Are implimentation specific."
+ "PKT_HEAD_SOURCE_ADDR": 00000001, // We are being sent by device 1
+ "PKT_HEAD_DEST_ADDR": 00000002, // intended for device 2
+ "command": "do_thing", // and we want them to "do_thing"
+ "other_fields": "Are implimentation specific."
}
+```
diff --git a/code/game/machinery/datanet/packetv2.md b/code/game/machinery/datanet/packetv2.md
new file mode 100644
index 000000000000..aaaa0b946e2b
--- /dev/null
+++ b/code/game/machinery/datanet/packetv2.md
@@ -0,0 +1,17 @@
+```json
+{
+ "PKT_VERSION": 2,
+ "PKT_HEAD_SOURCE_ADDR": 00000001, // We are being sent by device 1
+ "PKT_HEAD_DEST_ADDR": 00000002, // intended for device 2.
+ "PKT_HEAD_SOURCE_PORT": 1, // The port the packet was broadcast from.
+ "PKT_HEAD_DEST_PORT": 320, // The port the packet is destined for. This is often a fixed number based on the usage.
+ "PKT_HEAD_NETCLASS": "NETCLASS_ADAPTER", // The type of device describing the source.
+ "PKT_HEAD_PROTOCOL": "PKT_PROTOCOL_PDP", // The protocol describing the exchange format.
+ "PKT_PAYLOAD": {
+ "PKT_ARG_CMD": "keepalive", // A command to give to the recieving machine, using payload as parameters.
+ // Any misc. data not specified by the format.
+ "impl. specific value": "true",
+ "impl. specific value 2": 27
+ }
+}
+```
diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm
index 6cf78c3ba8fe..7c0aef2fa87c 100644
--- a/code/game/machinery/embedded_controller/embedded_controller_base.dm
+++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm
@@ -7,7 +7,7 @@
master = null
. = ..()
-/datum/computer/file/embedded_program/proc/post_signal(datum/signal/signal, comm_line)
+/datum/computer/file/embedded_program/post_signal(datum/signal/signal, comm_line)
SHOULD_CALL_PARENT(FALSE) // This is more of a relay than anything else.
if(master)
master.post_signal(signal, comm_line)
diff --git a/code/game/machinery/telecomms/machines/message_server.dm b/code/game/machinery/telecomms/machines/message_server.dm
index d2d74f4d3ff0..74481e9a2f15 100644
--- a/code/game/machinery/telecomms/machines/message_server.dm
+++ b/code/game/machinery/telecomms/machines/message_server.dm
@@ -158,9 +158,9 @@ TYPEINFO_DEF(/obj/machinery/blackbox_recorder)
if(calibrating) // If we're calibrating, just return, the fancy part of us isn't ready yet.
return
var/list/sig_data = signal.data //cachemere sweater
- switch(signal.data[PACKET_CMD])
+ switch(signal.data[LEGACY_PACKET_COMMAND])
if(NETCMD_PDAMESSAGE)
- var/datum/data_pda_message/log_unit = new(sig_data[PACKET_DESTINATION_ADDRESS], sig_data[PACKET_SOURCE_ADDRESS], sig_data["message"])
+ var/datum/data_pda_message/log_unit = new(sig_data[LEGACY_PACKET_DESTINATION_ADDRESS], sig_data[LEGACY_PACKET_SOURCE_ADDRESS], sig_data["message"])
pda_msgs += log_unit
return RECEIVE_SIGNAL_FINISHED
else
@@ -171,7 +171,7 @@ TYPEINFO_DEF(/obj/machinery/blackbox_recorder)
if(isnull(sending_signal)) //nullcheck for sanic speed
return //You need a pipe and something to send down it, though.
if(!preserve_s_addr)
- sending_signal.data[PACKET_SOURCE_ADDRESS] = src.net_id
+ sending_signal.data[LEGACY_PACKET_SOURCE_ADDRESS] = src.net_id
sending_signal.transmission_method = TRANSMISSION_RADIO
sending_signal.author = WEAKREF(src) // Override the sending signal author.
diff --git a/code/modules/computer4/computer4.dm b/code/modules/computer4/computer4.dm
index f7289b12f7a6..cecb03fbaeb6 100644
--- a/code/modules/computer4/computer4.dm
+++ b/code/modules/computer4/computer4.dm
@@ -252,8 +252,8 @@ TYPEINFO_DEF(/obj/machinery/computer4)
if(!is_operational)
return
- for(var/datum/c4_file/terminal_program/program as anything in operating_system?.processing_programs)
- program.peripheral_input(invoker, command, packet)
+ // Operating system can be null here if the computer is mid-reboot.
+ operating_system?.peripheral_input(invoker, command, packet)
/obj/machinery/computer4/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src)
diff --git a/code/modules/computer4/data/socket.dm b/code/modules/computer4/data/socket.dm
new file mode 100644
index 000000000000..21c5164dd091
--- /dev/null
+++ b/code/modules/computer4/data/socket.dm
@@ -0,0 +1,78 @@
+/*
+ * Ported Datagram Protocol Socket
+ */
+
+#define PDP_SOCKET_QUEUE_DEPTH 50
+/// 'Real' insert position, subject to BYOND Listism.
+#define PDP_S_HEAD_REAL (head_pointer+1)
+/// 'Real' read position, subject to BYOND Listism.
+#define PDP_S_TAIL_REAL (tail_pointer+1)
+
+#if PDP_SOCKET_QUEUE_DEPTH < 3
+#error PDP_SOCKET_QUEUE_DEPTH is too short. If you trigger this, good fucking job:tm:
+#endif
+
+/datum/pdp_socket
+ /// Next Hop datum we send our packets to. Usually a network card.
+ var/datum/outgoing_datum
+ /// 'Owner' that registered for this socket. Used to assure ownership.
+ var/datum/owner
+ /// PDP Port the socket is bound to, Packets being sent out of this socket will have their source port set to this.
+ var/bound_port
+
+
+ /// Packet ring buffer
+ VAR_PRIVATE/list/packet_buffer
+ /// Insertion point, Incremented on packet receive. Will drop instead if =tail_pointer-1
+ /// Do not use directly, Must add 1 to account for list jank
+ VAR_PRIVATE/head_pointer
+ /// Read pointer, Incremented on packet read, Will not increment if there is no packet to read.
+ VAR_PRIVATE/tail_pointer
+
+/datum/pdp_socket/New(bound_port, outgoing_datum, owner)
+ if(!bound_port)
+ stack_trace("Created a PDP socket without a port number?")
+
+ src.bound_port = bound_port
+ src.outgoing_datum = outgoing_datum
+ src.owner = owner
+ packet_buffer = new /list(PDP_SOCKET_QUEUE_DEPTH)
+ head_pointer = 0
+ tail_pointer = 0
+
+/datum/pdp_socket/Destroy(force, ...)
+ outgoing_datum = null
+ owner = null
+ return ..()
+
+/// Place received packet into ringqueue. Returns TRUE if inserted, FALSE if dropped due to full queue.
+// No lohi I am not implimenting DSCP. Right now, at least. Maybe later.
+/datum/pdp_socket/proc/enqueue(datum/signal/packet)
+ if(((head_pointer + 1) % PDP_SOCKET_QUEUE_DEPTH) == tail_pointer)
+ return FALSE //Ring full, Drop the packet.
+ packet_buffer[PDP_S_HEAD_REAL] = packet
+ head_pointer = (head_pointer+1) % PDP_SOCKET_QUEUE_DEPTH
+ return TRUE
+
+/// Get next signal in queue, or null if queue is dry.
+/datum/pdp_socket/proc/pop()
+ . = packet_buffer[PDP_S_TAIL_REAL]
+ if(!.)
+ return null
+ tail_pointer = (tail_pointer+1) % PDP_SOCKET_QUEUE_DEPTH
+ //return .
+
+#undef PDP_SOCKET_QUEUE_DEPTH
+#undef PDP_S_HEAD_REAL
+#undef PDP_S_TAIL_REAL
+
+/// Send a PDP payload packet to the destionation port and address.
+/datum/pdp_socket/proc/send_data(d_address, d_port, list/payload)
+ // Packets come out of this preeetty skeletonized, Higher layers above this are expected to fill out some remaining details
+ // Such as source address.
+
+ var/list/packet_data = packetv2(null, d_address, bound_port, d_port, protocol = PKT_PROTOCOL_PDP, payload = payload)
+
+ var/datum/signal/packet = new(null, packet_data)
+
+ outgoing_datum.post_signal(packet)
diff --git a/code/modules/computer4/data/terminal/medtrak/medtrak.dm b/code/modules/computer4/data/terminal/medtrak/medtrak.dm
index fd8688a948f0..a688eab9d64b 100644
--- a/code/modules/computer4/data/terminal/medtrak/medtrak.dm
+++ b/code/modules/computer4/data/terminal/medtrak/medtrak.dm
@@ -68,7 +68,10 @@
/// Getter for the log file. This isn't kept as a ref because I don't want to manage the ref. :)
/datum/c4_file/terminal_program/medtrak/proc/get_log_file()
RETURN_TYPE(/datum/c4_file/text)
- var/datum/c4_file/folder/log_dir = get_os().get_log_folder()
+
+ // Sue me. Come up with a better way to do this that doesn't demand dead procs on the OS basetype.
+ // (Suggestion: Move medtrak and such to a thinkdos-specific subtype of terminal_program) (t_prog/dos/medtrak)
+ var/datum/c4_file/folder/log_dir = astype(get_os(), /datum/c4_file/terminal_program/operating_system/thinkdos).get_log_folder()
if(!log_dir)
return null
diff --git a/code/modules/computer4/data/terminal/medtrak/medtrak_comment_commands.dm b/code/modules/computer4/data/terminal/medtrak/medtrak_comment_commands.dm
index 74b4fd9fabb4..4bde9a1d5765 100644
--- a/code/modules/computer4/data/terminal/medtrak/medtrak_comment_commands.dm
+++ b/code/modules/computer4/data/terminal/medtrak/medtrak_comment_commands.dm
@@ -1,7 +1,7 @@
/datum/shell_command/medtrak/comment/quit
aliases = list("quit", "q", "exit")
-/datum/shell_command/medtrak/index/quit/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
+/datum/shell_command/medtrak/comment/quit/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
system.println("Quitting...")
system.unload_program(program)
diff --git a/code/modules/computer4/data/terminal/netpage/netpage.dm b/code/modules/computer4/data/terminal/netpage/netpage.dm
index 6b341467bb6e..f52751e587e1 100644
--- a/code/modules/computer4/data/terminal/netpage/netpage.dm
+++ b/code/modules/computer4/data/terminal/netpage/netpage.dm
@@ -19,8 +19,6 @@
/datum/c4_file/terminal_program/netpage/execute(datum/c4_file/terminal_program/operating_system/thinkdos/system)
. = ..()
- if(.)
- return
var/title_text = list(
@"
___ ___ __ __ ___
",
diff --git a/code/modules/computer4/data/terminal/netpage/netpage_commands.dm b/code/modules/computer4/data/terminal/netpage/netpage_commands.dm
index ee84fc9373cc..0bb21f3e7b8f 100644
--- a/code/modules/computer4/data/terminal/netpage/netpage_commands.dm
+++ b/code/modules/computer4/data/terminal/netpage/netpage_commands.dm
@@ -1,5 +1,16 @@
+/datum/shell_command/netpage/help
+ aliases = list("help")
+ help_text = "Lists all available commands. Use help \[command\] to view information about a specific command."
+
+/datum/shell_command/netpage/help/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
+ var/list/output = list()
+
+ if(generate_help_list(output, arguments, astype(program, /datum/c4_file/terminal_program/netpage).commands, system) != SHELL_CMD_HELP_ERROR)
+ system.println(jointext(output, "
"))
+
/datum/shell_command/netpage/quit
aliases = list("quit", "q", "exit")
+ help_text = "Exits the program."
/datum/shell_command/netpage/quit/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
system.println("Quitting...")
diff --git a/code/modules/computer4/data/terminal/notepad/notepad.dm b/code/modules/computer4/data/terminal/notepad/notepad.dm
index 916fe496e289..bb672b1ea206 100644
--- a/code/modules/computer4/data/terminal/notepad/notepad.dm
+++ b/code/modules/computer4/data/terminal/notepad/notepad.dm
@@ -16,8 +16,6 @@
/datum/c4_file/terminal_program/notepad/execute(datum/c4_file/terminal_program/operating_system/thinkdos/system)
. = ..()
- if(.)
- return
// Hope you saved your work motherfucker.
working_line = 0
diff --git a/code/modules/computer4/data/terminal/notepad/notepad_commands.dm b/code/modules/computer4/data/terminal/notepad/notepad_commands.dm
index e0520a1f45c4..4b79fffb8ece 100644
--- a/code/modules/computer4/data/terminal/notepad/notepad_commands.dm
+++ b/code/modules/computer4/data/terminal/notepad/notepad_commands.dm
@@ -3,50 +3,19 @@
help_text = "Lists all available commands. Use help \[command\] to view information about a specific command."
/datum/shell_command/notepad/edit_cmd/help/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
- // Stupid fucking byond compiler bug
-#if !defined(SPACEMAN_DMM) && !defined(OPENDREAM)
-#pragma push
-#pragma ignore unused_var
-#endif
- var/datum/c4_file/terminal_program/notepad/notepad = program
-#if !defined(SPACEMAN_DMM) && !defined(OPENDREAM)
-#pragma pop
-#endif
-
var/list/output = list()
-
- if(length(arguments))
- var/found = FALSE
- var/searching_for = jointext(arguments, "")
- for(var/datum/shell_command/command_iter as anything in notepad.edit_commands)
- if(searching_for in command_iter.aliases)
- found = TRUE
- output += "Displaying information for '[command_iter.aliases[1]]':"
- output += command_iter.help_text
- break
-
- if(!found)
- system.println("This command is not supported by the help utility. To see a list of commands, type !help.")
- return
-
- else
- for(var/datum/shell_command/command_iter as anything in notepad.edit_commands)
- if(length(command_iter.aliases) == 1)
- output += command_iter.aliases[1]
- continue
-
- output += "[command_iter.aliases[1]] ([jointext(command_iter.aliases.Copy(2), ", ")])"
-
- sortTim(output, GLOBAL_PROC_REF(cmp_text_asc))
- output.Insert(1,
- "Typing text without a '!' prefix will write to the current line.",
- "You can change lines by typing '!\[number\]'. Zero will change to highest line number.
",
- "Use help \[command\] to see specific information about a command.",
- "List of available commands:"
- )
-
-
- system.println(jointext(output, "
"))
+ switch(generate_help_list(output, arguments, astype(program, /datum/c4_file/terminal_program/notepad).edit_commands, system))
+ if(SHELL_CMD_HELP_GENERIC)
+ output.Insert(1,
+ "Typing text without a '!' prefix will write to the current line.",
+ "You can change lines by typing '!\[number\]'. Zero will change to highest line number.
",
+ "Use help \[command\] to see specific information about a command.",
+ "List of available commands:"
+ )
+ if(SHELL_CMD_HELP_ERROR)
+ noop()
+ else
+ system.println(jointext(output, "
"))
/datum/shell_command/notepad/edit_cmd/quit
aliases = list("quit", "q", "exit")
diff --git a/code/modules/computer4/data/terminal/operating_system.dm b/code/modules/computer4/data/terminal/operating_system.dm
deleted file mode 100644
index 7437791d825a..000000000000
--- a/code/modules/computer4/data/terminal/operating_system.dm
+++ /dev/null
@@ -1,237 +0,0 @@
-/datum/c4_file/terminal_program/operating_system
- name = "operating system"
- extension = "TSYS"
- size = 0
-
- is_executable = FALSE
-
- /// Gotta be logged in to access anything.
- var/logged_in = FALSE
-
- /// Current directory being operated on.
- var/datum/c4_file/folder/current_directory
-
- /// The current focused program.
- var/tmp/datum/c4_file/terminal_program/active_program
- /// All programs currently running on the machine.
- var/tmp/list/datum/c4_file/terminal_program/processing_programs = list()
-
- /// Halt And Catch Fire (Prevents STDIN, closest we can get to a HALT state.)
- var/deadlocked = FALSE
-
-/datum/c4_file/terminal_program/operating_system/Destroy()
- if(length(processing_programs))
- clean_up()
- return ..()
-
-/// Should run this before executing any commands.
-/datum/c4_file/terminal_program/operating_system/proc/is_operational()
- return (!!get_computer()?.is_operational) && (!deadlocked)
-
-/// Change active directory.
-/datum/c4_file/terminal_program/operating_system/proc/change_dir(datum/c4_file/folder/directory)
- current_directory = directory
- return TRUE
-
-/// Move a file to another location.
-/datum/c4_file/terminal_program/operating_system/proc/move_file(datum/c4_file/file, datum/c4_file/folder/destination, error_pointer, overwrite = FALSE, new_name = "")
- if(file.containing_folder == destination)
- *error_pointer = "Cannot move folder into itself."
- return FALSE
-
- if(!destination.can_add_file(file))
- *error_pointer = "Unable to move file to target."
- return FALSE
-
- var/datum/c4_file/file_at_dest = destination.get_file(new_name || file.name, TRUE)
- if(file_at_dest)
- if(!overwrite)
- *error_pointer = "Target in use."
- return FALSE
-
- if(!destination.can_remove_file(file_at_dest))
- *error_pointer = "Unable to delete target."
- return FALSE
-
- if(!file.containing_folder.can_remove_file(file))
- *error_pointer = "Unable to delete source."
- return FALSE
-
- if(file_at_dest)
- destination.try_delete_file(file_at_dest)
-
- file.containing_folder.try_remove_file(file)
- destination.try_add_file(file)
-
- file.set_name(new_name)
- return TRUE
-
-/// Find a file by it's name in the given directory. Defaults to the current directory.
-/datum/c4_file/terminal_program/operating_system/proc/get_file(file_name, datum/c4_file/folder/working_directory, include_folders = FALSE)
- if(!file_name)
- return
-
- if(!working_directory)
- working_directory = current_directory
-
- return working_directory.get_file(file_name, include_folders)
-
-/// Returns a file or folder with the given name inside of the given filepath relative to the working directory.
-/datum/c4_file/terminal_program/operating_system/proc/resolve_filepath(file_path, datum/c4_file/folder/working_directory)
- if(!file_path)
- return
-
- if(!working_directory)
- working_directory = current_directory
-
- var/list/split_path = splittext(file_path, "/")
-
- if(length(split_path) == 1)
- if(split_path[1] == ".")
- return working_directory
- return get_file(split_path[1], working_directory, include_folders = TRUE)
-
- var/datum/file_path/path_info = text_to_filepath(file_path)
-
- var/datum/c4_file/folder/found_folder = parse_directory(path_info.directory, working_directory)
- if(!found_folder)
- return null
-
- if(!path_info.file_name)
- return found_folder
-
- return get_file(path_info.file_name, found_folder, include_folders = TRUE)
-
-/// Write to the terminal.
-/datum/c4_file/terminal_program/operating_system/proc/println(text, update_ui = TRUE)
- if(isnull(text) || !is_operational())
- return FALSE
-
-
- var/obj/machinery/computer4/computer = get_computer()
- computer.text_buffer += "[text]
"
- if(update_ui)
- SStgui.update_uis(computer)
- return TRUE
-
-/// Clear the screen completely.
-/datum/c4_file/terminal_program/operating_system/proc/clear_screen(fully = FALSE)
- if(!is_operational())
- return FALSE
-
- get_computer().text_buffer = ""
- if(!fully)
- println("Screen cleared.")
- return TRUE
-
-/datum/c4_file/terminal_program/operating_system/proc/get_log_folder()
- return
-
-/// Wrapper around handling text input to make sure we can actually handle it.
-/datum/c4_file/terminal_program/operating_system/proc/try_std_in(text)
- if(!text || !is_operational())
- return FALSE
-
- return active_program?.std_in(text)
-
-/// Unload everything including myself
-/datum/c4_file/terminal_program/operating_system/proc/clean_up()
- for(var/datum/c4_file/terminal_program/program as anything in processing_programs - src)
- unload_program(program)
-
- if(src in processing_programs)
- unload_program(src)
-
- get_computer()?.text_buffer = ""
- get_computer()?.operating_system = null
-
-/// Run a program.
-/datum/c4_file/terminal_program/operating_system/proc/execute_program(datum/c4_file/terminal_program/program)
- if(!program)
- return FALSE
-
- if(!program.can_execute(src))
- return FALSE
-
- if(!(program in processing_programs))
- add_processing_program(program)
-
- set_active_program(program)
- program.execute(src)
- return TRUE
-
-/// Close a program.
-/datum/c4_file/terminal_program/operating_system/proc/unload_program(datum/c4_file/terminal_program/program)
- if(!program)
- return FALSE
-
- // Un-deadlock ourselves.
- deadlocked = FALSE
-
- if(!(program in processing_programs))
- CRASH("Tried tried to remove a program we aren't even running.")
-
- remove_processing_program(program)
-
- if(active_program == program)
- if(active_program == src)
- set_active_program(null)
- clean_up()
- else
- set_active_program(src)
-
- return TRUE
-
-/// Move a program to background
-/datum/c4_file/terminal_program/operating_system/proc/try_background_program(datum/c4_file/terminal_program/program)
- if(length(processing_programs) > 6) // Sane limit IMO
- return FALSE
-
- if(active_program == program)
- set_active_program(src)
-
- return TRUE
-
-/// Setter for the processing programs list. Use execute_program() instead!
-/datum/c4_file/terminal_program/operating_system/proc/add_processing_program(datum/c4_file/terminal_program/program)
- PRIVATE_PROC(TRUE)
-
- processing_programs += program
- RegisterSignal(program, list(COMSIG_PARENT_QDELETING, COMSIG_COMPUTER4_FILE_ADDED), PROC_REF(processing_program_moved))
-
-
-/// Setter for the processing programs list. Use unload_program() instead!
-/datum/c4_file/terminal_program/operating_system/proc/remove_processing_program(datum/c4_file/terminal_program/program)
- processing_programs -= program
- program.on_close(src)
- UnregisterSignal(program, list(COMSIG_PARENT_QDELETING, COMSIG_COMPUTER4_FILE_ADDED))
-
-/// Setter for active program. Use execute_program() or unload_program() instead!
-/datum/c4_file/terminal_program/operating_system/proc/set_active_program(datum/c4_file/terminal_program/program)
- PRIVATE_PROC(TRUE)
-
- active_program = program
-
-/// Handles any running programs being moved in the filesystem.
-/datum/c4_file/terminal_program/operating_system/proc/processing_program_moved(datum/source)
- SIGNAL_HANDLER
-
- if(source == src)
- var/obj/machinery/computer4/computer = get_computer()
- if(QDELING(src))
- clean_up()
- return
-
- // Check if it's still in the root of either disk, this is fine :)
- if(src in computer.internal_disk?.root.contents)
- return
-
- if(src in computer.inserted_disk?.root.contents)
- return
-
- // OS is not in a root folder, KILL!!!
- clean_up()
- return
-
-
- unload_program(active_program)
diff --git a/code/modules/computer4/data/terminal/operating_system/_operating_system.dm b/code/modules/computer4/data/terminal/operating_system/_operating_system.dm
new file mode 100644
index 000000000000..c32b890deda5
--- /dev/null
+++ b/code/modules/computer4/data/terminal/operating_system/_operating_system.dm
@@ -0,0 +1,94 @@
+/datum/c4_file/terminal_program/operating_system
+ name = "operating system"
+ extension = "TSYS"
+ size = 0
+
+ is_executable = FALSE
+
+ /// Gotta be logged in to access anything.
+ var/logged_in = FALSE
+
+ /// Current directory being operated on.
+ var/datum/c4_file/folder/current_directory
+
+ /// The current focused program.
+ var/tmp/datum/c4_file/terminal_program/active_program
+ /// All programs currently running on the machine.
+ var/tmp/list/datum/c4_file/terminal_program/processing_programs = list()
+
+ /// Halt And Catch Fire (Prevents STDIN, closest we can get to a HALT state.)
+ var/deadlocked = FALSE
+
+ /// Associative list of "port_num" : socket_instance
+ var/list/datum/pdp_socket/pdp_port_map = list()
+
+/datum/c4_file/terminal_program/operating_system/Destroy()
+ if(length(processing_programs))
+ clean_up()
+ return ..()
+
+/// Should run this before executing any commands.
+/datum/c4_file/terminal_program/operating_system/proc/is_operational()
+ return (!!get_computer()?.is_operational) && (!deadlocked)
+
+/// Write to the terminal.
+/datum/c4_file/terminal_program/operating_system/proc/println(text, update_ui = TRUE)
+ if(isnull(text) || !is_operational())
+ return FALSE
+
+
+ var/obj/machinery/computer4/computer = get_computer()
+ computer.text_buffer += "[text]
"
+ if(update_ui)
+ SStgui.update_uis(computer)
+ return TRUE
+
+/// Clear the screen completely.
+/datum/c4_file/terminal_program/operating_system/proc/clear_screen(fully = FALSE)
+ if(!is_operational())
+ return FALSE
+
+ get_computer().text_buffer = ""
+ if(!fully)
+ println("Screen cleared.")
+ return TRUE
+
+/// Wrapper around handling text input to make sure we can actually handle it.
+/datum/c4_file/terminal_program/operating_system/proc/try_std_in(text)
+ if(!text || !is_operational())
+ return FALSE
+
+ return active_program?.std_in(text)
+
+/// Unload everything including myself
+/datum/c4_file/terminal_program/operating_system/proc/clean_up()
+ for(var/datum/c4_file/terminal_program/program as anything in processing_programs - src)
+ unload_program(program)
+
+ if(src in processing_programs)
+ unload_program(src)
+
+ get_computer()?.text_buffer = ""
+ get_computer()?.operating_system = null
+
+ //Programs should clean these up, but just in case.
+ QDEL_LIST_ASSOC_VAL(pdp_port_map)
+ pdp_port_map.Cut()
+
+/datum/c4_file/terminal_program/operating_system/execute(datum/c4_file/terminal_program/operating_system/system)
+ if(system != src)
+ //If we aren't executing ourselves, something is nasty and wrong.
+ CRASH("System [system.type] tried to execute OS that isn't itself??")
+
+ prepare_networking()
+
+/datum/c4_file/terminal_program/operating_system/peripheral_input(obj/item/peripheral/invoker, command, datum/signal/packet)
+
+ if(command == PERIPHERAL_CMD_RECEIVE_PDP_PACKET)
+ pdp_incoming(packet)
+ return
+
+ for(var/datum/c4_file/terminal_program/program as anything in processing_programs)
+ if(program == src)
+ continue
+ program.peripheral_input(invoker, command, packet)
diff --git a/code/modules/computer4/data/terminal/operating_system/filesystem.dm b/code/modules/computer4/data/terminal/operating_system/filesystem.dm
new file mode 100644
index 000000000000..aafbe274ccff
--- /dev/null
+++ b/code/modules/computer4/data/terminal/operating_system/filesystem.dm
@@ -0,0 +1,73 @@
+/// Change active directory.
+/datum/c4_file/terminal_program/operating_system/proc/change_dir(datum/c4_file/folder/directory)
+ current_directory = directory
+ return TRUE
+
+/// Move a file to another location.
+/datum/c4_file/terminal_program/operating_system/proc/move_file(datum/c4_file/file, datum/c4_file/folder/destination, error_pointer, overwrite = FALSE, new_name = "")
+ if(file.containing_folder == destination)
+ *error_pointer = "Cannot move folder into itself."
+ return FALSE
+
+ if(!destination.can_add_file(file))
+ *error_pointer = "Unable to move file to target."
+ return FALSE
+
+ var/datum/c4_file/file_at_dest = destination.get_file(new_name || file.name, TRUE)
+ if(file_at_dest)
+ if(!overwrite)
+ *error_pointer = "Target in use."
+ return FALSE
+
+ if(!destination.can_remove_file(file_at_dest))
+ *error_pointer = "Unable to delete target."
+ return FALSE
+
+ if(!file.containing_folder.can_remove_file(file))
+ *error_pointer = "Unable to delete source."
+ return FALSE
+
+ if(file_at_dest)
+ destination.try_delete_file(file_at_dest)
+
+ file.containing_folder.try_remove_file(file)
+ destination.try_add_file(file)
+
+ file.set_name(new_name)
+ return TRUE
+
+/// Find a file by it's name in the given directory. Defaults to the current directory.
+/datum/c4_file/terminal_program/operating_system/proc/get_file(file_name, datum/c4_file/folder/working_directory, include_folders = FALSE)
+ if(!file_name)
+ return
+
+ if(!working_directory)
+ working_directory = current_directory
+
+ return working_directory.get_file(file_name, include_folders)
+
+/// Returns a file or folder with the given name inside of the given filepath relative to the working directory.
+/datum/c4_file/terminal_program/operating_system/proc/resolve_filepath(file_path, datum/c4_file/folder/working_directory)
+ if(!file_path)
+ return
+
+ if(!working_directory)
+ working_directory = current_directory
+
+ var/list/split_path = splittext(file_path, "/")
+
+ if(length(split_path) == 1)
+ if(split_path[1] == ".")
+ return working_directory
+ return get_file(split_path[1], working_directory, include_folders = TRUE)
+
+ var/datum/file_path/path_info = text_to_filepath(file_path)
+
+ var/datum/c4_file/folder/found_folder = parse_directory(path_info.directory, working_directory)
+ if(!found_folder)
+ return null
+
+ if(!path_info.file_name)
+ return found_folder
+
+ return get_file(path_info.file_name, found_folder, include_folders = TRUE)
diff --git a/code/modules/computer4/data/terminal/operating_system/networking.dm b/code/modules/computer4/data/terminal/operating_system/networking.dm
new file mode 100644
index 000000000000..d42ca7ffb1f3
--- /dev/null
+++ b/code/modules/computer4/data/terminal/operating_system/networking.dm
@@ -0,0 +1,70 @@
+/datum/c4_file/terminal_program/operating_system/proc/prepare_networking()
+
+/// Request a socket from the OS.
+/// A `port_number` of `PDP_BIND_EPHEMERAL_PORT` will result in being allocated an ephemeral port.
+/datum/c4_file/terminal_program/operating_system/proc/bind_port(port_number, reliable = FALSE, datum/c4_file/binder)
+ RETURN_TYPE(/datum/pdp_socket)
+ if(!binder)
+ CRASH("Tried to bind a port: [isnull(port_number) ? "!NULL!" : port_number] without a binding program.")
+ if(isnull(port_number))
+ CRASH("Program [binder] tried to bind a null port.")
+ if(port_number > PDP_MAX_PORT)
+ CRASH("Program [binder] tried to bind port above max, [port_number] > [PDP_MAX_PORT]")
+
+ // Assign ephemeral port.
+ if(port_number == PDP_BIND_EPHEMERAL_PORT)
+ do
+ port_number = rand(PDP_EPHEMERAL_START, PDP_MAX_PORT)
+ while(pdp_port_map["[port_number]"])
+ else
+ if(pdp_port_map["[port_number]"])
+ return FALSE //Port already bound.
+
+ var/datum/pdp_socket/socket = new(port_number, src, binder)
+
+ pdp_port_map["[port_number]"] = socket
+
+ return socket
+
+/// Requests the OS to free a socket.
+/// A `port_number` of `PDP_FREE_ALL_PORTS` will result in all ports bound by the binder being freed. It is also the default behaviour.
+/datum/c4_file/terminal_program/operating_system/proc/free_port(port_number = PDP_FREE_ALL_PORTS, datum/c4_file/binder)
+ if(isnull(port_number))
+ CRASH("Program [binder] tried to free a null or 0 port.")
+ if(port_number > PDP_MAX_PORT)
+ CRASH("Program [binder] tried to free port above max, [port_number] > [PDP_MAX_PORT]")
+ if(port_number != PDP_FREE_ALL_PORTS)
+ // Free by specific port number
+ if(pdp_port_map["[port_number]"].owner == binder) //If the binder matches
+ pdp_port_map["[port_number]"] = null
+ return TRUE
+ else
+ //This should throw an OS error but we have no concept of stderr.
+ return FALSE
+ //else: free all ports therein bound.
+ var/freed_at_least_one = FALSE
+ for(var/port_num, port_socket in pdp_port_map)
+ if(astype(port_socket, /datum/pdp_socket).owner == binder)
+ pdp_port_map[port_num] = null
+ freed_at_least_one = TRUE
+ return freed_at_least_one
+
+
+
+/// Finish up outgoing program signals.
+/// Eventually: Routing table?
+/datum/c4_file/terminal_program/operating_system/post_signal(datum/signal/signal)
+ if(!signal)
+ CRASH("post signal wi no signal")
+
+ var/obj/item/peripheral/network_card/wireless/wcard = get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)
+ if(!wcard)
+ return //cry
+
+ wcard.post_signal(signal)
+
+/datum/c4_file/terminal_program/operating_system/proc/pdp_incoming(datum/signal/packet)
+ var/list/fields = packet.data
+ var/datum/pdp_socket/socket = pdp_port_map["[fields[PKT_HEAD_DEST_PORT]]"]
+ socket?.enqueue(packet)
+
diff --git a/code/modules/computer4/data/terminal/operating_system/program_management.dm b/code/modules/computer4/data/terminal/operating_system/program_management.dm
new file mode 100644
index 000000000000..0e640ba1623d
--- /dev/null
+++ b/code/modules/computer4/data/terminal/operating_system/program_management.dm
@@ -0,0 +1,90 @@
+/// Run a program.
+/datum/c4_file/terminal_program/operating_system/proc/execute_program(datum/c4_file/terminal_program/program)
+ if(!program)
+ return FALSE
+
+ if(!program.can_execute(src))
+ return FALSE
+
+ if(!(program in processing_programs))
+ add_processing_program(program)
+
+ set_active_program(program)
+ program.execute(src)
+ return TRUE
+
+/// Close a program.
+/datum/c4_file/terminal_program/operating_system/proc/unload_program(datum/c4_file/terminal_program/program)
+ if(!program)
+ return FALSE
+
+ // Un-deadlock ourselves.
+ deadlocked = FALSE
+
+ if(!(program in processing_programs))
+ CRASH("Tried tried to remove a program we aren't even running.")
+
+ remove_processing_program(program)
+
+ if(active_program == program)
+ if(active_program == src)
+ set_active_program(null)
+ clean_up()
+ else
+ set_active_program(src)
+
+ return TRUE
+
+/// Move a program to background
+/datum/c4_file/terminal_program/operating_system/proc/try_background_program(datum/c4_file/terminal_program/program)
+ if(length(processing_programs) > 6) // Sane limit IMO
+ return FALSE
+
+ if(active_program == program)
+ set_active_program(src)
+
+ return TRUE
+
+/// Setter for the processing programs list. Use execute_program() instead!
+/datum/c4_file/terminal_program/operating_system/proc/add_processing_program(datum/c4_file/terminal_program/program)
+ PRIVATE_PROC(TRUE)
+
+ processing_programs += program
+ RegisterSignal(program, list(COMSIG_PARENT_QDELETING, COMSIG_COMPUTER4_FILE_ADDED), PROC_REF(processing_program_moved))
+
+
+/// Setter for the processing programs list. Use unload_program() instead!
+/datum/c4_file/terminal_program/operating_system/proc/remove_processing_program(datum/c4_file/terminal_program/program)
+ processing_programs -= program
+ program.on_close(src)
+ UnregisterSignal(program, list(COMSIG_PARENT_QDELETING, COMSIG_COMPUTER4_FILE_ADDED))
+
+/// Setter for active program. Use execute_program() or unload_program() instead!
+/datum/c4_file/terminal_program/operating_system/proc/set_active_program(datum/c4_file/terminal_program/program)
+ PRIVATE_PROC(TRUE)
+
+ active_program = program
+
+/// Handles any running programs being moved in the filesystem.
+/datum/c4_file/terminal_program/operating_system/proc/processing_program_moved(datum/source)
+ SIGNAL_HANDLER
+
+ if(source == src)
+ var/obj/machinery/computer4/computer = get_computer()
+ if(QDELING(src))
+ clean_up()
+ return
+
+ // Check if it's still in the root of either disk, this is fine :)
+ if(src in computer.internal_disk?.root.contents)
+ return
+
+ if(src in computer.inserted_disk?.root.contents)
+ return
+
+ // OS is not in a root folder, KILL!!!
+ clean_up()
+ return
+
+
+ unload_program(active_program)
diff --git a/code/modules/computer4/data/terminal/rtos/_rtos.dm b/code/modules/computer4/data/terminal/operating_system/rtos/_rtos.dm
similarity index 98%
rename from code/modules/computer4/data/terminal/rtos/_rtos.dm
rename to code/modules/computer4/data/terminal/operating_system/rtos/_rtos.dm
index 0f16f1984f3e..8513c98c4075 100644
--- a/code/modules/computer4/data/terminal/rtos/_rtos.dm
+++ b/code/modules/computer4/data/terminal/operating_system/rtos/_rtos.dm
@@ -103,7 +103,7 @@
/** RTOS.h - Post Signal
* Follows standard post_signal calling conventions.
*/
-/datum/c4_file/terminal_program/operating_system/rtos/proc/post_signal(datum/signal/sending_signal, filter)
+/datum/c4_file/terminal_program/operating_system/rtos/post_signal(datum/signal/sending_signal, filter)
if(!is_operational())
return
diff --git a/code/modules/computer4/data/terminal/rtos/access_door.dm b/code/modules/computer4/data/terminal/operating_system/rtos/access_door.dm
similarity index 97%
rename from code/modules/computer4/data/terminal/rtos/access_door.dm
rename to code/modules/computer4/data/terminal/operating_system/rtos/access_door.dm
index 2423be6ba5a1..c8980e5dc002 100644
--- a/code/modules/computer4/data/terminal/rtos/access_door.dm
+++ b/code/modules/computer4/data/terminal/operating_system/rtos/access_door.dm
@@ -226,7 +226,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "secure_open"
+ LEGACY_PACKET_COMMAND = "secure_open"
)
)
expected_airlock_state = "open"
@@ -235,7 +235,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "secure_close"
+ LEGACY_PACKET_COMMAND = "secure_close"
)
)
expected_airlock_state = "closed"
@@ -244,7 +244,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "status"
+ LEGACY_PACKET_COMMAND = "status"
)
)
if(AC_COMMAND_BOLT)
@@ -252,7 +252,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "lock"
+ LEGACY_PACKET_COMMAND = "lock"
)
)
expected_bolt_state = "locked"
@@ -261,7 +261,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "unlock"
+ LEGACY_PACKET_COMMAND = "unlock"
)
)
expected_bolt_state = "unlocked"
@@ -348,7 +348,7 @@
src,
list(
"tag" = tag_slave,
- PACKET_CMD = NETCMD_UPDATE_DATA,
+ LEGACY_PACKET_COMMAND = NETCMD_UPDATE_DATA,
PACKET_ARG_TEXTBUFFER = list2params(print_history),
PACKET_ARG_DISPLAY = display_icon,
PACKET_ARG_LEDS = display_indicators
@@ -362,7 +362,7 @@
return //what
if(tag_slave && (data["tag"] == tag_slave))
- switch(data[PACKET_CMD])
+ switch(data[LEGACY_PACKET_COMMAND])
if("key")
std_in(copytext(data["key"],1,2)) //Only one char, sorry.
if(NETCMD_ECSLAVE_ACCESS)
diff --git a/code/modules/computer4/data/terminal/rtos/pincode_door.dm b/code/modules/computer4/data/terminal/operating_system/rtos/pincode_door.dm
similarity index 98%
rename from code/modules/computer4/data/terminal/rtos/pincode_door.dm
rename to code/modules/computer4/data/terminal/operating_system/rtos/pincode_door.dm
index 9caadc16310f..4efed399d4ac 100644
--- a/code/modules/computer4/data/terminal/rtos/pincode_door.dm
+++ b/code/modules/computer4/data/terminal/operating_system/rtos/pincode_door.dm
@@ -385,7 +385,7 @@
src,
list(
"tag" = tag_slave,
- PACKET_CMD = NETCMD_UPDATE_DATA,
+ LEGACY_PACKET_COMMAND = NETCMD_UPDATE_DATA,
PACKET_ARG_TEXTBUFFER = list2params(print_history),
PACKET_ARG_DISPLAY = display_icon,
PACKET_ARG_LEDS = display_indicators
@@ -398,7 +398,7 @@
if(!data["tag"])
return //what
if(tag_slave && (data["tag"] == tag_slave))
- switch(data[PACKET_CMD])
+ switch(data[LEGACY_PACKET_COMMAND])
if("key")
std_in(copytext(data["key"],1,2)) //Only one char, sorry.
if(NETCMD_UPDATE_REQUEST)
@@ -434,7 +434,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "secure_open"
+ LEGACY_PACKET_COMMAND = "secure_open"
)
)
expected_airlock_state = "open"
@@ -443,7 +443,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "secure_close"
+ LEGACY_PACKET_COMMAND = "secure_close"
)
)
expected_airlock_state = "closed"
@@ -452,7 +452,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "status"
+ LEGACY_PACKET_COMMAND = "status"
)
)
if(AC_COMMAND_BOLT)
@@ -460,7 +460,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "lock"
+ LEGACY_PACKET_COMMAND = "lock"
)
)
expected_bolt_state = "locked"
@@ -469,7 +469,7 @@
src,
list(
"tag" = tag_target,
- PACKET_CMD = "unlock"
+ LEGACY_PACKET_COMMAND = "unlock"
)
)
expected_bolt_state = "unlocked"
diff --git a/code/modules/computer4/data/terminal/rtos/simple_door_control.dm b/code/modules/computer4/data/terminal/operating_system/rtos/simple_door_control.dm
similarity index 93%
rename from code/modules/computer4/data/terminal/rtos/simple_door_control.dm
rename to code/modules/computer4/data/terminal/operating_system/rtos/simple_door_control.dm
index 016508082ebb..766a2edcbbb4 100644
--- a/code/modules/computer4/data/terminal/rtos/simple_door_control.dm
+++ b/code/modules/computer4/data/terminal/operating_system/rtos/simple_door_control.dm
@@ -88,7 +88,7 @@
src,
list(
"tag" = id_tag,
- PACKET_CMD = "update" //Doesn't matter.
+ LEGACY_PACKET_COMMAND = "update" //Doesn't matter.
)
)
post_signal(signal, RADIO_AIRLOCK)
@@ -102,7 +102,7 @@
src,
list(
"tag" = id_tag,
- PACKET_CMD = (doorbolt_state == "locked") ? "unlock" : "lock"
+ LEGACY_PACKET_COMMAND = (doorbolt_state == "locked") ? "unlock" : "lock"
)
)
if("#") //Toggle Open
@@ -110,7 +110,7 @@
src,
list(
"tag" = id_tag,
- PACKET_CMD = (dooropen_state == "closed") ? "open" : "close"
+ LEGACY_PACKET_COMMAND = (dooropen_state == "closed") ? "open" : "close"
)
)
else //Just probe the airlock to update state.
diff --git a/code/modules/computer4/data/terminal/rtos/slave.dm b/code/modules/computer4/data/terminal/operating_system/rtos/slave.dm
similarity index 92%
rename from code/modules/computer4/data/terminal/rtos/slave.dm
rename to code/modules/computer4/data/terminal/operating_system/rtos/slave.dm
index 6194f2fde70f..91a77529414c 100644
--- a/code/modules/computer4/data/terminal/rtos/slave.dm
+++ b/code/modules/computer4/data/terminal/operating_system/rtos/slave.dm
@@ -25,7 +25,7 @@
src,
list(
"tag" = id_tag,
- PACKET_CMD = NETCMD_UPDATE_REQUEST,
+ LEGACY_PACKET_COMMAND = NETCMD_UPDATE_REQUEST,
)
)
post_signal(signal)
@@ -40,7 +40,7 @@
/datum/c4_file/terminal_program/operating_system/rtos/slave/proc/handle_packet(datum/signal/packet)
var/list/fields = packet.data
- if(fields[PACKET_CMD] != NETCMD_UPDATE_DATA)
+ if(fields[LEGACY_PACKET_COMMAND] != NETCMD_UPDATE_DATA)
return
var/redraw_screen = FALSE
@@ -76,7 +76,7 @@
src,
list(
"tag" = id_tag,
- PACKET_CMD = NETCMD_ECSLAVE_ACCESS,
+ LEGACY_PACKET_COMMAND = NETCMD_ECSLAVE_ACCESS,
"packet" = list2params(packet.data)
)
)
@@ -87,7 +87,7 @@
src,
list(
"tag" = id_tag,
- PACKET_CMD = "key",
+ LEGACY_PACKET_COMMAND = "key",
"key" = text
)
)
diff --git a/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm b/code/modules/computer4/data/terminal/operating_system/thinkdos/thinkdos.dm
similarity index 96%
rename from code/modules/computer4/data/terminal/thinkdos/thinkdos.dm
rename to code/modules/computer4/data/terminal/operating_system/thinkdos/thinkdos.dm
index 5cc1f8c6bc57..0436a0ffba21 100644
--- a/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm
+++ b/code/modules/computer4/data/terminal/operating_system/thinkdos/thinkdos.dm
@@ -55,6 +55,10 @@
else
println("Type 'help' to get started.")
+/datum/c4_file/terminal_program/operating_system/thinkdos/clean_up()
+ LAZYNULL(queued_commands)
+ return ..()
+
/datum/c4_file/terminal_program/operating_system/thinkdos/parse_std_in(text)
RETURN_TYPE(/list/datum/shell_stdin)
@@ -97,6 +101,12 @@
if(.)
handle_command_queue()
+/datum/c4_file/terminal_program/operating_system/thinkdos/tick(delta_time)
+ for(var/datum/c4_file/terminal_program/program as anything in processing_programs)
+ if(program == src)
+ continue
+
+ program.tick(delta_time)
/datum/c4_file/terminal_program/operating_system/thinkdos/proc/handle_command_queue()
while(LAZYLEN(queued_commands)) //hmm...
@@ -170,6 +180,38 @@
set_current_user(null)
return TRUE
+/// Returns the logging folder, attempting to create it if it doesn't already exist.
+/datum/c4_file/terminal_program/operating_system/thinkdos/proc/get_log_folder()
+ var/datum/c4_file/folder/log_dir = parse_directory("logs", drive.root)
+ if(!log_dir)
+ log_dir = new /datum/c4_file/folder
+ log_dir.set_name("logs")
+ if(!drive.root.try_add_file(log_dir))
+ qdel(log_dir)
+ return null
+
+ return log_dir
+
+/// Create the log file, or append a startup log.
+/datum/c4_file/terminal_program/operating_system/thinkdos/proc/initialize_logs()
+ if(command_log)
+ return TRUE
+
+ var/datum/c4_file/folder/log_dir = get_log_folder()
+ var/datum/c4_file/text/log_file = log_dir.get_file("syslog")
+ if(!log_file)
+ log_file = new /datum/c4_file/text()
+ log_file.set_name("syslog")
+ if(!log_dir.try_add_file(log_file))
+ qdel(log_file)
+ return FALSE
+
+ command_log = log_file
+ RegisterSignal(command_log, list(COMSIG_COMPUTER4_FILE_RENAMED, COMSIG_COMPUTER4_FILE_ADDED, COMSIG_PARENT_QDELETING), PROC_REF(log_file_gone))
+
+ log_file.data += "
STARTUP: [stationtime2text()], [stationdate2text()]"
+ return TRUE
+
/datum/c4_file/terminal_program/operating_system/thinkdos/proc/initialize_accounts()
var/datum/c4_file/folder/account_dir = parse_directory("users")
if(!istype(account_dir))
@@ -204,38 +246,6 @@
//set_current_user(user_data)
return TRUE
-/// Returns the logging folder, attempting to create it if it doesn't already exist.
-/datum/c4_file/terminal_program/operating_system/thinkdos/get_log_folder()
- var/datum/c4_file/folder/log_dir = parse_directory("logs", drive.root)
- if(!log_dir)
- log_dir = new /datum/c4_file/folder
- log_dir.set_name("logs")
- if(!drive.root.try_add_file(log_dir))
- qdel(log_dir)
- return null
-
- return log_dir
-
-/// Create the log file, or append a startup log.
-/datum/c4_file/terminal_program/operating_system/thinkdos/proc/initialize_logs()
- if(command_log)
- return TRUE
-
- var/datum/c4_file/folder/log_dir = get_log_folder()
- var/datum/c4_file/text/log_file = log_dir.get_file("syslog")
- if(!log_file)
- log_file = new /datum/c4_file/text()
- log_file.set_name("syslog")
- if(!log_dir.try_add_file(log_file))
- qdel(log_file)
- return FALSE
-
- command_log = log_file
- RegisterSignal(command_log, list(COMSIG_COMPUTER4_FILE_RENAMED, COMSIG_COMPUTER4_FILE_ADDED, COMSIG_PARENT_QDELETING), PROC_REF(log_file_gone))
-
- log_file.data += "
STARTUP: [stationtime2text()], [stationdate2text()]"
- return TRUE
-
/datum/c4_file/terminal_program/operating_system/thinkdos/proc/set_current_user(datum/c4_file/user/new_user)
if(current_user)
UnregisterSignal(current_user, list(COMSIG_COMPUTER4_FILE_RENAMED, COMSIG_COMPUTER4_FILE_ADDED, COMSIG_COMPUTER4_FILE_REMOVED))
@@ -251,6 +261,3 @@
unload_program(running_program)
-/datum/c4_file/terminal_program/operating_system/thinkdos/clean_up()
- LAZYNULL(queued_commands)
- . = ..()
diff --git a/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm b/code/modules/computer4/data/terminal/operating_system/thinkdos/thinkdos_commands.dm
similarity index 95%
rename from code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm
rename to code/modules/computer4/data/terminal/operating_system/thinkdos/thinkdos_commands.dm
index abf075a28dc7..23f46fa7bc78 100644
--- a/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm
+++ b/code/modules/computer4/data/terminal/operating_system/thinkdos/thinkdos_commands.dm
@@ -5,6 +5,36 @@
/// How to use this command, usually printed by a "help" command.
var/help_text = "N/A"
+/datum/shell_command/New()
+ help_text = generate_help_text()
+
+/datum/shell_command/proc/generate_help_text()
+ return help_text
+
+/// Generates an output for a list of help commands.
+/datum/shell_command/proc/generate_help_list(list/output, list/arguments, list/commands, datum/c4_file/terminal_program/operating_system/system) as /list
+ if(length(arguments))
+ var/searching_for = jointext(arguments, "")
+ for(var/datum/shell_command/command_iter as anything in commands)
+ if(searching_for in command_iter.aliases)
+ output += "Displaying information for '[command_iter.aliases[1]]':"
+ output += command_iter.help_text
+ return SHELL_CMD_HELP_COMMAND
+
+ system.println("This command is not supported by the help utility. To see a list of commands, type !help.")
+ return SHELL_CMD_HELP_ERROR
+
+ // Listing all commands.
+ for(var/datum/shell_command/command_iter as anything in commands)
+ if(length(command_iter.aliases) == 1)
+ output += command_iter.aliases[1]
+ continue
+
+ output += "[command_iter.aliases[1]] ([jointext(command_iter.aliases.Copy(2), ", ")])"
+
+ sortTim(output, GLOBAL_PROC_REF(cmp_text_asc))
+ return SHELL_CMD_HELP_GENERIC
+
/// Attempt to execute the command. Return TRUE if *any* action is taken.
/datum/shell_command/proc/try_exec(command_name, datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
if(!(lowertext(command_name) in aliases))
@@ -24,33 +54,8 @@
/datum/shell_command/thinkdos/help/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
var/list/output = list()
- if(length(arguments))
- var/found = FALSE
- var/searching_for = jointext(arguments, "")
- for(var/datum/shell_command/command_iter as anything in system.commands)
- if(searching_for in command_iter.aliases)
- found = TRUE
- output += "Displaying information for '[command_iter.aliases[1]]':"
- output += command_iter.help_text
- break
-
- if(!found)
- system.print_error("This command is not supported by the help utility. To see a list of commands, type help.")
- return
-
- else
- for(var/datum/shell_command/command_iter as anything in system.commands)
- if(length(command_iter.aliases) == 1)
- output += command_iter.aliases[1]
- continue
-
- output += "[command_iter.aliases[1]] ([jointext(command_iter.aliases.Copy(2), ", ")])"
-
- sortTim(output, GLOBAL_PROC_REF(cmp_text_asc))
- output.Insert(1, "Use help \[command\] to see specific information about a command.", "List of available commands:")
-
-
- system.println(jointext(output, "
"))
+ if(generate_help_list(output, arguments, system.commands, system) != SHELL_CMD_HELP_ERROR)
+ system.println(jointext(output, "
"))
/// Clear the screen
/datum/shell_command/thinkdos/home
diff --git a/code/modules/computer4/data/terminal/thinkdos/thinkdos_signals.dm b/code/modules/computer4/data/terminal/operating_system/thinkdos/thinkdos_signals.dm
similarity index 100%
rename from code/modules/computer4/data/terminal/thinkdos/thinkdos_signals.dm
rename to code/modules/computer4/data/terminal/operating_system/thinkdos/thinkdos_signals.dm
diff --git a/code/modules/computer4/data/terminal/packMAN/packman.dm b/code/modules/computer4/data/terminal/packMAN/packman.dm
new file mode 100644
index 000000000000..011674cf7a7e
--- /dev/null
+++ b/code/modules/computer4/data/terminal/packMAN/packman.dm
@@ -0,0 +1,63 @@
+/datum/c4_file/terminal_program/packman
+ name = "packMAN"
+
+ var/mode = PACKMAN_MODE_UNDEFINED
+ var/datum/pdp_socket/socket
+
+ var/static/list/commands
+
+/datum/c4_file/terminal_program/packman/New()
+ if(!commands)
+ commands = list()
+ for(var/path as anything in subtypesof(/datum/shell_command/packman_cmd))
+ commands += new path
+
+/datum/c4_file/terminal_program/packman/execute(datum/c4_file/terminal_program/operating_system/thinkdos/system)
+ . = ..()
+ if(!.)
+ return
+
+ system.println("packMAN V1.01", FALSE)
+ system.println("Welcome to packMAN, type 'help' to get started.")
+
+ if(!get_adapter())
+ system.println("Error: No network adapter found.")
+
+/datum/c4_file/terminal_program/packman/on_close(datum/c4_file/terminal_program/operating_system/thinkdos/system)
+ . = ..()
+ mode = PACKMAN_MODE_UNDEFINED
+ socket = null
+
+/// Getter for a network adapter.
+/datum/c4_file/terminal_program/packman/proc/get_adapter()
+ RETURN_TYPE(/obj/item/peripheral/network_card)
+ return get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)
+
+/datum/c4_file/terminal_program/packman/std_in(text)
+ . = ..()
+ if(.)
+ return
+
+ get_os().println(text)
+
+ var/datum/shell_stdin/parsed_input = parse_std_in(text)
+
+ var/datum/c4_file/terminal_program/operating_system/os = get_os()
+
+ for(var/datum/shell_command/potential_command as anything in commands)
+ if(potential_command.try_exec(parsed_input.command, os, src, parsed_input.arguments, parsed_input.options))
+ return TRUE
+
+ get_os().println("'[html_encode(parsed_input.raw)]' is not recognized as an internal or external command.")
+
+/datum/c4_file/terminal_program/packman/tick(delta_time)
+ . = ..()
+ if(!socket)
+ return
+
+ var/datum/c4_file/terminal_program/operating_system/system = get_os()
+ var/datum/signal/packet
+ while((packet = socket.pop()))
+ system.println(
+ html_encode(json_encode(packet.data))
+ )
diff --git a/code/modules/computer4/data/terminal/packMAN/packman_commands.dm b/code/modules/computer4/data/terminal/packMAN/packman_commands.dm
new file mode 100644
index 000000000000..e8efbf06e6f0
--- /dev/null
+++ b/code/modules/computer4/data/terminal/packMAN/packman_commands.dm
@@ -0,0 +1,53 @@
+/datum/shell_command/packman_cmd/chat
+ aliases = list("c", "chat")
+
+/datum/shell_command/packman_cmd/chat/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
+ if(length(arguments) < 2)
+ system.println("Error: Invalid argument count.")
+ return
+
+ var/datum/c4_file/terminal_program/packman/packman = program
+
+ var/address = arguments[1]
+ var/payload = jointext(arguments.Copy(2), " ")
+
+ packman.socket.send_data(address, PDP_PORT_NETTEST, list(payload))
+ system.println(html_encode(payload))
+
+/datum/shell_command/packman_cmd/init
+ aliases = list("init")
+
+/datum/shell_command/packman_cmd/init/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
+ if(!length(arguments))
+ system.println("Error: Invalid argument count.")
+ return
+
+ if(!(lowertext(arguments[1]) in list("c", "s")))
+ system.println("Error: Invalid argument.")
+ return
+
+ var/datum/c4_file/terminal_program/packman/packman = program
+
+ if(packman.mode != PACKMAN_MODE_UNDEFINED)
+ return
+
+ packman.mode = (lowertext(arguments[1]) == "s") ? PACKMAN_MODE_SERVER : PACKMAN_MODE_CLIENT
+
+ if(packman.mode == PACKMAN_MODE_SERVER)
+ packman.socket = system.bind_port(PDP_PORT_NETTEST, FALSE, packman)
+ if(!packman.socket)
+ system.println("Error: Socket binding failed.")
+ return
+
+ else
+ packman.socket = system.bind_port(PDP_BIND_EPHEMERAL_PORT, FALSE, packman)
+ if(!packman.socket)
+ system.println("Error: Socket binding failed.")
+ return
+
+ system.println("Bound port to [packman.socket.bound_port] as [packman.mode == PACKMAN_MODE_SERVER ? "SERVER" : "CLIENT"]")
+
+/datum/shell_command/packman_cmd/quit/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
+ system.println("Quitting...")
+ system.unload_program(program)
+ return
diff --git a/code/modules/computer4/data/terminal/probe/probe.dm b/code/modules/computer4/data/terminal/probe/probe.dm
index 22048c7966c1..d9b6c1add768 100644
--- a/code/modules/computer4/data/terminal/probe/probe.dm
+++ b/code/modules/computer4/data/terminal/probe/probe.dm
@@ -13,8 +13,6 @@
/datum/c4_file/terminal_program/probe/execute(datum/c4_file/terminal_program/operating_system/thinkdos/system)
. = ..()
- if(!.)
- return
system.println("NetProbe V2.4", FALSE)
system.println("Welcome to NetProbe, type 'help' to get started.")
@@ -48,11 +46,11 @@
if(!packet)
return
- if(!packet.data[PACKET_NETCLASS] || !packet.data[PACKET_SOURCE_ADDRESS] || (packet.data[PACKET_CMD] != NET_COMMAND_PING_REPLY))
+ if(!packet.data[LEGACY_PACKET_NETCLASS] || !packet.data[LEGACY_PACKET_SOURCE_ADDRESS] || (packet.data[LEGACY_PACKET_COMMAND] != NET_COMMAND_PING_REPLY))
return
- var/reply_netclass = packet.data[PACKET_NETCLASS]
- var/reply_id = packet.data[PACKET_SOURCE_ADDRESS]
+ var/reply_netclass = packet.data[LEGACY_PACKET_NETCLASS]
+ var/reply_id = packet.data[LEGACY_PACKET_SOURCE_ADDRESS]
ping_replies[reply_id] = reply_netclass
get_os().println("\[[reply_netclass]\]-TYPE: [reply_id]")
diff --git a/code/modules/computer4/data/terminal/probe/probe_commands.dm b/code/modules/computer4/data/terminal/probe/probe_commands.dm
index 09b2a4f834f7..31c1faf4fc88 100644
--- a/code/modules/computer4/data/terminal/probe/probe_commands.dm
+++ b/code/modules/computer4/data/terminal/probe/probe_commands.dm
@@ -1,5 +1,16 @@
+/datum/shell_command/probe_cmd/help
+ aliases = list("help")
+ help_text = "Lists all available commands. Use help \[command\] to view information about a specific command."
+
+/datum/shell_command/probe_cmd/help/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
+ var/list/output = list()
+
+ if(generate_help_list(output, arguments, astype(program, /datum/c4_file/terminal_program/probe).commands, system) != SHELL_CMD_HELP_ERROR)
+ system.println(jointext(output, "
"))
+
/datum/shell_command/probe_cmd/ping
- aliases = list("ping, p")
+ aliases = list("ping", "p")
+ help_text = "Pings the radio frequency the network card is tuned to."
/datum/shell_command/probe_cmd/ping/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
var/datum/c4_file/terminal_program/probe/probe = program
@@ -10,10 +21,12 @@
return
if(adapter.ping())
+ probe.ping_replies.Cut()
system.println("Pinging '[format_frequency(adapter.frequency)]'...")
/datum/shell_command/probe_cmd/view
- aliases = list("view, v")
+ aliases = list("view", "v")
+ help_text = "Lists all ping responses since the last ping."
/datum/shell_command/probe_cmd/view/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
var/datum/c4_file/terminal_program/probe/probe = program
@@ -34,6 +47,14 @@
/datum/shell_command/probe_cmd/quit
aliases = list("quit", "q")
+/datum/shell_command/probe_cmd/quit/generate_help_text()
+ return jointext(list(
+ "Exits the program, moving it to the background.",
+ "Usage: 'quit'",
+ "",
+ "-f, --force [FOURSPACES]Exits the program without moving it to background.",
+ ), "
")
+
/datum/shell_command/probe_cmd/quit/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options)
var/force = !!length(options & list("f", "force"))
diff --git a/code/modules/computer4/peripherals/network_card.dm b/code/modules/computer4/peripherals/network_card.dm
index 49b65fd8a687..9ad424bc6617 100644
--- a/code/modules/computer4/peripherals/network_card.dm
+++ b/code/modules/computer4/peripherals/network_card.dm
@@ -52,17 +52,17 @@
radio_connection = SSpackets.add_object(src, new_frequency)
/// Post a signal. Has safety checks, so calling this with a timer is OK.
-/obj/item/peripheral/network_card/wireless/proc/post_signal(datum/signal/packet, filter)
+/obj/item/peripheral/network_card/wireless/post_signal(datum/signal/packet, filter)
if(!master_pc?.is_operational || !radio_connection)
return
- packet.data[PACKET_SOURCE_ADDRESS] = network_id
+ packet.data[LEGACY_PACKET_SOURCE_ADDRESS] = network_id
// Rewrite the author so we don't get the packet we just sent back.
packet.author = WEAKREF(src)
radio_connection.post_signal(packet, filter)
/obj/item/peripheral/network_card/wireless/proc/deferred_post_signal(datum/signal/packet, filter, time)
- addtimer(CALLBACK(src, PROC_REF(post_signal), packet, filter), time)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, post_signal), packet, filter), time)
/obj/item/peripheral/network_card/wireless/receive_signal(datum/signal/signal)
if(!master_pc)
@@ -74,19 +74,19 @@
switch(listen_mode)
if(WIRELESS_FILTER_NETADDR)
// Isn't meant for us, but could be a ping
- if(signal.data[PACKET_DESTINATION_ADDRESS] != network_id)
- if(!signal.data[PACKET_SOURCE_ADDRESS] || (signal.data[PACKET_DESTINATION_ADDRESS] != NET_ADDRESS_PING))
+ if(signal.data[LEGACY_PACKET_DESTINATION_ADDRESS] != network_id)
+ if(!signal.data[LEGACY_PACKET_SOURCE_ADDRESS] || (signal.data[LEGACY_PACKET_DESTINATION_ADDRESS] != NET_ADDRESS_PING))
return // Is not a ping, bye bye!
var/list/data = list(
- PACKET_SOURCE_ADDRESS = network_id,
- PACKET_DESTINATION_ADDRESS = signal.data[PACKET_SOURCE_ADDRESS],
- PACKET_CMD = NET_COMMAND_PING_REPLY,
- PACKET_NETCLASS = NETCLASS_ADAPTER,
+ LEGACY_PACKET_SOURCE_ADDRESS = network_id,
+ LEGACY_PACKET_DESTINATION_ADDRESS = signal.data[LEGACY_PACKET_SOURCE_ADDRESS],
+ LEGACY_PACKET_COMMAND = NET_COMMAND_PING_REPLY,
+ LEGACY_PACKET_NETCLASS = NETCLASS_ADAPTER,
)
var/datum/signal/packet = new(src, data, TRANSMISSION_RADIO)
- addtimer(CALLBACK(src, PROC_REF(post_signal), packet), 1 SECOND)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, post_signal), packet), 1 SECOND)
return
if(WIRELESS_FILTER_ID_TAGS)
//Drop packets not in our ID tags list.
@@ -97,10 +97,11 @@
//allow all
*/
-
-
var/datum/signal/clone = signal.Copy()
- master_pc.peripheral_input(src, PERIPHERAL_CMD_RECEIVE_PACKET, clone)
+ if(signal.data[PKT_HEAD_PROTOCOL] == PKT_PROTOCOL_PDP)
+ master_pc.peripheral_input(src, PERIPHERAL_CMD_RECEIVE_PDP_PACKET, clone)
+ else
+ master_pc.peripheral_input(src, PERIPHERAL_CMD_RECEIVE_PACKET, clone)
/obj/item/peripheral/network_card/wireless/proc/ping()
if(!COOLDOWN_FINISHED(src, ping_cooldown))
@@ -108,8 +109,8 @@
COOLDOWN_START(src, ping_cooldown, 2 SECONDS)
var/list/data = list(
- PACKET_SOURCE_ADDRESS = network_id,
- PACKET_DESTINATION_ADDRESS = NET_ADDRESS_PING,
+ LEGACY_PACKET_SOURCE_ADDRESS = network_id,
+ LEGACY_PACKET_DESTINATION_ADDRESS = NET_ADDRESS_PING,
)
var/datum/signal/packet = new(src, data)
diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm
index 6dfda716ac35..6da89cfd01a7 100644
--- a/code/modules/modular_computers/file_system/programs/budgetordering.dm
+++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm
@@ -292,7 +292,7 @@
if(.)
post_signal(cargo_shuttle)
-/datum/computer_file/program/budgetorders/proc/post_signal(command)
+/datum/computer_file/program/budgetorders/post_signal(command)
SHOULD_CALL_PARENT(FALSE) //TODO: Tie to cablenet
var/datum/radio_frequency/frequency = SSpackets.return_frequency(FREQ_STATUS_DISPLAYS)
diff --git a/code/modules/modular_computers/file_system/programs/ntmessenger.dm b/code/modules/modular_computers/file_system/programs/ntmessenger.dm
index e95b26bebebd..29c3d273963b 100644
--- a/code/modules/modular_computers/file_system/programs/ntmessenger.dm
+++ b/code/modules/modular_computers/file_system/programs/ntmessenger.dm
@@ -93,7 +93,7 @@
var/list/signal_data = signal.data
if(!signal_data)
return
- var/signal_command = signal_data[PACKET_CMD]
+ var/signal_command = signal_data[LEGACY_PACKET_COMMAND]
//Network ID verification is "hardware accelerated" (AKA: Done for us by the card)
var/rigged = FALSE//are we going to explode?
@@ -104,7 +104,7 @@
// ESPECIALLY THIS FUCKER RIGHT HERE vvvv
if(signal_data[SSpackets.pda_exploitable_register] == SSpackets.detomatix_magic_packet)
//This one falls through to standard PDA behaviour, so we need to be checked first.
- if(signal_data[PACKET_DESTINATION_ADDRESS] == netcard_cache.hardware_id)//No broadcast bombings, fuck off.
+ if(signal_data[LEGACY_PACKET_DESTINATION_ADDRESS] == netcard_cache.hardware_id)//No broadcast bombings, fuck off.
//Calculate our "difficulty"
var/difficulty
var/obj/item/computer_hardware/hard_drive/role/our_jobdisk = computer.all_components[MC_HDD_JOB]
@@ -114,7 +114,7 @@
difficulty++ //if cartridge has manifest access it has extra snowflake difficulty
if(!((SEND_SIGNAL(computer, COMSIG_TABLET_CHECK_DETONATE) & COMPONENT_TABLET_NO_DETONATE) || prob(difficulty * 15)))
rigged = TRUE //Cool, we're allowed to blow up. Really glad this whole check wasn't for nothing.
- var/trait_timer_key = signal_data[PACKET_SOURCE_ADDRESS]
+ var/trait_timer_key = signal_data[LEGACY_PACKET_SOURCE_ADDRESS]
ADD_TRAIT(computer, TRAIT_PDA_CAN_EXPLODE, trait_timer_key)
ADD_TRAIT(computer, TRAIT_PDA_MESSAGE_MENU_RIGGED, trait_timer_key)
addtimer(TRAIT_CALLBACK_REMOVE(computer, TRAIT_PDA_MESSAGE_MENU_RIGGED, trait_timer_key), 10 SECONDS)
@@ -127,7 +127,7 @@
html_decode("\"[signal_data["message"]]\"") || "#ERROR_MISSING_FIELD",
FALSE,
signal_data["automated"] || FALSE,
- signal_data[PACKET_SOURCE_ADDRESS] || null
+ signal_data[LEGACY_PACKET_SOURCE_ADDRESS] || null
)
if(ringer_status)
@@ -189,7 +189,7 @@
L = get(holder.holder, /mob/living/silicon)
if(L && L.stat == CONSCIOUS)
- var/reply = "(Reply)"
+ var/reply = "(Reply)"
var/hrefstart
var/hrefend
var/job_string = signal_data["job"] ? " ([signal_data["job"]])" : ""
@@ -197,7 +197,7 @@
hrefstart = ""
hrefend = ""
- if(signal_data[PACKET_SOURCE_ADDRESS] == null)
+ if(signal_data[LEGACY_PACKET_SOURCE_ADDRESS] == null)
reply = "\[#ERRNOADDR\]"
if(signal_data["automated"])
@@ -269,11 +269,11 @@
var/datum/signal/pda_message = new(
src,
list(
- PACKET_CMD = NETCMD_PDAMESSAGE,
+ LEGACY_PACKET_COMMAND = NETCMD_PDAMESSAGE,
"name" = fake_name || computer.saved_identification,
"job" = fake_job || computer.saved_job,
"message" = html_decode(message),
- PACKET_DESTINATION_ADDRESS = target_address
+ LEGACY_PACKET_DESTINATION_ADDRESS = target_address
),
logging_data = user
)
@@ -354,7 +354,7 @@
if(!istype(pnetcard)) //This catches nulls too, so...
to_chat(usr, span_warning("Radio missing or bad driver!"))
var/datum/signal/ping_sig = new(src, list(
- PACKET_DESTINATION_ADDRESS = NET_ADDRESS_PING,
+ LEGACY_PACKET_DESTINATION_ADDRESS = NET_ADDRESS_PING,
"pda_scan" = "true"
))
pnetcard.post_signal(ping_sig)
diff --git a/code/modules/modular_computers/hardware/gprs_card.dm b/code/modules/modular_computers/hardware/gprs_card.dm
index 4b5ad47f60b7..123f8573d933 100644
--- a/code/modules/modular_computers/hardware/gprs_card.dm
+++ b/code/modules/modular_computers/hardware/gprs_card.dm
@@ -62,14 +62,14 @@
if(signal.transmission_method != TRANSMISSION_RADIO)
CRASH("[src] received non-radio packet, transmission method ID [signal.transmission_method], Expected [TRANSMISSION_RADIO]")
var/list/signal_data = signal.data //medium velocity silver hedgehog
- var/signal_d_addr = signal_data[PACKET_DESTINATION_ADDRESS]
+ var/signal_d_addr = signal_data[LEGACY_PACKET_DESTINATION_ADDRESS]
if(signal_d_addr == NET_ADDRESS_PING) //Ping.
var/datum/signal/outgoing = new(
src,
list(
- PACKET_DESTINATION_ADDRESS = signal_data[PACKET_SOURCE_ADDRESS],
- PACKET_CMD = NET_COMMAND_PING_REPLY,
- PACKET_NETCLASS = NETCLASS_GRPS_CARD,
+ LEGACY_PACKET_DESTINATION_ADDRESS = signal_data[LEGACY_PACKET_SOURCE_ADDRESS],
+ LEGACY_PACKET_COMMAND = NET_COMMAND_PING_REPLY,
+ LEGACY_PACKET_NETCLASS = NETCLASS_GRPS_CARD,
"netaddr" = hardware_id
)
)
@@ -83,11 +83,11 @@
//Either it's broadcast or directed to us.
if(isnull(signal_d_addr) || signal_d_addr == hardware_id)
// If it's a ping reply, check for a PDA.
- if(signal.data[PACKET_CMD] == NET_COMMAND_PING_REPLY)
+ if(signal.data[LEGACY_PACKET_COMMAND] == NET_COMMAND_PING_REPLY)
//If it's from a GPRS card, respond, otherwise, who cares.
- if(signal.data[PACKET_NETCLASS] == NETCLASS_GRPS_CARD)
+ if(signal.data[LEGACY_PACKET_NETCLASS] == NETCLASS_GRPS_CARD)
var/list/new_pda_info = list(
- "target_addr" = signal.data[PACKET_SOURCE_ADDRESS],
+ "target_addr" = signal.data[LEGACY_PACKET_SOURCE_ADDRESS],
"name" = signal.data["reg_name"] || "#UNK",
"job" = signal.data["reg_job"] || "#UNK"
)
@@ -98,10 +98,10 @@
append_signal(signal)
-/obj/item/computer_hardware/network_card/packetnet/proc/post_signal(datum/signal/signal)
+/obj/item/computer_hardware/network_card/packetnet/post_signal(datum/signal/signal)
if(!radio_connection || !signal)
return FALSE // Something went wrong.
- signal.data[PACKET_SOURCE_ADDRESS] = hardware_id //Readdress outgoing packets.
+ signal.data[LEGACY_PACKET_SOURCE_ADDRESS] = hardware_id //Readdress outgoing packets.
signal.author = WEAKREF(src)
radio_connection.post_signal(signal, RADIO_PDAMESSAGE)
return TRUE //We at least tried.
diff --git a/code/modules/modular_computers/hardware/virus_disk.dm b/code/modules/modular_computers/hardware/virus_disk.dm
index 2bbda2e7c554..4d65293906da 100644
--- a/code/modules/modular_computers/hardware/virus_disk.dm
+++ b/code/modules/modular_computers/hardware/virus_disk.dm
@@ -30,7 +30,7 @@
var/list/user_input_tuple = user_input(target_addr, user)
var/datum/signal/outgoing = new(src, list(
SSpackets.pda_exploitable_register = magic_packet,
- PACKET_DESTINATION_ADDRESS = target_addr
+ LEGACY_PACKET_DESTINATION_ADDRESS = target_addr
))
var/signal_data = outgoing.data
switch(user_input_tuple[1])
@@ -121,7 +121,7 @@
var/list/pda_data_staple = list(
// We already have the target address
// GPRS Card handles the source address
- PACKET_CMD = NETCMD_PDAMESSAGE,
+ LEGACY_PACKET_COMMAND = NETCMD_PDAMESSAGE,
"name" = sender_name,
"job" = sender_job,
"message" = text_message
diff --git a/daedalus.dme b/daedalus.dme
index 80c89495262b..2a680d07a51c 100644
--- a/daedalus.dme
+++ b/daedalus.dme
@@ -403,6 +403,7 @@
#include "code\__HELPERS\mouse_control.dm"
#include "code\__HELPERS\nameof.dm"
#include "code\__HELPERS\names.dm"
+#include "code\__HELPERS\packetnet.dm"
#include "code\__HELPERS\piping_colors_lists.dm"
#include "code\__HELPERS\priority_announce.dm"
#include "code\__HELPERS\pronouns.dm"
@@ -2999,6 +3000,7 @@
#include "code\modules\computer4\computer4_types.dm"
#include "code\modules\computer4\data\metadata.dm"
#include "code\modules\computer4\data\shell_stdin.dm"
+#include "code\modules\computer4\data\socket.dm"
#include "code\modules\computer4\data\c4_file\_c4_file.dm"
#include "code\modules\computer4\data\c4_file\fab_design_bundle.dm"
#include "code\modules\computer4\data\c4_file\folder.dm"
@@ -3007,7 +3009,6 @@
#include "code\modules\computer4\data\c4_file\text.dm"
#include "code\modules\computer4\data\c4_file\user_data.dm"
#include "code\modules\computer4\data\terminal\_terminal_program.dm"
-#include "code\modules\computer4\data\terminal\operating_system.dm"
#include "code\modules\computer4\data\terminal\directive\directman.dm"
#include "code\modules\computer4\data\terminal\directive\directman_commands.dm"
#include "code\modules\computer4\data\terminal\medtrak\medtrak.dm"
@@ -3018,19 +3019,25 @@
#include "code\modules\computer4\data\terminal\medtrak\medtrak_record_commands.dm"
#include "code\modules\computer4\data\terminal\netpage\netpage.dm"
#include "code\modules\computer4\data\terminal\netpage\netpage_commands.dm"
+#include "code\modules\computer4\data\terminal\operating_system\_operating_system.dm"
+#include "code\modules\computer4\data\terminal\operating_system\filesystem.dm"
+#include "code\modules\computer4\data\terminal\operating_system\networking.dm"
+#include "code\modules\computer4\data\terminal\operating_system\program_management.dm"
+#include "code\modules\computer4\data\terminal\operating_system\rtos\_rtos.dm"
+#include "code\modules\computer4\data\terminal\operating_system\rtos\access_door.dm"
+#include "code\modules\computer4\data\terminal\operating_system\rtos\pincode_door.dm"
+#include "code\modules\computer4\data\terminal\operating_system\rtos\simple_door_control.dm"
+#include "code\modules\computer4\data\terminal\operating_system\rtos\slave.dm"
+#include "code\modules\computer4\data\terminal\operating_system\thinkdos\thinkdos.dm"
+#include "code\modules\computer4\data\terminal\operating_system\thinkdos\thinkdos_commands.dm"
+#include "code\modules\computer4\data\terminal\operating_system\thinkdos\thinkdos_signals.dm"
+#include "code\modules\computer4\data\terminal\packMAN\packman.dm"
+#include "code\modules\computer4\data\terminal\packMAN\packman_commands.dm"
+#include "code\modules\computer4\data\terminal\probe\probe.dm"
+#include "code\modules\computer4\data\terminal\probe\probe_commands.dm"
#include "code\modules\computer4\data\terminal\notepad\notepad.dm"
#include "code\modules\computer4\data\terminal\notepad\notepad_commands.dm"
#include "code\modules\computer4\data\terminal\packet_hound\packet_hound.dm"
-#include "code\modules\computer4\data\terminal\probe\probe.dm"
-#include "code\modules\computer4\data\terminal\probe\probe_commands.dm"
-#include "code\modules\computer4\data\terminal\rtos\_rtos.dm"
-#include "code\modules\computer4\data\terminal\rtos\access_door.dm"
-#include "code\modules\computer4\data\terminal\rtos\pincode_door.dm"
-#include "code\modules\computer4\data\terminal\rtos\simple_door_control.dm"
-#include "code\modules\computer4\data\terminal\rtos\slave.dm"
-#include "code\modules\computer4\data\terminal\thinkdos\thinkdos.dm"
-#include "code\modules\computer4\data\terminal\thinkdos\thinkdos_commands.dm"
-#include "code\modules\computer4\data\terminal\thinkdos\thinkdos_signals.dm"
#include "code\modules\computer4\embedded_controller\access_door.dm"
#include "code\modules\computer4\embedded_controller\embedded_controller.dm"
#include "code\modules\computer4\embedded_controller\pincode_door.dm"
diff --git a/tgui/packages/tgui/interfaces/Terminal/TerminalOutputSection.tsx b/tgui/packages/tgui/interfaces/Terminal/TerminalOutputSection.tsx
index 551ad1712e92..e7111e97ad8b 100644
--- a/tgui/packages/tgui/interfaces/Terminal/TerminalOutputSection.tsx
+++ b/tgui/packages/tgui/interfaces/Terminal/TerminalOutputSection.tsx
@@ -42,6 +42,9 @@ export const TerminalOutputSection = (props: TerminalOutputSectionProps) => {
height="100%"
color={fontColor}
fontSize="1.2em"
+ style={{
+ wordBreak: 'break-all',
+ }}
preserveWhitespace
dangerouslySetInnerHTML={{ __html: displayHTML }}
/>