commit 877d445
uint
·
2026-08-04 21:10:37 +0000 UTC
parent d86ed5d
refactor main() to be less test like
1 files changed,
+225,
-194
+225,
-194
1@@ -9,135 +9,187 @@
2 #include "client/world.h"
3 #include "net/mc_chunk.h"
4 #include "net/mc_proto.h"
5-#include "net/packet.h"
6 #include "platform/platform.h"
7 #include "renderer/renderer.h"
8 #include "world/block.h"
9 #include "world/chunk.h"
10
11-#define TEST_WORLD_WIDTH 50
12-#define TEST_WORLD_DEPTH 50
13-
14-static MGContext ctx = {
15- NULL, { NULL }, "Magnolia", 0, 0, 1024, 768, { MGK_UNKNOWN },
16-};
17-
18-static MGCamera cam;
19-static ClientWorld world;
20-static i8 server_mode;
21-
22-static void test_scene_create(Player* player, i8 server)
23+#define LOCAL_WORLD_WIDTH 50
24+#define LOCAL_WORLD_DEPTH 50
25+#define PACKETS_PER_FRAME 64
26+
27+typedef struct {
28+ MGContext ctx;
29+ MGCamera cam;
30+ Player player;
31+ ClientWorld world;
32+ MCClient* client;
33+} Magnolia;
34+
35+static Magnolia game;
36+
37+static void create_local_world(void);
38+static void game_dies(void);
39+static void game_init(int argc, char** argv);
40+static void game_run(void);
41+static void handle_chunk_packet(const MCPacket* packet);
42+static void handle_input(void);
43+static i8 handle_server_packets(void);
44+static void render_world(void);
45+
46+static void create_local_world(void)
47 {
48 Chunk* chunk;
49 i32 chunk_index;
50 i32 x;
51 i32 z;
52
53- if (!block_textures_create("assets/resourcepacks/default"))
54- mg_die(MG_EC_TEXTURE_LOAD, "Failed to load block textures");
55+ for (chunk_index = 0; chunk_index < CLIENT_WORLD_MAX_CHUNKS; chunk_index++) {
56+ x = chunk_index % LOCAL_WORLD_WIDTH;
57+ z = chunk_index / LOCAL_WORLD_WIDTH;
58+ if (z >= LOCAL_WORLD_DEPTH)
59+ break;
60
61- camera_init(&cam, MG_V3(8.0f, 38.0f, 24.0f));
62- cam.yaw = MG_AngleDeg(-180.0f);
63- cam.pitch = MG_AngleDeg(-25.0f);
64-
65- server_mode = server;
66- world_init(&world);
67- player->x = 0.0;
68- player->y = 0.0;
69- player->z = 0.0;
70- player->yaw = 0.0f;
71- player->pitch = 0.0f;
72- player->positioned = 0;
73-
74- if (server_mode) {
75- cam.far = 512.0f;
76- }
77- else { /* test world */
78- for (chunk_index = 0; chunk_index < CLIENT_WORLD_MAX_CHUNKS; chunk_index++) {
79- x = chunk_index % TEST_WORLD_WIDTH;
80- z = chunk_index / TEST_WORLD_WIDTH;
81- if (z >= TEST_WORLD_DEPTH)
82- break;
83-
84- chunk = &world.chunks[chunk_index];
85- chunk->used = 1;
86- chunk->x = x;
87- chunk->z = z;
88- chunk_section_create(&chunk->sections[0]);
89- chunk->section_mask = 1;
90-
91- if (!chunk_section_mesh_create(&chunk->sections[0]))
92- mg_die(MG_EC_MESH_CREATE, "Failed to create chunk mesh");
93- }
94+ chunk = &game.world.chunks[chunk_index];
95+ chunk->used = 1;
96+ chunk->x = x;
97+ chunk->z = z;
98+ chunk_section_create(&chunk->sections[0]);
99+ chunk->section_mask = 1;
100+
101+ if (!chunk_section_mesh_create(&chunk->sections[0]))
102+ mg_die(MG_EC_MESH_CREATE, "Failed to create chunk mesh");
103 }
104 }
105
106-static void test_scene_draw(MGContext* ctx)
107+static void game_dies(void)
108 {
109- MGMat4 proj;
110- MGMat4 view;
111- MGMat4 mvp;
112- MGMat4 model;
113- i32 width;
114- i32 height;
115- float aspect;
116- i32 texture;
117+ if (game.client)
118+ mc_client_destroy(game.client);
119
120- Chunk* column;
121- ChunkSection* section_chunk;
122- i32 section;
123- i32 i;
124+ world_destroy(&game.world);
125+ block_textures_destroy();
126+ renderer_destroy();
127+ platform_shutdown(&game.ctx);
128+}
129
130- platform_get_drawable_size(ctx, &width, &height);
131+static void game_init(int argc, char** argv)
132+{
133+ MCAccount account;
134+ char error[1024];
135
136- if (width <= 0 || height <= 0)
137- return;
138+ if (argc != 1 && argc != 3)
139+ mg_die(MG_EC_INVALID_ARGS, "usage: %s [server[:port] username]\n", argv[0]);
140
141- aspect = (float)width / (float)height;
142+ if (argc == 3) {
143+ account.username = argv[2];
144+ account.profile_uuid = getenv("MANGOLIA_PROFILE_UUID");
145+ account.access_token = getenv("MANGOLIA_ACCESS_TOKEN");
146
147- proj = camera_get_proj(&cam, aspect);
148- view = camera_get_view(&cam);
149+ if (!mc_client_login(&game.client, argv[1], &account, error, sizeof(error)))
150+ mg_die(MG_EC_MCCLIENT_LOGIN, "%s\n", error);
151+ }
152
153- for (i = 0; i < CLIENT_WORLD_MAX_CHUNKS; i++) {
154- column = &world.chunks[i];
155+ config_default();
156
157- if (!column->used)
158- continue;
159+ game.ctx.title = "Magnolia";
160+ game.ctx.width = mg_config.display_width;
161+ game.ctx.height = mg_config.display_height;
162
163- for (section = 0; section < CHUNK_SECTION_COUNT; section++) {
164- if (!(column->section_mask & (1U << section)))
165- continue;
166+ platform_init(&game.ctx);
167+ platform_window_create(&game.ctx);
168+ renderer_create(&game.ctx);
169+ platform_set_mouse_relative(1);
170
171- section_chunk = &column->sections[section];
172- model = MG_Translate(
173- MG_V3(column->x * CHUNK_WIDTH, section * CHUNK_SECTION_HEIGHT, column->z * CHUNK_DEPTH));
174- mvp = MG_MulM4(proj, MG_MulM4(view, model));
175- for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
176- if (section_chunk->meshes[texture] != MGMESH_INVALID)
177- renderer_mesh_draw(section_chunk->meshes[texture], block_texture_get(texture),
178- &mvp);
179- }
180- }
181+ if (!block_textures_create("assets/resourcepacks/default"))
182+ mg_die(MG_EC_TEXTURE_LOAD, "Failed to load block textures");
183+
184+ camera_init(&game.cam, MG_V3(8.0f, 38.0f, 24.0f));
185+ game.cam.yaw = MG_AngleDeg(-180.0f);
186+ game.cam.pitch = MG_AngleDeg(-25.0f);
187+
188+ world_init(&game.world);
189+
190+ game.player.x = 0.0;
191+ game.player.y = 0.0;
192+ game.player.z = 0.0;
193+ game.player.yaw = 0.0f;
194+ game.player.pitch = 0.0f;
195+ game.player.positioned = 0;
196+
197+ if (!game.client)
198+ create_local_world();
199+}
200+
201+static void game_run(void)
202+{
203+ while (platform_poll(&game.ctx)) {
204+ if (!handle_server_packets())
205+ break;
206+
207+ handle_input();
208+
209+ if (game.client)
210+ player_server_send_position(&game.player, &game.cam, game.client);
211+
212+ renderer_begin();
213+ render_world();
214+ renderer_end();
215+
216+ platform_present(&game.ctx);
217+ platform_wait(1);
218 }
219 }
220
221-static void test_scene_destroy(void)
222+static void handle_chunk_packet(const MCPacket* packet)
223 {
224- world_destroy(&world);
225- block_textures_destroy();
226+ MCChunkPacket chunk;
227+ size_t offset;
228+ size_t size;
229+ i32 update;
230+
231+ if (!mc_chunk_decode(&chunk, packet))
232+ return;
233+
234+ if (chunk.unload) {
235+ world_remove_chunk(&game.world, chunk.unload_x, chunk.unload_z);
236+ mc_chunk_destroy(&chunk);
237+ return;
238+ }
239+
240+ offset = 0;
241+
242+ for (update = 0; update < chunk.update_count; update++) {
243+ size = mc_chunk_update_size(chunk.updates[update].mask, chunk.skylight, chunk.bulk);
244+ if (offset > chunk.data_size || size > chunk.data_size - offset) {
245+ mg_log(LOG_ERR, "Chunk data is incomplete");
246+ break;
247+ }
248+
249+ if (!world_update_chunk(&game.world, &chunk.updates[update], chunk.data + offset))
250+ break;
251+
252+ offset += size;
253+ }
254+
255+ mc_chunk_destroy(&chunk);
256 }
257
258-static void input(void)
259+static void handle_input(void)
260 {
261 static u32 last;
262
263- MGVec3 move = MG_V3(0.0f, 0.0f, 0.0f);
264- u64 now = platform_get_ticks();
265- float sensitivity = mg_config.input_sensitivity;
266+ MGVec3 move;
267+ u64 now;
268+ float sensitivity;
269 float dt;
270 i32 mx;
271 i32 my;
272
273+ move = MG_V3(0.0f, 0.0f, 0.0f);
274+ now = platform_get_ticks();
275+ sensitivity = mg_config.input_sensitivity;
276+
277 if (!last) {
278 last = now;
279 return;
280@@ -146,148 +198,127 @@ static void input(void)
281 dt = (now - last) / 1000.0f;
282 last = now;
283
284- if (platform_key_down(&ctx, MGK_W))
285+ /* TODO normalise */
286+ if (platform_key_down(&game.ctx, MGK_W))
287 move.z += 1.0f;
288- if (platform_key_down(&ctx, MGK_A))
289+ if (platform_key_down(&game.ctx, MGK_A))
290 move.x -= 1.0f;
291- if (platform_key_down(&ctx, MGK_S))
292+ if (platform_key_down(&game.ctx, MGK_S))
293 move.z -= 1.0f;
294- if (platform_key_down(&ctx, MGK_D))
295+ if (platform_key_down(&game.ctx, MGK_D))
296 move.x += 1.0f;
297- if (platform_key_down(&ctx, MGK_SPACE))
298+ if (platform_key_down(&game.ctx, MGK_SPACE))
299 move.y += 1.0f;
300- if (platform_key_down(&ctx, MGK_LCTRL))
301+ if (platform_key_down(&game.ctx, MGK_LCTRL))
302 move.y -= 1.0f;
303
304- /* debug keys */
305- if (platform_key_down(&ctx, MGK_L)) {
306+ if (platform_key_down(&game.ctx, MGK_L)) {
307 mg_config.debug_wireframe = !mg_config.debug_wireframe;
308 renderer_debug_wireframe(mg_config.debug_wireframe);
309 }
310
311 move = MG_MulV3F(move, mg_config.temp_movement_speed * dt);
312- camera_move(&cam, move);
313+ camera_move(&game.cam, move);
314 platform_get_mouse_state(&mx, &my);
315- camera_rotate(&cam, mx * sensitivity, -my * sensitivity);
316+ camera_rotate(&game.cam, mx * sensitivity, -my * sensitivity);
317 }
318
319-static void chunk_packet(ClientWorld* world, const MCPacket* packet)
320+static i8 handle_server_packets(void)
321 {
322- MCChunkPacket chunk;
323- size_t offset;
324- size_t size;
325- i32 update;
326+ MCPacket packet;
327+ char error[1024];
328+ int poll_result;
329+ int packets;
330
331- if (!mc_chunk_decode(&chunk, packet))
332- return;
333+ if (!game.client)
334+ return 1;
335
336- if (chunk.unload) {
337- world_remove_chunk(world, chunk.unload_x, chunk.unload_z);
338- mc_chunk_destroy(&chunk);
339- return;
340- }
341+ for (packets = 0; packets < PACKETS_PER_FRAME; packets++) {
342+ poll_result = mc_client_poll(game.client, &packet, error, sizeof(error));
343+ if (poll_result <= 0)
344+ break;
345
346- offset = 0;
347+ printf("packet 0x%02x size %lu\n", packet.id, packet.size);
348+ switch (packet.id) {
349+ case 0x00:
350+ if (!mc_client_write(game.client, 0x00, packet.data, packet.size))
351+ mg_log(LOG_ERR, "Failed to echo keepalive");
352+ break;
353
354- for (update = 0; update < chunk.update_count; update++) {
355- size = mc_chunk_update_size(chunk.updates[update].mask, chunk.skylight, chunk.bulk);
356- if (offset > chunk.data_size || size > chunk.data_size - offset) {
357- mg_log(LOG_ERR, "Chunk data is incomplete");
358+ case 0x08:
359+ player_server_handle_position(&game.player, &game.cam, &packet);
360 break;
361- }
362
363- if (!world_update_chunk(world, &chunk.updates[update], chunk.data + offset))
364+ case 0x21:
365+ case 0x26:
366+ /* both single and bulk changes
367+ handled in handle_chunk_packet */
368+ handle_chunk_packet(&packet);
369 break;
370+ }
371
372- offset += size;
373+ mc_packet_free(&packet);
374 }
375
376- mc_chunk_destroy(&chunk);
377+ if (poll_result < 0) {
378+ mg_log(LOG_ERR, "%s", error);
379+ return 0;
380+ }
381+
382+ return 1;
383 }
384
385-int main(int argc, char** argv)
386+static void render_world(void)
387 {
388- MCClient* client = NULL;
389- MCAccount account;
390- MCPacket packet;
391- char error[1024];
392- int poll_result;
393- int packets;
394-
395- Player player;
396+ MGMat4 proj;
397+ MGMat4 view;
398+ MGMat4 mvp;
399+ MGMat4 model;
400+ i32 width;
401+ i32 height;
402+ float aspect;
403+ i32 texture;
404
405- if (argc != 1 && argc != 3)
406- mg_die(MG_EC_INVALID_ARGS, "usage: %s [server[:port] username]\n", argv[0]);
407- if (argc == 3) {
408- account.username = argv[2];
409- account.profile_uuid = getenv("MANGOLIA_PROFILE_UUID");
410- account.access_token = getenv("MANGOLIA_ACCESS_TOKEN");
411+ Chunk* chunk;
412+ ChunkSection* section;
413+ i32 section_index;
414+ i32 chunk_index;
415
416- if (!mc_client_login(&client, argv[1], &account, error, sizeof(error)))
417- mg_die(MG_EC_MCCLIENT_LOGIN, "%s\n", error);
418- }
419+ platform_get_drawable_size(&game.ctx, &width, &height);
420+ if (width <= 0 || height <= 0)
421+ return;
422
423- config_default();
424+ aspect = (float)width / (float)height;
425+ proj = camera_get_proj(&game.cam, aspect);
426+ view = camera_get_view(&game.cam);
427
428- ctx.width = mg_config.display_width;
429- ctx.height = mg_config.display_height;
430+ for (chunk_index = 0; chunk_index < CLIENT_WORLD_MAX_CHUNKS; chunk_index++) {
431+ chunk = &game.world.chunks[chunk_index];
432+ if (!chunk->used)
433+ continue;
434
435- platform_init(&ctx);
436- platform_window_create(&ctx);
437- renderer_create(&ctx);
438- test_scene_create(&player, client != NULL);
439- platform_set_mouse_relative(1);
440+ for (section_index = 0; section_index < CHUNK_SECTION_COUNT; section_index++) {
441+ if (!(chunk->section_mask & (1U << section_index)))
442+ continue;
443
444- while (platform_poll(&ctx)) {
445- if (client) {
446- for (packets = 0; packets < 64; packets++) {
447- poll_result = mc_client_poll(client, &packet, error, sizeof(error));
448- if (poll_result <= 0)
449- break;
450-
451- printf("packet 0x%02x size %lu\n", packet.id, packet.size);
452- switch (packet.id) {
453- case 0x00:
454- if (!mc_client_write(client, 0x00, packet.data, packet.size))
455- mg_log(LOG_ERR, "Failed to echo keepalive");
456- break;
457- case 0x08:
458- player_server_handle_position(&player, &cam, &packet);
459- break;
460- case 0x21:
461- case 0x26:
462- chunk_packet(&world, &packet);
463- break;
464- }
465-
466- mc_packet_free(&packet);
467- }
468+ section = &chunk->sections[section_index];
469+ model = MG_Translate(MG_V3(chunk->x * CHUNK_WIDTH, section_index * CHUNK_SECTION_HEIGHT,
470+ chunk->z * CHUNK_DEPTH));
471+ mvp = MG_MulM4(proj, MG_MulM4(view, model));
472
473- if (poll_result < 0) {
474- mg_log(LOG_ERR, "%s", error);
475- break;
476+ for (texture = 0; texture < BLOCK_TEXTURE_COUNT; texture++) {
477+ if (section->meshes[texture] != MGMESH_INVALID)
478+ renderer_mesh_draw(section->meshes[texture], block_texture_get(texture), &mvp);
479 }
480 }
481-
482- input();
483-
484- if (client)
485- player_server_send_position(&player, &cam, client);
486-
487- renderer_begin();
488-
489- /* ... */
490- test_scene_draw(&ctx);
491-
492- renderer_end();
493- platform_present(&ctx);
494- platform_wait(1);
495 }
496+}
497+
498+int main(int argc, char** argv)
499+{
500+ game_init(argc, argv);
501+ game_run();
502+ game_dies();
503
504- if (client)
505- mc_client_destroy(client);
506- test_scene_destroy();
507- renderer_destroy();
508- platform_shutdown(&ctx);
509 return MG_EC_EXIT_SUCCESS;
510 }