code: demo record/playback feature

isolation_bkp/dynres
Dominik Madarász 2021-08-10 17:21:25 +02:00
parent f7ee8d0f11
commit f56c45e195
12 changed files with 201 additions and 14 deletions

View File

@ -0,0 +1,94 @@
#include "debug_replay.h"
#include "camera.h"
#include "entity.h"
typedef struct {
pkt_send_keystate pkt;
uint64_t delay;
} replay_record;
static uint8_t is_recording = false;
static replay_record *records = NULL;
static uint64_t last_record_time = 0.0f;
static uint8_t is_playing = false;
static int record_pos = 0;
static uint64_t playback_time = 0;
static ecs_entity_t mime = 0;
static ecs_entity_t plr = 0;
void debug_replay_start(void) {
is_recording = true;
if (records) zpl_array_free(records);
zpl_array_init(records, zpl_heap());
last_record_time = zpl_time_rel_ms();
}
void debug_replay_clear(void) {
if (!records || is_playing || is_recording) return;
zpl_array_free(records);
records = NULL;
}
void debug_replay_stop(void) {
is_recording = false;
// TODO(zaklaus):
}
void debug_replay_run(void) {
if (mime) return;
is_playing = true;
record_pos = 0;
playback_time = zpl_time_rel_ms();
plr = camera_get().ent_id;
Position const *p1 = ecs_get(world_ecs(), plr, Position);
mime = entity_spawn(EKIND_DEMO_NPC);
Position *pos = ecs_get_mut(world_ecs(), mime, Position, NULL);
*pos = *p1;
ecs_set(world_ecs(), mime, Input, {0});
camera_set_follow(mime);
}
void debug_replay_update(void) {
if (!is_playing) return;
if (playback_time >= zpl_time_rel_ms()) return;
replay_record *r = &records[record_pos];
playback_time = zpl_time_rel() + r->delay;
Input *i = ecs_get_mut(world_ecs(), mime, Input, NULL);
i->x = r->pkt.x;
i->y = r->pkt.y;
i->use = r->pkt.use;
i->sprint = r->pkt.sprint;
record_pos += 1;
// NOTE(zaklaus): remove our dummy art exhibist
if (mime && record_pos == zpl_array_count(records)) {
entity_despawn(mime);
mime = 0;
is_playing = false;
camera_set_follow(plr);
}
}
void debug_replay_record_keystate(pkt_send_keystate state) {
if (!is_recording) return;
float record_time = zpl_time_rel_ms();
replay_record rec = {
.pkt = state,
.delay = (record_time - last_record_time),
};
zpl_array_append(records, rec);
last_record_time = zpl_time_rel_ms();
}

View File

@ -0,0 +1,6 @@
#pragma once
#include "system.h"
#include "packets/pkt_send_keystate.h"
void debug_replay_record_keystate(pkt_send_keystate state);
void debug_replay_update(void);

View File

@ -7,6 +7,7 @@ typedef enum {
DITEM_BUTTON,
DITEM_SLIDER,
DITEM_LIST,
DITEM_COND,
DITEM_END,
DITEM_FORCE_UINT8 = UINT8_MAX
@ -52,6 +53,8 @@ typedef struct debug_item {
} slider;
void (*on_click)(void);
uint8_t (*on_success)(void);
};
debug_draw_result (*proc)(struct debug_item*, float, float);
@ -99,6 +102,29 @@ static debug_item items[] = {
}
}
},
{
.kind = DITEM_LIST,
.name = "replay system",
.list = {
.items = (debug_item[]) {
{ .kind = DITEM_TEXT, .name = "macro", .text = "<unnamed>", .proc = DrawLiteral },
{ .kind = DITEM_BUTTON, .name = "load", .on_click = NULL },
{ .kind = DITEM_BUTTON, .name = "save", .on_click = NULL },
{ .kind = DITEM_COND, .on_success = CondReplayStatusOff },
{ .kind = DITEM_BUTTON, .name = "record", .on_click = ActReplayBegin },
{ .kind = DITEM_COND, .on_success = CondReplayStatusOn },
{ .kind = DITEM_BUTTON, .name = "stop", .on_click = ActReplayEnd },
{ .kind = DITEM_COND, .on_success = CondReplayDataPresent },
{ .kind = DITEM_BUTTON, .name = "replay", .on_click = ActReplayRun },
{ .kind = DITEM_BUTTON, .name = "clear", .on_click = ActReplayClear },
{ .kind = DITEM_END },
}
}
},
{
.kind = DITEM_LIST,
.name = "profilers",
@ -129,6 +155,13 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool
is_shadow_rendered = is_shadow;
for (debug_item *it = list; it->kind != DITEM_END; it += 1) {
switch (it->kind) {
case DITEM_COND: {
assert(it->on_success);
if (!it->on_success()) {
it += 1;
}
}break;
case DITEM_LIST: {
// NOTE(zaklaus): calculate and cache name width for future use
if (it->name_width == 0) {
@ -166,16 +199,19 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool
}break;
case DITEM_BUTTON: {
assert(it->on_click);
char const *text = TextFormat("> %s", it->name);
if (it->name_width == 0) {
it->name_width = UIMeasureText(text, DBG_FONT_SIZE);
}
Color color = RAYWHITE;
if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color)) {
if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color) && it->on_click) {
it->on_click();
}
if (!it->on_click) {
color = GRAY;
}
debug_draw_result res = DrawColoredText(xpos, ypos, text, color);
ypos = res.y;
}break;

View File

@ -7,6 +7,8 @@
#include "modules/components.h"
#include "debug_replay.c"
static inline void
ActExitGame(void) {
game_request_close();
@ -21,3 +23,41 @@ ActSpawnCar(void) {
Position * dest = ecs_get_mut(world_ecs(), e, Position, NULL);
*dest = *origin;
}
// NOTE(zaklaus): Replay system
static inline uint8_t
CondReplayStatusOn(void) {
return is_recording;
}
static inline uint8_t
CondReplayStatusOff(void) {
return !is_recording;
}
static inline uint8_t
CondReplayDataPresent(void) {
return records != NULL && !is_recording;
}
static inline void
ActReplayBegin(void) {
debug_replay_start();
}
static inline void
ActReplayEnd(void) {
debug_replay_stop();
}
static inline void
ActReplayRun(void) {
debug_replay_run();
}
static inline void
ActReplayClear(void) {
debug_replay_clear();
}

View File

@ -27,6 +27,7 @@ uint64_t entity_spawn(uint16_t class_id) {
if (class_id != EKIND_SERVER) {
librg_entity_track(world_tracker(), e);
librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), pos->x, pos->y, 0));
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
}
return (uint64_t)e;

View File

@ -12,8 +12,8 @@
#include "profiler.h"
#include "flecs/flecs.h"
#include "flecs/flecs_dash.h"
#include "flecs/flecs_systems_civetweb.h"
//#include "flecs/flecs_dash.h"
//#include "flecs/flecs_systems_civetweb.h"
#include "flecs/flecs_os_api_stdcpp.h"
#include "modules/components.h"
@ -98,10 +98,12 @@ void game_world_view_set_active(world_view *view) {
}
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() {

View File

@ -49,7 +49,7 @@ int main(int argc, char** argv) {
uint16_t num_viewers = zpl_opts_integer(&opts, "viewer-count", 1);
uint16_t chunk_size = DEFAULT_CHUNK_SIZE; //zpl_opts_integer(&opts, "chunk-size", DEFAULT_CHUNK_SIZE);
uint16_t world_size = zpl_opts_integer(&opts, "world-size", DEFAULT_WORLD_SIZE);
uint32_t npc_count = zpl_opts_integer(&opts, "npc-count", 10000);
uint32_t npc_count = zpl_opts_integer(&opts, "npc-count", 1000);
if (zpl_opts_has_arg(&opts, "random-seed")) {
zpl_random rnd={0};
@ -67,8 +67,8 @@ int main(int argc, char** argv) {
game_init(is_viewer_only, num_viewers, seed, chunk_size, world_size, is_dash_enabled);
// TODO(zaklaus): VERY TEMPORARY -- SPAWN SOME NPCS THAT RANDOMLY MOVE
#if 1
{
ECS_IMPORT(world_ecs(), Components);
for (uint32_t i = 0; i < npc_count; i++) {
uint64_t e = entity_spawn(EKIND_DEMO_NPC);
ecs_add(world_ecs(), e, EcsDemoNPC);
@ -81,6 +81,7 @@ int main(int argc, char** argv) {
v->y = (rand()%3-1) * 100;
}
}
#endif
while (game_is_running()) {
profile (PROF_MAIN_LOOP) {

View File

@ -4,6 +4,8 @@
#include "modules/systems.h"
#include "world/world.h"
#include "debug_replay.h"
pkt_desc pkt_send_keystate_desc[] = {
{ PKT_REAL(pkt_send_keystate, x) },
{ PKT_REAL(pkt_send_keystate, y) },
@ -39,6 +41,7 @@ int32_t pkt_send_keystate_handler(pkt_header *header) {
i->y = table.y;
i->use = table.use;
i->sprint = table.sprint;
debug_replay_record_keystate(table);
}
return 0;

View File

@ -34,6 +34,3 @@ uint64_t player_spawn(char *name) {
void player_despawn(uint64_t ent_id) {
entity_despawn(ent_id);
}
void player_freeze(uint64_t id, uint8_t state, uint8_t clear) {
}

View File

@ -3,5 +3,3 @@
uint64_t player_spawn(char *name);
void player_despawn(uint64_t ent_id);
void player_freeze(uint64_t id, uint8_t state, uint8_t clear);

View File

@ -4,6 +4,7 @@
#include "modules/systems.h"
#include "world/world.h"
#include "entity_view.h"
#include "debug_replay.h"
#include "world/worldgen/worldgen.h"
#include "platform.h"
#include "profiler.h"
@ -151,7 +152,6 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
}
world.ecs = ecs_init();
ecs_set_entity_range(world.ecs, 0, UINT32_MAX);
ECS_IMPORT(world.ecs, Components);
ECS_IMPORT(world.ecs, Systems);
@ -257,6 +257,8 @@ int32_t world_update() {
world_tracker_update(0, WORLD_TRACKER_UPDATE_FAST_MS, 2);
world_tracker_update(1, WORLD_TRACKER_UPDATE_NORMAL_MS, 4);
world_tracker_update(2, WORLD_TRACKER_UPDATE_SLOW_MS, 6);
debug_replay_update();
return 0;
}
@ -408,3 +410,8 @@ int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t 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);
}

View File

@ -82,3 +82,5 @@ uint8_t world_chunk_is_dirty(ecs_entity_t e);
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);