Files
ZandarioandGitHub ed05e01a95 __HELPERS Cleaning and other things I decided to do. (#4584)
* Schizoposting

* The Crungly

* Tabbin' the JSON y'all

* Strings
2022-10-21 01:56:59 -07:00

147 lines
6.9 KiB
Plaintext

/**
*! Exonet Protocol Version 2
*
* This is designed to be a fairly simple fake-networking system, allowing you to send and receive messages
* between the exonet_protocol datums, and for atoms to react to those messages, based on the contents of the message.
* Hopefully, this can evolve to be a more robust fake-networking system and allow for some devious network hacking in the future.
*
* Version 1 never existed.
*
* *Setting up*
*
* To set up the exonet link, define a variable on your desired atom it is like this;
* var/datum/exonet_protocol/exonet = null
* Afterwards, before you want to do networking, call exonet = New(src), then exonet.make_address(string), and give it a string to hash into the new IP.
* The reason it needs a string is so you can have the addresses be persistant, assuming no-one already took it first.
*
* When you're no longer wanting to use the address and want to free it up, like when you want to Destroy() it, you need to call remove_address()
*
* *Sending messages*
*
* To send a message to another datum, you need to know it's EPv2 (fake IP) address. Once you know that, call send_message(), place your
* intended address in the first argument, then the message in the second. For example, send_message(exonet.address, "ping") will make you
* ping yourself.
*
* *Receiving messages*
* You don't need to do anything special to receive the messages, other than give your target exonet datum an address as well. Once something hits
* your datum with send_message(), receive_message() is called, and the default action is to call receive_exonet_message() on the datum's holder.
* You'll want to override receive_exonet_message() on your atom, and define what will occur when the message is received.
* The receiving atom will receive the origin atom (the atom that sent the message), the origin address, and finally the message itself.
* It's suggested to start with an if or switch statement for the message, to determine what to do.
*/
var/global/list/all_exonet_connections = list()
/datum/exonet_protocol
var/address = "" //Resembles IPv6, but with only five 'groups', e.g. XXXX:XXXX:XXXX:XXXX:XXXX
var/atom/movable/holder = null
/datum/exonet_protocol/New(var/atom/holder)
src.holder = holder
..()
/datum/exonet_protocol/Destroy()
remove_address()
holder = null
return ..()
// Proc: make_address()
// Parameters: 1 (string - used to make into a hash that will be part of the new address)
// Description: Allocates a new address based on the string supplied. It results in consistant addresses for each round assuming it is not already taken..
/datum/exonet_protocol/proc/make_address(var/string)
set waitfor = FALSE // will this break things? yes. do i care? no fuck you.
if(string)
var/new_address = null
while(new_address == find_address(new_address)) //Collision test.
var/hash = md5(string)
var/raw_address = copytext(hash,1,25)
var/addr_0 = "fc00" //Used for unique local address in real-life IPv6.
var/addr_1 = hexadecimal_to_EPv2(raw_address)
new_address = "[addr_0]:[addr_1]"
string = "[string]0" //If we did get a collision, this should make the next attempt not have one.
CHECK_TICK
address = new_address
all_exonet_connections |= src
// Proc: make_arbitrary_address()
// Parameters: 1 (new_address - the desired address)
// Description: Allocates that specific address, if it is available.
/datum/exonet_protocol/proc/make_arbitrary_address(var/new_address)
if(new_address)
if(new_address == find_address(new_address) ) //Collision test.
return 0
address = new_address
all_exonet_connections |= src
return 1
// Proc: hexadecimal_to_EPv2()
// Parameters: 1 (hex - a string of hexadecimals to convert)
// Description: Helper proc to add colons to a string in the right places.
/proc/hexadecimal_to_EPv2(var/hex)
if(!hex)
return null
var/addr_1 = copytext(hex,1,5)
var/addr_2 = copytext(hex,5,9)
var/addr_3 = copytext(hex,9,13)
var/addr_4 = copytext(hex,13,17)
var/new_address = "[addr_1]:[addr_2]:[addr_3]:[addr_4]"
return new_address
// Proc: remove_address()
// Parameters: None
// Description: Deallocates the address, freeing it for use.
/datum/exonet_protocol/proc/remove_address()
address = ""
all_exonet_connections.Remove(src)
// Proc: find_address()
// Parameters: 1 (target_address - the desired address to find)
// Description: Searches the global list all_exonet_connections for a specific address, and returns it if found, otherwise returns null.
/datum/exonet_protocol/proc/find_address(var/target_address)
for(var/datum/exonet_protocol/exonet in all_exonet_connections)
if(exonet.address == target_address)
return exonet.address
return null
// Proc: get_atom_from_address()
// Parameters: 1 (target_address - the desired address to find)
// Description: Searches an address for the atom it is attached for, otherwise returns null.
/datum/exonet_protocol/proc/get_atom_from_address(var/target_address)
for(var/datum/exonet_protocol/exonet in all_exonet_connections)
if(exonet.address == target_address)
return exonet.holder
return null
// Proc: send_message()
// Parameters: 3 (target_address - the desired address to send the message to, data_type - text stating what the content is meant to be used for,
// content - the actual 'message' being sent to the address)
// Description: Sends the message to target_address, by calling receive_message() on the desired datum. Returns true if the message is recieved.
/datum/exonet_protocol/proc/send_message(var/target_address, var/data_type, var/content)
if(!address)
return FALSE
var/obj/machinery/exonet_node/node = get_exonet_node()
if(!node) // Telecomms went boom, ion storm, etc.
return FALSE
for(var/datum/exonet_protocol/exonet in all_exonet_connections)
if(exonet.address == target_address)
node.write_log(src.address, target_address, data_type, content)
return exonet.receive_message(holder, address, data_type, content)
// Proc: receive_message()
// Parameters: 4 (origin_atom - the origin datum's holder, origin_address - the address the message originated from,
// data_type - text stating what the content is meant to be used for, content - the actual 'message' being sent from origin_atom)
// Description: Called when send_message() successfully reaches the intended datum. By default, calls receive_exonet_message() on the holder atom.
/datum/exonet_protocol/proc/receive_message(var/atom/origin_atom, var/origin_address, var/data_type, var/content)
holder.receive_exonet_message(origin_atom, origin_address, data_type, content)
return TRUE // for send_message()
// Proc: receive_exonet_message()
// Parameters: 3 (origin_atom - the origin datum's holder, origin_address - the address the message originated from, message - the message that was sent)
// Description: Override this to make your atom do something when a message is received.
/atom/proc/receive_exonet_message(var/atom/origin_atom, var/origin_address, var/message, var/text)
return