Last active
December 27, 2021 03:47
-
-
Save smolgumball/b05fff8b56ea7b22671f7073f0f88e2b to your computer and use it in GitHub Desktop.
Vue in BitBurner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let _window = eval('window') | |
let _document = eval('document') | |
let vueAppId = 'vueApp' | |
/** @param {NS} ns **/ | |
export async function main(ns) { | |
let app = _window._vueApp || null | |
if (!_window.Vue) { | |
_window.Vue = await import( | |
'https://cdn.jsdelivr.net/npm/vue@3.2.26/dist/vue.esm-browser.js' | |
) | |
} | |
if (!_window.mittModule) { | |
_window.mittModule = await import( | |
'https://unpkg.com/mitt@3.0.0/dist/mitt.mjs' | |
) | |
} | |
_window.mitt = mittModule.default() | |
if (app) { | |
try { | |
app.unmount() | |
_document | |
.querySelectorAll(`#${vueAppId}-wrap`) | |
.forEach((x) => x.remove()) | |
} catch (error) { | |
console.log(`Issue unmounting _window._vueApp:`, error) | |
} | |
} | |
// Create Vue app + persist in _vueApp global | |
app = Vue.createApp({}) | |
_window._vueApp = app | |
/** | |
* Define Vue components | |
*/ | |
app.component('command-palette', { | |
style: ` | |
.command_palette { | |
display: flex; | |
flex-wrap: wrap; | |
margin-bottom: 30px; | |
.command { | |
cursor: pointer; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
background-color: fade-out(white, 0.95); | |
padding: 2em 0.5em; | |
margin-right: 2.5%; | |
margin-bottom: 0.25em; | |
width: calc(42.5% / 2); | |
&:hover { | |
background-color: fade-out(white, 0.85); | |
} | |
} | |
} | |
`, | |
template: ` | |
<div class='command_palette'> | |
<div | |
v-for='command in commands' | |
:key='command.name' | |
@click='run(command)' | |
class='command' | |
> | |
{{ command.name }} | |
</div> | |
</div> | |
`, | |
data() { | |
return { | |
commands: null, | |
} | |
}, | |
methods: { | |
run(command) { | |
this.commands.find((x) => x.name == command.name).main() | |
}, | |
}, | |
created() { | |
this.commands = [ | |
{ | |
name: 'showPlayerInfo', | |
main: () => { | |
console.log('Hello from showPlayerInfo', this.commands) | |
}, | |
}, | |
{ | |
name: 'showScriptInfo', | |
main: () => { | |
console.log('Hello from showScriptInfo', this.commands) | |
}, | |
}, | |
] | |
}, | |
}) | |
/** | |
* Define Vue components | |
*/ | |
app.component('store-display', { | |
style: ` | |
.store_display { | |
display: flex; | |
flex-wrap: wrap; | |
justify-content: space-between; | |
margin-bottom: 30px; | |
.key_display { | |
width: 47.5%; | |
padding: 0.5em; | |
margin-bottom: 15px; | |
background-color: fade-out(white, 0.95); | |
h3 { | |
margin-left: 0.5em; | |
} | |
code { | |
display: block; | |
overflow: auto; | |
height: 40vh; | |
background-color: black; | |
padding: 0.25em 0.8em; | |
} | |
pre { | |
font-family: inherit; | |
color: lawngreen; | |
} | |
em { | |
font-size: 150%; | |
font-weight: bold; | |
font-style: normal; | |
display: block; | |
margin-top: 1em; | |
margin-left: .15em; | |
} | |
.script_info { | |
display: block; | |
margin: 0 -.8em; | |
background-color: fade-out(midnightblue, 0.4); | |
padding: .8em; | |
&:nth-child(2n) { | |
background-color: fade-out(midnightblue, 0.3); | |
} | |
} | |
} | |
} | |
`, | |
template: ` | |
<div class='store_display'> | |
<div | |
v-for='(val, key) in store' | |
:key='key' | |
class='key_display' | |
> | |
<h3>{{ key }}</h3> | |
<template v-if='key == "recentEvents"'> | |
<code><pre><span v-for='logItem in val' :key='logItem.epoch'>{{ prepLogItem(logItem) }}</span></pre></code> | |
</template> | |
<template v-else-if='key == "scriptInfo"'> | |
<code><pre> | |
<span v-for='(serverInfo, hostname) in val' :key='hostname'><em>{{hostname}}</em> | |
<span class='script_info' v-for='script in serverInfo' v-if='serverInfo.length' :key='script.pid'>{{ prepScriptInfo(script) }}</span><span class='script_info' v-else>no scripts running</span> | |
</span> | |
</pre></code> | |
</template> | |
<template v-else> | |
<code><pre>{{ toJson(val) }}</pre></code> | |
</template> | |
</div> | |
</div> | |
`, | |
data() { | |
return { | |
commands: null, | |
store: null, | |
} | |
}, | |
methods: { | |
run(command) { | |
this.commands.find((x) => x.name == command.name).main() | |
}, | |
toJson(val) { | |
return JSON.stringify(val, null, ' ') | |
}, | |
prepLogItem(logItem) { | |
let toRet = '' | |
toRet += `${logItem.time} - ${logItem.type}\n` | |
toRet += `${this.toJson(logItem.event)}\n\n` | |
return toRet | |
}, | |
prepScriptInfo(scriptInfo) { | |
let toRet = '' | |
let scriptDetails = { | |
...scriptInfo, | |
threads: undefined, | |
pid: undefined, | |
filename: undefined, | |
} | |
toRet += `${scriptInfo.filename} (t=${scriptInfo.threads}) - PID: ${scriptInfo.pid}\n` | |
toRet += `${this.toJson(scriptDetails)}\n` | |
return toRet | |
}, | |
}, | |
created() { | |
console.log('Reference store') | |
this.store = _window._vueApp.$store | |
this.commands = [ | |
{ | |
name: 'showPlayerInfo', | |
main: () => { | |
console.log('Hello from showPlayerInfo', this.commands) | |
}, | |
}, | |
{ | |
name: 'showScriptInfo', | |
main: () => { | |
console.log('Hello from showScriptInfo', this.commands) | |
}, | |
}, | |
] | |
}, | |
}) | |
app.component('app-root', { | |
style: ` | |
& { | |
color: white; | |
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", | |
Consolas, "Courier New", Courier, monospace, "Times New Roman"; | |
} | |
.layout { | |
z-index: 1500; | |
position: fixed; | |
top: 0; | |
right: 0; | |
bottom: 0; | |
left: 0; | |
display: flex; | |
pointer-events: none; | |
} | |
.inner { | |
width: 100%; | |
margin: 2.5vh 10vw; | |
padding: 2em; | |
border: 4px solid lightslategray; | |
background-color: darkslategray; | |
overflow: auto; | |
pointer-events: auto; | |
} | |
.trigger { | |
z-index: 1500; | |
position: fixed; | |
bottom: 0.5em; | |
left: 1em; | |
width: auto; | |
height: auto; | |
padding: 0.5em; | |
margin: 0; | |
background-color: white; | |
button { | |
background-color: black; | |
color: currentColor; | |
font-weight: bold; | |
border: none; | |
padding: 0.5em 1em; | |
} | |
} | |
`, | |
template: ` | |
<div class='app_root'> | |
<div class='layout' v-show='isOpen'> | |
<div class='inner'> | |
<command-palette /> | |
<store-display /> | |
</div> | |
</div> | |
<div class='trigger'> | |
<button @click='toggleDisplay'> | |
{{toggleWord}} Vue App | |
</button> | |
</div> | |
</div> | |
`, | |
data() { | |
return { | |
isOpen: false, | |
maxNumRecentEvents: 100, | |
store: { | |
recentEvents: [], | |
scriptInfo: {}, | |
playerInfo: {}, | |
homeInfo: {}, | |
}, | |
} | |
}, | |
computed: { | |
toggleWord() { | |
return this.isOpen ? 'Close' : 'Open' | |
}, | |
}, | |
methods: { | |
toggleDisplay() { | |
this.isOpen = !this.isOpen | |
}, | |
handleEvent(type, event) { | |
let date = new Date() | |
this.store.recentEvents = [ | |
{ | |
type, | |
time: date.toLocaleTimeString(), | |
event, | |
epoch: +date, | |
}, | |
...this.store.recentEvents.slice( | |
0, | |
this.maxNumRecentEvents - 1 | |
), | |
] | |
}, | |
}, | |
created() { | |
console.log('Assign store', this.$data.store) | |
_window._vueApp.$store = this.$data.store | |
// Register event handlers | |
_window.mitt.on('*', this.handleEvent) | |
}, | |
}) | |
// Gather styles from individual components | |
let appStyles = [] | |
Object.values(app._context.components).forEach((cmp) => { | |
appStyles.push(cmp.style.trim()) | |
}) | |
// Build Vue app container in DOM | |
let body = _document.querySelector('body') | |
body.insertAdjacentHTML( | |
'afterbegin', | |
` | |
<div id='${vueAppId}-wrap'> | |
<div id='${vueAppId}'> | |
<app-root /> | |
</div> | |
<style type='text/scss'>.app_root { ${appStyles.join( | |
'\n' | |
)} }</style> | |
</div> | |
` | |
) | |
// Mount Vue app | |
app.mount(`#${vueAppId}`) | |
// Add SCSS compiler for in-browser compilation (kekw) | |
addScssCompiler() | |
// Kick off data-watcher | |
ns.run('2-watcher.js') | |
return app | |
} | |
function addScssCompiler() { | |
function findAndConvertTags() { | |
// Restore `window.define` | |
_window.define = _window._defineBak | |
var sassTags = _document.getElementsByTagName('style') | |
for (var i = sassTags.length - 1; i >= 0; i--) { | |
if ( | |
sassTags[i].type.toLowerCase() === 'text/scss' && | |
sassTags[i]._scssCompiled !== true | |
) { | |
Sass.compile(sassTags[i].innerHTML, function (compiledCSS) { | |
var rawStyle = _document.createElement('style') | |
rawStyle.type = 'text/css' | |
rawStyle.innerHTML = compiledCSS.text | |
_document | |
.getElementById(`${vueAppId}-wrap`) | |
.appendChild(rawStyle) | |
}) | |
sassTags[i]._scssCompiled = true | |
} | |
} | |
} | |
if (typeof _window !== 'undefined' && typeof _document !== 'undefined') { | |
if (typeof Sass === 'undefined' || typeof Sass.compile !== 'function') { | |
var sassJSScript = _document.createElement('script') | |
sassJSScript.type = 'text/javascript' | |
sassJSScript.src = | |
'https://cdn.jsdelivr.net/npm/sass.js@0.11.1/dist/sass.sync.js' | |
sassJSScript.onload = findAndConvertTags | |
// Monkey patch `window.define` to ensure sass installs properly | |
_window._defineBak = _window.define | |
_window.define = undefined | |
_document.head.appendChild(sassJSScript) | |
} else { | |
findAndConvertTags() | |
} | |
if ( | |
typeof _window !== 'undefined' && | |
_window !== null && | |
typeof Sass !== 'undefined' && | |
typeof Sass.compile === 'function' | |
) { | |
setTimeout(findAndConvertTags, 0) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @param {NS} ns **/ | |
export async function main(ns) { | |
ns.disableLog('sleep') | |
while (true) { | |
setStore('playerInfo', ns.getPlayer()) | |
setStore('homeInfo', ns.getServer('home')) | |
await ns.sleep(1000) | |
} | |
/** | |
* Sets a given key in the global store to a value | |
*/ | |
function setStore(key, value) { | |
let _window = eval('window') | |
let app = _window._vueApp | |
if (app) { | |
if (app.$store) { | |
app.$store[key] = value | |
} else { | |
console.error(`_window._vueApp.\$store doesn't exist`) | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const emit = (type, data) => { | |
let _window = eval('window') | |
if (_window.mitt) { | |
_window.mitt.emit(type, data) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { emit } from 'lib.js' | |
/** @param {NS} ns **/ | |
export async function main(ns) { | |
let target = ns.args[0] | |
while (true) { | |
let securityThen = ns.getServerSecurityLevel(target) | |
await ns.weaken(target) | |
let securityNow = ns.getServerSecurityLevel(target) | |
emit('weakenSuccess', { | |
target, | |
change: `${ns.nFormat(securityThen, '(0.00)')} -> ${ns.nFormat( | |
securityNow, | |
'(0.00)' | |
)}`, | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment