feat(minecraft): hook up prismarine-viewer
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
"es-toolkit": "^1.44.0",
|
||||
"eventemitter3": "^5.0.4",
|
||||
"minecraft-data": "^3.102.3",
|
||||
"canvas": "^3.2.1",
|
||||
"mineflayer": "^4.33.0",
|
||||
"mineflayer-armor-manager": "^2.0.1",
|
||||
"mineflayer-auto-eat": "^5.0.3",
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import type { Bot } from 'mineflayer'
|
||||
|
||||
import { useLogger } from '../utils/logger'
|
||||
|
||||
interface MineflayerViewerOptions {
|
||||
port: number
|
||||
firstPerson?: boolean
|
||||
}
|
||||
|
||||
export function setupMineflayerViewer(mineflayer: { bot: Bot }, options: MineflayerViewerOptions): void {
|
||||
let isViewerStarted = false
|
||||
|
||||
mineflayer.bot.once('spawn', async () => {
|
||||
if (isViewerStarted)
|
||||
return
|
||||
|
||||
isViewerStarted = true
|
||||
|
||||
const logger = useLogger()
|
||||
try {
|
||||
const { mineflayer: mineflayerViewer } = await import('prismarine-viewer')
|
||||
|
||||
mineflayerViewer(mineflayer.bot, {
|
||||
port: options.port,
|
||||
firstPerson: options.firstPerson ?? true,
|
||||
})
|
||||
|
||||
logger.log(`Mineflayer viewer running at http://localhost:${options.port}`)
|
||||
}
|
||||
catch (err) {
|
||||
const e = err as NodeJS.ErrnoException
|
||||
const message = typeof e.message === 'string' ? e.message : ''
|
||||
const isCanvasMissing = e?.code === 'MODULE_NOT_FOUND' && message.includes('\'canvas\'')
|
||||
const isCanvasBinaryMissing = e?.code === 'MODULE_NOT_FOUND' && message.includes('canvas.node')
|
||||
|
||||
if (isCanvasMissing || isCanvasBinaryMissing) {
|
||||
logger.log('Mineflayer viewer disabled: node-canvas is not available')
|
||||
return
|
||||
}
|
||||
|
||||
logger.errorWithError('Failed to start mineflayer viewer', e as Error)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -18,6 +18,24 @@ import { useLogger } from '../utils/logger'
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
|
||||
function createViewerHtml(targetUrl: string): string {
|
||||
return `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Mineflayer Viewer</title>
|
||||
<style>
|
||||
html, body { height: 100%; margin: 0; }
|
||||
iframe { width: 100%; height: 100%; border: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="${targetUrl}" allow="fullscreen" referrerpolicy="no-referrer"></iframe>
|
||||
</body>
|
||||
</html>`
|
||||
}
|
||||
|
||||
interface ClientInfo {
|
||||
ws: WebSocket
|
||||
id: string
|
||||
@@ -165,6 +183,16 @@ export class DebugServer {
|
||||
return
|
||||
}
|
||||
|
||||
if (req.method === 'GET' && (req.url === '/viewer' || req.url?.startsWith('/viewer?'))) {
|
||||
const html = createViewerHtml('http://localhost:3007')
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
'Cache-Control': 'no-cache',
|
||||
})
|
||||
res.end(html)
|
||||
return
|
||||
}
|
||||
|
||||
// Serve static files from web directory
|
||||
let filePath = req.url === '/' ? '/index.html' : req.url || '/index.html'
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { initBot } from './composables/bot'
|
||||
import { config, initEnv } from './composables/config'
|
||||
import { createNeuriAgent } from './composables/neuri'
|
||||
import { DebugService } from './debug'
|
||||
import { setupMineflayerViewer } from './debug/mineflayer-viewer'
|
||||
import { wrapPlugin } from './libs/mineflayer'
|
||||
import { initLogger, useLogger } from './utils/logger'
|
||||
|
||||
@@ -38,6 +39,8 @@ async function main() {
|
||||
],
|
||||
})
|
||||
|
||||
setupMineflayerViewer(bot, { port: 3007, firstPerson: true })
|
||||
|
||||
// Connect airi server
|
||||
const airiClient = new Client({
|
||||
name: config.airi.clientName,
|
||||
|
||||
Reference in New Issue
Block a user