a few fixes (#20)

fix: ios improvements: always display full ui & no magnifier on double tap anymore
fix: regression: connect to p2p peers
This commit is contained in:
Vitaly 2023-09-19 03:18:01 +03:00 committed by GitHub
commit 14a9a2bf18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 13080 additions and 56 deletions

View file

@ -12,11 +12,10 @@ jobs:
run: npm i -g pnpm
- run: pnpm install
- run: pnpm build
- run: nohup pnpm prod-start & node cypress/minecraft-server.mjs &
- uses: cypress-io/github-action@v5
with:
install: false
# start: pnpm prod-start & node cypress/minecraft-server.mjs
start: pnpm prod-start
- uses: actions/upload-artifact@v3
if: failure()
with:

View file

@ -3,11 +3,17 @@ env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on:
workflow_dispatch:
pull_request:
issue_comment:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
# todo skip already created deploys on that commit
if: >-
github.event.issue.pull_request != '' &&
(
contains(github.event.comment.body, '/deploy')
)
steps:
- name: Checkout
uses: actions/checkout@v2

1
.npmrc
View file

@ -1,4 +1,3 @@
package-lock=false
public-hoist-pattern=*
ignore-workspace-root-check=true
shell-emulator=true

View file

@ -30,7 +30,7 @@ it('Loads & renders singleplayer', () => {
})
})
it.skip('Joins to server', () => {
it('Joins to server', () => {
cy.visit('/')
setLocalStorageSettings()
// todo replace with data-test

View file

@ -61,7 +61,7 @@ const ctx = await esbuild.context({
],
metafile: true,
plugins,
sourcesContent: process.argv.includes('--no-sources'),
sourcesContent: !process.argv.includes('--no-sources'),
minify: process.argv.includes('--minify'),
define: {
'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production'),

View file

@ -37,8 +37,8 @@
<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
</head>
<body>
<div id="react-root"></div>
<div id="ui-root">
<div id="react-root"></div>
<pmui-hud id="hud" style="display: none;"></pmui-hud>
<pmui-pausescreen id="pause-screen" style="display: none;"></pmui-pausescreen>
<pmui-loading-error-screen id="loading-error-screen" style="display: none;"></pmui-loading-error-screen>

View file

@ -20,7 +20,7 @@
"author": "PrismarineJS",
"license": "MIT",
"dependencies": {
"@dimaka/interface": "0.0.1",
"@dimaka/interface": "0.0.3-alpha.0",
"@emotion/css": "^11.11.2",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
"@types/react": "^18.2.20",
@ -46,9 +46,7 @@
"net-browserify": "github:PrismarineJS/net-browserify",
"peerjs": "^1.5.0",
"pretty-bytes": "^6.1.1",
"prismarine-world": "^3.6.2",
"qrcode.react": "^3.1.0",
"querystring": "^0.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-refresh": "^0.14.0",
@ -100,7 +98,8 @@
"webpack-dev-middleware": "^6.1.1",
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.9.0",
"workbox-webpack-plugin": "^6.6.0"
"workbox-webpack-plugin": "^6.6.0",
"yaml": "^2.3.2"
},
"pnpm": {
"overrides": {

12974
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -37,21 +37,22 @@
"devDependencies": {
"assert": "^2.0.0",
"buffer": "^6.0.3",
"process": "^0.11.10",
"minecraft-assets": "^1.9.0",
"canvas": "^2.11.2",
"filesize": "^10.0.12",
"fs-extra": "^11.0.0",
"jest": "^27.0.4",
"jest-puppeteer": "^6.0.0",
"minecraft-assets": "^1.9.0",
"minecraft-wrap": "^1.3.0",
"minecrafthawkeye": "^1.2.5",
"mineflayer": "^4.0.0",
"mineflayer-pathfinder": "^2.0.0",
"prismarine-schematic": "^1.2.0",
"minecrafthawkeye": "^1.2.5",
"prismarine-viewer": "file:./",
"process": "^0.11.10",
"puppeteer": "^16.0.0",
"standard": "^17.0.0",
"webpack": "^5.10.2",
"webpack-cli": "^5.1.1",
"canvas": "^2.11.2",
"fs-extra": "^11.0.0"
"webpack-cli": "^5.1.1"
}
}

View file

@ -0,0 +1,26 @@
// pnpm bug workaround
import fs from 'fs'
import { parse } from 'yaml'
const lockfile = parse(fs.readFileSync('./pnpm-lock.yaml', 'utf8'))
const depsKeys = ['dependencies', 'devDependencies']
for (const importer of Object.values(lockfile.importers)) {
for (const depsKey of depsKeys) {
for (const [depName, { specifier, version }] of Object.entries(importer[depsKey])) {
if (!specifier.startsWith('github:')) continue
let branch = specifier.match(/#(.*)$/)?.[1] ?? ''
if (branch) branch = `/${branch}`
const sha = version.split('/').slice(3).join('/').replace(/\(.+/, '')
const repo = version.split('/').slice(1, 3).join('/')
const lastCommitJson = await fetch(`https://api.github.com/repos/${repo}/commits${branch}?per_page=1`).then(res => res.json())
const lastCommitActual = lastCommitJson ?? lastCommitJson[0]
const lastCommitActualSha = lastCommitActual?.sha
if (lastCommitActualSha === undefined) debugger
if (sha !== lastCommitActualSha) {
console.log(`Outdated ${depName} github.com/${repo} : ${sha} -> ${lastCommitActualSha} (${lastCommitActual.commit.message})`)
}
}
}
}

View file

@ -28,6 +28,7 @@ class Cursor {
static instance = null
constructor (viewer, renderer, /** @type {import('mineflayer').Bot} */bot) {
bot.on('physicsTick', () => { if (this.lastBlockPlaced < 4) this.lastBlockPlaced++ })
if (Cursor.instance) return Cursor.instance
// Init state
@ -108,7 +109,6 @@ class Cursor {
bot.attack(entity)
}
})
bot.on('physicsTick', () => { if (this.lastBlockPlaced < 4) this.lastBlockPlaced++ })
}
// todo this shouldnt be done in the render loop, migrate the code to dom events to avoid delays on lags

View file

@ -1,6 +1,6 @@
import EventEmitter from 'events'
import Client from 'minecraft-protocol/src/client'
import CustomChannelClient from 'minecraft-protocol/src/customChannelClient'
window.serverDataChannel ??= {}
export const customCommunication = {
@ -27,7 +27,7 @@ export class LocalServer extends EventEmitter.EventEmitter {
}
listen() {
this.emit('connection', new Client(true, this.version, this.customPackets, this.hideErrors, customCommunication))
this.emit('connection', new CustomChannelClient(true, this.version, customCommunication))
}
close() { }

View file

@ -136,6 +136,7 @@ const minPitch = -0.5 * Math.PI
const renderer = new THREE.WebGLRenderer()
renderer.setPixelRatio(window.devicePixelRatio || 1) // todo this value is too high on ios, need to check, probably we should use avg, also need to make it configurable
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.domElement.id = 'viewer-canvas'
document.body.appendChild(renderer.domElement)
// Create viewer
@ -196,14 +197,21 @@ const optionsScrn = document.getElementById('options-screen')
const pauseMenu = document.getElementById('pause-screen')
let mouseMovePostHandle = (e) => { }
let lastMouseCall
function onMouseMove(e) {
let lastMouseMove: number
let cursor: Cursor
let debugMenu
const updateCursor = () => {
cursor.update(bot)
debugMenu ??= hud.shadowRoot.querySelector('#debug-overlay')
debugMenu.cursorBlock = cursor.cursorBlock
}
function onCameraMove(e) {
if (e.type !== 'touchmove' && !pointerLock.hasPointerLock) return
e.stopPropagation?.()
const now = performance.now()
// todo: limit camera movement for now to avoid unexpected jumps
if (now - lastMouseCall < 4) return
lastMouseCall = now
if (now - lastMouseMove < 4) return
lastMouseMove = now
let { mouseSensX, mouseSensY } = optionsScrn
if (mouseSensY === true) mouseSensY = mouseSensX
// debugPitch.innerText = +debugPitch.innerText + e.movementX
@ -211,8 +219,10 @@ function onMouseMove(e) {
x: e.movementX * mouseSensX * 0.0001,
y: e.movementY * mouseSensY * 0.0001
})
// todo do it also on every block update within radius 5
updateCursor()
}
window.addEventListener('mousemove', onMouseMove, { capture: true })
window.addEventListener('mousemove', onCameraMove, { capture: true })
function hideCurrentScreens() {
@ -289,8 +299,6 @@ async function connect(connectOptions: {
timeouts.push(id)
return id
}
const debugMenu = hud.shadowRoot.querySelector('#debug-overlay')
const { renderDistance, maxMultiplayerRenderDistance } = options
const hostprompt = connectOptions.server
const proxyprompt = connectOptions.proxy
@ -478,6 +486,9 @@ async function connect(connectOptions: {
handleError(err)
}
if (!bot) return
cursor = new Cursor(viewer, renderer, bot)
// bot.on('move', () => updateCursor())
let p2pConnectTimeout = p2pMultiplayer ? setTimeout(() => { throw new Error('Spawn timeout. There might be error on other side, check console.') }, 20_000) : undefined
hud.preload(bot)
@ -546,8 +557,11 @@ async function connect(connectOptions: {
})
optionsScrn.addEventListener('fov_changed', updateFov)
bot.on('physicsTick', () => updateCursor())
viewer.setVersion(version)
const debugMenu = hud.shadowRoot.querySelector('#debug-overlay')
window.viewer = viewer
window.loadedData = mcData
window.worldView = worldView
@ -559,11 +573,8 @@ async function connect(connectOptions: {
initVR(bot, renderer, viewer)
const cursor = new Cursor(viewer, renderer, bot)
postRenderFrameFn = () => {
viewer.setFirstPersonCamera(null, bot.entity.yaw, bot.entity.pitch)
cursor.update(bot)
debugMenu.cursorBlock = cursor.cursorBlock
// viewer.setFirstPersonCamera(null, bot.entity.yaw, bot.entity.pitch)
}
try {
@ -615,6 +626,7 @@ async function connect(connectOptions: {
let virtualClickTimeout
let screenTouches = 0
let capturedPointer: { id; x; y; sourceX; sourceY; activateCameraMove; time } | null
document.body.addEventListener('touchstart', (e) => e.preventDefault(), { passive: false })
registerListener(document, 'pointerdown', (e) => {
const clickedEl = e.composedPath()[0]
if (!isGameActive(true) || !miscUiState.currentTouch || clickedEl !== cameraControlEl || e.pointerId === undefined) {
@ -656,7 +668,7 @@ async function connect(connectOptions: {
if (capturedPointer.activateCameraMove) {
clearTimeout(virtualClickTimeout)
}
onMouseMove({ movementX: e.pageX - capturedPointer.x, movementY: e.pageY - capturedPointer.y, type: 'touchmove' })
onCameraMove({ movementX: e.pageX - capturedPointer.x, movementY: e.pageY - capturedPointer.y, type: 'touchmove' })
capturedPointer.x = e.pageX
capturedPointer.y = e.pageY
}, { passive: false })
@ -666,7 +678,7 @@ async function connect(connectOptions: {
let { x, z } = vector
if (Math.abs(x) < 0.18) x = 0
if (Math.abs(z) < 0.18) z = 0
onMouseMove({ movementX: x * 10, movementY: z * 10, type: 'touchmove' })
onCameraMove({ movementX: x * 10, movementY: z * 10, type: 'touchmove' })
})
registerListener(document, 'lostpointercapture', (e) => {
@ -766,6 +778,7 @@ downloadAndOpenFile().then((downloadAction) => {
if (peerId) {
let username = options.guestUsername
if (options.askGuestName) username = prompt('Enter your username', username)
if (!username) return
options.guestUsername = username
connect({
server: '', port: '', proxy: '', password: '',
@ -775,6 +788,7 @@ downloadAndOpenFile().then((downloadAction) => {
})
}
})
if (document.getElementById('hud').isReady) window.dispatchEvent(new Event('hud-ready'))
}, (err) => {
console.error(err)
alert(`Failed to download file: ${err}`)

View file

@ -50,7 +50,7 @@ export const openToWanAndCopyJoinLink = async (writeText: (text) => void, doCopy
peer.on('connection', (connection) => {
console.log('connection')
const serverDuplex = new CustomDuplex({}, (data) => connection.send(data))
const client = new Client(true, localServer.options.version, undefined, false, undefined, /* true */);
const client = new Client(true, localServer.options.version, undefined)
client.setSocket(serverDuplex)
localServer._server.emit('connection', client)
@ -62,7 +62,7 @@ export const openToWanAndCopyJoinLink = async (writeText: (text) => void, doCopy
console.log('connection.close')
serverDuplex.end()
connection.close()
};
}
serverDuplex.on('end', endConnection)
serverDuplex.on('force-close', endConnection)
client.on('end', endConnection)
@ -120,7 +120,7 @@ export const connectToPeer = async (peerId: string) => {
connection.once('error', (error) => {
console.log(error.type, error.name)
console.log(error)
return reject(error.message);
return reject(error.message)
})
connection.once('open', resolve)
}))
@ -128,7 +128,7 @@ export const connectToPeer = async (peerId: string) => {
const clientDuplex = new CustomDuplex({}, (data) => {
// todo rm debug
console.debug('sending', data.toString())
connection.send(data);
connection.send(data)
})
connection.on('data', (data: any) => {
console.debug('received', Buffer.from(data).toString())

View file

@ -14,7 +14,7 @@ class HealthBar extends LitElement {
static get styles () {
return css`
.health {
position: absolute;
position: fixed;
display: flex;
flex-direction: row;
left: calc(50% - 91px);

View file

@ -10,7 +10,7 @@ class Hotbar extends LitElement {
static get styles () {
return css`
.hotbar {
position: absolute;
position: fixed;
bottom: ${unsafeCSS(isProbablyIphone() ? '40px' : '0')};
left: 50%;
transform: translate(-50%);

View file

@ -10,18 +10,19 @@ export const guiIcons1_16_4 = require('minecraft-assets/minecraft-assets/data/1.
class Hud extends LitElement {
firstUpdated () {
this.isReady = true
window.dispatchEvent(new CustomEvent('hud-ready', { detail: this }))
}
static get styles () {
return css`
:host {
position: absolute;
position: fixed;
top: 0;
left: 0;
z-index: -2;
width: 100%;
height: 100%;
height: 100vh;
touch-action: none;
}
@ -30,7 +31,7 @@ class Hud extends LitElement {
height: 16px;
background: url('${unsafeCSS(guiIcons1_17_1)}');
background-size: 256px;
position: absolute;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
@ -38,7 +39,7 @@ class Hud extends LitElement {
}
#xp-label {
position: absolute;
position: fixed;
top: -8px;
left: 50%;
transform: translate(-50%);
@ -50,7 +51,7 @@ class Hud extends LitElement {
}
#xp-bar-bg {
position: absolute;
position: fixed;
left: 50%;
bottom: 24px;
transform: translate(-50%);
@ -72,7 +73,7 @@ class Hud extends LitElement {
.mobile-top-btns {
display: none;
flex-direction: row;
position: absolute;
position: fixed;
top: 0;
left: 50%;
transform: translate(-50%);
@ -318,17 +319,17 @@ class Hud extends LitElement {
render () {
return html`
<div class="mobile-top-btns" id="mobile-top">
<button class="debug-btn" @click=${(e) => {
<button class="debug-btn" @pointerdown=${(e) => {
window.dispatchEvent(new MouseEvent('mousedown', { button: 1, }))
}}>Select</button>
<button class="debug-btn" @click=${(e) => {
<button class="debug-btn" @pointerdown=${(e) => {
this.shadowRoot.getElementById('debug-overlay').showOverlay = !this.shadowRoot.getElementById('debug-overlay').showOverlay
}}>F3</button>
<button class="chat-btn" @click=${(e) => {
<button class="chat-btn" @pointerdown=${(e) => {
e.stopPropagation()
this.shadowRoot.querySelector('#chat').enableChat()
}}></button>
<button class="pause-btn" @click=${(e) => {
<button class="pause-btn" @pointerdown=${(e) => {
e.stopPropagation()
showModal(document.getElementById('pause-screen'))
}}></button>

View file

@ -17,7 +17,7 @@ class PlayScreen extends LitElement {
}
.edit-boxes {
position: absolute;
position: fixed;
top: 59px;
left: 50%;
display: flex;

View file

@ -15,7 +15,7 @@ import { createPortal } from 'react-dom'
useInterfaceState.setState({
isFlying: false,
uiCustomization: {
touchButtonSize: isProbablyIphone() ? 30 : 40,
touchButtonSize: 40,
},
updateCoord: ([coord, state]) => {
const coordToAction = [

View file

@ -12,6 +12,11 @@
box-sizing: border-box;
}
#react-root {
z-index: 9;
position: fixed;
}
a {
color: white;
}
@ -63,8 +68,8 @@ body {
user-select: none;
}
canvas {
position: absolute;
#viewer-canvas {
position: fixed;
top: 0;
left: 0;
height: 100%;
@ -75,14 +80,14 @@ canvas {
}
#ui-root {
position: absolute;
position: fixed;
top: 0;
left: 0;
transform-origin: top left;
transform: scale(var(--guiScale));
width: calc(100% / var(--guiScale));
height: calc(100% / var(--guiScale));
z-index: 10;
z-index: 8;
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;