Skip to content

Commit

Permalink
feat: peerjs (p2p) wan multiplayer! (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
zardoy authored Sep 16, 2023
2 parents 111188d + b4952e1 commit 25276a6
Show file tree
Hide file tree
Showing 14 changed files with 316 additions and 32 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
fetch-depth: 0
- name: Install Global Dependencies
run: npm install --global vercel pnpm
- run: pnpm install
- run: pnpm run build
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
Expand Down
11 changes: 3 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,6 @@
],
"author": "PrismarineJS",
"license": "MIT",
"release": {
"initialVersion": {
"version": "0.1.0",
"releaseNotes": "The start of something new...",
"releaseNotesWithExisting": "The start of something new..."
}
},
"dependencies": {
"@dimaka/interface": "0.0.1",
"@emotion/css": "^11.11.2",
Expand All @@ -51,8 +44,10 @@
"lit": "^2.8.0",
"minecraft-data": "^3.0.0",
"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",
Expand All @@ -77,6 +72,7 @@
"cypress": "^9.5.4",
"cypress-esbuild-preprocessor": "^1.0.2",
"events": "^3.3.0",
"filesize": "^10.0.12",
"html-webpack-plugin": "^5.5.3",
"http-browserify": "^1.7.0",
"http-server": "^14.1.1",
Expand All @@ -97,7 +93,6 @@
"three": "0.128.0",
"timers-browserify": "^2.0.12",
"url-loader": "^4.1.1",
"filesize": "^10.0.12",
"use-typed-event-listener": "^4.0.2",
"vite": "^4.4.9",
"webpack": "^5.88.2",
Expand Down
22 changes: 19 additions & 3 deletions src/builtinCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import JSZip from 'jszip'
import fs from 'fs'
import { join } from 'path'
import { fsState } from './loadSave'
import { closeWan, openToWanAndCopyJoinLink } from './localServerMultiplayer'

const notImplemented = () => {
return 'Not implemented yet'
Expand Down Expand Up @@ -50,15 +51,30 @@ const exportWorld = async () => {

window.exportWorld = exportWorld

const writeText = (text) => {
bot._client.emit('chat', {
message: JSON.stringify({ text })
})
}

const commands = [
{
command: ['/download', '/export'],
invoke: exportWorld
},
{
command: ['/publish'],
// todo
invoke: notImplemented
command: ['/publish', '/share'],
invoke: async () => {
const text = await openToWanAndCopyJoinLink(writeText)
if (text) writeText(text)
}
},
{
command: ['/close'],
invoke: () => {
const text = closeWan()
if (text) writeText(text)
}
},
{
command: '/reset-world -y',
Expand Down
1 change: 1 addition & 0 deletions src/customServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import EventEmitter from 'events'

import Client from 'minecraft-protocol/src/client'

window.serverDataChannel ??= {}
export const customCommunication = {
sendData(data) {
//@ts-ignore
Expand Down
1 change: 1 addition & 0 deletions src/defaultLocalServerOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
'header': 'Flying squid',
'footer': 'Test server'
},
keepAlive: false,
'everybody-op': true,
'max-entities': 100,
'version': '1.14.4',
Expand Down
6 changes: 6 additions & 0 deletions src/downloadAndOpenWorld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ const getConstantFilesize = (bytes: number) => {
return prettyBytes(bytes, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
}

export const hasMapUrl = () => {
const qs = new URLSearchParams(window.location.search)
const mapUrl = qs.get('map')
return !!mapUrl
}

export default async () => {
const qs = new URLSearchParams(window.location.search)
const mapUrl = qs.get('map')
Expand Down
4 changes: 4 additions & 0 deletions src/globalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,11 @@ export const showContextmenu = (items: ContextMenuItem[], { clientX, clientY })
// ---

export const miscUiState = proxy({
currentDisplayQr: null as string | null,
currentTouch: null as boolean | null,
singleplayer: false,
flyingSquid: false,
wanOpened: false,
gameLoaded: false,
resourcePackInstalled: false,
})
Expand Down Expand Up @@ -148,6 +151,7 @@ window.addEventListener('beforeunload', (event) => {
// todo-low maybe exclude chat?
if (!isGameActive(true) && activeModalStack.at(-1)?.elem.id !== 'chat') return
if (sessionStorage.lastReload && options.preventDevReloadWhilePlaying === false) return
if (options.closeConfirmation === false) return

// For major browsers doning only this is enough
event.preventDefault()
Expand Down
71 changes: 53 additions & 18 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import './controls'
import './dragndrop'
import './browserfs'
import './eruda'
import downloadAndOpenWorld from './downloadAndOpenWorld'
import downloadAndOpenWorld, { hasMapUrl } from './downloadAndOpenWorld'

import net from 'net'
import Stats from 'stats.js'
Expand Down Expand Up @@ -63,7 +63,8 @@ import {
isCypress,
loadScript,
toMajorVersion,
setLoadingScreenStatus
setLoadingScreenStatus,
resolveTimeout
} from './utils'

import {
Expand All @@ -74,13 +75,14 @@ import {

import { startLocalServer, unsupportedLocalServerFeatures } from './createLocalServer'
import serverOptions from './defaultLocalServerOptions'
import { customCommunication } from './customServer'
import { clientDuplex, customCommunication } from './customServer'
import updateTime from './updateTime'
import { options } from './optionsStorage'
import { subscribeKey } from 'valtio/utils'
import _ from 'lodash'
import { contro } from './controls'
import { genTexturePackTextures, watchTexturepackInViewer } from './texturePack'
import { connectToPeer } from './localServerMultiplayer'

//@ts-ignore
window.THREE = THREE
Expand Down Expand Up @@ -263,16 +265,17 @@ const removeAllListeners = () => {
disposables = []
}

/**
* @param {{ server: any; port?: string; singleplayer: any; username: any; password: any; proxy: any; botVersion?: any; serverOverrides? }} connectOptions
*/
async function connect(connectOptions) {
async function connect(connectOptions: {
server: any; port?: string; singleplayer?: any; username: any; password: any; proxy: any; botVersion?: any; serverOverrides?; peerId?: string
}) {
const menu = document.getElementById('play-screen')
menu.style = 'display: none;'
removePanorama()

const singeplayer = connectOptions.singleplayer
const p2pMultiplayer = !!connectOptions.peerId
miscUiState.singleplayer = singeplayer
miscUiState.flyingSquid = singeplayer || p2pMultiplayer
const oldSetInterval = window.setInterval
// @ts-ignore
window.setInterval = (callback, ms) => {
Expand Down Expand Up @@ -403,9 +406,9 @@ async function connect(connectOptions) {
await loadScript(`./mc-data/${toMajorVersion(version)}.js`)
}

const version = connectOptions.botVersion ?? serverOptions.version
if (version) {
await downloadMcData(version)
const downloadVersion = connectOptions.botVersion || singeplayer ? serverOptions.version : undefined
if (downloadVersion) {
await downloadMcData(downloadVersion)
}

if (singeplayer) {
Expand All @@ -421,7 +424,6 @@ async function connect(connectOptions) {
// flying-squid: 'login' -> player.login -> now sends 'login' event to the client (handled in many plugins in mineflayer) -> then 'update_health' is sent which emits 'spawn' in mineflayer

setLoadingScreenStatus('Starting local server')
window.serverDataChannel ??= {}
localServer = window.localServer = startLocalServer()
// todo need just to call quit if started
// loadingScreen.maybeRecoverable = false
Expand All @@ -433,16 +435,23 @@ async function connect(connectOptions) {
}
}

const usingCustomCommunication = true

const botDuplex = !p2pMultiplayer ? undefined/* clientDuplex */ : await connectToPeer(connectOptions.peerId);

setLoadingScreenStatus('Creating mineflayer bot')
bot = mineflayer.createBot({
host,
port,
version: connectOptions.botVersion === '' ? false : connectOptions.botVersion,
version: !connectOptions.botVersion ? false : connectOptions.botVersion,
...singeplayer || p2pMultiplayer ? {
keepAlive: false,
stream: botDuplex,
} : {},
...singeplayer ? {
version: serverOptions.version,
connect() { },
keepAlive: false,
customCommunication
customCommunication: usingCustomCommunication ? customCommunication : undefined,
} : {},
username,
password,
Expand All @@ -454,7 +463,8 @@ async function connect(connectOptions) {
await downloadMcData(client.version)
}
})
if (singeplayer) {
if (singeplayer || p2pMultiplayer) {
// p2pMultiplayer still uses the same flying-squid server
const _supportFeature = bot.supportFeature
bot.supportFeature = (feature) => {
if (unsupportedLocalServerFeatures.includes(feature)) {
Expand All @@ -463,13 +473,16 @@ async function connect(connectOptions) {
return _supportFeature(feature)
}

bot.emit('inject_allowed')
bot._client.emit('connect')
if (usingCustomCommunication) {
bot.emit('inject_allowed')
bot._client.emit('connect')
}
}
} catch (err) {
handleError(err)
}
if (!bot) return
let p2pConnectTimeout = p2pMultiplayer ? setTimeout(() => { throw new Error('Spawn timeout. There might be error on other side, check console.') }, 20_000) : undefined
hud.preload(bot)

// bot.on('inject_allowed', () => {
Expand Down Expand Up @@ -500,6 +513,7 @@ async function connect(connectOptions) {
})

bot.once('spawn', () => {
if (p2pConnectTimeout) clearTimeout(p2pConnectTimeout)
// todo display notification if not critical
const mcData = require('minecraft-data')(bot.version)

Expand Down Expand Up @@ -702,6 +716,7 @@ async function connect(connectOptions) {
errorAbortController.abort()
if (loadingScreen.hasError) return
// remove loading screen, wait a second to make sure a frame has properly rendered
setLoadingScreenStatus(undefined)
hideCurrentScreens()
}, singeplayer ? 0 : 2500)
})
Expand Down Expand Up @@ -744,4 +759,24 @@ window.addEventListener('keydown', (e) => {
addPanoramaCubeMap()
showModal(document.getElementById('title-screen'))
main()
downloadAndOpenWorld()
if (hasMapUrl()) {
downloadAndOpenWorld()
} else {
window.addEventListener('hud-ready', (e) => {
// try to connect to peer
const qs = new URLSearchParams(window.location.search)
const peerId = qs.get('connectPeer')
const version = qs.get('peerVersion')
if (peerId) {
let username = options.guestUsername
if (!options.askGuestName) username = prompt('Enter your username', username)
options.guestUsername = username
connect({
server: '', port: '', proxy: '', password: '',
username,
botVersion: version || undefined,
peerId
})
}
})
}
Loading

0 comments on commit 25276a6

Please sign in to comment.