commit 8867659
uint
·
2026-08-05 01:04:54 +0000 UTC
parent f4c5c63
reorder functions, add comments above function impl
14 files changed,
+603,
-489
M
TODO
M
TODO
+2,
-1
1@@ -1,8 +1,9 @@
2 uint:
3 [ ] Clean up test_scene.h
4 [ ] Consolidate variable initaliasation methods
5-[ ] mc_proto, mg_random type cleanup (int->i8 on bool)
6+[ ] mc_*, mg_random type cleanup (int->i8 on bool)
7 [ ] Review `block`
8+[ ] Small3DLib inline?? check ANSI
9
10 [-] Face Hiding
11 [-] Chunk meshing
+4,
-0
1@@ -9,6 +9,8 @@
2 static Chunk* world_chunk_find(ClientWorld* world, i32 x, i32 z);
3 static Chunk* world_chunk_get(ClientWorld* world, i32 x, i32 z);
4
5+/* find chunk in world by coordinates
6+ returns NULL if not found */
7 static Chunk* world_chunk_find(ClientWorld* world, i32 x, i32 z)
8 {
9 i32 i;
10@@ -24,6 +26,8 @@ static Chunk* world_chunk_find(ClientWorld* world, i32 x, i32 z)
11 return NULL;
12 }
13
14+/* get chunk from world by coordinates
15+ returns NULL if not found and no free chunk available */
16 static Chunk* world_chunk_get(ClientWorld* world, i32 x, i32 z)
17 {
18 Chunk* chunk;
+9,
-1
1@@ -37,6 +37,7 @@ static void handle_input(void);
2 static i8 handle_server_packets(void);
3 static void render_world(void);
4
5+/* create local fallback world */
6 static void create_local_world(void)
7 {
8 Chunk* chunk;
9@@ -62,6 +63,7 @@ static void create_local_world(void)
10 }
11 }
12
13+/* destroy game resources */
14 static void game_dies(void)
15 {
16 if (game.client)
17@@ -73,6 +75,7 @@ static void game_dies(void)
18 platform_shutdown(&game.ctx);
19 }
20
21+/* initialise game */
22 static void game_init(int argc, char** argv)
23 {
24 MCAccount account;
25@@ -121,6 +124,7 @@ static void game_init(int argc, char** argv)
26 create_local_world();
27 }
28
29+/* run main loop */
30 static void game_run(void)
31 {
32 while (platform_poll(&game.ctx)) {
33@@ -141,6 +145,7 @@ static void game_run(void)
34 }
35 }
36
37+/* handle decoded chunk packet */
38 static void handle_chunk_packet(const MCPacket* packet)
39 {
40 MCChunkPacket chunk;
41@@ -175,6 +180,7 @@ static void handle_chunk_packet(const MCPacket* packet)
42 mc_chunk_destroy(&chunk);
43 }
44
45+/* handle local input */
46 static void handle_input(void)
47 {
48 static u32 last;
49@@ -223,6 +229,8 @@ static void handle_input(void)
50 camera_rotate(&game.cam, mx * sensitivity, -my * sensitivity);
51 }
52
53+/* handle pending server packets
54+ returns 1 to continue, 0 to stop */
55 static i8 handle_server_packets(void)
56 {
57 MCPacket packet;
58@@ -238,7 +246,6 @@ static i8 handle_server_packets(void)
59 if (poll_result <= 0)
60 break;
61
62- printf("packet 0x%02x size %lu\n", packet.id, packet.size);
63 switch (packet.id) {
64 case 0x00:
65 if (!mc_client_write(game.client, 0x00, packet.data, packet.size))
66@@ -268,6 +275,7 @@ static i8 handle_server_packets(void)
67 return 1;
68 }
69
70+/* render loaded world */
71 static void render_world(void)
72 {
73 MGMat4 proj;
+13,
-0
1@@ -12,6 +12,13 @@
2 #define MC_JOIN_PORT "443"
3 #define MC_JOIN_PATH "/session/minecraft/join"
4
5+static long http_status(const MCBuffer* response);
6+static int json_string(MCBuffer* buffer, const char* value);
7+static int tls_read_response(struct tls* tls, MCBuffer* response);
8+static int tls_write_all(struct tls* tls, const void* data, size_t size);
9+
10+/* write escaped JSON string
11+ returns 1 on success, 0 on failure */
12 static int json_string(MCBuffer* buffer, const char* value)
13 {
14 const unsigned char* p = (const unsigned char*)value;
15@@ -72,6 +79,8 @@ static int json_string(MCBuffer* buffer, const char* value)
16 return bufwrite_u8(buffer, '"');
17 }
18
19+/* write all bytes to TLS connection
20+ returns 1 on success, 0 on failure */
21 static int tls_write_all(struct tls* tls, const void* data, size_t size)
22 {
23 const u8* bytes = (const u8*)data;
24@@ -88,6 +97,8 @@ static int tls_write_all(struct tls* tls, const void* data, size_t size)
25 return 1;
26 }
27
28+/* read TLS response
29+ returns 1 on success, 0 on failure */
30 static int tls_read_response(struct tls* tls, MCBuffer* response)
31 {
32 u8 data[2048];
33@@ -107,6 +118,8 @@ static int tls_read_response(struct tls* tls, MCBuffer* response)
34 }
35 }
36
37+/* parse HTTP status from response
38+ returns status code, or 0 on failure */
39 static long http_status(const MCBuffer* response)
40 {
41 long status;
+42,
-39
1@@ -10,47 +10,12 @@
2 #define MC_CHUNK_SECTION_LIGHT_BYTES 2048
3 #define MC_CHUNK_BIOME_BYTES 256
4
5-static i8 mc_chunk_decode_single(MCChunkPacket* chunk, PacketReader* reader);
6 static i8 mc_chunk_decode_bulk(MCChunkPacket* chunk, PacketReader* reader);
7+static i8 mc_chunk_decode_single(MCChunkPacket* chunk, PacketReader* reader);
8 static i32 mc_chunk_section_count(u16 mask);
9
10-/* decode single chunk data from packet */
11-static i8 mc_chunk_decode_single(MCChunkPacket* chunk, PacketReader* reader)
12-{
13- MCChunkUpdate* update;
14- i32 declared_size;
15- u8 ground_up;
16-
17- update = &chunk->single_update;
18-
19- /* read chunk coordinates, mask, size */
20- if (!packet_read_i32(reader, &update->x) || !packet_read_i32(reader, &update->z) ||
21- !packet_read_u8(reader, &ground_up) || !packet_read_u16(reader, &update->mask) ||
22- !packet_read_varint(reader, &declared_size)) {
23- mg_log(LOG_ERR, "Malformed Chunk Data packet");
24- return 0;
25- }
26-
27- if (declared_size < 0 || (size_t)declared_size > reader->size - reader->cursor)
28- return 0;
29-
30- /* if ground_up set and mask is 0, set chunk for unload */
31- if (ground_up && update->mask == 0) {
32- chunk->unload = 1;
33- chunk->unload_x = update->x;
34- chunk->unload_z = update->z;
35- return 1;
36- }
37-
38- update->replace = ground_up;
39- chunk->updates = update;
40- chunk->update_count = 1;
41- chunk->data = reader->data + reader->cursor;
42- chunk->data_size = declared_size;
43- return 1;
44-}
45-
46-/* decode bulk chunk data from packet */
47+/* decode bulk chunk data from packet
48+ returns 1 on success, 0 on failure */
49 static i8 mc_chunk_decode_bulk(MCChunkPacket* chunk, PacketReader* reader)
50 {
51 i32 update;
52@@ -86,7 +51,45 @@ static i8 mc_chunk_decode_bulk(MCChunkPacket* chunk, PacketReader* reader)
53 return 1;
54 }
55
56-/* count number of chunk sections mask */
57+/* decode single chunk data from packet
58+ returns 1 on success, 0 on failure */
59+static i8 mc_chunk_decode_single(MCChunkPacket* chunk, PacketReader* reader)
60+{
61+ MCChunkUpdate* update;
62+ i32 declared_size;
63+ u8 ground_up;
64+
65+ update = &chunk->single_update;
66+
67+ /* read chunk coordinates, mask, size */
68+ if (!packet_read_i32(reader, &update->x) || !packet_read_i32(reader, &update->z) ||
69+ !packet_read_u8(reader, &ground_up) || !packet_read_u16(reader, &update->mask) ||
70+ !packet_read_varint(reader, &declared_size)) {
71+ mg_log(LOG_ERR, "Malformed Chunk Data packet");
72+ return 0;
73+ }
74+
75+ if (declared_size < 0 || (size_t)declared_size > reader->size - reader->cursor)
76+ return 0;
77+
78+ /* if ground_up set and mask is 0, set chunk for unload */
79+ if (ground_up && update->mask == 0) {
80+ chunk->unload = 1;
81+ chunk->unload_x = update->x;
82+ chunk->unload_z = update->z;
83+ return 1;
84+ }
85+
86+ update->replace = ground_up;
87+ chunk->updates = update;
88+ chunk->update_count = 1;
89+ chunk->data = reader->data + reader->cursor;
90+ chunk->data_size = declared_size;
91+ return 1;
92+}
93+
94+/* count number of chunk sections mask
95+ returns section count */
96 static i32 mc_chunk_section_count(u16 mask)
97 {
98 i32 section;
+247,
-220
1@@ -18,7 +18,251 @@
2 #define MSG_NOSIGNAL 0
3 #endif
4
5-/* parse into host and port + validate */
6+static int handle_encryption(MCClient* client, const MCAccount* account, MCPacket* packet, char* error,
7+ size_t error_size);
8+static int login_loop(MCClient* client, const MCAccount* account, char* error, size_t error_size);
9+static int send_handshake(MCClient* client);
10+static int send_login_start(MCClient* client);
11+static int socket_read(MCClient* client, u8* data, size_t size);
12+static int socket_read_varint(MCClient* client, i32* value);
13+static int socket_write(MCClient* client, const u8* data, size_t size);
14+static int split_address(const char* address, char* host, size_t host_size, u16* port, char* error, size_t error_size);
15+static int tcp_connect(MCClient* client, char* error, size_t error_size);
16+
17+/* handle server encryption request
18+ returns 1 on success, 0 on failure */
19+static int handle_encryption(MCClient* client, const MCAccount* account, MCPacket* packet, char* error,
20+ size_t error_size)
21+{
22+ MCBuffer input = { packet->data, packet->size, packet->size, 0 };
23+ MCBuffer response;
24+ char server_id[256];
25+ const u8* public_key;
26+ const u8* verify_token;
27+ size_t public_key_size;
28+ size_t verify_token_size;
29+ u8 shared_secret[16];
30+ u8* encrypted_secret = NULL;
31+ u8* encrypted_token = NULL;
32+ size_t encrypted_secret_size = 0;
33+ size_t encrypted_token_size = 0;
34+ int ok = 0;
35+
36+ /* parse the server encryption request */
37+ if (!bufread_string(&input, server_id, sizeof(server_id)) ||
38+ !bufread_bytes(&input, &public_key, &public_key_size) ||
39+ !bufread_bytes(&input, &verify_token, &verify_token_size)) {
40+ mc_set_error(MC_EC_MALFORMED_PACKET, error, error_size, "malformed encryption request");
41+ return 0;
42+ }
43+ /* create the AES shared secret for the connection */
44+ if (!mg_random_bytes(shared_secret, sizeof(shared_secret))) {
45+ mc_set_error(MC_EC_RANDOM, error, error_size, "could not obtain secure random bytes");
46+ return 0;
47+ }
48+ /* join the mojang session, encrypt the secret, verify token */
49+ if (!mc_join_server(account, server_id, shared_secret, public_key, public_key_size, error, error_size) ||
50+ !mc_rsa_encrypt(public_key, public_key_size, shared_secret, sizeof(shared_secret), &encrypted_secret,
51+ &encrypted_secret_size, error, error_size) ||
52+ !mc_rsa_encrypt(public_key, public_key_size, verify_token, verify_token_size, &encrypted_token,
53+ &encrypted_token_size, error, error_size))
54+ goto cleanup;
55+
56+ bufinit(&response);
57+ if (bufwrite_varint(&response, (i32)encrypted_secret_size) &&
58+ bufwrite(&response, encrypted_secret, encrypted_secret_size) &&
59+ bufwrite_varint(&response, (i32)encrypted_token_size) &&
60+ bufwrite(&response, encrypted_token, encrypted_token_size) &&
61+ mc_client_write(client, 0x01, response.data, response.size)) {
62+ /* enable encryption after sending the unencrypted response */
63+ mc_cipher_init(&client->read_cipher, shared_secret);
64+ mc_cipher_init(&client->write_cipher, shared_secret);
65+ client->encrypted = 1;
66+ ok = 1;
67+ }
68+ else {
69+ mc_set_error(MC_EC_SOCKET_WRITE, error, error_size, "could not send encryption response");
70+ }
71+ buffree(&response);
72+
73+cleanup:
74+ memset(shared_secret, 0, sizeof(shared_secret));
75+ free(encrypted_secret);
76+ free(encrypted_token);
77+ return ok;
78+}
79+
80+/* process login packets until play state
81+ returns 1 on success, 0 on failure */
82+static int login_loop(MCClient* client, const MCAccount* account, char* error, size_t error_size)
83+{
84+ /* login state packet ids for protocol 47:
85+ 0x00 disconnect
86+ 0x01 encryption request
87+ 0x02 login success
88+ 0x03 set compression
89+
90+ https://prismarinejs.github.io/minecraft-data/protocol/pc/1.8/#login.toClient.types */
91+ for (;;) {
92+ MCPacket packet;
93+ MCBuffer input;
94+ if (!mc_client_read(client, &packet, error, error_size))
95+ return 0;
96+ input.data = packet.data;
97+ input.size = packet.size;
98+ input.capacity = packet.size;
99+ input.cursor = 0;
100+ if (packet.id == 0x00) {
101+ char reason[1024];
102+ if (!bufread_string(&input, reason, sizeof(reason)))
103+ strcpy(reason, "malformed disconnect reason");
104+ mc_set_error(MC_EC_LOGIN_DISCONNECT, error, error_size, "server disconnected during login: %s",
105+ reason);
106+ mc_packet_free(&packet);
107+ return 0;
108+ }
109+ if (packet.id == 0x01) {
110+ int ok = handle_encryption(client, account, &packet, error, error_size);
111+ mc_packet_free(&packet);
112+ if (!ok)
113+ return 0;
114+ continue;
115+ }
116+ if (packet.id == 0x02) {
117+ char username[64];
118+ if (!bufread_string(&input, client->uuid, sizeof(client->uuid)) ||
119+ !bufread_string(&input, username, sizeof(username))) {
120+ mc_set_error(MC_EC_MALFORMED_PACKET, error, error_size,
121+ "malformed login success packet");
122+ mc_packet_free(&packet);
123+ return 0;
124+ }
125+ printf("login success: uuid=%s username=%s\n", client->uuid, username);
126+ mc_packet_free(&packet);
127+ return 1;
128+ }
129+ if (packet.id == 0x03) {
130+ i32 threshold;
131+ if (!bufread_varint(&input, &threshold) || threshold < 0) {
132+ mc_set_error(MC_EC_INVALID_COMPRESSED_PACKET, error, error_size,
133+ "invalid compression threshold");
134+ mc_packet_free(&packet);
135+ return 0;
136+ }
137+ client->compression_threshold = threshold;
138+ printf("compression enabled: threshold=%d\n", threshold);
139+ mc_packet_free(&packet);
140+ continue;
141+ }
142+ mc_set_error(MC_EC_UNEXPECTED_LOGIN_PACKET, error, error_size, "unexpected login packet 0x%02x",
143+ packet.id);
144+ mc_packet_free(&packet);
145+ return 0;
146+ }
147+}
148+
149+/* send handshake packet
150+ returns 1 on success, 0 on failure */
151+static int send_handshake(MCClient* client)
152+{
153+ MCBuffer payload;
154+ int ok;
155+ bufinit(&payload);
156+ /* handshake to next state 2 switches the connection into login state */
157+ ok = bufwrite_varint(&payload, MC_PROTOCOL_1_8_9) && bufwrite_string(&payload, client->host) &&
158+ bufwrite_u16(&payload, client->port) && bufwrite_varint(&payload, 2) &&
159+ mc_client_write(client, 0x00, payload.data, payload.size);
160+ buffree(&payload);
161+ return ok;
162+}
163+
164+/* send login start packet
165+ returns 1 on success, 0 on failure */
166+static int send_login_start(MCClient* client)
167+{
168+ MCBuffer payload;
169+ int ok;
170+ bufinit(&payload);
171+ ok = bufwrite_string(&payload, client->username) && mc_client_write(client, 0x00, payload.data, payload.size);
172+ buffree(&payload);
173+ return ok;
174+}
175+
176+/* read bytes from socket
177+ returns 1 on success, 0 on failure */
178+static int socket_read(MCClient* client, u8* data, size_t size)
179+{
180+ size_t offset = 0;
181+ while (offset < size) {
182+ ssize_t count = recv(client->socket_fd, data + offset, size - offset, 0);
183+ if (count > 0) {
184+ offset += (size_t)count;
185+ }
186+ else if (count < 0 && errno == EINTR) {
187+ continue;
188+ }
189+ else {
190+ return 0;
191+ }
192+ }
193+ if (client->encrypted)
194+ mc_cipher_decrypt(&client->read_cipher, data, size);
195+ return 1;
196+}
197+
198+/* read VarInt from socket
199+ returns 1 on success, 0 on failure */
200+static int socket_read_varint(MCClient* client, i32* value)
201+{
202+ u32 result = 0;
203+ int shift;
204+ for (shift = 0; shift < 35; shift += 7) {
205+ u8 byte;
206+ if (!socket_read(client, &byte, 1))
207+ return 0;
208+ result |= (u32)(byte & 0x7f) << shift;
209+ if (!(byte & 0x80)) {
210+ *value = (i32)result;
211+ return 1;
212+ }
213+ }
214+ return 0;
215+}
216+
217+/* write bytes to socket
218+ returns 1 on success, 0 on failure */
219+static int socket_write(MCClient* client, const u8* data, size_t size)
220+{
221+ u8 encrypted[4096];
222+ size_t offset = 0;
223+
224+ while (offset < size) {
225+ size_t chunk = size - offset;
226+ size_t sent = 0;
227+ if (chunk > sizeof(encrypted))
228+ chunk = sizeof(encrypted);
229+ memcpy(encrypted, data + offset, chunk);
230+ if (client->encrypted)
231+ mc_cipher_encrypt(&client->write_cipher, encrypted, chunk);
232+ while (sent < chunk) {
233+ ssize_t count = send(client->socket_fd, encrypted + sent, chunk - sent, MSG_NOSIGNAL);
234+ if (count > 0) {
235+ sent += (size_t)count;
236+ }
237+ else if (count < 0 && errno == EINTR) {
238+ continue;
239+ }
240+ else {
241+ return 0;
242+ }
243+ }
244+ offset += chunk;
245+ }
246+ return 1;
247+}
248+
249+/* parse address into host and port
250+ returns 1 on success, 0 on failure */
251 static int split_address(const char* address, char* host, size_t host_size, u16* port, char* error, size_t error_size)
252 {
253 const char* colon = strrchr(address, ':');
254@@ -60,7 +304,8 @@ static int split_address(const char* address, char* host, size_t host_size, u16*
255 return 1;
256 }
257
258-/* connect to the configured server address */
259+/* connect to the configured server address
260+ returns 1 on success, 0 on failure */
261 static int tcp_connect(MCClient* client, char* error, size_t error_size)
262 {
263 struct addrinfo hints;
264@@ -100,73 +345,6 @@ static int tcp_connect(MCClient* client, char* error, size_t error_size)
265 return 1;
266 }
267
268-static int socket_write(MCClient* client, const u8* data, size_t size)
269-{
270- u8 encrypted[4096];
271- size_t offset = 0;
272-
273- while (offset < size) {
274- size_t chunk = size - offset;
275- size_t sent = 0;
276- if (chunk > sizeof(encrypted))
277- chunk = sizeof(encrypted);
278- memcpy(encrypted, data + offset, chunk);
279- if (client->encrypted)
280- mc_cipher_encrypt(&client->write_cipher, encrypted, chunk);
281- while (sent < chunk) {
282- ssize_t count = send(client->socket_fd, encrypted + sent, chunk - sent, MSG_NOSIGNAL);
283- if (count > 0) {
284- sent += (size_t)count;
285- }
286- else if (count < 0 && errno == EINTR) {
287- continue;
288- }
289- else {
290- return 0;
291- }
292- }
293- offset += chunk;
294- }
295- return 1;
296-}
297-
298-static int socket_read(MCClient* client, u8* data, size_t size)
299-{
300- size_t offset = 0;
301- while (offset < size) {
302- ssize_t count = recv(client->socket_fd, data + offset, size - offset, 0);
303- if (count > 0) {
304- offset += (size_t)count;
305- }
306- else if (count < 0 && errno == EINTR) {
307- continue;
308- }
309- else {
310- return 0;
311- }
312- }
313- if (client->encrypted)
314- mc_cipher_decrypt(&client->read_cipher, data, size);
315- return 1;
316-}
317-
318-static int socket_read_varint(MCClient* client, i32* value)
319-{
320- u32 result = 0;
321- int shift;
322- for (shift = 0; shift < 35; shift += 7) {
323- u8 byte;
324- if (!socket_read(client, &byte, 1))
325- return 0;
326- result |= (u32)(byte & 0x7f) << shift;
327- if (!(byte & 0x80)) {
328- *value = (i32)result;
329- return 1;
330- }
331- }
332- return 0;
333-}
334-
335 int mc_client_write(MCClient* client, i32 packet_id, const void* data, size_t size)
336 {
337 MCBuffer body;
338@@ -316,157 +494,6 @@ void mc_packet_free(MCPacket* packet)
339 memset(packet, 0, sizeof(*packet));
340 }
341
342-static int send_handshake(MCClient* client)
343-{
344- MCBuffer payload;
345- int ok;
346- bufinit(&payload);
347- /* handshake to next state 2 switches the connection into login state */
348- ok = bufwrite_varint(&payload, MC_PROTOCOL_1_8_9) && bufwrite_string(&payload, client->host) &&
349- bufwrite_u16(&payload, client->port) && bufwrite_varint(&payload, 2) &&
350- mc_client_write(client, 0x00, payload.data, payload.size);
351- buffree(&payload);
352- return ok;
353-}
354-
355-static int send_login_start(MCClient* client)
356-{
357- MCBuffer payload;
358- int ok;
359- bufinit(&payload);
360- ok = bufwrite_string(&payload, client->username) && mc_client_write(client, 0x00, payload.data, payload.size);
361- buffree(&payload);
362- return ok;
363-}
364-
365-static int handle_encryption(MCClient* client, const MCAccount* account, MCPacket* packet, char* error,
366- size_t error_size)
367-{
368- MCBuffer input = { packet->data, packet->size, packet->size, 0 };
369- MCBuffer response;
370- char server_id[256];
371- const u8* public_key;
372- const u8* verify_token;
373- size_t public_key_size;
374- size_t verify_token_size;
375- u8 shared_secret[16];
376- u8* encrypted_secret = NULL;
377- u8* encrypted_token = NULL;
378- size_t encrypted_secret_size = 0;
379- size_t encrypted_token_size = 0;
380- int ok = 0;
381-
382- /* parse the server encryption request */
383- if (!bufread_string(&input, server_id, sizeof(server_id)) ||
384- !bufread_bytes(&input, &public_key, &public_key_size) ||
385- !bufread_bytes(&input, &verify_token, &verify_token_size)) {
386- mc_set_error(MC_EC_MALFORMED_PACKET, error, error_size, "malformed encryption request");
387- return 0;
388- }
389- /* create the AES shared secret for the connection */
390- if (!mg_random_bytes(shared_secret, sizeof(shared_secret))) {
391- mc_set_error(MC_EC_RANDOM, error, error_size, "could not obtain secure random bytes");
392- return 0;
393- }
394- /* join the mojang session, encrypt the secret, verify token */
395- if (!mc_join_server(account, server_id, shared_secret, public_key, public_key_size, error, error_size) ||
396- !mc_rsa_encrypt(public_key, public_key_size, shared_secret, sizeof(shared_secret), &encrypted_secret,
397- &encrypted_secret_size, error, error_size) ||
398- !mc_rsa_encrypt(public_key, public_key_size, verify_token, verify_token_size, &encrypted_token,
399- &encrypted_token_size, error, error_size))
400- goto cleanup;
401-
402- bufinit(&response);
403- if (bufwrite_varint(&response, (i32)encrypted_secret_size) &&
404- bufwrite(&response, encrypted_secret, encrypted_secret_size) &&
405- bufwrite_varint(&response, (i32)encrypted_token_size) &&
406- bufwrite(&response, encrypted_token, encrypted_token_size) &&
407- mc_client_write(client, 0x01, response.data, response.size)) {
408- /* enable encryption after sending the unencrypted response */
409- mc_cipher_init(&client->read_cipher, shared_secret);
410- mc_cipher_init(&client->write_cipher, shared_secret);
411- client->encrypted = 1;
412- ok = 1;
413- }
414- else {
415- mc_set_error(MC_EC_SOCKET_WRITE, error, error_size, "could not send encryption response");
416- }
417- buffree(&response);
418-
419-cleanup:
420- memset(shared_secret, 0, sizeof(shared_secret));
421- free(encrypted_secret);
422- free(encrypted_token);
423- return ok;
424-}
425-
426-/* process the login packets until we enter play state */
427-static int login_loop(MCClient* client, const MCAccount* account, char* error, size_t error_size)
428-{
429- /* login state packet ids for protocol 47:
430- 0x00 disconnect
431- 0x01 encryption request
432- 0x02 login success
433- 0x03 set compression
434-
435- https://prismarinejs.github.io/minecraft-data/protocol/pc/1.8/#login.toClient.types */
436- for (;;) {
437- MCPacket packet;
438- MCBuffer input;
439- if (!mc_client_read(client, &packet, error, error_size))
440- return 0;
441- input.data = packet.data;
442- input.size = packet.size;
443- input.capacity = packet.size;
444- input.cursor = 0;
445- if (packet.id == 0x00) {
446- char reason[1024];
447- if (!bufread_string(&input, reason, sizeof(reason)))
448- strcpy(reason, "malformed disconnect reason");
449- mc_set_error(MC_EC_LOGIN_DISCONNECT, error, error_size, "server disconnected during login: %s",
450- reason);
451- mc_packet_free(&packet);
452- return 0;
453- }
454- if (packet.id == 0x01) {
455- int ok = handle_encryption(client, account, &packet, error, error_size);
456- mc_packet_free(&packet);
457- if (!ok)
458- return 0;
459- continue;
460- }
461- if (packet.id == 0x02) {
462- char username[64];
463- if (!bufread_string(&input, client->uuid, sizeof(client->uuid)) ||
464- !bufread_string(&input, username, sizeof(username))) {
465- mc_set_error(MC_EC_MALFORMED_PACKET, error, error_size,
466- "malformed login success packet");
467- mc_packet_free(&packet);
468- return 0;
469- }
470- printf("login success: uuid=%s username=%s\n", client->uuid, username);
471- mc_packet_free(&packet);
472- return 1;
473- }
474- if (packet.id == 0x03) {
475- i32 threshold;
476- if (!bufread_varint(&input, &threshold) || threshold < 0) {
477- mc_set_error(MC_EC_INVALID_COMPRESSED_PACKET, error, error_size,
478- "invalid compression threshold");
479- mc_packet_free(&packet);
480- return 0;
481- }
482- client->compression_threshold = threshold;
483- printf("compression enabled: threshold=%d\n", threshold);
484- mc_packet_free(&packet);
485- continue;
486- }
487- mc_set_error(MC_EC_UNEXPECTED_LOGIN_PACKET, error, error_size, "unexpected login packet 0x%02x",
488- packet.id);
489- mc_packet_free(&packet);
490- return 0;
491- }
492-}
493 /*create a client */
494 int mc_client_login(MCClient** out, const char* address, const MCAccount* account, char* error, size_t error_size)
495 {
+37,
-23
1@@ -11,6 +11,25 @@ typedef struct {
2 size_t cursor;
3 } DERReader;
4
5+static u8 cipher_byte(MCCipher* cipher);
6+static int der_length(DERReader* reader, size_t* length);
7+static int der_value(DERReader* reader, u8 tag, DERReader* value);
8+static int rsa_public_key(const u8* der, size_t der_size, br_rsa_public_key* key, char* error, size_t error_size);
9+static void twos_complement(u8* data, size_t size);
10+
11+/* generate next cipher stream byte
12+ returns cipher byte */
13+static u8 cipher_byte(MCCipher* cipher)
14+{
15+ u8 zero_iv[16] = { 0 };
16+ u8 block[16];
17+ memcpy(block, cipher->state, 16);
18+ br_aes_ct_cbcenc_run(&cipher->key, zero_iv, block, sizeof(block));
19+ return block[0];
20+}
21+
22+/* read DER length
23+ returns 1 on success, 0 on failure */
24 static int der_length(DERReader* reader, size_t* length)
25 {
26 u8 first;
27@@ -36,6 +55,8 @@ static int der_length(DERReader* reader, size_t* length)
28 return 1;
29 }
30
31+/* read DER value by tag
32+ returns 1 on success, 0 on failure */
33 static int der_value(DERReader* reader, u8 tag, DERReader* value)
34 {
35 size_t length;
36@@ -48,7 +69,8 @@ static int der_value(DERReader* reader, u8 tag, DERReader* value)
37 return 1;
38 }
39
40-/* parse the server's X.509 RSA pubkey for BearSSL */
41+/* parse the server's X.509 RSA pubkey for BearSSL
42+ returns 1 on success, 0 on failure */
43 static int rsa_public_key(const u8* der, size_t der_size, br_rsa_public_key* key, char* error, size_t error_size)
44 {
45 DERReader root = { der, der_size, 0 };
46@@ -84,19 +106,24 @@ static int rsa_public_key(const u8* der, size_t der_size, br_rsa_public_key* key
47 return key->nlen >= 64 && key->elen > 0;
48 }
49
50-void mc_cipher_init(MCCipher* cipher, const u8 key[16])
51+/* convert bytes to twos complement in place */
52+static void twos_complement(u8* data, size_t size)
53 {
54- br_aes_ct_cbcenc_init(&cipher->key, key, 16);
55- memcpy(cipher->state, key, 16);
56+ size_t i = size;
57+ unsigned carry = 1;
58+ while (i > 0) {
59+ unsigned value;
60+ --i;
61+ value = (unsigned)(data[i] ^ 0xff) + carry;
62+ data[i] = (u8)value;
63+ carry = value >> 8;
64+ }
65 }
66
67-static u8 cipher_byte(MCCipher* cipher)
68+void mc_cipher_init(MCCipher* cipher, const u8 key[16])
69 {
70- u8 zero_iv[16] = { 0 };
71- u8 block[16];
72- memcpy(block, cipher->state, 16);
73- br_aes_ct_cbcenc_run(&cipher->key, zero_iv, block, sizeof(block));
74- return block[0];
75+ br_aes_ct_cbcenc_init(&cipher->key, key, 16);
76+ memcpy(cipher->state, key, 16);
77 }
78
79 void mc_cipher_encrypt(MCCipher* cipher, u8* data, size_t size)
80@@ -174,19 +201,6 @@ int mc_rsa_encrypt(const u8* public_key, size_t public_key_size, const u8* messa
81 return 1;
82 }
83
84-static void twos_complement(u8* data, size_t size)
85-{
86- size_t i = size;
87- unsigned carry = 1;
88- while (i > 0) {
89- unsigned value;
90- --i;
91- value = (unsigned)(data[i] ^ 0xff) + carry;
92- data[i] = (u8)value;
93- carry = value >> 8;
94- }
95-}
96-
97 void mc_server_hash(const char* server_id, const u8 shared_secret[16], const u8* public_key, size_t public_key_size,
98 char output[42])
99 {
+2,
-2
1@@ -2,10 +2,10 @@
2
3 #include "net/packet.h"
4
5-/* read a number of bytes from packet, copy them to output.
6- returns 1 on success, 0 on failure */
7 static i8 packet_read(PacketReader* reader, void* output, size_t size);
8
9+/* read a number of bytes from packet, copy them to output.
10+ returns 1 on success, 0 on failure */
11 static i8 packet_read(PacketReader* reader, void* output, size_t size)
12 {
13 if (reader->cursor > reader->size)
+1,
-1
1@@ -3,9 +3,9 @@
2 #include "util.h"
3 #include "platform/platform.h"
4
5-/* set the state of a logical key */
6 static void platform_key_set(MGContext* ctx, MGKey key, i32 down);
7
8+/* set the state of a logical key */
9 static void platform_key_set(MGContext* ctx, MGKey key, i32 down)
10 {
11 int i;
+29,
-9
1@@ -85,15 +85,17 @@ static void create_screen_texture(i32 width, i32 height);
2 /* destroy the SDL texture and software framebuffer */
3 static void destroy_screen_texture(void);
4
5-static ClipVertex transform_vertex(const MGMat4* mvp, const MGVertex* v);
6-static i8 triangle_outside(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c);
7-static i8 triangle_front_facing(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c);
8-static S3L_Vec4 clip_to_screen(const ClipVertex* v);
9-static u32 sample_texture(const RendererMesh* mesh, const RendererTexture* texture, const S3L_PixelInfo* pixel);
10-static u16 rgba_to_rgb565(u32 rgba);
11-static u32 rgb565_to_argb(u16 rgb);
12-static u32 pack_texture_pixel(u32 rgba);
13-
14+static ClipVertex transform_vertex(const MGMat4* mvp, const MGVertex* v);
15+static i8 triangle_outside(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c);
16+static i8 triangle_front_facing(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c);
17+static S3L_Vec4 clip_to_screen(const ClipVertex* v);
18+static u32 sample_texture(const RendererMesh* mesh, const RendererTexture* texture, const S3L_PixelInfo* pixel);
19+static u16 rgba_to_rgb565(u32 rgba);
20+static u32 rgb565_to_argb(u16 rgb);
21+static u32 pack_texture_pixel(u32 rgba);
22+static inline void small3dlib_pixel(S3L_PixelInfo* pixel);
23+
24+/* shade small3dlib pixel */
25 static inline void small3dlib_pixel(S3L_PixelInfo* pixel)
26 {
27 u32 index;
28@@ -120,6 +122,7 @@ static inline void small3dlib_pixel(S3L_PixelInfo* pixel)
29 ren.framebuffer[index] = (u16)sampled;
30 }
31
32+/* create or resize screen texture */
33 static void create_screen_texture(i32 width, i32 height)
34 {
35 u64 pixel_count;
36@@ -165,6 +168,7 @@ static void create_screen_texture(i32 width, i32 height)
37 S3L_resolutionY = (uint16_t)height;
38 }
39
40+/* destroy screen texture */
41 static void destroy_screen_texture(void)
42 {
43 #ifdef MG_PLATFORM_SDL
44@@ -180,6 +184,8 @@ static void destroy_screen_texture(void)
45 ren.height = 0;
46 }
47
48+/* transform vertex to clip space
49+ returns clip space vertex */
50 static ClipVertex transform_vertex(const MGMat4* mvp, const MGVertex* v)
51 {
52 ClipVertex r;
53@@ -196,6 +202,8 @@ static ClipVertex transform_vertex(const MGMat4* mvp, const MGVertex* v)
54 return r;
55 }
56
57+/* test whether triangle is outside clip volume
58+ returns 1 if outside, 0 otherwise */
59 static i8 triangle_outside(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c)
60 {
61 if (a->w <= 0.0001f || b->w <= 0.0001f || c->w <= 0.0001f)
62@@ -217,6 +225,8 @@ static i8 triangle_outside(const ClipVertex* a, const ClipVertex* b, const ClipV
63 return 0;
64 }
65
66+/* test whether triangle faces camera
67+ returns 1 if front facing, 0 otherwise */
68 static i8 triangle_front_facing(const ClipVertex* a, const ClipVertex* b, const ClipVertex* c)
69 {
70 float ax = a->x / a->w;
71@@ -230,6 +240,8 @@ static i8 triangle_front_facing(const ClipVertex* a, const ClipVertex* b, const
72 return area > 0.0f;
73 }
74
75+/* convert clip vertex to screen coordinates
76+ returns small3dlib screen vertex */
77 static S3L_Vec4 clip_to_screen(const ClipVertex* v)
78 {
79 S3L_Vec4 r;
80@@ -249,6 +261,8 @@ static S3L_Vec4 clip_to_screen(const ClipVertex* v)
81 return r;
82 }
83
84+/* sample texture for rasterized pixel
85+ returns packed sampled colour */
86 static u32 sample_texture(const RendererMesh* mesh, const RendererTexture* texture, const S3L_PixelInfo* pixel)
87 {
88 u32 sampled;
89@@ -307,6 +321,8 @@ static u32 sample_texture(const RendererMesh* mesh, const RendererTexture* textu
90 return (sampled & 0xff000000) | r | g | b;
91 }
92
93+/* convert RGBA8 colour to RGB565
94+ returns RGB565 colour */
95 static u16 rgba_to_rgb565(u32 rgba)
96 {
97 u32 r = (rgba >> 0) & 0xff;
98@@ -316,6 +332,8 @@ static u16 rgba_to_rgb565(u32 rgba)
99 return (u16)(((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3));
100 }
101
102+/* convert RGB565 colour to ARGB8
103+ returns ARGB8 colour */
104 static u32 rgb565_to_argb(u16 rgb)
105 {
106 u32 r = (rgb >> 11) & 31;
107@@ -329,6 +347,8 @@ static u32 rgb565_to_argb(u16 rgb)
108 return 0xff000000 | (r << 16) | (g << 8) | b;
109 }
110
111+/* pack texture pixel for renderer
112+ returns packed texture pixel */
113 static u32 pack_texture_pixel(u32 rgba)
114 {
115 return ((rgba >> 24) << 24) | rgba_to_rgb565(rgba);
+3,
-5
1@@ -37,13 +37,8 @@ typedef struct {
2 RendererTexture textures[MAX_TEXTURES];
3 } RendererState;
4
5-/* create the shader and render pipeline */
6 static void create_pipelines(void);
7-
8-/* create texture sampler */
9 static void create_sampler(void);
10-
11-/* logging function for passing to sokol */
12 static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user);
13
14 static RendererState ren;
15@@ -77,6 +72,7 @@ static const char* fsdr = "#version 330\n"
16 " if (frag_colour.a < 0.5) discard;\n"
17 "}\n";
18
19+/* create shader and render pipeline */
20 static void create_pipelines(void)
21 {
22 sg_shader_desc shader_desc = { 0 };
23@@ -126,6 +122,7 @@ static void create_pipelines(void)
24 mg_die(MG_EC_PIPELINE_CREATE, "Failed to create pipeline");
25 }
26
27+/* create texture sampler */
28 static void create_sampler(void)
29 {
30 sg_sampler_desc desc = { 0 };
31@@ -141,6 +138,7 @@ static void create_sampler(void)
32 mg_die(MG_EC_SAMPLER_CREATE, "Failed to create sampler");
33 }
34
35+/* logging function for passing to sokol */
36 static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user)
37 {
38 LogType type;
+1,
-0
1@@ -6,6 +6,7 @@
2
3 static void vlog(FILE* fd, const char* fmt, va_list ap);
4
5+/* write formatted log message to file */
6 static void vlog(FILE* fd, const char* fmt, va_list ap)
7 {
8 fprintf(fd, "Magnolia: ");
+212,
-187
1@@ -66,6 +66,112 @@ static char pack_path[PACK_SIZE];
2 static i32 texture_count;
3 static cJSON* block_registry;
4
5+static void block_load(BlockType block);
6+static i8 face_covers_cube(const ModelFace* face);
7+static float json_number(cJSON* array, i32 index);
8+static cJSON* json_load(const char* path);
9+static void model_cube(BlockModel* model, const char* texture);
10+static void model_face(BlockModel* model, cJSON* from, cJSON* to, cJSON* faces, i32 face);
11+static i8 model_load(const char* name, BlockModel* model, i32 depth);
12+static const char* model_resolve(const BlockModel* model, const char* name);
13+static void model_rotate(BlockModel* model, i32 first, cJSON* rotation);
14+static void model_texture(BlockModel* model, const char* name, const char* value);
15+static i32 texture_load(const char* name);
16+
17+/* check whether face covers cube side
18+ returns 1 if face covers side, 0 otherwise */
19+static i8 face_covers_cube(const ModelFace* face)
20+{
21+ /* only faces spanning their entire boundary plane can occlude neighbours */
22+ static const i32 face_axes[BLOCK_FACE_COUNT] = { 2, 2, 0, 0, 1, 1 };
23+ float low[3] = { 1, 1, 1 };
24+ float high[3] = { 0, 0, 0 };
25+ float value[3];
26+ i32 vertex;
27+ i32 axis;
28+ i32 plane;
29+
30+ for (vertex = 0; vertex < 4; vertex++) {
31+ value[0] = face->vertices[vertex].x;
32+ value[1] = face->vertices[vertex].y;
33+ value[2] = face->vertices[vertex].z;
34+ for (axis = 0; axis < 3; axis++) {
35+ if (value[axis] < low[axis])
36+ low[axis] = value[axis];
37+ if (value[axis] > high[axis])
38+ high[axis] = value[axis];
39+ }
40+ }
41+ plane = face_axes[face->cullface];
42+ for (axis = 0; axis < 3; axis++) {
43+ if (axis != plane && (low[axis] != 0 || high[axis] != 1))
44+ return 0;
45+ }
46+ return face->cullface & 1 ? high[plane] == 1 : low[plane] == 0;
47+}
48+
49+/* load block model and textures */
50+static void block_load(BlockType block)
51+{
52+ LoadedBlock* loaded;
53+ BlockModel model;
54+ cJSON* entry;
55+ cJSON* item;
56+ const char* name;
57+ const char* fallback;
58+ i32 face;
59+ i32 mask;
60+ char path[PATH_SIZE];
61+
62+ loaded = &blocks[block];
63+ loaded->loaded = 1;
64+ entry = cJSON_GetArrayItem(block_registry, block >> 4);
65+ item = cJSON_GetObjectItemCaseSensitive(entry, "blockState");
66+ name = cJSON_IsString(item) ? item->valuestring : NULL;
67+ item = cJSON_GetObjectItemCaseSensitive(entry, "texture");
68+ fallback = cJSON_IsString(item) ? item->valuestring : NULL;
69+ memset(&model, 0, sizeof(model));
70+ if (name) {
71+ snprintf(path, sizeof(path), "%s/blockstates/%s.json", pack_path, name);
72+ item = json_load(path);
73+ if (item) {
74+ entry = cJSON_GetObjectItemCaseSensitive(item, "variants");
75+ if (cJSON_GetArraySize(entry) > (block & 15))
76+ entry = cJSON_GetArrayItem(entry, block & 15);
77+ else
78+ entry = entry ? entry->child : NULL;
79+ if (cJSON_IsArray(entry))
80+ entry = cJSON_GetArrayItem(entry, 0);
81+ entry = cJSON_GetObjectItemCaseSensitive(entry, "model");
82+ if (cJSON_IsString(entry))
83+ model_load(entry->valuestring, &model, 0);
84+ cJSON_Delete(item);
85+ }
86+ }
87+ if (!model.face_count && fallback)
88+ model_cube(&model, fallback);
89+ if (!model.face_count)
90+ return;
91+ loaded->faces = malloc(model.face_count * sizeof(*loaded->faces));
92+ if (!loaded->faces)
93+ return;
94+ loaded->face_count = model.face_count;
95+ mask = 0;
96+ for (face = 0; face < model.face_count; face++) {
97+ name = model_resolve(&model, model.faces[face].texture);
98+ if (!name)
99+ name = fallback;
100+ loaded->faces[face].texture = name ? texture_load(name) : -1;
101+ loaded->faces[face].cullface = model.faces[face].cullface;
102+ memcpy(loaded->faces[face].vertices, model.faces[face].vertices, sizeof(model.faces[face].vertices));
103+ if (model.faces[face].cullface >= 0 && face_covers_cube(&model.faces[face]))
104+ mask |= 1 << model.faces[face].cullface;
105+ }
106+ loaded->occludes = mask == (1 << BLOCK_FACE_COUNT) - 1;
107+}
108+
109+/* load JSON file
110+ returns parsed JSON, or NULL on failure */
111 static cJSON* json_load(const char* path)
112 {
113 FILE* file;
114@@ -92,30 +198,83 @@ static cJSON* json_load(const char* path)
115 return json;
116 }
117
118-/* add or override model texture */
119-static void model_texture(BlockModel* model, const char* name, const char* value)
120+/* read normalized JSON number
121+ returns number, or 0 on failure */
122+static float json_number(cJSON* array, i32 index)
123 {
124- i32 i;
125+ cJSON* item = cJSON_GetArrayItem(array, index);
126+ return cJSON_IsNumber(item) ? (float)item->valuedouble / 16.0f : 0.0f;
127+}
128
129- for (i = 0; i < model->variable_count; i++) {
130- if (strcmp(model->variables[i].name, name) == 0)
131- break;
132+/* create cube model from texture */
133+static void model_cube(BlockModel* model, const char* texture)
134+{
135+ i32 face;
136+
137+ for (face = 0; face < BLOCK_FACE_COUNT; face++) {
138+ model->faces[face].cullface = face;
139+ memcpy(model->faces[face].vertices, face_vertices[face], sizeof(face_vertices[face]));
140+ snprintf(model->faces[face].texture, NAME_SIZE, "%s", texture);
141 }
142- if (i == VARIABLE_COUNT)
143- return;
144- if (i == model->variable_count)
145- model->variable_count++;
146- snprintf(model->variables[i].name, NAME_SIZE, "%s", name);
147- snprintf(model->variables[i].value, NAME_SIZE, "%s", value);
148+ model->face_count = BLOCK_FACE_COUNT;
149 }
150
151-static float json_number(cJSON* array, i32 index)
152+/* load inherited model data and apply overrides
153+ returns 1 on success, 0 on failure */
154+static i8 model_load(const char* name, BlockModel* model, i32 depth)
155 {
156- cJSON* item = cJSON_GetArrayItem(array, index);
157- return cJSON_IsNumber(item) ? (float)item->valuedouble / 16.0f : 0.0f;
158+ char path[PATH_SIZE];
159+ cJSON* json;
160+ cJSON* item;
161+ cJSON* element;
162+ cJSON* from;
163+ cJSON* to;
164+ cJSON* faces;
165+ i32 face;
166+ i32 first;
167+
168+ if (depth == 16)
169+ return 0;
170+ if (strchr(name, ':'))
171+ name = strchr(name, ':') + 1;
172+ snprintf(path, sizeof(path), "%s/models/%s%s.json", pack_path, strchr(name, '/') ? "" : "block/", name);
173+ json = json_load(path);
174+ if (!json)
175+ return 0;
176+ /* load the parent first... */
177+ item = cJSON_GetObjectItemCaseSensitive(json, "parent");
178+ if (cJSON_IsString(item) && !model_load(item->valuestring, model, depth + 1)) {
179+ cJSON_Delete(json);
180+ return 0;
181+ }
182+ item = cJSON_GetObjectItemCaseSensitive(json, "textures");
183+ cJSON_ArrayForEach(faces, item)
184+ {
185+ if (cJSON_IsString(faces))
186+ model_texture(model, faces->string, faces->valuestring);
187+ }
188+ item = cJSON_GetObjectItemCaseSensitive(json, "elements");
189+ if (cJSON_IsArray(item)) {
190+ /* ... then let the child override*/
191+ model->face_count = 0;
192+ /* get the from and to values and then turn them into verticies*/
193+ /*TODO we dont rotate stairs*/
194+ cJSON_ArrayForEach(element, item)
195+ {
196+ first = model->face_count;
197+ from = cJSON_GetObjectItemCaseSensitive(element, "from");
198+ to = cJSON_GetObjectItemCaseSensitive(element, "to");
199+ faces = cJSON_GetObjectItemCaseSensitive(element, "faces");
200+ for (face = 0; face < BLOCK_FACE_COUNT; face++)
201+ model_face(model, from, to, faces, face);
202+ model_rotate(model, first, cJSON_GetObjectItemCaseSensitive(element, "rotation"));
203+ }
204+ }
205+ cJSON_Delete(json);
206+ return 1;
207 }
208
209-/*convert json face element into verticies*/
210+/* convert JSON face element into vertices */
211 static void model_face(BlockModel* model, cJSON* from, cJSON* to, cJSON* faces, i32 face)
212 {
213 ModelFace* out;
214@@ -167,7 +326,41 @@ static void model_face(BlockModel* model, cJSON* from, cJSON* to, cJSON* faces,
215 }
216 }
217
218-/*rotate stuff that whats to be rotated the amount it wants to be rotated by*/
219+/* add or override model texture */
220+static void model_texture(BlockModel* model, const char* name, const char* value)
221+{
222+ i32 i;
223+
224+ for (i = 0; i < model->variable_count; i++) {
225+ if (strcmp(model->variables[i].name, name) == 0)
226+ break;
227+ }
228+ if (i == VARIABLE_COUNT)
229+ return;
230+ if (i == model->variable_count)
231+ model->variable_count++;
232+ snprintf(model->variables[i].name, NAME_SIZE, "%s", name);
233+ snprintf(model->variables[i].value, NAME_SIZE, "%s", value);
234+}
235+
236+/* resolve model texture variable
237+ returns resolved texture name, or NULL on failure */
238+static const char* model_resolve(const BlockModel* model, const char* name)
239+{
240+ i32 depth;
241+ i32 i;
242+
243+ for (depth = 0; name && name[0] == '#' && depth < VARIABLE_COUNT; depth++) {
244+ for (i = 0; i < model->variable_count; i++) {
245+ if (strcmp(model->variables[i].name, name + 1) == 0)
246+ break;
247+ }
248+ name = i == model->variable_count ? NULL : model->variables[i].value;
249+ }
250+ return name;
251+}
252+
253+/* rotate model faces from JSON rotation */
254 static void model_rotate(BlockModel* model, i32 first, cJSON* rotation)
255 {
256 cJSON* origin;
257@@ -227,75 +420,8 @@ static void model_rotate(BlockModel* model, i32 first, cJSON* rotation)
258 }
259 }
260
261-/* load inhereted model data from the parent feild and then apply per model overrides*/
262-static i8 model_load(const char* name, BlockModel* model, i32 depth)
263-{
264- char path[PATH_SIZE];
265- cJSON* json;
266- cJSON* item;
267- cJSON* element;
268- cJSON* from;
269- cJSON* to;
270- cJSON* faces;
271- i32 face;
272- i32 first;
273-
274- if (depth == 16)
275- return 0;
276- if (strchr(name, ':'))
277- name = strchr(name, ':') + 1;
278- snprintf(path, sizeof(path), "%s/models/%s%s.json", pack_path, strchr(name, '/') ? "" : "block/", name);
279- json = json_load(path);
280- if (!json)
281- return 0;
282- /* load the parent first... */
283- item = cJSON_GetObjectItemCaseSensitive(json, "parent");
284- if (cJSON_IsString(item) && !model_load(item->valuestring, model, depth + 1)) {
285- cJSON_Delete(json);
286- return 0;
287- }
288- item = cJSON_GetObjectItemCaseSensitive(json, "textures");
289- cJSON_ArrayForEach(faces, item)
290- {
291- if (cJSON_IsString(faces))
292- model_texture(model, faces->string, faces->valuestring);
293- }
294- item = cJSON_GetObjectItemCaseSensitive(json, "elements");
295- if (cJSON_IsArray(item)) {
296- /* ... then let the child override*/
297- model->face_count = 0;
298- /* get the from and to values and then turn them into verticies*/
299- /*TODO we dont rotate stairs*/
300- cJSON_ArrayForEach(element, item)
301- {
302- first = model->face_count;
303- from = cJSON_GetObjectItemCaseSensitive(element, "from");
304- to = cJSON_GetObjectItemCaseSensitive(element, "to");
305- faces = cJSON_GetObjectItemCaseSensitive(element, "faces");
306- for (face = 0; face < BLOCK_FACE_COUNT; face++)
307- model_face(model, from, to, faces, face);
308- model_rotate(model, first, cJSON_GetObjectItemCaseSensitive(element, "rotation"));
309- }
310- }
311- cJSON_Delete(json);
312- return 1;
313-}
314-
315-static const char* model_resolve(const BlockModel* model, const char* name)
316-{
317- i32 depth;
318- i32 i;
319-
320- for (depth = 0; name && name[0] == '#' && depth < VARIABLE_COUNT; depth++) {
321- for (i = 0; i < model->variable_count; i++) {
322- if (strcmp(model->variables[i].name, name + 1) == 0)
323- break;
324- }
325- name = i == model->variable_count ? NULL : model->variables[i].value;
326- }
327- return name;
328-}
329-
330+/* load texture by name
331+ returns texture index, or -1 on failure */
332 static i32 texture_load(const char* name)
333 {
334 char path[PATH_SIZE];
335@@ -319,107 +445,6 @@ static i32 texture_load(const char* name)
336 return texture_count++;
337 }
338
339-static void model_cube(BlockModel* model, const char* texture)
340-{
341- i32 face;
342-
343- for (face = 0; face < BLOCK_FACE_COUNT; face++) {
344- model->faces[face].cullface = face;
345- memcpy(model->faces[face].vertices, face_vertices[face], sizeof(face_vertices[face]));
346- snprintf(model->faces[face].texture, NAME_SIZE, "%s", texture);
347- }
348- model->face_count = BLOCK_FACE_COUNT;
349-}
350-
351-static i8 face_covers_cube(const ModelFace* face)
352-{
353- /* only faces spanning their entire boundary plane can occlude neighbours */
354- static const i32 face_axes[BLOCK_FACE_COUNT] = { 2, 2, 0, 0, 1, 1 };
355- float low[3] = { 1, 1, 1 };
356- float high[3] = { 0, 0, 0 };
357- float value[3];
358- i32 vertex;
359- i32 axis;
360- i32 plane;
361-
362- for (vertex = 0; vertex < 4; vertex++) {
363- value[0] = face->vertices[vertex].x;
364- value[1] = face->vertices[vertex].y;
365- value[2] = face->vertices[vertex].z;
366- for (axis = 0; axis < 3; axis++) {
367- if (value[axis] < low[axis])
368- low[axis] = value[axis];
369- if (value[axis] > high[axis])
370- high[axis] = value[axis];
371- }
372- }
373- plane = face_axes[face->cullface];
374- for (axis = 0; axis < 3; axis++) {
375- if (axis != plane && (low[axis] != 0 || high[axis] != 1))
376- return 0;
377- }
378- return face->cullface & 1 ? high[plane] == 1 : low[plane] == 0;
379-}
380-
381-static void block_load(BlockType block)
382-{
383- LoadedBlock* loaded;
384- BlockModel model;
385- cJSON* entry;
386- cJSON* item;
387- const char* name;
388- const char* fallback;
389- i32 face;
390- i32 mask;
391- char path[PATH_SIZE];
392-
393- loaded = &blocks[block];
394- loaded->loaded = 1;
395- entry = cJSON_GetArrayItem(block_registry, block >> 4);
396- item = cJSON_GetObjectItemCaseSensitive(entry, "blockState");
397- name = cJSON_IsString(item) ? item->valuestring : NULL;
398- item = cJSON_GetObjectItemCaseSensitive(entry, "texture");
399- fallback = cJSON_IsString(item) ? item->valuestring : NULL;
400- memset(&model, 0, sizeof(model));
401- if (name) {
402- snprintf(path, sizeof(path), "%s/blockstates/%s.json", pack_path, name);
403- item = json_load(path);
404- if (item) {
405- entry = cJSON_GetObjectItemCaseSensitive(item, "variants");
406- if (cJSON_GetArraySize(entry) > (block & 15))
407- entry = cJSON_GetArrayItem(entry, block & 15);
408- else
409- entry = entry ? entry->child : NULL;
410- if (cJSON_IsArray(entry))
411- entry = cJSON_GetArrayItem(entry, 0);
412- entry = cJSON_GetObjectItemCaseSensitive(entry, "model");
413- if (cJSON_IsString(entry))
414- model_load(entry->valuestring, &model, 0);
415- cJSON_Delete(item);
416- }
417- }
418- if (!model.face_count && fallback)
419- model_cube(&model, fallback);
420- if (!model.face_count)
421- return;
422- loaded->faces = malloc(model.face_count * sizeof(*loaded->faces));
423- if (!loaded->faces)
424- return;
425- loaded->face_count = model.face_count;
426- mask = 0;
427- for (face = 0; face < model.face_count; face++) {
428- name = model_resolve(&model, model.faces[face].texture);
429- if (!name)
430- name = fallback;
431- loaded->faces[face].texture = name ? texture_load(name) : -1;
432- loaded->faces[face].cullface = model.faces[face].cullface;
433- memcpy(loaded->faces[face].vertices, model.faces[face].vertices, sizeof(model.faces[face].vertices));
434- if (model.faces[face].cullface >= 0 && face_covers_cube(&model.faces[face]))
435- mask |= 1 << model.faces[face].cullface;
436- }
437- loaded->occludes = mask == (1 << BLOCK_FACE_COUNT) - 1;
438-}
439-
440 i8 block_textures_create(const char* path)
441 {
442 block_textures_destroy();
+1,
-1
1@@ -14,7 +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+/* add face vertices and indices */
7 static void add_face(MGVertex* vertices, u16* indices, u32* vertex_count, u32* index_count, const BlockFace* face,
8 i32 x, i32 y, i32 z)
9 {