Skip to content

Commit

Permalink
fix: update "save remote world to your device" function to support ne…
Browse files Browse the repository at this point in the history
…w HTTP backend
  • Loading branch information
zardoy committed Aug 26, 2024
1 parent 34a6f1d commit ffc9a0c
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 8 deletions.
47 changes: 45 additions & 2 deletions src/browserfs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ export const mountGoogleDriveFolder = async (readonly: boolean, rootId: string)
fsState.isReadonly = readonly
fsState.syncFs = false
fsState.inMemorySave = false
fsState.remoteBackend = true
return true
}

Expand Down Expand Up @@ -313,6 +314,7 @@ export const openWorldDirectory = async (dragndropHandle?: FileSystemDirectoryHa
fsState.isReadonly = !writeAccess
fsState.syncFs = false
fsState.inMemorySave = false
fsState.remoteBackend = false
await loadSave()
}

Expand Down Expand Up @@ -352,7 +354,33 @@ export const possiblyCleanHandle = (callback = () => { }) => {
}
}

export const copyFilesAsyncWithProgress = async (pathSrc: string, pathDest: string, throwRootNotExist = true) => {
const readdirSafe = async (path: string) => {
try {
return await fs.promises.readdir(path)
} catch (err) {
return null
}
}

export const collectFilesToCopy = async (basePath: string, safe = false): Promise<string[]> => {
const result: string[] = []
const countFiles = async (relPath: string) => {
const resolvedPath = join(basePath, relPath)
const files = relPath === '.' && !safe ? await fs.promises.readdir(resolvedPath) : await readdirSafe(resolvedPath)
if (!files) return null
await Promise.all(files.map(async file => {
const res = await countFiles(join(relPath, file))
if (res === null) {
// is file
result.push(join(relPath, file))
}
}))
}
await countFiles('.')
return result
}

export const copyFilesAsyncWithProgress = async (pathSrc: string, pathDest: string, throwRootNotExist = true, addMsg = '') => {
const stat = await existsViaStats(pathSrc)
if (!stat) {
if (throwRootNotExist) throw new Error(`Cannot copy. Source directory ${pathSrc} does not exist`)
Expand Down Expand Up @@ -387,7 +415,7 @@ export const copyFilesAsyncWithProgress = async (pathSrc: string, pathDest: stri
let copied = 0
await copyFilesAsync(pathSrc, pathDest, (name) => {
copied++
setLoadingScreenStatus(`Copying files (${copied}/${filesCount}): ${name}`)
setLoadingScreenStatus(`Copying files${addMsg} (${copied}/${filesCount}): ${name}`)
})
} finally {
setLoadingScreenStatus(undefined)
Expand All @@ -402,6 +430,19 @@ export const existsViaStats = async (path: string) => {
}
}

export const fileExistsAsyncOptimized = async (path: string) => {
try {
await fs.promises.readdir(path)
} catch (err) {
if (err.code === 'ENOTDIR') return true
// eslint-disable-next-line sonarjs/prefer-single-boolean-return
if (err.code === 'ENOENT') return false
// throw err
return false
}
return true
}

export const copyFilesAsync = async (pathSrc: string, pathDest: string, fileCopied?: (name) => void) => {
// query: can't use fs.copy! use fs.promises.writeFile and readFile
const files = await fs.promises.readdir(pathSrc)
Expand Down Expand Up @@ -467,6 +508,7 @@ export const openWorldFromHttpDir = async (fileDescriptorUrl: string/* | undefi
fsState.isReadonly = true
fsState.syncFs = false
fsState.inMemorySave = false
fsState.remoteBackend = true

await loadSave()
}
Expand Down Expand Up @@ -497,6 +539,7 @@ const openWorldZipInner = async (file: File | ArrayBuffer, name = file['name'])
fsState.isReadonly = true
fsState.syncFs = true
fsState.inMemorySave = false
fsState.remoteBackend = false

if (fs.existsSync('/world/level.dat')) {
await loadSave()
Expand Down
2 changes: 1 addition & 1 deletion src/globalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const hideModal = (modal = activeModalStack.at(-1), data: any = undefined
}

if (!cancel) {
let lastModal = activeModalStack.at(-1)
const lastModal = activeModalStack.at(-1)
for (let i = activeModalStack.length - 1; i >= 0; i--) {
if (activeModalStack[i].reactType === modal.reactType) {
activeModalStack.splice(i, 1)
Expand Down
1 change: 1 addition & 0 deletions src/loadSave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const fsState = proxy({
saveLoaded: false,
openReadOperations: 0,
openWriteOperations: 0,
remoteBackend: false
})

const PROPOSE_BACKUP = true
Expand Down
47 changes: 42 additions & 5 deletions src/react/PauseScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { join } from 'path'
import fs from 'fs'
import { useEffect } from 'react'
import { useSnapshot } from 'valtio'
import { usedServerPathsV1 } from 'flying-squid/dist/lib/modules/world'
Expand All @@ -14,7 +15,7 @@ import { fsState } from '../loadSave'
import { disconnect } from '../flyingSquidUtils'
import { pointerLock, setLoadingScreenStatus } from '../utils'
import { closeWan, openToWanAndCopyJoinLink, getJoinLink } from '../localServerMultiplayer'
import { copyFilesAsyncWithProgress, mkdirRecursive, uniqueFileNameFromWorldName } from '../browserfs'
import { collectFilesToCopy, fileExistsAsyncOptimized, mkdirRecursive, uniqueFileNameFromWorldName } from '../browserfs'
import { useIsModalActive } from './utilsApp'
import { showOptionsModal } from './SelectOption'
import Button from './Button'
Expand All @@ -29,12 +30,44 @@ export const saveToBrowserMemory = async () => {
const { worldFolder } = localServer.options
const saveRootPath = await uniqueFileNameFromWorldName(worldFolder.split('/').pop(), `/data/worlds`)
await mkdirRecursive(saveRootPath)
for (const copyPath of [...usedServerPathsV1, 'icon.png']) {
const srcPath = join(worldFolder, copyPath)
const savePath = join(saveRootPath, copyPath)
const allRootPaths = [...usedServerPathsV1]
const allFilesToCopy = [] as string[]
for (const dirBase of allRootPaths) {
if (dirBase.includes('.') && await fileExistsAsyncOptimized(join(worldFolder, dirBase))) {
allFilesToCopy.push(dirBase)
continue
}
let res = await collectFilesToCopy(join(worldFolder, dirBase), true)
if (dirBase === 'region') {
res = res.filter(x => x.endsWith('.mca'))
}
allFilesToCopy.push(...res.map(x => join(dirBase, x)))
}
const pathsSplit = allFilesToCopy.reduce((acc, cur, i) => {
if (i % 15 === 0) {
acc.push([])
}
acc.at(-1)!.push(cur)
return acc
// eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter
}, [] as string[][])
let copied = 0
const upProgress = () => {
copied++
const action = fsState.remoteBackend ? 'Downloading & copying' : 'Copying'
setLoadingScreenStatus(`${action} files (${copied}/${allFilesToCopy.length})`)
}
for (const copyPaths of pathsSplit) {
// eslint-disable-next-line no-await-in-loop
await copyFilesAsyncWithProgress(srcPath, savePath, false)
await Promise.all(copyPaths.map(async (copyPath) => {
const srcPath = join(worldFolder, copyPath)
const savePath = join(saveRootPath, copyPath)
await mkdirRecursive(savePath)
await fs.promises.writeFile(savePath, await fs.promises.readFile(srcPath))
upProgress()
}))
}

return saveRootPath
} catch (err) {
void showOptionsModal(`Error while saving the world: ${err.message}`, [])
Expand Down Expand Up @@ -101,6 +134,10 @@ export default () => {
const action = await showOptionsModal('World actions...', ['Save to browser memory'])
if (action === 'Save to browser memory') {
await saveToBrowserMemory()
// fsState.inMemorySave = true
// fsState.syncFs = false
// fsState.isReadonly = false
// fsState.remoteBackend = false
}
}

Expand Down

0 comments on commit ffc9a0c

Please sign in to comment.