// todo implement async options storage import { proxy, subscribe } from 'valtio/vanilla' // weird webpack configuration bug: it cant import valtio/utils in this file import { subscribeKey } from 'valtio/utils' import { omitObj } from '@zardoy/utils' const defaultOptions = { renderDistance: 3, multiplayerRenderDistance: 3, closeConfirmation: true, autoFullScreen: false, mouseRawInput: false, autoExitFullscreen: false, localUsername: 'wanderer', mouseSensX: 50, mouseSensY: -1, // mouseInvertX: false, chatWidth: 320, chatHeight: 180, chatScale: 100, chatOpacity: 100, chatOpacityOpened: 100, messagesLimit: 200, volume: 50, // fov: 70, fov: 75, guiScale: 3, autoRequestCompletions: true, touchButtonsSize: 40, touchButtonsOpacity: 80, touchButtonsPosition: 12, touchControlsPositions: getDefaultTouchControlsPositions(), touchControlsType: 'classic' as 'classic' | 'joystick-buttons', gpuPreference: 'default' as 'default' | 'high-performance' | 'low-power', backgroundRendering: '20fps' as 'full' | '20fps' | '5fps', /** @unstable */ disableAssets: false, /** @unstable */ debugLogNotFrequentPackets: false, unimplementedContainers: false, dayCycleAndLighting: true, loadPlayerSkins: true, lowMemoryMode: false, starfieldRendering: true, // antiAliasing: false, showChunkBorders: false, // todo rename option frameLimit: false as number | false, alwaysBackupWorldBeforeLoading: undefined as boolean | undefined | null, alwaysShowMobileControls: false, excludeCommunicationDebugEvents: [], preventDevReloadWhilePlaying: false, numWorkers: 4, localServerOptions: { gameMode: 1 } as any, preferLoadReadonly: false, disableLoadPrompts: false, guestUsername: 'guest', askGuestName: true, errorReporting: true, /** Actually might be useful */ showCursorBlockInSpectator: false, renderEntities: true, smoothLighting: true, newVersionsLighting: false, chatSelect: false, autoJump: 'auto' as 'auto' | 'always' | 'never', autoParkour: false, // advanced bot options autoRespawn: false, mutedSounds: [] as string[], plugins: [] as Array<{ enabled: boolean, name: string, description: string, script: string }>, /** Wether to popup sign editor on server action */ autoSignEditor: true, wysiwygSignEditor: 'auto' as 'auto' | 'always' | 'never', } function getDefaultTouchControlsPositions () { return { action: [ 70, 76 ], sneak: [ 84, 76 ], break: [ 70, 60 ], jump: [ 84, 60 ], } as Record } const qsOptionsRaw = new URLSearchParams(location.search).getAll('setting') export const qsOptions = Object.fromEntries(qsOptionsRaw.map(o => { const [key, value] = o.split(':') return [key, JSON.parse(value)] })) const migrateOptions = (options: Partial>) => { if (options.highPerformanceGpu) { options.gpuPreference = 'high-performance' delete options.highPerformanceGpu } if (Object.keys(options.touchControlsPositions ?? {}).length === 0) { options.touchControlsPositions = defaultOptions.touchControlsPositions } if (options.touchControlsPositions?.jump === undefined) { options.touchControlsPositions!.jump = defaultOptions.touchControlsPositions.jump } return options } export type AppOptions = typeof defaultOptions export const options: AppOptions = proxy({ ...defaultOptions, ...migrateOptions(JSON.parse(localStorage.options || '{}')), ...qsOptions }) window.options = window.settings = options export const resetOptions = () => { Object.assign(options, defaultOptions) } Object.defineProperty(window, 'debugChangedOptions', { get () { return Object.fromEntries(Object.entries(options).filter(([key, v]) => defaultOptions[key] !== v)) }, }) subscribe(options, () => { const saveOptions = omitObj(options, ...Object.keys(qsOptions) as [any]) localStorage.options = JSON.stringify(saveOptions) }) type WatchValue = >(proxy: T, callback: (p: T) => void) => void export const watchValue: WatchValue = (proxy, callback) => { const watchedProps = new Set() callback(new Proxy(proxy, { get (target, p, receiver) { watchedProps.add(p.toString()) return Reflect.get(target, p, receiver) }, })) for (const prop of watchedProps) { subscribeKey(proxy, prop, () => { callback(proxy) }) } } watchValue(options, o => { globalThis.excludeCommunicationDebugEvents = o.excludeCommunicationDebugEvents }) watchValue(options, o => { document.body.classList.toggle('disable-assets', o.disableAssets) }) watchValue(options, o => { document.body.style.setProperty('--touch-movement-buttons-opacity', (o.touchButtonsOpacity / 100).toString()) }) watchValue(options, o => { document.body.style.setProperty('--touch-movement-buttons-position', (o.touchButtonsPosition * 2) + 'px') }) export const useOptionValue = (setting, valueCallback) => { valueCallback(setting) subscribe(setting, valueCallback) }