/**
* @param {NS} ns
* @returns interactive server map
*/
export function main(ns) {
const factionServers = ["CSEC", "avmnite-02h", "I.I.I.I", "run4theh111z", "w0r1d_d43m0n", "fulcrumassets"],
css = ` `,
doc = eval("document"),
terminalInsert = html => doc.getElementById("terminal").insertAdjacentHTML('beforeend', `
${html}`),
terminalInput = doc.getElementById("terminal-input"),
terminalEventHandlerKey = Object.keys(terminalInput)[1],
setNavCommand = async inputValue => {
terminalInput.value = inputValue
terminalInput[terminalEventHandlerKey].onChange({ target: terminalInput })
terminalInput.focus()
await terminalInput[terminalEventHandlerKey].onKeyDown({ key: 'Enter', preventDefault: () => 0 })
},
myHackLevel = ns.getHackingLevel(),
serverInfo = (serverName) => {
// Costs 2 GB. If you can't don't need backdoor links, uncomment and use the alternate implementations below
return ns.getServer(serverName)
/* return {
requiredHackingSkill: ns.getServerRequiredHackingLevel(serverName),
hasAdminRights: ns.hasRootAccess(serverName),
purchasedByPlayer: serverName.includes('daemon') || serverName.includes('hacknet'),
backdoorInstalled: true // No way of knowing without ns.getServer
} */
},
createServerEntry = serverName => {
let server = serverInfo(serverName),
requiredHackLevel = server.requiredHackingSkill,
rooted = server.hasAdminRights,
canHack = requiredHackLevel <= myHackLevel,
shouldBackdoor = !server?.backdoorInstalled && canHack && serverName != 'home' && rooted && !server.purchasedByPlayer,
contracts = ns.ls(serverName, ".cct")
return ``
+ `${serverName}`
+ (server.purchasedByPlayer ? '' : ` (${requiredHackLevel})`)
+ `${((canHack && !rooted) || shouldBackdoor ? ' [backdoor]' : '')}`
+ ` ${contracts.map(c => `@`)}`
+ ""
},
buildOutput = (parent = servers[0], prefix = ["\n"]) => {
let output = prefix.join("") + createServerEntry(parent)
for (let i = 0; i < servers.length; i++) {
if (parentByIndex[i] != parent) continue
let newPrefix = prefix.slice()
const appearsAgain = parentByIndex.slice(i + 1).includes(parentByIndex[i]),
lastElementIndex = newPrefix.length - 1
newPrefix.push(appearsAgain ? "├╴" : "└╴")
newPrefix[lastElementIndex] = newPrefix[lastElementIndex].replace("├╴", "│ ").replace("└╴", " ")
output += buildOutput(servers[i], newPrefix)
}
return output
},
ordering = (serverA, serverB) => {
// Sort servers with fewer connections towards the top.
let orderNumber = ns.scan(serverA).length - ns.scan(serverB).length
// Purchased servers to the very top
orderNumber = orderNumber != 0 ? orderNumber
: serverInfo(serverB).purchasedByPlayer - serverInfo(serverA).purchasedByPlayer
// Hack: compare just the first 2 chars to keep purchased servers in order purchased
orderNumber = orderNumber != 0 ? orderNumber
: serverA.slice(0, 2).toLowerCase().localeCompare(serverB.slice(0, 2).toLowerCase())
return orderNumber
}
// refresh css (in case it changed)
doc.getElementById("scanCSS")?.remove()
doc.head.insertAdjacentHTML('beforeend', css)
let servers = ["home"],
parentByIndex = [""],
routes = { home: "home" }
for (let server of servers)
for (let oneScanResult of ns.scan(server).sort(ordering))
if (!servers.includes(oneScanResult)) {
const backdoored = serverInfo(oneScanResult)?.backdoorInstalled
servers.push(oneScanResult)
parentByIndex.push(server)
routes[oneScanResult] = backdoored ? "connect " + oneScanResult : routes[server] + ";connect " + oneScanResult
}
terminalInsert(`${buildOutput()}
`)
doc.querySelectorAll(".serverscan.new .server").forEach(serverEntry => serverEntry
.addEventListener('click', setNavCommand.bind(null, routes[serverEntry.childNodes[0].nodeValue])))
doc.querySelectorAll(".serverscan.new .monitor").forEach(monitorButton => monitorButton
.addEventListener('click', setNavCommand.bind(null, "run monitor.js " + monitorButton.parentNode.childNodes[0].childNodes[0].nodeValue)))
doc.querySelectorAll(".serverscan.new .backdoor").forEach(backdoorButton => backdoorButton
.addEventListener('click', setNavCommand.bind(null, routes[backdoorButton.parentNode.childNodes[0].childNodes[0].nodeValue] + ";run brutessh.exe;run httpworm.exe;run sqlinject.exe;run ftpcrack.exe;run relaysmtp.exe;run nuke.exe;backdoor")))
doc.querySelector(".serverscan.new").classList.remove("new")
}