Game Files | Electric Cherry
{#if error}{/if} {#if notice}

{notice}

{/if} {#if !canManage}

Administrators upload game files. Active hosts prepare verified copies automatically.

{/if} {#if canManage && showUpload}

{phase === 'published' ? `${game.toUpperCase()} files uploaded` : uploadId ? `Continue ${game.toUpperCase()} upload` : `Upload ${game.toUpperCase()} game folder`}

Select your complete {gameName(game)} game folder, including its installed DLC. Once verified, this upload becomes the latest files and hosts download it automatically.

{#if uploadError}{/if}
{#if phase !== 'verifying' && phase !== 'published'}

{uploadId ? 'To resume, file names, sizes, and modification dates must match the original upload.' : 'Choose the game directory itself. Its top-level folder name is removed once; all files and subfolders inside it keep their paths.'}

{/if} {#if folder}
{folder.name}{folder.files.length.toLocaleString()} files · {formatGameBytes(folder.size)}
Preview folder paths
    {#each folder.files.slice(0, 8) as file}
  • {file.path}
  • {/each}
{#if folder.files.length > 8}

And {(folder.files.length - 8).toLocaleString()} more files.

{/if}
{/if} {#if phase !== 'idle'}
{phase === 'preparing' ? 'Preparing upload…' : phase === 'uploading' ? 'Uploading game files' : phase === 'verifying' ? 'Verifying files…' : phase === 'published' ? 'Ready · host downloads are automatic' : 'Upload paused'}{completedFiles.toLocaleString()} / {totalFiles.toLocaleString()} files

{formatGameBytes(progressBytes)} / {formatGameBytes(totalBytes)} saved or transferring

{#if currentPath}

{currentPath}

{/if} {#if phase === 'verifying'}

The controller checks every file before making this upload available to hosts. Verification continues if you leave this page.

{:else if phase === 'paused'}

Saved chunks are kept on the controller. Resume this upload to check the saved progress and retry.

{/if}
{/if}
{#if transferring} {:else if phase !== 'verifying' && phase !== 'published'}{/if} {#if phase === 'idle' || phase === 'uploading'}Keep this page open while files transfer. Interrupted uploads can be resumed below.{/if}
{/if} {#if !loaded}

Loading game files…

{/if} {#each libraries as library (library.game)} {@const latest = library.versions.find(item => item.id === library.latestVersionId)} {@const unfinished = library.versions.filter(item => item.state !== 'ready')} {@const history = library.versions.filter(item => item.state === 'ready' && item.id !== library.latestVersionId)}

{gameName(library.game)}

{latest ? 'Latest verified game files' : 'Game folder not uploaded yet'}

{#if canManage}{/if}
{#if latest}

{formatGameBytes(latest.sizeBytes)} · {latest.fileCount.toLocaleString()} files · Uploaded {uploadDate(latest)}

Available

Active hosts keep these files ready automatically, including hosts added later. Running servers keep using their existing files.

{@render hostCopies(latest, true)} {@render uploadDetails(latest)} {:else}

Upload the complete {library.game.toUpperCase()} game folder once. Hosts will download and verify it automatically as part of setup.

{/if} {#if unfinished.length}

Unfinished uploads

{#each unfinished as item (item.id)} {@const browserUpload = !item.sourceHostId}

Started {new Date(item.createdAt).toLocaleString()} · {formatGameBytes(item.sizeBytes)} · {item.fileCount.toLocaleString()} files

{item.state === 'failed' ? 'Failed' : browserUpload ? item.id === uploadId && phase === 'verifying' ? 'Verifying' : 'Uploading' : 'Importing'}
{#if item.error}

{item.error}

{/if} {#if browserUpload}

Resume with the original unchanged folder. The latest files stay available while this upload completes.

{#if canManage}
{/if} {:else if item.state === 'importing'}

The existing host import continues independently. It is reconciled when the host reconnects.

{/if}
{/each}
{/if} {#if history.length}
Previous uploads ({history.length})

Previous files remain available for servers that already use them.

{#each history as item (item.id)}

Uploaded {uploadDate(item)}

{formatGameBytes(item.sizeBytes)} · {item.fileCount.toLocaleString()} files

{@render uploadDetails(item)}
Host copies{@render hostCopies(item, false)}
{/each}
{/if}
{/each}
{#snippet uploadDetails(item: OverviewLibrary['versions'][number])}
Upload details
Upload ID
{item.id}
Manifest SHA256
{item.manifestSha256 ?? 'Available after verification'}
Uploaded from
{!item.sourceHostId ? 'Game folder in browser' : `${hostName(item.sourceHostId)} / ${item.sourceVolume}`}
View complete file manifest
{/snippet} {#snippet hostCopies(item: OverviewLibrary['versions'][number], latest: boolean)} {#if !hosts.length}

Files are ready. Add a host to prepare them automatically.

{/if}
{#each item.caches as cache} {@const cacheHost = hosts.find(host => host.id === cache.hostId)} {@const awaitingAutomaticDownload = latest && cache.status === 'missing'} {:else}{/each}
HostFile statusProgressDetail
{hostName(cache.hostId)} {awaitingAutomaticDownload ? 'Waiting' : cache.status}{!cacheHost?.connected ? ' · offline' : ''} {#if cache.status !== 'missing'}{:else}—{/if} {cache.error ?? (awaitingAutomaticDownload ? automaticDownloadDetail(cacheHost) : '')}{#if awaitingAutomaticDownload || cache.status === 'failed'} View host setup{/if}
No host copies yet.
{#if canManage}
Advanced: download or verify these files

Retry a download or prepare a previous upload on a host. The latest files are downloaded automatically.

{/if} {/snippet}