From 536eb0d201ba250dbebe1622e02dedddd6f3279a Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 6 Apr 2025 23:34:38 +0300 Subject: [PATCH] NEW! WAKE LOCK API SUPPORT! --- src/mineflayer/timers.ts | 63 ++++++++++++++++++++++++-- src/optionsGuiScheme.tsx | 6 ++- src/react/IndicatorEffects.tsx | 4 +- src/react/IndicatorEffectsProvider.tsx | 12 ++++- 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/mineflayer/timers.ts b/src/mineflayer/timers.ts index 698b624e..b1cb5c41 100644 --- a/src/mineflayer/timers.ts +++ b/src/mineflayer/timers.ts @@ -1,9 +1,66 @@ +import { subscribeKey } from 'valtio/utils' import { preventThrottlingWithSound } from '../core/timers' import { options } from '../optionsStorage' customEvents.on('mineflayerBotCreated', () => { - if (options.preventBackgroundTimeoutKick) { - const unsub = preventThrottlingWithSound() - bot.on('end', unsub) + const abortController = new AbortController() + + const maybeGoBackgroundKickPrevention = () => { + if (options.preventBackgroundTimeoutKick && !bot.backgroundKickPrevention) { + const unsub = preventThrottlingWithSound() + bot.on('end', unsub) + bot.backgroundKickPrevention = true + } } + maybeGoBackgroundKickPrevention() + subscribeKey(options, 'preventBackgroundTimeoutKick', (value) => { + maybeGoBackgroundKickPrevention() + }) + + // wake lock + const requestWakeLock = async () => { + if (options.preventSleep && !bot.wakeLock && !bot.lockRequested) { + bot.lockRequested = true + bot.wakeLock = await navigator.wakeLock.request('screen').finally(() => { + bot.lockRequested = false + }) + + bot.wakeLock.addEventListener('release', () => { + bot.wakeLock = undefined + }, { + once: true, + }) + } + + if (!options.preventSleep && bot.wakeLock) { + void bot.wakeLock.release() + } + } + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'visible') { + // we are back to the tab, request wake lock again + void requestWakeLock() + } + }, { + signal: abortController.signal, + }) + void requestWakeLock() + subscribeKey(options, 'preventSleep', (value) => { + void requestWakeLock() + }) + + bot.on('end', () => { + if (bot.wakeLock) { + void bot.wakeLock.release() + } + abortController.abort() + }) }) + +declare module 'mineflayer' { + interface Bot { + backgroundKickPrevention?: boolean + wakeLock?: WakeLockSentinel + lockRequested?: boolean + } +} diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 5b83922f..ef8d9a8e 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -526,7 +526,11 @@ export const guiOptionsScheme: { }, }, { - preventBackgroundTimeoutKick: {} + preventBackgroundTimeoutKick: {}, + preventSleep: { + disabledReason: navigator.wakeLock ? undefined : 'Your browser does not support wake lock API', + enableWarning: 'When connected to a server, prevent PC from sleeping or screen dimming. Useful for purpusely staying AFK for long time. Some events might still prevent this like loosing tab focus or going low power mode.', + }, }, { custom () { diff --git a/src/react/IndicatorEffects.tsx b/src/react/IndicatorEffects.tsx index 18ab0785..5b05290f 100644 --- a/src/react/IndicatorEffects.tsx +++ b/src/react/IndicatorEffects.tsx @@ -46,7 +46,8 @@ export const defaultIndicatorsState = { readonlyFiles: false, writingFiles: false, // saving appHasErrors: false, - connectionIssues: 0 + connectionIssues: 0, + preventSleep: false, } const indicatorIcons: Record = { @@ -56,6 +57,7 @@ const indicatorIcons: Record = { appHasErrors: 'alert', readonlyFiles: 'file-off', connectionIssues: pixelartIcons['cellular-signal-off'], + preventSleep: pixelartIcons.moon, } const colorOverrides = { diff --git a/src/react/IndicatorEffectsProvider.tsx b/src/react/IndicatorEffectsProvider.tsx index 83b321ee..1913fb7b 100644 --- a/src/react/IndicatorEffectsProvider.tsx +++ b/src/react/IndicatorEffectsProvider.tsx @@ -1,5 +1,5 @@ import { proxy, subscribe, useSnapshot } from 'valtio' -import { useEffect, useMemo } from 'react' +import { useEffect, useMemo, useState } from 'react' import { subscribeKey } from 'valtio/utils' import { inGameError } from '../utils' import { fsState } from '../loadSave' @@ -51,6 +51,7 @@ const getEffectIndex = (newEffect: EffectType) => { } export default () => { + const [dummyState, setDummyState] = useState(false) const stateIndicators = useSnapshot(state.indicators) const chunksLoading = !useSnapshot(appViewer.rendererState).world.allChunksLoaded const { mesherWork } = useSnapshot(appViewer.rendererState).world @@ -66,12 +67,21 @@ export default () => { appHasErrors: hasErrors, connectionIssues: poorConnection ? 1 : noConnection ? 2 : 0, chunksLoading, + preventSleep: !!bot.wakeLock, // mesherWork, ...stateIndicators, } const effects = useSnapshot(state.effects) + useEffect(() => { + // update bot related states + const interval = setInterval(() => { + setDummyState(s => !s) + }, 1000) + return () => clearInterval(interval) + }, []) + useMemo(() => { const effectsImages = Object.fromEntries(loadedData.effectsArray.map((effect) => { const nameKebab = effect.name.replaceAll(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`).slice(1)