prismarine-web-client-mirror/index.js

455 lines
14 KiB
JavaScript
Raw Normal View History

//@ts-check
/* global THREE */
2021-03-14 20:34:35 +01:00
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')
2021-03-24 02:23:02 +01:00
const Cursor = require('./lib/cursor')
2021-02-27 22:12:11 +00:00
// Workaround for process.versions.node not existing in the browser
process.versions.node = '14.0.0'
const mineflayer = require('mineflayer')
const { WorldView, Viewer } = require('prismarine-viewer/viewer')
2021-03-13 00:14:17 +00:00
const pathfinder = require('mineflayer-pathfinder')
2021-03-13 01:19:07 +00:00
const { Vec3 } = require('vec3')
//@ts-ignore
2021-02-27 22:12:11 +00:00
global.THREE = require('three')
2021-03-21 23:13:32 +01:00
const { initVR } = require('./lib/vr')
2021-03-04 03:11:55 +01:00
2021-03-21 01:54:10 +00:00
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
2021-03-20 16:12:20 +01:00
// Create three.js context, add to page
const renderer = new THREE.WebGLRenderer()
renderer.setPixelRatio(window.devicePixelRatio || 1)
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
// Create viewer
const viewer = new Viewer(renderer)
// Menu panorama background
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 }), // WS
new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_3.png'), transparent: true, side: THREE.DoubleSide }), // ES
new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_4.png'), transparent: true, side: THREE.DoubleSide }), // Up
new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_5.png'), transparent: true, side: THREE.DoubleSide }), // Down
new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_0.png'), transparent: true, side: THREE.DoubleSide }), // NS
new THREE.MeshBasicMaterial({ map: loader.load('extra-textures/background/panorama_2.png'), transparent: true, side: THREE.DoubleSide }) // SS
]
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
2021-03-20 16:12:20 +01:00
}
2021-04-01 12:24:26 +02:00
const group = new THREE.Object3D()
group.add(panoramaBox)
2021-04-01 12:24:26 +02:00
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)
2021-04-01 12:24:26 +02:00
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
2021-04-01 12:24:26 +02:00
}
group.add(m)
}
viewer.scene.add(group)
2021-04-01 12:24:26 +02:00
return group
2021-03-20 16:12:20 +01:00
}
const panoramaCubeMap = addPanoramaCubeMap()
2021-03-20 16:12:20 +01:00
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)
2021-03-20 16:12:20 +01:00
}
2021-03-24 02:23:02 +01:00
let animate = () => {
2021-03-20 16:12:20 +01:00
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 loadingScreen = document.getElementById('loading-screen')
const hud = document.getElementById('hud')
const optionsScrn = document.getElementById('options-screen')
const keyBindScrn = document.getElementById('keybinds-screen')
const pauseMenu = document.getElementById('pause-screen')
function setLoadingScreenStatus (status, isError = false) {
showModal(loadingScreen)
if (loadingScreen.hasError) return
loadingScreen.status = status
loadingScreen.hasError = isError
}
2021-02-27 22:12:11 +00:00
async function main () {
const menu = document.getElementById('play-screen')
menu.addEventListener('connect', e => {
const options = e.detail
menu.style = 'display: none;'
2021-03-20 16:12:20 +01:00
removePanorama()
connect(options)
})
}
async function connect (options) {
const debugMenu = hud.shadowRoot.querySelector('#debug-overlay')
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)
}
2021-02-27 22:12:11 +00:00
console.log(`connecting to ${host} ${port} with ${username}`)
if (proxy) {
console.log(`using proxy ${proxy} ${proxyport}`)
//@ts-ignore
net.setProxy({ hostname: proxy, port: proxyport })
}
setLoadingScreenStatus('Logging in')
2021-02-27 22:12:11 +00:00
const bot = mineflayer.createBot({
host,
port,
version: options.botVersion === '' ? false : options.botVersion,
2021-02-27 22:12:11 +00:00
username,
2021-03-13 02:35:07 +01:00
password,
viewDistance: 'tiny',
2021-03-13 01:51:45 +00:00
checkTimeoutInterval: 240 * 1000,
noPongTimeout: 240 * 1000,
closeTimeout: 240 * 1000
2021-02-27 22:12:11 +00:00
})
hud.preload(bot)
2021-02-27 22:12:11 +00:00
bot.on('error', (err) => {
console.log('Encountered error!', err)
setLoadingScreenStatus(`Error encountered. Error message: ${err}. Please reload the page`, true)
})
2021-03-13 02:27:03 +01:00
bot.on('kicked', (kickReason) => {
console.log('User was kicked!', kickReason)
setLoadingScreenStatus(`The Minecraft server kicked you. Kick reason: ${kickReason}. Please reload the page to rejoin`, true)
})
2021-03-13 02:27:03 +01:00
bot.on('end', (endReason) => {
console.log('disconnected for', endReason)
setLoadingScreenStatus(`You have been disconnected from the server. End reason: ${endReason}. Please reload the page to rejoin`, true)
})
bot.once('login', () => {
// server is ok, add it to the history
/** @type {string[]} */
const serverHistory = JSON.parse(localStorage.getItem('serverHistory') || '[]')
serverHistory.unshift(options.server)
localStorage.setItem('serverHistory', JSON.stringify([...new Set(serverHistory)]))
setLoadingScreenStatus('Loading world')
2021-02-27 22:12:11 +00:00
})
bot.once('spawn', () => {
2021-03-13 00:14:17 +00:00
const mcData = require('minecraft-data')(bot.version)
setLoadingScreenStatus('Placing blocks (starting viewer)')
2021-02-27 22:12:11 +00:00
console.log('bot spawned - starting viewer')
const version = bot.version
const center = bot.entity.position
const worldView = new WorldView(bot.world, viewDistance, center)
optionsScrn.isInsideWorld = true
optionsScrn.addEventListener('fov_changed', (e) => {
viewer.camera.fov = e.detail.fov
viewer.camera.updateProjectionMatrix()
})
2021-02-27 22:12:11 +00:00
viewer.setVersion(version)
2021-03-13 00:14:17 +00:00
window.worldView = worldView
window.bot = bot
window.mcData = mcData
window.viewer = viewer
window.Vec3 = Vec3
window.pathfinder = pathfinder
window.debugMenu = debugMenu
window.settings = optionsScrn
2021-03-18 08:28:47 +01:00
window.renderer = renderer
2021-03-13 00:14:17 +00:00
2021-03-21 23:13:32 +01:00
initVR(bot, renderer, viewer)
const cursor = new Cursor(viewer, renderer, bot)
2021-03-24 02:23:02 +01:00
animate = () => {
window.requestAnimationFrame(animate)
viewer.update()
cursor.update(bot)
debugMenu.cursorBlock = cursor.cursorBlock
renderer.render(viewer.scene, viewer.camera)
}
try {
const gl = renderer.getContext()
debugMenu.rendererDevice = gl.getParameter(gl.getExtension('WEBGL_debug_renderer_info').UNMASKED_RENDERER_WEBGL)
} catch (err) {
console.error(err)
debugMenu.rendererDevice = '???'
}
2021-03-13 04:35:11 +01:00
// Link WorldView and Viewer
viewer.listen(worldView)
2021-02-27 22:12:11 +00:00
worldView.listenToBot(bot)
worldView.init(bot.entity.position)
// Bot position callback
2021-02-27 22:12:11 +00:00
function botPosition () {
viewer.setFirstPersonCamera(bot.entity.position, bot.entity.yaw, bot.entity.pitch)
worldView.updatePosition(bot.entity.position)
}
bot.on('move', botPosition)
2021-03-21 23:13:32 +01:00
botPosition()
2021-02-27 22:12:11 +00:00
setLoadingScreenStatus('Setting callbacks')
2021-02-27 22:12:11 +00:00
function moveCallback (e) {
if (!pointerLock.hasPointerLock) return
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)
2021-02-27 22:12:11 +00:00
}
2021-03-02 00:00:57 +00:00
2021-02-27 22:12:11 +00:00
function changeCallback () {
if (!pointerLock.hasPointerLock && activeModalStack.length === 0) {
showModal(pauseMenu)
2021-02-27 22:12:11 +00:00
}
}
document.addEventListener('mousemove', moveCallback, false)
2021-02-27 22:12:11 +00:00
document.addEventListener('pointerlockchange', changeCallback, false)
document.addEventListener('mozpointerlockchange', changeCallback, false)
document.addEventListener('webkitpointerlockchange', changeCallback, false)
2021-03-21 00:32:46 +00:00
let lastTouch
document.addEventListener('touchmove', (e) => {
2021-03-21 00:57:54 +00:00
window.scrollTo(0, 0)
e.preventDefault()
2021-03-21 00:57:54 +00:00
e.stopPropagation()
2021-03-21 00:32:46 +00:00
if (lastTouch !== undefined) {
moveCallback({ movementX: e.touches[0].pageX - lastTouch.pageX, movementY: e.touches[0].pageY - lastTouch.pageY })
}
lastTouch = e.touches[0]
2021-03-21 00:57:54 +00:00
}, { passive: false })
2021-03-21 00:32:46 +00:00
document.addEventListener('touchend', (e) => {
lastTouch = undefined
}, { passive: false })
const requestPointerLock = renderer.domElement.requestPointerLock ||
2021-02-27 22:12:11 +00:00
renderer.domElement.mozRequestPointerLock ||
renderer.domElement.webkitRequestPointerLock
renderer.domElement.requestPointerLock = async () => {
// await renderer.domElement.requestFullscreen()
2023-08-09 19:41:20 +03:00
// request full keyboard access
// navigator.keyboard.lock(['KeyW'])
const promise = requestPointerLock.apply(renderer.domElement, {
unadjustedMovement: window.localStorage.getItem('mouseRawInput') === 'true'
})
2023-08-09 19:41:20 +03:00
promise?.catch((error) => {
if (error.name === "NotSupportedError") {
// Some platforms may not support unadjusted movement, request again a regular pointer lock.
requestPointerLock.apply(renderer.domElement)
} else {
console.error(error)
}
})
}
2021-02-27 22:12:11 +00:00
document.addEventListener('mousedown', (e) => {
if (!chat.inChat && !gameMenu.inMenu) {
renderer.domElement.requestPointerLock()
}
2021-02-27 22:12:11 +00:00
})
document.addEventListener('contextmenu', (e) => e.preventDefault(), false)
window.addEventListener('blur', (e) => {
bot.clearControlStates()
}, false)
2021-02-27 22:12:11 +00:00
document.addEventListener('keydown', (e) => {
if (activeModalStack.length) 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':
2023-08-09 19:34:09 +03:00
bot.setControlState('right', true)
break
case 'KeyA':
2023-08-09 19:34:09 +03:00
bot.setControlState('left', true)
break
case 'KeyS':
bot.setControlState('back', true)
break
case 'KeyW':
bot.setControlState('forward', true)
break
}
}
})
2021-02-27 22:12:11 +00:00
}, false)
2021-02-27 22:12:11 +00:00
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':
2023-08-09 20:38:03 +03:00
bot.setControlState('right', false)
break
case 'KeyA':
2023-08-09 20:38:03 +03:00
bot.setControlState('left', false)
break
case 'KeyS':
bot.setControlState('back', false)
break
case 'KeyW':
bot.setControlState('forward', false)
break
}
}
})
2021-02-27 22:12:11 +00:00
}, false)
setLoadingScreenStatus('Done!')
console.log('Done!')
hud.init(renderer, bot, host)
hud.style.display = 'block'
setTimeout(function () {
if (loadingScreen.hasError) return
// remove loading screen, wait a second to make sure a frame has properly rendered
loadingScreen.style.display = 'none'
activeModalStack.splice(0, activeModalStack.length)
}, 2500)
2021-02-27 22:12:11 +00:00
})
2021-02-27 22:27:59 +01:00
}
const requestPointerLock = () => {
if (hud.style.display === 'none' || activeModalStack.length) return
renderer.domElement.requestPointerLock()
}
window.addEventListener('mousedown', (e) => {
requestPointerLock()
})
window.addEventListener('keydown', (e) => {
if (e.code === 'Escape') {
hideModal(undefined, { event: e, renderer }, () => {
requestPointerLock() // if no modals left
})
}
})
showModal(document.getElementById('title-screen'))
main()