Просмотр исходного кода

send player position to websocket when changed

jblanked 9 месяцев назад
Родитель
Сommit
d379b1c426
4 измененных файлов с 280 добавлено и 149 удалено
  1. 7 1
      game/game.c
  2. 19 2
      game/player.c
  3. 252 145
      game/storage.c
  4. 2 1
      game/storage.h

+ 7 - 1
game/game.c

@@ -144,7 +144,13 @@ static void game_stop(void *ctx)
         uint32_t tick_count = furi_get_tick();
         furi_delay_ms(800);
 
-        save_player_context_api(player_context);
+        // save the player context to the API
+        game_context->fhttp = flipper_http_alloc();
+        if (game_context->fhttp)
+        {
+            save_player_context_api(player_context, game_context->fhttp);
+            flipper_http_free(game_context->fhttp);
+        }
 
         const uint32_t delay = 3500;
         tick_count = (tick_count + delay) - furi_get_tick();

+ 19 - 2
game/player.c

@@ -213,7 +213,7 @@ static void vgm_direction(Imu *imu, PlayerContext *player, Vector *pos)
         player->direction = ENTITY_UP;
     }
 }
-
+uint16_t elapsed_ws_timer = 0;
 static void player_update(Entity *self, GameManager *manager, void *context)
 {
     if (!self || !manager || !context)
@@ -222,9 +222,26 @@ static void player_update(Entity *self, GameManager *manager, void *context)
     PlayerContext *player = (PlayerContext *)context;
     InputState input = game_manager_input_get(manager);
     Vector pos = entity_pos_get(self);
-    player->old_position = pos;
     GameContext *game_context = game_manager_game_context_get(manager);
 
+    // update websocket player context
+    if (game_context->game_mode == GAME_MODE_PVP && (player->old_position.x != pos.x || player->old_position.y != pos.y))
+    {
+        elapsed_ws_timer++;
+        // only send the websocket update every 100ms
+        if (elapsed_ws_timer >= (game_context->fps / 10))
+        {
+            if (game_context->fhttp)
+            {
+                player->start_position = player->old_position;
+                websocket_player_context(player, game_context->fhttp);
+            }
+            elapsed_ws_timer = 0;
+        }
+    }
+
+    player->old_position = pos;
+
     // Determine the player's level based on XP
     player->level = get_player_level_iterative(player->xp);
     player->strength = 10 + (player->level * 1);           // 1 strength per level

+ 252 - 145
game/storage.c

@@ -203,177 +203,250 @@ bool save_player_context(PlayerContext *player_context)
     return true;
 }
 
-bool save_player_context_api(PlayerContext *player_context)
+static FuriString *player_context_json(PlayerContext *player_context, bool websocket)
 {
-    if (!player_context)
-    {
-        FURI_LOG_E(TAG, "Invalid player context");
-        return false;
-    }
-
-    FlipperHTTP *fhttp = flipper_http_alloc();
-    if (!fhttp)
-    {
-        FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
-        return false;
-    }
-
-    // create JSON for all the player context data
     FuriString *json = furi_string_alloc();
     if (!json)
     {
         FURI_LOG_E(TAG, "Failed to allocate JSON string");
-        return false;
+        return NULL;
     }
 
-    // opening brace
     furi_string_cat_str(json, "{");
 
-    // 1. Username (String)
-    furi_string_cat_str(json, "\"username\":\"");
-    furi_string_cat_str(json, player_context->username);
-    furi_string_cat_str(json, "\",");
-
-    // 2. Level (uint32_t)
-    furi_string_cat_str(json, "\"level\":");
-    char buffer[32];
-    snprintf(buffer, sizeof(buffer), "%lu", player_context->level);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
-
-    // 3. XP (uint32_t)
-    furi_string_cat_str(json, "\"xp\":");
-    snprintf(buffer, sizeof(buffer), "%lu", player_context->xp);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
-
-    // 4. Health (uint32_t)
-    furi_string_cat_str(json, "\"health\":");
-    snprintf(buffer, sizeof(buffer), "%lu", player_context->health);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
-
-    // 5. Strength (uint32_t)
-    furi_string_cat_str(json, "\"strength\":");
-    snprintf(buffer, sizeof(buffer), "%lu", player_context->strength);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
-
-    // 6. Max Health (uint32_t)
-    furi_string_cat_str(json, "\"max_health\":");
-    snprintf(buffer, sizeof(buffer), "%lu", player_context->max_health);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
-
-    // 7. Health Regen (uint32_t)
-    furi_string_cat_str(json, "\"health_regen\":");
-    snprintf(buffer, sizeof(buffer), "%lu", player_context->health_regen);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
+    if (websocket)
+    {
+        // Minimal JSON for WebSocket (abbreviated, <128 characters)
+        // "u": username
+        furi_string_cat_str(json, "\"u\":\"");
+        furi_string_cat_str(json, player_context->username);
+        furi_string_cat_str(json, "\",");
+
+        // "xp": experience
+        furi_string_cat_str(json, "\"xp\":");
+        char buffer[32];
+        snprintf(buffer, sizeof(buffer), "%lu", player_context->xp);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // "h": health
+        furi_string_cat_str(json, "\"h\":");
+        snprintf(buffer, sizeof(buffer), "%lu", player_context->health);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // "ehr": elapsed health regen (1 decimal)
+        furi_string_cat_str(json, "\"ehr\":");
+        snprintf(buffer, sizeof(buffer), "%.1f", (double)player_context->elapsed_health_regen);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // "eat": elapsed attack timer (1 decimal)
+        furi_string_cat_str(json, "\"eat\":");
+        snprintf(buffer, sizeof(buffer), "%.1f", (double)player_context->elapsed_attack_timer);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // "d": direction (numeric code)
+        furi_string_cat_str(json, "\"d\":");
+        snprintf(buffer, sizeof(buffer), "%d", player_context->direction);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // "s": state (numeric code)
+        furi_string_cat_str(json, "\"s\":");
+        snprintf(buffer, sizeof(buffer), "%d", player_context->state);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // "sp": start position object with x and y (1 decimal)
+        furi_string_cat_str(json, "\"sp\":{");
+        furi_string_cat_str(json, "\"x\":");
+        snprintf(buffer, sizeof(buffer), "%.1f", (double)player_context->start_position.x);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",\"y\":");
+        snprintf(buffer, sizeof(buffer), "%.1f", (double)player_context->start_position.y);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, "}");
+    }
+    else
+    {
+        // Full JSON output (unchanged)
+        // 1. Username
+        furi_string_cat_str(json, "\"username\":\"");
+        furi_string_cat_str(json, player_context->username);
+        furi_string_cat_str(json, "\",");
+
+        // 2. Level
+        furi_string_cat_str(json, "\"level\":");
+        char buffer[32];
+        snprintf(buffer, sizeof(buffer), "%lu", player_context->level);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // 3. XP
+        furi_string_cat_str(json, "\"xp\":");
+        snprintf(buffer, sizeof(buffer), "%lu", player_context->xp);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // 4. Health
+        furi_string_cat_str(json, "\"health\":");
+        snprintf(buffer, sizeof(buffer), "%lu", player_context->health);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // 5. Strength
+        furi_string_cat_str(json, "\"strength\":");
+        snprintf(buffer, sizeof(buffer), "%lu", player_context->strength);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // 6. Max Health
+        furi_string_cat_str(json, "\"max_health\":");
+        snprintf(buffer, sizeof(buffer), "%lu", player_context->max_health);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // 7. Health Regen
+        furi_string_cat_str(json, "\"health_regen\":");
+        snprintf(buffer, sizeof(buffer), "%lu", player_context->health_regen);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // 8. Elapsed Health Regen
+        furi_string_cat_str(json, "\"elapsed_health_regen\":");
+        snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->elapsed_health_regen);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // 9. Attack Timer
+        furi_string_cat_str(json, "\"attack_timer\":");
+        snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->attack_timer);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // 10. Elapsed Attack Timer
+        furi_string_cat_str(json, "\"elapsed_attack_timer\":");
+        snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->elapsed_attack_timer);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
+
+        // 11. Direction (string representation)
+        furi_string_cat_str(json, "\"direction\":");
+        switch (player_context->direction)
+        {
+        case ENTITY_UP:
+            furi_string_cat_str(json, "\"up\",");
+            break;
+        case ENTITY_DOWN:
+            furi_string_cat_str(json, "\"down\",");
+            break;
+        case ENTITY_LEFT:
+            furi_string_cat_str(json, "\"left\",");
+            break;
+        case ENTITY_RIGHT:
+        default:
+            furi_string_cat_str(json, "\"right\",");
+            break;
+        }
 
-    // 8. Elapsed Health Regen (float)
-    furi_string_cat_str(json, "\"elapsed_health_regen\":");
-    snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->elapsed_health_regen);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
+        // 12. State (string representation)
+        furi_string_cat_str(json, "\"state\":");
+        switch (player_context->state)
+        {
+        case ENTITY_IDLE:
+            furi_string_cat_str(json, "\"idle\",");
+            break;
+        case ENTITY_MOVING:
+            furi_string_cat_str(json, "\"moving\",");
+            break;
+        case ENTITY_ATTACKING:
+            furi_string_cat_str(json, "\"attacking\",");
+            break;
+        case ENTITY_ATTACKED:
+            furi_string_cat_str(json, "\"attacked\",");
+            break;
+        case ENTITY_DEAD:
+            furi_string_cat_str(json, "\"dead\",");
+            break;
+        default:
+            furi_string_cat_str(json, "\"unknown\",");
+            break;
+        }
 
-    // 9. Attack Timer (float)
-    furi_string_cat_str(json, "\"attack_timer\":");
-    snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->attack_timer);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
+        // 13. Start Position X
+        furi_string_cat_str(json, "\"start_position_x\":");
+        snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->start_position.x);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
 
-    // 10. Elapsed Attack Timer (float)
-    furi_string_cat_str(json, "\"elapsed_attack_timer\":");
-    snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->elapsed_attack_timer);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
+        // 14. Start Position Y
+        furi_string_cat_str(json, "\"start_position_y\":");
+        snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->start_position.y);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
 
-    // 11. Direction (enum PlayerDirection)
-    furi_string_cat_str(json, "\"direction\":");
-    switch (player_context->direction)
-    {
-    case ENTITY_UP:
-        furi_string_cat_str(json, "\"up\",");
-        break;
-    case ENTITY_DOWN:
-        furi_string_cat_str(json, "\"down\",");
-        break;
-    case ENTITY_LEFT:
-        furi_string_cat_str(json, "\"left\",");
-        break;
-    case ENTITY_RIGHT:
-    default:
-        furi_string_cat_str(json, "\"right\",");
-        break;
-    }
+        // 15. dx
+        furi_string_cat_str(json, "\"dx\":");
+        snprintf(buffer, sizeof(buffer), "%d", player_context->dx);
+        furi_string_cat_str(json, buffer);
+        furi_string_cat_str(json, ",");
 
-    // 12. State (enum PlayerState)
-    furi_string_cat_str(json, "\"state\":");
-    switch (player_context->state)
-    {
-    case ENTITY_IDLE:
-        furi_string_cat_str(json, "\"idle\",");
-        break;
-    case ENTITY_MOVING:
-        furi_string_cat_str(json, "\"moving\",");
-        break;
-    case ENTITY_ATTACKING:
-        furi_string_cat_str(json, "\"attacking\",");
-        break;
-    case ENTITY_ATTACKED:
-        furi_string_cat_str(json, "\"attacked\",");
-        break;
-    case ENTITY_DEAD:
-        furi_string_cat_str(json, "\"dead\",");
-        break;
-    default:
-        furi_string_cat_str(json, "\"unknown\",");
-        break;
+        // 16. dy
+        furi_string_cat_str(json, "\"dy\":");
+        snprintf(buffer, sizeof(buffer), "%d", player_context->dy);
+        furi_string_cat_str(json, buffer);
     }
 
-    // 13. Start Position X (float)
-    furi_string_cat_str(json, "\"start_position_x\":");
-    snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->start_position.x);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
-
-    // 14. Start Position Y (float)
-    furi_string_cat_str(json, "\"start_position_y\":");
-    snprintf(buffer, sizeof(buffer), "%.6f", (double)player_context->start_position.y);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
-
-    // 15. dx (int8_t)
-    furi_string_cat_str(json, "\"dx\":");
-    snprintf(buffer, sizeof(buffer), "%d", player_context->dx);
-    furi_string_cat_str(json, buffer);
-    furi_string_cat_str(json, ",");
-
-    // 16. dy (int8_t)
-    furi_string_cat_str(json, "\"dy\":");
-    snprintf(buffer, sizeof(buffer), "%d", player_context->dy);
-    furi_string_cat_str(json, buffer);
-
-    // closing brace
     furi_string_cat_str(json, "}");
 
-    // create new JSON with username key (of just username), and game_stats key (of the all of the data)
+    // For websocket, output only the minimal JSON (without extra wrapping)
     FuriString *json_data = furi_string_alloc();
     if (!json_data)
     {
         FURI_LOG_E(TAG, "Failed to allocate JSON string");
         furi_string_free(json);
-        return false;
+        return NULL;
     }
 
-    furi_string_cat_str(json_data, "{\"username\":\"");
-    furi_string_cat_str(json_data, player_context->username);
-    furi_string_cat_str(json_data, "\",\"game_stats\":");
-    furi_string_cat(json_data, json);
-    furi_string_cat_str(json_data, "}");
+    if (websocket)
+    {
+        furi_string_cat(json_data, json);
+    }
+    else
+    {
+        furi_string_cat_str(json_data, "{\"username\":\"");
+        furi_string_cat_str(json_data, player_context->username);
+        furi_string_cat_str(json_data, "\",\"game_stats\":");
+        furi_string_cat(json_data, json);
+        furi_string_cat_str(json_data, "}");
+    }
 
     furi_string_free(json);
+    return json_data;
+}
+
+bool save_player_context_api(PlayerContext *player_context, FlipperHTTP *fhttp)
+{
+    if (!player_context)
+    {
+        FURI_LOG_E(TAG, "Invalid player context");
+        return false;
+    }
+
+    if (!fhttp)
+    {
+        FURI_LOG_E(TAG, "FlipperHTTP is NULL");
+        return false;
+    }
+
+    FuriString *json_data = player_context_json(player_context, false);
+    if (!json_data)
+    {
+        FURI_LOG_E(TAG, "Failed to create JSON data");
+        return false;
+    }
 
     // save the json_data to the API
     if (!flipper_http_request(fhttp, POST, "https://www.jblanked.com/flipper/api/user/update-game-stats/", "{\"Content-Type\": \"application/json\"}", furi_string_get_cstr(json_data)))
@@ -388,7 +461,41 @@ bool save_player_context_api(PlayerContext *player_context)
         furi_delay_ms(100);
     }
     furi_string_free(json_data);
-    flipper_http_free(fhttp);
+
+    return true;
+}
+
+bool websocket_player_context(PlayerContext *player_context, FlipperHTTP *fhttp)
+{
+    if (!player_context)
+    {
+        FURI_LOG_E(TAG, "Invalid player context");
+        return false;
+    }
+
+    if (!fhttp)
+    {
+        FURI_LOG_E(TAG, "FlipperHTTP is NULL");
+        return false;
+    }
+
+    // create JSON for all the player context data
+    FuriString *json = player_context_json(player_context, true);
+    if (!json)
+    {
+        FURI_LOG_E(TAG, "Failed to create JSON data");
+        return false;
+    }
+
+    // websocket session is already started, so just send json to esp32
+    if (!flipper_http_send_data(fhttp, furi_string_get_cstr(json)))
+    {
+        FURI_LOG_E(TAG, "Failed to send player context to websocket");
+        furi_string_free(json);
+        return false;
+    }
+    furi_string_free(json);
+
     return true;
 }
 

+ 2 - 1
game/storage.h

@@ -5,7 +5,8 @@
 #include <flip_storage/storage.h>
 
 bool save_player_context(PlayerContext *player_context);
-bool save_player_context_api(PlayerContext *player_context);
+bool save_player_context_api(PlayerContext *player_context, FlipperHTTP *fhttp);
+bool websocket_player_context(PlayerContext *player_context, FlipperHTTP *fhttp);
 bool load_player_context(PlayerContext *player_context);
 bool set_player_context();