Skip to content

Commit

Permalink
Stage 3
Browse files Browse the repository at this point in the history
  • Loading branch information
Stakks committed Apr 10, 2018
1 parent 4c3528a commit 33de053
Show file tree
Hide file tree
Showing 7 changed files with 363 additions and 1 deletion.
127 changes: 127 additions & 0 deletions code/datums/EPv2.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
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
..()


// 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)
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.
sleep(1)
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: send_message()
// Parameters: 2 (target_address - the desired address to send the message to, message - the message to send)
// Description: Sends the message to target_address, by calling receive_message() on the desired datum.
/datum/exonet_protocol/proc/send_message(var/target_address, var/message)
if(!address)
return 0
for(var/datum/exonet_protocol/exonet in all_exonet_connections)
if(exonet.address == target_address)
exonet.receive_message(holder, address, message)
break

// Proc: receive_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: 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/message)
holder.receive_exonet_message(origin_atom, origin_address, message)
return

// 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)
return
173 changes: 173 additions & 0 deletions code/game/machinery/exonet_node.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/obj/machinery/exonet_node
name = "exonet node"
desc = "This machine is one of many, many nodes inside Vir's section of the Exonet, connecting the Northern Star to the rest of the system, at least \
electronically."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "exonet_node"
idle_power_usage = 2500
density = 1
anchored = 1
var/on = 1
var/toggle = 1

var/allow_external_PDAs = 1
var/allow_external_communicators = 1
var/allow_external_newscasters = 1

var/opened = 0

// Proc: New()
// Parameters: None
// Description: Adds components to the machine for deconstruction.
/obj/machinery/exonet_node/New()
..()

component_parts = list()
component_parts += new /obj/item/weapon/circuitboard/telecomms/exonet_node(src)
component_parts += new /obj/item/weapon/stock_parts/subspace/ansible(src)
component_parts += new /obj/item/weapon/stock_parts/subspace/filter(src)
component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
component_parts += new /obj/item/weapon/stock_parts/micro_laser(src)
component_parts += new /obj/item/weapon/stock_parts/subspace/crystal(src)
component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src)
component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src)
component_parts += new /obj/item/stack/cable_coil(src, 2)
RefreshParts()

// Proc: update_icon()
// Parameters: None
// Description: Self explanatory.
/obj/machinery/exonet_node/update_icon()
if(on)
if(!allow_external_PDAs && !allow_external_communicators && !allow_external_newscasters)
icon_state = "[initial(icon_state)]_idle"
else
icon_state = initial(icon_state)
else
icon_state = "[initial(icon_state)]_off"

// Proc: update_power()
// Parameters: None
// Description: Sets the device on/off and adjusts power draw based on stat and toggle variables.
/obj/machinery/exonet_node/proc/update_power()
if(toggle)
if(stat & (BROKEN|NOPOWER|EMPED))
on = 0
idle_power_usage = 0
else
on = 1
idle_power_usage = 2500
else
on = 0
idle_power_usage = 0

// Proc: emp_act()
// Parameters: 1 (severity - how strong the EMP is, with lower numbers being stronger)
// Description: Shuts off the machine for awhile if an EMP hits it. Ion anomalies also call this to turn it off.
/obj/machinery/exonet_node/emp_act(severity)
if(!(stat & EMPED))
stat |= EMPED
var/duration = (300 * 10)/severity
spawn(rand(duration - 20, duration + 20))
stat &= ~EMPED
update_icon()
..()

// Proc: process()
// Parameters: None
// Description: Calls the procs below every tick.
/obj/machinery/exonet_node/process()
update_power()

// Proc: attackby()
// Parameters: 2 (I - the item being whacked against the machine, user - the person doing the whacking)
// Description: Handles deconstruction.
/obj/machinery/exonet_node/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/weapon/screwdriver))
default_deconstruction_screwdriver(user, I)
else if(istype(I, /obj/item/weapon/crowbar))
default_deconstruction_crowbar(user, I)
else
..()

// Proc: attack_ai()
// Parameters: 1 (user - the AI clicking on the machine)
// Description: Redirects to attack_hand()
/obj/machinery/exonet_node/attack_ai(mob/user)
attack_hand(user)

// Proc: attack_hand()
// Parameters: 1 (user - the person clicking on the machine)
// Description: Opens the NanoUI interface with ui_interact()
/obj/machinery/exonet_node/attack_hand(mob/user)
ui_interact(user)

// Proc: ui_interact()
// Parameters: 4 (standard NanoUI arguments)
// Description: Allows the user to turn the machine on or off, or open or close certain 'ports' for things like external PDA messages, newscasters, etc.
/obj/machinery/exonet_node/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
// this is the data which will be sent to the ui
var/data = list()


data["on"] = on ? 1 : 0
data["allowPDAs"] = allow_external_PDAs
data["allowCommunicators"] = allow_external_communicators
data["allowNewscasters"] = allow_external_newscasters

// update the ui if it exists, returns null if no ui is passed/found
ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "exonet_node.tmpl", "Exonet Node #157", 400, 400)
// when the ui is first opened this is the data it will use
ui.set_initial_data(data)
// open the new ui window
ui.open()
// auto update every Master Controller tick
ui.set_auto_update(1)

// Proc: Topic()
// Parameters: 2 (standard Topic arguments)
// Description: Responds to button presses on the NanoUI interface.
/obj/machinery/exonet_node/Topic(href, href_list)
if(..())
return 1
if(href_list["toggle_power"])
toggle = !toggle
update_power()
if(!toggle)
var/msg = "[usr.client.key] ([usr]) has turned [src] off, at [x],[y],[z]."
message_admins(msg)
log_game(msg)

if(href_list["toggle_PDA_port"])
allow_external_PDAs = !allow_external_PDAs

if(href_list["toggle_communicator_port"])
allow_external_communicators = !allow_external_communicators
if(!allow_external_communicators)
var/msg = "[usr.client.key] ([usr]) has turned [src]'s communicator port off, at [x],[y],[z]."
message_admins(msg)
log_game(msg)

if(href_list["toggle_newscaster_port"])
allow_external_newscasters = !allow_external_newscasters
if(!allow_external_newscasters)
var/msg = "[usr.client.key] ([usr]) has turned [src]'s newscaster port off, at [x],[y],[z]."
message_admins(msg)
log_game(msg)

update_icon()
SSnano.update_uis(src)
add_fingerprint(usr)

// Proc: get_exonet_node()
// Parameters: None
// Description: Helper proc to get a reference to an Exonet node.
/proc/get_exonet_node()
for(var/obj/machinery/exonet_node/E in machines)
if(E.on)
return E
14 changes: 14 additions & 0 deletions code/game/machinery/telecomms/machines/relay.dm
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,17 @@
hide = 1
toggled = 0
autolinkers = list("r_relay")

//This isn't a real telecomms board but I don't want to make a whole file to hold only one circuitboard.
/obj/item/weapon/circuitboard/telecomms/exonet_node
name = "exonet node"
build_path = "/obj/machinery/exonet_node"
origin_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5, TECH_BLUESPACE = 4)
req_components = list(
"/obj/item/weapon/stock_parts/subspace/ansible" = 1,
"/obj/item/weapon/stock_parts/subspace/filter" = 1,
"/obj/item/weapon/stock_parts/manipulator" = 2,
"/obj/item/weapon/stock_parts/micro_laser" = 1,
"/obj/item/weapon/stock_parts/subspace/crystal" = 1,
"/obj/item/weapon/stock_parts/subspace/treatment" = 2,
"/obj/item/stack/cable_coil" = 2)
9 changes: 8 additions & 1 deletion code/modules/research/designs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -806,4 +806,11 @@ other types of metals and chemistry for reagents).

/datum/design/circuit/integrated_circuit/time/clock
id = "cc-clock"
build_path = /obj/item/integrated_circuit/time/clock
build_path = /obj/item/integrated_circuit/time/clock


/datum/design/circuit/tcom/exonet_node
name = "exonet node"
id = "tcom-exonet_node"
req_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5, TECH_BLUESPACE = 4)
build_path = /obj/item/weapon/circuitboard/telecomms/exonet_node
Binary file modified icons/obj/stationobjs.dmi
Binary file not shown.
40 changes: 40 additions & 0 deletions nano/templates/exonet_node.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!--
Title: Exonet Node UI
Used In File(s): code\game\machinery/exonet_node.dm
-->

<h1>Status</h1>
<div class="item">
<div class="itemLabel">
Power:
</div>
<div class="itemContent">
{{:helper.link('On', 'power', {'toggle_power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'close', {'toggle_power' : 1}, data.on ? null : 'selected', data.on ? 'redButton' : null)}}
</div>
</div>

<h1>Ports</h1>
<div class="item">
<div class="itemLabel">
Incoming PDA Messages:
</div>
<div class="itemContent">
{{:helper.link('Open', 'check', {'toggle_PDA_port' : 1}, data.allowPDAs ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_PDA_port' : 1}, data.allowPDAs ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Incoming Communicators:
</div>
<div class="itemContent">
{{:helper.link('Open', 'check', {'toggle_communicator_port' : 1}, data.allowCommunicators ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_communicator_port' : 1}, data.allowCommunicators ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Incoming Newscaster Content:
</div>
<div class="itemContent">
{{:helper.link('Open', 'check', {'toggle_newscaster_port' : 1}, data.allowNewscasters ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_newscaster_port' : 1}, data.allowNewscasters ? null : 'selected')}}
</div>
</div>
1 change: 1 addition & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
#include "code\datums\computerfiles.dm"
#include "code\datums\datacore.dm"
#include "code\datums\datumvars.dm"
#include "code\datums\EPv2.dm"
#include "code\datums\gas_mixture.dm"
#include "code\datums\hud.dm"
#include "code\datums\martial.dm"
Expand Down

0 comments on commit 33de053

Please sign in to comment.