Compare commits
5 Commits
c65b7b6796
...
d851d84c05
Author | SHA1 | Date |
---|---|---|
Massaki Archambault | d851d84c05 | |
Tanimodori | ad0171f878 | |
Tanimodori | fd2a6f6222 | |
Tanimodori | a5f4ca5db8 | |
Tanimodori | d0dceaab8f |
|
@ -1,40 +1,149 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
|
||||
# Dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# Testing
|
||||
/coverage
|
||||
|
||||
# Distributable
|
||||
/build
|
||||
/dist/
|
||||
/playground/dist/
|
||||
|
||||
# Misc
|
||||
# MacOS folder attributes
|
||||
.DS_Store
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
# Netscript definitions is pulled automatically
|
||||
NetScriptDefinitions.d.ts
|
||||
|
||||
# Node
|
||||
# https://github.com/github/gitignore/blob/main/Node.gitignore
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Netscript definitions is pulled automatically
|
||||
NetScriptDefinitions.d.ts
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -9,16 +9,16 @@
|
|||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.49.0",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"@types/node": "^18.15.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
||||
"@typescript-eslint/parser": "^5.55.0",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-prettier": "^8.7.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"prettier": "^2.8.3",
|
||||
"typescript": "^4.9.4",
|
||||
"vite": "^4.0.4",
|
||||
"viteburner": "^0.5.1"
|
||||
"prettier": "^2.8.4",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.1.4",
|
||||
"viteburner": "^0.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@antfu/utils": {
|
||||
|
@ -382,15 +382,39 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz",
|
||||
"integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/regexpp": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz",
|
||||
"integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
|
||||
"integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz",
|
||||
"integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.4",
|
||||
"debug": "^4.3.2",
|
||||
"espree": "^9.4.0",
|
||||
"espree": "^9.5.0",
|
||||
"globals": "^13.19.0",
|
||||
"ignore": "^5.2.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
|
@ -405,6 +429,15 @@
|
|||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz",
|
||||
"integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
|
||||
|
@ -486,9 +519,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
|
||||
"version": "18.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
|
||||
"integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
|
@ -498,18 +531,19 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
|
||||
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz",
|
||||
"integrity": "sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/type-utils": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.55.0",
|
||||
"@typescript-eslint/type-utils": "5.55.0",
|
||||
"@typescript-eslint/utils": "5.55.0",
|
||||
"debug": "^4.3.4",
|
||||
"grapheme-splitter": "^1.0.4",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
"regexpp": "^3.2.0",
|
||||
"semver": "^7.3.7",
|
||||
"tsutils": "^3.21.0"
|
||||
},
|
||||
|
@ -531,14 +565,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
|
||||
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz",
|
||||
"integrity": "sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/scope-manager": "5.55.0",
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"@typescript-eslint/typescript-estree": "5.55.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -558,13 +592,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
|
||||
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz",
|
||||
"integrity": "sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0"
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"@typescript-eslint/visitor-keys": "5.55.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
|
@ -575,13 +609,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
|
||||
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz",
|
||||
"integrity": "sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.55.0",
|
||||
"@typescript-eslint/utils": "5.55.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
},
|
||||
|
@ -602,9 +636,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
|
||||
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz",
|
||||
"integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
|
@ -615,13 +649,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
|
||||
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz",
|
||||
"integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0",
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"@typescript-eslint/visitor-keys": "5.55.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
|
@ -642,18 +676,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
|
||||
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.55.0.tgz",
|
||||
"integrity": "sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/scope-manager": "5.55.0",
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"@typescript-eslint/typescript-estree": "5.55.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"semver": "^7.3.7"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -668,12 +702,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
|
||||
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz",
|
||||
"integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -1024,12 +1058,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
|
||||
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz",
|
||||
"integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint/eslintrc": "^1.4.1",
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@eslint/eslintrc": "^2.0.1",
|
||||
"@eslint/js": "8.36.0",
|
||||
"@humanwhocodes/config-array": "^0.11.8",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
|
@ -1040,10 +1077,9 @@
|
|||
"doctrine": "^3.0.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"eslint-scope": "^7.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"eslint-visitor-keys": "^3.3.0",
|
||||
"espree": "^9.4.0",
|
||||
"esquery": "^1.4.0",
|
||||
"espree": "^9.5.0",
|
||||
"esquery": "^1.4.2",
|
||||
"esutils": "^2.0.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"file-entry-cache": "^6.0.1",
|
||||
|
@ -1064,7 +1100,6 @@
|
|||
"minimatch": "^3.1.2",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.9.1",
|
||||
"regexpp": "^3.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"strip-json-comments": "^3.1.0",
|
||||
"text-table": "^0.2.0"
|
||||
|
@ -1080,9 +1115,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint-config-prettier": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz",
|
||||
"integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==",
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz",
|
||||
"integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
|
@ -1125,33 +1160,6 @@
|
|||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
|
||||
"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mysticatea"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=5"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
|
||||
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-visitor-keys": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
|
||||
|
@ -1184,9 +1192,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
|
||||
"integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
|
||||
"version": "9.5.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz",
|
||||
"integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.8.0",
|
||||
|
@ -1201,9 +1209,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/esquery": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
|
||||
"integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
|
||||
"integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"estraverse": "^5.1.0"
|
||||
|
@ -1714,9 +1722,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
||||
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
|
||||
"integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.13"
|
||||
|
@ -1966,9 +1974,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
||||
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
|
||||
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
|
@ -2046,18 +2054,6 @@
|
|||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regexpp": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
|
||||
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mysticatea"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
|
@ -2320,9 +2316,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
@ -2356,15 +2352,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz",
|
||||
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.1.4.tgz",
|
||||
"integrity": "sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.16.3",
|
||||
"postcss": "^8.4.20",
|
||||
"esbuild": "^0.16.14",
|
||||
"postcss": "^8.4.21",
|
||||
"resolve": "^1.22.1",
|
||||
"rollup": "^3.7.0"
|
||||
"rollup": "^3.10.0"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
|
@ -2405,23 +2401,23 @@
|
|||
}
|
||||
},
|
||||
"node_modules/viteburner": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/viteburner/-/viteburner-0.5.1.tgz",
|
||||
"integrity": "sha512-LPXCFYU4HOF8LmBBKXiW1uL+8qrJJm3KtoGAsEgtMXhDnx8LQZMrLVl+hwQmYuIGl770SVaFJBXd4vpoH4JYbQ==",
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/viteburner/-/viteburner-0.5.3.tgz",
|
||||
"integrity": "sha512-93eSxQK3XyFF9t3JixEs0a2N6nbZkaOA1YAB7I86S1xm7qyFqcwv3oCLdNI5XzsPc/+Aue/26sHP3epbP5IEzA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.8.2",
|
||||
"cac": "^6.7.14",
|
||||
"chokidar": "^3.5.3",
|
||||
"fast-glob": "^3.2.12",
|
||||
"magic-string": "^0.27.0",
|
||||
"magic-string": "^0.30.0",
|
||||
"micromatch": "^4.0.5",
|
||||
"pathe": "^1.1.0",
|
||||
"picocolors": "^1.0.0",
|
||||
"prompts": "^2.4.2",
|
||||
"unconfig": "^0.3.7",
|
||||
"ws": "^8.12.0",
|
||||
"zod": "^3.19.1"
|
||||
"ws": "^8.12.1",
|
||||
"zod": "^3.20.6"
|
||||
},
|
||||
"bin": {
|
||||
"viteburner": "bin/viteburner.js"
|
||||
|
@ -2461,9 +2457,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
|
||||
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
||||
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
@ -2500,9 +2496,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.19.1",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.19.1.tgz",
|
||||
"integrity": "sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==",
|
||||
"version": "3.20.6",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.20.6.tgz",
|
||||
"integrity": "sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
|
@ -2670,15 +2666,30 @@
|
|||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@eslint-community/eslint-utils": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz",
|
||||
"integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"@eslint-community/regexpp": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz",
|
||||
"integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
|
||||
"integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz",
|
||||
"integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.12.4",
|
||||
"debug": "^4.3.2",
|
||||
"espree": "^9.4.0",
|
||||
"espree": "^9.5.0",
|
||||
"globals": "^13.19.0",
|
||||
"ignore": "^5.2.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
|
@ -2687,6 +2698,12 @@
|
|||
"strip-json-comments": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"@eslint/js": {
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz",
|
||||
"integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==",
|
||||
"dev": true
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
|
||||
|
@ -2749,9 +2766,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.11.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
|
||||
"version": "18.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
|
||||
"integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/semver": {
|
||||
|
@ -2761,70 +2778,71 @@
|
|||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
|
||||
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz",
|
||||
"integrity": "sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/type-utils": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.55.0",
|
||||
"@typescript-eslint/type-utils": "5.55.0",
|
||||
"@typescript-eslint/utils": "5.55.0",
|
||||
"debug": "^4.3.4",
|
||||
"grapheme-splitter": "^1.0.4",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
"regexpp": "^3.2.0",
|
||||
"semver": "^7.3.7",
|
||||
"tsutils": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
|
||||
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz",
|
||||
"integrity": "sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/scope-manager": "5.55.0",
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"@typescript-eslint/typescript-estree": "5.55.0",
|
||||
"debug": "^4.3.4"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
|
||||
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz",
|
||||
"integrity": "sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0"
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"@typescript-eslint/visitor-keys": "5.55.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/type-utils": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
|
||||
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz",
|
||||
"integrity": "sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.55.0",
|
||||
"@typescript-eslint/utils": "5.55.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
|
||||
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz",
|
||||
"integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
|
||||
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz",
|
||||
"integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0",
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"@typescript-eslint/visitor-keys": "5.55.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
|
@ -2833,28 +2851,28 @@
|
|||
}
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
|
||||
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.55.0.tgz",
|
||||
"integrity": "sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/scope-manager": "5.55.0",
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"@typescript-eslint/typescript-estree": "5.55.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"semver": "^7.3.7"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
|
||||
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
|
||||
"version": "5.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz",
|
||||
"integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/types": "5.55.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
}
|
||||
},
|
||||
|
@ -3108,12 +3126,15 @@
|
|||
"dev": true
|
||||
},
|
||||
"eslint": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
|
||||
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz",
|
||||
"integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint/eslintrc": "^1.4.1",
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@eslint/eslintrc": "^2.0.1",
|
||||
"@eslint/js": "8.36.0",
|
||||
"@humanwhocodes/config-array": "^0.11.8",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
|
@ -3124,10 +3145,9 @@
|
|||
"doctrine": "^3.0.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"eslint-scope": "^7.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"eslint-visitor-keys": "^3.3.0",
|
||||
"espree": "^9.4.0",
|
||||
"esquery": "^1.4.0",
|
||||
"espree": "^9.5.0",
|
||||
"esquery": "^1.4.2",
|
||||
"esutils": "^2.0.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"file-entry-cache": "^6.0.1",
|
||||
|
@ -3148,7 +3168,6 @@
|
|||
"minimatch": "^3.1.2",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.9.1",
|
||||
"regexpp": "^3.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"strip-json-comments": "^3.1.0",
|
||||
"text-table": "^0.2.0"
|
||||
|
@ -3173,9 +3192,9 @@
|
|||
}
|
||||
},
|
||||
"eslint-config-prettier": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz",
|
||||
"integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==",
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz",
|
||||
"integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
|
@ -3198,23 +3217,6 @@
|
|||
"estraverse": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"eslint-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
|
||||
"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eslint-visitor-keys": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
|
||||
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-visitor-keys": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
|
||||
|
@ -3222,9 +3224,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"espree": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
|
||||
"integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
|
||||
"version": "9.5.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz",
|
||||
"integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "^8.8.0",
|
||||
|
@ -3233,9 +3235,9 @@
|
|||
}
|
||||
},
|
||||
"esquery": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
|
||||
"integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
|
||||
"integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"estraverse": "^5.1.0"
|
||||
|
@ -3628,9 +3630,9 @@
|
|||
}
|
||||
},
|
||||
"magic-string": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
||||
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
|
||||
"integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.13"
|
||||
|
@ -3807,9 +3809,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
||||
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
|
||||
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
|
||||
"dev": true
|
||||
},
|
||||
"prettier-linter-helpers": {
|
||||
|
@ -3852,12 +3854,6 @@
|
|||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"regexpp": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
|
||||
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
|
@ -4026,9 +4022,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true
|
||||
},
|
||||
"unconfig": {
|
||||
|
@ -4052,36 +4048,36 @@
|
|||
}
|
||||
},
|
||||
"vite": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz",
|
||||
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.1.4.tgz",
|
||||
"integrity": "sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esbuild": "^0.16.3",
|
||||
"esbuild": "^0.16.14",
|
||||
"fsevents": "~2.3.2",
|
||||
"postcss": "^8.4.20",
|
||||
"postcss": "^8.4.21",
|
||||
"resolve": "^1.22.1",
|
||||
"rollup": "^3.7.0"
|
||||
"rollup": "^3.10.0"
|
||||
}
|
||||
},
|
||||
"viteburner": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/viteburner/-/viteburner-0.5.1.tgz",
|
||||
"integrity": "sha512-LPXCFYU4HOF8LmBBKXiW1uL+8qrJJm3KtoGAsEgtMXhDnx8LQZMrLVl+hwQmYuIGl770SVaFJBXd4vpoH4JYbQ==",
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/viteburner/-/viteburner-0.5.3.tgz",
|
||||
"integrity": "sha512-93eSxQK3XyFF9t3JixEs0a2N6nbZkaOA1YAB7I86S1xm7qyFqcwv3oCLdNI5XzsPc/+Aue/26sHP3epbP5IEzA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "^8.8.2",
|
||||
"cac": "^6.7.14",
|
||||
"chokidar": "^3.5.3",
|
||||
"fast-glob": "^3.2.12",
|
||||
"magic-string": "^0.27.0",
|
||||
"magic-string": "^0.30.0",
|
||||
"micromatch": "^4.0.5",
|
||||
"pathe": "^1.1.0",
|
||||
"picocolors": "^1.0.0",
|
||||
"prompts": "^2.4.2",
|
||||
"unconfig": "^0.3.7",
|
||||
"ws": "^8.12.0",
|
||||
"zod": "^3.19.1"
|
||||
"ws": "^8.12.1",
|
||||
"zod": "^3.20.6"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
|
@ -4106,9 +4102,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
|
||||
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
||||
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
|
@ -4125,9 +4121,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"zod": {
|
||||
"version": "3.19.1",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.19.1.tgz",
|
||||
"integrity": "sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==",
|
||||
"version": "3.20.6",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.20.6.tgz",
|
||||
"integrity": "sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
|
18
package.json
18
package.json
|
@ -6,16 +6,16 @@
|
|||
},
|
||||
"author": "Tanimodori",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.49.0",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"@types/node": "^18.15.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
||||
"@typescript-eslint/parser": "^5.55.0",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-prettier": "^8.7.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"prettier": "^2.8.3",
|
||||
"typescript": "^4.9.4",
|
||||
"vite": "^4.0.4",
|
||||
"viteburner": "^0.5.1"
|
||||
"prettier": "^2.8.4",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.1.4",
|
||||
"viteburner": "^0.5.3"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const moneyLowPercent = 0.75
|
||||
const securityHighOffset = 5
|
||||
|
||||
const moneyFormat = new Intl.NumberFormat({ style: "currency" })
|
||||
|
||||
const pwnMethods = []
|
||||
if (ns.fileExists("BruteSSH.exe")) {
|
||||
pwnMethods.push(ns.brutessh)
|
||||
}
|
||||
if (ns.fileExists("FTPCrack.exe")) {
|
||||
pwnMethods.push(ns.ftpcrack)
|
||||
}
|
||||
if (ns.fileExists("HTTPWorm.exe")) {
|
||||
pwnMethods.push(ns.httpworm)
|
||||
}
|
||||
if (ns.fileExists("relaySMTP.exe")) {
|
||||
pwnMethods.push(ns.relaysmtp)
|
||||
}
|
||||
if (ns.fileExists("SQLInject.exe")) {
|
||||
pwnMethods.push(ns.sqlinject)
|
||||
}
|
||||
|
||||
// ns.clearLog()
|
||||
// ns.tail()
|
||||
// ns.disableLog("getServerMaxRam")
|
||||
// ns.disableLog("scp")
|
||||
// ns.disableLog("killall")
|
||||
ns.print(`${pwnMethods.length} pwning method(s) available!`)
|
||||
|
||||
class Server {
|
||||
constructor(hostname) {
|
||||
this.hostname = hostname
|
||||
this.maxMoney = ns.getServerMaxMoney(hostname)
|
||||
this.minSecurityLevel = ns.getServerMinSecurityLevel(hostname)
|
||||
this.maxRam = ns.getServerMaxRam(hostname)
|
||||
this.requiredHackLevel = ns.getServerRequiredHackingLevel(hostname)
|
||||
this.hasRootAccess = ns.hasRootAccess(hostname)
|
||||
this.numPortRequired = ns.getServerNumPortsRequired(hostname)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.hostname
|
||||
}
|
||||
|
||||
get usedRam() {
|
||||
return ns.getServerUsedRam(this.hostname)
|
||||
}
|
||||
|
||||
get availRam() {
|
||||
return this.maxRam - this.usedRam
|
||||
}
|
||||
|
||||
get availMoney() {
|
||||
return ns.getServerMoneyAvailable(this.hostname)
|
||||
}
|
||||
|
||||
get currSecurityLevel() {
|
||||
return ns.getServerSecurityLevel(this.hostname)
|
||||
}
|
||||
|
||||
get weakenTime() {
|
||||
return ns.getWeakenTime(this.hostname)
|
||||
}
|
||||
|
||||
get hackTime() {
|
||||
return ns.getHackTime(this.hostname)
|
||||
}
|
||||
|
||||
get growTime() {
|
||||
return ns.getGrowTime(this.hostname)
|
||||
}
|
||||
|
||||
get score() {
|
||||
if (this.requiredHackLevel > ns.getHackingLevel() || this.maxMoney == 0) {
|
||||
return Infinity
|
||||
}
|
||||
else {
|
||||
const percentMinSecurityLevel = (this.minSecurityLevel / 100)
|
||||
const targetRequiredHackLevel = ns.getHackingLevel() * 0.33
|
||||
const percentLevelDeviation = Math.abs((this.requiredHackLevel - targetRequiredHackLevel) / targetRequiredHackLevel)
|
||||
// return Math.round(this.maxMoney * percentMinSecurityLevel * percentDeviation)
|
||||
return percentMinSecurityLevel + percentLevelDeviation
|
||||
}
|
||||
}
|
||||
|
||||
computeScriptCapacity(script) {
|
||||
return Math.floor(this.availRam / ns.getScriptRam(script))
|
||||
}
|
||||
|
||||
pwn() {
|
||||
if (nodeNumPortsRequired <= pwnMethods.length) {
|
||||
ns.tprint(`pwning ${this.hostname}`)
|
||||
for (let i = 0; i < this.numPortRequired; i++) {
|
||||
pwnMethods[i](this.hostname)
|
||||
}
|
||||
ns.nuke(this.hostname)
|
||||
return true
|
||||
}
|
||||
else {
|
||||
ns.print(`missing requirements to pwn ${this.hostname}. (${pwnMethods.length} < ${this.numPortRequired})`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
exploit(target) {
|
||||
const moneyLowThresh = target.maxMoney * moneyLowPercent
|
||||
const securityHighThresh = target.minSecurityLevel + securityHighOffset
|
||||
|
||||
ns.print(`setup ${this}`)
|
||||
const capacity = this.computeScriptCapacity("exploit.js")
|
||||
if (capacity > 0) {
|
||||
ns.scp("exploit.js", this.hostname)
|
||||
ns.killall(this.hostname)
|
||||
ns.exec("exploit.js", this.hostname, capacity, target.hostname, moneyLowThresh, securityHighThresh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively list all available worker nodes
|
||||
*/
|
||||
const getAvailableNodes = (hostnameToAnalyze = "home", availableNodes = []) => {
|
||||
for (const parentHostname of ns.scan(hostnameToAnalyze)) {
|
||||
const parentServer = new Server(parentHostname)
|
||||
// check if we alreay have added this node to the list
|
||||
if (availableNodes.map((n) => n.toString()).includes(parentServer.toString())) {
|
||||
continue //skip this node
|
||||
}
|
||||
// if we are not root on the server yet
|
||||
if (!parentServer.hasRootAccess) {
|
||||
// gain access
|
||||
if (!parentServer.pwn()) {
|
||||
// failed, skip this node
|
||||
}
|
||||
}
|
||||
ns.print(`adding ${parentServer} to worker list`)
|
||||
availableNodes.push(parentServer)
|
||||
availableNodes = getAvailableNodes(parentServer.hostname, availableNodes)
|
||||
}
|
||||
return availableNodes
|
||||
}
|
||||
|
||||
const printNodeTab = (nodes) => {
|
||||
const format = "%-15ss %10s %10s %10s %10s %10s %10s"
|
||||
ns.tprintf(format, "host", "score", "maxMoney", "minSecurity", "root?", "hackLevel", "maxRam")
|
||||
nodes.forEach((n) => {
|
||||
ns.tprintf(format, n.hostname, ns.formatNumber(n.score), ns.formatNumber(n.maxMoney), ns.formatNumber(n.minSecurityLevel), n.hasRootAccess, n.requiredHackLevel, ns.formatRam(n.maxRam))
|
||||
})
|
||||
ns.tprint(`total nodes: ${ns.formatNumber(nodes.length)}`)
|
||||
ns.tprint(`total RAM: ${ns.formatRam(nodes.reduce((x, y) => x + y.maxRam, 0))}`)
|
||||
}
|
||||
|
||||
// find optimal target
|
||||
const availableNodes = getAvailableNodes()
|
||||
availableNodes.sort((a, b) => a.score - b.score)
|
||||
const target = availableNodes.at(0)
|
||||
|
||||
// print status
|
||||
printNodeTab(availableNodes)
|
||||
ns.tprint(`targeting ${target}`)
|
||||
|
||||
//execute
|
||||
availableNodes.forEach((n) => { n.exploit(target) })
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const moneyLowPercent = 0.75
|
||||
const securityHighOffset = 5
|
||||
|
||||
const moneyFormat = new Intl.NumberFormat({ style: "currency" })
|
||||
|
||||
const pwnMethods = []
|
||||
if (ns.fileExists("BruteSSH.exe")) {
|
||||
pwnMethods.push(ns.brutessh)
|
||||
}
|
||||
if (ns.fileExists("FTPCrack.exe")) {
|
||||
pwnMethods.push(ns.ftpcrack)
|
||||
}
|
||||
if (ns.fileExists("HTTPWorm.exe")) {
|
||||
pwnMethods.push(ns.httpworm)
|
||||
}
|
||||
if (ns.fileExists("relaySMTP.exe")) {
|
||||
pwnMethods.push(ns.relaysmtp)
|
||||
}
|
||||
if (ns.fileExists("SQLInject.exe")) {
|
||||
pwnMethods.push(ns.sqlinject)
|
||||
}
|
||||
|
||||
// ns.clearLog()
|
||||
// ns.tail()
|
||||
// ns.disableLog("getServerMaxRam")
|
||||
// ns.disableLog("scp")
|
||||
// ns.disableLog("killall")
|
||||
ns.print(`${pwnMethods.length} pwning method(s) available!`)
|
||||
|
||||
class Server {
|
||||
constructor(hostname) {
|
||||
this.hostname = hostname
|
||||
this.maxMoney = ns.getServerMaxMoney(hostname)
|
||||
this.minSecurityLevel = ns.getServerMinSecurityLevel(hostname)
|
||||
this.maxRam = ns.getServerMaxRam(hostname)
|
||||
this.requiredHackLevel = ns.getServerRequiredHackingLevel(hostname)
|
||||
this.hasRootAccess = ns.hasRootAccess(hostname)
|
||||
this.numPortRequired = ns.getServerNumPortsRequired(hostname)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.hostname
|
||||
}
|
||||
|
||||
get usedRam() {
|
||||
return ns.getServerUsedRam(this.hostname)
|
||||
}
|
||||
|
||||
get availRam() {
|
||||
return this.maxRam - this.usedRam
|
||||
}
|
||||
|
||||
get availMoney() {
|
||||
return ns.getServerMoneyAvailable(this.hostname)
|
||||
}
|
||||
|
||||
get currSecurityLevel() {
|
||||
return ns.getServerSecurityLevel(this.hostname)
|
||||
}
|
||||
|
||||
get weakenTime() {
|
||||
return ns.getWeakenTime(this.hostname)
|
||||
}
|
||||
|
||||
get hackTime() {
|
||||
return ns.getHackTime(this.hostname)
|
||||
}
|
||||
|
||||
get growTime() {
|
||||
return ns.getGrowTime(this.hostname)
|
||||
}
|
||||
|
||||
get score() {
|
||||
if (this.requiredHackLevel > ns.getHackingLevel() || this.maxMoney == 0) {
|
||||
return Infinity
|
||||
}
|
||||
else {
|
||||
const percentMinSecurityLevel = (this.minSecurityLevel / 100)
|
||||
const targetRequiredHackLevel = ns.getHackingLevel() * 0.33
|
||||
const percentLevelDeviation = Math.abs((this.requiredHackLevel - targetRequiredHackLevel) / targetRequiredHackLevel)
|
||||
// return Math.round(this.maxMoney * percentMinSecurityLevel * percentDeviation)
|
||||
return percentMinSecurityLevel + percentLevelDeviation
|
||||
}
|
||||
}
|
||||
|
||||
computeScriptCapacity(script) {
|
||||
return Math.floor(this.maxRam / ns.getScriptRam(script))
|
||||
}
|
||||
|
||||
pwn() {
|
||||
if (nodeNumPortsRequired <= pwnMethods.length) {
|
||||
ns.tprint(`pwning ${this.hostname}`)
|
||||
for (let i = 0; i < this.numPortRequired; i++) {
|
||||
pwnMethods[i](this.hostname)
|
||||
}
|
||||
ns.nuke(this.hostname)
|
||||
return true
|
||||
}
|
||||
else {
|
||||
ns.print(`missing requirements to pwn ${this.hostname}. (${pwnMethods.length} < ${this.numPortRequired})`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
exploit(target) {
|
||||
const moneyLowThresh = target.maxMoney * moneyLowPercent
|
||||
const securityHighThresh = target.minSecurityLevel + securityHighOffset
|
||||
|
||||
ns.print(`setup ${this}`)
|
||||
const capacity = this.computeScriptCapacity("exploit.js")
|
||||
if (capacity > 0) {
|
||||
ns.scp("exploit.js", this.hostname)
|
||||
ns.killall(this.hostname)
|
||||
ns.exec("exploit.js", this.hostname, capacity, target.hostname, moneyLowThresh, securityHighThresh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively list all available worker nodes
|
||||
*/
|
||||
const getAvailableNodes = (hostnameToAnalyze = "home", availableNodes = []) => {
|
||||
for (const parentHostname of ns.scan(hostnameToAnalyze)) {
|
||||
const parentServer = new Server(parentHostname)
|
||||
// check if we alreay have added this node to the list
|
||||
if (availableNodes.map((n) => n.toString()).includes(parentServer.toString())) {
|
||||
continue //skip this node
|
||||
}
|
||||
// if we are not root on the server yet
|
||||
if (!parentServer.hasRootAccess) {
|
||||
// gain access
|
||||
if (!parentServer.pwn()) {
|
||||
// failed, skip this node
|
||||
}
|
||||
}
|
||||
ns.print(`adding ${parentServer} to worker list`)
|
||||
availableNodes.push(parentServer)
|
||||
availableNodes = getAvailableNodes(parentServer.hostname, availableNodes)
|
||||
}
|
||||
return availableNodes
|
||||
}
|
||||
|
||||
const printNodeTab = (nodes) => {
|
||||
const format = "%-15ss %10s %10s %10s %10s %10s %10s"
|
||||
ns.tprintf(format, "host", "score", "maxMoney", "minSecurity", "root?", "hackLevel", "maxRam")
|
||||
nodes.forEach((n) => {
|
||||
ns.tprintf(format, n.hostname, ns.formatNumber(n.score), ns.formatNumber(n.maxMoney), ns.formatNumber(n.minSecurityLevel), n.hasRootAccess, n.requiredHackLevel, ns.formatRam(n.maxRam))
|
||||
})
|
||||
ns.tprint(`total nodes: ${ns.formatNumber(nodes.length)}`)
|
||||
ns.tprint(`total RAM: ${ns.formatRam(nodes.reduce((x, y) => x + y.maxRam, 0))}`)
|
||||
}
|
||||
|
||||
// find optimal target
|
||||
const availableNodes = getAvailableNodes()
|
||||
availableNodes.sort((a, b) => a.score - b.score)
|
||||
const target = availableNodes.at(0)
|
||||
|
||||
// print status
|
||||
printNodeTab(availableNodes)
|
||||
ns.tprint(`targeting ${target}`)
|
||||
|
||||
//execute
|
||||
availableNodes.forEach((n) => { n.exploit(target) })
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const target = ns.args[0]
|
||||
const moneyLowThresh = ns.args[1]
|
||||
const securityHighThresh = ns.args[2]
|
||||
|
||||
while (true) {
|
||||
if (ns.getServerSecurityLevel(target) > securityHighThresh) {
|
||||
await ns.weaken(target)
|
||||
}
|
||||
else if (ns.getServerMoneyAvailable(target) < moneyLowThresh) {
|
||||
await ns.grow(target)
|
||||
}
|
||||
else {
|
||||
await ns.hack(target)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import { getServerTree } from "/lib/ServerTree.js"
|
||||
|
||||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const serverTree = getServerTree(ns)
|
||||
|
||||
// 1st pass: recursively find servers to backdoor
|
||||
const getTreeOfServerToBackdoor = (subtree) => {
|
||||
const childrenToBackdoor = []
|
||||
for (const child of subtree["children"]) {
|
||||
const backdoorSubtreeOfChild = getTreeOfServerToBackdoor(child)
|
||||
if (backdoorSubtreeOfChild != null) {
|
||||
childrenToBackdoor.push(backdoorSubtreeOfChild)
|
||||
}
|
||||
}
|
||||
if (childrenToBackdoor.length != 0 || (!subtree.info.backdoorInstalled && subtree.info.hasAdminRights && subtree.info.requiredHackingSkill <= ns.getHackingLevel())) {
|
||||
return {
|
||||
"info": subtree.info,
|
||||
"children": childrenToBackdoor,
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// 2nd pass: walk the tree and backdoor servers
|
||||
const backdoor = async (subtree) => {
|
||||
if (!subtree.info.backdoorInstalled) {
|
||||
ns.tprintf("backdoor %s, do not change server", subtree.info.hostname)
|
||||
await ns.singularity.installBackdoor()
|
||||
ns.tprint("backdoor complete")
|
||||
}
|
||||
for (const child of subtree.children) {
|
||||
// connect to child
|
||||
if (!ns.singularity.connect(child.info.hostname)) {
|
||||
ns.tprintf("failed to connect: %s", child.info.hostname)
|
||||
ns.exit()
|
||||
}
|
||||
// backdoor the child
|
||||
await backdoor(child)
|
||||
// connect back to current host
|
||||
if (!ns.singularity.connect(subtree.info.hostname)) {
|
||||
ns.tprintf("failed to connect: %s", subtree.info.hostname)
|
||||
ns.exit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// execute
|
||||
const serverTreeToBackdoor = getTreeOfServerToBackdoor(serverTree)
|
||||
if (serverTreeToBackdoor == null) {
|
||||
ns.tprint("nothing to do")
|
||||
}
|
||||
else {
|
||||
await backdoor(serverTreeToBackdoor)
|
||||
}
|
||||
ns.tprint("script complete")
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const maxServerCount = ns.getPurchasedServerLimit()
|
||||
let serverCount = ns.getPurchasedServers().length
|
||||
|
||||
ns.tprint(`we have ${serverCount} servers`)
|
||||
ns.tprint(`we can have ${maxServerCount} servers`)
|
||||
|
||||
if (serverCount < maxServerCount) {
|
||||
// create missing servers
|
||||
const requestedRAM = 4
|
||||
const serverCost = ns.getPurchasedServerCost(requestedRAM)
|
||||
ns.tprint(`server cost is ${ns.formatNumber(serverCost)}`)
|
||||
|
||||
while (serverCount < maxServerCount) {
|
||||
const serverName = `worker-${serverCount}`
|
||||
if (ns.getServerMoneyAvailable("home") >= serverCost) {
|
||||
ns.tprint(`racking server ${serverName}`)
|
||||
ns.purchaseServer(serverName, requestedRAM)
|
||||
serverCount++
|
||||
}
|
||||
else {
|
||||
await ns.sleep(60000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ns.tprint("done! spawning auto-upgrade")
|
||||
ns.spawn("auto/upgrade-workers.js")
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
|
||||
const programs = [
|
||||
"BruteSSH.exe",
|
||||
"FTPCrack.exe",
|
||||
"relaySMTP.exe",
|
||||
"HTTPWorm.exe",
|
||||
"SQLInject.exe",
|
||||
"ServerProfiler.exe",
|
||||
"DeepscanV1.exe",
|
||||
"DeepscanV2.exe",
|
||||
"AutoLink.exe",
|
||||
"Formulas.exe",
|
||||
]
|
||||
|
||||
// attempt to buy tor every second
|
||||
while (!ns.singularity.purchaseTor()) {
|
||||
await ns.sleep(1000)
|
||||
}
|
||||
|
||||
// build a list to track which program we have in our possession
|
||||
let programsPurchased = new Array(programs.length)
|
||||
for (let i = 0; i < programs.length; i++) {
|
||||
programsPurchased[i] = ns.ls("home").includes(programs[i])
|
||||
}
|
||||
|
||||
// attempt to buy the programs every seconds
|
||||
do {
|
||||
await ns.sleep(1000)
|
||||
for (let i = 0; i < programs.length; i++) {
|
||||
if (!programsPurchased[i]) {
|
||||
const purchaseResult = ns.singularity.purchaseProgram(programs[i])
|
||||
if (purchaseResult) {
|
||||
ns.tprintf("auto-tor: purchased %s", programs[i])
|
||||
}
|
||||
else {
|
||||
// purchase the programs in the order listed
|
||||
// if we are unable to buy a program, break and retry later
|
||||
break
|
||||
}
|
||||
programsPurchased[i] = purchaseResult
|
||||
}
|
||||
}
|
||||
} while (!programsPurchased.reduce((x, y) => x && y, true))
|
||||
|
||||
ns.tprint("auto-tor: complete")
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const servers = ns.getPurchasedServers()
|
||||
|
||||
ns.clearLog()
|
||||
ns.tail()
|
||||
ns.disableLog('ALL')
|
||||
|
||||
const getLowestServerRAM = () => {
|
||||
return Math.min.apply(Math, servers.map(ns.getServerMaxRam))
|
||||
}
|
||||
|
||||
while (getLowestServerRAM() < ns.getPurchasedServerMaxRam()) {
|
||||
const requestedRAM = getLowestServerRAM()*2
|
||||
for (const serverName of servers) {
|
||||
const serverRAM = ns.getServerMaxRam(serverName)
|
||||
if (serverRAM < requestedRAM) {
|
||||
const serverCost = ns.getPurchasedServerUpgradeCost(serverName, requestedRAM)
|
||||
ns.print(`selected server ${serverName} to be upgraded (current RAM: ${ns.formatRam(serverRAM)}, upgraded RAM: ${ns.formatRam(requestedRAM)}, upgrade cost: ${ns.formatNumber(serverCost)})`)
|
||||
while (true) {
|
||||
if (ns.getServerMoneyAvailable("home") >= serverCost) {
|
||||
ns.killall(serverName) //TODO: make it more graceful
|
||||
ns.upgradePurchasedServer(serverName, requestedRAM)
|
||||
break
|
||||
}
|
||||
else {
|
||||
await ns.sleep(60000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,668 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const hackMinMoneyLimitPercent = 0.75
|
||||
const hackMaxSecurityIncreaseLimit = 5
|
||||
const hackMinLevelThreshPercent = 1
|
||||
const maxTargets = Infinity
|
||||
const loopSettings = {
|
||||
"poolBlacklist": [],
|
||||
"poolBlacklist": ["home"],
|
||||
|
||||
"moneyTargetsWhitelist": [],
|
||||
// "moneyTargetsWhitelist": ["n00dles", "harakiri-sushi", "phantasy"], // early game list
|
||||
// "moneyTargetsBlacklist": [],
|
||||
"moneyTargetsBlacklist": ["joesguns"], // blacklist joesguns to avoid conflict with exp farm
|
||||
|
||||
"expTargetHostname": "joesguns",
|
||||
"expMaxAvailRamUsePercent": 0.99,
|
||||
}
|
||||
|
||||
const hackRamUsage = ns.getScriptRam("/payloads/hack.js")
|
||||
const growRamUsage = ns.getScriptRam("/payloads/grow.js")
|
||||
const weakenRamUsage = ns.getScriptRam("/payloads/weaken.js")
|
||||
const autoRackRamUsage = ns.getScriptRam("/payloads/autorack.js")
|
||||
const autoRackPwnUsage = ns.getScriptRam("/payloads/autopwn.js")
|
||||
|
||||
const moneyFormat = new Intl.NumberFormat({ style: "currency" })
|
||||
|
||||
ns.clearLog()
|
||||
ns.tail()
|
||||
ns.disableLog('ALL')
|
||||
// ns.disableLog('getServerUsedRam')
|
||||
// ns.disableLog('getServerMaxRam')
|
||||
// ns.print(`${pwnMethods.length} pwning method(s) available!`)
|
||||
|
||||
class PoolException {
|
||||
constructor(message) {
|
||||
this.message = message
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `WARNING: ${this.message}`
|
||||
}
|
||||
}
|
||||
|
||||
class Server {
|
||||
constructor(hostname) {
|
||||
this.hostname = hostname
|
||||
ns.scp(["/payloads/grow.js", "/payloads/hack.js", "/payloads/weaken.js", "/payloads/autorack.js", "/payloads/autopwn.js"], this.hostname)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.hostname
|
||||
}
|
||||
|
||||
get hasRootAccess() {
|
||||
return ns.hasRootAccess(this.hostname)
|
||||
}
|
||||
|
||||
get hackable() {
|
||||
return this.hasRootAccess && this.maxMoney > 0 && (this.requiredHackLevel <= ns.getHackingLevel() * hackMinLevelThreshPercent || this.requiredHackLevel == 1)
|
||||
}
|
||||
|
||||
get requiredHackLevel() {
|
||||
return ns.getServerRequiredHackingLevel(this.hostname)
|
||||
}
|
||||
|
||||
get maxRam() {
|
||||
return ns.getServerMaxRam(this.hostname)
|
||||
}
|
||||
|
||||
get usedRam() {
|
||||
return ns.getServerUsedRam(this.hostname)
|
||||
}
|
||||
|
||||
get availRam() {
|
||||
return this.maxRam - this.usedRam
|
||||
}
|
||||
|
||||
get maxMoney() {
|
||||
return ns.getServerMaxMoney(this.hostname)
|
||||
}
|
||||
|
||||
get availMoney() {
|
||||
return ns.getServerMoneyAvailable(this.hostname)
|
||||
}
|
||||
|
||||
get minSecurityLevel() {
|
||||
return ns.getServerMinSecurityLevel(this.hostname)
|
||||
}
|
||||
|
||||
get currSecurityLevel() {
|
||||
return ns.getServerSecurityLevel(this.hostname)
|
||||
}
|
||||
|
||||
get hackTime() {
|
||||
return Math.ceil(ns.getHackTime(this.hostname))
|
||||
}
|
||||
|
||||
get growTime() {
|
||||
return Math.ceil(ns.getGrowTime(this.hostname))
|
||||
}
|
||||
|
||||
get weakenTime() {
|
||||
return Math.ceil(ns.getWeakenTime(this.hostname))
|
||||
}
|
||||
|
||||
get hackChance() {
|
||||
return ns.hackAnalyzeChance(this.hostname)
|
||||
}
|
||||
|
||||
get pids() {
|
||||
return ns.ps(this.hostname).map(process => process.pid)
|
||||
}
|
||||
|
||||
getScriptCapacity(script) {
|
||||
return Math.floor(this.availRam / ns.getScriptRam(script))
|
||||
}
|
||||
|
||||
getHackCapacity() {
|
||||
return Math.floor(this.availRam / hackRamUsage)
|
||||
}
|
||||
|
||||
getGrowCapacity() {
|
||||
return Math.floor(this.availRam / growRamUsage)
|
||||
}
|
||||
|
||||
getWeakenCapacity() {
|
||||
return Math.floor(this.availRam / weakenRamUsage)
|
||||
}
|
||||
|
||||
computeOptimalHackThreadCount() {
|
||||
const requiredThreads = Math.ceil(((this.availMoney - (this.maxMoney * hackMinMoneyLimitPercent)) / this.availMoney) / ns.hackAnalyze(this.hostname))
|
||||
let securityLimitedThreads = 0
|
||||
let predictedSecurityIncrease = 0
|
||||
while ((predictedSecurityIncrease + this.currSecurityLevel) < (this.minSecurityLevel + hackMaxSecurityIncreaseLimit)) {
|
||||
predictedSecurityIncrease = ns.hackAnalyzeSecurity(++securityLimitedThreads)
|
||||
}
|
||||
return Math.min(requiredThreads, securityLimitedThreads)
|
||||
}
|
||||
|
||||
computeOptimalGrowThreadCount() {
|
||||
const requiredThreads = Math.ceil(ns.growthAnalyze(this.hostname, this.maxMoney / this.availMoney))
|
||||
// let securityLimitedThreads = 0
|
||||
// let predictedSecurityIncrease = 0
|
||||
// while ((predictedSecurityIncrease + this.currSecurityLevel) < (this.minSecurityLevel + hackMaxSecurityIncreaseLimit)) {
|
||||
// predictedSecurityIncrease = ns.growthAnalyzeSecurity(++securityLimitedThreads)
|
||||
// }
|
||||
// return Math.min(requiredThreads, securityLimitedThreads)
|
||||
return requiredThreads
|
||||
}
|
||||
|
||||
computeOptimalWeakenThreadCount() {
|
||||
let requiredThreads = 0
|
||||
let prediction = this.currSecurityLevel
|
||||
while (prediction > this.minSecurityLevel) {
|
||||
prediction = this.currSecurityLevel - ns.weakenAnalyze(++requiredThreads)
|
||||
}
|
||||
return requiredThreads
|
||||
}
|
||||
|
||||
execHack(target, numThreads = 1) {
|
||||
const pid = ns.exec("/payloads/hack.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run hack() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getHackCapacity()}`
|
||||
}
|
||||
return new Task(this, pid, "hack", Date.now() + target.hackTime, target)
|
||||
}
|
||||
|
||||
execGrow(target, numThreads = 1) {
|
||||
const pid = ns.exec("/payloads/grow.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run grow() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getGrowCapacity()}`
|
||||
}
|
||||
return new Task(this, pid, "grow", Date.now() + target.growTime, target)
|
||||
}
|
||||
|
||||
execWeaken(target, numThreads = 1) {
|
||||
const pid = ns.exec("/payloads/weaken.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run weaken() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getHackCapacity()}`
|
||||
}
|
||||
return new Task(this, pid, "weaken", Date.now() + target.weakenTime, target)
|
||||
}
|
||||
|
||||
execAutoRack() {
|
||||
const pid = ns.exec("/payloads/autorack.js", this.hostname, 1, crypto.randomUUID())
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run autorack on ${this.hostname}, availRAM = ${this.availRam}`
|
||||
}
|
||||
return new Task(this, pid, "autorack", Date.now())
|
||||
}
|
||||
|
||||
execAutoPwn() {
|
||||
const pid = ns.exec("/payloads/autopwn.js", this.hostname, 1, crypto.randomUUID())
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run autopwn on ${this.hostname}, availRAM = ${this.availRam}`
|
||||
}
|
||||
return new Task(this, pid, "autorack", Date.now())
|
||||
}
|
||||
}
|
||||
|
||||
class ServerPool {
|
||||
constructor() {
|
||||
this.pool = []
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.pool.length
|
||||
}
|
||||
|
||||
get maxRam() {
|
||||
return this.pool.reduce((x, y) => x + y.maxRam, 0)
|
||||
}
|
||||
|
||||
get usedRam() {
|
||||
return this.pool.reduce((x, y) => x + y.usedRam, 0)
|
||||
}
|
||||
|
||||
get availRam() {
|
||||
return this.pool.reduce((x, y) => x + y.availRam, 0)
|
||||
}
|
||||
|
||||
getHackCapacity() {
|
||||
return this.pool.reduce((x, y) => x + y.getHackCapacity(), 0)
|
||||
}
|
||||
|
||||
getGrowCapacity() {
|
||||
return this.pool.reduce((x, y) => x + y.getGrowCapacity(), 0)
|
||||
}
|
||||
|
||||
getWeakenCapacity() {
|
||||
return this.pool.reduce((x, y) => x + y.getWeakenCapacity(), 0)
|
||||
}
|
||||
|
||||
killAll() {
|
||||
this.pool.forEach(server => ns.killall(server.hostname))
|
||||
}
|
||||
|
||||
execHack(target, numThreads) {
|
||||
let tasks = []
|
||||
|
||||
if (numThreads == 0) {
|
||||
throw new PoolException(`attempted to schedule task with threads = 0 (operation = hack, target = ${target})`)
|
||||
}
|
||||
|
||||
for (const server of this.pool) {
|
||||
const capacity = server.getHackCapacity()
|
||||
if (capacity == 0) {
|
||||
// this server is full, so we skip it
|
||||
continue
|
||||
}
|
||||
const newThreads = numThreads > capacity ? capacity : numThreads
|
||||
const task = server.execHack(target, newThreads)
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
// reduce the number of remaining threads by the number of thread started by the new task
|
||||
numThreads -= newThreads
|
||||
if (numThreads <= 0) {
|
||||
// all threads scheduled, we can stop assigning them now
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
throw new PoolException(`attempted to schedule task, put pool has no capacity (operation = hack, target = ${target}, threads = ${numThreads})`)
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execGrow(target, numThreads) {
|
||||
let tasks = []
|
||||
|
||||
if (numThreads == 0) {
|
||||
throw new PoolException(`attempted to schedule task with threads = 0 (operation = grow, target = ${target})`)
|
||||
}
|
||||
|
||||
for (const server of this.pool) {
|
||||
const capacity = server.getGrowCapacity()
|
||||
if (capacity == 0) {
|
||||
// this server is full, so we skip it
|
||||
continue
|
||||
}
|
||||
const newThreads = numThreads > capacity ? capacity : numThreads
|
||||
const task = server.execGrow(target, newThreads)
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
// reduce the number of remaining threads by the number of thread started by the new task
|
||||
numThreads -= newThreads
|
||||
if (numThreads <= 0) {
|
||||
// all threads scheduled, we can stop assigning them now
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
throw new PoolException(`attempted to schedule task, put pool has no capacity (task = grow, target = ${target}, threads = ${numThreads})`)
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execWeaken(target, numThreads) {
|
||||
let tasks = []
|
||||
|
||||
if (numThreads == 0) {
|
||||
throw new PoolException(`attempted to schedule task with threads = 0 (tasks = weaken, target = ${target} `)
|
||||
}
|
||||
|
||||
for (const server of this.pool) {
|
||||
const capacity = server.getWeakenCapacity()
|
||||
if (capacity == 0) {
|
||||
// this server is full, so we skip it
|
||||
continue
|
||||
}
|
||||
const newThreads = numThreads > capacity ? capacity : numThreads
|
||||
const task = server.execWeaken(target, newThreads)
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
// reduce the number of remaining threads by the number of thread started by the new task
|
||||
numThreads -= newThreads
|
||||
if (numThreads <= 0) {
|
||||
// all threads scheduled, we can stop assigning them now
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
throw new PoolException(`attempted to schedule task, put pool has no capacity (task = weaken, target = ${target}, threads = ${numThreads})`)
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execAutoRack() {
|
||||
// TODO: skip if fully upgraded
|
||||
let tasks = []
|
||||
for (const server of this.pool) {
|
||||
if (server.availRam >= autoRackRamUsage) {
|
||||
const task = server.execAutoRack()
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execAutoPwn() {
|
||||
// TODO: skip if fully upgraded
|
||||
let tasks = []
|
||||
for (const server of this.pool) {
|
||||
if (server.availRam >= autoRackPwnUsage) {
|
||||
const task = server.execAutoPwn()
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execOptimalHack(target, factor = 1) {
|
||||
const numThreads = Math.min(this.getHackCapacity(), target.computeOptimalHackThreadCount() * factor)
|
||||
ns.print(`INFO: hacking ${target} with ${numThreads} threads`)
|
||||
return this.execHack(target, numThreads)
|
||||
}
|
||||
|
||||
execOptimalGrow(target, factor = 1) {
|
||||
const numThreads = Math.min(this.getGrowCapacity(), target.computeOptimalGrowThreadCount() * factor)
|
||||
ns.print(`INFO: growing ${target} with ${numThreads} threads`)
|
||||
return this.execGrow(target, numThreads)
|
||||
}
|
||||
|
||||
execOptimalWeaken(target, factor = 1) {
|
||||
const numThreads = Math.min(this.getWeakenCapacity(), target.computeOptimalWeakenThreadCount() * factor)
|
||||
ns.print(`INFO: weakening ${target} with ${numThreads} threads`)
|
||||
return this.execWeaken(target, numThreads)
|
||||
}
|
||||
|
||||
refresh() {
|
||||
// recursively list all available servers on network
|
||||
const getAvailableServers = (hostnameToAnalyze = null, availableServers = []) => {
|
||||
for (const parentHostname of ns.scan(hostnameToAnalyze)) {
|
||||
const parentServer = new Server(parentHostname)
|
||||
// check if we alreay have added this server to the list
|
||||
if (!availableServers.map((n) => n.toString()).includes(parentServer.toString())) {
|
||||
// add to the list
|
||||
availableServers.push(parentServer)
|
||||
// add parent servers to the list
|
||||
availableServers = getAvailableServers(parentServer.hostname, availableServers)
|
||||
}
|
||||
}
|
||||
return availableServers
|
||||
}
|
||||
|
||||
const availableServers = getAvailableServers()
|
||||
ns.print(`INFO: found ${availableServers.length} servers in network`)
|
||||
|
||||
let serversToAdd = []
|
||||
availableServers
|
||||
.filter(server => server.hasRootAccess && server.maxRam > 0) // is able to run tasks
|
||||
.filter(server => !this.pool.find(poolServer => poolServer.hostname == server.hostname)) // we don't already have this server in the pool
|
||||
.filter(server => !loopSettings.poolBlacklist.find(blacklistedHostname => blacklistedHostname == server.hostname)) // hostname is blacklisted
|
||||
.forEach(server => serversToAdd.push(server))
|
||||
this.pool = this.pool.concat(serversToAdd)
|
||||
ns.print(`INFO: added ${serversToAdd.length} servers to pool`)
|
||||
|
||||
return serversToAdd
|
||||
}
|
||||
|
||||
prepare(target) {
|
||||
let tasks = []
|
||||
if (target.availMoney != target.maxMoney) {
|
||||
// schedule as many grow() as able or necessary
|
||||
const growThreads = Math.min(this.getGrowCapacity(), Math.ceil(ns.growthAnalyze(target.hostname, target.maxMoney / target.availMoney)))
|
||||
tasks = tasks.concat(this.execGrow(target, growThreads))
|
||||
// try to offset the security growth with some parallel weaken() as able
|
||||
const weakenThreads = Math.min(this.getWeakenCapacity(), Math.ceil(((target.currSecurityLevel - target.minSecurityLevel) + (growThreads * 0.004)) / 0.04)) // 0.05 is more optimal, but t doesn't leave a lot of margin
|
||||
if (weakenThreads > 0) {
|
||||
// it's okay to skip weaken() if we have no capacity, we will do it next cycle
|
||||
tasks = tasks.concat(this.execWeaken(target, weakenThreads))
|
||||
}
|
||||
}
|
||||
return new Batch(tasks, target)
|
||||
}
|
||||
|
||||
attack(target) {
|
||||
if (target.availMoney != target.maxMoney) {
|
||||
// if money is not at maximum, grow()
|
||||
return this.execOptimalGrow(target)
|
||||
}
|
||||
else if (target.currSecurityLevel != target.minSecurityLevel) {
|
||||
// if security is not at minimum, weaken()
|
||||
return this.execOptimalWeaken(target)
|
||||
}
|
||||
else {
|
||||
// if money is at maximum and security is at minimum, hack()
|
||||
return this.execOptimalHack(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Task {
|
||||
constructor(server, pid, operation, completionTime, target = null) {
|
||||
this.server = server
|
||||
this.pid = pid
|
||||
this.operation = operation
|
||||
this.completionTime = completionTime
|
||||
this.target = target
|
||||
}
|
||||
|
||||
isRunning() {
|
||||
return this.server.pids.includes(this.pid)
|
||||
}
|
||||
|
||||
getTimeUntilComplete() {
|
||||
return Math.max(0, this.completionTime - Date.now())
|
||||
}
|
||||
|
||||
async sleepUntilCompleted() {
|
||||
while (this.isRunning()) {
|
||||
const sleepTime = this.getTimeUntilComplete() + 200
|
||||
ns.print(`INFO: sleeping ${sleepTime} milliseconds for pid ${this.pid} on server ${this.server} to complete (op = ${this.operation}, target = ${this.target})`)
|
||||
await ns.sleep(sleepTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Batch {
|
||||
constructor(tasks, target = null) {
|
||||
this.tasks = tasks
|
||||
this.target = target
|
||||
}
|
||||
|
||||
get completionTime() {
|
||||
return Math.max(...this.tasks.map(t => t.completionTime))
|
||||
}
|
||||
|
||||
isRunning() {
|
||||
return this.tasks.reduce((r, t) => r || t.isRunning(), false)
|
||||
}
|
||||
|
||||
getTimeUntilComplete() {
|
||||
return Math.max(...this.tasks.map(t => t.getTimeUntilComplete()))
|
||||
}
|
||||
|
||||
async sleepUntilCompleted() {
|
||||
while (this.isRunning()) {
|
||||
const sleepTime = this.getTimeUntilComplete() + 200
|
||||
ns.print(`INFO: sleeping ${sleepTime} milliseconds for batch to complete (tasks = [${this.tasks.map(t => t.operation).join(",")}], target = ${this.target})`)
|
||||
await ns.sleep(sleepTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ns.exec("map.js", "home")
|
||||
|
||||
const loop = async () => {
|
||||
const pool = new ServerPool()
|
||||
let tasks = []
|
||||
let preparingTargets = []
|
||||
let targets = []
|
||||
let lastRefreshLevel = ns.getHackingLevel()
|
||||
let refresh = true
|
||||
let reScore = false
|
||||
|
||||
const expFarmTarget = new Server(loopSettings.expTargetHostname)
|
||||
expFarmTarget.targetOf = 0
|
||||
|
||||
ns.atExit(() => pool.killAll())
|
||||
|
||||
// run autopwn once on startup to enable new servers
|
||||
ns.exec("/payloads/autopwn.js", "home")
|
||||
await ns.sleep(500)
|
||||
|
||||
while (true) {
|
||||
// every 10 level-up refresh the list of targets and recalculate the target priorities
|
||||
if (ns.getHackingLevel() - lastRefreshLevel >= 10) {
|
||||
ns.print(`INFO: triggering auto-refresh of targets`)
|
||||
refresh = true
|
||||
reScore = true
|
||||
lastRefreshLevel = ns.getHackingLevel()
|
||||
}
|
||||
|
||||
// refresh targets
|
||||
if (refresh) {
|
||||
pool.refresh()
|
||||
tasks.concat(pool.execAutoPwn())
|
||||
pool.pool
|
||||
.filter(target => !preparingTargets.includes(target))
|
||||
.filter(target => !targets.includes(target))
|
||||
.filter(target => target.hackable) // server is hackable
|
||||
.filter(target => loopSettings.moneyTargetsWhitelist.length != 0 ? loopSettings.moneyTargetsWhitelist.includes(target.hostname) : true)
|
||||
.filter(target => !loopSettings.moneyTargetsBlacklist.includes(target.hostname))
|
||||
.forEach(target => {
|
||||
target.targetOf = 0
|
||||
ns.print(`INFO: added ${target} as potential target`)
|
||||
preparingTargets.push(target)
|
||||
})
|
||||
refresh = false
|
||||
await ns.sleep(500)
|
||||
}
|
||||
|
||||
// prepare targets
|
||||
// TODO: this is currently very expensive because we need to place all target in ideal conditions before computing the score
|
||||
// in the future, calculate score and prepare only relevant targets
|
||||
let tmpPreparingTargets = [] // to avoid concurrent modification
|
||||
for (const target of preparingTargets) {
|
||||
try {
|
||||
if (target.targetOf == 0) {
|
||||
const prepareBatch = pool.prepare(target)
|
||||
if (prepareBatch.tasks.length == 0) {
|
||||
// target is ready to go
|
||||
// target.targetData.operation = "ready"
|
||||
targets.push(target)
|
||||
reScore = true // trigger a reScore
|
||||
}
|
||||
else {
|
||||
// track the prepare batch
|
||||
tasks.push(prepareBatch)
|
||||
target.targetOf += 1
|
||||
tmpPreparingTargets.push(target)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// we can't schedule more tasks, but keep track of the target
|
||||
tmpPreparingTargets.push(target)
|
||||
}
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof PoolException) {
|
||||
ns.print(e.toString())
|
||||
tmpPreparingTargets.push(target) // TODO: this is very jank
|
||||
if (e.fatal) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
preparingTargets = tmpPreparingTargets
|
||||
|
||||
// score targets
|
||||
if (reScore) {
|
||||
ns.print(`INFO: sorting ${targets.length} targets`)
|
||||
const score = (target) => target.maxMoney * target.hackChance / target.growTime
|
||||
targets.sort((t1, t2) => score(t2) - score(t1))
|
||||
reScore = false
|
||||
}
|
||||
|
||||
// extract money
|
||||
if (targets.length != 0) {
|
||||
for (let i = 0; i < Math.min(maxTargets, targets.length); i++) {
|
||||
try {
|
||||
const target = targets[i]
|
||||
if (target.targetOf == 0) {
|
||||
// ns.print(`hacking ${target}`)
|
||||
// ns.print(`maxMoney: ${target.maxMoney}`)
|
||||
// ns.print(`availMoney: ${target.availMoney}`)
|
||||
// ns.print(`minSecurity: ${target.minSecurityLevel}`)
|
||||
// ns.print(`currSecurity: ${target.currSecurityLevel}`)
|
||||
const newTasks = pool.attack(target)
|
||||
tasks = tasks.concat(newTasks)
|
||||
target.targetOf += newTasks.length
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof PoolException) {
|
||||
ns.print(e.toString())
|
||||
}
|
||||
else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// farm exp
|
||||
if (expFarmTarget.targetOf == 0 && loopSettings.expMaxAvailRamUsePercent != 0) {
|
||||
// we want to avoid filling up the ram to not starve other tasks
|
||||
const usableRam = pool.availRam * loopSettings.expMaxAvailRamUsePercent
|
||||
let newTasks = []
|
||||
try {
|
||||
if (expFarmTarget.currSecurityLevel > expFarmTarget.minSecurityLevel) {
|
||||
// minimize security to speed up hack()
|
||||
newTasks = pool.execWeaken(expFarmTarget, Math.floor(usableRam / weakenRamUsage))
|
||||
}
|
||||
else {
|
||||
// hack() nonstop
|
||||
newTasks = pool.execHack(expFarmTarget, Math.floor(usableRam / hackRamUsage))
|
||||
}
|
||||
expFarmTarget.targetOf += newTasks.length
|
||||
tasks = tasks.concat(newTasks)
|
||||
}
|
||||
catch (e) {
|
||||
// ns.print(e.toString())
|
||||
if (!e instanceof PoolException) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sleep until new tick
|
||||
// ns.print(tasks)
|
||||
// ns.print(targets)
|
||||
// ns.print(preparingTargets)
|
||||
ns.print(`pool = ${pool.size}, RAM = ${ns.formatRam(pool.usedRam)}/${ns.formatRam(pool.maxRam)} (${ns.formatPercent(pool.usedRam / pool.maxRam)}), targets = ${targets.length}/${targets.length + preparingTargets.length}, tasks = ${tasks.length}, refreshCountDown = ${10 - (ns.getHackingLevel() - lastRefreshLevel)}`)
|
||||
if (tasks.length > 0) {
|
||||
tasks.sort((t1, t2) => t1.completionTime - t2.completionTime)
|
||||
await tasks[0].sleepUntilCompleted()
|
||||
while (tasks.length != 0 && !tasks[0].isRunning()) {
|
||||
const task = tasks.shift()
|
||||
task.target.targetOf--
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw "idle"
|
||||
}
|
||||
}
|
||||
}
|
||||
await loop()
|
||||
}
|
|
@ -0,0 +1,680 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const hackMinMoneyLimitPercent = 0.75
|
||||
const hackMaxSecurityIncreaseLimit = 5
|
||||
const hackMinLevelThreshPercent = 1
|
||||
const maxTargets = Infinity
|
||||
const loopSettings = {
|
||||
"poolBlacklist": [],
|
||||
// "poolBlacklist": ["home"],
|
||||
|
||||
"moneyTargetsWhitelist": [],
|
||||
"moneyTargetsWhitelist": ["n00dles", "harakiri-sushi", "phantasy"],
|
||||
// "moneyTargetsBlacklist": [],
|
||||
"moneyTargetsBlacklist": ["joesguns"], // blacklist joesguns to avoid conflict with exp farm
|
||||
|
||||
"expTargetHostname": "joesguns",
|
||||
"expMaxAvailRamUsePercent": 0.95,
|
||||
}
|
||||
|
||||
const hackRamUsage = ns.getScriptRam("/payloads/hack.js")
|
||||
const growRamUsage = ns.getScriptRam("/payloads/grow.js")
|
||||
const weakenRamUsage = ns.getScriptRam("/payloads/weaken.js")
|
||||
const autoRackRamUsage = ns.getScriptRam("/payloads/autorack.js")
|
||||
const autoRackPwnUsage = ns.getScriptRam("/payloads/autopwn.js")
|
||||
|
||||
const moneyFormat = new Intl.NumberFormat({ style: "currency" })
|
||||
|
||||
ns.clearLog()
|
||||
ns.tail()
|
||||
ns.disableLog('ALL')
|
||||
// ns.disableLog('getServerUsedRam')
|
||||
// ns.disableLog('getServerMaxRam')
|
||||
// ns.print(`${pwnMethods.length} pwning method(s) available!`)
|
||||
|
||||
class PoolException {
|
||||
constructor(message) {
|
||||
this.message = message
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `WARNING: ${this.message}`
|
||||
}
|
||||
}
|
||||
|
||||
class Server {
|
||||
constructor(hostname) {
|
||||
this.hostname = hostname
|
||||
ns.scp(["/payloads/grow.js", "/payloads/hack.js", "/payloads/weaken.js", "/payloads/autorack.js", "/payloads/autopwn.js"], this.hostname)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.hostname
|
||||
}
|
||||
|
||||
get hasRootAccess() {
|
||||
return ns.hasRootAccess(this.hostname)
|
||||
}
|
||||
|
||||
get hackable() {
|
||||
return this.hasRootAccess && this.maxMoney > 0 && (this.requiredHackLevel <= ns.getHackingLevel() * hackMinLevelThreshPercent || this.requiredHackLevel == 1)
|
||||
}
|
||||
|
||||
get requiredHackLevel() {
|
||||
return ns.getServerRequiredHackingLevel(this.hostname)
|
||||
}
|
||||
|
||||
get maxRam() {
|
||||
return ns.getServerMaxRam(this.hostname)
|
||||
}
|
||||
|
||||
get usedRam() {
|
||||
return ns.getServerUsedRam(this.hostname)
|
||||
}
|
||||
|
||||
get availRam() {
|
||||
return this.maxRam - this.usedRam
|
||||
}
|
||||
|
||||
get maxMoney() {
|
||||
return ns.getServerMaxMoney(this.hostname)
|
||||
}
|
||||
|
||||
get availMoney() {
|
||||
return ns.getServerMoneyAvailable(this.hostname)
|
||||
}
|
||||
|
||||
get minSecurityLevel() {
|
||||
return ns.getServerMinSecurityLevel(this.hostname)
|
||||
}
|
||||
|
||||
get currSecurityLevel() {
|
||||
return ns.getServerSecurityLevel(this.hostname)
|
||||
}
|
||||
|
||||
get hackTime() {
|
||||
return Math.ceil(ns.getHackTime(this.hostname))
|
||||
}
|
||||
|
||||
get growTime() {
|
||||
return Math.ceil(ns.getGrowTime(this.hostname))
|
||||
}
|
||||
|
||||
get weakenTime() {
|
||||
return Math.ceil(ns.getWeakenTime(this.hostname))
|
||||
}
|
||||
|
||||
get hackChance() {
|
||||
return ns.hackAnalyzeChance(this.hostname)
|
||||
}
|
||||
|
||||
get pids() {
|
||||
return ns.ps(this.hostname).map(process => process.pid)
|
||||
}
|
||||
|
||||
getScriptCapacity(script) {
|
||||
return Math.floor(this.availRam / ns.getScriptRam(script))
|
||||
}
|
||||
|
||||
getHackCapacity() {
|
||||
return Math.floor(this.availRam / hackRamUsage)
|
||||
}
|
||||
|
||||
getGrowCapacity() {
|
||||
return Math.floor(this.availRam / growRamUsage)
|
||||
}
|
||||
|
||||
getWeakenCapacity() {
|
||||
return Math.floor(this.availRam / weakenRamUsage)
|
||||
}
|
||||
|
||||
computeOptimalHackThreadCount() {
|
||||
const requiredThreads = Math.ceil(((this.availMoney - (this.maxMoney * hackMinMoneyLimitPercent)) / this.availMoney) / ns.hackAnalyze(this.hostname))
|
||||
let securityLimitedThreads = 0
|
||||
let predictedSecurityIncrease = 0
|
||||
while ((predictedSecurityIncrease + this.currSecurityLevel) < (this.minSecurityLevel + hackMaxSecurityIncreaseLimit)) {
|
||||
predictedSecurityIncrease = ns.hackAnalyzeSecurity(++securityLimitedThreads)
|
||||
}
|
||||
return Math.min(requiredThreads, securityLimitedThreads)
|
||||
}
|
||||
|
||||
computeOptimalGrowThreadCount() {
|
||||
const requiredThreads = Math.ceil(ns.growthAnalyze(this.hostname, this.maxMoney / this.availMoney))
|
||||
// let securityLimitedThreads = 0
|
||||
// let predictedSecurityIncrease = 0
|
||||
// while ((predictedSecurityIncrease + this.currSecurityLevel) < (this.minSecurityLevel + hackMaxSecurityIncreaseLimit)) {
|
||||
// predictedSecurityIncrease = ns.growthAnalyzeSecurity(++securityLimitedThreads)
|
||||
// }
|
||||
// return Math.min(requiredThreads, securityLimitedThreads)
|
||||
return requiredThreads
|
||||
}
|
||||
|
||||
computeOptimalWeakenThreadCount() {
|
||||
let requiredThreads = 0
|
||||
let prediction = this.currSecurityLevel
|
||||
while (prediction > this.minSecurityLevel) {
|
||||
prediction = this.currSecurityLevel - ns.weakenAnalyze(++requiredThreads)
|
||||
}
|
||||
return requiredThreads
|
||||
}
|
||||
|
||||
execHack(target, numThreads = 1) {
|
||||
const pid = ns.exec("/payloads/hack.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run hack() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getHackCapacity()}`
|
||||
}
|
||||
return new Task(this, pid, "hack", Date.now() + target.hackTime, target)
|
||||
}
|
||||
|
||||
execGrow(target, numThreads = 1) {
|
||||
const pid = ns.exec("/payloads/grow.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run grow() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getGrowCapacity()}`
|
||||
}
|
||||
return new Task(this, pid, "grow", Date.now() + target.growTime, target)
|
||||
}
|
||||
|
||||
execWeaken(target, numThreads = 1) {
|
||||
const pid = ns.exec("/payloads/weaken.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run weaken() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getHackCapacity()}`
|
||||
}
|
||||
return new Task(this, pid, "weaken", Date.now() + target.weakenTime, target)
|
||||
}
|
||||
|
||||
execAutoRack() {
|
||||
const pid = ns.exec("/payloads/autorack.js", this.hostname, 1, crypto.randomUUID())
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run autorack on ${this.hostname}, availRAM = ${this.availRam}`
|
||||
}
|
||||
return new Task(this, pid, "autorack", Date.now())
|
||||
}
|
||||
|
||||
execAutoPwn() {
|
||||
const pid = ns.exec("/payloads/autopwn.js", this.hostname, 1, crypto.randomUUID())
|
||||
if (pid == 0) {
|
||||
throw `ERROR: failed to run autopwn on ${this.hostname}, availRAM = ${this.availRam}`
|
||||
}
|
||||
return new Task(this, pid, "autorack", Date.now())
|
||||
}
|
||||
}
|
||||
|
||||
class ServerPool {
|
||||
constructor() {
|
||||
this.pool = []
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.pool.length
|
||||
}
|
||||
|
||||
get maxRam() {
|
||||
return this.pool.reduce((x, y) => x + y.maxRam, 0)
|
||||
}
|
||||
|
||||
get usedRam() {
|
||||
return this.pool.reduce((x, y) => x + y.usedRam, 0)
|
||||
}
|
||||
|
||||
get availRam() {
|
||||
return this.pool.reduce((x, y) => x + y.availRam, 0)
|
||||
}
|
||||
|
||||
getHackCapacity() {
|
||||
return this.pool.reduce((x, y) => x + y.getHackCapacity(), 0)
|
||||
}
|
||||
|
||||
getGrowCapacity() {
|
||||
return this.pool.reduce((x, y) => x + y.getGrowCapacity(), 0)
|
||||
}
|
||||
|
||||
getWeakenCapacity() {
|
||||
return this.pool.reduce((x, y) => x + y.getWeakenCapacity(), 0)
|
||||
}
|
||||
|
||||
killAll() {
|
||||
this.pool.forEach(server => ns.killall(server.hostname))
|
||||
}
|
||||
|
||||
execHack(target, numThreads, split=true) {
|
||||
let tasks = []
|
||||
|
||||
if (numThreads == 0) {
|
||||
throw new PoolException(`attempted to schedule task with threads = 0 (operation = hack, target = ${target})`)
|
||||
}
|
||||
|
||||
for (const server of this.pool) {
|
||||
const capacity = server.getHackCapacity()
|
||||
if (capacity == 0) {
|
||||
// this server is full, so we skip it
|
||||
continue
|
||||
}
|
||||
else if(!split && capacity < numThreads) {
|
||||
// we don't want to split into multiple threads and this server doesn't have enough capacity, so we skip it
|
||||
continue
|
||||
}
|
||||
const newThreads = numThreads > capacity ? capacity : numThreads
|
||||
const task = server.execHack(target, newThreads)
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
// reduce the number of remaining threads by the number of thread started by the new task
|
||||
numThreads -= newThreads
|
||||
if (numThreads <= 0) {
|
||||
// all threads scheduled, we can stop assigning them now
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
throw new PoolException(`attempted to schedule task, put pool has no capacity (operation = hack, target = ${target}, threads = ${numThreads})`)
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execGrow(target, numThreads, split=true) {
|
||||
let tasks = []
|
||||
|
||||
if (numThreads == 0) {
|
||||
throw new PoolException(`attempted to schedule task with threads = 0 (operation = grow, target = ${target})`)
|
||||
}
|
||||
|
||||
for (const server of this.pool) {
|
||||
const capacity = server.getGrowCapacity()
|
||||
if (capacity == 0) {
|
||||
// this server is full, so we skip it
|
||||
continue
|
||||
}
|
||||
else if(!split && capacity < numThreads) {
|
||||
// we don't want to split into multiple threads and this server doesn't have enough capacity, so we skip it
|
||||
continue
|
||||
}
|
||||
const newThreads = numThreads > capacity ? capacity : numThreads
|
||||
const task = server.execGrow(target, newThreads)
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
// reduce the number of remaining threads by the number of thread started by the new task
|
||||
numThreads -= newThreads
|
||||
if (numThreads <= 0) {
|
||||
// all threads scheduled, we can stop assigning them now
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
throw new PoolException(`attempted to schedule task, put pool has no capacity (task = grow, target = ${target}, threads = ${numThreads})`)
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execWeaken(target, numThreads, split=true) {
|
||||
let tasks = []
|
||||
|
||||
if (numThreads == 0) {
|
||||
throw new PoolException(`attempted to schedule task with threads = 0 (tasks = weaken, target = ${target} `)
|
||||
}
|
||||
|
||||
for (const server of this.pool) {
|
||||
const capacity = server.getWeakenCapacity()
|
||||
if (capacity == 0) {
|
||||
// this server is full, so we skip it
|
||||
continue
|
||||
}
|
||||
else if(!split && capacity < numThreads) {
|
||||
// we don't want to split into multiple threads and this server doesn't have enough capacity, so we skip it
|
||||
continue
|
||||
}
|
||||
const newThreads = numThreads > capacity ? capacity : numThreads
|
||||
const task = server.execWeaken(target, newThreads)
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
// reduce the number of remaining threads by the number of thread started by the new task
|
||||
numThreads -= newThreads
|
||||
if (numThreads <= 0) {
|
||||
// all threads scheduled, we can stop assigning them now
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
throw new PoolException(`attempted to schedule task, put pool has no capacity (task = weaken, target = ${target}, threads = ${numThreads})`)
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execAutoRack() {
|
||||
// TODO: skip if fully upgraded
|
||||
let tasks = []
|
||||
for (const server of this.pool) {
|
||||
if (server.availRam >= autoRackRamUsage) {
|
||||
const task = server.execAutoRack()
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execAutoPwn() {
|
||||
// TODO: skip if fully upgraded
|
||||
let tasks = []
|
||||
for (const server of this.pool) {
|
||||
if (server.availRam >= autoRackPwnUsage) {
|
||||
const task = server.execAutoPwn()
|
||||
if (task.pid != 0) {
|
||||
tasks.push(task)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
execOptimalHack(target, factor = 1) {
|
||||
const numThreads = Math.min(this.getHackCapacity(), target.computeOptimalHackThreadCount() * factor)
|
||||
ns.print(`INFO: hacking ${target} with ${numThreads} threads`)
|
||||
return this.execHack(target, numThreads)
|
||||
}
|
||||
|
||||
execOptimalGrow(target, factor = 1) {
|
||||
const numThreads = Math.min(this.getGrowCapacity(), target.computeOptimalGrowThreadCount() * factor)
|
||||
ns.print(`INFO: growing ${target} with ${numThreads} threads`)
|
||||
return this.execGrow(target, numThreads)
|
||||
}
|
||||
|
||||
execOptimalWeaken(target, factor = 1) {
|
||||
const numThreads = Math.min(this.getWeakenCapacity(), target.computeOptimalWeakenThreadCount() * factor)
|
||||
ns.print(`INFO: weakening ${target} with ${numThreads} threads`)
|
||||
return this.execWeaken(target, numThreads)
|
||||
}
|
||||
|
||||
refresh() {
|
||||
// recursively list all available servers on network
|
||||
const getAvailableServers = (hostnameToAnalyze = null, availableServers = []) => {
|
||||
for (const parentHostname of ns.scan(hostnameToAnalyze)) {
|
||||
const parentServer = new Server(parentHostname)
|
||||
// check if we alreay have added this server to the list
|
||||
if (!availableServers.map((n) => n.toString()).includes(parentServer.toString())) {
|
||||
// add to the list
|
||||
availableServers.push(parentServer)
|
||||
// add parent servers to the list
|
||||
availableServers = getAvailableServers(parentServer.hostname, availableServers)
|
||||
}
|
||||
}
|
||||
return availableServers
|
||||
}
|
||||
|
||||
const availableServers = getAvailableServers()
|
||||
ns.print(`INFO: found ${availableServers.length} servers in network`)
|
||||
|
||||
let serversToAdd = []
|
||||
availableServers
|
||||
.filter(server => server.hasRootAccess && server.maxRam > 0) // is able to run tasks
|
||||
.filter(server => !this.pool.find(poolServer => poolServer.hostname == server.hostname)) // we don't already have this server in the pool
|
||||
.filter(server => !loopSettings.poolBlacklist.find(blacklistedHostname => blacklistedHostname == server.hostname)) // hostname is blacklisted
|
||||
.forEach(server => serversToAdd.push(server))
|
||||
this.pool = this.pool.concat(serversToAdd)
|
||||
ns.print(`INFO: added ${serversToAdd.length} servers to pool`)
|
||||
|
||||
return serversToAdd
|
||||
}
|
||||
|
||||
prepare(target) {
|
||||
let tasks = []
|
||||
if (target.availMoney != target.maxMoney) {
|
||||
// schedule as many grow() as able or necessary
|
||||
const growThreads = Math.min(this.getGrowCapacity(), Math.ceil(ns.growthAnalyze(target.hostname, target.maxMoney / target.availMoney)))
|
||||
tasks = tasks.concat(this.execGrow(target, growThreads, true))
|
||||
// try to offset the security growth with some parallel weaken() as able
|
||||
const weakenThreads = Math.min(this.getWeakenCapacity(), Math.ceil(((target.currSecurityLevel - target.minSecurityLevel) + (growThreads * 0.004)) / 0.04)) // 0.05 is more optimal, but t doesn't leave a lot of margin
|
||||
if (weakenThreads > 0) {
|
||||
// it's okay to skip weaken() if we have no capacity, we will do it next cycle
|
||||
tasks = tasks.concat(this.execWeaken(target, weakenThreads, true))
|
||||
}
|
||||
}
|
||||
return new Batch(tasks, target)
|
||||
}
|
||||
|
||||
attack(target) {
|
||||
if (target.availMoney != target.maxMoney) {
|
||||
// if money is not at maximum, grow()
|
||||
return this.execOptimalGrow(target)
|
||||
}
|
||||
else if (target.currSecurityLevel != target.minSecurityLevel) {
|
||||
// if security is not at minimum, weaken()
|
||||
return this.execOptimalWeaken(target)
|
||||
}
|
||||
else {
|
||||
// if money is at maximum and security is at minimum, hack()
|
||||
return this.execOptimalHack(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Task {
|
||||
constructor(server, pid, operation, completionTime, target = null) {
|
||||
this.server = server
|
||||
this.pid = pid
|
||||
this.operation = operation
|
||||
this.completionTime = completionTime
|
||||
this.target = target
|
||||
}
|
||||
|
||||
isRunning() {
|
||||
return this.server.pids.includes(this.pid)
|
||||
}
|
||||
|
||||
getTimeUntilComplete() {
|
||||
return Math.max(0, this.completionTime - Date.now())
|
||||
}
|
||||
|
||||
async sleepUntilCompleted() {
|
||||
while (this.isRunning()) {
|
||||
const sleepTime = this.getTimeUntilComplete() + 200
|
||||
ns.print(`INFO: sleeping ${sleepTime} milliseconds for pid ${this.pid} on server ${this.server} to complete (op = ${this.operation}, target = ${this.target})`)
|
||||
await ns.sleep(sleepTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Batch {
|
||||
constructor(tasks, target = null) {
|
||||
this.tasks = tasks
|
||||
this.target = target
|
||||
}
|
||||
|
||||
get completionTime() {
|
||||
return Math.max(...this.tasks.map(t => t.completionTime))
|
||||
}
|
||||
|
||||
isRunning() {
|
||||
return this.tasks.reduce((r, t) => r || t.isRunning(), false)
|
||||
}
|
||||
|
||||
getTimeUntilComplete() {
|
||||
return Math.max(...this.tasks.map(t => t.getTimeUntilComplete()))
|
||||
}
|
||||
|
||||
async sleepUntilCompleted() {
|
||||
while (this.isRunning()) {
|
||||
const sleepTime = this.getTimeUntilComplete() + 200
|
||||
ns.print(`INFO: sleeping ${sleepTime} milliseconds for batch to complete (tasks = [${this.tasks.map(t => t.operation).join(",")}], target = ${this.target})`)
|
||||
await ns.sleep(sleepTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ns.exec("map.js", "home")
|
||||
|
||||
const loop = async () => {
|
||||
const pool = new ServerPool()
|
||||
let tasks = []
|
||||
let preparingTargets = []
|
||||
let targets = []
|
||||
let lastRefreshLevel = ns.getHackingLevel()
|
||||
let refresh = true
|
||||
let reScore = false
|
||||
|
||||
const expFarmTarget = new Server(loopSettings.expTargetHostname)
|
||||
expFarmTarget.targetOf = 0
|
||||
|
||||
ns.atExit(() => pool.killAll())
|
||||
|
||||
// run autopwn once on startup to enable new servers
|
||||
ns.exec("/payloads/autopwn.js", "home")
|
||||
await ns.sleep(500)
|
||||
|
||||
while (true) {
|
||||
// every 10 level-up refresh the list of targets and recalculate the target priorities
|
||||
if (ns.getHackingLevel() - lastRefreshLevel >= 10) {
|
||||
ns.print(`INFO: triggering auto-refresh of targets`)
|
||||
refresh = true
|
||||
reScore = true
|
||||
lastRefreshLevel = ns.getHackingLevel()
|
||||
}
|
||||
|
||||
// refresh targets
|
||||
if (refresh) {
|
||||
pool.refresh()
|
||||
tasks.concat(pool.execAutoPwn())
|
||||
pool.pool
|
||||
.filter(target => !preparingTargets.includes(target))
|
||||
.filter(target => !targets.includes(target))
|
||||
.filter(target => target.hackable) // server is hackable
|
||||
.filter(target => loopSettings.moneyTargetsWhitelist.length != 0 ? loopSettings.moneyTargetsWhitelist.includes(target.hostname) : true)
|
||||
.filter(target => !loopSettings.moneyTargetsBlacklist.includes(target.hostname))
|
||||
.forEach(target => {
|
||||
target.targetOf = 0
|
||||
ns.print(`INFO: added ${target} as potential target`)
|
||||
preparingTargets.push(target)
|
||||
})
|
||||
refresh = false
|
||||
await ns.sleep(500)
|
||||
}
|
||||
|
||||
// prepare targets
|
||||
// TODO: this is currently very expensive because we need to place all target in ideal conditions before computing the score
|
||||
// in the future, calculate score and prepare only relevant targets
|
||||
let tmpPreparingTargets = [] // to avoid concurrent modification
|
||||
for (const target of preparingTargets) {
|
||||
try {
|
||||
if (target.targetOf == 0) {
|
||||
const prepareBatch = pool.prepare(target)
|
||||
if (prepareBatch.tasks.length == 0) {
|
||||
// target is ready to go
|
||||
// target.targetData.operation = "ready"
|
||||
targets.push(target)
|
||||
reScore = true // trigger a reScore
|
||||
}
|
||||
else {
|
||||
// track the prepare batch
|
||||
tasks.push(prepareBatch)
|
||||
target.targetOf += 1
|
||||
tmpPreparingTargets.push(target)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// we can't schedule more tasks, but keep track of the target
|
||||
tmpPreparingTargets.push(target)
|
||||
}
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof PoolException) {
|
||||
ns.print(e.toString())
|
||||
tmpPreparingTargets.push(target) // TODO: this is very jank
|
||||
if (e.fatal) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
preparingTargets = tmpPreparingTargets
|
||||
|
||||
// score targets
|
||||
if (reScore) {
|
||||
ns.print(`INFO: sorting ${targets.length} targets`)
|
||||
const score = (target) => target.maxMoney * target.hackChance / target.growTime
|
||||
targets.sort((t1, t2) => score(t2) - score(t1))
|
||||
reScore = false
|
||||
}
|
||||
|
||||
// extract money
|
||||
if (targets.length != 0) {
|
||||
for (let i = 0; i < Math.min(maxTargets, targets.length); i++) {
|
||||
try {
|
||||
const target = targets[i]
|
||||
if (target.targetOf == 0) {
|
||||
// ns.print(`hacking ${target}`)
|
||||
// ns.print(`maxMoney: ${target.maxMoney}`)
|
||||
// ns.print(`availMoney: ${target.availMoney}`)
|
||||
// ns.print(`minSecurity: ${target.minSecurityLevel}`)
|
||||
// ns.print(`currSecurity: ${target.currSecurityLevel}`)
|
||||
const newTasks = pool.attack(target)
|
||||
tasks = tasks.concat(newTasks)
|
||||
target.targetOf += newTasks.length
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof PoolException) {
|
||||
ns.print(e.toString())
|
||||
}
|
||||
else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// farm exp
|
||||
if (expFarmTarget.targetOf == 0 && loopSettings.expMaxAvailRamUsePercent != 0) {
|
||||
// we want to avoid filling up the ram to not starve other tasks
|
||||
const usableRam = pool.availRam * loopSettings.expMaxAvailRamUsePercent
|
||||
let newTasks = []
|
||||
try {
|
||||
if (expFarmTarget.currSecurityLevel > expFarmTarget.minSecurityLevel) {
|
||||
// minimize security to speed up hack()
|
||||
newTasks = pool.execWeaken(expFarmTarget, Math.floor(usableRam / weakenRamUsage))
|
||||
}
|
||||
else {
|
||||
// hack() nonstop
|
||||
newTasks = pool.execHack(expFarmTarget, Math.floor(usableRam / hackRamUsage))
|
||||
}
|
||||
expFarmTarget.targetOf += newTasks.length
|
||||
tasks = tasks.concat(newTasks)
|
||||
}
|
||||
catch (e) {
|
||||
// ns.print(e.toString())
|
||||
if (!e instanceof PoolException) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sleep until new tick
|
||||
// ns.print(tasks)
|
||||
// ns.print(targets)
|
||||
// ns.print(preparingTargets)
|
||||
ns.print(`pool = ${pool.size}, RAM = ${ns.formatRam(pool.usedRam)}/${ns.formatRam(pool.maxRam)} (${ns.formatPercent(pool.usedRam / pool.maxRam)}), targets = ${targets.length}/${targets.length + preparingTargets.length}, tasks = ${tasks.length}, refreshCountDown = ${10 - (ns.getHackingLevel() - lastRefreshLevel)}`)
|
||||
if (tasks.length > 0) {
|
||||
tasks.sort((t1, t2) => t1.completionTime - t2.completionTime)
|
||||
await tasks[0].sleepUntilCompleted()
|
||||
while (tasks.length != 0 && !tasks[0].isRunning()) {
|
||||
const task = tasks.shift()
|
||||
task.target.targetOf--
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw "idle"
|
||||
}
|
||||
}
|
||||
}
|
||||
await loop()
|
||||
}
|
|
@ -0,0 +1,411 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
|
||||
ns.clearLog()
|
||||
ns.tail()
|
||||
ns.disableLog('ALL')
|
||||
|
||||
const getServers = (hostnameToAnalyze = "home", servers = ["home"]) => {
|
||||
for (const hostname of ns.scan(hostnameToAnalyze)) {
|
||||
// check if we alreay have added this server to the list
|
||||
if (!servers.includes(hostname)) {
|
||||
// add to the list
|
||||
servers.push(hostname)
|
||||
// map the host
|
||||
servers = getServers(hostname, servers)
|
||||
}
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
const solvers = new Map([
|
||||
["Find Largest Prime Factor", (data) => {
|
||||
const factorize = (n) => {
|
||||
let results = [];
|
||||
for (let i = 2; i <= n; i++) {
|
||||
// check if divisible
|
||||
if (n % i == 0) {
|
||||
// divide the number to test
|
||||
n = n / i;
|
||||
// store the divisor
|
||||
results.push(i);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
return factorize(data).pop()
|
||||
}],
|
||||
["Subarray with Maximum Sum", (data) => {
|
||||
let result = 0
|
||||
// loop 1: walk starting index
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let sum = 0
|
||||
// loop 2: sum everything from the stating index
|
||||
for (let j = i; j < data.length; j++) {
|
||||
sum += data[j]
|
||||
// if at any point during computing the sum we find a greater sum
|
||||
if (sum > result) {
|
||||
result = sum
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}],
|
||||
["Total Ways to Sum", (data) => {
|
||||
// This can be solved with using the Partition function P
|
||||
// solution = P(n) - 1
|
||||
// https://en.wikipedia.org/wiki/Partition_function_(number_theory)
|
||||
// https://rosettacode.org/wiki/Partition_function_P
|
||||
const p = n => {
|
||||
var a = new Array(n + 1)
|
||||
a[0] = 1n
|
||||
|
||||
for (let i = 1; i <= n; i++) {
|
||||
a[i] = 0n
|
||||
for (let k = 1, s = 1; s <= i;) {
|
||||
a[i] += (k & 1 ? a[i - s] : -a[i - s])
|
||||
k > 0 ? (s += k, k = -k) : (k = -k + 1, s = k * (3 * k - 1) / 2)
|
||||
}
|
||||
}
|
||||
|
||||
return a[n]
|
||||
}
|
||||
return Number(p(data) - 1n)
|
||||
}],
|
||||
["Total Ways to Sum II", (data) => {
|
||||
// https://www.geeksforgeeks.org/coin-change-dp-7/
|
||||
// https://www.geeksforgeeks.org/understanding-the-coin-change-problem-with-dynamic-programming/
|
||||
const target = data[0]
|
||||
const numberPool = data[1]
|
||||
let memoizationTable = new Array(target + 1).fill(0)
|
||||
|
||||
// there is always 1 way of returning 0 sum; with 0 number
|
||||
memoizationTable[0] = 1
|
||||
|
||||
// loop through available number in the pool
|
||||
for (let i = 0; i < numberPool.length; i++) {
|
||||
// compute each entry in the memoization table
|
||||
for (let j = 0; j < memoizationTable.length; j++) {
|
||||
// ensure we do not lookup negative indices
|
||||
if (numberPool[i] <= j) {
|
||||
// compute the value at this index and store in the table to be referenced next iteration
|
||||
memoizationTable[j] += memoizationTable[j - numberPool[i]]
|
||||
}
|
||||
}
|
||||
}
|
||||
// last entry in the table is our awnser
|
||||
return memoizationTable.pop()
|
||||
}],
|
||||
["Spiralize Matrix", (data) => {
|
||||
const matrix = data
|
||||
|
||||
let resultSize = matrix.length * matrix[0].length
|
||||
let result = []
|
||||
|
||||
let topStop = 1
|
||||
let bottomStop = matrix.length - 1
|
||||
let leftStop = 0
|
||||
let rightStop = matrix[0].length - 1
|
||||
|
||||
let pos = [0, 0]
|
||||
let vector = [1, 0]
|
||||
|
||||
while (result.length < resultSize) {
|
||||
if (vector[0] == 1 && vector[1] == 0) {
|
||||
// going right
|
||||
while (pos[1] <= rightStop) {
|
||||
result.push(matrix[pos[0]][pos[1]])
|
||||
pos[1]++
|
||||
}
|
||||
rightStop--
|
||||
pos[0]++
|
||||
pos[1]--
|
||||
// go down next
|
||||
vector = [0, 1]
|
||||
}
|
||||
else if (vector[0] == -1 && vector[1] == 0) {
|
||||
// going left
|
||||
while (pos[1] >= leftStop) {
|
||||
result.push(matrix[pos[0]][pos[1]])
|
||||
pos[1]--
|
||||
}
|
||||
leftStop++;
|
||||
// go up next
|
||||
pos[0]--
|
||||
pos[1]++
|
||||
vector = [0, -1]
|
||||
}
|
||||
else if (vector[0] == 0 && vector[1] == 1) {
|
||||
// going down
|
||||
while (pos[0] <= bottomStop) {
|
||||
result.push(matrix[pos[0]][pos[1]])
|
||||
pos[0]++
|
||||
}
|
||||
bottomStop--
|
||||
// go right next
|
||||
pos[0]--
|
||||
pos[1]--
|
||||
vector = [-1, 0]
|
||||
}
|
||||
else if (vector[0] == 0 && vector[1] == -1) {
|
||||
// going up
|
||||
while (pos[0] >= topStop) {
|
||||
result.push(matrix[pos[0]][pos[1]])
|
||||
pos[0]--
|
||||
}
|
||||
topStop++
|
||||
// go left next
|
||||
pos[0]++
|
||||
pos[1]++
|
||||
vector = [1, 0]
|
||||
}
|
||||
else {
|
||||
ns.print("Invalid state")
|
||||
break
|
||||
}
|
||||
}
|
||||
return result
|
||||
}],
|
||||
["Array Jumping Game", (data) => {
|
||||
return solvers.get("Array Jumping Game II")(data) == 0 ? 0 : 1
|
||||
}],
|
||||
["Array Jumping Game II", (data) => {
|
||||
let indexJumpSums = [] // contain an array of to index this index can jump
|
||||
let position = 0
|
||||
let moves = [position]
|
||||
let goal = data.length - 1
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
indexJumpSums[i] = data[i] + i
|
||||
}
|
||||
|
||||
do {
|
||||
position = moves[moves.length - 1]
|
||||
const jumpValue = data[position]
|
||||
|
||||
if (goal - position <= jumpValue) { // check if the goal is within reach
|
||||
moves.push(goal) // reached the goal
|
||||
break
|
||||
}
|
||||
else {
|
||||
// find the largest jump possible at our current position
|
||||
let maxJump = position
|
||||
for (let i = position; i <= position + jumpValue; i++) {
|
||||
if (indexJumpSums[i] > indexJumpSums[maxJump]) {
|
||||
maxJump = i // this index allows us to jump the furthest
|
||||
}
|
||||
}
|
||||
if (maxJump == position) { // we haven't moved, we are stuck at this index
|
||||
moves = [0] // impossible!
|
||||
break
|
||||
}
|
||||
moves.push(maxJump)
|
||||
}
|
||||
} while (position != goal)
|
||||
|
||||
return moves.length - 1
|
||||
}],
|
||||
["Merge Overlapping Intervals", (data) => {
|
||||
let result = []
|
||||
let newInterval = []
|
||||
// for this algo to work, we need to sort the input by the min of the intervals
|
||||
data.sort((a, b) => a[0] - b[0])
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
// first interval
|
||||
if (newInterval.length == 0) {
|
||||
newInterval = data[i]
|
||||
}
|
||||
// if the min of the current inverval is less than the max of the newInterval
|
||||
if (data[i][0] <= newInterval[1]) {
|
||||
newInterval[1] = Math.max(data[i][1], newInterval[1]) // set the largest of the max to be the max of the newInterval
|
||||
}
|
||||
else {
|
||||
result.push(newInterval) // we are out of the new interval so push it to the result
|
||||
newInterval = data[i] // start a new newInterval
|
||||
}
|
||||
// last interval
|
||||
if (i == data.length - 1) {
|
||||
newInterval[1] = Math.max(data[i][1], newInterval[1])
|
||||
result.push(newInterval)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}],
|
||||
// ["Generate IP Addresses", (data) => {
|
||||
// }],
|
||||
["HammingCodes: Integer to Encoded Binary", (data) => {
|
||||
const dataBits = data.toString(2).split("").map(x => parseInt(x))
|
||||
|
||||
// compute the number of parity bits
|
||||
let parityBitsCount = 0
|
||||
while ((1 << parityBitsCount) < (dataBits.length + parityBitsCount + 1)) {
|
||||
parityBitsCount++
|
||||
}
|
||||
|
||||
// initialize encodedData and setup the data at the correct indices
|
||||
let encodedData = new Array(dataBits.length + parityBitsCount)
|
||||
let dataIndex = 0
|
||||
for (let i = 0; i < encodedData.length; i++) {
|
||||
// TODO: why this works?
|
||||
if (Math.log2(i + 1) % 1 == 0) { // check if i is power of 2
|
||||
// initialize parity bit
|
||||
encodedData[i] = 0
|
||||
}
|
||||
else {
|
||||
// set data bit
|
||||
encodedData[i] = dataBits[dataIndex++]
|
||||
}
|
||||
}
|
||||
|
||||
// set the parity bits
|
||||
for (let i = 0; i < parityBitsCount; i++) { // loop every parity bits
|
||||
const parityIndex = (1 << i) - 1;
|
||||
let parity = 0;
|
||||
|
||||
// xor together every bits this parity bit covers
|
||||
for (let j = parityIndex; j < encodedData.length; j += (parityIndex + 1) * 2) {
|
||||
for (let k = j; k < j + parityIndex + 1; k++) {
|
||||
parity ^= encodedData[k];
|
||||
}
|
||||
}
|
||||
|
||||
// set parity bit
|
||||
encodedData[parityIndex] = parity;
|
||||
}
|
||||
|
||||
// insert the overall parity bit at index 0
|
||||
encodedData.unshift(encodedData.reduce((x, y) => x ^ y))
|
||||
|
||||
// reduce encodedData to a string of 1s and 0s
|
||||
return encodedData.reduce((x, y) => x + y, "")
|
||||
}],
|
||||
["HammingCodes: Encoded Binary to Integer", (data) => {
|
||||
// https://www.youtube.com/watch?v=b3NxrZOu_CE
|
||||
|
||||
let encodedData = data.split("").map(x => parseInt(x))
|
||||
|
||||
// NOTE: bit at index 0 is the overall parity bit
|
||||
// we will not get a string with 2 errors, so can ignore it
|
||||
|
||||
// check for flipped bit and correct it
|
||||
let errorBitIndex = 0
|
||||
for (let i = 0; i < encodedData.length; i++) {
|
||||
if (encodedData[i] == 1) {
|
||||
errorBitIndex ^= i
|
||||
}
|
||||
}
|
||||
if (errorBitIndex != 0) {
|
||||
encodedData[errorBitIndex] ^= 1
|
||||
}
|
||||
|
||||
// extract data
|
||||
let dataBits = []
|
||||
for (let i = 0; i < encodedData.length; i++) {
|
||||
if ((i & (i - 1)) != 0 && i != 0) { // check if i is not power of 2
|
||||
// extract data bit
|
||||
dataBits.push(encodedData[i])
|
||||
}
|
||||
}
|
||||
|
||||
return parseInt(dataBits.reduce((x, y) => x + y, ""), 2).toString(10)
|
||||
}],
|
||||
["Encryption I: Caesar Cipher", (data) => {
|
||||
const charList = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
|
||||
const plaintext = data[0].split("")
|
||||
const key = data[1]
|
||||
|
||||
return plaintext.map(c => {
|
||||
const i = charList.indexOf(c)
|
||||
if (i >= 0) {
|
||||
return charList[(i + (26 - key)) % 26]
|
||||
}
|
||||
else {
|
||||
return ' '
|
||||
}
|
||||
}).reduce((x, y) => x + y, "")
|
||||
|
||||
}],
|
||||
["Encryption II: Vigenère Cipher", (data) => {
|
||||
const charList = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
|
||||
const cypher = (a, b) => charList[(charList.indexOf(a) + charList.indexOf(b)) % 26]
|
||||
const plaintext = data[0].split("")
|
||||
const key = data[1].split("")
|
||||
|
||||
let anwser = ""
|
||||
let keyIndex = 0
|
||||
for (const char of plaintext) {
|
||||
anwser += cypher(char, key[keyIndex++ % key.length])
|
||||
}
|
||||
return anwser
|
||||
}]
|
||||
])
|
||||
|
||||
// ns.codingcontract.createDummyContract("Find Largest Prime Factor") // ok
|
||||
// ns.codingcontract.createDummyContract("Subarray with Maximum Sum") // ok
|
||||
// ns.codingcontract.createDummyContract("Total Ways to Sum") // ok
|
||||
// ns.codingcontract.createDummyContract("Total Ways to Sum II") // ok
|
||||
// ns.codingcontract.createDummyContract("Spiralize Matrix") // ok
|
||||
// ns.codingcontract.createDummyContract("Array Jumping Game") // ok
|
||||
// ns.codingcontract.createDummyContract("Array Jumping Game II") //ok
|
||||
// ns.codingcontract.createDummyContract("Merge Overlapping Intervals") // ok
|
||||
// ns.codingcontract.createDummyContract("Generate IP Addresses")
|
||||
// ns.codingcontract.createDummyContract("Algorithmic Stock Trader I")
|
||||
// ns.codingcontract.createDummyContract("Algorithmic Stock Trader II")
|
||||
// ns.codingcontract.createDummyContract("Algorithmic Stock Trader III")
|
||||
// ns.codingcontract.createDummyContract("Algorithmic Stock Trader IV")
|
||||
// ns.codingcontract.createDummyContract("Minimum Path Sum in a Triangle")
|
||||
// ns.codingcontract.createDummyContract("Unique Paths in a Grid I")
|
||||
// ns.codingcontract.createDummyContract("Unique Paths in a Grid II")
|
||||
// ns.codingcontract.createDummyContract("Shortest Path in a Grid")
|
||||
// ns.codingcontract.createDummyContract("Sanitize Parentheses in Expression")
|
||||
// ns.codingcontract.createDummyContract("Find All Valid Math Expressions")
|
||||
// ns.codingcontract.createDummyContract("HammingCodes: Integer to Encoded Binary") // ok
|
||||
// ns.codingcontract.createDummyContract("HammingCodes: Encoded Binary to Integer") // ok
|
||||
// ns.codingcontract.createDummyContract("Proper 2-Coloring of a Graph")
|
||||
// ns.codingcontract.createDummyContract("Compression I: RLE Compression")
|
||||
// ns.codingcontract.createDummyContract("Compression II: LZ Decompression")
|
||||
// ns.codingcontract.createDummyContract("Compression III: LZ Compression")
|
||||
// ns.codingcontract.createDummyContract("Encryption I: Caesar Cipher") // ok
|
||||
// ns.codingcontract.createDummyContract("Encryption II: Vigenère Cipher") // ok
|
||||
|
||||
// const server = "home"
|
||||
// for (const contract of ns.ls(server, '.cct')) {
|
||||
// const contractType = ns.codingcontract.getContractType(contract, server)
|
||||
// ns.print(`found contract ${contract} of type "${contractType}" on server ${server}`)
|
||||
// if (solvers.has(contractType)) {
|
||||
// ns.print("attempting to solve")
|
||||
// const anwser = solvers.get(contractType)(ns.codingcontract.getData(contract, server))
|
||||
// const reward = ns.codingcontract.attempt(anwser, contract, server)
|
||||
// if (reward == "") {
|
||||
// ns.print(`ERROR: failed to solve contract! anwser = ${anwser}`)
|
||||
// break
|
||||
// }
|
||||
// else {
|
||||
// ns.print(reward)
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// ns.print(`WARNING: unknown contract type: ${contractType}`)
|
||||
// }
|
||||
// }
|
||||
|
||||
for (const server of getServers()) {
|
||||
for (const contract of ns.ls(server, '.cct')) {
|
||||
const contractType = ns.codingcontract.getContractType(contract, server)
|
||||
ns.print(`found contract ${contract} of type "${contractType}" on server ${server}`)
|
||||
if (solvers.has(contractType)) {
|
||||
const anwser = solvers.get(contractType)(ns.codingcontract.getData(contract, server))
|
||||
const reward = ns.codingcontract.attempt(anwser, contract, server)
|
||||
if (anwser == "") {
|
||||
ns.print(`ERROR: failed to solve contract! anwser = ${anwser}`)
|
||||
break
|
||||
}
|
||||
else {
|
||||
ns.print(reward)
|
||||
}
|
||||
}
|
||||
else {
|
||||
ns.print(`WARNING: unknown contract type: ${contractType}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,186 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
ns.clearLog()
|
||||
ns.tail()
|
||||
ns.disableLog('ALL')
|
||||
|
||||
const MAKE_MONEY = "Human Trafficking"
|
||||
const VIGILANTE_JUSTICE = "Vigilante Justice"
|
||||
const TERRORISM = "Terrorism"
|
||||
const TRAIN_COMBAT = "Train Combat"
|
||||
const TERRITORY_WARFARE = "Territory Warfare"
|
||||
const maxMemberCount = 12
|
||||
const memberNamePool = [
|
||||
"CJ",
|
||||
"Big Smoke",
|
||||
"Sweet",
|
||||
"Ryder",
|
||||
"OG Loc",
|
||||
"Big Bear",
|
||||
"Emmet",
|
||||
"Big Devil",
|
||||
"Little Devil",
|
||||
"Tony",
|
||||
"LB",
|
||||
"Madd Dogg",
|
||||
"Little Weasel",
|
||||
]
|
||||
|
||||
function estimateMemberPower(memberName) {
|
||||
const memberInfo = ns.gang.getMemberInformation(memberName)
|
||||
return Math.floor(Math.min(
|
||||
memberInfo.str_asc_mult,
|
||||
memberInfo.def_asc_mult,
|
||||
memberInfo.dex_asc_mult,
|
||||
memberInfo.agi_asc_mult,
|
||||
) / 1.5)
|
||||
}
|
||||
|
||||
// Credit: Mysteyes. https://discord.com/channels/415207508303544321/415207923506216971/940379724214075442
|
||||
function getAscendTreshold(memberName) {
|
||||
const memberInfo = ns.gang.getMemberInformation(memberName)
|
||||
const mult = Math.min(
|
||||
memberInfo.str_asc_mult,
|
||||
memberInfo.def_asc_mult,
|
||||
memberInfo.dex_asc_mult,
|
||||
memberInfo.agi_asc_mult,
|
||||
)
|
||||
if (mult < 1.632) return 1.6326;
|
||||
if (mult < 2.336) return 1.4315;
|
||||
if (mult < 2.999) return 1.284;
|
||||
if (mult < 3.363) return 1.2125;
|
||||
if (mult < 4.253) return 1.1698;
|
||||
if (mult < 4.860) return 1.1428;
|
||||
if (mult < 5.455) return 1.1225;
|
||||
if (mult < 5.977) return 1.0957;
|
||||
if (mult < 6.496) return 1.0869;
|
||||
if (mult < 7.008) return 1.0789;
|
||||
if (mult < 7.519) return 1.073;
|
||||
if (mult < 8.025) return 1.0673;
|
||||
if (mult < 8.513) return 1.0631;
|
||||
return 1.0591;
|
||||
}
|
||||
|
||||
function getMinAscensionResult(memberName) {
|
||||
const memberAscensionResult = ns.gang.getAscensionResult(memberName)
|
||||
return memberAscensionResult === undefined ? undefined : Math.min(
|
||||
memberAscensionResult.str,
|
||||
memberAscensionResult.def,
|
||||
memberAscensionResult.dex,
|
||||
memberAscensionResult.agi
|
||||
)
|
||||
}
|
||||
|
||||
function isAscensionReady(memberName) {
|
||||
return getMinAscensionResult(memberName) >= getAscendTreshold(memberName)
|
||||
}
|
||||
|
||||
function isRecruitementReady(relativePower, memberCount) {
|
||||
const res = relativePower / 3 > memberCount - 3 // TODO
|
||||
// ns.print(ascentionCount / 3)
|
||||
return res
|
||||
}
|
||||
|
||||
function getGangWarWinChances() {
|
||||
const gangInfo = ns.gang.getGangInformation()
|
||||
const otherGangNames = Object.keys(ns.gang.getOtherGangInformation()).filter(x => gangInfo.faction != x && x.territory > 0)
|
||||
const chancesToWin = otherGangNames.map(x => ns.gang.getChanceToWinClash(x))
|
||||
return Math.min(...chancesToWin)
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const gangInfo = ns.gang.getGangInformation()
|
||||
|
||||
ns.print("running loop")
|
||||
|
||||
// check for and recruit new members if available
|
||||
while (ns.gang.canRecruitMember()) {
|
||||
const availableNames = memberNamePool.filter(x => memberNamePool.includes(x))
|
||||
const memberName = availableNames[Math.floor(Math.random() * availableNames.length)]
|
||||
ns.gang.recruitMember(memberName)
|
||||
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
|
||||
}
|
||||
|
||||
const gangMembers = ns.gang.getMemberNames()
|
||||
|
||||
// check for and ascend members who are ready
|
||||
for (const memberName of gangMembers) {
|
||||
if (isAscensionReady(memberName)) {
|
||||
// ns.print("ascending " + memberName)
|
||||
ns.gang.ascendMember(memberName)
|
||||
}
|
||||
}
|
||||
|
||||
for (const memberName of gangMembers) {
|
||||
if (isAscensionReady(memberName)) {
|
||||
// ns.print("ascending " + memberName)
|
||||
ns.gang.ascendMember(memberName)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: equipement
|
||||
|
||||
|
||||
// gang war
|
||||
if (getGangWarWinChances() >= 0.6) {
|
||||
// go to war
|
||||
ns.gang.setTerritoryWarfare(true)
|
||||
}
|
||||
else if (getGangWarWinChances() <= 0.55) {
|
||||
// disengage
|
||||
ns.gang.setTerritoryWarfare(false)
|
||||
}
|
||||
|
||||
// score each member by their estimated power level
|
||||
let memberDatas = gangMembers.map(x => [x, estimateMemberPower(x)])
|
||||
memberDatas.sort((a, b) => b[1] - a[1])
|
||||
const targetRelativePower = memberDatas[0][1] - 1
|
||||
for (const memberData of memberDatas) {
|
||||
const memberName = memberData[0]
|
||||
const relativePower = memberData[1]
|
||||
|
||||
ns.print(memberName)
|
||||
// ns.print(getMinAscensionResult(memberName) / getAscendTreshold(memberName))
|
||||
// ns.print(isAscensionReady(memberName))
|
||||
|
||||
if (relativePower < targetRelativePower && relativePower < 40) {
|
||||
ns.print("assignment: catchup")
|
||||
// bring up newcomers to the same level than established members
|
||||
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
|
||||
}
|
||||
else if (getMinAscensionResult(memberName) / getAscendTreshold(memberName) < 0.9) { // TODO: arbitrary constant to be ajusted
|
||||
ns.print("assignment: level up after ascention")
|
||||
// train a member back up after ascention
|
||||
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
|
||||
}
|
||||
else if (gangInfo.wantedPenalty < 0) {
|
||||
ns.print("assignment: reduce wanted level")
|
||||
// reduce wanted level
|
||||
ns.print(gangInfo.wantedPenalty)
|
||||
ns.gang.setMemberTask(memberName, VIGILANTE_JUSTICE)
|
||||
}
|
||||
else if (gangMembers.length < maxMemberCount && isRecruitementReady(relativePower, gangMembers.length)) {
|
||||
ns.print("assignment: gain respect")
|
||||
// gain respect to recruit new member
|
||||
ns.gang.setMemberTask(memberName, TERRORISM)
|
||||
}
|
||||
else if (relativePower < 20) { // TODO
|
||||
ns.print("assignment: train to next ascension")
|
||||
// train up to next ascention
|
||||
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
|
||||
}
|
||||
else if (gangInfo.territory < 1 && getGangWarWinChances() < 0.95) {
|
||||
ns.print("assignment: gang war")
|
||||
// prepare for gang war
|
||||
ns.gang.setMemberTask(memberName, TERRITORY_WARFARE)
|
||||
}
|
||||
else {
|
||||
ns.print("assignment: make money")
|
||||
// go for the money
|
||||
ns.gang.setMemberTask(memberName, MAKE_MONEY) // TODO
|
||||
}
|
||||
}
|
||||
|
||||
await ns.sleep(5000)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
ns.tprint(ns.heart.break())
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/** @param {NS} ns */
|
||||
const importantHosts = ["CSEC", "avmnite-02h", "I.I.I.I", "run4theh111z", "w0r1d_d43m0n"]
|
||||
|
||||
class ServerTreeNode {
|
||||
constructor(ns, serverName) {
|
||||
this.ns = ns
|
||||
this.serverName = serverName
|
||||
this.children = []
|
||||
this.info = ns.getServer(serverName)
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
[this.serverName]: {
|
||||
"info": this.info,
|
||||
"children": this.children.map(x => x.toObject()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return JSON.stringify(this.toObject())
|
||||
}
|
||||
|
||||
printTable(padding = "", pointer = "", lastNodeOfParent = true) {
|
||||
if (this.serverName == "home") {
|
||||
this.ns.tprintf("--R-P--LVL---")
|
||||
}
|
||||
this.ns.tprintf(
|
||||
"%s %s %s %4s %s",
|
||||
importantHosts.includes(this.serverName) ? "!" : " ",
|
||||
this.info.hasAdminRights ? "Y" : "N",
|
||||
this.info.openPortCount,
|
||||
this.info.requiredHackingSkill,
|
||||
padding + pointer + this.serverName
|
||||
)
|
||||
|
||||
// prepare padding for children
|
||||
let newPadding = padding
|
||||
if (this.serverName != "home") {
|
||||
if (lastNodeOfParent) {
|
||||
newPadding += " "
|
||||
}
|
||||
else {
|
||||
newPadding += "| "
|
||||
}
|
||||
}
|
||||
|
||||
// print children
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
if (i == this.children.length - 1) {
|
||||
this.children[i].printTable(newPadding, "└─", true)
|
||||
}
|
||||
else {
|
||||
this.children[i].printTable(newPadding, "├─", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getServerTree(ns, serverNameToScan = "home", scannedServerList = ["home"]) {
|
||||
const currentNode = new ServerTreeNode(ns, serverNameToScan)
|
||||
for (const serverName of ns.scan(serverNameToScan)) {
|
||||
// check if we have already scanned this server
|
||||
if (!scannedServerList.includes(serverName)) {
|
||||
// add to the list to avoid duplicates
|
||||
scannedServerList.push(serverName)
|
||||
// scan the node and add as a child to the current node
|
||||
currentNode.children.push(getServerTree(ns, serverName, scannedServerList))
|
||||
}
|
||||
}
|
||||
return currentNode
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import {getServerTree} from '/lib/ServerTree.js'
|
||||
|
||||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const serverTree = getServerTree(ns)
|
||||
ns.write("dump/map.txt", serverTree.toJson(), 'w')
|
||||
if (ns.args[0] != "noout") {
|
||||
ns.tprintf("")
|
||||
serverTree.printTable()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
// ns.clearLog()
|
||||
// ns.tail()
|
||||
ns.disableLog('ALL')
|
||||
|
||||
const pwnMethods = []
|
||||
if (ns.fileExists("BruteSSH.exe")) {
|
||||
pwnMethods.push(ns.brutessh)
|
||||
}
|
||||
if (ns.fileExists("FTPCrack.exe")) {
|
||||
pwnMethods.push(ns.ftpcrack)
|
||||
}
|
||||
if (ns.fileExists("HTTPWorm.exe")) {
|
||||
pwnMethods.push(ns.httpworm)
|
||||
}
|
||||
if (ns.fileExists("relaySMTP.exe")) {
|
||||
pwnMethods.push(ns.relaysmtp)
|
||||
}
|
||||
if (ns.fileExists("SQLInject.exe")) {
|
||||
pwnMethods.push(ns.sqlinject)
|
||||
}
|
||||
|
||||
const autopwn = (hostnameToAnalyze = "home", serverList = ["home"]) => {
|
||||
for (const hostname of ns.scan(hostnameToAnalyze)) {
|
||||
// check if we alreay have added this server to the list
|
||||
if (!serverList.includes(hostname)) {
|
||||
// nuke if able
|
||||
if (ns.hasRootAccess(hostname)) {
|
||||
ns.print(`already pwned ${hostname}`)
|
||||
}
|
||||
else if (ns.getServerNumPortsRequired(hostname) <= pwnMethods.length) {
|
||||
ns.print(`pwning ${hostname}`)
|
||||
for (let i = 0; i < ns.getServerNumPortsRequired(hostname); i++) {
|
||||
pwnMethods[i](hostname)
|
||||
}
|
||||
ns.nuke(hostname)
|
||||
}
|
||||
else {
|
||||
ns.print(`missing requirements to pwn ${hostname}. (${pwnMethods.length} < ${ns.getServerNumPortsRequired(hostname)})`)
|
||||
}
|
||||
|
||||
// add to the list
|
||||
serverList.push(hostname)
|
||||
// pwn childen
|
||||
autopwn(hostname, serverList)
|
||||
}
|
||||
}
|
||||
}
|
||||
autopwn()
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const maxServerCount = ns.getPurchasedServerLimit()
|
||||
let serverCount = ns.getPurchasedServers().length
|
||||
|
||||
ns.print(`we have ${serverCount} servers`)
|
||||
ns.print(`we can have ${maxServerCount} servers`)
|
||||
|
||||
const getLowestServerRAM = (servers) => {
|
||||
return Math.min.apply(Math, servers.map(ns.getServerMaxRam))
|
||||
}
|
||||
|
||||
if (serverCount < maxServerCount) {
|
||||
// create missing servers
|
||||
const requestedRAM = 2
|
||||
const serverCost = ns.getPurchasedServerCost(requestedRAM)
|
||||
ns.print(`server cost is ${ns.formatNumber(serverCost)}`)
|
||||
|
||||
while (serverCount < maxServerCount) {
|
||||
const serverName = `worker-${serverCount}`
|
||||
if (ns.getServerMoneyAvailable("home") >= serverCost) {
|
||||
ns.tprint(`racking server ${serverName}`)
|
||||
ns.purchaseServer(serverName, requestedRAM)
|
||||
serverCount++
|
||||
}
|
||||
else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serverCount == maxServerCount) {
|
||||
const servers = ns.getPurchasedServers()
|
||||
let success = true
|
||||
do {
|
||||
// upgrade servers
|
||||
const requestedRAM = getLowestServerRAM(servers) * 2
|
||||
for (const serverName of servers) {
|
||||
const serverRAM = ns.getServerMaxRam(serverName)
|
||||
if (serverRAM < requestedRAM) {
|
||||
const serverCost = ns.getPurchasedServerUpgradeCost(serverName, requestedRAM)
|
||||
ns.print(`selected server ${serverName} to be upgraded (current RAM: ${ns.formatRam(serverRAM)}, upgraded RAM: ${ns.formatRam(requestedRAM)}, upgrade cost: ${ns.formatNumber(serverCost)})`)
|
||||
if (ns.getServerMoneyAvailable("home") >= serverCost) {
|
||||
ns.tprint(`upgrading server ${serverName}`)
|
||||
ns.killall(serverName) //TODO: make it more graceful
|
||||
ns.upgradePurchasedServer(serverName, requestedRAM)
|
||||
}
|
||||
else {
|
||||
success = false
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (success)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
await ns.grow(ns.args[2], { "threads": ns.args[1] })
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
await ns.hack(ns.args[2], { "threads": ns.args[1] })
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
await ns.weaken(ns.args[2], { "threads": ns.args[1] })
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
const styles = ns.ui.getStyles();
|
||||
styles.fontFamily = 'Hack';
|
||||
ns.ui.setStyles(styles);
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
"types": ["vite/client"],
|
||||
"strict": true,
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"baseUrl": ".",
|
||||
|
@ -19,5 +20,5 @@
|
|||
"@ns": ["./NetscriptDefinitions.d.ts"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "NetscriptDefinitions.d.ts", "vite.config.ts"]
|
||||
"include": ["src/**/*.ts", "src/**/*.js", "NetscriptDefinitions.d.ts", "vite.config.ts", "vite.config.js"]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-env node */
|
||||
import { defineConfig } from 'viteburner';
|
||||
import { resolve } from 'path';
|
||||
|
||||
|
|
Loading…
Reference in New Issue