Skip to content

Instantly share code, notes, and snippets.

@smolgumball
Last active December 27, 2021 03:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smolgumball/b05fff8b56ea7b22671f7073f0f88e2b to your computer and use it in GitHub Desktop.
Save smolgumball/b05fff8b56ea7b22671f7073f0f88e2b to your computer and use it in GitHub Desktop.
Vue in BitBurner
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)
}
}
}
/** @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`)
}
}
}
}
export const emit = (type, data) => {
let _window = eval('window')
if (_window.mitt) {
_window.mitt.emit(type, data)
}
}
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