diff --git a/src/base/base_math.h b/src/base/base_math.h index 1a4c309a..fe0102a5 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -262,7 +262,10 @@ i32 SignF64(f64 v); u64 PowU64(u64 v, u8 exp); #define PowF32(v, exp) powf((v), (exp)) +#define PowF64(v, exp) pow((v), (exp)) + #define SqrtF32(v) sqrtf(v) +#define SqrtF64(v) sqrt(v) #define LnF32(v) log(v) #define Log2F32(v) log2f(v) diff --git a/src/config.h b/src/config.h index 1e7be96f..c6e950f5 100644 --- a/src/config.h +++ b/src/config.h @@ -39,7 +39,7 @@ #define SIM_TILES_PER_UNIT_SQRT (4) #define SIM_TILES_PER_CHUNK_SQRT (16) -// #define SIM_TICKS_PER_SECOND 100 +#define SIM_MAX_PING 5.0 #define SIM_TICKS_PER_SECOND 64 // Like USER_INTERP_RATIO, but applies to snapshots received by the local sim from the // master sim (how far back in time should the client render the server's state) diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index 7be8d0ea..fa7e1a02 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -36,7 +36,7 @@ void S_TickForever(WaveLaneCtx *lane) // FIXME: Header - Rng user_cmd_tick_range = RNG(0, SIM_TICKS_PER_SECOND * 1.0); + Rng user_cmd_tick_range = RNG(SIM_TICKS_PER_SECOND * -SIM_MAX_PING, SIM_TICKS_PER_SECOND * SIM_MAX_PING); P_CmdList queued_user_cmds = Zi; P_CmdNode *first_free_user_cmd_node = 0; @@ -89,7 +89,7 @@ void S_TickForever(WaveLaneCtx *lane) P_CmdList user_cmds = Zi; { - i64 user_cmds_min_tick = world_frame->tick - user_cmd_tick_range.min; + i64 user_cmds_min_tick = world_frame->tick + user_cmd_tick_range.min; i64 user_cmds_max_tick = world_frame->tick + user_cmd_tick_range.max; // Prune queued user cmds { @@ -97,7 +97,7 @@ void S_TickForever(WaveLaneCtx *lane) { P_CmdNode *next = cmd_node->next; b32 prune = 0; - if (cmd_node->cmd.tick < user_cmds_min_tick || cmd_node->cmd.tick > user_cmds_max_tick) + if (cmd_node->cmd.predicted && (cmd_node->cmd.tick < user_cmds_min_tick || cmd_node->cmd.tick > user_cmds_max_tick)) { prune = 1; } @@ -114,7 +114,7 @@ void S_TickForever(WaveLaneCtx *lane) { for (P_CmdNode *src_cmd_node = input->cmds.first; src_cmd_node; src_cmd_node = src_cmd_node->next) { - if (src_cmd_node->cmd.tick >= user_cmds_min_tick && src_cmd_node->cmd.tick <= user_cmds_max_tick) + if (!src_cmd_node->cmd.predicted || (src_cmd_node->cmd.tick >= user_cmds_min_tick && src_cmd_node->cmd.tick <= user_cmds_max_tick)) { P_CmdNode *dst_cmd_node = first_free_user_cmd_node; if (dst_cmd_node) @@ -132,25 +132,26 @@ void S_TickForever(WaveLaneCtx *lane) } } } - // Copy queued commands for current tick + // Pop frame user cmds from queue { - for (P_CmdNode *src_cmd_node = queued_user_cmds.first; src_cmd_node; src_cmd_node = src_cmd_node->next) + for (P_CmdNode *src_cmd_node = queued_user_cmds.first; src_cmd_node;) { - i64 cmd_tick = src_cmd_node->cmd.tick; - if (!src_cmd_node->cmd.predicted) + P_CmdNode *next = src_cmd_node->next; + if (!src_cmd_node->cmd.predicted || src_cmd_node->cmd.tick == world_frame->tick) { - // We can execute unpredicted cmds this frame, since they don't - // need to be buffered to match predicted behavior - cmd_tick = world_frame->tick; - } - if (cmd_tick == world_frame->tick) - { - P_CmdNode *dst_cmd_node = PushStruct(frame_arena, P_CmdNode); - dst_cmd_node->cmd = src_cmd_node->cmd; - dst_cmd_node->cmd.tick = cmd_tick; - DllQueuePush(user_cmds.first, user_cmds.last, dst_cmd_node); - ++user_cmds.count; + { + P_CmdNode *dst_cmd_node = PushStruct(frame_arena, P_CmdNode); + dst_cmd_node->cmd = src_cmd_node->cmd; + DllQueuePush(user_cmds.first, user_cmds.last, dst_cmd_node); + ++user_cmds.count; + } + { + DllQueueRemove(queued_user_cmds.first, queued_user_cmds.last, src_cmd_node); + SllStackPush(first_free_user_cmd_node, src_cmd_node); + --queued_user_cmds.count; + } } + src_cmd_node = next; } } } diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 1eafdc2b..05a8521c 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -279,11 +279,14 @@ V_WidgetTheme V_GetWidgetTheme(void) { V_WidgetTheme theme = Zi; - theme.text_font = GC_FontKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("font/seguisb.ttf"))); + theme.ui_font = GC_FontKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("font/seguisb.ttf"))); + // theme.chat_font = GC_FontKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("font/fixedsys.ttf"))); + theme.chat_font = GC_FontKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("font/seguisb.ttf"))); theme.icon_font = UI_BuiltinIconFont(); // theme.font_size = 14; - theme.font_size = TweakFloat("Font size", 14, 8, 24, .precision = 0); + theme.ui_font_size = TweakFloat("UI font size", 14, 8, 24, .precision = 0); + theme.chat_font_size = TweakFloat("Chat font size", 16, 8, 24, .precision = 0); theme.h1 = 2.00; theme.h2 = 1.50; theme.h3 = 1.25; @@ -322,11 +325,29 @@ V_WidgetTheme V_GetWidgetTheme(void) void V_PushWidgetThemeStyles(V_WidgetTheme theme) { - UI_Push(Font, theme.text_font); - UI_Push(FontSize, theme.font_size); + UI_Push(Font, theme.ui_font); + UI_Push(FontSize, theme.ui_font_size); UI_Push(TextColor, theme.col.text); } +//////////////////////////////////////////////////////////// +//~ Notification helpers + +void V_PushNotif(String msg) +{ + Arena *perm = PermArena(); + V_Notif *notif = PushStruct(perm, V_Notif); + notif->time_ns = TimeNs(); + notif->datetime = LocalDateTime(); + notif->msg = PushString(perm, msg); + if (V.last_notif) + { + notif->seq = V.last_notif->seq + 1; + } + SllQueuePushFront(V.first_notif, V.last_notif, notif); + LogInfoF("Notif: %F", FmtString(notif->msg)); +} + //////////////////////////////////////////////////////////// //~ Vis tick @@ -938,6 +959,116 @@ void V_TickForever(WaveLaneCtx *lane) } } + ////////////////////////////// + //- Build notifications + + { + i64 duration_ns = NsFromSeconds(10); + i64 min_notif_time_ns = frame->time_ns - duration_ns; + i64 max_notifs = 10; + + // Collect recent notifications + i64 draw_notifs_count = 0; + V_Notif **draw_notifs = PushStructs(frame->arena, V_Notif *, max_notifs); + { + i64 notif_idx = 0; + for (V_Notif *notif = V.first_notif; notif; notif = notif->next) + { + if (notif_idx < max_notifs && notif->time_ns > min_notif_time_ns) + { + draw_notifs[draw_notifs_count] = notif; + draw_notifs_count += 1; + } + else + { + break; + } + notif_idx += 1; + } + } + + // Build notification UI + { + UI_Key notifs_key = UI_KeyF("notifs"); + UI_BoxReport notifs_rep = UI_ReportsFromKey(notifs_key).draw; + + f32 width = 600; + + Vec2 pos = Zi; + pos.x = 10; + pos.y = frame->ui_dims.y * 0.5 - DimsFromRng2(notifs_rep.screen_rect).y * 0.5; + + // Vec4 bg = VEC4(0, 0, 0, 0.25); + Vec4 bg = VEC4(0, 0, 0, 0); + + UI_SetNext(Width, UI_PIX(width, 0)); + // UI_SetNext(Height, UI_PIX(height, 0)); + // UI_SetNext(Width, UI_GROW(0.9, 0)); + // UI_SetNext(Width, UI_SHRINK(0, 0)); + UI_SetNext(Height, UI_SHRINK(0, 0)); + UI_SetNext(FloatingPos, pos); + UI_SetNext(BackgroundColor, bg); + UI_SetNext(Rounding, 0); + UI_SetNext(Flags, UI_BoxFlag_Floating); + UI_PushCP(UI_BuildColumnEx(notifs_key)); + { + UI_Push(ChildAlignment, UI_Region_Left); + UI_Push(Font, theme.chat_font); + UI_Push(FontSize, theme.chat_font_size); + for (i64 notif_idx = draw_notifs_count - 1; notif_idx >= 0; --notif_idx) + { + V_Notif *notif = draw_notifs[notif_idx]; + + f32 opacity = 1.0; + f32 fade_curve = 0.8; + { + i64 remaining_ns = notif->time_ns - min_notif_time_ns; + opacity = PowF64(((f64)remaining_ns / (f64)duration_ns), fade_curve); + } + + UI_SetNext(Tint, 0); + UI_SetNext(Width, UI_SHRINK(0, 0)); + UI_SetNext(Height, UI_SHRINK(0, 0)); + UI_PushCP(UI_BuildRow()); + { + UI_Push(Tint, VEC4(1, 1, 1, opacity)); + UI_Push(Width, UI_SHRINK(0, 0)); + UI_Push(Height, UI_SHRINK(0, 0)); + { + String msg = StringF( + frame->arena, + "[%F:%F:%F] ", + FmtUint(notif->datetime.hour, .z = 2), + FmtUint(notif->datetime.minute, .z = 2), + FmtUint(notif->datetime.second, .z = 2) + ); + UI_SetNext(TextColor, VEC4(0.5, 0.5, 0.5, 1)); + UI_SetNext(Text, msg); + UI_SetNext(Flags, UI_BoxFlag_DrawText); + UI_BuildRow(); + } + { + String msg = notif->msg; + UI_SetNext(Text, msg); + UI_SetNext(Flags, UI_BoxFlag_DrawText); + UI_BuildRow(); + } + } + UI_PopCP(UI_TopCP()); + + if (notif_idx != 0) + { + UI_BuildSpacer(UI_PIX(5, 0), Axis_Y); + } + } + } + + UI_PopCP(UI_TopCP()); + } + + + } + ////////////////////////////// //- Init test layout @@ -2137,13 +2268,13 @@ void V_TickForever(WaveLaneCtx *lane) UI_BuildLabelF("Sim world tick: %F", FmtSint(sim_world->last_frame->tick)); UI_BuildLabelF("Sim world entities: %F", FmtSint(sim_world->last_frame->ents_count)); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); - UI_BuildLabelF("Predict world seed: 0x%F", FmtHex(predict_world->seed)); - UI_BuildLabelF("Predict world tick: %F", FmtSint(predict_world->last_frame->tick)); - UI_BuildLabelF("Predict world entities: %F", FmtSint(predict_world->last_frame->ents_count)); + UI_BuildLabelF("Predicted world seed: 0x%F", FmtHex(predict_world->seed)); + UI_BuildLabelF("Predicted world tick: %F", FmtSint(predict_world->last_frame->tick)); + UI_BuildLabelF("Predicted world entities: %F", FmtSint(predict_world->last_frame->ents_count)); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); - UI_BuildLabelF("Blend world seed: 0x%F", FmtHex(blend_world->seed)); - UI_BuildLabelF("Blend world tick: %F", FmtSint(blend_world->last_frame->tick)); - UI_BuildLabelF("Blend world entities: %F", FmtSint(blend_world->last_frame->ents_count)); + UI_BuildLabelF("Blended world seed: 0x%F", FmtHex(blend_world->seed)); + UI_BuildLabelF("Blended world tick: %F", FmtSint(blend_world->last_frame->tick)); + UI_BuildLabelF("Blended world entities: %F", FmtSint(blend_world->last_frame->ents_count)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); { @@ -2165,8 +2296,8 @@ void V_TickForever(WaveLaneCtx *lane) UI_Pop(FontSize); } UI_BuildLabelF(" Arenas: %F", FmtSint(GetGstat(NumArenas))); - UI_BuildLabelF(" Arena memory committed: %F MiB", FmtFloat((f64)GetGstat(ArenaMemoryCommitted) / 1024 / 1024)); - UI_BuildLabelF(" Arena memory reserved: %F TiB", FmtFloat((f64)GetGstat(ArenaMemoryReserved) / 1024 / 1024 / 1024 / 1024)); + UI_BuildLabelF(" Arena memory committed: %F MiB", FmtFloat((f64)GetGstat(ArenaMemoryCommitted) / 1024 / 1024, .p = 3)); + UI_BuildLabelF(" Arena memory reserved: %F TiB", FmtFloat((f64)GetGstat(ArenaMemoryReserved) / 1024 / 1024 / 1024 / 1024, .p = 3)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); { @@ -2176,8 +2307,8 @@ void V_TickForever(WaveLaneCtx *lane) UI_Pop(FontSize); } UI_BuildLabelF(" Arenas: %F", FmtUint(gpu_stats.arenas_count)); - UI_BuildLabelF(" Device memory usage: %F MiB", FmtFloat((f64)gpu_stats.device_committed / 1024 / 1024)); - UI_BuildLabelF(" Host memory usage: %F MiB", FmtFloat((f64)gpu_stats.host_committed / 1024 / 1024)); + UI_BuildLabelF(" Device memory usage: %F MiB", FmtFloat((f64)gpu_stats.device_committed / 1024 / 1024, .p = 3)); + UI_BuildLabelF(" Host memory usage: %F MiB", FmtFloat((f64)gpu_stats.host_committed / 1024 / 1024, .p = 3)); UI_BuildLabelF(" Non-reuse tally: %F", FmtUint(gpu_stats.cumulative_nonreuse_count)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); @@ -2228,7 +2359,6 @@ void V_TickForever(WaveLaneCtx *lane) } f32 fade_curve = 0.5; - i64 now_ns = TimeNs(); { UI_Push(FloatingPos, VEC2(0, 0)); UI_SetNext(Flags, UI_BoxFlag_Floating); @@ -2262,7 +2392,7 @@ void V_TickForever(WaveLaneCtx *lane) for (u64 i = logs.count; i-- > 0 && display_count < max && !done;) { LogEvent ev = logs.logs[i]; - if (ev.time_ns > (now_ns - max_time_ns)) + if (ev.time_ns > (frame->time_ns - max_time_ns)) { if (ev.level <= console_level) { @@ -2281,7 +2411,7 @@ void V_TickForever(WaveLaneCtx *lane) { LogEvent log = display_logs[i]; f32 opacity = 0.75; - f32 lin = 1.0 - ClampF64((f64)(now_ns - log.time_ns) / (f64)fade_time_ns, 0, 1); + f32 lin = 1.0 - ClampF64((f64)(frame->time_ns - log.time_ns) / (f64)fade_time_ns, 0, 1); opacity *= PowF32(lin, fade_curve); String text = log.msg; if (!minimized) @@ -2482,6 +2612,11 @@ void V_TickForever(WaveLaneCtx *lane) // { // should_clear_particles = 1; // } break; + + case V_CmdKind_test: + { + V_PushNotif(Lit("Hello!!!")); + } break; } } @@ -2544,6 +2679,12 @@ void V_TickForever(WaveLaneCtx *lane) { P_Msg *msg = &msg_node->msg; + //- Chat + // if (msg->kind == P_MsgKind_Chat) + // { + + // } + //- Tiles if (msg->kind == P_MsgKind_Tiles && sim_world->tiles_hash != msg->tiles_hash) { @@ -2633,8 +2774,8 @@ void V_TickForever(WaveLaneCtx *lane) //- Submit sim commands // FIXME: Real ping - f64 ping = 0.250; - // f64 ping = 0; + // f64 ping = 0.250; + f64 ping = 0; i64 ping_ns = NsFromSeconds(ping); frame->predict_to = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0); @@ -2682,37 +2823,40 @@ void V_TickForever(WaveLaneCtx *lane) predict_world->tiles_hash = sim_world->tiles_hash; CopyStructs(predict_world->tiles, sim_world->tiles, P_TilesCount); } + predict_world->seed = sim_world->seed; - // P_ClearFrames(predict_world, I64Min, I64Max); - // predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick); + P_ClearFrames(predict_world, I64Min, I64Max); + predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick); - // We want to keep previously predicted ticks to preserve constraints - P_ClearFrames(predict_world, I64Min, sim_world->last_frame->tick - 2); + // // We want to keep previously predicted ticks to preserve constraints + // P_ClearFrames(predict_world, I64Min, sim_world->last_frame->tick - 2); - i64 step_count = frame->predict_to - sim_world->last_frame->tick; - P_Frame *base_predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick); - for (i64 step_idx = 0; step_idx < step_count; ++step_idx) - { - P_Frame *step_frame = P_PushFrame(predict_world, predict_world->last_frame, predict_world->last_frame->tick + 1); + // i64 step_count = frame->predict_to - sim_world->last_frame->tick; + // P_Frame *base_predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick); + // for (i64 step_idx = 0; step_idx < step_count; ++step_idx) + // { + // P_Frame *step_frame = P_PushFrame(predict_world, predict_world->last_frame, predict_world->last_frame->tick + 1); - // FIXME: Cmds - P_CmdList step_cmds = Zi; - for (P_CmdNode *src_cmd_node = V.sim_cmds.first; src_cmd_node; src_cmd_node = src_cmd_node->next) - { - if (src_cmd_node->cmd.predicted && src_cmd_node->cmd.tick == step_frame->tick) - { - P_CmdNode *dst_cmd_node = PushStruct(frame->arena, P_CmdNode); - DllQueuePush(step_cmds.first, step_cmds.last, dst_cmd_node); - ++step_cmds.count; - } - } + // // FIXME: Cmds + // P_CmdList step_cmds = Zi; + // for (P_CmdNode *src_cmd_node = V.sim_cmds.first; src_cmd_node; src_cmd_node = src_cmd_node->next) + // { + // if (src_cmd_node->cmd.predicted && src_cmd_node->cmd.tick == step_frame->tick) + // { + // P_CmdNode *dst_cmd_node = PushStruct(frame->arena, P_CmdNode); + // DllQueuePush(step_cmds.first, step_cmds.last, dst_cmd_node); + // ++step_cmds.count; + // } + // } - P_StepFrame(step_frame, step_cmds); - } + // P_StepFrame(step_frame, step_cmds); + // } predict_frame = predict_world->last_frame; + + // TODO: Extract information that occurred between first & last prediction, like bullet hits etc? } @@ -2735,6 +2879,7 @@ void V_TickForever(WaveLaneCtx *lane) CopyStructs(blend_world->tiles, predict_world->tiles, P_TilesCount); tiles_dirty = 1; } + blend_world->seed = predict_world->seed; P_ClearFrames(blend_world, I64Min, I64Max); blend_frame = P_PushFrame(blend_world, predict_world->last_frame, predict_world->last_frame->tick); diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index f780a8a0..b228fe1d 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -18,6 +18,7 @@ X(delete, Delete entity at cursor, V_CmdDescFlag_None, V_HOTKEY( Button_M2 ), ) \ X(reset_world, Reset world, V_CmdDescFlag_None, V_HOTKEY( Button_R, .ctrl = 1, .alt = 1 ), ) \ X(clear_particles, Clear particles, V_CmdDescFlag_None, V_HOTKEY( Button_C ), ) \ + X(test, Test, V_CmdDescFlag_None, V_HOTKEY( Button_T, .ctrl = 1 ), ) \ /* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ //////////////////////////////////////////////////////////// @@ -25,10 +26,13 @@ Struct(V_WidgetTheme) { - GC_FontKey text_font; + GC_FontKey ui_font; + GC_FontKey chat_font; GC_FontKey icon_font; - f32 font_size; + f32 ui_font_size; + f32 chat_font_size; + f32 h1; f32 h2; f32 h3; @@ -133,6 +137,18 @@ Global Readonly V_CmdDesc V_cmd_descs[V_CmdKind_COUNT] = { #undef X }; +//////////////////////////////////////////////////////////// +//~ Notification types + +Struct(V_Notif) +{ + V_Notif *next; + DateTime datetime; + i64 time_ns; + i64 seq; + String msg; +}; + //////////////////////////////////////////////////////////// //~ Palette types @@ -289,6 +305,10 @@ Struct(V_Ctx) V_Panel *root_panel; V_Window *dragging_window; + // Notifications + V_Notif *first_notif; + V_Notif *last_notif; + // Sim commands P_CmdList sim_cmds; P_CmdNode *first_free_sim_cmd_node;