From 4ac3981845162d8a50e88236996aef647c4f0f05 Mon Sep 17 00:00:00 2001 From: clement Date: Thu, 18 Jun 2026 15:17:31 +0200 Subject: [PATCH] fix(npc): gateway scene notify non-blocking The scene->gateway POST ran synchronously (2 s timeout) on the scene transition, so an offline gateway lagged EVERY scene change by 2 s. Fire it on a detached one-shot task instead, so the game's scene transitions never wait on the network. Frees the heap scene copy and self-deletes. Builds clean. --- idf_zacus/components/npc_engine/npc_engine.c | 55 ++++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/idf_zacus/components/npc_engine/npc_engine.c b/idf_zacus/components/npc_engine/npc_engine.c index 2a4df1e..7282338 100644 --- a/idf_zacus/components/npc_engine/npc_engine.c +++ b/idf_zacus/components/npc_engine/npc_engine.c @@ -17,11 +17,14 @@ #include "npc_engine.h" #include +#include #include #include "esp_err.h" #include "esp_http_client.h" #include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "media_manager.h" #include "hints_client.h" @@ -39,11 +42,12 @@ void npc_engine_set_gateway(const char *base_url, const char *token) { if (token) snprintf(s_gw_token, sizeof(s_gw_token), "%s", token); } -// Fire a one-shot POST {gateway}/game/step?scene=. Short timeout, -// best-effort: the puzzle progresses fine without it, it only keeps the phone -// NPCs' hints in sync with the current scene. Never propagates failure. -static void notify_gateway_scene(const char *scene) { - if (!s_gw_url[0] || !scene || !scene[0]) return; +// One-shot worker: POST {gateway}/game/step?scene= then self-delete. +// Runs on its own task so a slow/unreachable gateway NEVER stalls the scene +// transition (the game must not lag 2 s on every scene change if the gateway is +// offline). `arg` is a heap copy of the scene, freed here. Best-effort. +static void gw_notify_task(void *arg) { + char *scene = (char *) arg; char url[256]; snprintf(url, sizeof(url), "%s/game/step?scene=%s", s_gw_url, scene); esp_http_client_config_t cfg = { @@ -52,20 +56,37 @@ static void notify_gateway_scene(const char *scene) { .timeout_ms = 2000, }; esp_http_client_handle_t cli = esp_http_client_init(&cfg); - if (!cli) return; - if (s_gw_token[0]) { - char auth[112]; - snprintf(auth, sizeof(auth), "Bearer %s", s_gw_token); - esp_http_client_set_header(cli, "Authorization", auth); + if (cli) { + if (s_gw_token[0]) { + char auth[112]; + snprintf(auth, sizeof(auth), "Bearer %s", s_gw_token); + esp_http_client_set_header(cli, "Authorization", auth); + } + esp_err_t err = esp_http_client_perform(cli); + if (err != ESP_OK) { + ESP_LOGW(TAG, "gateway /game/step notify failed: %s", esp_err_to_name(err)); + } else { + ESP_LOGI(TAG, "gateway scene -> %s (HTTP %d)", scene, + esp_http_client_get_status_code(cli)); + } + esp_http_client_cleanup(cli); } - esp_err_t err = esp_http_client_perform(cli); - if (err != ESP_OK) { - ESP_LOGW(TAG, "gateway /game/step notify failed: %s", esp_err_to_name(err)); - } else { - ESP_LOGI(TAG, "gateway scene -> %s (HTTP %d)", scene, - esp_http_client_get_status_code(cli)); + free(scene); + vTaskDelete(NULL); +} + +// Fire-and-forget the gateway scene notification on a detached task so a scene +// change is never blocked by the network. No-op if no gateway configured. +static void notify_gateway_scene(const char *scene) { + if (!s_gw_url[0] || !scene || !scene[0]) return; + size_t n = strlen(scene) + 1; + char *copy = malloc(n); + if (!copy) return; + memcpy(copy, scene, n); + if (xTaskCreate(gw_notify_task, "gw_notify", 4096, copy, 3, NULL) != pdPASS) { + ESP_LOGW(TAG, "gw_notify task spawn failed"); + free(copy); } - esp_http_client_cleanup(cli); } // ─── Core: scene/trigger/mood lookup tables (verbatim Arduino) ──────────────