feat: Add F3+H chunks debug screen! not really useful for now since chunks not visible bug was not fixed yet
This commit is contained in:
parent
143d4a3bb3
commit
73ccb48d02
6 changed files with 246 additions and 3 deletions
|
|
@ -37,8 +37,8 @@ export type WorldDataEmitterEvents = {
|
|||
* It's up to the consumer to serialize the data if needed
|
||||
*/
|
||||
export class WorldDataEmitter extends (EventEmitter as new () => TypedEmitter<WorldDataEmitterEvents>) {
|
||||
private loadedChunks: Record<ChunkPosKey, boolean>
|
||||
private readonly lastPos: Vec3
|
||||
loadedChunks: Record<ChunkPosKey, boolean>
|
||||
readonly lastPos: Vec3
|
||||
private eventListeners: Record<string, any> = {}
|
||||
private readonly emitter: WorldDataEmitter
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ export class WorldDataEmitter extends (EventEmitter as new () => TypedEmitter<Wo
|
|||
|
||||
bot._client.on('update_light', ({ chunkX, chunkZ }) => {
|
||||
const chunkPos = new Vec3(chunkX * 16, 0, chunkZ * 16)
|
||||
if (!this.waitingSpiralChunksLoad[`${chunkX},${chunkZ}`]) {
|
||||
if (!this.waitingSpiralChunksLoad[`${chunkX},${chunkZ}`] && this.loadedChunks[`${chunkX},${chunkZ}`]) {
|
||||
void this.loadChunk(chunkPos, true)
|
||||
}
|
||||
})
|
||||
|
|
@ -242,6 +242,8 @@ export class WorldDataEmitter extends (EventEmitter as new () => TypedEmitter<Wo
|
|||
|
||||
async loadChunk (pos: ChunkPos, isLightUpdate = false) {
|
||||
const [botX, botZ] = chunkPos(this.lastPos)
|
||||
console.log('loadChunk', botX - pos.x / 16, botZ - pos.z / 16)
|
||||
|
||||
const dx = Math.abs(botX - Math.floor(pos.x / 16))
|
||||
const dz = Math.abs(botZ - Math.floor(pos.z / 16))
|
||||
if (dx <= this.viewDistance && dz <= this.viewDistance) {
|
||||
|
|
|
|||
|
|
@ -614,6 +614,13 @@ export const f3Keybinds: Array<{
|
|||
},
|
||||
mobileTitle: 'Toggle chunk borders',
|
||||
},
|
||||
{
|
||||
key: 'KeyH',
|
||||
action () {
|
||||
showModal({ reactType: 'chunks-debug' })
|
||||
},
|
||||
mobileTitle: 'Show Chunks Debug',
|
||||
},
|
||||
{
|
||||
key: 'KeyY',
|
||||
async action () {
|
||||
|
|
|
|||
130
src/react/ChunksDebug.tsx
Normal file
130
src/react/ChunksDebug.tsx
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import { useEffect, useRef, useState } from 'react'
|
||||
import './LoadingChunks.css'
|
||||
|
||||
export interface ChunkDebug {
|
||||
x: number // like -32
|
||||
z: number // like -32
|
||||
lines: string[]
|
||||
sidebarLines: string[]
|
||||
state: 'server-waiting' | 'order-queued' | 'client-waiting' | 'client-processing' | 'done-empty' | 'done'
|
||||
}
|
||||
|
||||
interface ProcessedChunk extends ChunkDebug {
|
||||
relX: number
|
||||
relZ: number
|
||||
displayLines: string[]
|
||||
}
|
||||
|
||||
const stateColors: Record<ChunkDebug['state'], string> = {
|
||||
'server-waiting': 'gray',
|
||||
'order-queued': 'darkorange',
|
||||
'client-waiting': 'yellow',
|
||||
'client-processing': 'yellow',
|
||||
'done-empty': 'darkgreen',
|
||||
'done': 'limegreen',
|
||||
}
|
||||
|
||||
export default ({
|
||||
chunks,
|
||||
playerChunk,
|
||||
maxDistance,
|
||||
tileSize = 16,
|
||||
fontSize = 5,
|
||||
}: {
|
||||
chunks: ChunkDebug[]
|
||||
playerChunk: { x: number, z: number }
|
||||
maxDistance: number,
|
||||
tileSize?: number
|
||||
fontSize?: number
|
||||
}) => {
|
||||
const [selectedChunk, setSelectedChunk] = useState<ProcessedChunk | null>(null)
|
||||
const [showSidebar, setShowSidebar] = useState(false)
|
||||
|
||||
// Calculate grid dimensions based on maxDistance
|
||||
const gridSize = maxDistance * 2 + 1
|
||||
const centerIndex = maxDistance
|
||||
|
||||
// Process chunks to get only the last one for each position and within maxDistance
|
||||
const processedChunks = chunks.reduce<Record<string, ProcessedChunk>>((acc, chunk) => {
|
||||
const relX = Math.floor((chunk.x - playerChunk.x) / 16)
|
||||
const relZ = Math.floor((chunk.z - playerChunk.z) / 16)
|
||||
|
||||
// Skip chunks outside maxDistance
|
||||
if (Math.abs(relX) > maxDistance || Math.abs(relZ) > maxDistance) return acc
|
||||
|
||||
const key = `${chunk.x},${chunk.z}`
|
||||
acc[key] = {
|
||||
...chunk,
|
||||
relX,
|
||||
relZ,
|
||||
displayLines: [`${relX},${relZ} (${chunk.x},${chunk.z})`, ...chunk.lines]
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', gap: '10px' }}>
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: `repeat(${gridSize}, 1fr)`,
|
||||
gridTemplateRows: `repeat(${gridSize}, 1fr)`,
|
||||
gap: 1,
|
||||
// width: `${tileSize * gridSize}px`,
|
||||
// height: `${tileSize * gridSize}px`,
|
||||
}}>
|
||||
{Array.from({ length: gridSize * gridSize }).map((_, i) => {
|
||||
const relX = -maxDistance + (i % gridSize)
|
||||
const relZ = -maxDistance + Math.floor(i / gridSize)
|
||||
const x = playerChunk.x + relX * 16
|
||||
const z = playerChunk.z + relZ * 16
|
||||
const chunk = processedChunks[`${x},${z}`]
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`${x},${z}`}
|
||||
onClick={() => {
|
||||
if (chunk) {
|
||||
setSelectedChunk(chunk)
|
||||
setShowSidebar(true)
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
background: chunk ? stateColors[chunk.state] : 'black',
|
||||
color: 'white',
|
||||
fontSize: `${fontSize}px`,
|
||||
cursor: chunk ? 'pointer' : 'default',
|
||||
position: 'relative',
|
||||
width: `${tileSize}px`,
|
||||
height: `${tileSize}px`,
|
||||
// pre-wrap
|
||||
whiteSpace: 'pre',
|
||||
}}
|
||||
>
|
||||
{relX}, {relZ}{'\n'}
|
||||
{chunk?.lines.join('\n')}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{showSidebar && selectedChunk && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
|
||||
{selectedChunk.displayLines.map((line, i) => (
|
||||
<div key={i} style={{ fontSize: '10px', wordBreak: 'break-word' }}>
|
||||
{line}
|
||||
</div>
|
||||
))}
|
||||
<div style={{ marginTop: '10px', fontSize: '10px' }} className='text-select'>
|
||||
<div>Sidebar Info:</div>
|
||||
{selectedChunk.sidebarLines.map((line, i) => (
|
||||
<div key={i}>{line}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
100
src/react/ChunksDebugScreen.tsx
Normal file
100
src/react/ChunksDebugScreen.tsx
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import { useUtilsEffect } from '@zardoy/react-util'
|
||||
import { WorldRendererCommon } from 'renderer/viewer/lib/worldrendererCommon'
|
||||
import { WorldRendererThree } from 'renderer/viewer/three/worldrendererThree'
|
||||
import Screen from './Screen'
|
||||
import ChunksDebug, { ChunkDebug } from './ChunksDebug'
|
||||
import { useIsModalActive } from './utilsApp'
|
||||
|
||||
const Inner = () => {
|
||||
const [playerX, setPlayerX] = useState(Math.floor(worldView!.lastPos.x / 16) * 16)
|
||||
const [playerZ, setPlayerZ] = useState(Math.floor(worldView!.lastPos.z / 16) * 16)
|
||||
const [update, setUpdate] = useState(0)
|
||||
|
||||
useUtilsEffect(({ interval }) => {
|
||||
interval(
|
||||
500,
|
||||
() => {
|
||||
setPlayerX(Math.floor(worldView!.lastPos.x / 16) * 16)
|
||||
setPlayerZ(Math.floor(worldView!.lastPos.z / 16) * 16)
|
||||
setUpdate(u => u + 1)
|
||||
}
|
||||
)
|
||||
}, [])
|
||||
|
||||
const chunksWaitingServer = Object.keys(worldView!.waitingSpiralChunksLoad).map((x): ChunkDebug => ({
|
||||
x: Number(x.split(',')[0]),
|
||||
z: Number(x.split(',')[1]),
|
||||
state: 'server-waiting',
|
||||
lines: [],
|
||||
sidebarLines: [],
|
||||
}))
|
||||
|
||||
const world = globalThis.world as WorldRendererThree
|
||||
|
||||
const loadedSectionsChunks = Object.fromEntries(Object.keys(world.sectionObjects).map(sectionPos => {
|
||||
const [x, y, z] = sectionPos.split(',').map(Number)
|
||||
return [`${x},${z}`, true]
|
||||
}))
|
||||
|
||||
const chunksWaitingClient = Object.keys(worldView!.loadedChunks).map((x): ChunkDebug => ({
|
||||
x: Number(x.split(',')[0]),
|
||||
z: Number(x.split(',')[1]),
|
||||
state: 'client-waiting',
|
||||
lines: [],
|
||||
sidebarLines: [],
|
||||
}))
|
||||
|
||||
const clientProcessingChunks = Object.keys(world.loadedChunks).map((x): ChunkDebug => ({
|
||||
x: Number(x.split(',')[0]),
|
||||
z: Number(x.split(',')[1]),
|
||||
state: 'client-processing',
|
||||
lines: [],
|
||||
sidebarLines: [],
|
||||
}))
|
||||
|
||||
const chunksDoneEmpty = Object.keys(world.finishedChunks)
|
||||
.filter(chunkPos => !loadedSectionsChunks[chunkPos])
|
||||
.map((x): ChunkDebug => ({
|
||||
x: Number(x.split(',')[0]),
|
||||
z: Number(x.split(',')[1]),
|
||||
state: 'done-empty',
|
||||
lines: [],
|
||||
sidebarLines: [],
|
||||
}))
|
||||
|
||||
const chunksDone = Object.keys(world.finishedChunks).map((x): ChunkDebug => ({
|
||||
x: Number(x.split(',')[0]),
|
||||
z: Number(x.split(',')[1]),
|
||||
state: 'done',
|
||||
lines: [],
|
||||
sidebarLines: [],
|
||||
}))
|
||||
|
||||
const allChunks = [
|
||||
...chunksWaitingServer,
|
||||
...chunksWaitingClient,
|
||||
...clientProcessingChunks,
|
||||
...chunksDone,
|
||||
...chunksDoneEmpty,
|
||||
]
|
||||
return <Screen title="Chunks Debug">
|
||||
<ChunksDebug
|
||||
chunks={allChunks}
|
||||
playerChunk={{
|
||||
x: playerX,
|
||||
z: playerZ
|
||||
}}
|
||||
maxDistance={worldView!.viewDistance}
|
||||
tileSize={32}
|
||||
fontSize={8}
|
||||
/>
|
||||
</Screen>
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const isActive = useIsModalActive('chunks-debug')
|
||||
if (!isActive) return null
|
||||
|
||||
return <Inner />
|
||||
}
|
||||
|
|
@ -180,6 +180,7 @@ export default () => {
|
|||
<div className={styles.empty} />
|
||||
<p>XYZ: {pos.x.toFixed(3)} / {pos.y.toFixed(3)} / {pos.z.toFixed(3)}</p>
|
||||
<p>Chunk: {Math.floor(pos.x % 16)} ~ {Math.floor(pos.z % 16)} in {Math.floor(pos.x / 16)} ~ {Math.floor(pos.z / 16)}</p>
|
||||
<p>Section: {Math.floor(pos.x / 16) * 16}, {Math.floor(pos.y / 16) * 16}, {Math.floor(pos.z / 16) * 16}</p>
|
||||
<p>Packets: {packetsString}</p>
|
||||
<p>Client TPS: {clientTps}</p>
|
||||
<p>Facing (viewer): {bot.entity.yaw.toFixed(3)} {bot.entity.pitch.toFixed(3)}</p>
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ import PacketsReplayProvider from './react/PacketsReplayProvider'
|
|||
import TouchInteractionHint from './react/TouchInteractionHint'
|
||||
import { ua } from './react/utils'
|
||||
import ControDebug from './react/ControDebug'
|
||||
import ChunksDebug from './react/ChunksDebug'
|
||||
import ChunksDebugScreen from './react/ChunksDebugScreen'
|
||||
|
||||
const isFirefox = ua.getBrowser().name === 'Firefox'
|
||||
if (isFirefox) {
|
||||
|
|
@ -157,6 +159,7 @@ const InGameUi = () => {
|
|||
{!disabledUiParts.includes('crosshair') && <Crosshair />}
|
||||
{!disabledUiParts.includes('books') && <BookProvider />}
|
||||
{!disabledUiParts.includes('bossbars') && displayBossBars && <BossBarOverlayProvider />}
|
||||
<ChunksDebugScreen />
|
||||
</PerComponentErrorBoundary>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue