import { useEffect, useRef, useState } from 'react' import type { Block } from 'prismarine-block' import { getThreeJsRendererMethods } from 'renderer/viewer/three/threeJsMethods' import { getFixedFilesize } from '../downloadAndOpenFile' import { options } from '../optionsStorage' import { BlockStateModelInfo } from '../../renderer/viewer/lib/mesher/shared' import styles from './DebugOverlay.module.css' export default () => { const received = useRef({ ...defaultPacketsCount }) const sent = useRef({ ...defaultPacketsCount }) const customEntries = useRef({} as any) const receivedTotal = useRef(0) const packetsCountByNamePerSec = useRef({ received: {} as { [key: string]: number }, sent: {} as { [key: string]: number } }) window.packetsCountByNamePerSec = packetsCountByNamePerSec const packetsCountByNamePer10Sec = useRef({ received: {} as { [key: string]: number }, sent: {} as { [key: string]: number } }) window.packetsCountByNamePer10Sec = packetsCountByNamePer10Sec const packetsCountByName = useRef({ received: {} as { [key: string]: number }, sent: {} as { [key: string]: number } }) window.packetsCountByName = packetsCountByName const ignoredPackets = useRef(new Set([] as any[])) const [packetsString, setPacketsString] = useState('') const [showDebug, setShowDebug] = useState(false) const [pos, setPos] = useState<{ x: number, y: number, z: number }>({ x: 0, y: 0, z: 0 }) const [skyL, setSkyL] = useState(0) const [blockL, setBlockL] = useState(0) const [biomeId, setBiomeId] = useState(0) const [day, setDay] = useState(0) const [timeOfDay, setTimeOfDay] = useState(0) const [dimension, setDimension] = useState('') const [cursorBlock, setCursorBlock] = useState(null) const [blockInfo, setBlockInfo] = useState<{ customBlockName?: string, modelInfo?: BlockStateModelInfo } | null>(null) const [clientTps, setClientTps] = useState(0) const [serverTps, setServerTps] = useState(null as null | { value: number, frozen: boolean }) const minecraftYaw = useRef(0) const minecraftQuad = useRef(0) const rendererDevice = appViewer.rendererState.renderer ?? 'No render backend' const quadsDescription = [ 'north (towards negative Z)', 'east (towards positive X)', 'south (towards positive Z)', 'west (towards negative X)' ] const viewDegToMinecraft = (yaw) => yaw % 360 - 180 * (yaw < 0 ? -1 : 1) const handleF3 = (e) => { if (e.code === 'F3') { setShowDebug(prev => !prev) e.preventDefault() } } const readPacket = (data, { name }, _buf, fullBuffer) => { if (fullBuffer) { const size = fullBuffer.byteLength receivedTotal.current += size received.current.size += size } received.current.count++ managePackets('received', name, data) } const managePackets = (type, name, data) => { packetsCountByName.current[type][name] ??= 0 packetsCountByName.current[type][name]++ packetsCountByNamePerSec.current[type][name] ??= 0 packetsCountByNamePerSec.current[type][name]++ packetsCountByNamePer10Sec.current[type][name] ??= 0 packetsCountByNamePer10Sec.current[type][name]++ if (options.debugLogNotFrequentPackets && !ignoredPackets.current.has(name) && !hardcodedListOfDebugPacketsToIgnore[type].includes(name)) { if (packetsCountByNamePerSec.current[type][name] > 5 || packetsCountByName.current[type][name] > 100) { // todo think of tracking the count within 10s console.info(`[packet ${name} was ${type} too frequent] Ignoring...`) ignoredPackets.current.add(name) } else { console.info(`[packet ${type}] ${name}`, /* ${JSON.stringify(data, null, 2)}` */ data) } } } useEffect(() => { let lastTpsReset = 0 let lastTps = 0 let lastTickDate = 0 const updateTps = (increment = false) => { if (Date.now() - lastTpsReset >= 1000) { setClientTps(lastTps) window.lastTpsArray ??= [] window.lastTpsArray.push(lastTps) lastTps = 0 lastTpsReset = Date.now() } if (increment) { lastTickDate = Date.now() lastTps++ } } document.addEventListener('keydown', handleF3) let update = 0 const packetsUpdateInterval = setInterval(() => { setPacketsString(`↓ ${received.current.count} (${(received.current.size / 1024).toFixed(2)} KB/s, ${getFixedFilesize(receivedTotal.current)}) ↑ ${sent.current.count}`) received.current = { ...defaultPacketsCount } sent.current = { ...defaultPacketsCount } packetsCountByNamePerSec.current.received = {} packetsCountByNamePerSec.current.sent = {} if (update++ % 10 === 0) { packetsCountByNamePer10Sec.current.received = {} packetsCountByNamePer10Sec.current.sent = {} } updateTps(false) }, 1000) bot.on('physicsTick', () => { updateTps(true) }) bot._client.on('packet', () => { updateTps(false) }) const freqUpdateInterval = setInterval(() => { setPos({ ...bot.entity.position }) setSkyL(bot.world.getSkyLight(bot.entity.position)) setBlockL(bot.world.getBlockLight(bot.entity.position)) setBiomeId(bot.world.getBiome(bot.entity.position)) setDimension(bot.game.dimension) setDay(bot.time.day) setTimeOfDay(bot.time.timeOfDay) setCursorBlock(bot.mouse.getCursorState().cursorBlock) }, 100) const notFrequentUpdateInterval = setInterval(async () => { const block = bot.mouse.cursorBlock if (!block) { setBlockInfo(null) return } const { customBlockName, modelInfo } = await getThreeJsRendererMethods()?.getBlockInfo(block.position, block.stateId) ?? {} setBlockInfo({ customBlockName, modelInfo }) }, 300) // @ts-expect-error bot._client.on('packet', readPacket) // @ts-expect-error bot._client.on('packet_name' as any, (name, data) => readPacket(data, { name })) // custom client bot._client.on('writePacket' as any, (name, data) => { sent.current.count++ managePackets('sent', name, data) }) bot._client.on('set_ticking_state' as any, (data) => { setServerTps({ value: data.tick_rate, frozen: data.is_frozen }) }) return () => { document.removeEventListener('keydown', handleF3) clearInterval(packetsUpdateInterval) clearInterval(freqUpdateInterval) clearInterval(notFrequentUpdateInterval) console.log('Last physics tick before disconnect was', Date.now() - lastTickDate, 'ms ago') } }, []) useEffect(() => { minecraftYaw.current = viewDegToMinecraft(bot.entity.yaw * -180 / Math.PI) minecraftQuad.current = Math.floor(((minecraftYaw.current + 180) / 90 + 0.5) % 4) }, [bot.entity.yaw]) if (!showDebug) return null return <>

Prismarine Web Client ({bot.version})

{appViewer.backend?.getDebugOverlay?.().entitiesString &&

E: {appViewer.backend.getDebugOverlay().entitiesString}

}

{dimension}

XYZ: {pos.x.toFixed(3)} / {pos.y.toFixed(3)} / {pos.z.toFixed(3)}

Chunk: {Math.floor(pos.x % 16)} ~ {Math.floor(pos.z % 16)} in {Math.floor(pos.x / 16)} ~ {Math.floor(pos.z / 16)}

Section: {Math.floor(pos.x / 16) * 16}, {Math.floor(pos.y / 16) * 16}, {Math.floor(pos.z / 16) * 16}

Packets: {packetsString}

Client TPS: {clientTps} {serverTps ? `Server TPS: ${serverTps.value} ${serverTps.frozen ? '(frozen)' : ''}` : ''}

Facing (viewer): {bot.entity.yaw.toFixed(3)} {bot.entity.pitch.toFixed(3)}

Facing (minecraft): {quadsDescription[minecraftQuad.current]} ({minecraftYaw.current.toFixed(1)} {(bot.entity.pitch * -180 / Math.PI).toFixed(1)})

Light: {blockL} ({skyL} sky)

Biome: minecraft:{loadedData.biomesArray[biomeId]?.name ?? 'unknown biome'}

Day: {day} Time: {timeOfDay}

{Object.entries(appViewer.backend?.getDebugOverlay?.().left ?? {}).map(([name, value]) =>

{name}: {value}

)}

Backend: {appViewer.backend?.displayName}

Renderer: {rendererDevice}

{cursorBlock ? (<>

{cursorBlock.name}

{ Object.entries(cursorBlock.getProperties()).map(([name, value], idx, arr) => { return

{name}: { typeof value === 'boolean' ? ( {String(value)} ) : value }

}) } ) : ''} {cursorBlock ? (

Looking at: {cursorBlock.position.x} {cursorBlock.position.y} {cursorBlock.position.z}

) : ''}
{blockInfo && (() => { const { customBlockName, modelInfo } = blockInfo return modelInfo && ( <> {customBlockName &&

Custom block: {customBlockName}

} {modelInfo.issues.map((issue, i) => (

{issue}

))} {/*

Resolved models chain: {modelInfo.modelNames.join(' -> ')}

*/}

Resolved model: {modelInfo.modelNames[0] ?? '-'}

{modelInfo.conditions.join(', ')}

) })()} {Object.entries(appViewer.backend?.getDebugOverlay?.().right ?? {}).map(([name, value]) =>

{name}: {value}

)}
} const hardcodedListOfDebugPacketsToIgnore = { received: [ 'entity_velocity', 'sound_effect', 'rel_entity_move', 'entity_head_rotation', 'entity_metadata', 'entity_move_look', 'teams', 'entity_teleport', 'entity_look', 'ping', 'entity_update_attributes', 'player_info', 'update_time', 'animation', 'entity_equipment', 'entity_destroy', 'named_entity_spawn', 'update_light', 'set_slot', 'block_break_animation', 'map_chunk', 'spawn_entity', 'world_particles', 'keep_alive', 'chat', 'playerlist_header', 'scoreboard_objective', 'scoreboard_score', 'entity_status', 'set_ticking_state', 'ping_response', 'block_change', 'damage_event' ], sent: [ 'pong', 'position', 'look', 'keep_alive', 'position_look', 'ping_request' ] } const defaultPacketsCount = { count: 0, size: 0 }