commit a4f9b7a
uint
·
2026-08-04 19:50:05 +0000 UTC
parent 1b98d94
move test_scene chunking functionality to net/mc_chunk and client/world
10 files changed,
+583,
-420
+25,
-0
1@@ -0,0 +1,25 @@
2+#ifndef WORLD_H
3+#define WORLD_H
4+
5+#include "net/mc_chunk.h"
6+#include "world/chunk.h"
7+
8+#define CLIENT_WORLD_MAX_CHUNKS 128
9+
10+typedef struct {
11+ Chunk chunks[CLIENT_WORLD_MAX_CHUNKS];
12+} ClientWorld;
13+
14+/* initialise world with 0 */
15+void world_init(ClientWorld* world);
16+
17+/* destroy world (frees all chunks) */
18+void world_destroy(ClientWorld* world);
19+
20+/* remove chunk from world (clears data, marks unsued) */
21+void world_remove_chunk(ClientWorld* world, i32 x, i32 z);
22+
23+/* update chunk with data from server */
24+i8 world_update_chunk(ClientWorld* world, const MCChunkUpdate* update, const u8* data);
25+
26+#endif /* WORLD_H */
+38,
-0
1@@ -0,0 +1,38 @@
2+#ifndef MC_CHUNK_H
3+#define MC_CHUNK_H
4+
5+#include <stddef.h>
6+
7+#include "types.h"
8+#include "net/mc_proto.h"
9+
10+typedef struct {
11+ i32 x;
12+ i32 z;
13+ u16 mask;
14+ i8 replace;
15+} MCChunkUpdate;
16+
17+typedef struct {
18+ MCChunkUpdate* updates;
19+ MCChunkUpdate single_update;
20+ i32 update_count;
21+ const u8* data;
22+ size_t data_size;
23+ u8 skylight;
24+ i8 bulk;
25+ i8 unload;
26+ i32 unload_x;
27+ i32 unload_z;
28+} MCChunkPacket;
29+
30+/* general decode chunk data from packet */
31+i8 mc_chunk_decode(MCChunkPacket* chunk, const MCPacket* packet);
32+
33+/* destroy chunk data */
34+void mc_chunk_destroy(MCChunkPacket* chunk);
35+
36+/* calculate size of chunk data */
37+size_t mc_chunk_update_size(u16 mask, u8 skylight, i8 bulk);
38+
39+#endif /* MC_CHUNK_H */
+31,
-316
1@@ -5,6 +5,7 @@
2
3 #include "mgmath.h"
4 #include "util.h"
5+#include "client/world.h"
6 #include "net/mc_proto.h"
7 #include "net/packet.h"
8 #include "client/camera.h"
9@@ -15,293 +16,15 @@
10
11 /* big TODO. most of this stuff belongs elsewhere !!! */
12
13-typedef struct {
14- i32 x;
15- i32 z;
16-
17- Chunk sections[16]; /* a 16x256x16 chunk */
18- u16 section_mask;
19-
20- i8 used;
21-} ServerChunk;
22-
23-typedef struct {
24- i32 x;
25- i32 z;
26- u16 mask;
27-} BulkChunkMeta;
28+#define TEST_WORLD_WIDTH 50
29+#define TEST_WORLD_DEPTH 50
30
31 static MGCamera cam;
32-static Chunk chunks[WORLD_WIDTH][WORLD_DEPTH];
33+static Chunk chunks[TEST_WORLD_WIDTH][TEST_WORLD_DEPTH];
34
35-#define NCHUNKS 128
36-static ServerChunk server_chunks[NCHUNKS];
37+static ClientWorld server_world;
38 static i8 server_mode;
39
40-ServerChunk* server_chunk_find(i32 x, i32 z)
41-{
42- i32 i;
43-
44- for (i = 0; i < NCHUNKS; i++) {
45- if (!server_chunks[i].used)
46- continue;
47-
48- if (server_chunks[i].x == x && server_chunks[i].z == z)
49- return &server_chunks[i];
50- }
51-
52- return NULL;
53-}
54-
55-ServerChunk* server_chunk_get(i32 x, i32 z)
56-{
57- ServerChunk* col;
58- i32 i;
59-
60- col = server_chunk_find(x, z);
61- if (col)
62- return col;
63-
64- for (i = 0; i < NCHUNKS; i++) {
65- if (server_chunks[i].used)
66- continue;
67-
68- col = &server_chunks[i];
69-
70- memset(col, 0, sizeof(*col));
71-
72- col->used = 1;
73- col->x = x;
74- col->z = z;
75-
76- return col;
77- }
78-
79- mg_log(LOG_ERR, "Server chunk storage is full");
80- return NULL;
81-}
82-
83-void server_chunk_clear(ServerChunk* column)
84-{
85- i32 section;
86-
87- if (!column)
88- return;
89-
90- for (section = 0; section < 16; section++) {
91- chunk_mesh_destroy(&column->sections[section]);
92- memset(&column->sections[section], 0, sizeof(column->sections[section]));
93- }
94-
95- column->section_mask = 0;
96-}
97-
98-void server_chunk_remove(ServerChunk* column)
99-{
100- if (!column)
101- return;
102-
103- server_chunk_clear(column);
104- memset(column, 0, sizeof(*column));
105-}
106-
107-i32 section_count(u16 mask)
108-{
109- i32 section;
110- i32 count;
111-
112- count = 0;
113-
114- for (section = 0; section < 16; section++) {
115- if (mask & (1U << section))
116- count++;
117- }
118-
119- return count;
120-}
121-
122-i8 load_server_section(Chunk* section, const u8* data, size_t size)
123-{
124- i32 index;
125- i32 x;
126- i32 y;
127- i32 z;
128- u16 state;
129-
130- if (size < 8192)
131- return 0;
132-
133- chunk_mesh_destroy(section);
134- memset(section, 0, sizeof(*section));
135-
136- for (index = 0; index < 4096; index++) {
137- state = (u16)data[index * 2] | ((u16)data[index * 2 + 1] << 8);
138-
139- x = index & 15;
140- z = (index >> 4) & 15;
141- y = (index >> 8) & 15;
142-
143- chunk_set(section, x, y, z, state);
144- }
145-
146- if (!chunk_mesh_create(section)) {
147- mg_log(LOG_ERR, "Failed to create server section mesh");
148- return 0;
149- }
150-
151- return 1;
152-}
153-
154-void test_scene_chunk(const MCPacket* packet)
155-{
156- PacketReader reader = packet_reader_init(packet->data, packet->size);
157- ServerChunk* column;
158- const u8* data;
159- size_t required_size;
160-
161- i32 chunk_x;
162- i32 chunk_z;
163- i32 data_size;
164- i32 included;
165- i32 section;
166- i32 data_section;
167- u8 ground_up;
168- u16 mask;
169-
170- if (!packet_read_i32(&reader, &chunk_x) || !packet_read_i32(&reader, &chunk_z) ||
171- !packet_read_u8(&reader, &ground_up) || !packet_read_u16(&reader, &mask) ||
172- !packet_read_varint(&reader, &data_size)) {
173- mg_log(LOG_ERR, "Malformed Chunk Data packet");
174- return;
175- }
176-
177- if (data_size < 0)
178- return;
179-
180- if ((size_t)data_size > reader.size - reader.cursor)
181- return;
182-
183- if (ground_up && mask == 0) {
184- column = server_chunk_find(chunk_x, chunk_z);
185- if (column)
186- server_chunk_remove(column);
187- return;
188- }
189-
190- included = section_count(mask);
191- required_size = included * 8192;
192-
193- if (required_size > (size_t)data_size) {
194- mg_log(LOG_ERR, "Chunk packet block data is imcomplete");
195- return;
196- }
197-
198- column = server_chunk_get(chunk_x, chunk_z);
199-
200- if (!column)
201- return;
202-
203- if (ground_up)
204- server_chunk_clear(column);
205-
206- data = reader.data + reader.cursor;
207- data_section = 0;
208-
209- for (section = 0; section < 16; section++) {
210- if (!(mask & (1U << section)))
211- continue;
212-
213- if (!load_server_section(&column->sections[section], data + data_section * 8192, 8192))
214- return;
215-
216- column->section_mask |= (u16)(1U << section);
217- data_section++;
218- }
219-}
220-
221-void test_scene_bulk_chunks(const MCPacket* packet)
222-{
223- PacketReader reader = packet_reader_init(packet->data, packet->size);
224- BulkChunkMeta* metadata;
225- ServerChunk* column;
226- const u8* data;
227- size_t offset;
228- size_t block_size;
229- size_t column_size;
230- i32 count;
231- i32 index;
232- i32 sections;
233- i32 section;
234- i32 data_section;
235- u8 skylight;
236-
237- reader.data = packet->data;
238- reader.size = packet->size;
239- reader.cursor = 0;
240-
241- if (!packet_read_u8(&reader, &skylight) || !packet_read_varint(&reader, &count)) {
242- mg_log(LOG_ERR, "Malformed Map Chunk Bulk packet");
243- return;
244- }
245-
246- if (count <= 0 || count > 4096)
247- return;
248-
249- metadata = malloc(count * sizeof(*metadata));
250-
251- if (!metadata)
252- return;
253-
254- for (index = 0; index < count; index++) {
255- if (!packet_read_i32(&reader, &metadata[index].x) || !packet_read_i32(&reader, &metadata[index].z) ||
256- !packet_read_u16(&reader, &metadata[index].mask)) {
257- free(metadata);
258- return;
259- }
260- }
261-
262- data = reader.data + reader.cursor;
263- offset = 0;
264-
265- for (index = 0; index < count; index++) {
266- sections = section_count(metadata[index].mask);
267- block_size = sections * 8192;
268- column_size = block_size + sections * 2048 + (skylight ? sections * 2048 : 0) + 256;
269-
270- if (offset > reader.size - reader.cursor)
271- break;
272-
273- if (column_size > reader.size - reader.cursor - offset) {
274- mg_log(LOG_ERR, "Bulk chunk data is incomplete");
275- break;
276- }
277-
278- column = server_chunk_get(metadata[index].x, metadata[index].z);
279- if (!column) {
280- offset += column_size;
281- continue;
282- }
283-
284- server_chunk_clear(column);
285- data_section = 0;
286-
287- for (section = 0; section < 16; section++) {
288- if (!(metadata[index].mask & (1U << section)))
289- continue;
290-
291- if (!load_server_section(&column->sections[section], data + offset + data_section * 8192, 8192))
292- break;
293-
294- column->section_mask |= (u16)(1U << section);
295- data_section++;
296- }
297-
298- offset += column_size;
299- }
300-
301- free(metadata);
302-}
303-
304 void test_scene_create(Player* player, i8 server)
305 {
306 i32 x;
307@@ -315,7 +38,7 @@ void test_scene_create(Player* player, i8 server)
308 cam.pitch = MG_AngleDeg(-25.0f);
309
310 server_mode = server;
311- memset(server_chunks, 0, sizeof(server_chunks));
312+ world_init(&server_world);
313 player->x = 0.0;
314 player->y = 0.0;
315 player->z = 0.0;
316@@ -327,11 +50,13 @@ void test_scene_create(Player* player, i8 server)
317 cam.far = 512.0f;
318 }
319 else { /* test world */
320- for (z = 0; z < WORLD_DEPTH; z++) {
321- for (x = 0; x < WORLD_WIDTH; x++) {
322- chunk_create(&chunks[x][z]);
323+ for (z = 0; z < TEST_WORLD_DEPTH; z++) {
324+ for (x = 0; x < TEST_WORLD_WIDTH; x++) {
325+ chunk_clear(&chunks[x][z]);
326+ chunk_section_create(&chunks[x][z].sections[0]);
327+ chunks[x][z].section_mask = 1;
328
329- if (!chunk_mesh_create(&chunks[x][z]))
330+ if (!chunk_section_mesh_create(&chunks[x][z].sections[0]))
331 mg_die(MG_EC_MESH_CREATE, "Failed to create chunk mesh");
332 }
333 }
334@@ -351,10 +76,10 @@ void test_scene_draw(MGContext* ctx)
335 float aspect;
336 i32 texture;
337
338- ServerChunk* column;
339- Chunk* section_chunk;
340- i32 section;
341- i32 i;
342+ Chunk* column;
343+ ChunkSection* section_chunk;
344+ i32 section;
345+ i32 i;
346
347 platform_get_drawable_size(ctx, &width, &height);
348
349@@ -367,19 +92,19 @@ void test_scene_draw(MGContext* ctx)
350 view = camera_get_view(&cam);
351
352 if (server_mode) {
353- for (i = 0; i < NCHUNKS; i++) {
354- column = &server_chunks[i];
355+ for (i = 0; i < CLIENT_WORLD_MAX_CHUNKS; i++) {
356+ column = &server_world.chunks[i];
357
358 if (!column->used)
359 continue;
360
361- for (section = 0; section < 16; section++) {
362+ for (section = 0; section < CHUNK_SECTION_COUNT; section++) {
363 if (!(column->section_mask & (1U << section)))
364 continue;
365
366 section_chunk = &column->sections[section];
367- model = MG_Translate(
368- MG_V3(column->x * CHUNK_WIDTH, section * CHUNK_HEIGHT, column->z * CHUNK_DEPTH));
369+ model = MG_Translate(MG_V3(column->x * CHUNK_WIDTH, section * CHUNK_SECTION_HEIGHT,
370+ column->z * CHUNK_DEPTH));
371 mvp = MG_MulM4(proj, MG_MulM4(view, model));
372 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
373 if (section_chunk->meshes[texture] != MGMESH_INVALID)
374@@ -390,16 +115,16 @@ void test_scene_draw(MGContext* ctx)
375 }
376 }
377 else { /* test world */
378- for (z = 0; z < WORLD_DEPTH; z++) {
379- for (x = 0; x < WORLD_WIDTH; x++) {
380+ for (z = 0; z < TEST_WORLD_DEPTH; z++) {
381+ for (x = 0; x < TEST_WORLD_WIDTH; x++) {
382 model = MG_Translate(MG_V3(x * CHUNK_WIDTH, 0.0f, z * CHUNK_DEPTH));
383 mvp = MG_MulM4(proj, MG_MulM4(view, model));
384
385 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
386- if (chunks[x][z].meshes[texture] == MGMESH_INVALID)
387+ if (chunks[x][z].sections[0].meshes[texture] == MGMESH_INVALID)
388 continue;
389- renderer_mesh_draw(chunks[x][z].meshes[texture], block_texture_get(texture),
390- &mvp);
391+ renderer_mesh_draw(chunks[x][z].sections[0].meshes[texture],
392+ block_texture_get(texture), &mvp);
393 }
394 }
395 }
396@@ -411,22 +136,12 @@ void test_scene_destroy(void)
397 i32 x;
398 i32 z;
399
400- i32 section;
401- i32 i;
402-
403- if (server_mode) {
404- for (i = 0; i < NCHUNKS; i++) {
405- if (!server_chunks[i].used)
406- continue;
407-
408- for (section = 0; section < 16; section++)
409- chunk_mesh_destroy(&server_chunks[i].sections[section]);
410- }
411- }
412+ if (server_mode)
413+ world_destroy(&server_world);
414 else {
415- for (z = 0; z < WORLD_DEPTH; z++) {
416- for (x = 0; x < WORLD_WIDTH; x++)
417- chunk_mesh_destroy(&chunks[x][z]);
418+ for (z = 0; z < TEST_WORLD_DEPTH; z++) {
419+ for (x = 0; x < TEST_WORLD_WIDTH; x++)
420+ chunk_remove(&chunks[x][z]);
421 }
422 }
423
+35,
-22
1@@ -6,38 +6,51 @@
2 #include "renderer/renderer.h"
3
4 #define CHUNK_WIDTH 16
5-#define CHUNK_HEIGHT 16
6 #define CHUNK_DEPTH 16
7+#define CHUNK_HEIGHT 256
8
9-#define WORLD_WIDTH 10
10-#define WORLD_DEPTH 10
11+#define CHUNK_SECTION_WIDTH 16
12+#define CHUNK_SECTION_HEIGHT 16
13+#define CHUNK_SECTION_DEPTH 16
14+#define CHUNK_SECTION_COUNT 16
15
16 typedef struct {
17- BlockType blocks[CHUNK_WIDTH][CHUNK_HEIGHT][CHUNK_DEPTH];
18+ BlockType blocks[CHUNK_SECTION_WIDTH][CHUNK_SECTION_HEIGHT][CHUNK_SECTION_DEPTH];
19 MGMesh meshes[BLOCK_TEXTURE_COUNT];
20+} ChunkSection;
21+
22+typedef struct {
23+ i32 x;
24+ i32 z;
25+
26+ ChunkSection sections[CHUNK_SECTION_COUNT];
27+ u16 section_mask;
28+ i8 used;
29 } Chunk;
30
31-/* create a new chunk
32- just stone and dirt with a hint of brick for now */
33-void chunk_create(Chunk* chunk);
34+/* clear chunk contents and mark unused */
35+void chunk_clear(Chunk* chunk);
36+
37+/* clear chunk contents and keep unloaded */
38+void chunk_remove(Chunk* chunk);
39+
40+/* create a new chunk section
41+ TODO: make it general not only for test world */
42+void chunk_section_create(ChunkSection* section);
43+
44+/* decode packed block states into a chunk section */
45+i8 chunk_section_decode(ChunkSection* section, const u8* data, size_t size);
46
47-/* get the block at specified position
48- TODO? should we use x, y, z or a vec3? c89 doesnt
49- have compound literals so it may be a pain in
50- the future? who knows */
51-BlockType chunk_get(const Chunk* chunk, i32 x, i32 y, i32 z);
52+/* get the block at specified coordinate in chunk section */
53+BlockType chunk_section_get(const ChunkSection* section, i32 x, i32 y, i32 z);
54
55-/* set the block at specified position
56- TODO? should we use x, y, z or a vec3? c89 doesnt
57- have compound literals so it may be a pain in
58- the future? who knows */
59-void chunk_set(Chunk* chunk, i32 x, i32 y, i32 z, BlockType block);
60+/* set the block at specified coordinate in chunk section */
61+void chunk_section_set(ChunkSection* section, i32 x, i32 y, i32 z, BlockType block);
62
63-/* create a mesh for chunk by generating vertices
64- and indices for each (visible) face */
65-i8 chunk_mesh_create(Chunk* chunk);
66+/* create a mesh for a chunk section */
67+i8 chunk_section_mesh_create(ChunkSection* section);
68
69-/* destroy a chunks mesh */
70-void chunk_mesh_destroy(Chunk* chunk);
71+/* destroy a chunk sections mesh */
72+void chunk_section_mesh_destroy(ChunkSection* section);
73
74 #endif /* CHUNK_H */
+2,
-0
1@@ -29,7 +29,9 @@ sources = [
2 'source/world/chunk.c',
3 'source/client/camera.c',
4 'source/client/player.c',
5+ 'source/client/world.c',
6 'source/net/mc_auth.c',
7+ 'source/net/mc_chunk.c',
8 'source/net/mc_buffer.c',
9 'source/net/mc_client.c',
10 'source/net/mc_crypto.c',
+121,
-0
1@@ -0,0 +1,121 @@
2+#include <string.h>
3+
4+#include "types.h"
5+#include "util.h"
6+#include "client/world.h"
7+
8+#define MC_CHUNK_SECTION_BLOCK_BYTES 8192
9+
10+static Chunk* world_chunk_find(ClientWorld* world, i32 x, i32 z);
11+static Chunk* world_chunk_get(ClientWorld* world, i32 x, i32 z);
12+
13+static Chunk* world_chunk_find(ClientWorld* world, i32 x, i32 z)
14+{
15+ i32 i;
16+
17+ for (i = 0; i < CLIENT_WORLD_MAX_CHUNKS; i++) {
18+ if (!world->chunks[i].used)
19+ continue;
20+
21+ if (world->chunks[i].x == x && world->chunks[i].z == z)
22+ return &world->chunks[i];
23+ }
24+
25+ return NULL;
26+}
27+
28+static Chunk* world_chunk_get(ClientWorld* world, i32 x, i32 z)
29+{
30+ Chunk* chunk;
31+ i32 i;
32+
33+ chunk = world_chunk_find(world, x, z);
34+ if (chunk)
35+ return chunk;
36+
37+ for (i = 0; i < CLIENT_WORLD_MAX_CHUNKS; i++) {
38+ if (world->chunks[i].used)
39+ continue;
40+
41+ /* unused chunk: init and return */
42+ chunk = &world->chunks[i];
43+ memset(chunk, 0, sizeof(*chunk));
44+ chunk->used = 1;
45+ chunk->x = x;
46+ chunk->z = z;
47+ return chunk;
48+ }
49+
50+ mg_log(LOG_ERR, "World chunk storage is full");
51+ return NULL;
52+}
53+
54+void world_init(ClientWorld* world)
55+{
56+ if (!world)
57+ return;
58+
59+ memset(world, 0, sizeof(*world));
60+}
61+
62+void world_destroy(ClientWorld* world)
63+{
64+ i32 i;
65+
66+ if (!world)
67+ return;
68+
69+ for (i = 0; i < CLIENT_WORLD_MAX_CHUNKS; i++)
70+ chunk_remove(&world->chunks[i]);
71+}
72+
73+void world_remove_chunk(ClientWorld* world, i32 x, i32 z)
74+{
75+ Chunk* chunk;
76+
77+ if (!world)
78+ return;
79+
80+ chunk = world_chunk_find(world, x, z);
81+ if (chunk)
82+ chunk_remove(chunk);
83+}
84+
85+i8 world_update_chunk(ClientWorld* world, const MCChunkUpdate* update, const u8* data)
86+{
87+ Chunk* chunk;
88+ i32 section;
89+ i32 data_section;
90+
91+ if (!world || !update || !data)
92+ return 0;
93+
94+ chunk = world_chunk_get(world, update->x, update->z);
95+ if (!chunk)
96+ return 1;
97+
98+ if (update->replace)
99+ chunk_clear(chunk);
100+
101+ /* decode each section in chunk */
102+ data_section = 0;
103+ for (section = 0; section < CHUNK_SECTION_COUNT; section++) {
104+ /* skip sections not in update mask */
105+ if (!(update->mask & (1U << section)))
106+ continue;
107+
108+ /* decode section data into mesh */
109+ if (!chunk_section_decode(&chunk->sections[section],
110+ data + (size_t)data_section * MC_CHUNK_SECTION_BLOCK_BYTES,
111+ MC_CHUNK_SECTION_BLOCK_BYTES)) {
112+ mg_log(LOG_ERR, "Failed to create chunk mesh");
113+ return 0;
114+ }
115+
116+ /* mark section present in chunk */
117+ chunk->section_mask |= (u16)(1U << section);
118+ data_section++;
119+ }
120+
121+ return 1;
122+}
+36,
-3
1@@ -4,6 +4,7 @@
2 #include "config.h"
3 #include "util.h"
4 #include "client/player.h"
5+#include "net/mc_chunk.h"
6 #include "net/mc_proto.h"
7 #include "platform/platform.h"
8 #include "renderer/renderer.h"
9@@ -57,6 +58,40 @@ static void input(void)
10 camera_rotate(&cam, mx * sensitivity, -my * sensitivity);
11 }
12
13+static void chunk_packet(ClientWorld* world, const MCPacket* packet)
14+{
15+ MCChunkPacket chunk;
16+ size_t offset;
17+ size_t size;
18+ i32 update;
19+
20+ if (!mc_chunk_decode(&chunk, packet))
21+ return;
22+
23+ if (chunk.unload) {
24+ world_remove_chunk(world, chunk.unload_x, chunk.unload_z);
25+ mc_chunk_destroy(&chunk);
26+ return;
27+ }
28+
29+ offset = 0;
30+
31+ for (update = 0; update < chunk.update_count; update++) {
32+ size = mc_chunk_update_size(chunk.updates[update].mask, chunk.skylight, chunk.bulk);
33+ if (offset > chunk.data_size || size > chunk.data_size - offset) {
34+ mg_log(LOG_ERR, "Chunk data is incomplete");
35+ break;
36+ }
37+
38+ if (!world_update_chunk(world, &chunk.updates[update], chunk.data + offset))
39+ break;
40+
41+ offset += size;
42+ }
43+
44+ mc_chunk_destroy(&chunk);
45+}
46+
47 int main(int argc, char** argv)
48 {
49 MCClient* client = NULL;
50@@ -107,10 +142,8 @@ int main(int argc, char** argv)
51 player_server_handle_position(&player, &cam, &packet);
52 break;
53 case 0x21:
54- test_scene_chunk(&packet);
55- break;
56 case 0x26:
57- test_scene_bulk_chunks(&packet);
58+ chunk_packet(&server_world, &packet);
59 break;
60 }
61
+151,
-0
1@@ -0,0 +1,151 @@
2+#include <stdlib.h>
3+#include <string.h>
4+
5+#include "util.h"
6+#include "net/mc_chunk.h"
7+#include "net/packet.h"
8+#include "world/chunk.h"
9+
10+#define MC_CHUNK_SECTION_BLOCK_BYTES 8192
11+#define MC_CHUNK_SECTION_LIGHT_BYTES 2048
12+#define MC_CHUNK_BIOME_BYTES 256
13+
14+static i8 mc_chunk_decode_single(MCChunkPacket* chunk, PacketReader* reader);
15+static i8 mc_chunk_decode_bulk(MCChunkPacket* chunk, PacketReader* reader);
16+static i32 mc_chunk_section_count(u16 mask);
17+
18+/* decode single chunk data from packet */
19+static i8 mc_chunk_decode_single(MCChunkPacket* chunk, PacketReader* reader)
20+{
21+ MCChunkUpdate* update;
22+ i32 declared_size;
23+ u8 ground_up;
24+
25+ update = &chunk->single_update;
26+
27+ /* read chunk coordinates, mask, size */
28+ if (!packet_read_i32(reader, &update->x) || !packet_read_i32(reader, &update->z) ||
29+ !packet_read_u8(reader, &ground_up) || !packet_read_u16(reader, &update->mask) ||
30+ !packet_read_varint(reader, &declared_size)) {
31+ mg_log(LOG_ERR, "Malformed Chunk Data packet");
32+ return 0;
33+ }
34+
35+ if (declared_size < 0 || (size_t)declared_size > reader->size - reader->cursor)
36+ return 0;
37+
38+ /* if ground_up set and mask is 0, set chunk for unload */
39+ if (ground_up && update->mask == 0) {
40+ chunk->unload = 1;
41+ chunk->unload_x = update->x;
42+ chunk->unload_z = update->z;
43+ return 1;
44+ }
45+
46+ update->replace = ground_up;
47+ chunk->updates = update;
48+ chunk->update_count = 1;
49+ chunk->data = reader->data + reader->cursor;
50+ chunk->data_size = declared_size;
51+ return 1;
52+}
53+
54+/* decode bulk chunk data from packet */
55+static i8 mc_chunk_decode_bulk(MCChunkPacket* chunk, PacketReader* reader)
56+{
57+ i32 update;
58+
59+ if (!packet_read_u8(reader, &chunk->skylight) || !packet_read_varint(reader, &chunk->update_count)) {
60+ mg_log(LOG_ERR, "Malformed Map Chunk Bulk packet");
61+ return 0;
62+ }
63+
64+ if (chunk->update_count <= 0 || chunk->update_count > 4096)
65+ return 0;
66+
67+ chunk->updates = malloc((size_t)chunk->update_count * sizeof(*chunk->updates));
68+ if (!chunk->updates)
69+ return 0;
70+
71+ /* read chunk updates */
72+ for (update = 0; update < chunk->update_count; update++) {
73+ if (!packet_read_i32(reader, &chunk->updates[update].x) ||
74+ !packet_read_i32(reader, &chunk->updates[update].z) ||
75+ !packet_read_u16(reader, &chunk->updates[update].mask)) {
76+ mg_log(LOG_ERR, "Malformed Map Chunk Bulk packet");
77+ mc_chunk_destroy(chunk);
78+ return 0;
79+ }
80+
81+ chunk->updates[update].replace = 1;
82+ }
83+
84+ chunk->bulk = 1;
85+ chunk->data = reader->data + reader->cursor;
86+ chunk->data_size = reader->size - reader->cursor;
87+ return 1;
88+}
89+
90+/* count number of chunk sections mask */
91+static i32 mc_chunk_section_count(u16 mask)
92+{
93+ i32 section;
94+ i32 count;
95+
96+ count = 0;
97+
98+ for (section = 0; section < CHUNK_SECTION_COUNT; section++) {
99+ if (mask & (1U << section)) /* section present in mask */
100+ count++;
101+ }
102+
103+ return count;
104+}
105+
106+i8 mc_chunk_decode(MCChunkPacket* chunk, const MCPacket* packet)
107+{
108+ PacketReader reader;
109+
110+ if (!chunk || !packet)
111+ return 0;
112+
113+ memset(chunk, 0, sizeof(*chunk));
114+ reader = packet_reader_init(packet->data, packet->size);
115+
116+ switch (packet->id) {
117+ case 0x21:
118+ return mc_chunk_decode_single(chunk, &reader);
119+ case 0x26:
120+ return mc_chunk_decode_bulk(chunk, &reader);
121+ }
122+
123+ return 0;
124+}
125+
126+void mc_chunk_destroy(MCChunkPacket* chunk)
127+{
128+ if (!chunk)
129+ return;
130+
131+ if (chunk->updates != &chunk->single_update)
132+ free(chunk->updates);
133+
134+ memset(chunk, 0, sizeof(*chunk));
135+}
136+
137+size_t mc_chunk_update_size(u16 mask, u8 skylight, i8 bulk)
138+{
139+ i32 sections;
140+ size_t size;
141+
142+ sections = mc_chunk_section_count(mask);
143+ size = sections * MC_CHUNK_SECTION_BLOCK_BYTES;
144+ if (bulk) {
145+ size += sections * MC_CHUNK_SECTION_LIGHT_BYTES;
146+ size += MC_CHUNK_BIOME_BYTES;
147+ if (skylight)
148+ size += sections * MC_CHUNK_SECTION_LIGHT_BYTES;
149+ }
150+
151+ return size;
152+}
+30,
-30
1@@ -8,7 +8,7 @@
2 #include "renderer/renderer.h"
3 #include "renderer_backend.h"
4
5-#define MAX_MESHES 8192
6+#define MAX_MESHES 8192
7 #define MAX_TEXTURES 256
8
9 typedef struct {
10@@ -47,35 +47,35 @@ static void create_sampler(void);
11 static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user);
12
13 static RendererState ren;
14-static const char* vsdr = "#version 330\n"
15- "layout(location=0) in vec3 position;\n"
16- "layout(location=1) in vec2 texcoord0;\n"
17- "layout(location=2) in vec4 color0;\n"
18- "\n"
19- "uniform mat4 mvp;\n"
20- "\n"
21- "out vec2 uv;\n"
22- "out vec4 colour;\n"
23- "\n"
24- "void main(void)\n"
25- "{\n"
26- " gl_Position = mvp * vec4(position, 1.0);\n"
27- " uv = texcoord0;\n"
28- " colour = color0;\n"
29- "}\n";
30-static const char* fsdr = "#version 330\n"
31- "in vec2 uv;\n"
32- "in vec4 colour;\n"
33- "\n"
34- "uniform sampler2D tex;\n"
35- "\n"
36- "out vec4 frag_colour;\n"
37- "\n"
38- "void main(void)\n"
39- "{\n"
40- " frag_colour = texture(tex, uv) * colour;\n"
41- " if (frag_colour.a < 0.5) discard;\n"
42- "}\n";
43+static const char* vsdr = "#version 330\n"
44+ "layout(location=0) in vec3 position;\n"
45+ "layout(location=1) in vec2 texcoord0;\n"
46+ "layout(location=2) in vec4 color0;\n"
47+ "\n"
48+ "uniform mat4 mvp;\n"
49+ "\n"
50+ "out vec2 uv;\n"
51+ "out vec4 colour;\n"
52+ "\n"
53+ "void main(void)\n"
54+ "{\n"
55+ " gl_Position = mvp * vec4(position, 1.0);\n"
56+ " uv = texcoord0;\n"
57+ " colour = color0;\n"
58+ "}\n";
59+static const char* fsdr = "#version 330\n"
60+ "in vec2 uv;\n"
61+ "in vec4 colour;\n"
62+ "\n"
63+ "uniform sampler2D tex;\n"
64+ "\n"
65+ "out vec4 frag_colour;\n"
66+ "\n"
67+ "void main(void)\n"
68+ "{\n"
69+ " frag_colour = texture(tex, uv) * colour;\n"
70+ " if (frag_colour.a < 0.5) discard;\n"
71+ "}\n";
72
73 static void create_pipelines(void)
74 {
+114,
-49
1@@ -14,6 +14,7 @@ static const i32 face_offsets[6][3] = {
2 { 0, 0, -1 }, { 0, 0, 1 }, { -1, 0, 0 }, { 1, 0, 0 }, { 0, -1, 0 }, { 0, 1, 0 }
3 };
4
5+/* add a face to the mesh */
6 static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count, u32* index_count, const BlockFace* face,
7 i32 x, i32 y, i32 z)
8 {
9@@ -41,67 +42,131 @@ static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count, u32* i
10 indices[(*index_count)++] = (u16)(base + 3);
11 }
12
13-void chunk_create(Chunk* chunk)
14+void chunk_clear(Chunk* chunk)
15+{
16+ i32 section;
17+
18+ if (!chunk)
19+ return;
20+
21+ for (section = 0; section < CHUNK_SECTION_COUNT; section++) {
22+ chunk_section_mesh_destroy(&chunk->sections[section]);
23+ memset(&chunk->sections[section], 0, sizeof(chunk->sections[section]));
24+ }
25+
26+ chunk->section_mask = 0;
27+}
28+
29+void chunk_remove(Chunk* chunk)
30+{
31+ if (!chunk)
32+ return;
33+
34+ chunk_clear(chunk);
35+ memset(chunk, 0, sizeof(*chunk));
36+}
37+
38+void chunk_section_create(ChunkSection* section)
39 {
40 i32 x;
41 i32 y;
42 i32 z;
43
44- if (!chunk)
45+ if (!section)
46 return;
47
48- memset(chunk, 0, sizeof(*chunk));
49- for (z = 0; z < CHUNK_DEPTH; z++) {
50- for (x = 0; x < CHUNK_WIDTH; x++) {
51- for (y = 0; y < CHUNK_HEIGHT - 5; y++)
52- chunk_set(chunk, x, y, z, BLOCK_STONE);
53+ memset(section, 0, sizeof(*section));
54+ for (z = 0; z < CHUNK_SECTION_DEPTH; z++) {
55+ for (x = 0; x < CHUNK_SECTION_WIDTH; x++) {
56+ for (y = 0; y < CHUNK_SECTION_HEIGHT - 5; y++)
57+ chunk_section_set(section, x, y, z, BLOCK_STONE);
58
59- chunk_set(chunk, x, CHUNK_HEIGHT - 5, z, BLOCK_DIRT);
60+ chunk_section_set(section, x, CHUNK_SECTION_HEIGHT - 5, z, BLOCK_DIRT);
61 }
62 }
63
64- chunk_set(chunk, 6, 12, 8, BLOCK_BRICK);
65- chunk_set(chunk, 8, 12, 8, BLOCK_BRICK);
66- chunk_set(chunk, 7, 13, 8, BLOCK_BRICK);
67- chunk_set(chunk, 7, 14, 8, BLOCK_BRICK);
68- chunk_set(chunk, 7, 15, 8, BLOCK_STONE);
69+ /* dih */
70+ chunk_section_set(section, 6, 12, 8, BLOCK_BRICK);
71+ chunk_section_set(section, 8, 12, 8, BLOCK_BRICK);
72+ chunk_section_set(section, 7, 13, 8, BLOCK_BRICK);
73+ chunk_section_set(section, 7, 14, 8, BLOCK_BRICK);
74+ chunk_section_set(section, 7, 15, 8, BLOCK_STONE);
75 }
76
77-BlockType chunk_get(const Chunk* chunk, i32 x, i32 y, i32 z)
78+i8 chunk_section_decode(ChunkSection* section, const u8* data, size_t size)
79 {
80- if (!chunk)
81+ i32 index;
82+ i32 x;
83+ i32 y;
84+ i32 z;
85+ u16 state;
86+
87+ if (!section || !data)
88+ return 0;
89+
90+ if (size < 8192)
91+ return 0;
92+
93+ chunk_section_mesh_destroy(section);
94+ memset(section, 0, sizeof(*section));
95+
96+ for (index = 0; index < 4096; index++) {
97+ /* combine first two bytes to get block state (l operation
98+ is little endian) */
99+ state = (u16)data[index * 2] | ((u16)data[index * 2 + 1] << 8);
100+
101+ /* convert index->3D */
102+ x = index & 15;
103+ z = (index >> 4) & 15;
104+ y = (index >> 8) & 15;
105+
106+ chunk_section_set(section, x, y, z, state);
107+ }
108+
109+ /* create mesh for section */
110+ if (!chunk_section_mesh_create(section)) {
111+ mg_log(LOG_ERR, "Failed to create server section mesh");
112+ return 0;
113+ }
114+
115+ return 1;
116+}
117+
118+BlockType chunk_section_get(const ChunkSection* section, i32 x, i32 y, i32 z)
119+{
120+ if (!section)
121 return BLOCK_AIR;
122
123- if (x < 0 || x >= CHUNK_WIDTH)
124+ if (x < 0 || x >= CHUNK_SECTION_WIDTH)
125 return BLOCK_AIR;
126
127- if (y < 0 || y >= CHUNK_HEIGHT)
128+ if (y < 0 || y >= CHUNK_SECTION_HEIGHT)
129 return BLOCK_AIR;
130
131- if (z < 0 || z >= CHUNK_DEPTH)
132+ if (z < 0 || z >= CHUNK_SECTION_DEPTH)
133 return BLOCK_AIR;
134
135- return chunk->blocks[x][y][z];
136+ return section->blocks[x][y][z];
137 }
138
139-void chunk_set(Chunk* chunk, i32 x, i32 y, i32 z, BlockType block)
140+void chunk_section_set(ChunkSection* section, i32 x, i32 y, i32 z, BlockType block)
141 {
142- if (!chunk)
143+ if (!section)
144 return;
145
146- if (x < 0 || x >= CHUNK_WIDTH)
147+ if (x < 0 || x >= CHUNK_SECTION_WIDTH)
148 return;
149
150- if (y < 0 || y >= CHUNK_HEIGHT)
151+ if (y < 0 || y >= CHUNK_SECTION_HEIGHT)
152 return;
153
154- if (z < 0 || z >= CHUNK_DEPTH)
155+ if (z < 0 || z >= CHUNK_SECTION_DEPTH)
156 return;
157
158- chunk->blocks[x][y][z] = block;
159+ section->blocks[x][y][z] = block;
160 }
161
162-i8 chunk_mesh_create(Chunk* chunk)
163+i8 chunk_section_mesh_create(ChunkSection* section)
164 {
165 MGVertex* vertices[BLOCK_TEXTURE_COUNT];
166 u16* indices[BLOCK_TEXTURE_COUNT];
167@@ -119,7 +184,7 @@ i8 chunk_mesh_create(Chunk* chunk)
168 i32 y;
169 i32 z;
170
171- if (!chunk)
172+ if (!section)
173 return 0;
174
175 memset(vertices, 0, sizeof(vertices));
176@@ -129,13 +194,13 @@ i8 chunk_mesh_create(Chunk* chunk)
177 memset(index_counts, 0, sizeof(index_counts));
178
179 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++)
180- chunk->meshes[texture] = MGMESH_INVALID;
181+ section->meshes[texture] = MGMESH_INVALID;
182
183 /* count visible faces belonging to each block type */
184- for (z = 0; z < CHUNK_DEPTH; z++) {
185- for (y = 0; y < CHUNK_HEIGHT; y++) {
186- for (x = 0; x < CHUNK_WIDTH; x++) {
187- block = chunk_get(chunk, x, y, z);
188+ for (z = 0; z < CHUNK_SECTION_DEPTH; z++) {
189+ for (y = 0; y < CHUNK_SECTION_HEIGHT; y++) {
190+ for (x = 0; x < CHUNK_SECTION_WIDTH; x++) {
191+ block = chunk_section_get(section, x, y, z);
192
193 if (block == BLOCK_AIR)
194 continue;
195@@ -146,9 +211,9 @@ i8 chunk_mesh_create(Chunk* chunk)
196 continue;
197 if (faces[face].cullface >= 0) {
198 texture = faces[face].cullface;
199- neighbour = chunk_get(chunk, x + face_offsets[texture][0],
200- y + face_offsets[texture][1],
201- z + face_offsets[texture][2]);
202+ neighbour = chunk_section_get(section, x + face_offsets[texture][0],
203+ y + face_offsets[texture][1],
204+ z + face_offsets[texture][2]);
205 if (neighbour != BLOCK_AIR && block_model_occludes(neighbour))
206 continue;
207 }
208@@ -177,10 +242,10 @@ i8 chunk_mesh_create(Chunk* chunk)
209 }
210
211 /* omit culled model faces hidden by a full cube */
212- for (z = 0; z < CHUNK_DEPTH; z++) {
213- for (y = 0; y < CHUNK_HEIGHT; y++) {
214- for (x = 0; x < CHUNK_WIDTH; x++) {
215- block = chunk_get(chunk, x, y, z);
216+ for (z = 0; z < CHUNK_SECTION_DEPTH; z++) {
217+ for (y = 0; y < CHUNK_SECTION_HEIGHT; y++) {
218+ for (x = 0; x < CHUNK_SECTION_WIDTH; x++) {
219+ block = chunk_section_get(section, x, y, z);
220
221 if (block == BLOCK_AIR)
222 continue;
223@@ -192,9 +257,9 @@ i8 chunk_mesh_create(Chunk* chunk)
224 continue;
225 if (faces[face].cullface >= 0) {
226 texture = faces[face].cullface;
227- neighbour = chunk_get(chunk, x + face_offsets[texture][0],
228- y + face_offsets[texture][1],
229- z + face_offsets[texture][2]);
230+ neighbour = chunk_section_get(section, x + face_offsets[texture][0],
231+ y + face_offsets[texture][1],
232+ z + face_offsets[texture][2]);
233 if (neighbour != BLOCK_AIR && block_model_occludes(neighbour))
234 continue;
235 }
236@@ -217,9 +282,9 @@ i8 chunk_mesh_create(Chunk* chunk)
237 desc.idc = indices[texture];
238 desc.idcc = index_counts[texture];
239
240- chunk->meshes[texture] = renderer_mesh_create(&desc);
241+ section->meshes[texture] = renderer_mesh_create(&desc);
242
243- if (chunk->meshes[texture] == MGMESH_INVALID) {
244+ if (section->meshes[texture] == MGMESH_INVALID) {
245 mg_log(LOG_ERR, "Failed to upload chunk mesh");
246 goto fail;
247 }
248@@ -238,19 +303,19 @@ fail:
249 free(vertices[texture]);
250 }
251
252- chunk_mesh_destroy(chunk);
253+ chunk_section_mesh_destroy(section);
254 return 0;
255 }
256
257-void chunk_mesh_destroy(Chunk* chunk)
258+void chunk_section_mesh_destroy(ChunkSection* section)
259 {
260 i32 texture;
261
262- if (!chunk)
263+ if (!section)
264 return;
265
266 for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
267- renderer_mesh_destroy(chunk->meshes[texture]);
268- chunk->meshes[texture] = MGMESH_INVALID;
269+ renderer_mesh_destroy(section->meshes[texture]);
270+ section->meshes[texture] = MGMESH_INVALID;
271 }
272 }