#include "core/game.h" #include "zpl.h" #include "platform/platform.h" #include "world/world.h" #include "pkt/packet.h" #include "platform/signal_handling.h" #include "net/network.h" #include "models/entity.h" #include "world/world_view.h" #include "world/entity_view.h" #include "core/camera.h" #include "platform/profiler.h" #include "platform/renderer.h" #include "flecs/flecs_os_api_stdcpp.h" #include "flecs.h" #include "models/components.h" #include "systems/systems.h" #include "packets/pkt_00_init.h" #include "packets/pkt_01_welcome.h" #include "packets/pkt_switch_viewer.h" static uint8_t game_mode; static uint8_t game_should_close; static world_view *world_viewers; static world_view *active_viewer; static WORLD_PKT_READER(pkt_reader) { pkt_header header = {0}; uint32_t ok = pkt_header_decode(&header, data, datalen); header.udata = udata; if (ok && header.ok) { return pkt_handlers[header.id].handler(&header) >= 0; } else { zpl_printf("[warn] unknown packet id %d (header %d data %d)\n", header.id, ok, header.ok); } return -1; } static WORLD_PKT_WRITER(sp_pkt_writer) { (void)udata; return world_read(pkt->data, pkt->datalen, 0); } static WORLD_PKT_WRITER(mp_pkt_writer) { if (pkt->is_reliable) { return network_msg_send(udata, pkt->data, pkt->datalen, pkt->channel_id); } else { return network_msg_send_unreliable(udata, pkt->data, pkt->datalen, pkt->channel_id); } } static WORLD_PKT_WRITER(mp_cli_pkt_writer) { (void)udata; if (pkt->is_reliable) { return network_msg_send(0, pkt->data, pkt->datalen, pkt->channel_id); } else { return network_msg_send_unreliable(0, pkt->data, pkt->datalen, pkt->channel_id); } } void world_viewers_init(uint32_t num_viewers) { zpl_buffer_init(world_viewers, zpl_heap(), num_viewers); for (uint32_t i = 0; i < num_viewers; i++) { zpl_buffer_append(world_viewers, world_view_create(i)); } } void world_viewers_destroy() { for (zpl_isize i = 0; i < zpl_buffer_count(world_viewers); i++) { world_view_destroy(&world_viewers[i]); } zpl_buffer_free(world_viewers); } world_view *game_world_view_get(uint16_t idx) { return &world_viewers[idx]; } world_view *game_world_view_get_active(void) { return active_viewer; } void game_world_view_cycle_active(int8_t dir) { uint16_t idx = (uint16_t)(active_viewer - world_viewers); game_world_view_set_active_by_idx(zpl_max(0, (idx+dir)%zpl_buffer_count(world_viewers))); } void game_world_view_set_active_by_idx(uint16_t idx) { ZPL_ASSERT(idx >= 0 && idx < zpl_buffer_count(world_viewers)); game_world_view_set_active(&world_viewers[idx]); } void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)) { entity_view_map(&active_viewer->entities, map_proc); } entity_view *game_world_view_active_get_entity(uint64_t ent_id) { return entity_view_get(&active_viewer->entities, ent_id); } void game_world_view_set_active(world_view *view) { active_viewer = view; camera_set_follow(view->owner_id); pkt_switch_viewer_send(view->view_id); } size_t game_world_view_count(void) { return zpl_buffer_count(world_viewers); } void flecs_dash_init() { #if !defined(ZPL_SYSTEM_EMSCRIPTEN) ecs_singleton_set(world_ecs(), EcsRest, {0}); ECS_IMPORT(world_ecs(), FlecsMonitor); #endif } float game_time() { return (float)get_cached_time(); } void game_init(const char *ip, uint16_t port, game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled) { game_mode = play_mode; game_should_close = false; #ifndef _DEBUG const char *host_ip = "lab.zakto.pw"; #else const char *host_ip = "127.0.0.1"; #endif uint16_t host_port = (port > 0) ? port : 27000; if (ip != NULL) { host_ip = ip; } if (game_mode != GAMEKIND_HEADLESS) { platform_init(); world_viewers_init(num_viewers); active_viewer = &world_viewers[0]; camera_reset(); } if (game_mode != GAMEKIND_SINGLE) { network_init(); } if (game_mode == GAMEKIND_CLIENT) { world_setup_pkt_handlers(pkt_reader, mp_cli_pkt_writer); network_client_connect(host_ip, host_port); } else { stdcpp_set_os_api(); world_setup_pkt_handlers(pkt_reader, game_mode == GAMEKIND_SINGLE ? sp_pkt_writer : mp_pkt_writer); world_init(seed, chunk_size, chunk_amount); if (is_dash_enabled) flecs_dash_init(); if (game_mode == GAMEKIND_HEADLESS) { network_server_start(0, 27000); //ecs_set_target_fps(world_ecs(), 60); } } if (game_mode == GAMEKIND_SINGLE) { for (uint32_t i = 0; i < num_viewers; i++) { pkt_00_init_send(i); } } } int8_t game_is_networked() { return game_mode != GAMEKIND_SINGLE; } void game_shutdown() { if (game_mode == GAMEKIND_CLIENT) { network_client_disconnect(); } else { world_destroy(); if (game_mode == GAMEKIND_HEADLESS) { network_server_stop(); } } if (game_mode != GAMEKIND_SINGLE) { network_destroy(); } if (game_mode != GAMEKIND_HEADLESS) { world_viewers_destroy(); // TODO(zaklaus): crashes on exit //platform_shutdown(); } } uint8_t game_is_running() { uint8_t is_running = !game_should_close; if (game_mode != GAMEKIND_HEADLESS) { is_running = platform_is_running(); } return is_running; } game_kind game_get_kind(void) { return game_mode; } void game_input() { if (game_mode != GAMEKIND_HEADLESS) { platform_input(); } } void game_update() { static double last_update = 0.0f; if (game_mode == GAMEKIND_CLIENT) { network_client_tick(); } else { world_update(); if (game_mode == GAMEKIND_HEADLESS) { network_server_tick(); static float ms_report = 2.5f; if (ms_report < get_cached_time()) { ms_report = get_cached_time() + 5.f; zpl_printf("delta: %f ms.\n", (get_cached_time() - last_update)*1000.0f); } } } last_update = get_cached_time(); } void game_render() { if (game_mode != GAMEKIND_HEADLESS) { platform_render(); } } void game_action_send_keystate(game_keystate_data *data) { pkt_send_keystate_send(active_viewer->view_id, data); } void game_action_send_blockpos(float mx, float my) { pkt_send_blockpos data = { .mx = mx, .my = my }; pkt_send_blockpos_send(active_viewer->view_id, &data); } void game_request_close() { game_should_close = true; if (game_mode != GAMEKIND_HEADLESS) { platform_request_close(); } } static game_world_render_entry* render_queue = NULL; static void game__world_view_render_push_entry(uint64_t key, entity_view * data) { if (!data) return; if (data->kind == EKIND_CHUNK) { world_view *view = game_world_view_get_active(); float size = (float)(view->chunk_size * WORLD_BLOCK_SIZE); float offset = 0.0; for (size_t ty = 0; ty < view->chunk_size; ty++) { for (size_t tx = 0; tx < view->chunk_size; tx++) { block_id blk_id = data->outer_blocks[(ty*view->chunk_size)+tx]; if (blk_id != 0) { game_world_render_entry entry = { .key = key, .data = data, .blk_id = blk_id, .x = (data->x*size + offset) + (float)tx*WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2, .y = (data->y*size + offset) + (float)ty*WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2, }; zpl_array_append(render_queue, entry); } } } return; } game_world_render_entry entry = { .key = key, .data = data, .x = data->x, .y = data->y, .blk_id = 0, }; zpl_array_append(render_queue, entry); } static void game__world_view_render_ground(uint64_t key, entity_view * data) { if (data->kind != EKIND_CHUNK) return; renderer_draw_entry(key, data, 0); } void game_world_view_render_world(void) { if (!render_queue) { zpl_array_init(render_queue, zpl_heap()); } zpl_array_clear(render_queue); profile(PROF_RENDER_PUSH_AND_SORT_ENTRIES) { game_world_view_active_entity_map(game__world_view_render_push_entry); zpl_sort_array(render_queue, zpl_array_count(render_queue), zpl_f32_cmp(zpl_offset_of(game_world_render_entry, y))); } game_world_view_active_entity_map(game__world_view_render_ground); for (zpl_isize i = 0; i < zpl_array_count(render_queue); i++) { renderer_draw_entry(render_queue[i].key, render_queue[i].data, &render_queue[i]); } }