Skip to content

Commit

Permalink
feat: texturepack install via qs
Browse files Browse the repository at this point in the history
fix: fix slow texturepack install
fix: fix compatibility with most texturepack block format
todo: plan to do support for huge textures soon (i hope)
  • Loading branch information
zardoy committed Sep 17, 2023
1 parent 88ec993 commit b6d236d
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 35 deletions.
33 changes: 19 additions & 14 deletions src/downloadAndOpenWorld.ts → src/downloadAndOpenFile.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
import { openWorldZip } from './browserfs'
import { installTexturePack } from './texturePack'
import { setLoadingScreenStatus } from './utils'
import prettyBytes from 'pretty-bytes'

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')
if (!mapUrl) return
let mapUrl = qs.get('map')
const texturepack = qs.get('texturepack')
// fixme
if (texturepack) mapUrl = texturepack
if (!mapUrl) return false

const menu = document.getElementById('play-screen')
menu.style = 'display: none;'
if (!texturepack) {
const menu = document.getElementById('play-screen')
menu.style = 'display: none;'
}
const name = mapUrl.slice(mapUrl.lastIndexOf('/') + 1).slice(-25)
setLoadingScreenStatus(`Downloading world ${name}...`)
const downloadThing = texturepack ? 'texturepack' : 'world'
setLoadingScreenStatus(`Downloading ${downloadThing} ${name}...`)

const response = await fetch(mapUrl)
const contentType = response.headers.get('Content-Type')
if (!contentType || !contentType.startsWith('application/zip')) {
alert('Invalid map file')
}
const contentLength = +response.headers.get('Content-Length')
setLoadingScreenStatus(`Downloading world ${name}: have to download ${getConstantFilesize(contentLength)}...`)
setLoadingScreenStatus(`Downloading ${downloadThing} ${name}: have to download ${getConstantFilesize(contentLength)}...`)

let downloadedBytes = 0
const buffer = await new Response(
Expand All @@ -51,7 +52,7 @@ export default async () => {

// Update your progress bar or display the progress value as needed
if (contentLength) {
setLoadingScreenStatus(`Download Progress: ${Math.floor(progress)}% (${getConstantFilesize(downloadedBytes)} / ${getConstantFilesize(contentLength)}))`, false, true)
setLoadingScreenStatus(`Download ${downloadThing} progress: ${Math.floor(progress)}% (${getConstantFilesize(downloadedBytes)} / ${getConstantFilesize(contentLength)}))`, false, true)
}

// Pass the received data to the controller
Expand All @@ -60,5 +61,9 @@ export default async () => {
},
})
).arrayBuffer()
await openWorldZip(buffer)
if (texturepack) {
await installTexturePack(buffer)
} else {
await openWorldZip(buffer)
}
}
16 changes: 9 additions & 7 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, { hasMapUrl } from './downloadAndOpenWorld'
import downloadAndOpenFile from './downloadAndOpenFile'

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

import {
Expand Down Expand Up @@ -756,9 +755,9 @@ window.addEventListener('keydown', (e) => {
addPanoramaCubeMap()
showModal(document.getElementById('title-screen'))
main()
if (hasMapUrl()) {
downloadAndOpenWorld()
} else {
downloadAndOpenFile().then((downloadAction) => {
if (downloadAction !== false) return

window.addEventListener('hud-ready', (e) => {
// try to connect to peer
const qs = new URLSearchParams(window.location.search)
Expand All @@ -776,4 +775,7 @@ if (hasMapUrl()) {
})
}
})
}
}, (err) => {
console.error(err)
alert(`Failed to download file: ${err}`)
})
42 changes: 28 additions & 14 deletions src/texturePack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { Viewer } from 'prismarine-viewer/viewer/lib/viewer'
import { removeFileRecursiveAsync } from './browserfs'
import { miscUiState } from './globalState'
import { subscribeKey } from 'valtio/utils'
import { showNotification } from './menus/notification'

function nextPowerOfTwo(n) {
if (n === 0) return 1
Expand Down Expand Up @@ -63,29 +64,36 @@ export const installTexturePack = async (file: File | ArrayBuffer) => {
await uninstallTexturePack()
} catch (err) {
}
const status = 'Installing resource pack: copying all files';
const status = 'Installing resource pack: copying all files'
setLoadingScreenStatus(status)
// extract the zip and write to fs every file in it
const zip = new JSZip()
const zipFile = await zip.loadAsync(file)
if (!zipFile.file('pack.mcmeta')) throw new Error('Not a resource pack: missing pack.mcmeta')
await mkdirRecursive(texturePackBasePath)

const allFilesArr = Object.entries(zipFile.files);
let i = 0
for (const [path, file] of allFilesArr) {
const writePath = join(texturePackBasePath, path);
if (path.endsWith('/')) continue
const allFilesArr = Object.entries(zipFile.files)
let done = 0
const upStatus = () => {
setLoadingScreenStatus(`${status} ${Math.round(++done / allFilesArr.length * 100)}%`)
}
await Promise.all(allFilesArr.map(async ([path, file]) => {
const writePath = join(texturePackBasePath, path)
if (path.endsWith('/')) return
await mkdirRecursive(dirname(writePath))
await fs.promises.writeFile(writePath, Buffer.from(await file.async('arraybuffer')))
setLoadingScreenStatus(`${status} ${Math.round(++i / allFilesArr.length * 100)}%`)
}
done++
upStatus()
}))
await fs.promises.writeFile(join(texturePackBasePath, 'name.txt'), file['name'] ?? '??', 'utf8')

if (viewer?.world.active) {
await genTexturePackTextures(viewer.version)
}
setLoadingScreenStatus(undefined)
showNotification({
message: 'Texturepack installed!',
})
}

const existsAsync = async (path) => {
Expand Down Expand Up @@ -144,7 +152,7 @@ const setCustomTexturePackData = (blockTextures, blockStates) => {

const getSizeFromImage = async (filePath: string) => {
const probeImg = new Image()
const file = await fs.promises.readFile(filePath, 'base64');
const file = await fs.promises.readFile(filePath, 'base64')
probeImg.src = `data:image/png;base64,${file}`
await new Promise((resolve, reject) => {
probeImg.onload = resolve
Expand All @@ -155,11 +163,17 @@ const getSizeFromImage = async (filePath: string) => {

export const genTexturePackTextures = async (version: string) => {
setCustomTexturePackData(undefined, undefined)
const blocksBasePath = '/userData/resourcePacks/default/assets/minecraft/textures/blocks';
let blocksBasePath = '/userData/resourcePacks/default/assets/minecraft/textures/block'
// todo not clear why this is needed
const blocksBasePathAlt = '/userData/resourcePacks/default/assets/minecraft/textures/blocks'
const blocksGenereatedPath = `/userData/resourcePacks/default/${version}.png`
const genereatedPathData = `/userData/resourcePacks/default/${version}.json`
if (await existsAsync(blocksBasePath) === false) {
return
if (await existsAsync(blocksBasePathAlt) === false) {
return
} else {
blocksBasePath = blocksBasePathAlt
}
}
if (await existsAsync(blocksGenereatedPath) === true) {
applyTexturePackData(version, JSON.parse(await fs.promises.readFile(genereatedPathData, 'utf8')), await fs.promises.readFile(blocksGenereatedPath, 'utf8'))
Expand Down Expand Up @@ -208,7 +222,7 @@ export const genTexturePackTextures = async (version: string) => {
await new Promise<void>(resolve => {
_imgCustom.onload = () => {
imgCustom = _imgCustom
resolve();
resolve()
}
_imgCustom.onerror = () => {
console.log('Skipping issued texture', fileName)
Expand All @@ -227,10 +241,10 @@ export const genTexturePackTextures = async (version: string) => {
ctx.drawImage(img, xOrig, yOrig, originalTileSize, originalTileSize, x, y, tileSize, tileSize)
}
}
const blockDataUrl = canvas.toDataURL('image/png');
const blockDataUrl = canvas.toDataURL('image/png')
const newData: TextureResolvedData = {
blockSize: tileSize,
};
}
await fs.promises.writeFile(genereatedPathData, JSON.stringify(newData), 'utf8')
await fs.promises.writeFile(blocksGenereatedPath, blockDataUrl, 'utf8')
await applyTexturePackData(version, newData, blockDataUrl)
Expand Down

0 comments on commit b6d236d

Please sign in to comment.