Browse Source

:bug: Debug json encoder + creation game et liste game

Arthur Brandao 6 years ago
parent
commit
2f274aa907
11 changed files with 410 additions and 38 deletions
  1. 65 8
      Serveur/game.c
  2. 4 0
      Serveur/game.h
  3. 110 1
      Serveur/handler.c
  4. 2 0
      Serveur/handler.h
  5. 32 9
      Serveur/json_array.c
  6. 97 18
      Serveur/json_encoder.c
  7. 1 1
      Serveur/main.c
  8. 61 0
      Serveur/player.c
  9. 3 0
      Serveur/player.h
  10. 27 1
      Serveur/str.c
  11. 8 0
      Serveur/str.h

+ 65 - 8
Serveur/game.c

@@ -67,6 +67,7 @@ JsonArray* list_map(){
 
 JsonArray* list_game(){
     JsonArray* ja;
+    JsonEncoder* je;
     int compteur = 0, i = 0;
     //Si il n' y a aucune game
     if(nbGame == 0){
@@ -82,15 +83,16 @@ JsonArray* list_game(){
             continue;
         }
         //Creation objet json
-        JsonEncoder je;
-        ini_encoder(&je);
-        add_string(&je, "name", game[i].name);
-        add_integer(&je, "nbPlayer", game[i].nbPlayer);
-        add_string(&je, "map", game[i].mapName);
+        je = malloc(sizeof(JsonEncoder));
+        ini_encoder(je);
+        add_string(je, "name", game[i].name);
+        add_integer(je, "nbPlayer", game[i].nbPlayer);
+        add_string(je, "map", game[i].mapName);
         //Ajout dans le tableau
-        add_array_object(ja, &je);
+        add_array_object(ja, je);
         //Suppr encoder objet
-        clean_json_encoder(&je);
+        clean_json_encoder(je);
+        free(je);
         //Incremente
         i++;
         compteur++;
@@ -164,6 +166,61 @@ int create_game(char* name, char* map){
     return index;
 }
 
+int add_player(Game* g, int cliId){
+    int index = 0;
+    Client* cli;
+    //Verif que la game n'est pas deja pleine
+    if(g->nbPlayer >= MAXPLAYER){
+        return ERR;
+    }
+    //Cherche un joueur non initialisé
+    while(index < MAXPLAYER){
+        if(!g->player[index]->ini){
+            break;
+        }
+        index++;
+    }
+    //Recup du client
+    cli = get_client(cliId);
+    if(cli == NULL){
+        return ERR;
+    }
+    //Creation du joueur
+    create_player(g->player[index], cli);
+    g->nbPlayer++;
+    return index;
+}
+
+JsonEncoder* describe_game(Game* g, int playerIndex){
+    JsonEncoder* player;
+    JsonEncoder* desc = malloc(sizeof(JsonEncoder));
+    JsonEncoder* map = malloc(sizeof(JsonEncoder));
+    char* content;
+    //Initialisation
+    ini_encoder(desc);
+    ini_encoder(map);
+    //Info map
+    content = remove_char(g->mapContent, '\n'); //La map sans \n car interdit en JSON
+    add_integer(map, "width", g->width);
+    add_integer(map, "height", g->height);
+    add_string(map, "content", content);
+    free(content);
+    //Ajout info
+    add_integer(desc, "nbPlayers", g->nbPlayer);
+    add_object(desc, "map", map);
+    //Si besoins ajout un joueur
+    if(playerIndex >= 0 && playerIndex < MAXPLAYER && g->player[playerIndex]->ini){
+        player = describe_player(g->player[playerIndex]);
+        add_object(desc, "player", player);
+        clean_json_encoder(player);
+        free(player);
+    }
+    //Nettoyage
+    clean_json_encoder(map);
+    free(map);
+    return desc;
+}
+
 void stop_game(Game* g){
     //Indique comme inactive
     g->active = false;
@@ -175,7 +232,7 @@ void stop_game(Game* g){
         free(g->player[i]);
     }
     //Libere la memoire
-    for(int i = 0; g->width; i++){
+    for(int i = 0; i < g->width; i++){
         free(g->map[i]);
     }
     free(g->map);

+ 4 - 0
Serveur/game.h

@@ -61,6 +61,10 @@ int* map_size(char*);
 
 int create_game(char*, char*);
 
+int add_player(Game*, int);
+
+JsonEncoder* describe_game(Game*, int);
+
 void stop_game(Game*);
 
 void clean_games();

+ 110 - 1
Serveur/handler.c

@@ -15,12 +15,48 @@
 #include "handler.h"
 
 /* --- Extern --- */
+extern Game game[MAXGAME];
 extern int nbGame;
 
+/* --- Fonctions privées --- */
+/**
+ * Cherche dans quel partie est un client
+ * @param int L'id du client
+ * @return int L'index de la partie
+ */
+int search_client_game(int cliId){
+    int index = ERR;
+    for(int i = 0; i < MAXGAME; i++){
+        //Regarde si la game est active et avec des joueurs
+        if(game[i].active && game[i].nbPlayer > 0){
+            //Parcours les joueurs
+            for(int j = 0; j < MAXPLAYER; j++){
+                //Si le joueur est actif
+                if(game[i].player[j]->ini){
+                    //Si l'id est le bon
+                    if(game[i].player[j]->id == cliId){
+                        index = i;
+                        break;
+                    }
+                }
+            }
+        }
+        //Si on a un resultat
+        if(index != ERR){
+            break;
+        }
+    }
+    //Retour
+    return index;
+}
+
 /* --- Fonctions publiques --- */
 void ini_handler(){
+    //Get
     add_handler("GET", "client/end", handler_client_end);
     add_handler("GET", "game/list", handler_game_list);
+    //Post
+    add_handler("POST", "game/create", handler_game_create);
 }
 
 int handler_client_end(int cliId, JsonParser* json){
@@ -39,7 +75,7 @@ int handler_game_list(int cliId, JsonParser* json){
     //Creation reponse
     ini_encoder(&reponse);
     add_string(&reponse, "action", "game/list");
-    add_string(&reponse, "statut", "200");
+    add_string(&reponse, "status", "200");
     add_string(&reponse, "message", "ok");
     if(game == NULL){
         add_integer(&reponse, "numberGameList", 0);
@@ -60,4 +96,77 @@ int handler_game_list(int cliId, JsonParser* json){
     clean_json_array(map);
     free(map);
     return SUCCESS;
+}
+
+int handler_game_create(int cliId, JsonParser* json){
+    char* map, * name, * msg;
+    int index, joueur;
+    JsonEncoder* reponse;
+    //Verification arguments
+    if(get_pos(json, "name") == JSON_ERROR){
+        send_err_client(cliId, EREQUEST);
+        adderror("Le json du client est incorrect");
+        return FAIL;
+    }
+    if(get_pos(json, "map") == JSON_ERROR){
+        send_err_client(cliId, EREQUEST);
+        adderror("Le json du client est incorrect");
+        return FAIL;
+    }
+    //Recup valeur
+    map = get_string(json, "map");
+    name = get_string(json, "name");
+    //Creation
+    index = create_game(name, map);
+    if(index == ERR){
+        reponse = malloc(sizeof(JsonEncoder));
+        ini_encoder(reponse);
+        add_string(reponse, "status", "501");
+        add_string(reponse, "message", "Cannot create game");
+        if(!send_client(cliId, reponse)){
+            adderror("Impossible de répondre au client");
+        }
+        clean_json_encoder(reponse);
+        free(reponse);
+        return FAIL;
+    }
+    //Ajout du joueur dans la partie
+    joueur = add_player(&game[index], cliId);
+    if(joueur == ERR){
+        stop_game(&game[index]);
+        reponse = malloc(sizeof(JsonEncoder));
+        ini_encoder(reponse);
+        add_string(reponse, "action", "game/create");
+        add_string(reponse, "status", "501");
+        add_string(reponse, "message", "Cannot create game");
+        if(!send_client(cliId, reponse)){
+            adderror("Impossible de répondre au client");
+        }
+        clean_json_encoder(reponse);
+        free(reponse);
+        return FAIL;
+    }
+    //Recup info
+    reponse = describe_game(&game[index], joueur);
+    //Ajout infos
+    msg = new_string(25 + strlen(map));
+    sprintf(msg, "Game created with %s", map);
+    add_string(reponse, "action", "game/create");
+    add_string(reponse, "status", "201");
+    add_string(reponse, "message", msg);
+    add_string(reponse, "startPos", "0,0");
+    free(msg);
+    //Envoi
+    if(!send_client(cliId, reponse)){
+        adderror("Impossible de répondre au client");
+        clean_json_encoder(reponse);
+        free(reponse);
+        return FAIL;
+    }
+    //Nettoyage
+    clean_json_encoder(reponse);
+    free(reponse);
+    free(map);
+    free(name);
+    return SUCCESS;
 }

+ 2 - 0
Serveur/handler.h

@@ -34,5 +34,7 @@ int handler_client_end(int, JsonParser*);
  */
 int handler_game_list(int, JsonParser*);
 
+int handler_game_create(int, JsonParser*);
+
 #endif /* HANDLER_H */
 

+ 32 - 9
Serveur/json_array.c

@@ -439,10 +439,15 @@ boolean add_array_string(JsonArray* this, char* val) {
         return false;
     }
     //Creation chaine
-    int length = strlen(val) + 2 + 1; //val + 2 guillemet + \0
+    int index = 0, length = strlen(val) + 2 + 1; //val + 2 guillemet + \0
     char* str = malloc(length * sizeof (char));
     memset(str, 0, length);
-    sprintf(str, "\"%s\"", val);
+    //Copie
+    str[index++] = '"';
+    for(int i = 0; i < strlen(val); i++, index++){
+        str[index] = val[i];
+    }
+    str[index++] = '"';
     //Ajout
     add_array_value(this, str);
     free(str);
@@ -472,7 +477,7 @@ boolean add_array_integer(JsonArray* this, int val) {
     int length = ceil(val / 10.0) + 1; //val + \0
     char* str = malloc(length * sizeof (char));
     memset(str, 0, length);
-    sprintf(str, "%d", val);
+    snprintf(str, length, "%d", val);
     //Ajout
     add_array_value(this, str);
     free(str);
@@ -497,22 +502,28 @@ boolean add_array_boolean(JsonArray* this, boolean val) {
 }
 
 boolean add_array_array(JsonArray* this, JsonArray* val) {
+    char* json;
     //Verification
     if (this->mode != JSON_ARRAY_ENCODER) {
         return false;
     }
     //Ajout
-    add_array_value(this, json_encode_array(val));
+    json = json_encode_array(val);
+    add_array_value(this, json);
+    free(json);
     return true;
 }
 
 boolean add_array_object(JsonArray* this, JsonEncoder* val) {
+    char* json;
     //Verification
     if (this->mode != JSON_ARRAY_ENCODER) {
         return false;
     }
     //Ajout
-    add_array_value(this, json_encode(val));
+    json = json_encode(val);
+    add_array_value(this, json);
+    free(json);
     return true;
 }
 
@@ -522,6 +533,7 @@ char* json_encode_array(JsonArray* this) {
         return false;
     }
     boolean first = true;
+    int length, index = 0;
     //Allocation chaine
     char* str;
     str = malloc((this->encoder->length + 2) * sizeof (char)); // La chaine + []
@@ -529,17 +541,28 @@ char* json_encode_array(JsonArray* this) {
     //Creation de la chaine
     JsonNode* node;
     node = this->encoder->tab;
-    str[0] = '[';
+    str[index++] = '[';
     while (node != NULL) {
-        if (first) {
+        if(!first){
+            str[index++] = ',';
+            str[index++] = ' ';
+        }
+        length = strlen(node->str);
+        for(int i = 0; i < length; i++, index++){
+            str[index] = node->str[i];
+        }
+        if(first){
+            first = false;
+        }
+        /*if (first) {
             sprintf(str, "%s%s", str, node->str);
             first = false;
         } else {
             sprintf(str, "%s, %s", str, node->str);
-        }
+        }*/
         node = node->next;
     }
-    sprintf(str, "%s]", str);
+    str[index++] = ']';
     //Retour
     return str;
 }

+ 97 - 18
Serveur/json_encoder.c

@@ -62,10 +62,22 @@ void add_value(JsonEncoder* this, char* str){
 
 void add_string(JsonEncoder* this, char* key, char* val){
     //Creation chaine
-    int length = strlen(key) + strlen(val) + 4 + 2 + 1; //clef + val + 4 guillemet + ": " + \0
+    int index = 0, length = strlen(key) + strlen(val) + 4 + 2 + 1; //clef + val + 4 guillemet + ": " + \0
     char* str = malloc(length * sizeof(char)); 
     memset(str, 0, length);
-    sprintf(str, "\"%s\": \"%s\"", key, val);
+    //Copie
+    str[index++] = '"';
+    for(int i = 0; i < strlen(key); i++, index++){
+        str[index] = key[i];
+    }
+    str[index++] = '"';
+    str[index++] = ':';
+    str[index++] = ' ';
+    str[index++] = '"';
+    for(int i = 0; i < strlen(val); i++, index++){
+        str[index] = val[i];
+    }
+    str[index++] = '"';
     //Ajout
     add_value(this, str);
     free(str);
@@ -76,21 +88,44 @@ void add_number(JsonEncoder* this, char* key, double val, int ndigit){
     char nombre[20];
     ftoa(val, nombre, ndigit);
     //Creation chaine
-    int length = strlen(key) + strlen(nombre) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
+    int index = 0, length = strlen(key) + strlen(nombre) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
     char* str = malloc(length * sizeof(char));
     memset(str, 0, length);
-    sprintf(str, "\"%s\": %s", key, nombre);
+    //Copie
+    str[index++] = '"';
+    for(int i = 0; i < strlen(key); i++, index++){
+        str[index] = key[i];
+    }
+    str[index++] = '"';
+    str[index++] = ':';
+    str[index++] = ' ';
+    for(int i = 0; i < strlen(nombre); i++, index++){
+        str[index] = nombre[i];
+    }
     //Ajout
     add_value(this, str);
     free(str);
 }
 
 void add_integer(JsonEncoder* this, char* key, int val){
+    //Double en string
+    char nombre[20];
+    snprintf(nombre, 20, "%d", val);
     //Creation chaine
-    int length = strlen(key) + ceil(val/10.0) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
-    char* str = malloc(length * sizeof(char)); 
+    int index = 0, length = strlen(key) + strlen(nombre) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
+    char* str = malloc(length * sizeof(char));
     memset(str, 0, length);
-    sprintf(str, "\"%s\": %d", key, val);
+    //Copie
+    str[index++] = '"';
+    for(int i = 0; i < strlen(key); i++, index++){
+        str[index] = key[i];
+    }
+    str[index++] = '"';
+    str[index++] = ':';
+    str[index++] = ' ';
+    for(int i = 0; i < strlen(nombre); i++, index++){
+        str[index] = nombre[i];
+    }
     //Ajout
     add_value(this, str);
     free(str);
@@ -105,10 +140,20 @@ void add_boolean(JsonEncoder* this, char* key, boolean val){
         strcpy(bool, "false");
     }
     //Creation chaine
-    int length = strlen(key) + strlen(bool) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
+    int index = 0, length = strlen(key) + strlen(bool) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
     char* str = malloc(length * sizeof(char));
     memset(str, 0, length);
-    sprintf(str, "\"%s\": %s", key, bool);
+    //Copie
+    str[index++] = '"';
+    for(int i = 0; i < strlen(key); i++, index++){
+        str[index] = key[i];
+    }
+    str[index++] = '"';
+    str[index++] = ':';
+    str[index++] = ' ';
+    for(int i = 0; i < strlen(bool); i++, index++){
+        str[index] = bool[i];
+    }
     //Ajout
     add_value(this, str);
     free(str);
@@ -123,10 +168,20 @@ void add_array(JsonEncoder* this, char* key, JsonArray* val){
     char* json;
     json = json_encode_array(val);
     //Creation chaine
-    int length = strlen(key) + strlen(json) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
+    int index = 0, length = strlen(key) + strlen(json) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
     char* str = malloc(length * sizeof(char));
     memset(str, 0, length);
-    sprintf(str, "\"%s\": %s", key, json);
+    //Copie
+    str[index++] = '"';
+    for(int i = 0; i < strlen(key); i++, index++){
+        str[index] = key[i];
+    }
+    str[index++] = '"';
+    str[index++] = ':';
+    str[index++] = ' ';
+    for(int i = 0; i < strlen(json); i++, index++){
+        str[index] = json[i];
+    }
     //Ajout
     add_value(this, str);
     free(str);
@@ -138,10 +193,20 @@ void add_object(JsonEncoder* this, char* key, JsonEncoder* val){
     char* json;
     json = json_encode(val);
     //Creation chaine
-    int length = strlen(key) + strlen(json) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
+    int index = 0, length = strlen(key) + strlen(json) + 2 + 2 + 1; //clef + val + 2 guillemets + ": " + \0
     char* str = malloc(length * sizeof(char));
     memset(str, 0, length);
-    sprintf(str, "\"%s\": %s", key, json);
+    //Copie
+    str[index++] = '"';
+    for(int i = 0; i < strlen(key); i++, index++){
+        str[index] = key[i];
+    }
+    str[index++] = '"';
+    str[index++] = ':';
+    str[index++] = ' ';
+    for(int i = 0; i < strlen(json); i++, index++){
+        str[index] = json[i];
+    }
     //Ajout
     add_value(this, str);
     free(str);
@@ -150,6 +215,7 @@ void add_object(JsonEncoder* this, char* key, JsonEncoder* val){
 
 char* json_encode(JsonEncoder* this){
     boolean first = true;
+    int length, index = 0;
     //Allocation chaine
     char* str;
     str = malloc((this->length + 2) * sizeof(char)); // La chaine + {}
@@ -157,17 +223,30 @@ char* json_encode(JsonEncoder* this){
     //Creation de la chaine
     JsonNode* node;
     node = this->head;
-    str[0] = '{';
+    str[index++] = '{';
     while(node != NULL){
+        if(!first){
+            str[index++] = ',';
+            str[index++] = ' ';
+        }
+        length = strlen(node->str);
+        for(int i = 0; i < length; i++, index++){
+            str[index] = node->str[i];
+        }
         if(first){
-            sprintf(str, "%s%s", str, node->str);
             first = false;
-        } else {
-            sprintf(str, "%s, %s", str, node->str);
         }
+        /*if(first){
+            snprintf(str, this->length + 2, "%s%s", str, node->str);
+            printf("Json %s\n", str);
+            first = false;
+        } else {
+            snprintf(str, this->length + 2, "%s, %s", str, node->str);
+            printf("Json %s\n", str);
+        }*/
         node = node->next;
     }
-    sprintf(str, "%s}", str);
+    str[index++] = '}';
     //Retour
     return str;
 }

+ 1 - 1
Serveur/main.c

@@ -46,7 +46,7 @@ void notify_close(int code){
     //Encode la reponse
     JsonEncoder je;
     ini_encoder(&je);
-    add_string(&je, "statut", codestr);
+    add_string(&je, "status", codestr);
     add_string(&je, "message", "Server close");
     //Avertit tous les clients
     notify_all("POST", "server/end", &je);

+ 61 - 0
Serveur/player.c

@@ -11,6 +11,7 @@
 
 void create_player(Player* p, Client* c){
     //Initialisation valeurs
+    p->ini = true;
     p->id = c->id;
     p->cli = c;
     p->x = 0;
@@ -32,6 +33,66 @@ void create_player(Player* p, Client* c){
     p->major = false;
 }
 
+JsonEncoder* describe_player(Player* p){
+    JsonEncoder* desc = malloc(sizeof(JsonEncoder));
+    JsonArray* bonus = malloc(sizeof(JsonArray));
+    char buffer[BUFFER_SIZE];
+    //Initialisation
+    ini_encoder(desc);
+    ini_array_encoder(bonus);
+    //Calcul bonus malus
+    if(p->bombUp > 0){
+        memset(buffer, 0, BUFFER_SIZE);
+        snprintf(buffer, BUFFER_SIZE, "{\"class\": \"bomb_up\", \"number\": %d}", p->bombUp);
+        add_array_value(bonus, buffer);
+    }
+    if(p->bombDown > 0){
+        memset(buffer, 0, BUFFER_SIZE);
+        snprintf(buffer, BUFFER_SIZE, "{\"class\": \"bomb_down\", \"number\": %d}", p->bombDown);
+        add_array_value(bonus, buffer);
+    }
+    if(p->firePower > 0){
+        memset(buffer, 0, BUFFER_SIZE);
+        snprintf(buffer, BUFFER_SIZE, "{\"class\": \"fire_power\", \"number\": %d}", p->firePower);
+        add_array_value(bonus, buffer);
+    }
+    if(p->scooter > 0){
+        memset(buffer, 0, BUFFER_SIZE);
+        snprintf(buffer, BUFFER_SIZE, "{\"class\": \"scooter\", \"number\": %d}", p->scooter);
+        add_array_value(bonus, buffer);
+    }
+    if(p->brokenLeg > 0){
+        memset(buffer, 0, BUFFER_SIZE);
+        snprintf(buffer, BUFFER_SIZE, "{\"class\": \"broken_leg\", \"number\": %d}", p->brokenLeg);
+        add_array_value(bonus, buffer);
+    }
+    if(p->lifeUp > 0){
+        memset(buffer, 0, BUFFER_SIZE);
+        snprintf(buffer, BUFFER_SIZE, "{\"class\": \"life_up\", \"number\": %d}", p->lifeUp);
+        add_array_value(bonus, buffer);
+    }
+    if(p->lifeMax > 0){
+        memset(buffer, 0, BUFFER_SIZE);
+        snprintf(buffer, BUFFER_SIZE, "{\"class\": \"life_max\", \"number\": %d}", p->lifeMax);
+        add_array_value(bonus, buffer);
+    }
+    //Ajout valeur
+    add_integer(desc, "id", p->id);
+    add_integer(desc, "life", p->life);
+    add_integer(desc, "maxLife", p->maxLife);
+    add_integer(desc, "speed", p->speed);
+    add_integer(desc, "currentNbClassicBomb", p->classicBomb);
+    add_integer(desc, "currentNbMine", p->mine);
+    add_integer(desc, "currentNbRemoteBomb", p->remoteBomb);
+    add_integer(desc, "maxNbBomb", p->maxBomb);
+    add_array(desc, "bonus-malus", bonus);
+    //Nettoyage
+    clean_json_array(bonus);
+    free(bonus);
+    return desc;
+}
+
 void delete_player(Player* p){
     p->cli = NULL;
+    p->ini = false;
 }

+ 3 - 0
Serveur/player.h

@@ -11,6 +11,7 @@
 /* --- Include --- */
 #include "constante.h"
 #include "client.h"
+#include "json.h"
 
 /* --- Structure --- */
 typedef struct{
@@ -46,6 +47,8 @@ typedef struct{
  */
 void create_player(Player*, Client*);
 
+JsonEncoder* describe_player(Player*);
+
 /**
  * Supprime un joueur
  * @param Player* La structure à supprimer

+ 27 - 1
Serveur/str.c

@@ -141,4 +141,30 @@ void ftoa(float n, char *res, int afterpoint) {
   
         intToStr((int)fpart, res + i + 1, afterpoint); 
     } 
-} 
+} 
+
+char* remove_char(char* src, char carac){
+    int length, compteur = 0;
+    char* tmp = src;
+    char* str;
+    //Compte le nombre de fois ou le caracère apparait
+    while(*tmp){
+        if(*tmp == carac){
+            compteur++;
+        }
+        tmp++;
+    }
+    //Creation nouvelle chaine
+    length = strlen(src) - compteur;
+    str = new_string(length);
+    tmp = str;
+    //Copie la chaine
+    while(*src){
+        if(*src != carac){
+            *tmp = *src;
+            tmp++;
+        }
+        src++;
+    }
+    return str;
+}

+ 8 - 0
Serveur/str.h

@@ -69,5 +69,13 @@ void reverse(char*, int);
  */
 void ftoa(float, char*, int);
 
+/**
+ * Supprime un craractere d'une chaine
+ * @param char* La chaine à traiter
+ * @param char Le caractere à supprimer
+ * @return char* La chaine sans le caractere
+ */
+char* remove_char(char*, char);
+
 #endif /* STR_H */