Skip to content

Commit

Permalink
fix all known issues with chunk storage, cover with test, remove data…
Browse files Browse the repository at this point in the history
… from buffer
  • Loading branch information
zardoy committed Dec 5, 2024
1 parent 9794847 commit 912cf73
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 63 deletions.
146 changes: 121 additions & 25 deletions prismarine-viewer/examples/chunksStorage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,46 @@ import { ChunksStorage } from './chunksStorage'
globalThis.reportError = err => {
throw err
}
const testTile = {} as any
test('Free areas', () => {
const storage = new ChunksStorage()
const blocks100 = Object.fromEntries(Array.from({ length: 100 }).map((_, i) => {
return [`${i},0,0`, testTile]
storage.chunkSizeDisplay = 1
const blocksWith1 = Object.fromEntries(Array.from({ length: 100 }).map((_, i) => {
return [`${i},0,0`, 1 as any]
}))
const blocks100Test = Object.fromEntries(Array.from({ length: 100 }).map((_, i) => {
return [`${i},0,0`, 10 as any]
const blocksWith2 = Object.fromEntries(Array.from({ length: 100 }).map((_, i) => {
return [`${i},0,0`, 2 as any]
}))
const blocks10 = Object.fromEntries(Array.from({ length: 10 }).map((_, i) => {
return [`${i},0,0`, testTile]
const blocksWith3 = Object.fromEntries(Array.from({ length: 10 }).map((_, i) => {
return [`${i},0,0`, 3 as any]
}))
const blocksWith4 = Object.fromEntries(Array.from({ length: 10 }).map((_, i) => {
return [`${i},0,0`, 4 as any]
}))

const getRangeString = () => {
const ranges = {}
let lastNum = storage.allBlocks[0]?.[3]
let lastNumI = 0
for (let i = 0; i < storage.allBlocks.length; i++) {
const num = storage.allBlocks[i]?.[3]
if (lastNum !== num || i === storage.allBlocks.length - 1) {
const inclusive = i === storage.allBlocks.length - 1
ranges[`[${lastNumI}-${i}${inclusive ? ']' : ')'}`] = lastNum
lastNum = num
lastNumI = i
}
}
return ranges
}

const testRange = (start, end, number) => {
for (let i = start; i < end; i++) {
expect(storage.allBlocks[i]?.[3], `allblocks ${i} (range ${start}-${end})`).toBe(number)
}
}

storage.addChunk(blocks100, '0,0,0')
storage.addChunk(blocks100Test, '1,0,0')
storage.addChunk(blocksWith1, '0,0,0')
storage.addChunk(blocksWith2, '1,0,0')
expect(storage.chunksMap).toMatchInlineSnapshot(`
Map {
"0,0,0" => 0,
Expand All @@ -36,7 +61,7 @@ test('Free areas', () => {
{
"free": false,
"length": 100,
"x": 0.0625,
"x": 1,
"z": 0,
},
]
Expand All @@ -46,22 +71,31 @@ test('Free areas', () => {
"chunk": {
"free": false,
"length": 100,
"x": 0.0625,
"x": 1,
"z": 0,
},
"index": 1,
}
`)
expect(storage.allBlocks[99]?.[3]).not.toBe(10)
for (let i = 100; i < 200; i++) {
expect(storage.allBlocks[i]?.[3]).toBe(10)
}
expect(getRangeString()).toMatchInlineSnapshot(`
{
"[0-100)": 1,
"[100-199]": 2,
}
`)

storage.removeChunk('0,0,0')
expect(storage.chunks[0].free).toBe(true)
expect(storage.chunks[0].length).toBe(100)

storage.addChunk(blocks10, `0,0,2`)
expect(getRangeString()).toMatchInlineSnapshot(`
{
"[0-100)": undefined,
"[100-199]": 2,
}
`)

storage.addChunk(blocksWith3, `0,0,2`)
expect(storage.chunksMap).toMatchInlineSnapshot(`
Map {
"1,0,0" => 1,
Expand All @@ -74,19 +108,26 @@ test('Free areas', () => {
"free": false,
"length": 100,
"x": 0,
"z": 0.125,
"z": 2,
},
{
"free": false,
"length": 100,
"x": 0.0625,
"x": 1,
"z": 0,
},
]
`)
expect(getRangeString()).toMatchInlineSnapshot(`
{
"[0-10)": 3,
"[10-100)": undefined,
"[100-199]": 2,
}
`)

// update (no map changes)
storage.addChunk(blocks10, `0,0,2`)
storage.addChunk(blocksWith4, `0,0,2`)
expect(storage.chunksMap).toMatchInlineSnapshot(`
Map {
"1,0,0" => 1,
Expand All @@ -99,18 +140,25 @@ test('Free areas', () => {
"free": false,
"length": 100,
"x": 0,
"z": 0.125,
"z": 2,
},
{
"free": false,
"length": 100,
"x": 0.0625,
"x": 1,
"z": 0,
},
]
`)
expect(getRangeString()).toMatchInlineSnapshot(`
{
"[0-10)": 4,
"[10-100)": undefined,
"[100-199]": 2,
}
`)

storage.addChunk(blocks10, `0,0,3`)
storage.addChunk(blocksWith3, `0,0,3`)
expect(storage.chunksMap).toMatchInlineSnapshot(`
Map {
"1,0,0" => 1,
Expand All @@ -124,20 +172,68 @@ test('Free areas', () => {
"free": false,
"length": 100,
"x": 0,
"z": 0.125,
"z": 2,
},
{
"free": false,
"length": 100,
"x": 1,
"z": 0,
},
{
"free": false,
"length": 10,
"x": 0,
"z": 3,
},
]
`)
expect(getRangeString()).toMatchInlineSnapshot(`
{
"[0-10)": 4,
"[10-100)": undefined,
"[100-200)": 2,
"[200-209]": 3,
}
`)
expect(storage.allBlocks.length).toBe(210)

// update 0,0,2
storage.addChunk(blocksWith1, `0,0,2`)
expect(storage.chunksMap).toMatchInlineSnapshot(`
Map {
"1,0,0" => 1,
"0,0,3" => 2,
"0,0,2" => 0,
}
`)
expect(storage.chunks).toMatchInlineSnapshot(`
[
{
"free": false,
"length": 100,
"x": 0,
"z": 2,
},
{
"free": false,
"length": 100,
"x": 0.0625,
"x": 1,
"z": 0,
},
{
"free": false,
"length": 10,
"x": 0,
"z": 0.1875,
"z": 3,
},
]
`)
expect(getRangeString()).toMatchInlineSnapshot(`
{
"[0-100)": 1,
"[100-200)": 2,
"[200-209]": 3,
}
`)
})
75 changes: 46 additions & 29 deletions prismarine-viewer/examples/chunksStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export class ChunksStorage {
awaitingUpdateEnd: number | undefined
// dataSize = 0
lastFetchedSize = 0
chunkSizeDisplay = 16

get dataSize () {
return this.allBlocks.length
Expand Down Expand Up @@ -48,19 +49,23 @@ export class ChunksStorage {
}
}

setAwaitingUpdate ({ awaitingUpdateStart, awaitingUpdateSize }: { awaitingUpdateStart: number, awaitingUpdateSize: number }) {
this.awaitingUpdateStart = awaitingUpdateStart
this.awaitingUpdateEnd = awaitingUpdateStart + awaitingUpdateSize
}

clearData () {
this.chunks = []
this.allBlocks = []
this.awaitingUpdateStart = undefined
this.awaitingUpdateEnd = undefined
}

addBlocksData (start: number, newData: typeof this.allBlocks) {
let i = 0
while (i < newData.length) {
this.allBlocks.splice(start + i, 0, ...newData.slice(i, i + 1024))
i += 1024
replaceBlocksData (start: number, newData: typeof this.allBlocks) {
if (newData.length > 16 * 16 * 16) {
throw new Error(`Chunk cant be that big: ${newData.length}`)
}
this.allBlocks.splice(start, newData.length, ...newData)
}

getAvailableChunk (size: number) {
Expand All @@ -73,6 +78,7 @@ export class ChunksStorage {
if (chunkLength >= size) {
usingChunk = chunk
usingChunk.free = false
currentStart -= chunkLength
break
}
}
Expand All @@ -97,31 +103,33 @@ export class ChunksStorage {
removeChunk (chunkPosKey: string) {
if (!this.chunksMap.has(chunkPosKey)) return
let currentStart = 0
const chunkIndex = this.chunksMap.get(chunkPosKey)!
let chunkIndex = this.chunksMap.get(chunkPosKey)!
const chunk = this.chunks[chunkIndex]
for (let i = 0; i <= chunkIndex; i++) {
for (let i = 0; i < chunkIndex; i++) {
const chunk = this.chunks[i]!
currentStart += chunk.length
}

this.addBlocksData(currentStart, Array.from({ length: chunk.length }).map(() => undefined)) // empty data, will be filled with 0
this.replaceBlocksData(currentStart, Array.from({ length: chunk.length }).map(() => undefined)) // empty data, will be filled with 0
this.requestRangeUpdate(currentStart, currentStart + chunk.length)
chunk.free = true
this.chunksMap.delete(chunkPosKey)
// try merge backwards
for (let i = chunkIndex - 1; i >= 0; i--) {
const chunk = this.chunks[i]!
if (!chunk.free) break
chunk.length += this.chunks[i]!.length
this.chunks.splice(i, 1)
}
// try merge forwards
for (let i = chunkIndex + 1; i < this.chunks.length; i++) {
const chunk = this.chunks[i]!
if (!chunk.free) break
chunk.length += this.chunks[i]!.length
this.chunks.splice(i, 1)
i--
}
// for (let i = chunkIndex - 1; i >= 0; i--) {
// const chunk = this.chunks[i]!
// if (!chunk.free) break
// chunk.length += this.chunks[i]!.length
// this.chunks.splice(i, 1)
// chunkIndex--
// }
// // try merge forwards
// for (let i = chunkIndex + 1; i < this.chunks.length; i++) {
// const chunk = this.chunks[i]!
// if (!chunk.free) break
// chunk.length += this.chunks[i]!.length
// this.chunks.splice(i, 1)
// i--
// }
}

addChunk (blocks: Record<string, BlockType>, rawPosKey: string) {
Expand Down Expand Up @@ -160,18 +168,27 @@ export class ChunksStorage {
// }

const { chunk, start } = this.getAvailableChunk(newData.length)
chunk.x = xSection / 16
chunk.z = zSection / 16
chunk.x = xSection / this.chunkSizeDisplay
chunk.z = zSection / this.chunkSizeDisplay
const chunkIndex = this.chunks.indexOf(chunk)
this.chunksMap.set(rawPosKey, chunkIndex)

for (const newDatum of newData) {
//@ts-expect-error
newDatum[3].chunk = chunkIndex
for (const b of newData) {
if (b[3] && typeof b[3] === 'object') {
b[3].chunk = chunkIndex
}
}

this.addBlocksData(start, newData)
this.replaceBlocksData(start, newData)
this.requestRangeUpdate(start, start + newData.length)
}

requestRangeUpdate (start: number, end: number) {
this.awaitingUpdateStart = Math.min(this.awaitingUpdateStart ?? Infinity, start)
this.awaitingUpdateEnd = Math.max(this.awaitingUpdateEnd ?? -Infinity, start + newData.length)
this.awaitingUpdateEnd = Math.max(this.awaitingUpdateEnd ?? -Infinity, end)
}

clearRange (start: number, end: number) {
// this.replaceBlocksData(start, Array.from({ length: end - start }).map(() => undefined))
}
}
1 change: 1 addition & 0 deletions prismarine-viewer/examples/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type BlockType = {
tint?: [number, number, number]

// for testing
chunk?: number
block: string
}

Expand Down
Loading

0 comments on commit 912cf73

Please sign in to comment.