Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config-json): Only either bundle or load from remote #291

Open
wants to merge 4 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"version": 1,
"configSource": "bundled",
"defaultHost": "<from-proxy>",
"defaultProxy": "proxy.mcraft.fun",
"mapsProvider": "https://maps.mcraft.fun/",
Expand Down
6 changes: 4 additions & 2 deletions rsbuild.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const appConfig = defineConfig({
'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(configJson.configSource === 'bundled' ? configJson : null),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defining this value in the same config which one wants to switch the behaviour of seems like a roundabout way. (As you would need to manually modify the config before building)

I would expect it to be toggleable at built time without modifying the checked out source e.g. via a build param, feature flag, custom goal, or environment variable.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are absolutely right, it affects only bundling process and should work like in the same way as disabling service worker works

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I put it here to make it noticeable. Imagine you don't know about the build flag, anyway, I'm not sure what behaviour I should use in repo by default (remote I guess?)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well remote would make more sense to keep the behaviour compatible with the previous one.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous behavior didn't make app menu delay to load before config.json is set, that's why I'm so unsure

},
},
server: {
Expand Down Expand Up @@ -109,7 +109,9 @@ const appConfig = defineConfig({
fs.copyFileSync('./assets/release.json', './dist/release.json')
}

fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8')
if (configJson.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')
}
Expand Down
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 } 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]) {
options[key] = appConfig.defaultSettings[key]
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will break setting from qs params , eed to fix

}
} 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) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably use await not void to ensure the config was loaded before the client continues to load.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I was avoiding any usage of top level awaits because I think it can break the app in some unexpected way and don't really see any benefit of using it

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I don't really see what is stopping the client from using the bundled config.json values until the remote one is loaded and imo await is the proper way to force async queries to block the app until it's loaded? If it really breaks something then that would be a bug elsewhere (and if it fails to load the file then it should of course break as the app doesn't work without the config)

// 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 ?? '')

Expand Down
4 changes: 2 additions & 2 deletions src/browserfs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
})

Expand Down
6 changes: 3 additions & 3 deletions src/customChannels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' }]
},
]
]
Expand Down
41 changes: 2 additions & 39 deletions src/globalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down
17 changes: 4 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 { RenderItem } from './mineflayer/items'
Expand Down Expand Up @@ -50,7 +51,6 @@ import initializePacketsReplay from './packetsReplay/packetsReplayLegacy'

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

import { parseServerAddress } from './parseServerAddress'
Expand Down Expand Up @@ -915,8 +914,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) {
Expand Down Expand Up @@ -977,15 +977,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) => {
Expand Down
19 changes: 14 additions & 5 deletions src/optionsStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?? {}
Expand Down Expand Up @@ -187,7 +187,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>()
Expand All @@ -197,10 +197,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()
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/panorama.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/react/MainMenuRenderApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ export const mainMenuState = proxy({
let disableAnimation = false
export default () => {
const haveModals = useSnapshot(activeModalStack).length
const { gameLoaded, appLoaded, appConfig, singleplayerAvailable } = useSnapshot(miscUiState)
const { gameLoaded, fsReady, appConfig, singleplayerAvailable } = useSnapshot(miscUiState)

const noDisplay = haveModals || gameLoaded || !appLoaded
const noDisplay = haveModals || gameLoaded || !fsReady

useEffect(() => {
if (noDisplay && appLoaded) disableAnimation = true
if (noDisplay && fsReady) disableAnimation = true
}, [noDisplay])

const [versionStatus, setVersionStatus] = useState('')
Expand Down
Loading