Ensure physics aren't tied to framerate
parent
16da0ad158
commit
6f57fb178a
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue