diff --git a/code/game/src/game.c b/code/game/src/game.c index dd6761c..dc50cf6 100644 --- a/code/game/src/game.c +++ b/code/game/src/game.c @@ -1,278 +1,278 @@ -#include "game.h" -#include "zpl.h" -#include "platform.h" -#include "world/world.h" -#include "packet.h" -#include "signal_handling.h" -#include "network.h" -#include "entity.h" -#include "world_view.h" -#include "entity_view.h" -#include "camera.h" -#include "profiler.h" - -#include "flecs/flecs.h" -//#include "flecs/flecs_dash.h" -//#include "flecs/flecs_systems_civetweb.h" -#include "flecs/flecs_os_api_stdcpp.h" - -#include "modules/components.h" -#include "modules/systems.h" - -#include "packets/pkt_00_init.h" -#include "packets/pkt_01_welcome.h" -#include "packets/pkt_send_keystate.h" - -static uint8_t game_mode; - -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, (void*)game_world_view_get_active()->owner_id); -} - -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 (uint32_t 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); -} - -void flecs_dash_init() { -#if 0 - ECS_IMPORT(world_ecs(), FlecsDash); - ECS_IMPORT(world_ecs(), FlecsSystemsCivetweb); - - ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001}); -#endif -} - -float game_time() { - return zpl_time_rel(); -} - -void game_init(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; - - 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); -#ifndef _DEBUG - network_client_connect("lab.zakto.pw", 27000); -#else - network_client_connect("127.0.0.1", 27000); -#endif - } 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() { - return game_mode == GAMEKIND_HEADLESS || platform_is_running(); -} - -game_kind game_get_kind(void) { - return game_mode; -} - -void game_input() { - if (game_mode != GAMEKIND_HEADLESS) { - platform_input(); - } -} - -void game_update() { - if (game_mode == GAMEKIND_CLIENT) { - network_client_tick(); - } - else { - world_update(); - - if (game_mode == GAMEKIND_HEADLESS) { - network_server_tick(); - } - } - - if (game_mode != GAMEKIND_HEADLESS) { - game_world_cleanup_entities(); - } -} - -void game_render() { - if (game_mode != GAMEKIND_HEADLESS) { - platform_render(); - } -} - -void game_action_send_keystate(float x, - float y, - float mx, - float my, - uint8_t use, - uint8_t sprint, - uint8_t ctrl, - uint8_t drop, - uint8_t selected_item, - uint8_t swap, - uint8_t swap_from, - uint8_t swap_to) { - pkt_send_keystate_send(active_viewer->view_id, x, y, mx, my, use, sprint, ctrl, drop, selected_item, swap, swap_from, swap_to); -} - -#define GAME_ENT_REMOVAL_TIME 10000 -#define GAME_ENT_REMOVAL_TRESHOLD 500 - -void game_world_cleanup_entities(void) { - // TODO(zaklaus): not the best approach to do a cleanup, let memory stay for a while as it might be reused later on anyway. -#if 1 - profile(PROF_ENTITY_REMOVAL) { - static uint64_t last_removal_time = 0; - if (last_removal_time > zpl_time_rel_ms()) return; - last_removal_time = zpl_time_rel_ms() + GAME_ENT_REMOVAL_TIME; - - for (int i = 0; i < zpl_buffer_count(world_viewers); i += 1){ - entity_view_tbl *view = &world_viewers[i].entities; - uint32_t deletions = 0; - - for (int j = 0; j < zpl_array_count(view->entries); j += 1) { - if (deletions > GAME_ENT_REMOVAL_TRESHOLD) return; - - entity_view *e = &view->entries[j].value; - if (e->tran_effect == ETRAN_REMOVE) { - entity_view_tbl_remove(view, e->ent_id); - deletions++; - } - } - } - } -#endif -} - -void game_request_close() { - platform_request_close(); +#include "game.h" +#include "zpl.h" +#include "platform.h" +#include "world/world.h" +#include "packet.h" +#include "signal_handling.h" +#include "network.h" +#include "entity.h" +#include "world_view.h" +#include "entity_view.h" +#include "camera.h" +#include "profiler.h" + +#include "flecs/flecs.h" +//#include "flecs/flecs_dash.h" +//#include "flecs/flecs_systems_civetweb.h" +#include "flecs/flecs_os_api_stdcpp.h" + +#include "modules/components.h" +#include "modules/systems.h" + +#include "packets/pkt_00_init.h" +#include "packets/pkt_01_welcome.h" +#include "packets/pkt_send_keystate.h" + +static uint8_t game_mode; + +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, (void*)game_world_view_get_active()->owner_id); +} + +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 (uint32_t 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); +} + +void flecs_dash_init() { +#if 0 + ECS_IMPORT(world_ecs(), FlecsDash); + ECS_IMPORT(world_ecs(), FlecsSystemsCivetweb); + + ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001}); +#endif +} + +float game_time() { + return zpl_time_rel(); +} + +void game_init(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; + + 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); +#ifndef _DEBUG + network_client_connect("lab.zakto.pw", 27000); +#else + network_client_connect("127.0.0.1", 27000); +#endif + } 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() { + return game_mode == GAMEKIND_HEADLESS || platform_is_running(); +} + +game_kind game_get_kind(void) { + return game_mode; +} + +void game_input() { + if (game_mode != GAMEKIND_HEADLESS) { + platform_input(); + } +} + +void game_update() { + if (game_mode == GAMEKIND_CLIENT) { + network_client_tick(); + } + else { + world_update(); + + if (game_mode == GAMEKIND_HEADLESS) { + network_server_tick(); + } + } + + if (game_mode != GAMEKIND_HEADLESS) { + game_world_cleanup_entities(); + } +} + +void game_render() { + if (game_mode != GAMEKIND_HEADLESS) { + platform_render(); + } +} + +void game_action_send_keystate(float x, + float y, + float mx, + float my, + uint8_t use, + uint8_t sprint, + uint8_t ctrl, + uint8_t drop, + uint8_t selected_item, + uint8_t swap, + uint8_t swap_from, + uint8_t swap_to) { + pkt_send_keystate_send(active_viewer->view_id, x, y, mx, my, use, sprint, ctrl, drop, selected_item, swap, swap_from, swap_to); +} + +#define GAME_ENT_REMOVAL_TIME 10000 +#define GAME_ENT_REMOVAL_TRESHOLD 500 + +void game_world_cleanup_entities(void) { + // TODO(zaklaus): not the best approach to do a cleanup, let memory stay for a while as it might be reused later on anyway. +#if 1 + profile(PROF_ENTITY_REMOVAL) { + static uint64_t last_removal_time = 0; + if (last_removal_time > zpl_time_rel_ms()) return; + last_removal_time = zpl_time_rel_ms() + GAME_ENT_REMOVAL_TIME; + + for (int i = 0; i < zpl_buffer_count(world_viewers); i += 1){ + entity_view_tbl *view = &world_viewers[i].entities; + uint32_t deletions = 0; + + for (int j = 0; j < zpl_array_count(view->entries); j += 1) { + if (deletions > GAME_ENT_REMOVAL_TRESHOLD) return; + + entity_view *e = &view->entries[j].value; + if (e->tran_effect == ETRAN_REMOVE) { + entity_view_tbl_remove(view, e->ent_id); + deletions++; + } + } + } + } +#endif +} + +void game_request_close() { + platform_request_close(); } \ No newline at end of file diff --git a/code/game/src/platform_raylib.c b/code/game/src/platform_raylib.c index 9628b4f..ceeae7c 100644 --- a/code/game/src/platform_raylib.c +++ b/code/game/src/platform_raylib.c @@ -1,158 +1,158 @@ -#include "platform.h" -#include "raylib.h" -#include "raymath.h" -#include "network.h" -#include "game.h" -#include "entity_view.h" -#include "prediction.h" -#include "camera.h" -#include "math.h" -#include "world/blocks.h" -#include "assets.h" -#include "profiler.h" -#include "debug_ui.h" -#include "utils/raylib_helpers.h" - -static uint16_t screenWidth = 1600; -static uint16_t screenHeight = 900; -static float target_zoom = 1.5f; -static bool request_shutdown; - -#define GFX_KIND 2 -#include "renderer_bridge.c" - -// NOTE(zaklaus): add-ins -#include "gui/inventory.c" - -void platform_init() { - InitWindow(screenWidth, screenHeight, "eco2d"); - SetWindowState(FLAG_WINDOW_UNDECORATED|FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT); - - SetTargetFPS(60); - screenWidth = GetScreenWidth(); - screenHeight = GetScreenHeight(); - renderer_init(); -} - -void display_conn_status() { - if (game_is_networked()) { - if (network_client_is_connected()) { - DrawText("Connection: online", 5, 5, 12, GREEN); - } else { - DrawText("Connection: offline", 5, 5, 12, RED); - } - } else { - DrawText("Connection: single-player", 5, 5, 12, BLUE); - } -} - -void platform_shutdown() { - renderer_shutdown(); - CloseWindow(); -} - -uint8_t platform_is_running() { - return !WindowShouldClose(); -} - -void platform_input() { - float mouse_z = (GetMouseWheelMove()*0.5f); - - if (mouse_z != 0.0f) { - target_zoom = zpl_clamp(target_zoom+mouse_z, 0.1f, 10.0f); - } - - // NOTE(zaklaus): keystate handling - { - float x=0.0f, y=0.0f; - uint8_t use, sprint, drop, ctrl; - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) x += 1.0f; - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f; - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f; - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y -= 1.0f; - - use = IsKeyPressed(KEY_SPACE); - sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT); - ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL); - drop = IsKeyPressed(KEY_G) || inv_drop_item; - - // NOTE(zaklaus): NEW! mouse movement - Vector2 mouse_pos = GetMousePosition(); - mouse_pos.x /= screenWidth; - mouse_pos.y /= screenHeight; - mouse_pos.x -= 0.5f; - mouse_pos.y -= 0.5f; - mouse_pos = Vector2Normalize(mouse_pos); - - if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) { - x = mouse_pos.x; - y = -mouse_pos.y; - } - - game_action_send_keystate(x, y, mouse_pos.x, mouse_pos.y, use, sprint, ctrl, drop, inv_selected_item, inv_swap, inv_swap_from, inv_swap_to); - } - - // NOTE(zaklaus): cycle through viewers - { - if (IsKeyPressed(KEY_Q)) { - game_world_view_cycle_active(-1); - } - else if (IsKeyPressed(KEY_E)) { - game_world_view_cycle_active(1); - } - } - - // NOTE(zaklaus): switch render modes - { - if (IsKeyPressed(KEY_O)) { - renderer_switch(1-gfx_kind); - } - } - - // NOTE(zaklaus): toggle debug drawing -#ifndef ECO2D_PROD - { - if (IsKeyPressed(KEY_T)) { - debug_draw_enable(!debug_draw_state()); - } - } -#endif -} - -void platform_render() { - profile(PROF_ENTITY_LERP) { - game_world_view_active_entity_map(lerp_entity_positions); - game_world_view_active_entity_map(do_entity_fadeinout); - } - - BeginDrawing(); - { - profile (PROF_RENDER) { - renderer_draw(); - } - renderer_debug_draw(); - { - // NOTE(zaklaus): add-ins - inventory_draw(); - } - display_conn_status(); - debug_draw(); - } - EndDrawing(); - - if (request_shutdown) { - CloseWindow(); - } -} - -float platform_frametime() { - return GetFrameTime(); -} - -float platform_zoom_get(void) { - return target_zoom; -} - -void platform_request_close(void) { - request_shutdown = true; -} +#include "platform.h" +#include "raylib.h" +#include "raymath.h" +#include "network.h" +#include "game.h" +#include "entity_view.h" +#include "prediction.h" +#include "camera.h" +#include "math.h" +#include "world/blocks.h" +#include "assets.h" +#include "profiler.h" +#include "debug_ui.h" +#include "utils/raylib_helpers.h" + +static uint16_t screenWidth = 1600; +static uint16_t screenHeight = 900; +static float target_zoom = 1.5f; +static bool request_shutdown; + +#define GFX_KIND 2 +#include "renderer_bridge.c" + +// NOTE(zaklaus): add-ins +#include "gui/inventory.c" + +void platform_init() { + InitWindow(screenWidth, screenHeight, "eco2d"); + SetWindowState(FLAG_WINDOW_UNDECORATED|FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT); + + //SetTargetFPS(144); + screenWidth = GetScreenWidth(); + screenHeight = GetScreenHeight(); + renderer_init(); +} + +void display_conn_status() { + if (game_is_networked()) { + if (network_client_is_connected()) { + DrawText("Connection: online", 5, 5, 12, GREEN); + } else { + DrawText("Connection: offline", 5, 5, 12, RED); + } + } else { + DrawText("Connection: single-player", 5, 5, 12, BLUE); + } +} + +void platform_shutdown() { + renderer_shutdown(); + CloseWindow(); +} + +uint8_t platform_is_running() { + return !WindowShouldClose(); +} + +void platform_input() { + float mouse_z = (GetMouseWheelMove()*0.5f); + + if (mouse_z != 0.0f) { + target_zoom = zpl_clamp(target_zoom+mouse_z, 0.1f, 10.0f); + } + + // NOTE(zaklaus): keystate handling + { + float x=0.0f, y=0.0f; + uint8_t use, sprint, drop, ctrl; + if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) x += 1.0f; + if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f; + if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f; + if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y -= 1.0f; + + use = IsKeyPressed(KEY_SPACE); + sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT); + ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL); + drop = IsKeyPressed(KEY_G) || inv_drop_item; + + // NOTE(zaklaus): NEW! mouse movement + Vector2 mouse_pos = GetMousePosition(); + mouse_pos.x /= screenWidth; + mouse_pos.y /= screenHeight; + mouse_pos.x -= 0.5f; + mouse_pos.y -= 0.5f; + mouse_pos = Vector2Normalize(mouse_pos); + + if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) { + x = mouse_pos.x; + y = -mouse_pos.y; + } + + game_action_send_keystate(x, y, mouse_pos.x, mouse_pos.y, use, sprint, ctrl, drop, inv_selected_item, inv_swap, inv_swap_from, inv_swap_to); + } + + // NOTE(zaklaus): cycle through viewers + { + if (IsKeyPressed(KEY_Q)) { + game_world_view_cycle_active(-1); + } + else if (IsKeyPressed(KEY_E)) { + game_world_view_cycle_active(1); + } + } + + // NOTE(zaklaus): switch render modes + { + if (IsKeyPressed(KEY_O)) { + renderer_switch(1-gfx_kind); + } + } + + // NOTE(zaklaus): toggle debug drawing +#ifndef ECO2D_PROD + { + if (IsKeyPressed(KEY_T)) { + debug_draw_enable(!debug_draw_state()); + } + } +#endif +} + +void platform_render() { + profile(PROF_ENTITY_LERP) { + game_world_view_active_entity_map(lerp_entity_positions); + game_world_view_active_entity_map(do_entity_fadeinout); + } + + BeginDrawing(); + { + profile (PROF_RENDER) { + renderer_draw(); + } + renderer_debug_draw(); + { + // NOTE(zaklaus): add-ins + inventory_draw(); + } + display_conn_status(); + debug_draw(); + } + EndDrawing(); + + if (request_shutdown) { + CloseWindow(); + } +} + +float platform_frametime() { + return GetFrameTime(); +} + +float platform_zoom_get(void) { + return target_zoom; +} + +void platform_request_close(void) { + request_shutdown = true; +} diff --git a/code/game/src/vehicle.c b/code/game/src/vehicle.c index 5fb9e5f..3d6d463 100644 --- a/code/game/src/vehicle.c +++ b/code/game/src/vehicle.c @@ -1,22 +1,23 @@ -#include "vehicle.h" -#include "entity.h" -#include "entity_view.h" -#include "world/world.h" - -#include "modules/components.h" - -uint64_t vehicle_spawn(void) { - ecs_entity_t e = entity_spawn(EKIND_VEHICLE); - - Vehicle *veh = ecs_get_mut(world_ecs(), e, Vehicle, NULL); - zpl_zero_item(veh); - veh->wheel_base = 50.0f; - veh->speed = 50.0f; - veh->reverse_speed = -20.0f; - veh->force = 0.0f; - return (uint64_t)e; -} - -void vehicle_despawn(uint64_t ent_id) { - entity_despawn(ent_id); -} +#include "vehicle.h" +#include "entity.h" +#include "entity_view.h" +#include "world/world.h" + +#include "modules/components.h" + +uint64_t vehicle_spawn(void) { + ecs_entity_t e = entity_spawn(EKIND_VEHICLE); + + Vehicle *veh = ecs_get_mut(world_ecs(), e, Vehicle, NULL); + *veh = (Vehicle){ + .wheel_base = 50.0f, + .speed = 50.0f, + .reverse_speed = -20.0f, + .force = 0.0f, + }; + return (uint64_t)e; +} + +void vehicle_despawn(uint64_t ent_id) { + entity_despawn(ent_id); +} diff --git a/code/game/src/world/world.c b/code/game/src/world/world.c index c0e3ad0..f828e84 100644 --- a/code/game/src/world/world.c +++ b/code/game/src/world/world.c @@ -1,488 +1,488 @@ -#include "zpl.h" -#include "librg.h" -#include "modules/components.h" -#include "modules/systems.h" -#include "world/world.h" -#include "entity_view.h" -#include "debug_replay.h" -#include "items.h" -#include "world/worldgen/worldgen.h" -#include "platform.h" -#include "profiler.h" -#include "game.h" - -#include "packets/pkt_send_librg_update.h" - -ZPL_TABLE(static, world_snapshot, world_snapshot_, entity_view); - -static world_data world = {0}; -static world_snapshot streamer_snapshot; - -entity_view world_build_entity_view(int64_t e) { - entity_view *cached_ev = world_snapshot_get(&streamer_snapshot, e); - if (cached_ev) return *cached_ev; - - entity_view view = {0}; - - const Classify *classify = ecs_get(world_ecs(), e, Classify); - ZPL_ASSERT(classify); - - view.kind = classify->id; - - const Position *pos = ecs_get(world_ecs(), e, Position); - if (pos) { - view.x = pos->x; - view.y = pos->y; - } - - const Velocity *vel = ecs_get(world_ecs(), e, Velocity); - if (vel) { - view.flag |= EFLAG_INTERP; - view.vx = vel->x; - view.vy = vel->y; - } - - const Health *health = ecs_get(world_ecs(), e, Health); - if (health) { - view.hp = health->hp; - view.max_hp = health->max_hp; - } - - if (ecs_get(world_ecs(), e, Vehicle)) { - Vehicle const* veh = ecs_get(world_ecs(), e, Vehicle); - view.heading = veh->heading; - } - - if (ecs_get(world_ecs(), e, ItemDrop)) { - ItemDrop const* dr = ecs_get(world_ecs(), e, ItemDrop); - view.asset = item_get_asset(dr->kind); - view.quantity = dr->quantity; - } - - view.inside_vehicle = ecs_get(world_ecs(), e, IsInVehicle) != 0 ? true : false; - - Inventory *inv = 0; - if ((inv = ecs_get_mut_if(world_ecs(), e, Inventory))) { - view.has_items = true; - - for (int i = 0; i < ITEMS_INVENTORY_SIZE; i += 1) { - view.items[i] = inv->items[i]; - } - - const Input *in = ecs_get(world_ecs(), e, Input); - if (in) - view.selected_item = in->selected_item; - } - - Chunk *chpos = 0; - if ((chpos = ecs_get_mut_if(world_ecs(), e, Chunk))) { - view.x = chpos->x; - view.y = chpos->y; - view.blocks_used = 1; - view.is_dirty = chpos->is_dirty; - chpos->is_dirty = false; - - for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) { - view.blocks[i] = world.block_mapping[chpos->id][i]; - } - - for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) { - view.outer_blocks[i] = world.outer_block_mapping[chpos->id][i]; - } - } - - world_snapshot_set(&streamer_snapshot, e, view); - return view; -} - -int32_t tracker_write_create(librg_world *w, librg_event *e) { - int64_t entity_id = librg_event_entity_get(w, e); -#ifdef WORLD_LAYERING - if (world.active_layer_id != WORLD_TRACKER_LAYERS-1) { - // NOTE(zaklaus): reject updates from smaller layers - return LIBRG_WRITE_REJECT; - } -#endif - size_t actual_length = librg_event_size_get(w, e); - char *buffer = librg_event_buffer_get(w, e); - - return (int32_t)entity_view_pack_struct(buffer, actual_length, world_build_entity_view(entity_id)); -} - -int32_t tracker_write_remove(librg_world *w, librg_event *e) { - (void)e; - (void)w; -#ifdef WORLD_LAYERING - if (world.active_layer_id != WORLD_TRACKER_LAYERS-1) { - // NOTE(zaklaus): reject updates from smaller layers - return LIBRG_WRITE_REJECT; - } -#endif - return 0; -} - -int32_t tracker_write_update(librg_world *w, librg_event *e) { - int64_t entity_id = librg_event_entity_get(w, e); - size_t actual_length = librg_event_size_get(w, e); - char *buffer = librg_event_buffer_get(w, e); - entity_view view = world_build_entity_view(entity_id); - - // NOTE(zaklaus): exclude chunks from updates as they never move - { - if (view.kind == EKIND_CHUNK && !view.is_dirty) { - return LIBRG_WRITE_REJECT; - } - } - - return (int32_t)entity_view_pack_struct(buffer, actual_length, view); -} - -void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc) { - world.reader_proc = reader_proc; - world.writer_proc = writer_proc; -} - -int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) { - if (world.data) { - return 0; - } - - world.seed = seed; - world.chunk_size = chunk_size; - world.chunk_amount = chunk_amount; - - world.dim = (world.chunk_size * world.chunk_amount); - world.size = world.dim * world.dim; - - if (world.tracker == NULL) { - world.tracker = librg_world_create(); - } - - if (world.tracker == NULL) { - zpl_printf("[ERROR] An error occurred while trying to create a server world.\n"); - return WORLD_ERROR_TRACKER_FAILED; - } - - /* config our world grid */ - librg_config_chunksize_set(world.tracker, WORLD_BLOCK_SIZE * world.chunk_size, WORLD_BLOCK_SIZE * world.chunk_size, 0); - librg_config_chunkamount_set(world.tracker, world.chunk_amount, world.chunk_amount, 0); - librg_config_chunkoffset_set(world.tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG); - - librg_event_set(world.tracker, LIBRG_WRITE_CREATE, tracker_write_create); - librg_event_set(world.tracker, LIBRG_WRITE_REMOVE, tracker_write_remove); - librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, tracker_write_update); - - world.data = zpl_malloc(sizeof(uint8_t)*world.size); - - if (!world.data) { - return WORLD_ERROR_OUTOFMEM; - } - - world.ecs = ecs_init(); - - ECS_IMPORT(world.ecs, Components); - ECS_IMPORT(world.ecs, Systems); - world.ecs_update = ecs_query_new(world.ecs, "components.ClientInfo, components.Position"); - world.chunk_mapping = zpl_malloc(sizeof(ecs_entity_t)*zpl_square(chunk_amount)); - world.block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount)); - world.outer_block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount)); - world_snapshot_init(&streamer_snapshot, zpl_heap()); - - int32_t world_build_status = worldgen_test(&world); - ZPL_ASSERT(world_build_status >= 0); - - for (int i = 0; i < world.chunk_amount * world.chunk_amount; ++i) { - ecs_entity_t e = ecs_new(world.ecs, 0); - ecs_set(world.ecs, e, Classify, {.id = EKIND_CHUNK }); - Chunk *chunk = ecs_get_mut(world.ecs, e, Chunk, NULL); - librg_entity_track(world.tracker, e); - librg_entity_chunk_set(world.tracker, e, i); - librg_chunk_to_chunkpos(world.tracker, i, &chunk->x, &chunk->y, NULL); - world.chunk_mapping[i] = e; - world.block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size)); - world.outer_block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size)); - chunk->id = i; - chunk->is_dirty = false; - - for (int y = 0; y < chunk_size; y += 1) { - for (int x = 0; x < chunk_size; x += 1) { - int chk_x = chunk->x * chunk_size; - int chk_y = chunk->y * chunk_size; - - uint8_t *c = &world.block_mapping[i][(y*chunk_size)+x]; - *c = world.data[(chk_y+y)*world.dim + (chk_x+x)]; - - c = &world.outer_block_mapping[i][(y*chunk_size)+x]; - *c = 0; - } - } - } - - zpl_mfree(world.data); - world.data = NULL; - - zpl_printf("[INFO] Created a new server world\n"); - - return world_build_status; -} - -int32_t world_destroy(void) { - librg_world_destroy(world.tracker); - ecs_fini(world.ecs); - zpl_mfree(world.chunk_mapping); - for (int i = 0; i < zpl_square(world.chunk_amount); i+=1) { - zpl_mfree(world.block_mapping[i]); - zpl_mfree(world.outer_block_mapping[i]); - } - zpl_mfree(world.block_mapping); - zpl_mfree(world.outer_block_mapping); - world_snapshot_destroy(&streamer_snapshot); - zpl_memset(&world, 0, sizeof(world)); - zpl_printf("[INFO] World was destroyed.\n"); - return WORLD_ERROR_NONE; -} - -#define WORLD_LIBRG_BUFSIZ 2000000 - -static void world_tracker_update(uint8_t ticker, uint32_t freq, uint8_t radius) { - if (world.tracker_update[ticker] > zpl_time_rel_ms()) return; - world.tracker_update[ticker] = zpl_time_rel_ms() + freq; - - profile(PROF_WORLD_WRITE) { - ecs_iter_t it = ecs_query_iter(world.ecs_update); - static char buffer[WORLD_LIBRG_BUFSIZ] = {0}; - world.active_layer_id = ticker; - - while (ecs_query_next(&it)) { - ClientInfo *p = ecs_column(&it, ClientInfo, 1); - - for (int i = 0; i < it.count; i++) { - size_t datalen = WORLD_LIBRG_BUFSIZ; - - // TODO(zaklaus): SUPER TEMPORARY HOT !!! simulate variable radius queries - { - librg_entity_radius_set(world_tracker(), it.entities[i], radius); - } - // TODO(zaklaus): push radius once librg patch comes in - int32_t result = librg_world_write(world_tracker(), it.entities[i], buffer, &datalen, NULL); - - if (result > 0) { - zpl_printf("[info] buffer size was not enough, please increase it by at least: %d\n", result); - } else if (result < 0) { - zpl_printf("[error] an error happened writing the world %d\n", result); - } - - pkt_send_librg_update((uint64_t)p[i].peer, p[i].view_id, ticker, buffer, datalen); - } - } - - // NOTE(zaklaus): clear out our streaming snapshot - // TODO(zaklaus): move this to zpl - { - zpl_array_clear(streamer_snapshot.hashes); - zpl_array_clear(streamer_snapshot.entries); - } - } -} - -int32_t world_update() { - profile (PROF_UPDATE_SYSTEMS) { - ecs_progress(world.ecs, 0.0f); - } - - uint32_t fast_ms = WORLD_TRACKER_UPDATE_FAST_MS; - uint32_t normal_ms = WORLD_TRACKER_UPDATE_NORMAL_MS; - uint32_t slow_ms = WORLD_TRACKER_UPDATE_SLOW_MS; - - if (game_get_kind() != GAMEKIND_SINGLE) { - fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS; - normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS; - slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS; - } - - world_tracker_update(0, fast_ms, 2); - world_tracker_update(1, normal_ms, 4); - world_tracker_update(2, slow_ms, 6); - - debug_replay_update(); - return 0; -} - -int32_t world_read(void* data, uint32_t datalen, void *udata) { - if (world.reader_proc) { - return world.reader_proc(data, datalen, udata); - } - return -1; -} - -int32_t world_write(pkt_header *pkt, void *udata) { - if (world.writer_proc) { - return world.writer_proc(pkt, udata); - } - return -1; -} - -uint32_t world_buf(uint8_t const **ptr, uint32_t *width) { - ZPL_ASSERT_NOT_NULL(world.data); - ZPL_ASSERT_NOT_NULL(ptr); - *ptr = world.data; - if (width) *width = world.dim; - return world.size; -} - -uint32_t world_seed(void) { - return world.seed; -} - -ecs_world_t * world_ecs() { - if (world.ecs_stage != NULL) { - return world.ecs_stage; - } - return world.ecs; -} - -void world_set_stage(ecs_world_t *ecs) { - world.ecs_stage = ecs; -} - -librg_world *world_tracker() { - return world.tracker; -} - -uint16_t world_chunk_size(void) { - return world.chunk_size; -} - -uint16_t world_chunk_amount(void) { - return world.chunk_amount; -} - -uint16_t world_dim(void) { - return WORLD_BLOCK_SIZE * world.chunk_size * world.chunk_amount; -} - -ecs_entity_t world_chunk_mapping(librg_chunk id) { - ZPL_ASSERT(id >= 0 && id < zpl_square(world.chunk_amount)); - return world.chunk_mapping[id]; -} - -world_block_lookup world_block_from_realpos(float x, float y) { - x = zpl_clamp(x, 0, world_dim()-1); - y = zpl_clamp(y, 0, world_dim()-1); - librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0); - ecs_entity_t e = world.chunk_mapping[chunk_id]; - int32_t size = world.chunk_size * WORLD_BLOCK_SIZE; - int16_t chunk_x, chunk_y; - librg_chunk_to_chunkpos(world.tracker, chunk_id, &chunk_x, &chunk_y, NULL); - - // NOTE(zaklaus): pos relative to chunk - float chx = x - chunk_x * size; - float chy = y - chunk_y * size; - - uint32_t bx = (uint32_t)chx / WORLD_BLOCK_SIZE; - uint32_t by = (uint32_t)chy / WORLD_BLOCK_SIZE; - uint32_t block_idx = (by*world.chunk_size)+bx; - uint8_t block_id = world.outer_block_mapping[chunk_id][block_idx]; - if (block_id == 0) { - block_id = world.block_mapping[chunk_id][block_idx]; - } - - // NOTE(zaklaus): pos relative to block's center - float box = chx - bx * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f; - float boy = chy - by * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f; - - world_block_lookup lookup = { - .id = block_idx, - .block_id = block_id, - .chunk_id = chunk_id, - .chunk_e = e, - .ox = box, - .oy = boy, - }; - - return lookup; -} - -world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) { - uint8_t block_id = world.outer_block_mapping[id][block_idx]; - if (block_id == 0) { - block_id = world.block_mapping[id][block_idx]; - } - - world_block_lookup lookup = { - .id = block_idx, - .block_id = block_id, - .chunk_id = id, - .chunk_e = world.chunk_mapping[id], - }; - - return lookup; -} - -int64_t world_chunk_from_realpos(float x, float y) { - librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0); - return world.chunk_mapping[chunk_id]; -} - -int64_t world_chunk_from_entity(ecs_entity_t id) { - return librg_entity_chunk_get(world.tracker, id); -} - -void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id) { - ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); - world.block_mapping[id][block_idx] = block_id; - world_chunk_mark_dirty(world.chunk_mapping[id]); -} - -bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id) { - ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); - if (world.outer_block_mapping[id][block_idx] != 0 && block_id != 0) return false; - world.outer_block_mapping[id][block_idx] = block_id; - world_chunk_mark_dirty(world.chunk_mapping[id]); - return true; -} - -uint8_t *world_chunk_get_blocks(int64_t id) { - return world.block_mapping[id]; -} - -void world_chunk_mark_dirty(ecs_entity_t e) { - bool was_added=false; - Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added); - ZPL_ASSERT(!was_added); - if (chunk) chunk->is_dirty = true; -} - -uint8_t world_chunk_is_dirty(ecs_entity_t e) { - bool was_added=false; - Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added); - ZPL_ASSERT(!was_added); - if (chunk) return chunk->is_dirty; - return false; -} - -int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len) { - ZPL_ASSERT_NOT_NULL(ents_len); - static int64_t ents[UINT16_MAX]; - *ents_len = UINT16_MAX; - librg_world_fetch_chunk(world.tracker, chunk_id, ents, ents_len); - return ents; -} - -int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len) { - return world_chunk_fetch_entities(librg_chunk_from_realpos(world.tracker, x, y, 0), ents_len); -} - -int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius) { - ZPL_ASSERT_NOT_NULL(ents_len); - static int64_t ents[UINT16_MAX]; - *ents_len = UINT16_MAX; - librg_entity_radius_set(world.tracker, e, radius); - librg_world_query(world.tracker, e, ents, ents_len); - return ents; -} - -uint8_t world_entity_valid(ecs_entity_t e) { - if (!e) return false; - return ecs_is_alive(world_ecs(), e); -} +#include "zpl.h" +#include "librg.h" +#include "modules/components.h" +#include "modules/systems.h" +#include "world/world.h" +#include "entity_view.h" +#include "debug_replay.h" +#include "items.h" +#include "world/worldgen/worldgen.h" +#include "platform.h" +#include "profiler.h" +#include "game.h" + +#include "packets/pkt_send_librg_update.h" + +ZPL_TABLE(static, world_snapshot, world_snapshot_, entity_view); + +static world_data world = {0}; +static world_snapshot streamer_snapshot; + +entity_view world_build_entity_view(int64_t e) { + entity_view *cached_ev = world_snapshot_get(&streamer_snapshot, e); + if (cached_ev) return *cached_ev; + + entity_view view = {0}; + + const Classify *classify = ecs_get(world_ecs(), e, Classify); + ZPL_ASSERT(classify); + + view.kind = classify->id; + + const Position *pos = ecs_get(world_ecs(), e, Position); + if (pos) { + view.x = pos->x; + view.y = pos->y; + } + + const Velocity *vel = ecs_get(world_ecs(), e, Velocity); + if (vel) { + view.flag |= EFLAG_INTERP; + view.vx = vel->x; + view.vy = vel->y; + } + + const Health *health = ecs_get(world_ecs(), e, Health); + if (health) { + view.hp = health->hp; + view.max_hp = health->max_hp; + } + + if (ecs_get(world_ecs(), e, Vehicle)) { + Vehicle const* veh = ecs_get(world_ecs(), e, Vehicle); + view.heading = veh->heading; + } + + if (ecs_get(world_ecs(), e, ItemDrop)) { + ItemDrop const* dr = ecs_get(world_ecs(), e, ItemDrop); + view.asset = item_get_asset(dr->kind); + view.quantity = dr->quantity; + } + + view.inside_vehicle = ecs_get(world_ecs(), e, IsInVehicle) != 0 ? true : false; + + Inventory *inv = 0; + if ((inv = ecs_get_mut_if(world_ecs(), e, Inventory))) { + view.has_items = true; + + for (int i = 0; i < ITEMS_INVENTORY_SIZE; i += 1) { + view.items[i] = inv->items[i]; + } + + const Input *in = ecs_get(world_ecs(), e, Input); + if (in) + view.selected_item = in->selected_item; + } + + Chunk *chpos = 0; + if ((chpos = ecs_get_mut_if(world_ecs(), e, Chunk))) { + view.x = chpos->x; + view.y = chpos->y; + view.blocks_used = 1; + view.is_dirty = chpos->is_dirty; + chpos->is_dirty = false; + + for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) { + view.blocks[i] = world.block_mapping[chpos->id][i]; + } + + for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) { + view.outer_blocks[i] = world.outer_block_mapping[chpos->id][i]; + } + } + + world_snapshot_set(&streamer_snapshot, e, view); + return view; +} + +int32_t tracker_write_create(librg_world *w, librg_event *e) { + int64_t entity_id = librg_event_entity_get(w, e); +#ifdef WORLD_LAYERING + if (world.active_layer_id != WORLD_TRACKER_LAYERS-1) { + // NOTE(zaklaus): reject updates from smaller layers + return LIBRG_WRITE_REJECT; + } +#endif + size_t actual_length = librg_event_size_get(w, e); + char *buffer = librg_event_buffer_get(w, e); + + return (int32_t)entity_view_pack_struct(buffer, actual_length, world_build_entity_view(entity_id)); +} + +int32_t tracker_write_remove(librg_world *w, librg_event *e) { + (void)e; + (void)w; +#ifdef WORLD_LAYERING + if (world.active_layer_id != WORLD_TRACKER_LAYERS-1) { + // NOTE(zaklaus): reject updates from smaller layers + return LIBRG_WRITE_REJECT; + } +#endif + return 0; +} + +int32_t tracker_write_update(librg_world *w, librg_event *e) { + int64_t entity_id = librg_event_entity_get(w, e); + size_t actual_length = librg_event_size_get(w, e); + char *buffer = librg_event_buffer_get(w, e); + entity_view view = world_build_entity_view(entity_id); + + // NOTE(zaklaus): exclude chunks from updates as they never move + { + if (view.kind == EKIND_CHUNK && !view.is_dirty) { + return LIBRG_WRITE_REJECT; + } + } + + return (int32_t)entity_view_pack_struct(buffer, actual_length, view); +} + +void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc) { + world.reader_proc = reader_proc; + world.writer_proc = writer_proc; +} + +int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) { + if (world.data) { + return 0; + } + + world.seed = seed; + world.chunk_size = chunk_size; + world.chunk_amount = chunk_amount; + + world.dim = (world.chunk_size * world.chunk_amount); + world.size = world.dim * world.dim; + + if (world.tracker == NULL) { + world.tracker = librg_world_create(); + } + + if (world.tracker == NULL) { + zpl_printf("[ERROR] An error occurred while trying to create a server world.\n"); + return WORLD_ERROR_TRACKER_FAILED; + } + + /* config our world grid */ + librg_config_chunksize_set(world.tracker, WORLD_BLOCK_SIZE * world.chunk_size, WORLD_BLOCK_SIZE * world.chunk_size, 0); + librg_config_chunkamount_set(world.tracker, world.chunk_amount, world.chunk_amount, 0); + librg_config_chunkoffset_set(world.tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG); + + librg_event_set(world.tracker, LIBRG_WRITE_CREATE, tracker_write_create); + librg_event_set(world.tracker, LIBRG_WRITE_REMOVE, tracker_write_remove); + librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, tracker_write_update); + + world.data = zpl_malloc(sizeof(uint8_t)*world.size); + + if (!world.data) { + return WORLD_ERROR_OUTOFMEM; + } + + world.ecs = ecs_init(); + + ECS_IMPORT(world.ecs, Components); + ECS_IMPORT(world.ecs, Systems); + world.ecs_update = ecs_query_new(world.ecs, "components.ClientInfo, components.Position"); + world.chunk_mapping = zpl_malloc(sizeof(ecs_entity_t)*zpl_square(chunk_amount)); + world.block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount)); + world.outer_block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount)); + world_snapshot_init(&streamer_snapshot, zpl_heap()); + + int32_t world_build_status = worldgen_test(&world); + ZPL_ASSERT(world_build_status >= 0); + + for (int i = 0; i < world.chunk_amount * world.chunk_amount; ++i) { + ecs_entity_t e = ecs_new(world.ecs, 0); + ecs_set(world.ecs, e, Classify, {.id = EKIND_CHUNK }); + Chunk *chunk = ecs_get_mut(world.ecs, e, Chunk, NULL); + librg_entity_track(world.tracker, e); + librg_entity_chunk_set(world.tracker, e, i); + librg_chunk_to_chunkpos(world.tracker, i, &chunk->x, &chunk->y, NULL); + world.chunk_mapping[i] = e; + world.block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size)); + world.outer_block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size)); + chunk->id = i; + chunk->is_dirty = false; + + for (int y = 0; y < chunk_size; y += 1) { + for (int x = 0; x < chunk_size; x += 1) { + int chk_x = chunk->x * chunk_size; + int chk_y = chunk->y * chunk_size; + + uint8_t *c = &world.block_mapping[i][(y*chunk_size)+x]; + *c = world.data[(chk_y+y)*world.dim + (chk_x+x)]; + + c = &world.outer_block_mapping[i][(y*chunk_size)+x]; + *c = 0; + } + } + } + + zpl_mfree(world.data); + world.data = NULL; + + zpl_printf("[INFO] Created a new server world\n"); + + return world_build_status; +} + +int32_t world_destroy(void) { + librg_world_destroy(world.tracker); + ecs_fini(world.ecs); + zpl_mfree(world.chunk_mapping); + for (int i = 0; i < zpl_square(world.chunk_amount); i+=1) { + zpl_mfree(world.block_mapping[i]); + zpl_mfree(world.outer_block_mapping[i]); + } + zpl_mfree(world.block_mapping); + zpl_mfree(world.outer_block_mapping); + world_snapshot_destroy(&streamer_snapshot); + zpl_memset(&world, 0, sizeof(world)); + zpl_printf("[INFO] World was destroyed.\n"); + return WORLD_ERROR_NONE; +} + +#define WORLD_LIBRG_BUFSIZ 2000000 + +static void world_tracker_update(uint8_t ticker, uint32_t freq, uint8_t radius) { + if (world.tracker_update[ticker] > zpl_time_rel_ms()) return; + world.tracker_update[ticker] = zpl_time_rel_ms() + freq; + + profile(PROF_WORLD_WRITE) { + ecs_iter_t it = ecs_query_iter(world.ecs_update); + static char buffer[WORLD_LIBRG_BUFSIZ] = {0}; + world.active_layer_id = ticker; + + while (ecs_query_next(&it)) { + ClientInfo *p = ecs_column(&it, ClientInfo, 1); + + for (int i = 0; i < it.count; i++) { + size_t datalen = WORLD_LIBRG_BUFSIZ; + + // TODO(zaklaus): SUPER TEMPORARY HOT !!! simulate variable radius queries + { + librg_entity_radius_set(world_tracker(), it.entities[i], radius); + } + // TODO(zaklaus): push radius once librg patch comes in + int32_t result = librg_world_write(world_tracker(), it.entities[i], buffer, &datalen, NULL); + + if (result > 0) { + zpl_printf("[info] buffer size was not enough, please increase it by at least: %d\n", result); + } else if (result < 0) { + zpl_printf("[error] an error happened writing the world %d\n", result); + } + + pkt_send_librg_update((uint64_t)p[i].peer, p[i].view_id, ticker, buffer, datalen); + } + } + + // NOTE(zaklaus): clear out our streaming snapshot + // TODO(zaklaus): move this to zpl + { + zpl_array_clear(streamer_snapshot.hashes); + zpl_array_clear(streamer_snapshot.entries); + } + } +} + +int32_t world_update() { + profile (PROF_UPDATE_SYSTEMS) { + ecs_progress(world.ecs, 0.0f); + } + + uint32_t fast_ms = WORLD_TRACKER_UPDATE_FAST_MS; + uint32_t normal_ms = WORLD_TRACKER_UPDATE_NORMAL_MS; + uint32_t slow_ms = WORLD_TRACKER_UPDATE_SLOW_MS; + + if (game_get_kind() != GAMEKIND_SINGLE) { + fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS; + normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS; + slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS; + } + + world_tracker_update(0, fast_ms, 2); + world_tracker_update(1, normal_ms, 4); + world_tracker_update(2, slow_ms, 6); + + debug_replay_update(); + return 0; +} + +int32_t world_read(void* data, uint32_t datalen, void *udata) { + if (world.reader_proc) { + return world.reader_proc(data, datalen, udata); + } + return -1; +} + +int32_t world_write(pkt_header *pkt, void *udata) { + if (world.writer_proc) { + return world.writer_proc(pkt, udata); + } + return -1; +} + +uint32_t world_buf(uint8_t const **ptr, uint32_t *width) { + ZPL_ASSERT_NOT_NULL(world.data); + ZPL_ASSERT_NOT_NULL(ptr); + *ptr = world.data; + if (width) *width = world.dim; + return world.size; +} + +uint32_t world_seed(void) { + return world.seed; +} + +ecs_world_t * world_ecs() { + if (world.ecs_stage != NULL) { + return world.ecs_stage; + } + return world.ecs; +} + +void world_set_stage(ecs_world_t *ecs) { + world.ecs_stage = ecs; +} + +librg_world *world_tracker() { + return world.tracker; +} + +uint16_t world_chunk_size(void) { + return world.chunk_size; +} + +uint16_t world_chunk_amount(void) { + return world.chunk_amount; +} + +uint16_t world_dim(void) { + return WORLD_BLOCK_SIZE * world.chunk_size * world.chunk_amount; +} + +ecs_entity_t world_chunk_mapping(librg_chunk id) { + ZPL_ASSERT(id >= 0 && id < zpl_square(world.chunk_amount)); + return world.chunk_mapping[id]; +} + +world_block_lookup world_block_from_realpos(float x, float y) { + x = zpl_clamp(x, 0, world_dim()-1); + y = zpl_clamp(y, 0, world_dim()-1); + librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0); + ecs_entity_t e = world.chunk_mapping[chunk_id]; + int32_t size = world.chunk_size * WORLD_BLOCK_SIZE; + int16_t chunk_x, chunk_y; + librg_chunk_to_chunkpos(world.tracker, chunk_id, &chunk_x, &chunk_y, NULL); + + // NOTE(zaklaus): pos relative to chunk + float chx = x - chunk_x * size; + float chy = y - chunk_y * size; + + uint32_t bx = (uint32_t)chx / WORLD_BLOCK_SIZE; + uint32_t by = (uint32_t)chy / WORLD_BLOCK_SIZE; + uint32_t block_idx = (by*world.chunk_size)+bx; + uint8_t block_id = world.outer_block_mapping[chunk_id][block_idx]; + if (block_id == 0) { + block_id = world.block_mapping[chunk_id][block_idx]; + } + + // NOTE(zaklaus): pos relative to block's center + float box = chx - bx * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f; + float boy = chy - by * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f; + + world_block_lookup lookup = { + .id = block_idx, + .block_id = block_id, + .chunk_id = chunk_id, + .chunk_e = e, + .ox = box, + .oy = boy, + }; + + return lookup; +} + +world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) { + uint8_t block_id = world.outer_block_mapping[id][block_idx]; + if (block_id == 0) { + block_id = world.block_mapping[id][block_idx]; + } + + world_block_lookup lookup = { + .id = block_idx, + .block_id = block_id, + .chunk_id = id, + .chunk_e = world.chunk_mapping[id], + }; + + return lookup; +} + +int64_t world_chunk_from_realpos(float x, float y) { + librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0); + return world.chunk_mapping[chunk_id]; +} + +int64_t world_chunk_from_entity(ecs_entity_t id) { + return librg_entity_chunk_get(world.tracker, id); +} + +void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id) { + ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); + world.block_mapping[id][block_idx] = block_id; + world_chunk_mark_dirty(world.chunk_mapping[id]); +} + +bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id) { + ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); + if (world.outer_block_mapping[id][block_idx] != 0 && block_id != 0) return false; + world.outer_block_mapping[id][block_idx] = block_id; + world_chunk_mark_dirty(world.chunk_mapping[id]); + return true; +} + +uint8_t *world_chunk_get_blocks(int64_t id) { + return world.block_mapping[id]; +} + +void world_chunk_mark_dirty(ecs_entity_t e) { + bool was_added=false; + Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added); + ZPL_ASSERT(!was_added); + if (chunk) chunk->is_dirty = true; +} + +uint8_t world_chunk_is_dirty(ecs_entity_t e) { + bool was_added=false; + Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added); + ZPL_ASSERT(!was_added); + if (chunk) return chunk->is_dirty; + return false; +} + +int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len) { + ZPL_ASSERT_NOT_NULL(ents_len); + static int64_t ents[UINT16_MAX]; + *ents_len = UINT16_MAX; + librg_world_fetch_chunk(world.tracker, chunk_id, ents, ents_len); + return ents; +} + +int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len) { + return world_chunk_fetch_entities(librg_chunk_from_realpos(world.tracker, x, y, 0), ents_len); +} + +int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius) { + ZPL_ASSERT_NOT_NULL(ents_len); + static int64_t ents[UINT16_MAX]; + *ents_len = UINT16_MAX; + librg_entity_radius_set(world.tracker, e, radius); + librg_world_query(world.tracker, e, ents, ents_len); + return ents; +} + +uint8_t world_entity_valid(ecs_entity_t e) { + if (!e) return false; + return ecs_is_alive(world_ecs(), e); +} diff --git a/code/game/src/world/world.h b/code/game/src/world/world.h index ae388be..77e096e 100644 --- a/code/game/src/world/world.h +++ b/code/game/src/world/world.h @@ -1,94 +1,94 @@ -#pragma once -#include "system.h" -#include "librg.h" -#include "packet.h" -#include "flecs/flecs.h" -#include "flecs/flecs_meta.h" -#include "modules/components.h" - -#define WORLD_ERROR_NONE +0x0000 -#define WORLD_ERROR_OUTOFMEM -0x0001 -#define WORLD_ERROR_INVALID_BLOCKS -0x0002 -#define WORLD_ERROR_INVALID_DIMENSIONS -0x0003 -#define WORLD_ERROR_INVALID_BUFFER -0x0004 -#define WORLD_ERROR_TRACKER_FAILED -0x0005 - -#define WORLD_LAYERING 0 -#define WORLD_TRACKER_LAYERS 3 -#define WORLD_TRACKER_UPDATE_FAST_MS 10 -#define WORLD_TRACKER_UPDATE_NORMAL_MS 50 -#define WORLD_TRACKER_UPDATE_SLOW_MS 100 -#define WORLD_TRACKER_UPDATE_MP_FAST_MS 50 -#define WORLD_TRACKER_UPDATE_MP_NORMAL_MS 150 -#define WORLD_TRACKER_UPDATE_MP_SLOW_MS 300 -#define WORLD_BLOCK_SIZE 64 - -#define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen, void *udata) -typedef WORLD_PKT_READER(world_pkt_reader_proc); - -#define WORLD_PKT_WRITER(name) int32_t name(pkt_header *pkt, void *udata) -typedef WORLD_PKT_WRITER(world_pkt_writer_proc); - -typedef struct { - uint8_t *data; - uint32_t seed; - uint32_t size; - uint16_t chunk_size; - uint16_t chunk_amount; - uint8_t **block_mapping; - uint8_t **outer_block_mapping; - uint16_t dim; - uint64_t tracker_update[3]; - uint8_t active_layer_id; - ecs_world_t *ecs; - ecs_world_t *ecs_stage; - ecs_query_t *ecs_update; - ecs_entity_t *chunk_mapping; - librg_world *tracker; - world_pkt_reader_proc *reader_proc; - world_pkt_writer_proc *writer_proc; -} world_data; - -void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc); -int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount); -int32_t world_destroy(void); -int32_t world_update(void); - -int32_t world_read(void* data, uint32_t datalen, void *udata); -int32_t world_write(pkt_header *pkt, void *udata); - -uint32_t world_buf(uint8_t const **ptr, uint32_t *width); -uint32_t world_seed(void); -ecs_world_t *world_ecs(void); -void world_set_stage(ecs_world_t *ecs); -librg_world *world_tracker(void); - -uint16_t world_chunk_size(void); -uint16_t world_chunk_amount(void); -uint16_t world_dim(void); -ecs_entity_t world_chunk_mapping(librg_chunk id); - -typedef struct { - uint32_t id; - uint8_t block_id; - ecs_entity_t chunk_e; - int64_t chunk_id; - float ox, oy; -} world_block_lookup; - -world_block_lookup world_block_from_realpos(float x, float y); -world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx); -int64_t world_chunk_from_realpos(float x, float y); -int64_t world_chunk_from_entity(ecs_entity_t id); -void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id); -bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id); -uint8_t *world_chunk_get_blocks(int64_t id); -void world_chunk_mark_dirty(ecs_entity_t e); -uint8_t world_chunk_is_dirty(ecs_entity_t e); - -// NOTE(zaklaus): Uses locally persistent buffer !! -int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len); -int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len); -int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius); - +#pragma once +#include "system.h" +#include "librg.h" +#include "packet.h" +#include "flecs/flecs.h" +#include "flecs/flecs_meta.h" +#include "modules/components.h" + +#define WORLD_ERROR_NONE +0x0000 +#define WORLD_ERROR_OUTOFMEM -0x0001 +#define WORLD_ERROR_INVALID_BLOCKS -0x0002 +#define WORLD_ERROR_INVALID_DIMENSIONS -0x0003 +#define WORLD_ERROR_INVALID_BUFFER -0x0004 +#define WORLD_ERROR_TRACKER_FAILED -0x0005 + +#define WORLD_LAYERING 0 +#define WORLD_TRACKER_LAYERS 3 +#define WORLD_TRACKER_UPDATE_FAST_MS 10 +#define WORLD_TRACKER_UPDATE_NORMAL_MS 50 +#define WORLD_TRACKER_UPDATE_SLOW_MS 100 +#define WORLD_TRACKER_UPDATE_MP_FAST_MS 50 +#define WORLD_TRACKER_UPDATE_MP_NORMAL_MS 150 +#define WORLD_TRACKER_UPDATE_MP_SLOW_MS 300 +#define WORLD_BLOCK_SIZE 64 + +#define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen, void *udata) +typedef WORLD_PKT_READER(world_pkt_reader_proc); + +#define WORLD_PKT_WRITER(name) int32_t name(pkt_header *pkt, void *udata) +typedef WORLD_PKT_WRITER(world_pkt_writer_proc); + +typedef struct { + uint8_t *data; + uint32_t seed; + uint32_t size; + uint16_t chunk_size; + uint16_t chunk_amount; + uint8_t **block_mapping; + uint8_t **outer_block_mapping; + uint16_t dim; + uint64_t tracker_update[3]; + uint8_t active_layer_id; + ecs_world_t *ecs; + ecs_world_t *ecs_stage; + ecs_query_t *ecs_update; + ecs_entity_t *chunk_mapping; + librg_world *tracker; + world_pkt_reader_proc *reader_proc; + world_pkt_writer_proc *writer_proc; +} world_data; + +void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc); +int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount); +int32_t world_destroy(void); +int32_t world_update(void); + +int32_t world_read(void* data, uint32_t datalen, void *udata); +int32_t world_write(pkt_header *pkt, void *udata); + +uint32_t world_buf(uint8_t const **ptr, uint32_t *width); +uint32_t world_seed(void); +ecs_world_t *world_ecs(void); +void world_set_stage(ecs_world_t *ecs); +librg_world *world_tracker(void); + +uint16_t world_chunk_size(void); +uint16_t world_chunk_amount(void); +uint16_t world_dim(void); +ecs_entity_t world_chunk_mapping(librg_chunk id); + +typedef struct { + uint32_t id; + uint8_t block_id; + ecs_entity_t chunk_e; + int64_t chunk_id; + float ox, oy; +} world_block_lookup; + +world_block_lookup world_block_from_realpos(float x, float y); +world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx); +int64_t world_chunk_from_realpos(float x, float y); +int64_t world_chunk_from_entity(ecs_entity_t id); +void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id); +bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id); +uint8_t *world_chunk_get_blocks(int64_t id); +void world_chunk_mark_dirty(ecs_entity_t e); +uint8_t world_chunk_is_dirty(ecs_entity_t e); + +// NOTE(zaklaus): Uses locally persistent buffer !! +int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len); +int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len); +int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius); + uint8_t world_entity_valid(ecs_entity_t e); \ No newline at end of file diff --git a/code/modules/modules/systems.c b/code/modules/modules/systems.c index f8f4c78..58c12f7 100644 --- a/code/modules/modules/systems.c +++ b/code/modules/modules/systems.c @@ -8,7 +8,7 @@ #include "game.h" #define PHY_BLOCK_COLLISION 1 -#define PHY_WALK_DRAG 0.12 +#define PHY_WALK_DRAG 4.23f #define PHY_LOOKAHEAD(x) (zpl_sign(x)*16.0f) #include "source/system_onfoot.c" @@ -29,7 +29,7 @@ void IntegratePositions(ecs_iter_t *it) { for (int i = 0; i < it->count; i++) { // NOTE(zaklaus): world bounds { - double w = (double)world_dim(); + float w = (float)world_dim(); p[i].x = zpl_clamp(p[i].x, 0, w-1); p[i].y = zpl_clamp(p[i].y, 0, w-1); } @@ -127,8 +127,8 @@ void ApplyWorldDragOnVelocity(ecs_iter_t *it) { world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y); float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f); float friction = blocks_get_friction(lookup.block_id); - v[i].x = zpl_lerp(v[i].x, 0.0f, PHY_WALK_DRAG*drag*friction); - v[i].y = zpl_lerp(v[i].y, 0.0f, PHY_WALK_DRAG*drag*friction); + v[i].x = zpl_lerp(v[i].x, 0.0f, PHY_WALK_DRAG*drag*friction*it->delta_time); + v[i].y = zpl_lerp(v[i].y, 0.0f, PHY_WALK_DRAG*drag*friction*it->delta_time); } } diff --git a/code/modules/modules/systems.h b/code/modules/modules/systems.h index 88cf392..6e25fcb 100644 --- a/code/modules/modules/systems.h +++ b/code/modules/modules/systems.h @@ -1,10 +1,11 @@ -#pragma once -#include "flecs/flecs.h" - -typedef struct { - // NOTE(zaklaus): Public systems are exposed here -} Systems; - -#define SystemsImportHandles(handles) (void)(handles) - -void SystemsImport(ecs_world_t *ecs); +#pragma once +#include "flecs/flecs.h" + +typedef struct { + // NOTE(zaklaus): Public systems are exposed here + int32_t _unused; +} Systems; + +#define SystemsImportHandles(handles) (void)(handles) + +void SystemsImport(ecs_world_t *ecs); diff --git a/code/modules/source/system_demo.c b/code/modules/source/system_demo.c index 23d7fe3..e798982 100644 --- a/code/modules/source/system_demo.c +++ b/code/modules/source/system_demo.c @@ -1,12 +1,12 @@ - -#define DEMO_NPC_MOVE_SPEED 50 -#define DEMO_NPC_STEER_SPEED 30 - -void DemoNPCMoveAround(ecs_iter_t *it) { - Velocity *v = ecs_column(it, Velocity, 1); - for (int i = 0; i < it->count; i++) { - float d = zpl_quake_rsqrt(v[i].x*v[i].x + v[i].y*v[i].y); - v[i].x += (v[i].x*d*DEMO_NPC_MOVE_SPEED + zpl_cos(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED); - v[i].y += (v[i].y*d*DEMO_NPC_MOVE_SPEED + zpl_sin(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED); - } -} + +#define DEMO_NPC_MOVE_SPEED 500 +#define DEMO_NPC_STEER_SPEED 300 + +void DemoNPCMoveAround(ecs_iter_t *it) { + Velocity *v = ecs_column(it, Velocity, 1); + for (int i = 0; i < it->count; i++) { + float d = zpl_quake_rsqrt(v[i].x*v[i].x + v[i].y*v[i].y); + v[i].x += (v[i].x*d*DEMO_NPC_MOVE_SPEED*it->delta_time + zpl_cos(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED*it->delta_time); + v[i].y += (v[i].y*d*DEMO_NPC_MOVE_SPEED*it->delta_time + zpl_sin(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED*it->delta_time); + } +} diff --git a/code/modules/source/system_onfoot.c b/code/modules/source/system_onfoot.c index 46be5d8..43bab03 100644 --- a/code/modules/source/system_onfoot.c +++ b/code/modules/source/system_onfoot.c @@ -1,4 +1,4 @@ -#define PLR_MOVE_SPEED 30.0 +#define PLR_MOVE_SPEED 800.0 #define PLR_MOVE_SPEED_MULT 1.5 void MovementImpulse(ecs_iter_t *it) { @@ -10,7 +10,7 @@ void MovementImpulse(ecs_iter_t *it) { world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y); float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f); double speed = PLR_MOVE_SPEED * (in[i].sprint ? PLR_MOVE_SPEED_MULT : 1.0); - v[i].x += in[i].x*speed*drag; - v[i].y -= in[i].y*speed*drag; + v[i].x += in[i].x*speed*drag*it->delta_time; + v[i].y -= in[i].y*speed*drag*it->delta_time; } } diff --git a/code/modules/source/system_vehicle.c b/code/modules/source/system_vehicle.c index 8c3a0d5..1ef12b1 100644 --- a/code/modules/source/system_vehicle.c +++ b/code/modules/source/system_vehicle.c @@ -1,191 +1,192 @@ -#include "debug_draw.h" - -#define VEH_ENTER_RADIUS 45.0f - -void LeaveVehicle(ecs_iter_t *it) { - Input *in = ecs_column(it, Input, 1); - IsInVehicle *vehp = ecs_column(it, IsInVehicle, 2); - Velocity *v = ecs_column(it, Velocity, 3); - - for (int i = 0; i < it->count; i++) { - if (!in[i].use) continue; - - Vehicle *veh = 0; - if ((veh = ecs_get_mut_if(it->world, vehp->veh, Vehicle))) { - for (int k = 0; k < 4; k++) { - if (veh->seats[k] == it->entities[i]) { - veh->seats[k] = 0; - break; - } - } - - in[i].use = false; - ecs_remove(it->world, it->entities[i], IsInVehicle); - - // NOTE(zaklaus): push passenger out - { - float px = zpl_cos(veh->heading)*400.0f; - float py = zpl_sin(veh->heading)*400.0f; - v->x += py; - v->y -= px; - } - } else { - ZPL_PANIC("unreachable code"); - } - } -} - -void EnterVehicle(ecs_iter_t *it) { - Input *in = ecs_column(it, Input, 1); - Position *p = ecs_column(it, Position, 2); - - for (int i = 0; i < it->count; i++) { - if (!in[i].use) continue; - - size_t ents_count; - int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2); - bool has_entered_veh = false; - - for (size_t j = 0; j < ents_count; j++) { - Vehicle *veh = 0; - - if (has_entered_veh) break; - - if ((veh = ecs_get_mut_if(it->world, ents[j], Vehicle))) { - Position const* p2 = ecs_get(it->world, ents[j], Position); - - float dx = p2->x - p[i].x; - float dy = p2->y - p[i].y; - float range = zpl_sqrt(dx*dx + dy*dy); - if (range <= VEH_ENTER_RADIUS) { - for (int k = 0; k < 4; k++) { - if (veh->seats[k] != 0) continue; - - // NOTE(zaklaus): We can enter the vehicle, yay! - veh->seats[k] = it->entities[i]; - ecs_set(it->world, it->entities[i], IsInVehicle, { - .veh = ents[j] - }); - p[i] = *p2; - in[i].use = false; - has_entered_veh = true; - break; - } - } - } - } - } -} - -#define VEHICLE_FORCE 34.8f -#define VEHICLE_ACCEL 0.42f -#define VEHICLE_DECEL 0.28f -#define VEHICLE_STEER 3.89f -#define VEHICLE_BRAKE_FORCE 0.84f - -void VehicleHandling(ecs_iter_t *it) { - Vehicle *veh = ecs_column(it, Vehicle, 1); - Position *p = ecs_column(it, Position, 2); - Velocity *v = ecs_column(it, Velocity, 3); - - for (int i = 0; i < it->count; i++) { - Vehicle *car = &veh[i]; - - for (int j = 0; j < 4; j++) { - // NOTE(zaklaus): Perform seat cleanup - if (!world_entity_valid(veh[i].seats[j])) { - veh[i].seats[j] = 0; - continue; - } - - ecs_entity_t pe = veh[i].seats[j]; - - // NOTE(zaklaus): Handle driver input - if (j == 0) { - Input const* in = ecs_get(it->world, pe, Input); - - car->force += zpl_lerp(0.0f, in->y * VEHICLE_FORCE, VEHICLE_ACCEL*it->delta_time); - if (in->sprint) { - car->force = zpl_lerp(car->force, 0.0f, VEHICLE_BRAKE_FORCE*it->delta_time); - - if (zpl_abs(car->force) < 5.5f) - car->force = 0.0f; - } - car->steer *= 0.97f; - car->steer += (in->x * VEHICLE_STEER)*it->delta_time; - car->steer = zpl_clamp(car->steer, -40.0f, 40.0f); - } - } - - car->force = zpl_clamp(car->force, car->reverse_speed, car->speed); - - // NOTE(zaklaus): Vehicle physics - float fr_x = p[i].x + (car->wheel_base/2.0f) * zpl_cos(car->heading); - float fr_y = p[i].y + (car->wheel_base/2.0f) * zpl_sin(car->heading); - - float bk_x = p[i].x - (car->wheel_base/2.0f) * zpl_cos(car->heading); - float bk_y = p[i].y - (car->wheel_base/2.0f) * zpl_sin(car->heading); - - world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y); - float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f); - - bk_x += car->force * drag * zpl_cos(car->heading); - bk_y += car->force * drag * zpl_sin(car->heading); - fr_x += car->force * drag * zpl_cos(car->heading + zpl_to_radians(car->steer)); - fr_y += car->force * drag * zpl_sin(car->heading + zpl_to_radians(car->steer)); - - v[i].x += (fr_x + bk_x) / 2.0f - p[i].x; - v[i].y += (fr_y + bk_y) / 2.0f - p[i].y; - car->heading = zpl_arctan2(fr_y - bk_y, fr_x - bk_x); - - world_block_lookup lookahead = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y+PHY_LOOKAHEAD(v[i].y)); - uint32_t flags = blocks_get_flags(lookahead.block_id); - if (flags & BLOCK_FLAG_COLLISION) { - car->force = 0.0f; - } - - for (int j = 0; j < 4; j++) { - if (!world_entity_valid(veh[i].seats[j])) continue; - ecs_entity_t pe = veh[i].seats[j]; - - // NOTE(zaklaus): Update passenger position - { - Position *p2 = ecs_get_mut(it->world, pe, Position, NULL); - Velocity *v2 = ecs_get_mut(it->world, pe, Velocity, NULL); - *p2 = p[i]; - *v2 = v[i]; - } - } - - { - debug_v2 b2 = {p[i].x + zpl_cos(car->heading)*(car->wheel_base), p[i].y + zpl_sin(car->heading)*(car->wheel_base)}; - debug_push_line((debug_v2){p[i].x, p[i].y}, b2, 0x0000FFFF); - - // NOTE(zaklaus): force - { - float dx = zpl_cos(car->heading); - float dy = zpl_sin(car->heading); - debug_push_circle((debug_v2){p[i].x+dx*car->force, p[i].y+dy*car->force}, 5.0f, 0x00FF00FF); - } - - // NOTE(zaklaus): steer - { - float dx = zpl_sin(car->heading); - float dy = -zpl_cos(car->heading); - debug_push_circle((debug_v2){p[i].x+dx*car->steer*-20, p[i].y+dy*car->steer*-20}, 5.0f, 0x00FFAAFF); - } - } - } -} - -void ClearVehicle(ecs_iter_t *it) { - Vehicle *veh = ecs_column(it, Vehicle, 1); - - for (int i = 0; i < it->count; i++) { - for (int k = 0; k < 4; k++) { - if (world_entity_valid(veh[i].seats[k])) { - ecs_remove(it->world, veh[i].seats[k], IsInVehicle); - } - } - } -} +#include "debug_draw.h" + +#define VEH_ENTER_RADIUS 45.0f + +void LeaveVehicle(ecs_iter_t *it) { + Input *in = ecs_column(it, Input, 1); + IsInVehicle *vehp = ecs_column(it, IsInVehicle, 2); + Velocity *v = ecs_column(it, Velocity, 3); + + for (int i = 0; i < it->count; i++) { + if (!in[i].use) continue; + + Vehicle *veh = 0; + if ((veh = ecs_get_mut_if(it->world, vehp->veh, Vehicle))) { + for (int k = 0; k < 4; k++) { + if (veh->seats[k] == it->entities[i]) { + veh->seats[k] = 0; + break; + } + } + + in[i].use = false; + ecs_remove(it->world, it->entities[i], IsInVehicle); + + // NOTE(zaklaus): push passenger out + { + float px = zpl_cos(veh->heading)*400.0f; + float py = zpl_sin(veh->heading)*400.0f; + v->x += py; + v->y -= px; + } + } else { + ZPL_PANIC("unreachable code"); + } + } +} + +void EnterVehicle(ecs_iter_t *it) { + Input *in = ecs_column(it, Input, 1); + Position *p = ecs_column(it, Position, 2); + + for (int i = 0; i < it->count; i++) { + if (!in[i].use) continue; + + size_t ents_count; + int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2); + bool has_entered_veh = false; + + for (size_t j = 0; j < ents_count; j++) { + Vehicle *veh = 0; + + if (has_entered_veh) break; + + if ((veh = ecs_get_mut_if(it->world, ents[j], Vehicle))) { + Position const* p2 = ecs_get(it->world, ents[j], Position); + + float dx = p2->x - p[i].x; + float dy = p2->y - p[i].y; + float range = zpl_sqrt(dx*dx + dy*dy); + if (range <= VEH_ENTER_RADIUS) { + for (int k = 0; k < 4; k++) { + if (veh->seats[k] != 0) continue; + + // NOTE(zaklaus): We can enter the vehicle, yay! + veh->seats[k] = it->entities[i]; + ecs_set(it->world, it->entities[i], IsInVehicle, { + .veh = ents[j] + }); + p[i] = *p2; + in[i].use = false; + has_entered_veh = true; + break; + } + } + } + } + } +} + +#define VEHICLE_FORCE 340.8f +#define VEHICLE_ACCEL 0.12f +#define VEHICLE_DECEL 0.28f +#define VEHICLE_STEER 3.89f +#define VEHICLE_POWER 34.89f +#define VEHICLE_BRAKE_FORCE 0.84f + +void VehicleHandling(ecs_iter_t *it) { + Vehicle *veh = ecs_column(it, Vehicle, 1); + Position *p = ecs_column(it, Position, 2); + Velocity *v = ecs_column(it, Velocity, 3); + + for (int i = 0; i < it->count; i++) { + Vehicle *car = &veh[i]; + + for (int j = 0; j < 4; j++) { + // NOTE(zaklaus): Perform seat cleanup + if (!world_entity_valid(veh[i].seats[j])) { + veh[i].seats[j] = 0; + continue; + } + + ecs_entity_t pe = veh[i].seats[j]; + + // NOTE(zaklaus): Handle driver input + if (j == 0) { + Input const* in = ecs_get(it->world, pe, Input); + + car->force += zpl_lerp(0.0f, in->y * VEHICLE_FORCE, VEHICLE_ACCEL*it->delta_time); + if (in->sprint) { + car->force = zpl_lerp(car->force, 0.0f, VEHICLE_BRAKE_FORCE*it->delta_time); + + if (zpl_abs(car->force) < 5.5f) + car->force = 0.0f; + } + car->steer *= 0.97f; + car->steer += (in->x * VEHICLE_STEER)*it->delta_time; + car->steer = zpl_clamp(car->steer, -40.0f, 40.0f); + } + } + + car->force = zpl_clamp(car->force, car->reverse_speed, car->speed); + + // NOTE(zaklaus): Vehicle physics + float fr_x = p[i].x + (car->wheel_base/2.0f) * zpl_cos(car->heading); + float fr_y = p[i].y + (car->wheel_base/2.0f) * zpl_sin(car->heading); + + float bk_x = p[i].x - (car->wheel_base/2.0f) * zpl_cos(car->heading); + float bk_y = p[i].y - (car->wheel_base/2.0f) * zpl_sin(car->heading); + + world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y); + float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f); + + bk_x += car->force * drag * zpl_cos(car->heading); + bk_y += car->force * drag * zpl_sin(car->heading); + fr_x += car->force * drag * zpl_cos(car->heading + zpl_to_radians(car->steer)); + fr_y += car->force * drag * zpl_sin(car->heading + zpl_to_radians(car->steer)); + + v[i].x += ((fr_x + bk_x) / 2.0f - p[i].x)*it->delta_time*VEHICLE_POWER; + v[i].y += ((fr_y + bk_y) / 2.0f - p[i].y)*it->delta_time*VEHICLE_POWER; + car->heading = zpl_arctan2(fr_y - bk_y, fr_x - bk_x); + + world_block_lookup lookahead = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y+PHY_LOOKAHEAD(v[i].y)); + uint32_t flags = blocks_get_flags(lookahead.block_id); + if (flags & BLOCK_FLAG_COLLISION) { + car->force = 0.0f; + } + + for (int j = 0; j < 4; j++) { + if (!world_entity_valid(veh[i].seats[j])) continue; + ecs_entity_t pe = veh[i].seats[j]; + + // NOTE(zaklaus): Update passenger position + { + Position *p2 = ecs_get_mut(it->world, pe, Position, NULL); + Velocity *v2 = ecs_get_mut(it->world, pe, Velocity, NULL); + *p2 = p[i]; + *v2 = v[i]; + } + } + + { + debug_v2 b2 = {p[i].x + zpl_cos(car->heading)*(car->wheel_base), p[i].y + zpl_sin(car->heading)*(car->wheel_base)}; + debug_push_line((debug_v2){p[i].x, p[i].y}, b2, 0x0000FFFF); + + // NOTE(zaklaus): force + { + float dx = zpl_cos(car->heading); + float dy = zpl_sin(car->heading); + debug_push_circle((debug_v2){p[i].x+dx*car->force, p[i].y+dy*car->force}, 5.0f, 0x00FF00FF); + } + + // NOTE(zaklaus): steer + { + float dx = zpl_sin(car->heading); + float dy = -zpl_cos(car->heading); + debug_push_circle((debug_v2){p[i].x+dx*car->steer*-20, p[i].y+dy*car->steer*-20}, 5.0f, 0x00FFAAFF); + } + } + } +} + +void ClearVehicle(ecs_iter_t *it) { + Vehicle *veh = ecs_column(it, Vehicle, 1); + + for (int i = 0; i < it->count; i++) { + for (int k = 0; k < 4; k++) { + if (world_entity_valid(veh[i].seats[k])) { + ecs_remove(it->world, veh[i].seats[k], IsInVehicle); + } + } + } +}