First implementation of NTNet Software Downloads, Fixes

- As usual, fixes various issues through the code, but likely creates more as this is still WIP project.
- Most importantly, NTNet software downloader program now exists and more or less works.
- Your device's connectivity has effect on how quickly you can download. Right now, things with wired connection (consoles) download at 0,5GQ/s, mobile devices on station where signal is good at 0,1GQ/s and mobile devices off station where signal is bad are limited to 0,025GQ/s. This is all controlled by three defines.
This commit is contained in:
Atlantis
2015-08-27 07:09:04 +02:00
parent 5492739805
commit 46280123ee
20 changed files with 584 additions and 213 deletions
+67 -1
View File
@@ -4,6 +4,14 @@ var/global/datum/ntnet/ntnet_global = new()
// This is the NTNet datum. There can be only one NTNet datum in game at once. Modular computers read data from this.
/datum/ntnet/
var/list/relays = list()
var/list/logs = list()
var/list/available_station_software = list()
var/list/available_antag_software = list()
// Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly.
// High values make displaying logs much laggier.
var/setting_maxlogcount = 100
var/setting_softwaredownload = 1
var/setting_peertopeer = 1
var/setting_communication = 1
@@ -17,7 +25,28 @@ var/global/datum/ntnet/ntnet_global = new()
for(var/obj/machinery/ntnet_relay/R in machines)
relays.Add(R)
R.NTNet = src
build_software_lists()
add_log("NTNet logging system activated.")
// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional.
/datum/ntnet/proc/add_log(var/log_string, var/datum/computer_hardware/network_card/source = null)
var/log_text = "[worldtime2text()] - "
if(source)
log_text += "[source.get_network_tag()] - "
else
log_text += " *SYSTEM* - "
log_text += log_string
logs.Add(log_text)
if(logs.len > setting_maxlogcount)
// We have too many logs, remove the oldest entries until we get into the limit
for(var/L in logs)
if(logs.len > setting_maxlogcount)
logs.Remove(L)
else
break
// Checks whether NTNet operates. If parameter is passed checks whether specific function is enabled.
/datum/ntnet/proc/check_function(var/specific_action = 0)
if(!relays || !relays.len) // No relays found. NTNet is down
return 0
@@ -38,4 +67,41 @@ var/global/datum/ntnet/ntnet_global = new()
return (operating && setting_communication)
if(specific_action == NTNET_SYSTEMCONTROL)
return (operating && setting_systemcontrol)
return operating
return operating
// Builds lists that contain downloadable software.
/datum/ntnet/proc/build_software_lists()
available_station_software = list()
available_antag_software = list()
for(var/F in typesof(/datum/computer_file/program))
var/datum/computer_file/program/prog = new F
// Invalid type (shouldn't be possible but just in case), invalid filetype (not executable program) or invalid filename (unset program)
if(!prog || !istype(prog) || prog.filename == "UnknownProgram" || prog.filetype != "PRG")
continue
// Check whether the program should be available for station/antag download, if yes, add it to lists.
if(prog.available_on_ntnet)
available_station_software.Add(prog)
if(prog.available_on_syndinet)
available_antag_software.Add(prog)
// Attempts to find a downloadable file according to filename var
/datum/ntnet/proc/find_ntnet_file_by_name(var/filename)
for(var/datum/computer_file/program/P in available_station_software)
if(filename == P.filename)
return P
for(var/datum/computer_file/program/P in available_antag_software)
if(filename == P.filename)
return P
@@ -7,7 +7,7 @@
icon_state = "bus"
anchored = 1
density = 1
var/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame.
var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame.
// TODO: Implement more logic here. For now it's only a placeholder.
/obj/machinery/ntnet_relay/proc/is_operational()
@@ -19,10 +19,12 @@
if(ntnet_global)
ntnet_global.relays.Add(src)
NTNet = ntnet_global
ntnet_global.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]")
..()
/obj/machinery/ntnet_relay/Destroy()
if(ntnet_global)
ntnet_global.relays.Remove(src)
ntnet_global.add_log("Quantum relay connection severed. Current amount of linked relays: [NTNet.relays.len]")
NTNet = null
..()
@@ -7,10 +7,14 @@
desc = "A small portable microcomputer"
var/enabled = 1 // Whether the computer is turned on.
var/open = 1 // Whether the computer is active/opened/it's screen is on.
var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
var/hardware_flag = 0 // A flag that describes this device type
var/last_power_usage = 0
var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
// Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..)
// must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently
// If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example.
@@ -97,7 +101,7 @@
// Operates NanoUI
/obj/item/modular_computer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(!open || !enabled)
if(!screen_on || !enabled)
if(ui)
ui.close()
return 0
@@ -141,7 +145,7 @@
/obj/item/modular_computer/attack_self(mob/user)
if(enabled)
ui_interact(user)
else if(battery && battery.charge) // Battery-run and charged or non-battery but powered by APC.
else if((battery && battery.charge) || check_power_override()) // Battery-run and charged or non-battery but powered by APC.
user << "You press the power button and start up \the [src]"
enabled = 1
update_icon()
@@ -159,6 +163,11 @@
kill_program(1)
visible_message("<span class='danger'>\The [src]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error.</span>")
if(active_program)
active_program.process_tick()
active_program.ntnet_status = get_ntnet_status()
// TODO: Implement computer_emagged handling for programs
handle_power() // Handles all computer power interaction
// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_"
@@ -181,7 +190,7 @@
data["PC_batteryicon"] = "batt_5.gif"
data["PC_batterypercent"] = "[round(battery.percent())] %"
data["PC_showbatteryicon"] = 1
else // Computer without battery shouldn't work at all, but in case we implement computers without batteries in future that run solely on APC network, it's here.
else
data["PC_batteryicon"] = "batt_5.gif"
data["PC_batterypercent"] = "N/C"
data["PC_showbatteryicon"] = battery ? 1 : 0
@@ -204,14 +213,8 @@
// Installs programs necessary for computer function.
// TODO: Implement program for downloading of other programs, and replace hardcoded program addition here
/obj/item/modular_computer/proc/install_default_programs()
hard_drive.store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar
//TODO: Remove once downloading is implemented
hard_drive.store_file(new/datum/computer_file/program/alarm_monitor(src))
hard_drive.store_file(new/datum/computer_file/program/power_monitor(src))
hard_drive.store_file(new/datum/computer_file/program/atmos_control(src))
hard_drive.store_file(new/datum/computer_file/program/rcon_console(src))
hard_drive.store_file(new/datum/computer_file/program/suit_sensors(src))
hard_drive.store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar
hard_drive.store_file(new/datum/computer_file/program/ntnetdownload(src)) // NTNet Downloader Utility, allows users to download more software from NTNet repository
// Relays kill program request to currently active program. Use this to quit current program.
/obj/item/modular_computer/proc/kill_program(var/forced = 0)
@@ -230,6 +233,18 @@
else
return 0
/obj/item/modular_computer/proc/add_log(var/text)
if(!get_ntnet_status())
return 0
return ntnet_global.add_log(text, network_card)
/obj/item/modular_computer/proc/shutdown_computer()
kill_program(1)
visible_message("\The [src] shuts down.")
enabled = 0
update_icon()
return
// Handles user's GUI input
/obj/item/modular_computer/Topic(href, href_list)
if(..())
@@ -248,11 +263,7 @@
H.enabled = 0
return
if( href_list["PC_shutdown"] )
kill_program(1)
visible_message("\The [src] shuts down.")
enabled = 0
update_icon()
return
shutdown_computer()
if( href_list["PC_runprogram"] )
var/prog = href_list["PC_runprogram"]
var/datum/computer_file/program/P = null
@@ -289,7 +300,7 @@
power_failure()
return 0
var/power_usage = open ? 50 : 5 // 50W when it's open and only 5W when closed (sleep mode)? Screen probably uses a lot.
var/power_usage = screen_on ? base_active_power_usage : base_idle_power_usage
if(network_card && network_card.enabled)
power_usage += network_card.power_usage
@@ -310,7 +321,7 @@
if(card_slot.stored_card)
user << "You try to insert \the [I] into \the [src], but it's ID card slot is occupied."
return
user.drop_from_inventory(I)
card_slot.stored_card = I
I.forceMove(src)
user << "You insert \the [I] into \the [src]."
@@ -10,9 +10,19 @@
var/obj/machinery/modular_computer/machinery_computer = null
// Process is handled via machinery half of this, due to power interaction.
/obj/item/modular_computer/processor/process()
return PROCESS_KILL
// Due to how processes work, we'd receive two process calls - one from machinery type and one from our own type.
// Since we want this to be in-sync with machinery (as it's hidden type for machinery-based computers) we'll ignore
// non-relayed process calls.
/obj/item/modular_computer/processor/process(var/relayed = 0)
if(relayed)
..()
else
return
// Power interaction is handled by our machinery part, due to machinery having APC connection.
/obj/item/modular_computer/processor/handle_power()
if(machinery_computer)
machinery_computer.handle_power()
/obj/item/modular_computer/processor/New(var/comp)
if(!comp || !istype(comp, /obj/machinery/modular_computer))
@@ -21,29 +31,6 @@
// Obtain reference to machinery computer
machinery_computer = comp
machinery_computer.cpu = src
// Now steal the computer's components and assume them as our own. The computer will send us commands, we'll do most of the work.
hard_drive = machinery_computer.hard_drive
machinery_computer.hard_drive = null
if(hard_drive)
hard_drive.holder2 = src
hard_drive.holder = null
network_card = machinery_computer.network_card
machinery_computer.network_card = null
if(network_card)
network_card.holder2 = src
network_card.holder = null
nano_printer = machinery_computer.nano_printer
machinery_computer.nano_printer = null
if(nano_printer)
nano_printer.holder = src
nano_printer.holder = null
card_slot = machinery_computer.card_slot
machinery_computer.card_slot = null
if(card_slot)
card_slot.holder2 = src
card_slot.holder = null
battery = machinery_computer.battery
machinery_computer.battery = null
hardware_flag = machinery_computer.hardware_flag
/obj/item/modular_computer/processor/find_hardware_by_name(var/N)
@@ -53,9 +40,13 @@
else
return ..()
/obj/item/modular_computer/processor/update_icon()
if(machinery_computer)
return machinery_computer.update_icon()
/obj/item/modular_computer/processor/get_header_data()
var/list/L = ..()
if(machinery_computer.tesla_link && machinery_computer.tesla_link.enabled)
if(machinery_computer.tesla_link && machinery_computer.tesla_link.enabled && machinery_computer.powered())
L["PC_apclinkicon"] = "charging.gif"
return L
@@ -65,11 +56,19 @@
return 0
if(!machinery_computer.tesla_link || !machinery_computer.tesla_link.enabled)
return 0
return 1
return machinery_computer.powered()
//
// This thing is not meant to be used on it's own, get topic data from our machinery owner.
/obj/item/modular_computer/processor/CanUseTopic(user, state)
if(!machinery_computer)
return 0
return machinery_computer.CanUseTopic(user, state)
/obj/item/modular_computer/processor/shutdown_computer()
if(!machinery_computer)
return
kill_program(1)
visible_message("\The [machinery_computer] shuts down.")
enabled = 0
machinery_computer.update_icon()
return
@@ -4,9 +4,6 @@
name = "modular computer"
desc = "An advanced computer"
var/open = 1 // Whether the computer is open. (0 = "low power" mode)
var/enabled = 1 // Whether the computer is turned on.
var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
var/battery_powered = 0 // Whether computer should be battery powered. It is set automatically
use_power = 0
var/hardware_flag = 0 // A flag that describes this device type
@@ -21,6 +18,10 @@
var/icon_state_unpowered = null // Icon state when the computer is turned off
var/screen_icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
var/keyboard_icon_state_menu = "keyboard1" // Keyboard's icon state overlay when the computer is turned on and no program is loaded
var/nokeyboard = 0 // Set to 1 to disable keyboard icons for this subtype.
var/base_active_power_usage = 100 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
var/base_idle_power_usage = 10 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
// Important hardware (must be installed for computer to work)
var/datum/computer_hardware/network_card/network_card // Network Card component of this computer. Allows connection to NTNet
@@ -37,14 +38,16 @@
icon_state = icon_state_unpowered
overlays.Cut()
if(!enabled)
if(!cpu || !cpu.enabled)
return
if(cpu && cpu.active_program)
if(cpu.active_program)
overlays.Add(cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu)
overlays.Add(cpu.active_program.keyboard_icon_state ? cpu.active_program.keyboard_icon_state : keyboard_icon_state_menu)
if(!nokeyboard)
overlays.Add(cpu.active_program.keyboard_icon_state ? cpu.active_program.keyboard_icon_state : keyboard_icon_state_menu)
else
overlays.Add(screen_icon_state_menu)
overlays.Add(keyboard_icon_state_menu)
if(!nokeyboard)
overlays.Add(keyboard_icon_state_menu)
// Eject ID card from computer, if it has ID slot with card inside.
/obj/machinery/modular_computer/verb/eject_id()
@@ -52,100 +55,30 @@
set category = "Object"
set src in view(1)
cpu.eject_id()
// Called after the computer is completed (in case it is created step by step, for example via laptop fabricator)
// This allows the device to work and creates processor object that handles most logic.
// ALWAYS CALL THIS ONCE ALL OTHER VARIABLES ARE SET, NOT BEFORE. IT MAY BE ONLY CALLED ONCE.
/obj/machinery/modular_computer/proc/shift_to_cpu()
if(cpu) // We already called this. Abort.
return
new/obj/item/modular_computer/processor(src) // It automatically handles everything in New()
if(cpu)
cpu.eject_id()
/obj/machinery/modular_computer/New()
..()
install_default_programs()
cpu = new(src)
// Installs programs necessary for computer function.
// TODO: Implement program for downloading of other programs, and replace hardcoded program addition here
/obj/machinery/modular_computer/proc/install_default_programs()
if(cpu && cpu.hard_drive)
cpu.hard_drive.store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar
//TODO: Remove once downloading is implemented
cpu.hard_drive.store_file(new/datum/computer_file/program/alarm_monitor(src))
cpu.hard_drive.store_file(new/datum/computer_file/program/power_monitor(src))
cpu.hard_drive.store_file(new/datum/computer_file/program/atmos_control(src))
cpu.hard_drive.store_file(new/datum/computer_file/program/rcon_console(src))
cpu.hard_drive.store_file(new/datum/computer_file/program/suit_sensors(src))
else if(hard_drive)
hard_drive.store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar
//TODO: Remove once downloading is implemented
hard_drive.store_file(new/datum/computer_file/program/alarm_monitor(src))
hard_drive.store_file(new/datum/computer_file/program/power_monitor(src))
hard_drive.store_file(new/datum/computer_file/program/atmos_control(src))
hard_drive.store_file(new/datum/computer_file/program/rcon_console(src))
hard_drive.store_file(new/datum/computer_file/program/suit_sensors(src))
// Operates NanoUI
/obj/machinery/modular_computer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(cpu)
cpu.ui_interact(user, ui_key, ui, force_open)
cpu.install_default_programs()
// On-click handling. Turns on the computer if it's off and opens the GUI.
/obj/machinery/modular_computer/attack_hand(mob/user)
if(!cpu) // If people started clicking us we're probably spawned for a while now. Generate the CPU datum.
shift_to_cpu()
if(!cpu) // Still no CPU? Something is very bad. Abort.
return
if(cpu.enabled)
ui_interact(user)
else if((cpu.battery && cpu.battery.charge > 0) || (!cpu.battery && powered())) // Battery-run and charged or non-battery but powered by APC.
user << "You press the power button and start up \the [src]"
cpu.enabled = 1
update_icon()
ui_interact(user)
else // Unpowered
user << "You press the power button but \the [src] does not respond."
if(cpu)
cpu.attack_self(user) // CPU is an item, that's why we route attack_hand to attack_self
// Process currently calls handle_power(), may be expanded in future if more things are added.
/obj/machinery/modular_computer/process()
if(!cpu.enabled) // The computer is turned off
use_power = 0
last_power_usage = 0
return 0
if(!cpu) // The computer lacks CPU object
return 0
if(cpu.active_program && cpu.active_program.requires_ntnet && !cpu.get_ntnet_status(cpu.active_program.requires_ntnet_feature)) // Active program requires NTNet to run but we've just lost connection. Crash.
cpu.kill_program(1)
visible_message("<span class='danger'>\The [src]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error.</span>")
battery_powered = battery ? 1 : 0
handle_power() // Handles all computer power interaction
// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_"
/obj/machinery/modular_computer/proc/get_header_data()
if(cpu)
return cpu.get_header_data()
else
return list()
// Relays kill program request to currently active program. Use this to quit current program.
/obj/machinery/modular_computer/proc/kill_program(var/forced = 0)
if(cpu)
cpu.kill_program(forced)
update_icon()
// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on)
/obj/machinery/modular_computer/proc/get_ntnet_status(var/specific_action = 0)
if(cpu)
cpu.get_ntnet_status(specific_action)
// Keep names in sync.
cpu.name = src.name
cpu.process(1)
// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null
/obj/machinery/modular_computer/proc/find_hardware_by_name(var/N)
@@ -153,27 +86,19 @@
return tesla_link
return null
// Handles user's GUI input
/obj/machinery/modular_computer/Topic(href, href_list)
if(..())
return 1
if(cpu)
return cpu.Topic(href, href_list)
// Used in following function to reduce copypaste
/obj/machinery/modular_computer/proc/power_failure()
if(cpu && cpu.enabled) // Shut down the computer
visible_message("<span class='danger'>\The [src]'s screen flickers [battery ? "\"BATTERY CRITICAL\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.</span>")
kill_program(1)
enabled = 0
if(cpu)
cpu.kill_program(1)
cpu.enabled = 0
battery_powered = 0
update_icon()
stat |= NOPOWER
// Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged, NOPOWER flag, etc.
// Called by cpu item's process() automatically, handles our power interaction.
/obj/machinery/modular_computer/proc/handle_power()
if(!cpu)
return 0
if(cpu.battery && cpu.battery.charge <= 0) // Battery-run but battery is depleted.
power_failure()
return 0
@@ -183,7 +108,12 @@
else if(stat & NOPOWER)
stat &= ~NOPOWER
var/power_usage = open ? 300 : 25 // 300W when it's open and only 25W when closed (sleep mode)? Screen probably uses a lot.
if(cpu.battery && cpu.battery.charge)
battery_powered = 1
else
battery_powered = 0
var/power_usage = cpu.screen_on ? base_active_power_usage : base_idle_power_usage
if(cpu.network_card && cpu.network_card.enabled)
power_usage += cpu.network_card.power_usage
@@ -224,19 +154,6 @@
..()
/obj/machinery/modular_computer/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if(istype(W, /obj/item/weapon/card/id)) // ID Card, try to insert it.
var/obj/item/weapon/card/id/I = W
if(!cpu.card_slot)
user << "You try to insert \the [I] into \the [src], but it does not have an ID card slot installed."
return
if(cpu.card_slot.stored_card)
user << "You try to insert \the [I] into \the [src], but it's ID card slot is occupied."
return
cpu.card_slot.stored_card = I
I.forceMove(src)
user << "You insert \the [I] into \the [src]."
return
..()
if(cpu)
return cpu.attackby(W, user)
return ..()
@@ -1,7 +1,6 @@
/obj/machinery/modular_computer/console/
name = "console"
desc = "A stationary computer."
enabled = 1
icon = 'icons/obj/modular_console.dmi'
icon_state = "console"
@@ -9,16 +8,64 @@
screen_icon_state_menu = "menu"
keyboard_icon_state_menu = "kb_menu"
hardware_flag = PROGRAM_CONSOLE
var/console_department = "" // Used in New() to set network tag according to our area.
anchored = 1
density = 1
base_idle_power_usage = 100
base_active_power_usage = 500
/obj/machinery/modular_computer/console/New()
..()
battery = null
network_card = new/datum/computer_hardware/network_card/wired(src)
cpu.network_card = new/datum/computer_hardware/network_card/wired(src)
tesla_link = new/datum/computer_hardware/tesla_link(src)
tesla_link.enabled = 1
tesla_link.critical = 1 // Consoles don't usually come with cells, and this prevents people from disabling their only power source, as they wouldn't be able to enable it again.
hard_drive = new/datum/computer_hardware/hard_drive/super(src) // Consoles generally have better HDDs due to lower space limitations
..()
shift_to_cpu()
update_icon()
cpu.hard_drive = new/datum/computer_hardware/hard_drive/super(src) // Consoles generally have better HDDs due to lower space limitations
var/area/A = get_area(src)
// Attempts to set this console's tag according to our area. Since some areas have stuff like "XX - YY" in their names we try to remove that too.
if(A && console_department)
cpu.network_card.identification_string = replacetext(replacetext(replacetext("[A.name] [console_department] Console", " ", "_"), "-", ""), "__", "_") // Replace spaces with "_"
else if(A)
cpu.network_card.identification_string = replacetext(replacetext(replacetext("[A.name] Console", " ", "_"), "-", ""), "__", "_")
else if(console_department)
cpu.network_card.identification_string = replacetext(replacetext(replacetext("[console_department] Console", " ", "_"), "-", ""), "__", "_")
else
cpu.network_card.identification_string = "Unknown Console"
if(cpu)
cpu.screen_on = 1
cpu.enabled = 1
install_default_programs()
update_icon()
/obj/machinery/modular_computer/console/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if(istype(W, /obj/item/weapon/cell)) // Power Cell. Try to insert it into the console, if it doesn't have cell installed.
if(!cpu || (stat & (BROKEN|MAINT)))
user << "\The [cpu] seems to be broken."
return
if(cpu.battery)
user << "You try to insert \the [W] into \the [cpu], but it't battery slot is occupied."
return
cpu.battery = W
user.drop_from_inventory(W)
W.forceMove(src)
user << "You insert \the [W] into \the [cpu]'s battery slot."
return
if(istype(W, /obj/item/weapon/screwdriver)) // TODO: Screwdriver - begin deconstructing the computer.
return
if(istype(W, /obj/item/weapon/crowbar)) // Crowbar, remove power cell, if it has one.
if(!cpu || (stat & (BROKEN|MAINT)))
user << "\The [cpu] seems to be broken."
return
if(!cpu.battery)
user << "\The [cpu] doesn't seem to have battery installed."
return
user << "You begin removing \the [cpu.battery] from \the [cpu]."
if(do_after(user, 20))
cpu.battery.forceMove(get_turf(src))
user << "You crowbar \the [cpu.battery] from \the [cpu]."
cpu.battery = null
battery_powered = 0
power_change()
return
return ..()
@@ -38,7 +38,8 @@
stored_computer.forceMove(src.loc)
stored_computer.stat &= ~MAINT
stored_computer.update_icon()
stored_computer.open = 1
if(stored_computer.cpu)
stored_computer.cpu.screen_on = 1
loc = stored_computer
usr << "You open \the [src]."
@@ -56,6 +57,9 @@
icon_state_unpowered = "laptop-open" // Icon state when the computer is turned off
icon = 'icons/obj/modular_laptop.dmi'
icon_state = "laptop-open"
nokeyboard = 1
base_idle_power_usage = 25
base_active_power_usage = 200
// Close the computer. collapsing it into movable item that can't be used.
/obj/machinery/modular_computer/laptop/verb/close_computer()
@@ -88,7 +92,8 @@
stat |= MAINT
if(user)
user << "You close \the [src]."
open = 0
if(cpu)
cpu.screen_on = 0
/obj/machinery/modular_computer/laptop/AltClick()
if(Adjacent(usr))
@@ -10,19 +10,22 @@
if(!holder)
return ..()
holder.stored_files -= src
holder.remove_file(src)
// holder.holder is the computer that has drive installed. If we are Destroy()ing program that's currently running kill it.
if(holder.holder && holder.holder.active_program == src)
holder.holder.kill_program(1)
if(holder.holder2 && holder.holder2.active_program == src)
holder.holder2.kill_program(1)
holder = null
..()
// Returns independent copy of this file.
/datum/computer_file/proc/clone()
var/datum/computer_file/temp
/datum/computer_file/proc/clone(var/rename = 0)
var/datum/computer_file/temp = new type
temp.unsendable = unsendable
temp.undeletable = undeletable
temp.size = size
temp.filename = filename + "(Copy)"
if(rename)
temp.filename = filename + "(Copy)"
else
temp.filename = filename
temp.filetype = filetype
return temp
@@ -1,23 +1,27 @@
// /program/ files are executable programs that do things.
/datum/computer_file/program
filetype = "PRG"
filename = "UnknownProgram" // File name.
filename = "UnknownProgram" // File name. FILE NAME MUST BE UNIQUE IF YOU WANT THE PROGRAM TO BE DOWNLOADABLE FROM NTNET!
var/required_access = null // List of required accesses to *run* the program.
var/datum/nano_module/NM = null // If the program uses NanoModule, put it here and it will be automagically opened. Otherwise implement ui_interact.
var/nanomodule_path = null // Path to nanomodule, make sure to set this if implementing new program.
var/running = 0 // Set to 1 when the program is run and back to 0 when it's stopped.
var/atom/movable/computer = null // Device that runs this program.
var/obj/item/modular_computer/computer // Device that runs this program.
var/filedesc = "Unknown Program" // User-friendly name of this program.
var/program_icon_state = null // Overlay for this program, selected by computer
var/keyboard_icon_state = null // Program-specific keboard icon state
var/program_icon_state = null // Program-specific screen icon state
var/keyboard_icon_state = null // Program-specific keboard icon state, only supported on some devices.
var/requires_ntnet = 0 // Set to 1 for program to require nonstop NTNet connection to run. If NTNet connection is lost program crashes.
var/requires_ntnet_feature = 0 // Optional, if above is set to 1 checks for specific function of NTNet (currently NTNET_SOFTWAREDOWNLOAD, NTNET_PEERTOPEER, NTNET_SYSTEMCONTROL and NTNET_COMMUNICATION)
var/ntnet_status = 1 // NTNet status, updated every tick by computer running this program. Don't use this for checks if NTNet works, computers do that. Use this for calculations, etc.
var/usage_flags = PROGRAM_ALL // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL
var/network_destination = null // Optional string that describes what NTNet server/system this program connects to. Used in default logging.
var/available_on_ntnet = 1 // Whether the program can be downloaded from NTNet. Set to 0 to disable.
var/available_on_syndinet = 0 // Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable.
var/computer_emagged = 0 // Set to 1 if computer that's running us was emagged. Computer updates this every Process() tick
/datum/computer_file/program/New(var/atom/movable/comp = null)
/datum/computer_file/program/New(var/obj/item/modular_computer/comp = null)
..()
if(comp)
if(comp && istype(comp))
computer = comp
/datum/computer_file/program/clone()
@@ -31,6 +35,12 @@
temp.usage_flags = usage_flags
return temp
// Attempts to create a log in global ntnet datum. Returns 1 on success, 0 on fail.
/datum/computer_file/program/proc/generate_network_log(var/text)
if(computer)
return computer.add_log(text)
return 0
/datum/computer_file/program/proc/is_supported_by_hardware(var/hardware_flag = 0, var/loud = 0, var/mob/user = null)
if(!(hardware_flag & usage_flags))
if(loud && computer && user)
@@ -38,11 +48,17 @@
return 0
return 1
// Called by Process() on device that runs us, once every tick.
/datum/computer_file/program/proc/process_tick()
return 1
// Check if the user can run program. Only humans can operate computer. Automatically called in run_program()
// User has to wear their ID or have it inhand for ID Scan to work.
/datum/computer_file/program/proc/can_run(var/mob/living/user, var/loud = 0)
if(!required_access) // No required_access, allow it.
return 1
if(istype(user, /mob/living/silicon)) // AI or robot. Allow it.
return 1
if(istype(user, /mob/living/carbon/human))
var/mob/living/carbon/human/H = user
var/obj/item/weapon/card/id/I = H.wear_id
@@ -68,21 +84,20 @@
// This attempts to retrieve header data for NanoUIs. If implementing completely new device of different type than existing ones
// always include the device here in this proc. This proc basically relays the request to whatever is running the program.
/datum/computer_file/program/proc/get_header_data()
if(istype(computer, /obj/machinery/modular_computer))
var/obj/machinery/modular_computer/L = computer
return L.get_header_data()
if(istype(computer, /obj/item/modular_computer))
var/obj/item/modular_computer/L = computer
return L.get_header_data()
if(computer)
return computer.get_header_data()
return list()
// This is performed on program startup. May be overriden to add extra logic. Remember to include ..() call. Return 1 on success, 0 on failure.
// When implementing new program based device, use this to run the program.
/datum/computer_file/program/proc/run_program(var/mob/living/user)
if(can_run(user, 1))
if(nanomodule_path)
NM = new nanomodule_path(computer) // Computer is passed here as it's (probably!) physical object. Some UI's perform get_turf() and passing program datum wouldn't go well with this.
NM.program = src // Set the program reference to separate variable, instead.
if(requires_ntnet && network_destination)
generate_network_log("Connection opened to [network_destination].")
running = 1
return 1
return 0
@@ -90,8 +105,11 @@
// Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client.
/datum/computer_file/program/proc/kill_program(var/forced = 0)
running = 0
qdel(NM)
NM = null
if(network_destination)
generate_network_log("Connection to [network_destination] closed.")
if(NM)
qdel(NM)
NM = null
return 1
// This is called every tick when the program is enabled. Ensure you do parent call if you override it. If parent returns 1 continue with UI initialisation.
@@ -113,7 +131,6 @@
// Calls beginning with "PC_" are reserved for computer handling (by whatever runs the program)
// ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE.
/datum/computer_file/program/Topic(href, href_list)
if(computer)
computer.Topic(href, href_list)
..()
@@ -7,6 +7,7 @@
program_icon_state = "power_monitor"
keyboard_icon_state = "keyboard9"
requires_ntnet = 1
network_destination = "power monitoring system"
size = 8
/datum/computer_file/program/alarm_monitor
@@ -16,6 +17,7 @@
program_icon_state = "alarm_monitor"
keyboard_icon_state = "keyboard4"
requires_ntnet = 1
network_destination = "alarm monitoring network"
size = 5
/datum/computer_file/program/atmos_control
@@ -26,6 +28,7 @@
keyboard_icon_state = "keyboard4"
required_access = access_atmospherics
requires_ntnet = 1
network_destination = "atmospheric control system"
requires_ntnet_feature = NTNET_SYSTEMCONTROL
usage_flags = PROGRAM_LAPTOP | PROGRAM_CONSOLE
size = 17
@@ -38,6 +41,7 @@
keyboard_icon_state = "keyboard1"
required_access = access_engine
requires_ntnet = 1
network_destination = "RCON remote control system"
requires_ntnet_feature = NTNET_SYSTEMCONTROL
usage_flags = PROGRAM_LAPTOP | PROGRAM_CONSOLE
size = 25
@@ -49,4 +53,5 @@
program_icon_state = "crew"
keyboard_icon_state = "keyboard7"
requires_ntnet = 1
network_destination = "crew lifesigns monitoring system"
size = 20
@@ -9,6 +9,8 @@
unsendable = 1
undeletable = 1
size = 2
available_on_ntnet = 0
requires_ntnet = 0
nanomodule_path = /datum/nano_module/computer_configurator/
/datum/nano_module/computer_configurator
@@ -0,0 +1,154 @@
/datum/computer_file/program/ntnetdownload
filename = "ntndownloader"
filedesc = "NTNet Software Download Tool"
program_icon_state = "generic"
unsendable = 1
undeletable = 1
size = 3
requires_ntnet = 1
requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD
available_on_ntnet = 0
nanomodule_path = /datum/nano_module/computer_ntnetdownload/
var/datum/computer_file/program/downloaded_file = null
var/hacked_download = 0
var/download_completion = 0 //GQ of downloaded data.
var/download_netspeed = 0
var/downloaderror = ""
/datum/computer_file/program/ntnetdownload/proc/begin_file_download(var/filename)
if(downloaded_file)
return 0
var/datum/computer_file/program/PRG = ntnet_global.find_ntnet_file_by_name(filename)
if(!PRG || !istype(PRG))
return 0
// Attempting to download antag only program, but without having emagged computer. No.
if(PRG.available_on_syndinet && !computer_emagged)
return 0
if(!computer || !computer.hard_drive || !computer.hard_drive.try_store_file(PRG))
return 0
if(PRG in ntnet_global.available_station_software)
generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.")
hacked_download = 0
else if(PRG in ntnet_global.available_antag_software)
generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.")
hacked_download = 1
else
generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from unspecified server.")
hacked_download = 0
downloaded_file = PRG.clone()
/datum/computer_file/program/ntnetdownload/proc/abort_file_download()
if(!downloaded_file)
return
generate_network_log("Aborted download of file [hacked_download ? "**ENCRYPTED**" : downloaded_file.filename].[downloaded_file.filetype].")
downloaded_file = null
download_completion = 0
/datum/computer_file/program/ntnetdownload/proc/complete_file_download()
if(!downloaded_file)
return
generate_network_log("Completed download of file [hacked_download ? "**ENCRYPTED**" : downloaded_file.filename].[downloaded_file.filetype].")
if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(downloaded_file))
// The download failed
downloaderror = "I/O ERROR - Unable to save file. Check whether you have enough free space on your hard drive and whether your hard drive is properly connected. If the issue persists contact your system administrator for assistance."
downloaded_file = null
download_completion = 0
/datum/computer_file/program/ntnetdownload/process_tick()
if(!downloaded_file)
return
if(download_completion >= downloaded_file.size)
complete_file_download()
// Download speed according to connectivity state. NTNet server is assumed to be on unlimited speed so we're limited by our local connectivity
download_netspeed = 0
// Speed defines are found in misc.dm
switch(ntnet_status)
if(1)
download_netspeed = NTNETSPEED_LOWSIGNAL
if(2)
download_netspeed = NTNETSPEED_HIGHSIGNAL
if(3)
download_netspeed = NTNETSPEED_ETHERNET
download_completion += download_netspeed
/datum/computer_file/program/ntnetdownload/Topic(href, href_list)
if(..())
return 1
if(href_list["PRG_downloadfile"])
if(!downloaded_file)
begin_file_download(href_list["PRG_downloadfile"])
return
if(href_list["PRG_reseterror"])
if(downloaderror)
download_completion = 0
download_netspeed = 0
downloaded_file = null
downloaderror = ""
return
return 0
/datum/nano_module/computer_ntnetdownload
name = "Network Downloader"
var/obj/item/modular_computer/my_computer = null
/datum/nano_module/computer_ntnetdownload/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
if(program)
my_computer = program.computer
if(!istype(my_computer))
return
var/list/data = list()
var/datum/computer_file/program/ntnetdownload/prog = program
// For now limited to execution by the downloader program
if(!prog || !istype(prog))
return
if(program)
data = program.get_header_data()
// This IF cuts on data transferred to client, so i guess it's worth it.
if(prog.downloaderror) // Download errored. Wait until user resets the program.
data["error"] = prog.downloaderror
else if(prog.downloaded_file) // Download running. Wait please..
data["downloadname"] = prog.downloaded_file.filename
data["downloaddesc"] = prog.downloaded_file.filedesc
data["downloadsize"] = prog.downloaded_file.size
data["downloadspeed"] = prog.download_netspeed
data["downloadcompletion"] = round(prog.download_completion, 0.1)
else // No download running, pick file.
data["disk_size"] = my_computer.hard_drive.max_capacity
data["disk_used"] = my_computer.hard_drive.used_capacity
var/list/all_entries[0]
for(var/datum/computer_file/program/P in ntnet_global.available_station_software)
all_entries.Add(list(list(
"filename" = P.filename,
"filedesc" = P.filedesc,
"size" = P.size,
)))
data["hackedavailable"] = 0
if(prog.computer_emagged) // If we are running on emagged computer we have access to some "bonus" software
var/list/hacked_programs[0]
for(var/datum/computer_file/program/P in ntnet_global.available_antag_software)
data["hackedavailable"] = 1
hacked_programs.Add(list(list(
"filename" = P.filename,
"filedesc" = P.filedesc,
"size" = P.size
)))
data["hacked_programs"] = hacked_programs
data["downloadable_programs"] = all_entries
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "ntnet_downloader.tmpl", "NTNet Download Program", 575, 700, state = state)
ui.auto_update_layout = 1
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
@@ -47,6 +47,7 @@
if(F in stored_files)
return 0
F.holder = src
stored_files.Add(F)
recalculate_size()
return 1
@@ -85,6 +86,18 @@
else
return 1
// Checks whether we can store the file. We can only store unique files, so this checks whether we wouldn't get a duplicity by adding a file.
/datum/computer_hardware/hard_drive/proc/try_store_file(var/datum/computer_file/F)
if(!F || !istype(F))
return 0
var/name = F.filename + "." + F.filetype
for(var/datum/computer_file/file in stored_files)
if((file.filename + "." + file.filetype) == name)
return 0
return can_store_file(F.size)
// Tries to find the file by filename. Returns null on failure
/datum/computer_hardware/hard_drive/proc/find_file_by_name(var/filename)
if(!filename)
@@ -1,12 +1,20 @@
var/global/ntnet_card_uid = 1
/datum/computer_hardware/network_card/
name = "NTNet Network Card"
desc = "A basic network card for usage with standard NTNet frequencies."
power_usage = 50
critical = 0
var/identification_string = "" // Identification string, technically nickname seen in the network. Can be set by user.
var/identification_id = null // Identification ID. Technically MAC address of this device. Can't be changed by user.
var/identification_string = "" // Identification string, technically nickname seen in the network. Can be set by user.
var/long_range = 0
var/ethernet = 0 // Hard-wired, therefore always on, ignores NTNet wireless checks.
/datum/computer_hardware/network_card/New(var/l)
..(l)
identification_id = ntnet_card_uid
ntnet_card_uid++
/datum/computer_hardware/network_card/advanced
desc = "An advanced network card for usage with standard NTNet frequencies. It's transmitter is strong enough to connect even off-station."
long_range = 1
@@ -25,6 +33,10 @@
holder = null
..()
// Returns a string identifier of this network card
/datum/computer_hardware/network_card/proc/get_network_tag()
return "[identification_string] (NID [identification_id])"
// 0 - No signal, 1 - Low signal, 2 - High signal. 3 - Wired Connection
/datum/computer_hardware/network_card/proc/get_signal(var/specific_action = 0)
if(!holder && !holder2) // Hardware is not installed in anything. No signal. How did this even get called?
+14 -15
View File
@@ -49,43 +49,43 @@
if(devtype == 1) // Laptop, generally cheaper to make it accessible for most station roles
if(fabricate)
fabricated_laptop = new(src)
fabricated_laptop.battery = new(fabricated_laptop)
fabricated_laptop.cpu.battery = new(fabricated_laptop.cpu)
total_price = 99
switch(dev_battery)
if(1) // Basic(500C)
if(fabricate)
fabricated_laptop.battery.maxcharge = 500
fabricated_laptop.battery.charge = 500
fabricated_laptop.cpu.battery.maxcharge = 500
fabricated_laptop.cpu.battery.charge = 500
if(2) // Upgraded(1000C)
if(fabricate)
fabricated_laptop.battery.maxcharge = 1000
fabricated_laptop.battery.charge = 1000
fabricated_laptop.cpu.battery.maxcharge = 1000
fabricated_laptop.cpu.battery.charge = 1000
total_price += 199
if(3) // Advanced(1500C)
if(fabricate)
fabricated_laptop.battery.maxcharge = 1500
fabricated_laptop.battery.charge = 1500
fabricated_laptop.cpu.battery.maxcharge = 1500
fabricated_laptop.cpu.battery.charge = 1500
total_price += 499
switch(dev_disk)
if(1) // Basic(128GQ)
if(fabricate)
fabricated_laptop.hard_drive = new/datum/computer_hardware/hard_drive(fabricated_laptop)
fabricated_laptop.cpu.hard_drive = new/datum/computer_hardware/hard_drive(fabricated_laptop.cpu)
if(2) // Upgraded(256GQ)
if(fabricate)
fabricated_laptop.hard_drive = new/datum/computer_hardware/hard_drive/advanced(fabricated_laptop)
fabricated_laptop.cpu.hard_drive = new/datum/computer_hardware/hard_drive/advanced(fabricated_laptop.cpu)
total_price += 99
if(3) // Advanced(512GQ)
if(fabricate)
fabricated_laptop.hard_drive = new/datum/computer_hardware/hard_drive/super(fabricated_laptop)
fabricated_laptop.cpu.hard_drive = new/datum/computer_hardware/hard_drive/super(fabricated_laptop.cpu)
total_price += 299
switch(dev_netcard)
if(1) // Basic(Short-Range)
if(fabricate)
fabricated_laptop.network_card = new/datum/computer_hardware/network_card(fabricated_laptop)
fabricated_laptop.cpu.network_card = new/datum/computer_hardware/network_card(fabricated_laptop.cpu)
total_price += 99
if(2) // Advanced (Long Range)
if(fabricate)
fabricated_laptop.network_card = new/datum/computer_hardware/network_card/advanced(fabricated_laptop)
fabricated_laptop.cpu.network_card = new/datum/computer_hardware/network_card/advanced(fabricated_laptop.cpu)
total_price += 299
if(dev_tesla)
total_price += 399
@@ -94,11 +94,11 @@
if(dev_nanoprint)
total_price += 99
if(fabricate)
fabricated_laptop.nano_printer = new/datum/computer_hardware/nano_printer(fabricated_laptop)
fabricated_laptop.cpu.nano_printer = new/datum/computer_hardware/nano_printer(fabricated_laptop.cpu)
if(dev_card)
total_price += 199
if(fabricate)
fabricated_laptop.card_slot = new/datum/computer_hardware/card_slot(fabricated_laptop)
fabricated_laptop.cpu.card_slot = new/datum/computer_hardware/card_slot(fabricated_laptop.cpu)
return total_price
else if(devtype == 2) // Tablet, more expensive, not everyone could probably afford this.
@@ -240,7 +240,6 @@ obj/machinery/lapvend/attackby(obj/item/weapon/W as obj, mob/user as mob)
if((devtype == 1) && fabricated_laptop)
fabricated_laptop.forceMove(src.loc)
fabricated_laptop.install_default_programs()
fabricated_laptop.shift_to_cpu()
fabricated_laptop.close_laptop()
fabricated_laptop = null
else if((devtype == 2) && fabricated_tablet)