From 0307b3334dfca4453497315eaf95e3b451d7155f Mon Sep 17 00:00:00 2001
From: Daniel Xie <daniel.xie@xrtrading.local>
Date: Mon, 24 Oct 2016 16:16:51 -0500
Subject: [PATCH] Added port properties to server. Wrote code for hacking() and
 PortHacking, currently untested

---
 src/Player.js   |  53 ++++++++++++++++++
 src/Server.js   | 145 ++++++++++++++++++++++++++++++++++--------------
 src/Terminal.js |  43 ++++++++++++--
 src/engine.js   |  44 +++++++++++++++
 4 files changed, 240 insertions(+), 45 deletions(-)

diff --git a/src/Player.js b/src/Player.js
index 334af03fc..25e65ac33 100644
--- a/src/Player.js
+++ b/src/Player.js
@@ -7,6 +7,8 @@ var Player = {
 	defense:		1,
 	dexterity: 		1,
 	agility: 		1, 
+	hacking_chance_multiplier:	2,	//Increase through ascensions/augmentations
+	hacking_speed_multiplier: 	5,	//Decrease through ascensions/augmentations
 	
 	//Note: "Lifetime" refers to current ascension, "total" refers to the entire game history
 	//Accumulative  stats and skills
@@ -50,13 +52,21 @@ var Player = {
 	//Achievements and achievement progress
 	
 	
+	//Flag to let the engine know the player is starting a hack
+	startHack: false,
+	finishHack : false,
+	hackingTime: 0,
+	
+	
 	init: function() {
+		/* Initialize properties of Player's home computer */
 		Player.homeComputer.init("19.42.93.219", "home", "Home PC", true, true, true, true, 1);
 		Player.currentServer = Player.homeComputer;
         
         var NetworkGroup1 = [ForeignServers.IronGym, ForeignServers.FoodNStuff, ForeignServers.SigmaCosmetics, ForeignServers.JoesGuns, ForeignServers.HongFangTeaHouse, ForeignServers.HaraKiriSushiBar];
         for (var i = 0; i < NetworkGroup1.length; i++) {
             Player.homeComputer.serversOnNetwork.push(NetworkGroup1[i]);
+			NetworkGroup1[i].serversOnNetwork.push(Player.homeComputer);
         }
 	},
 	
@@ -65,5 +75,48 @@ var Player = {
 	// 	Gets to level 1000 hacking skill at ~1,100,000,000 exp
 	calculateHackingSkill: function(exp) {
 		return Math.max(Math.floor(50 * log(9007199254740991+ 2.270) - 40), 1);
+	},
+	
+	//Calculates the chance of hacking a server
+	//The formula is:
+	//	(hacking_chance_multiplier * hacking_skill - requiredLevel)      100 - difficulty		
+	//  -----------------------------------------------------------  *  -----------------
+	//    hacking_chance_multiplier * hacking_skill)                          100
+	calculateHackingChance: function() {
+		var difficultyMult = (100 - Player.currentServer.hackDifficulty) / 100;
+		var skillMult = (Player.hacking_chance_multiplier * Player.hacking_skill);
+		var skillChance = (skillMult - Player.currentServer.requiredHackingSkill) / skillMult;
+		return (skillChance * difficultyMult);
+	},
+	
+	//Calculate the time it takes to hack a server in seconds. Returns the time
+	//The formula is:
+	//	  (requiredLevel * difficulty)       	
+	//  -------------------------------  *  hacking_speed_multiplier
+	//        hacking_skill                           
+	calculateHackingTime: function() {
+		var difficultyMult = Player.currentServer.requiredHackingSkill * Player.currentServer.difficulty;
+		var skillFactor = difficultyMult / Player.hacking_skill;
+		return skillFactor * Player.hacking_speed_multiplier;
+	},
+	
+	//Hack a server. Return the amount of money hacked.
+	//This assumes that the server being hacked is not purchased by the palyer, that the player's hacking skill is greater than the
+	//required hacking skill and that the player has admin rights.
+	hack: function(hackingSkill) {
+		Player.hackingTime = Player.calculateHackingTime();
+		console.log("Hacking time: " + Player.hackingTime);
+		//Set the startHack flag so the engine starts the hacking process
+		Player.startHack = true;
+		
+		while (Player.finishHack == false) {
+			//Waiting for hack to complete
+		}
+		Player.finishHack = false;
+		
+		//DEBUG
+		return 5;
 	}
+	
+
 };
\ No newline at end of file
diff --git a/src/Server.js b/src/Server.js
index debd7486d..f004f0405 100644
--- a/src/Server.js
+++ b/src/Server.js
@@ -37,12 +37,17 @@ function Server() {
 	this.serverGrowth			= 1;	//Affects how the moneyAvailable increases (1-100)
 	this.timesHacked 			= 0;
 	
-	//Manual hack state information (hacking done without script)
-	this.isHacking				= false;
-	this.hackingProgress		= 0;	
-	
 	//All servers reachable from this one (what shows up if you run scan/netstat)
 	this.serversOnNetwork		= [];
+	
+	//Port information, required for porthacking servers to get admin rights
+	this.numOpenPortsRequired 	= 5;
+	this.sshPortOpen 			= false;	//Port 22
+	this.ftpPortOpen 			= false;	//Port 21
+	this.smtpPortOpen 			= false;	//Port 25
+	this.httpPortOpen			= false;	//Port 80
+	this.sqlPortOpen 			= false; 	//Port 1433
+	this.openPortCount 			= 0;
 };
 
 //Initialize the properties of a server
@@ -57,14 +62,6 @@ Server.prototype.init = function(ip, hostname, organizationName, onlineStatus, i
 	this.maxRam = maxRam;
 }
 
-//Hack a server. Return the amount of money hacked.
-//This assumes that the server being hacked is not purchased by the palyer, that the player's hacking skill is greater than the
-//required hacking skill and that the player has admin rights.
-Server.prototype.hack = function(hackingSkill) {
-	
-	
-}
-
 //Set the hacking properties of a server
 Server.prototype.setHackingParameters = function(requiredHackingSkill, moneyAvailable, hackDifficulty, serverGrowth) {
 	this.requiredHackingSkill = requiredHackingSkill;
@@ -73,6 +70,12 @@ Server.prototype.setHackingParameters = function(requiredHackingSkill, moneyAvai
 	this.serverGrowth = serverGrowth;
 }
 
+//Set the port properties of a server
+//Right now its only the number of open ports needed to PortHack the server. 
+Server.prototype.setPortProperties = function(numOpenPortsReq) {
+	this.numOpenPortsRequired = numOpenPortsReq;
+}
+
 //Generate a random IP address. Used for the foreign servers
 createRandomIp = function() {
 	var ip = createRandomByte() +'.' +
@@ -168,189 +171,249 @@ ForeignServers = {
 		//MegaCorporations
 		ForeignServers.ECorp.init(createRandomIp(), "ecorp", "ECorp", true, false, false, false, 512);
 		ForeignServers.ECorp.setHackingParameters(900, 100000000000, 99, 99);
+		ForeignServers.ECorp.setPortProperties(5);
 		
 		ForeignServers.MegaCorp.init(createRandomIp(), "megacorp", "MegaCorp", true, false, false, false, 512);
 		ForeignServers.MegaCorp.setHackingParameters(900, 80000000000, 99, 99);
+		ForeignServers.MegaCorp.setPortProperties(5);
 		
 		ForeignServers.BachmanAndAssociates.init(createRandomIp(), "b-and-a", "Bachman & Associates", true, false, false, false, 480);
 		ForeignServers.BachmanAndAssociates.setHackingParameters(900, 32000000000, 80, 70);
+		ForeignServers.BachmanAndAssociates.setPortProperties(5);
 		
 		ForeignServers.BladeIndustries.init(createRandomIp(), "blade", "Blade Industries", true, false, false, false, 480);
 		ForeignServers.BladeIndustries.setHackingParameters(900, 20000000000, 90, 65);
+		ForeignServers.BladeIndustries.setPortProperties(5);
 		
 		ForeignServers.NWO.init(createRandomIp(), "nwo", "New World Order", true, false, false, false, 512);
 		ForeignServers.NWO.setHackingParameters(900, 40000000000, 99, 80);
+		ForeignServers.NWO.setPortProperties(5);
 		
 		ForeignServers.ClarkeIncorporated.init(createRandomIp(), "clarkeinc", "Clarke Incorporated", true, false, false, false, 448);
 		ForeignServers.ClarkeIncorporated.setHackingParameters(900, 15000000000, 50, 60);
+		ForeignServers.ClarkeIncorporated.setPortProperties(5);
 		
 		ForeignServers.OmniTekIncorporated.init(createRandomIp(), "omnitek", "OmniTek Incorporated", true, false, false, false, 1024);
 		ForeignServers.OmniTekIncorporated.setHackingParameters(900, 50000000000, 95, 99);
+		ForeignServers.OmniTekIncorporated.setPortProperties(5);
 		
 		ForeignServers.FourSigma.init(createRandomIp(), "4sigma", "FourSigma", true, false, false, false, 448);
 		ForeignServers.FourSigma.setHackingParameters(900, 25000000000, 60, 80);
+		ForeignServers.FourSigma.setPortProperties(5);
 		
 		ForeignServers.KuaiGongInternational.init(createRandomIp(), "kuai-gong", "KuaiGong International", true, false, false, false, 512);
 		ForeignServers.KuaiGongInternational.setHackingParameters(925, 75000000000, 99, 99);
+		ForeignServers.KuaiGongInternational.setPortProperties(5);
 		
 		//Technology and communications companies (large targets)
 		ForeignServers.FulcrumTechnologies.init(createRandomIp(), "fulcrumtech", "Fulcrum Technologies", true, false, false, false, 512);
 		ForeignServers.FulcrumTechnologies.setHackingParameters(900, 2000000000, 90, 85);
+		ForeignServers.FulcrumTechnologies.setPortProperties(5);
 		
 		ForeignServers.FulcrumSecretTechnologies.init(createRandomIp(), "fulcrumassets", "Fulcrum Technologies Assets", true, false, false, false, 1024);
 		ForeignServers.FulcrumSecretTechnologies.setHackingParameters(999, 1000000, 99, 1);
+		ForeignServers.FulcrumSecretTechnologies.setPortProperties(5);
 		
 		ForeignServers.StormTechnologies.init(createRandomIp(), "stormtech", "Storm Technologies", true, false, false, false, 256);
 		ForeignServers.StormTechnologies.setHackingParameters(850, 1500000000, 85, 80);
+		ForeignServers.StormTechnologies.setPortProperties(5);
 		
 		ForeignServers.DefComm.init(createRandomIp(), "defcomm", "DefComm", true, false, false, false, 256);
 		ForeignServers.DefComm.setHackingParameters(825, 900000000, 90, 60);
+		ForeignServers.DefComm.setPortProperties(5);
 		
 		ForeignServers.InfoComm.init(createRandomIp(), "infocomm", "InfoComm", true, false, false, false, 256);
 		ForeignServers.InfoComm.setHackingParameters(830, 750000000, 80, 50);
+		ForeignServers.InfoComm.setPortProperties(5);
 		
 		ForeignServers.HeliosLabs.init(createRandomIp(), "helios", "Helios Labs", true, false, false, false, 288);
 		ForeignServers.HeliosLabs.setHackingParameters(800, 500000000, 90, 75);
+		ForeignServers.HeliosLabs.setPortProperties(5);
 		
 		ForeignServers.VitaLife.init(createRandomIp(), "vitalife", "VitaLife", true, false, false, false, 224);
 		ForeignServers.VitaLife.setHackingParameters(775, 800000000, 85, 70);
+		ForeignServers.VitaLife.setPortProperties(5);
 		
 		ForeignServers.IcarusMicrosystems.init(createRandomIp(), "icarus", "Icarus Microsystems", true, false, false, false, 256);
 		ForeignServers.IcarusMicrosystems.setHackingParameters(810, 1100000000, 90, 90);
+		ForeignServers.IcarusMicrosystems.setPortProperties(5);
 		
 		ForeignServers.UniversalEnergy.init(createRandomIp(), "univ-energy", "Universal Energy", true, false, false, false, 256);
 		ForeignServers.UniversalEnergy.setHackingParameters(790, 1500000000, 85, 85);
+		ForeignServers.UniversalEnergy.setPortProperties(4);
 		
 		ForeignServers.TitanLabs.init(createRandomIp(), "titan-labs", "Titan Laboratories", true, false, false, false, 256);
 		ForeignServers.TitanLabs.setHackingParameters(795, 1000000000, 75, 70);
+		ForeignServers.TitanLabs.setPortProperties(5);
 		
 		ForeignServers.MicrodyneTechnologies.init(createRandomIp(), "microdyne", "Microdyne Technologies", true, false, false, false, 288);
 		ForeignServers.MicrodyneTechnologies.setHackingParameters(800, 900000000, 70, 80);
+		ForeignServers.MicrodyneTechnologies.setPortProperties(5);
 		
 		ForeignServers.TaiYangDigital.init(createRandomIp(), "taiyang-digital", "Taiyang Digital", true, false, false, false, 256);
 		ForeignServers.TaiYangDigital.setHackingParameters(850, 1100000000, 75, 75);
+		ForeignServers.TaiYangDigital.setPortProperties(5);
 		
 	    ForeignServers.GalacticCybersystems.init(createRandomIp(), "galactic-cyber", "Galactic Cybersystems", true, false, false, false, 288);
 		ForeignServers.GalacticCybersystems.setHackingParameters(825, 500000000, 60, 80);
+		ForeignServers.GalacticCybersystems.setPortProperties(5);
 		
 		//Defense Companies ("Large" Companies)
 		ForeignServers.AeroCorp.init(createRandomIp(), "aerocorp", "AeroCorp", true, false, false, false, 320);
 		ForeignServers.AeroCorp.setHackingParameters(850, 1500000000, 85, 60);
+		ForeignServers.AeroCorp.setPortProperties(5);
 		
 		ForeignServers.OmniaCybersystems.init(createRandomIp(), "omnia", "Omnia Cybersystems", true, false, false, false, 320);
 		ForeignServers.OmniaCybersystems.setHackingParameters(825, 1200000000, 90, 65);
+		ForeignServers.OmniaCybersystems.setPortProperties(5);
 		
 		ForeignServers.ZBDefense.init(createRandomIp(), "zb-def", "ZB Defense Industries", true, false, false, false, 288);
 		ForeignServers.ZBDefense.setHackingParameters(800, 1000000000, 60, 70);
+		ForeignServers.ZBDefense.setPortProperties(4);
 		
 		ForeignServers.AppliedEnergetics.init(createRandomIp(), "applied-energetics", "Applied Energetics", true, false, false, false, 288);
 		ForeignServers.AppliedEnergetics.setHackingParameters(775, 1200000000, 70, 72);
+		ForeignServers.AppliedEnergetics.setPortProperties(4);
 		
 		ForeignServers.SolarisSpaceSystems.init(createRandomIp(), "solaris", "Solaris Space Systems", true, false, false, false, 288);
 		ForeignServers.SolarisSpaceSystems.setHackingParameters(800, 900000000, 75, 75);
+		ForeignServers.SolarisSpaceSystems.setPortProperties(5);
 		
 		ForeignServers.DeltaOne.init(createRandomIp(), "deltaone", "Delta One", true, false, false, false, 288);
 		ForeignServers.DeltaOne.setHackingParameters(810, 1500000000, 80, 60);
+		ForeignServers.DeltaOne.setPortProperties(5);
 		
 		//Health, medicine, pharmaceutical companies ("Large" targets)
 		ForeignServers.GlobalPharmaceuticals.init(createRandomIp(), "global-pharm", "Global Pharmaceuticals", true, false, false, false, 256);
 		ForeignServers.GlobalPharmaceuticals.setHackingParameters(775, 2000000000, 80, 85);
+		ForeignServers.GlobalPharmaceuticals.setPortProperties(4);
 		
 		ForeignServers.NovaMedical.init(createRandomIp(), "nova-med", "Nova Medical", true, false, false, false, 288);
 		ForeignServers.NovaMedical.setHackingParameters(800, 1500000000, 70, 75);
+		ForeignServers.NovaMedical.setPortProperties(4);
 		
 		ForeignServers.ZeusMedical.init(createRandomIp(), "zeud-med", "Zeus Medical", true, false, false, false, 320);
 		ForeignServers.ZeusMedical.setHackingParameters(810, 1750000000, 80, 75);
+		ForeignServers.ZeusMedical.setPortProperties(5);
 		
 		ForeignServers.UnitaLifeGroup.init(createRandomIp(), "unitalife", "UnitaLife Group", true, false, false, false, 288);
 		ForeignServers.UnitaLifeGroup.setHackingParameters(790, 1400000000, 75, 75);
+		ForeignServers.UnitaLifeGroup.setPortProperties(4);
 		
 		//"Medium level" targets
 		ForeignServers.LexoCorp.init(createRandomIp(), "lexo-corp", "Lexo Corporation", true, false, false, false, 256);
 		ForeignServers.LexoCorp.setHackingParameters(700, 1000000000, 70, 60);
+		ForeignServers.LexoCorp.setPortProperties(4);
 		
 		ForeignServers.RhoConstruction.init(createRandomIp(), "rho-construction", "Rho Construction", true, false, false, false, 128);
 		ForeignServers.RhoConstruction.setHackingParameters(500, 750000000, 50, 50);
+		ForeignServers.RhoConstruction.setPortProperties(3);
 		
 		ForeignServers.AlphaEnterprises.init(createRandomIp(), "alpha-ent", "Alpha Enterprises", true, false, false, false, 192);
 		ForeignServers.AlphaEnterprises.setHackingParameters(550, 800000000, 60, 55);
+		ForeignServers.AlphaEnterprises.setPortProperties(4);
 		
 		ForeignServers.RothmanUniversity.init(createRandomIp(), "rothman-uni", "Rothman University Network", true, false, false, false, 160);
 		ForeignServers.RothmanUniversity.setHackingParameters(400, 250000000, 50, 40);
+		ForeignServers.RothmanUniversity.setPortProperties(3);
 		
 		ForeignServers.ZBInstituteOfTechnology.init(createRandomIp(), "zb-institute", "ZB Institute of Technology Network", true, false, false, false, 256);
 		ForeignServers.ZBInstituteOfTechnology.setHackingParameters(750, 1000000000, 75, 80);
+		ForeignServers.ZBInstituteOfTechnology.setPortProperties(5);
 		
 		ForeignServers.SummitUniversity.init(createRandomIp(), "summit-uni", "Summit University Network", true, false, false, false, 128);
 		ForeignServers.SummitUniversity.setHackingParameters(450, 200000000, 55, 50);
+		ForeignServers.SummitUniversity.setPortProperties(3);
 		
 		ForeignServers.SysCoreSecurities.init(createRandomIp(), "syscore", "SysCore Securities", true, false, false, false, 192);
 		ForeignServers.SysCoreSecurities.setHackingParameters(600, 600000000, 70, 65);
+		ForeignServers.SysCoreSecurities.setPortProperties(4);
 		
 		ForeignServers.CatalystVentures.init(createRandomIp(), "catalyst", "Catalyst Ventures", true, false, false, false, 160);
 		ForeignServers.CatalystVentures.setHackingParameters(425, 900000000, 65, 40);
+		ForeignServers.CatalystVentures.setPortProperties(3);
 		
 		ForeignServers.TheHub.init(createRandomIp(), "the-hub", "The Hub", true, false, false, false, 128);
 		ForeignServers.TheHub.setHackingParameters(300, 250000000, 40, 50);
+		ForeignServers.TheHub.setPortProperties(2);
 		
 		ForeignServers.CompuTek.init(createRandomIp(), "comptek", "CompuTek", true, false, false, false, 192);
 		ForeignServers.CompuTek.setHackingParameters(350, 300000000, 60, 55);
+		ForeignServers.CompuTek.setPortProperties(3);
 
 		ForeignServers.NetLinkTechnologies.init(createRandomIp(), "netlink", "NetLink Technologies", true, false, false, false, 192);
 		ForeignServers.NetLinkTechnologies.setHackingParameters(400, 350000000, 70, 60);
+		ForeignServers.NetLinkTechnologies.setPortProperties(3);
 		
 		//"Low level" targets
 		ForeignServers.FoodNStuff.init(createRandomIp(), "foodnstuff", "Food N Stuff Supermarket", true, false, false, false, 8);
 		ForeignServers.FoodNStuff.setHackingParameters(1, 1000000, 10, 20);
+		ForeignServers.FoodNStuff.setPortProperties(0);
 		
 		ForeignServers.SigmaCosmetics.init(createRandomIp(), "sigma-cosmetics", "Sigma Cosmetics", true, false, false, false, 16);
 		ForeignServers.SigmaCosmetics.setHackingParameters(5, 500000, 5, 10);
+		ForeignServers.SigmaCosmetics.setPortProperties(0);
 		
 		ForeignServers.JoesGuns.init(createRandomIp(), "joesguns", "Joe's Guns", true, false, false, false, 16);
 		ForeignServers.JoesGuns.setHackingParameters(10, 200000, 20, 20);
+		ForeignServers.JoesGuns.setPortProperties(0);
 		
 		ForeignServers.Zer0Nightclub.init(createRandomIp(), "zer0", "ZER0 Nightclub", true, false, false, false, 32);
 		ForeignServers.Zer0Nightclub.setHackingParameters(50, 750000, 25, 40);
+		ForeignServers.Zer0Nightclub.setPortProperties(1);
 		
 		ForeignServers.NectarNightclub.init(createRandomIp(), "nectar-net", "Nectar Nightclub Network", true, false, false, false, 16);
 		ForeignServers.NectarNightclub.setHackingParameters(25, 400000, 20, 25);
+		ForeignServers.NectarNightclub.setPortProperties(0);
 		
 		ForeignServers.NeoNightclub.init(createRandomIp(), "neo-net", "Neo Nightclub Network", true, false, false, false, 32);
 		ForeignServers.NeoNightclub.setHackingParameters(75, 500000, 25, 25);
+		ForeignServers.NeoNightclub.setPortProperties(1);
 		
 		ForeignServers.SilverHelix.init(createRandomIp(), "silver-helix", "Silver Helix", true, false, false, false, 16);
 		ForeignServers.SilverHelix.setHackingParameters(150, 1000000, 30, 30);
+		ForeignServers.SilverHelix.setPortProperties(2);
 		
 		ForeignServers.HongFangTeaHouse.init(createRandomIp(), "hong-fang-tea", "HongFang Teahouse", true, false, false, false, 16);
 		ForeignServers.HongFangTeaHouse.setHackingParameters(60, 250000, 15, 10);
+		ForeignServers.HongFangTeaHouse.setPortProperties(1);
 		
 		ForeignServers.HaraKiriSushiBar.init(createRandomIp(), "harakiri-sushi", "HaraKiri Sushi Bar Network", true, false, false, false, 8);
-		ForeignServers.HaraKiriSushiBar.setHackingParameters(50, 100000, 10, 40);
+		ForeignServers.HaraKiriSushiBar.setHackingParameters(50, 100000, 15, 40);
+		ForeignServers.HaraKiriSushiBar.setPortProperties(1);
 		
 		ForeignServers.Phantasy.init(createRandomIp(), "phantasy", "Phantasy Club", true, false, false, false, 16);
 		ForeignServers.Phantasy.setHackingParameters(100, 300000, 20, 35);
+		ForeignServers.Phantasy.setPortProperties(2);
 		
 		ForeignServers.MaxHardware.init(createRandomIp(), "max-hardware", "Max Hardware Store", true, false, false, false, 16);
 		ForeignServers.MaxHardware.setHackingParameters(80, 150000, 15, 10);
+		ForeignServers.MaxHardware.setPortProperties(1);
 		
 		ForeignServers.OmegaSoftware.init(createRandomIp(), "omega-net", "Omega Software", true, false, false, false, 64);
 		ForeignServers.OmegaSoftware.setHackingParameters(200, 1000000, 30, 30);
+		ForeignServers.OmegaSoftware.setPortProperties(2);
 
 		//Gyms
 		ForeignServers.CrushFitnessGym.init(createRandomIp(), "crush-fitness", "Crush Fitness", true, false, false, false, 8);
 		ForeignServers.CrushFitnessGym.setHackingParameters(250, 500000, 40, 25);
+		ForeignServers.CrushFitnessGym.setPortProperties(2);
 		
 		ForeignServers.IronGym.init(createRandomIp(), "iron-gym", "Iron Gym Network", true, false, false, false, 8);
 		ForeignServers.IronGym.setHackingParameters(100, 250000, 30, 15);
+		ForeignServers.IronGym.setPortProperties(1);
 		
 		ForeignServers.MilleniumFitnessGym.init(createRandomIp(), "millenium-fitness", "Millenium Fitness Network", true, false, false, false, 16);
 		ForeignServers.MilleniumFitnessGym.setHackingParameters(500, 600000, 50, 30);
+		ForeignServers.MilleniumFitnessGym.setPortProperties(3);
 		
 		ForeignServers.PowerhouseGym.init(createRandomIp(), "powerhouse-fitness", "Powerhouse Fitness", true, false, false, false, 16);
 		ForeignServers.PowerhouseGym.setHackingParameters(1000, 2000000, 60, 50);
+		ForeignServers.PowerhouseGym.setPortProperties(5);
 
 		ForeignServers.SnapFitnessGym.init(createRandomIp(), "snap-fitness", "Snap Fitness", true, false, false, false, 16);
 		ForeignServers.SnapFitnessGym.setHackingParameters(750, 1000000, 50, 45);
+		ForeignServers.SnapFitnessGym.setPortProperties(4);
         
         ForeignServers.createNetwork();
 	},
@@ -381,86 +444,86 @@ ForeignServers = {
         
         for (var i = 0; i < NetworkGroup2.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup1[Math.floor(Math.random() * NetworkGroup1.length)];
-            NetworkGroup2[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup2[i];
+            NetworkGroup2[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup2[i]);
         }
         
         for (var i = 0; i < NetworkGroup3.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup2[Math.floor(Math.random() * NetworkGroup2.length)];
-            NetworkGroup3[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup3[i];
+            NetworkGroup3[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup3[i]);
         }
         
         for (var i = 0; i < NetworkGroup4.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup3[Math.floor(Math.random() * NetworkGroup3.length)];
-            NetworkGroup4[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup4[i];
+            NetworkGroup4[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup4[i]);
         }
         
         for (var i = 0; i < NetworkGroup5.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup4[Math.floor(Math.random() * NetworkGroup4.length)];
-            NetworkGroup5[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup5[i];
+            NetworkGroup5[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup5[i]);
         }
         
         for (var i = 0; i < NetworkGroup6.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup5[Math.floor(Math.random() * NetworkGroup5.length)];
-            NetworkGroup6[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup6[i];
+            NetworkGroup6[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup6[i]);
         }
         
         for (var i = 0; i < NetworkGroup7.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup6[Math.floor(Math.random() * NetworkGroup6.length)];
-            NetworkGroup7[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup7[i];
+            NetworkGroup7[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup7[i]);
         }
         
         for (var i = 0; i < NetworkGroup8.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup7[Math.floor(Math.random() * NetworkGroup7.length)];
-            NetworkGroup8[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup8[i];
+            NetworkGroup8[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup8[i]);
         }
         
         for (var i = 0; i < NetworkGroup9.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup8[Math.floor(Math.random() * NetworkGroup8.length)];
-            NetworkGroup9[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup9[i];
+            NetworkGroup9[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup9[i]);
         }
         
         for (var i = 0; i < NetworkGroup10.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup9[Math.floor(Math.random() * NetworkGroup9.length)];
-            NetworkGroup10[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup10[i];
+            NetworkGroup10[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup10[i]);
         }
         
         for (var i = 0; i < NetworkGroup11.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup10[Math.floor(Math.random() * NetworkGroup10.length)];
-            NetworkGroup11[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup11[i];
+            NetworkGroup11[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup11[i]);
         }
         
         for (var i = 0; i < NetworkGroup12.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup11[Math.floor(Math.random() * NetworkGroup11.length)];
-            NetworkGroup12[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup12[i];
+            NetworkGroup12[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup12[i]);
         }
         
         for (var i = 0; i < NetworkGroup13.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup12[Math.floor(Math.random() * NetworkGroup12.length)];
-            NetworkGroup13[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup13[i];
+            NetworkGroup13[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup13[i]);
         }
         
         for (var i = 0; i < NetworkGroup14.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup13[Math.floor(Math.random() * NetworkGroup13.length)];
-            NetworkGroup14[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup14[i];
+            NetworkGroup14[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup14[i]);
         }
         
         for (var i = 0; i < NetworkGroup15.length; i++) {
             var randomServerFromPrevGroup = NetworkGroup14[Math.floor(Math.random() * NetworkGroup14.length)];
-            NetworkGroup15[i].serversOnNetwork = randomServerFromPrevGroup;
-            randomServerFromPrevGroup.serversOnNetwork = NetworkGroup15[i];
+            NetworkGroup15[i].serversOnNetwork.push(randomServerFromPrevGroup);
+            randomServerFromPrevGroup.serversOnNetwork.push(NetworkGroup15[i]);
         }
     }
 }
diff --git a/src/Terminal.js b/src/Terminal.js
index 58df8dc0b..1d3a3cc86 100644
--- a/src/Terminal.js
+++ b/src/Terminal.js
@@ -44,7 +44,7 @@ var Terminal = {
             case "telnet":
 				//Disconnect from current server in terminal and connect to new one..maybe rename this to telnet?
                 if (commandArray.length != 2) {
-                    post("Incorrect usage of connect/telnet command. Usage: connect/telnet [ip]");
+                    post("Incorrect usage of connect/telnet command. Usage: connect/telnet [ip/hostname]");
                     return;
                 }
                 
@@ -75,9 +75,11 @@ var Terminal = {
 				} else if (Player.currentServer.hasAdminRights == false ) {
 					post("You do not have admin rights for this machine! Cannot hack");
 				} else if (Player.currentServer.requiredHackingSkill > Player.hacking_skill) {
-					post("Your hacking skill is not high enough to attempt hacking this machine");
+					post("Your hacking skill is not high enough to attempt hacking this machine. Try analyzing the machine to determine the required hacking skill");
 				} else {
-					var hackResult = Player.currentServer.hack();
+					post("<p id='hacking-progress'> Time left: </p>");
+					post("<p id='hacking-progress-bar'> | </p>");
+					var hackResult = Player.hack();
 				}
 				break;
 			case "help":
@@ -116,7 +118,22 @@ var Terminal = {
 				//TODO
 				break;
 			case "run":
-				//TODO
+				//Run a program or a script
+				if (commandArray.length == 1) {
+					post("No program specified to run. Usage: run [program/script]");
+				} else if (commandArray.length > 2) {
+					post("Too many arguments. Usage: run [program/script]");
+				} else {
+					var executableName = commandArray[1];
+					//Check if its a script or just a program/executable 
+					if (executableName.indexOf(".script") == -1) {
+						//Not a script
+						Terminal.runProgram(executableName);
+					} else {
+						//Script
+						Terminal.runScript(executableName);
+					}
+				}
 				break;
 			case "scp":
 				//TODO
@@ -127,7 +144,25 @@ var Terminal = {
 	},
 	
 	runProgram: function(programName) {
+		switch (programName) {
+			case "PortHack":
+				console.log("Running PortHack executable");
+				if (Player.currentServer.openPortCount >= Player.currentServer.numOpenPortsRequired) {
+					Player.currentServer.hasAdminRights = true;
+					post("PortHack successful! Gained root access to " + Player.currentServer.hostname);
+					//TODO Make this take time rather than be instant
+				} else {
+					post("PortHack unsuccessful. Not enough ports have been opened");
+				}
+				break;
+			default:
+				post("Executable not found");
+				return;
+		}
+	},
 	
+	runScript: function(scriptName) {
+		
 	}
 };
 
diff --git a/src/engine.js b/src/engine.js
index e52beee23..6b969f8e1 100644
--- a/src/engine.js
+++ b/src/engine.js
@@ -131,6 +131,18 @@ var Engine = {
         
         var idleTime = Engine._idleSpeed - timeDifference;
         
+		//Manual hack
+		if (Player.startHack = true) {
+			Engine._totalHackTime = Player.hackingTime;
+			Engine._hackTimeLeft = Player.hackingTime;
+			Engine._manualHackInProgress = true;
+			Engine._hackProgressBarCount = 0;
+			Engine._hackProgressStr = "[";
+			Engine._hackTimeStr = "Time left: ";
+			Player.startHack = false;
+		}
+		
+		Engine.updateHackProgress();
 		
 		// Once that entire "while loop" has run, we call the IdleTimer 
 		// function again, but this time with a timeout (delay) of 
@@ -139,6 +151,38 @@ var Engine = {
 		
 	},
 	
+	/* Calculates the hack progress for a manual (non-scripted) hack and updates the progress bar/time accordingly */
+	_totalHackTime: 0,
+	_hackTimeLeft: 0,
+	_hackTimeStr: "Time left: ",
+	_hackProgressStr: "[",
+	_hackProgressBarCount: 0,
+	_manualHackInProgress: false,
+	updateHackProgress: function() {
+		if (Engine.manualHackInProgress) {
+			Engine._hackTimeLeft -= (_idleSpeed/ 1000);	//Substract idle speed (ms)
+		
+			//Calculate percent filled 
+			var percent = Math.floor((1 - Engine._hackTimeLeft / Engine.totalhackTime) * 100);
+			
+			//Update progress bar 
+			if (Engine._hackProgressBarCount * 2 < percent) {
+				Engine._hackProgressStr += '|';
+				$('#hacking-progress-bar').html(Engine._hackProgressStr);
+			}
+			
+			//Update hack time remaining
+			Engine._hackTimeStr = "Time left: " + Engine._hackTimeLeft.asString();
+			$('#hacking-progress').html(Engine._hackTimeStr);
+			
+			//Once percent is 100, the hack is completed
+			if (percent == 100) {
+				Engine.manualHackInProgress = false;
+				Player.finishHack = true;
+			}
+		}
+	},
+	
     /* Initialization */
 	init: function() {
 		//Initialize Player objects