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
@@ -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))