diff --git a/code/apps/client/header/prediction.h b/code/apps/client/header/prediction.h index 8c9a75a..4c047d0 100644 --- a/code/apps/client/header/prediction.h +++ b/code/apps/client/header/prediction.h @@ -1,5 +1,5 @@ #pragma once #include "entity_view.h" -float smooth_val(float cur, float tgt, float dt); +float smooth_val(float cur, float tgt, uint64_t dt); void predict_receive_update(entity_view *d, entity_view *data); \ No newline at end of file diff --git a/code/apps/client/header/world_view.h b/code/apps/client/header/world_view.h index 43a7fda..5ff53d6 100644 --- a/code/apps/client/header/world_view.h +++ b/code/apps/client/header/world_view.h @@ -14,6 +14,11 @@ typedef struct { uint16_t block_size; uint16_t chunk_size; uint16_t chunk_amount; + + // NOTE(zaklaus): metrics + uint64_t last_update[WORLD_TRACKER_LAYERS]; + uint64_t delta_time[WORLD_TRACKER_LAYERS]; + uint8_t active_layer_id; } world_view; world_view world_view_create(uint16_t view_id); diff --git a/code/apps/client/source/platform_raylib.c b/code/apps/client/source/platform_raylib.c index a0520ee..bad3e93 100644 --- a/code/apps/client/source/platform_raylib.c +++ b/code/apps/client/source/platform_raylib.c @@ -114,7 +114,6 @@ void platform_input() { y = mouse_pos.y; } - game_action_send_keystate(x, y, use, sprint); } @@ -236,7 +235,12 @@ void lerp_entity_positions(uint64_t key, entity_view data) { if (data.flag == EFLAG_INTERP) { entity_view *e = entity_view_get(&view->entities, key); - e->x = smooth_val(e->x, e->tx, 0); - e->y = smooth_val(e->y, e->ty, 0); +#if 1 + e->x = smooth_val(e->x, e->tx, view->delta_time[e->layer_id]); + e->y = smooth_val(e->y, e->ty, view->delta_time[e->layer_id]); +#else + e->x = e->tx; + e->y = e->ty; +#endif } } \ No newline at end of file diff --git a/code/apps/client/source/prediction.c b/code/apps/client/source/prediction.c index 92270de..966a0b7 100644 --- a/code/apps/client/source/prediction.c +++ b/code/apps/client/source/prediction.c @@ -1,12 +1,27 @@ #include "zpl.h" #include "prediction.h" #include "world/world.h" +#include "game.h" -#define PREDICT_SMOOTH_FACTOR_LO 0.8 -#define PREDICT_SMOOTH_FACTOR_HI 0.12 +#define PREDICT_SMOOTH_FACTOR_LO 0.80 +#define PREDICT_SMOOTH_FACTOR_HI 0.007 -float smooth_val(float cur, float tgt, float dt) { - return zpl_lerp(cur, tgt, zpl_lerp(PREDICT_SMOOTH_FACTOR_HI, PREDICT_SMOOTH_FACTOR_LO, zpl_unlerp(dt, WORLD_TRACKER_UPDATE_FAST_MS, WORLD_TRACKER_UPDATE_SLOW_MS))); +static inline float map_factor(float x) { + x = 1.0f - zpl_clamp01(x); + return 1.0f - x*x*x*x*x*x*x*x; +} + +float smooth_val(float cur, float tgt, uint64_t dt) { + float factor = zpl_clamp01(map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_FAST_MS, WORLD_TRACKER_UPDATE_SLOW_MS))); + +#if 0 + dt = 200; + factor = map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_FAST_MS, WORLD_TRACKER_UPDATE_SLOW_MS)); + zpl_printf("lerp factor: %f\n", factor); + zpl_exit(0); +#endif + + return zpl_lerp(cur, tgt, zpl_lerp(PREDICT_SMOOTH_FACTOR_LO, PREDICT_SMOOTH_FACTOR_HI, factor)); } void predict_receive_update(entity_view *d, entity_view *data) { diff --git a/code/apps/client/source/world_view.c b/code/apps/client/source/world_view.c index 58c2ece..7337eac 100644 --- a/code/apps/client/source/world_view.c +++ b/code/apps/client/source/world_view.c @@ -9,6 +9,11 @@ int32_t tracker_read_remove(librg_world *w, librg_event *e) { int64_t entity_id = librg_event_entity_get(w, e); zpl_printf("[INFO] An entity %d was removed for owner: %d\n", (int)entity_id, (int)owner_id); world_view *view = (world_view*)librg_world_userdata_get(w); + entity_view *d = entity_view_get(&view->entities, entity_id); + if (d && d->layer_id < view->active_layer_id) { + // NOTE(zaklaus): reject updates from slower layers + return 0; + } entity_view_destroy(&view->entities, entity_id); return 0; @@ -22,6 +27,11 @@ int32_t tracker_read_update(librg_world *w, librg_event *e) { entity_view data = entity_view_unpack_struct(buffer, actual_length); entity_view *d = entity_view_get(&view->entities, entity_id); + data.layer_id = view->active_layer_id; + if (d && d->layer_id < data.layer_id) { + // NOTE(zaklaus): reject updates from slower layers + return 0; + } predict_receive_update(d, &data); entity_view_update_or_create(&view->entities, entity_id, data); return 0; @@ -36,6 +46,7 @@ int32_t tracker_read_create(librg_world *w, librg_event *e) { world_view *view = (world_view*)librg_world_userdata_get(w); entity_view data = entity_view_unpack_struct(buffer, actual_length); + data.layer_id = view->active_layer_id; if (data.flag & EFLAG_INTERP) { data.tx = data.x; data.ty = data.y; @@ -57,7 +68,6 @@ void world_view_init(world_view *view, uint64_t ent_id, uint16_t block_size, uin view->block_size = block_size; view->chunk_size = chunk_size; view->chunk_amount = chunk_amount; - view->dim = block_size * chunk_size * chunk_amount; view->size = view->dim * view->dim; diff --git a/code/common/entity_view.h b/code/common/entity_view.h index dfbca83..cb091a1 100644 --- a/code/common/entity_view.h +++ b/code/common/entity_view.h @@ -27,6 +27,9 @@ typedef struct entity_view { float vy; float tx; float ty; + + // NOTE(zaklaus): internals + uint8_t layer_id; } entity_view; ZPL_TABLE_DECLARE(, entity_view_tbl, entity_view_tbl_, entity_view); diff --git a/code/common/packets/pkt_send_librg_update.c b/code/common/packets/pkt_send_librg_update.c index 0834040..c4dc7bb 100644 --- a/code/common/packets/pkt_send_librg_update.c +++ b/code/common/packets/pkt_send_librg_update.c @@ -1,27 +1,41 @@ +#include "zpl.h" #include "packet_utils.h" #include "packets/pkt_send_librg_update.h" #include "world/world.h" #include "game.h" -size_t pkt_send_librg_update_encode(void *data, int32_t data_length) { +size_t pkt_send_librg_update_encode(void *data, int32_t data_length, uint8_t layer_id) { cw_pack_context pc = {0}; - pkt_pack_msg(&pc, 1); + pkt_pack_msg(&pc, 2); + cw_pack_unsigned(&pc, layer_id); cw_pack_bin(&pc, data, data_length); return pkt_pack_msg_size(&pc); } int32_t pkt_send_librg_update_handler(pkt_header *header) { cw_unpack_context uc = {0}; - pkt_unpack_msg(&uc, header, 1); + pkt_unpack_msg(&uc, header, 2); + cw_unpack_next(&uc); + + if (uc.item.type != CWP_ITEM_POSITIVE_INTEGER) + return -1; + + uint8_t layer_id = (uint8_t)uc.item.as.u64; + cw_unpack_next(&uc); if (uc.item.type != CWP_ITEM_BIN) return -1; world_view *view = game_world_view_get(header->view_id); + view->active_layer_id = layer_id; int32_t state = librg_world_read(view->tracker, header->view_id, uc.item.as.bin.start, uc.item.as.bin.length, NULL); if (state < 0) zpl_printf("[ERROR] world read error: %d\n", state); + uint64_t now = zpl_time_rel_ms(); + view->delta_time[layer_id] = now - view->last_update[layer_id]; + view->last_update[layer_id] = now; + return state; } diff --git a/code/common/world/world.c b/code/common/world/world.c index 90c4893..6c3d9b6 100644 --- a/code/common/world/world.c +++ b/code/common/world/world.c @@ -152,7 +152,7 @@ int32_t world_destroy(void) { #define WORLD_LIBRG_BUFSIZ 2000000 -static void world_tracker_update(uint8_t ticker, uint8_t freq, uint8_t radius) { +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; @@ -178,7 +178,7 @@ static void world_tracker_update(uint8_t ticker, uint8_t freq, uint8_t radius) { zpl_printf("[error] an error happened writing the world %d\n", result); } - pkt_world_write(MSG_ID_LIBRG_UPDATE, pkt_send_librg_update_encode(buffer, datalen), 1, p[i].view_id, p[i].peer); + pkt_world_write(MSG_ID_LIBRG_UPDATE, pkt_send_librg_update_encode(buffer, datalen, ticker), 1, p[i].view_id, p[i].peer); } } } diff --git a/code/common/world/world.h b/code/common/world/world.h index 2c028f1..c94e3c8 100644 --- a/code/common/world/world.h +++ b/code/common/world/world.h @@ -11,9 +11,10 @@ #define WORLD_ERROR_INVALID_BUFFER -0x0004 #define WORLD_ERROR_TRACKER_FAILED -0x0005 +#define WORLD_TRACKER_LAYERS 3 #define WORLD_TRACKER_UPDATE_FAST_MS 10 #define WORLD_TRACKER_UPDATE_NORMAL_MS 100 -#define WORLD_TRACKER_UPDATE_SLOW_MS 800 +#define WORLD_TRACKER_UPDATE_SLOW_MS 400 #define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen, void *udata) typedef WORLD_PKT_READER(world_pkt_reader_proc);