Skip to content

Commit

Permalink
feat(config-json): Only either bundle or load from remote (#291)
Browse files Browse the repository at this point in the history
  • Loading branch information
zardoy authored Mar 4, 2025
1 parent 4b54be6 commit 1c700aa
Showing 15 changed files with 127 additions and 74 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/next-deploy.yml
Original file line number Diff line number Diff line change
@@ -32,6 +32,8 @@ jobs:
echo "{\"latestTag\": \"$(git rev-parse --short $GITHUB_SHA)\", \"isCommit\": true}" > assets/release.json
- name: Build Project Artifacts
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
env:
CONFIG_JSON_SOURCE: BUNDLED
- run: pnpm build-storybook
- name: Copy playground files
run: |
2 changes: 2 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -61,6 +61,8 @@ jobs:
echo "{\"latestTag\": \"$(git rev-parse --short ${{ github.event.pull_request.head.sha }})\", \"isCommit\": true}" > assets/release.json
- name: Build Project Artifacts
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
env:
CONFIG_JSON_SOURCE: BUNDLED
- run: pnpm build-storybook
- name: Copy playground files
run: |
2 changes: 2 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -30,6 +30,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: vercel build --token=${{ secrets.VERCEL_TOKEN }} --prod
env:
CONFIG_JSON_SOURCE: BUNDLED
- run: pnpm build-storybook
- name: Copy playground files
run: |
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ RUN npm i -g pnpm@9.0.4
# Build arguments
ARG DOWNLOAD_SOUNDS=false
ARG DISABLE_SERVICE_WORKER=false
ARG CONFIG_JSON_SOURCE=REMOTE
# TODO need flat --no-root-optional
RUN node ./scripts/dockerPrepare.mjs
RUN pnpm i
@@ -22,8 +23,8 @@ RUN if [ "$DOWNLOAD_SOUNDS" = "true" ] ; then node scripts/downloadSoundsMap.mjs
# ENTRYPOINT ["pnpm", "run", "run-all"]

# only for prod
RUN GITHUB_REPOSITORY=zardoy/minecraft-web-client \
DISABLE_SERVICE_WORKER=$DISABLE_SERVICE_WORKER \
RUN DISABLE_SERVICE_WORKER=$DISABLE_SERVICE_WORKER \
CONFIG_JSON_SOURCE=$CONFIG_JSON_SOURCE \
pnpm run build

# ---- Run Stage ----
12 changes: 9 additions & 3 deletions rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -25,12 +25,14 @@ const disableServiceWorker = process.env.DISABLE_SERVICE_WORKER === 'true'
let releaseTag
let releaseLink
let releaseChangelog
let githubRepositoryFallback

if (fs.existsSync('./assets/release.json')) {
const releaseJson = JSON.parse(fs.readFileSync('./assets/release.json', 'utf8'))
releaseTag = releaseJson.latestTag
releaseLink = releaseJson.isCommit ? `/commit/${releaseJson.latestTag}` : `/releases/${releaseJson.latestTag}`
releaseChangelog = releaseJson.changelog?.replace(/<!-- bump-type:[\w]+ -->/, '')
githubRepositoryFallback = releaseJson.repository
}

const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8'))
@@ -41,6 +43,8 @@ if (dev) {
configJson.defaultProxy = ':8080'
}

const configSource = process.env.CONFIG_JSON_SOURCE || 'REMOTE'

// base options are in ./renderer/rsbuildSharedConfig.ts
const appConfig = defineConfig({
html: {
@@ -66,13 +70,13 @@ const appConfig = defineConfig({
'process.env.BUILD_VERSION': JSON.stringify(!dev ? buildingVersion : 'undefined'),
'process.env.MAIN_MENU_LINKS': JSON.stringify(process.env.MAIN_MENU_LINKS),
'process.env.GITHUB_URL':
JSON.stringify(`https://github.com/${process.env.GITHUB_REPOSITORY || `${process.env.VERCEL_GIT_REPO_OWNER}/${process.env.VERCEL_GIT_REPO_SLUG}`}`),
JSON.stringify(`https://github.com/${process.env.GITHUB_REPOSITORY || `${process.env.VERCEL_GIT_REPO_OWNER}/${process.env.VERCEL_GIT_REPO_SLUG}` || githubRepositoryFallback}`),
'process.env.DEPS_VERSIONS': JSON.stringify({}),
'process.env.RELEASE_TAG': JSON.stringify(releaseTag),
'process.env.RELEASE_LINK': JSON.stringify(releaseLink),
'process.env.RELEASE_CHANGELOG': JSON.stringify(releaseChangelog),
'process.env.DISABLE_SERVICE_WORKER': JSON.stringify(disableServiceWorker),
'process.env.INLINED_APP_CONFIG': JSON.stringify(configJson),
'process.env.INLINED_APP_CONFIG': JSON.stringify(configSource === 'BUNDLED' ? configJson : null),
},
},
server: {
@@ -109,7 +113,9 @@ const appConfig = defineConfig({
fs.copyFileSync('./assets/release.json', './dist/release.json')
}

fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8')
if (configSource === 'REMOTE') {
fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8')
}
if (fs.existsSync('./generated/sounds.js')) {
fs.copyFileSync('./generated/sounds.js', './dist/sounds.js')
}
22 changes: 20 additions & 2 deletions scripts/dockerPrepare.mjs
Original file line number Diff line number Diff line change
@@ -4,9 +4,27 @@ import path from 'path'
import { fileURLToPath } from 'url'
import { execSync } from 'child_process'

// write release tag
// Get repository from git config
const getGitRepository = () => {
try {
const gitConfig = fs.readFileSync('.git/config', 'utf8')
const originUrlMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url = .*?github\.com[:/](.*?)(\.git)?\n/m)
if (originUrlMatch) {
return originUrlMatch[1]
}
} catch (err) {
console.warn('Failed to read git repository from config:', err)
}
return null
}

// write release tag and repository info
const commitShort = execSync('git rev-parse --short HEAD').toString().trim()
fs.writeFileSync('./assets/release.json', JSON.stringify({ latestTag: `${commitShort} (docker)` }), 'utf8')
const repository = getGitRepository()
fs.writeFileSync('./assets/release.json', JSON.stringify({
latestTag: `${commitShort} (docker)`,
repository
}), 'utf8')

const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'))
delete packageJson.optionalDependencies
59 changes: 59 additions & 0 deletions src/appConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { disabledSettings, options, qsOptions } from './optionsStorage'
import { miscUiState } from './globalState'
import { setLoadingScreenStatus } from './appStatus'

export type AppConfig = {
// defaultHost?: string
// defaultHostSave?: string
defaultProxy?: string
// defaultProxySave?: string
// defaultVersion?: string
peerJsServer?: string
peerJsServerFallback?: string
promoteServers?: Array<{ ip, description, version? }>
mapsProvider?: string

appParams?: Record<string, any> // query string params

defaultSettings?: Record<string, any>
forceSettings?: Record<string, boolean>
// hideSettings?: Record<string, boolean>
allowAutoConnect?: boolean
pauseLinks?: Array<Array<Record<string, any>>>
}

export const loadAppConfig = (appConfig: AppConfig) => {
if (miscUiState.appConfig) {
Object.assign(miscUiState.appConfig, appConfig)
} else {
miscUiState.appConfig = appConfig
}

if (appConfig.forceSettings) {
for (const [key, value] of Object.entries(appConfig.forceSettings)) {
if (value) {
disabledSettings.value.add(key)
// since the setting is forced, we need to set it to that value
if (appConfig.defaultSettings?.[key] && !qsOptions[key]) {
options[key] = appConfig.defaultSettings[key]
}
} else {
disabledSettings.value.delete(key)
}
}
}
}

export const isBundledConfigUsed = !!process.env.INLINED_APP_CONFIG

if (isBundledConfigUsed) {
loadAppConfig(process.env.INLINED_APP_CONFIG as AppConfig ?? {})
} else {
void window.fetch('config.json').then(async res => res.json()).then(c => c, (error) => {
// console.warn('Failed to load optional app config.json', error)
// return {}
setLoadingScreenStatus('Failed to load app config.json', true)
}).then((config: AppConfig) => {
loadAppConfig(config)
})
}
2 changes: 1 addition & 1 deletion src/appParams.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AppConfig } from './globalState'
import type { AppConfig } from './appConfig'

const qsParams = new URLSearchParams(window.location?.search ?? '')

4 changes: 2 additions & 2 deletions src/browserfs.ts
Original file line number Diff line number Diff line change
@@ -41,13 +41,13 @@ browserfs.configure({
throw e2
}
showNotification('Failed to access device storage', `Check you have free space. ${e.message}`, true)
miscUiState.appLoaded = true
miscUiState.fsReady = true
miscUiState.singleplayerAvailable = false
})
return
}
await updateTexturePackInstalledState()
miscUiState.appLoaded = true
miscUiState.fsReady = true
miscUiState.singleplayerAvailable = true
})

6 changes: 3 additions & 3 deletions src/customChannels.ts
Original file line number Diff line number Diff line change
@@ -85,15 +85,15 @@ const registeredJeiChannel = () => {
[
{
name: 'id',
type: 'pstring',
type: ['pstring', { countType: 'i16' }]
},
{
name: 'categoryTitle',
type: 'pstring',
type: ['pstring', { countType: 'i16' }]
},
{
name: 'items',
type: 'pstring',
type: ['pstring', { countType: 'i16' }]
},
]
]
41 changes: 2 additions & 39 deletions src/globalState.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import type { WorldWarp } from 'flying-squid/dist/lib/modules/warps'
import type { OptionsGroupType } from './optionsGuiScheme'
import { appQueryParams } from './appParams'
import { options, disabledSettings } from './optionsStorage'
import { AppConfig } from './appConfig'

// todo: refactor structure with support of hideNext=false

@@ -110,26 +111,6 @@ export const showContextmenu = (items: ContextMenuItem[], { clientX, clientY })

// ---

export type AppConfig = {
// defaultHost?: string
// defaultHostSave?: string
defaultProxy?: string
// defaultProxySave?: string
// defaultVersion?: string
peerJsServer?: string
peerJsServerFallback?: string
promoteServers?: Array<{ ip, description, version? }>
mapsProvider?: string

appParams?: Record<string, any> // query string params

defaultSettings?: Record<string, any>
forceSettings?: Record<string, boolean>
// hideSettings?: Record<string, boolean>
allowAutoConnect?: boolean
pauseLinks?: Array<Array<Record<string, any>>>
}

export const miscUiState = proxy({
currentDisplayQr: null as string | null,
currentTouch: null as boolean | null,
@@ -144,32 +125,14 @@ export const miscUiState = proxy({
loadedServerIndex: '',
/** currently trying to load or loaded mc version, after all data is loaded */
loadedDataVersion: null as string | null,
appLoaded: false,
fsReady: false,
singleplayerAvailable: false,
usingGamepadInput: false,
appConfig: null as AppConfig | null,
displaySearchInput: false,
displayFullmap: false
})

export const loadAppConfig = (appConfig: AppConfig) => {
if (miscUiState.appConfig) {
Object.assign(miscUiState.appConfig, appConfig)
} else {
miscUiState.appConfig = appConfig
}

if (appConfig.forceSettings) {
for (const [key, value] of Object.entries(appConfig.forceSettings)) {
if (value) {
disabledSettings.value.delete(key)
} else {
disabledSettings.value.add(key)
}
}
}
}

export const isGameActive = (foregroundCheck: boolean) => {
if (foregroundCheck && activeModalStack.length) return false
return miscUiState.gameLoaded
17 changes: 4 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ import './mineflayer/cameraShake'
import './shims/patchShims'
import './mineflayer/java-tester/index'
import './external'
import './appConfig'
import { getServerInfo } from './mineflayer/mc-protocol'
import { onGameLoad, renderSlot } from './inventoryWindows'
import { GeneralInputItem, RenderItem } from './mineflayer/items'
@@ -48,7 +49,6 @@ import initializePacketsReplay from './packetsReplay/packetsReplayLegacy'

import { initVR } from './vr'
import {
AppConfig,
activeModalStack,
activeModalStacks,
hideModal,
@@ -57,7 +57,6 @@ import {
miscUiState,
showModal,
gameAdditionalState,
loadAppConfig
} from './globalState'

import { parseServerAddress } from './parseServerAddress'
@@ -904,8 +903,9 @@ export async function connect (connectOptions: ConnectOptions) {
const reconnectOptions = sessionStorage.getItem('reconnectOptions') ? JSON.parse(sessionStorage.getItem('reconnectOptions')!) : undefined

listenGlobalEvents()
watchValue(miscUiState, async s => {
if (s.appLoaded) { // fs ready
const unsubscribe = watchValue(miscUiState, async s => {
if (s.fsReady && s.appConfig) {
unsubscribe()
if (reconnectOptions) {
sessionStorage.removeItem('reconnectOptions')
if (Date.now() - reconnectOptions.timestamp < 1000 * 60 * 2) {
@@ -966,15 +966,6 @@ document.body.addEventListener('touchstart', (e) => {
}, { passive: false })
// #endregion

loadAppConfig(process.env.INLINED_APP_CONFIG as AppConfig ?? {})
// load maybe updated config on the server with updated params (just in case)
void window.fetch('config.json').then(async res => res.json()).then(c => c, (error) => {
console.warn('Failed to load optional app config.json', error)
return {}
}).then((config: AppConfig | {}) => {
loadAppConfig(config)
})

// qs open actions
if (!reconnectOptions) {
downloadAndOpenFile().then((downloadAction) => {
19 changes: 14 additions & 5 deletions src/optionsStorage.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { proxy, subscribe } from 'valtio/vanilla'
import { subscribeKey } from 'valtio/utils'
import { omitObj } from '@zardoy/utils'
import { appQueryParamsArray } from './appParams'
import type { AppConfig } from './globalState'
import type { AppConfig } from './appConfig'

const isDev = process.env.NODE_ENV === 'development'
const initialAppConfig = process.env?.INLINED_APP_CONFIG as AppConfig ?? {}
@@ -188,7 +188,7 @@ subscribe(options, () => {
localStorage.options = JSON.stringify(saveOptions)
})

type WatchValue = <T extends Record<string, any>>(proxy: T, callback: (p: T, isChanged: boolean) => void) => void
type WatchValue = <T extends Record<string, any>>(proxy: T, callback: (p: T, isChanged: boolean) => void) => () => void

export const watchValue: WatchValue = (proxy, callback) => {
const watchedProps = new Set<string>()
@@ -198,10 +198,19 @@ export const watchValue: WatchValue = (proxy, callback) => {
return Reflect.get(target, p, receiver)
},
}), false)
const unsubscribes = [] as Array<() => void>
for (const prop of watchedProps) {
subscribeKey(proxy, prop, () => {
callback(proxy, true)
})
unsubscribes.push(
subscribeKey(proxy, prop, () => {
callback(proxy, true)
})
)
}

return () => {
for (const unsubscribe of unsubscribes) {
unsubscribe()
}
}
}

2 changes: 1 addition & 1 deletion src/panorama.ts
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ const updateResourcePackSupportPanorama = async () => {
}

watchValue(miscUiState, m => {
if (m.appLoaded) {
if (m.fsReady) {
// Also adds panorama on app load here
watchValue(resourcePackState, async (s) => {
const oldState = panoramaUsesResourcePack
Loading
Oops, something went wrong.

0 comments on commit 1c700aa

Please sign in to comment.