|
|
| require('./lib/chat')
|
|
|
| require('./lib/menus/components/button')
|
| require('./lib/menus/components/edit_box')
|
| require('./lib/menus/components/slider')
|
| require('./lib/menus/components/hotbar')
|
| require('./lib/menus/components/health_bar')
|
| require('./lib/menus/components/food_bar')
|
| require('./lib/menus/components/breath_bar')
|
| require('./lib/menus/components/debug_overlay')
|
| require('./lib/menus/components/playerlist_overlay')
|
| require('./lib/menus/components/bossbars_overlay')
|
| require('./lib/menus/hud')
|
| require('./lib/menus/play_screen')
|
| require('./lib/menus/pause_screen')
|
| require('./lib/menus/loading_screen')
|
| require('./lib/menus/keybinds_screen')
|
| require('./lib/menus/options_screen')
|
| require('./lib/menus/title_screen')
|
|
|
| const net = require('net')
|
| const Cursor = require('./lib/cursor')
|
|
|
|
|
| process.versions.node = '14.0.0'
|
|
|
| const mineflayer = require('mineflayer')
|
| const { WorldView, Viewer } = require('prismarine-viewer/viewer')
|
| const pathfinder = require('mineflayer-pathfinder')
|
| const { Vec3 } = require('vec3')
|
| global.THREE = require('three')
|
| const { initVR } = require('./lib/vr')
|
|
|
| if ('serviceWorker' in navigator) {
|
| window.addEventListener('load', () => {
|
| navigator.serviceWorker.register('/service-worker.js').then(registration => {
|
| console.log('SW registered: ', registration)
|
| }).catch(registrationError => {
|
| console.log('SW registration failed: ', registrationError)
|
| })
|
| })
|
| }
|
|
|
| const maxPitch = 0.5 * Math.PI
|
| const minPitch = -0.5 * Math.PI
|
|
|
|
|
| const renderer = new THREE.WebGLRenderer()
|
| renderer.setPixelRatio(window.devicePixelRatio || 1)
|
| renderer.setSize(window.innerWidth, window.innerHeight)
|
| document.body.appendChild(renderer.domElement)
|
|
|
|
|
| const viewer = new Viewer(renderer)
|
|
|
|
|
| function addPanoramaCubeMap () {
|
| let time = 0
|
| viewer.camera = new THREE.PerspectiveCamera(85, window.innerWidth / window.innerHeight, 0.05, 1000)
|
| viewer.camera.updateProjectionMatrix()
|
| viewer.camera.position.set(0, 0, 0)
|
| viewer.camera.rotation.set(0, 0, 0)
|
| const panorGeo = new THREE.BoxGeometry(1000, 1000, 1000)
|
|
|
| const loader = new THREE.TextureLoader()
|
| const panorMaterials = [
|
| new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_1.png'), transparent: true, side: THREE.DoubleSide }),
|
| new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_3.png'), transparent: true, side: THREE.DoubleSide }),
|
| new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_4.png'), transparent: true, side: THREE.DoubleSide }),
|
| new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_5.png'), transparent: true, side: THREE.DoubleSide }),
|
| new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_0.png'), transparent: true, side: THREE.DoubleSide }),
|
| new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_2.png'), transparent: true, side: THREE.DoubleSide })
|
| ]
|
|
|
| const panoramaBox = new THREE.Mesh(panorGeo, panorMaterials)
|
|
|
| panoramaBox.onBeforeRender = () => {
|
| time += 0.01
|
| panoramaBox.rotation.y = Math.PI + time * 0.01
|
| panoramaBox.rotation.z = Math.sin(-time * 0.001) * 0.001
|
| }
|
|
|
| const group = new THREE.Object3D()
|
| group.add(panoramaBox)
|
|
|
| const Entity = require('prismarine-viewer/viewer/lib/entity/Entity')
|
| for (let i = 0; i < 42; i++) {
|
| const m = new Entity('1.16.4', 'squid').mesh
|
| m.position.set(Math.random() * 30 - 15, Math.random() * 20 - 10, Math.random() * 10 - 17)
|
| m.rotation.set(0, Math.PI + Math.random(), -Math.PI / 4, 'ZYX')
|
| const v = Math.random() * 0.01
|
| m.children[0].onBeforeRender = () => {
|
| m.rotation.y += v
|
| m.rotation.z = Math.cos(panoramaBox.rotation.y * 3) * Math.PI / 4 - Math.PI / 2
|
| }
|
| group.add(m)
|
| }
|
|
|
| viewer.scene.add(group)
|
| return group
|
| }
|
|
|
| const panoramaCubeMap = addPanoramaCubeMap()
|
|
|
| function removePanorama () {
|
| viewer.camera = new THREE.PerspectiveCamera(document.getElementById('options-screen').fov, window.innerWidth / window.innerHeight, 0.1, 1000)
|
| viewer.camera.updateProjectionMatrix()
|
| viewer.scene.remove(panoramaCubeMap)
|
| }
|
|
|
| let animate = () => {
|
| window.requestAnimationFrame(animate)
|
| viewer.update()
|
| renderer.render(viewer.scene, viewer.camera)
|
| }
|
| animate()
|
|
|
| window.addEventListener('resize', () => {
|
| viewer.camera.aspect = window.innerWidth / window.innerHeight
|
| viewer.camera.updateProjectionMatrix()
|
| renderer.setSize(window.innerWidth, window.innerHeight)
|
| })
|
|
|
| const showEl = (str) => { document.getElementById(str).style = 'display:block' }
|
| async function main () {
|
| const menu = document.getElementById('play-screen')
|
|
|
| menu.addEventListener('connect', e => {
|
| const options = e.detail
|
| menu.style = 'display: none;'
|
| showEl('loading-screen')
|
| removePanorama()
|
| connect(options)
|
| })
|
| }
|
|
|
| async function connect (options) {
|
| const loadingScreen = document.getElementById('loading-screen')
|
|
|
| const hud = document.getElementById('hud')
|
| const chat = hud.shadowRoot.querySelector('#chat')
|
| const debugMenu = hud.shadowRoot.querySelector('#debug-overlay')
|
| const optionsScrn = document.getElementById('options-screen')
|
| const keyBindScrn = document.getElementById('keybinds-screen')
|
| const gameMenu = document.getElementById('pause-screen')
|
|
|
| const viewDistance = optionsScrn.renderDistance
|
| const hostprompt = options.server
|
| const proxyprompt = options.proxy
|
| const username = options.username
|
| const password = options.password
|
|
|
| let host, port, proxy, proxyport
|
| if (!hostprompt.includes(':')) {
|
| host = hostprompt
|
| port = 25565
|
| } else {
|
| [host, port] = hostprompt.split(':')
|
| port = parseInt(port, 10)
|
| }
|
|
|
| if (!proxyprompt.includes(':')) {
|
| proxy = proxyprompt
|
| proxyport = undefined
|
| } else {
|
| [proxy, proxyport] = proxyprompt.split(':')
|
| proxyport = parseInt(proxyport, 10)
|
| }
|
| console.log(`connecting to ${host} ${port} with ${username}`)
|
|
|
| if (proxy) {
|
| console.log(`using proxy ${proxy} ${proxyport}`)
|
| net.setProxy({ hostname: proxy, port: proxyport })
|
| }
|
|
|
| loadingScreen.status = 'Logging in'
|
|
|
| const errorAbortController = new AbortController()
|
| window.addEventListener('unhandledrejection', (e) => {
|
| handleError(e.reason)
|
| })
|
| window.addEventListener('error', (e) => {
|
| handleError(e.message)
|
| })
|
| const bot = mineflayer.createBot({
|
| host,
|
| port,
|
| version: options.botVersion === '' ? false : options.botVersion,
|
| username,
|
| password,
|
| viewDistance: 'tiny',
|
| checkTimeoutInterval: 240 * 1000,
|
| noPongTimeout: 240 * 1000,
|
| closeTimeout: 240 * 1000
|
| })
|
| hud.preload(bot)
|
|
|
| const handleError = (err) => {
|
| console.log('Encountered error!', err)
|
| loadingScreen.status = `Error encountered. Error message: ${err}. Please reload the page`
|
| loadingScreen.style = 'display: block;'
|
| loadingScreen.hasError = true
|
| }
|
| bot.on('error', handleError)
|
|
|
| bot.on('kicked', (kickReason) => {
|
| console.log('User was kicked!', kickReason)
|
| loadingScreen.status = `The Minecraft server kicked you. Kick reason: ${kickReason}. Please reload the page to rejoin`
|
| loadingScreen.style = 'display: block;'
|
| loadingScreen.hasError = true
|
| })
|
|
|
| bot.on('end', (endReason) => {
|
| console.log('disconnected for', endReason)
|
| loadingScreen.status = `You have been disconnected from the server. End reason: ${endReason}. Please reload the page to rejoin`
|
| loadingScreen.style = 'display: block;'
|
| loadingScreen.hasError = true
|
| })
|
|
|
| bot.once('login', () => {
|
| loadingScreen.status = 'Loading world'
|
| })
|
|
|
| bot.once('spawn', () => {
|
| const mcData = require('minecraft-data')(bot.version)
|
|
|
| loadingScreen.status = 'Placing blocks (starting viewer)'
|
|
|
| console.log('bot spawned - starting viewer')
|
|
|
| const version = bot.version
|
|
|
| const center = bot.entity.position
|
|
|
| console.log(viewDistance)
|
| const worldView = new WorldView(bot.world, viewDistance, center)
|
|
|
| gameMenu.init(renderer)
|
| optionsScrn.isInsideWorld = true
|
| optionsScrn.addEventListener('fov_changed', (e) => {
|
| viewer.camera.fov = e.detail.fov
|
| viewer.camera.updateProjectionMatrix()
|
| })
|
|
|
| viewer.setVersion(version)
|
|
|
| window.worldView = worldView
|
| window.bot = bot
|
| window.mcData = mcData
|
| window.viewer = viewer
|
| window.Vec3 = Vec3
|
| window.pathfinder = pathfinder
|
| window.debugMenu = debugMenu
|
| window.settings = optionsScrn
|
| window.renderer = renderer
|
|
|
| initVR(bot, renderer, viewer)
|
|
|
| const cursor = new Cursor(viewer, renderer, bot)
|
| animate = () => {
|
| window.requestAnimationFrame(animate)
|
| viewer.update()
|
| cursor.update(bot)
|
| debugMenu.cursorBlock = cursor.cursorBlock
|
| renderer.render(viewer.scene, viewer.camera)
|
| }
|
|
|
|
|
| viewer.listen(worldView)
|
| worldView.listenToBot(bot)
|
| worldView.init(bot.entity.position)
|
|
|
|
|
| function botPosition () {
|
| viewer.setFirstPersonCamera(bot.entity.position, bot.entity.yaw, bot.entity.pitch)
|
| worldView.updatePosition(bot.entity.position)
|
| }
|
| bot.on('move', botPosition)
|
| botPosition()
|
|
|
| loadingScreen.status = 'Setting callbacks'
|
|
|
| function moveCallback (e) {
|
| bot.entity.pitch -= e.movementY * optionsScrn.mouseSensitivityY * 0.0001
|
| bot.entity.pitch = Math.max(minPitch, Math.min(maxPitch, bot.entity.pitch))
|
| bot.entity.yaw -= e.movementX * optionsScrn.mouseSensitivityX * 0.0001
|
|
|
| viewer.setFirstPersonCamera(null, bot.entity.yaw, bot.entity.pitch)
|
| }
|
|
|
| function changeCallback () {
|
| if (document.pointerLockElement === renderer.domElement ||
|
| document.mozPointerLockElement === renderer.domElement ||
|
| document.webkitPointerLockElement === renderer.domElement) {
|
| document.addEventListener('mousemove', moveCallback, false)
|
| } else {
|
| document.removeEventListener('mousemove', moveCallback, false)
|
| }
|
| }
|
|
|
| document.addEventListener('pointerlockchange', changeCallback, false)
|
| document.addEventListener('mozpointerlockchange', changeCallback, false)
|
| document.addEventListener('webkitpointerlockchange', changeCallback, false)
|
|
|
| let lastTouch
|
| document.addEventListener('touchmove', (e) => {
|
| window.scrollTo(0, 0)
|
| e.preventDefault()
|
| e.stopPropagation()
|
| if (lastTouch !== undefined) {
|
| moveCallback({ movementX: e.touches[0].pageX - lastTouch.pageX, movementY: e.touches[0].pageY - lastTouch.pageY })
|
| }
|
| lastTouch = e.touches[0]
|
| }, { passive: false })
|
|
|
| document.addEventListener('touchend', (e) => {
|
| lastTouch = undefined
|
| }, { passive: false })
|
|
|
| renderer.domElement.requestPointerLock = renderer.domElement.requestPointerLock ||
|
| renderer.domElement.mozRequestPointerLock ||
|
| renderer.domElement.webkitRequestPointerLock
|
| document.addEventListener('mousedown', (e) => {
|
| if (!chat.inChat && !gameMenu.inMenu) {
|
| renderer.domElement.requestPointerLock()
|
| }
|
| })
|
|
|
| document.addEventListener('contextmenu', (e) => e.preventDefault(), false)
|
|
|
| window.addEventListener('blur', (e) => {
|
| bot.clearControlStates()
|
| }, false)
|
|
|
| document.addEventListener('keydown', (e) => {
|
| if (chat.inChat) return
|
| if (gameMenu.inMenu) return
|
|
|
| keyBindScrn.keymaps.forEach(km => {
|
| if (e.code === km.key) {
|
| switch (km.defaultKey) {
|
| case 'KeyQ':
|
| if (bot.heldItem) bot.tossStack(bot.heldItem)
|
| break
|
| case 'ControlLeft':
|
| bot.setControlState('sprint', true)
|
| break
|
| case 'ShiftLeft':
|
| bot.setControlState('sneak', true)
|
| break
|
| case 'Space':
|
| bot.setControlState('jump', true)
|
| break
|
| case 'KeyD':
|
| bot.setControlState('left', true)
|
| break
|
| case 'KeyA':
|
| bot.setControlState('right', true)
|
| break
|
| case 'KeyS':
|
| bot.setControlState('back', true)
|
| break
|
| case 'KeyW':
|
| bot.setControlState('forward', true)
|
| break
|
| }
|
| }
|
| })
|
| }, false)
|
|
|
| document.addEventListener('keyup', (e) => {
|
| keyBindScrn.keymaps.forEach(km => {
|
| if (e.code === km.key) {
|
| switch (km.defaultKey) {
|
| case 'ControlLeft':
|
| bot.setControlState('sprint', false)
|
| break
|
| case 'ShiftLeft':
|
| bot.setControlState('sneak', false)
|
| break
|
| case 'Space':
|
| bot.setControlState('jump', false)
|
| break
|
| case 'KeyD':
|
| bot.setControlState('left', false)
|
| break
|
| case 'KeyA':
|
| bot.setControlState('right', false)
|
| break
|
| case 'KeyS':
|
| bot.setControlState('back', false)
|
| break
|
| case 'KeyW':
|
| bot.setControlState('forward', false)
|
| break
|
| }
|
| }
|
| })
|
| }, false)
|
|
|
| loadingScreen.status = 'Done!'
|
| console.log(loadingScreen.status)
|
|
|
| hud.init(renderer, bot, host)
|
| hud.style.display = 'block'
|
|
|
| setTimeout(function () {
|
|
|
| errorAbortController.abort()
|
| if (loadingScreen.hasError) return
|
|
|
| loadingScreen.style = 'display: none;'
|
| }, 2500)
|
| })
|
| }
|
|
|
| |
| |
|
|
| async function fromTheOutside (params, addr) {
|
| const opts = {}
|
| const dfltConfig = await (await window.fetch('config.json')).json()
|
|
|
| let server, port, proxy, proxyPort
|
|
|
| if (address.includes(':')) {
|
| const s = address.split(':')
|
| server = s[0]
|
| port = Number(s[1]) || 25565
|
| } else {
|
| server = address
|
| port = Number(params.get('port')) || 25565
|
| }
|
|
|
| const proxyAddr = params.get('proxy')
|
| if (proxyAddr) {
|
| const s = proxyAddr.split(':')
|
| proxy = s[0]
|
| proxyPort = Number(s[1] ?? 'NaN') || 22
|
| } else {
|
| proxy = dfltConfig.defaultProxy
|
| proxyPort = !dfltConfig.defaultProxy && !dfltConfig.defaultProxyPort ? '' : dfltConfig.defaultProxyPort ?? 443
|
| }
|
|
|
| opts.server = `${server}:${port}`
|
| opts.proxy = `${proxy}:${proxyPort}`
|
| opts.username = params.get('username') ?? `pviewer${Math.floor(Math.random() * 1000)}`
|
| opts.password = params.get('password') ?? ''
|
| opts.botVersion = params.get('version') ?? false
|
|
|
| console.log(opts)
|
|
|
| showEl('loading-screen')
|
| removePanorama()
|
| connect(opts)
|
| }
|
|
|
| const params = new URLSearchParams(window.location.search)
|
| let address
|
| if ((address = params.get('address'))) {
|
| fromTheOutside(params, address)
|
| } else {
|
| showEl('title-screen')
|
| main()
|
| }
|
|
|