master uint/magnolia / source / net / mc_chunk.c
  1#include <stdlib.h>
  2#include <string.h>
  3
  4#include "util.h"
  5#include "net/mc_chunk.h"
  6#include "net/packet.h"
  7#include "world/chunk.h"
  8
  9#define MC_CHUNK_SECTION_BLOCK_BYTES 8192
 10#define MC_CHUNK_SECTION_LIGHT_BYTES 2048
 11#define MC_CHUNK_BIOME_BYTES 256
 12
 13static i8  mc_chunk_decode_bulk(MCChunkPacket* chunk, PacketReader* reader);
 14static i8  mc_chunk_decode_single(MCChunkPacket* chunk, PacketReader* reader);
 15static i32 mc_chunk_section_count(u16 mask);
 16
 17/* decode bulk chunk data from packet
 18   returns 1 on success, 0 on failure */
 19static i8 mc_chunk_decode_bulk(MCChunkPacket* chunk, PacketReader* reader)
 20{
 21	i32 update;
 22
 23	if (!packet_read_u8(reader, &chunk->skylight) || !packet_read_varint(reader, &chunk->update_count)) {
 24		mg_log(LOG_ERR, "Malformed Map Chunk Bulk packet");
 25		return 0;
 26	}
 27
 28	if (chunk->update_count <= 0 || chunk->update_count > 4096)
 29		return 0;
 30
 31	chunk->updates = malloc((size_t)chunk->update_count * sizeof(*chunk->updates));
 32	if (!chunk->updates)
 33		return 0;
 34
 35	/* read chunk updates */
 36	for (update = 0; update < chunk->update_count; update++) {
 37		if (!packet_read_i32(reader, &chunk->updates[update].x) ||
 38		    !packet_read_i32(reader, &chunk->updates[update].z) ||
 39		    !packet_read_u16(reader, &chunk->updates[update].mask)) {
 40			mg_log(LOG_ERR, "Malformed Map Chunk Bulk packet");
 41			mc_chunk_destroy(chunk);
 42			return 0;
 43		}
 44
 45		chunk->updates[update].replace = 1;
 46	}
 47
 48	chunk->bulk = 1;
 49	chunk->data = reader->data + reader->cursor;
 50	chunk->data_size = reader->size - reader->cursor;
 51	return 1;
 52}
 53
 54/* decode single chunk data from packet
 55   returns 1 on success, 0 on failure */
 56static i8 mc_chunk_decode_single(MCChunkPacket* chunk, PacketReader* reader)
 57{
 58	MCChunkUpdate* update;
 59	i32            declared_size;
 60	u8             ground_up;
 61
 62	update = &chunk->single_update;
 63
 64	/* read chunk coordinates, mask, size */
 65	if (!packet_read_i32(reader, &update->x) || !packet_read_i32(reader, &update->z) ||
 66	    !packet_read_u8(reader, &ground_up) || !packet_read_u16(reader, &update->mask) ||
 67	    !packet_read_varint(reader, &declared_size)) {
 68		mg_log(LOG_ERR, "Malformed Chunk Data packet");
 69		return 0;
 70	}
 71
 72	if (declared_size < 0 || (size_t)declared_size > reader->size - reader->cursor)
 73		return 0;
 74
 75	/* if ground_up set and mask is 0, set chunk for unload */
 76	if (ground_up && update->mask == 0) {
 77		chunk->unload = 1;
 78		chunk->unload_x = update->x;
 79		chunk->unload_z = update->z;
 80		return 1;
 81	}
 82
 83	update->replace = ground_up;
 84	chunk->updates = update;
 85	chunk->update_count = 1;
 86	chunk->data = reader->data + reader->cursor;
 87	chunk->data_size = declared_size;
 88	return 1;
 89}
 90
 91/* count number of chunk sections mask
 92   returns section count */
 93static i32 mc_chunk_section_count(u16 mask)
 94{
 95	i32 section;
 96	i32 count;
 97
 98	count = 0;
 99
100	for (section = 0; section < CHUNK_SECTION_COUNT; section++) {
101		if (mask & (1U << section)) /* section present in mask */
102			count++;
103	}
104
105	return count;
106}
107
108i8 mc_chunk_decode(MCChunkPacket* chunk, const MCPacket* packet)
109{
110	PacketReader reader;
111
112	if (!chunk || !packet)
113		return 0;
114
115	memset(chunk, 0, sizeof(*chunk));
116	reader = packet_reader_init(packet->data, packet->size);
117
118	switch (packet->id) {
119	case 0x21:
120		return mc_chunk_decode_single(chunk, &reader);
121	case 0x26:
122		return mc_chunk_decode_bulk(chunk, &reader);
123	}
124
125	return 0;
126}
127
128void mc_chunk_destroy(MCChunkPacket* chunk)
129{
130	if (!chunk)
131		return;
132
133	if (chunk->updates != &chunk->single_update)
134		free(chunk->updates);
135
136	memset(chunk, 0, sizeof(*chunk));
137}
138
139size_t mc_chunk_update_size(u16 mask, u8 skylight, i8 bulk)
140{
141	i32    sections;
142	size_t size;
143
144	sections = mc_chunk_section_count(mask);
145	size = sections * MC_CHUNK_SECTION_BLOCK_BYTES;
146	if (bulk) {
147		size += sections * MC_CHUNK_SECTION_LIGHT_BYTES;
148		size += MC_CHUNK_BIOME_BYTES;
149		if (skylight)
150			size += sections * MC_CHUNK_SECTION_LIGHT_BYTES;
151	}
152
153	return size;
154}