use comparray; type Chunk = struct { sections: nullable *[*]Section, }; type Section = struct { bstates: comparray::Array, dirty: bool, non_air_count: i32, // render state vertexbuf: uint, vertexcount: i32, }; // XXX: the view range behaves a bit weirdly sometimes; further investigation // needed. def VIEW_MARGIN: i32 = 2; let CHUNKS: []Chunk = []; // len = VIEW_SIZE * VIEW_SIZE let CHUNKS_POS: ChunkPos = ChunkPos { ... }; // -X,-Z corner of the rectangular view area let CHUNKS_SIZE = 0i32; // width of the view area let CHUNKS_MIN_Y = 0i8; // in chunks let CHUNKS_HEIGHT = 0u8; // in chunks fn chunks_respawn() void = { const dim_type = &DIM_TYPES[DIM_TYPE]; CHUNKS_MIN_Y = dim_type.min_y; CHUNKS_HEIGHT = dim_type.height; setview(ChunkPos { ... }); }; fn setview(center: ChunkPos) void = { const view_pos = ChunkPos { x = center.x - VIEW_DISTANCE - VIEW_MARGIN, z = center.z - VIEW_DISTANCE - VIEW_MARGIN, }; const view_size = VIEW_DISTANCE * 2 + VIEW_MARGIN * 2 + 1; let chunks = alloc([Chunk { ... }...], (view_size * view_size): size); for (let z = 0i32; z < CHUNKS_SIZE; z += 1) for (let x = 0i32; x < CHUNKS_SIZE; x += 1) { const pos = ChunkPos { x = CHUNKS_POS.x + x, z = CHUNKS_POS.z + z, }; const chunk = getchunk(pos) as *Chunk; const pos_ = ChunkPos { x = pos.x - view_pos.x, z = pos.z - view_pos.z, }; if (pos_.x >= 0 && pos_.x < view_size && pos_.z >= 0 && pos_.z < view_size) { chunks[pos_.x + pos_.z * view_size] = *chunk; } else { chunk_destroy(pos); }; }; free(CHUNKS); CHUNKS = chunks; CHUNKS_POS = view_pos; CHUNKS_SIZE = view_size; }; fn setblock(pos: BlockPos, bstate: u16) void = { const spos = block_section(pos); const section = getsection(spos) as *Section; const ref = if (section.bstates.palette_size == comparray::DIRECT) { yield bstate; } else :ref { const palette = comparray::get_palette(§ion.bstates); for (let i = 0u8; i < len(palette); i += 1) { if (palette[i] == bstate) yield :ref, i: u16; }; comparray::grow_palette(§ion.bstates, 4096, section.bstates.palette_size + 1); if (section.bstates.palette_size == comparray::DIRECT) { yield :ref, bstate; } else { const i = section.bstates.palette_size - 1; comparray::get_palette(§ion.bstates)[i] = bstate; yield :ref, i: u16; }; }; const i = (pos.x & 0xf): size | (pos.z & 0xf): size << 4 | (pos.y & 0xf): size << 8; comparray::set(§ion.bstates, i, ref); section.dirty = true; if (pos.x & 0xf == 0) section_make_dirty(spos.x - 1, spos.y, spos.z); if (pos.x & 0xf == 15) section_make_dirty(spos.x + 1, spos.y, spos.z); if (pos.y & 0xf == 0) section_make_dirty(spos.x, spos.y - 1, spos.z); if (pos.y & 0xf == 15) section_make_dirty(spos.x, spos.y + 1, spos.z); if (pos.z & 0xf == 0) section_make_dirty(spos.x, spos.y, spos.z - 1); if (pos.z & 0xf == 15) section_make_dirty(spos.x, spos.y, spos.z + 1); }; fn getchunk(pos: ChunkPos) nullable *Chunk = { pos.x -= CHUNKS_POS.x; pos.z -= CHUNKS_POS.z; if (pos.x < 0 || pos.x >= CHUNKS_SIZE) return null; if (pos.z < 0 || pos.z >= CHUNKS_SIZE) return null; return &CHUNKS[pos.x + pos.z * CHUNKS_SIZE]; }; fn getsection(pos: SectionPos) nullable *Section = { if (pos.y < CHUNKS_MIN_Y || pos.y >= CHUNKS_MIN_Y + CHUNKS_HEIGHT: i8) return null; match (getchunk(pos.chunk)) { case let chunk: *Chunk => match (chunk.sections) { case let sections: *[*]Section => return §ions[pos.y: size - CHUNKS_MIN_Y: size]; case null => return null; }; case null => return null; }; }; fn chunk_init(pos: ChunkPos) *Chunk = { chunk_destroy(pos); let sections: []Section = alloc([], CHUNKS_HEIGHT); for (let i = 0z; i < CHUNKS_HEIGHT; i += 1) { let bstates = comparray::new(1, 4096); comparray::clear(&bstates, 0); static append(sections, Section { bstates = bstates, ... }); }; const chunk = getchunk(pos) as *Chunk; *chunk = Chunk { sections = sections: *[*]Section, }; for (let i = 0u8; i < CHUNKS_HEIGHT; i += 1) { const spos = SectionPos { chunk = pos, y = i: i8 + CHUNKS_MIN_Y, }; section_make_dirty(spos.x - 1, spos.y, spos.z); section_make_dirty(spos.x + 1, spos.y, spos.z); section_make_dirty(spos.x, spos.y, spos.z - 1); section_make_dirty(spos.x, spos.y, spos.z + 1); }; return chunk; }; fn section_make_dirty(x: i32, y: i8, z: i32) void = { match (getsection(SectionPos { x = x, y = y, z = z })) { case let section: *Section => section.dirty = true; case null => void; }; }; fn chunk_destroy(pos: ChunkPos) void = { const chunk = match (getchunk(pos)) { case let chunk: *Chunk => yield chunk; case null => return; }; const sections = match (chunk.sections) { case let sections: *[*]Section => yield sections; case null => return; }; for (let i = 0u8; i < CHUNKS_HEIGHT; i += 1) { section_destroy(§ions[i]); }; free(sections); chunk.sections = null; }; fn section_destroy(section: *Section) void = { render_chunks_section_destroy(section); comparray::clear(§ion.bstates, 0); }; fn chunks_despawn() void = { for (let z = 0i32; z < CHUNKS_SIZE; z += 1) for (let x = 0i32; x < CHUNKS_SIZE; x += 1) { const pos = ChunkPos { x = CHUNKS_POS.x + x, z = CHUNKS_POS.z + z, }; chunk_destroy(pos); }; free(CHUNKS); CHUNKS = []; CHUNKS_POS = ChunkPos { ... }; CHUNKS_SIZE = 0; CHUNKS_MIN_Y = 0; CHUNKS_HEIGHT = 0; };