/* * File: handler.c * Author: Arthur Brandao * * Created on 23 novembre 2018 */ #include #include #include #include "error.h" #include "bomberstudent_server.h" #include "client.h" #include "player.h" #include "delay.h" #include "handler.h" /* --- Extern --- */ extern Game game[MAXGAME]; extern int nbGame; pthread_mutex_t gameMutex[MAXGAME]; pthread_mutex_t playerMutex[MAXGAME * MAXPLAYER]; /* --- 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++) { pthread_mutex_lock(&gameMutex[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++) { pthread_mutex_lock(&playerMutex[(i * 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; pthread_mutex_unlock(&playerMutex[(i * MAXPLAYER) + j]); break; } } pthread_mutex_unlock(&playerMutex[(i * MAXPLAYER) + j]); } } pthread_mutex_unlock(&gameMutex[i]); //Si on a un resultat if (index != ERR) { break; } } //Retour return index; } /** * Cherche une partie par son nom * @param char* Le nom de la partie * @return int L'index dans le tableau */ int search_game(char* name) { int index = ERR; for (int i = 0; i < MAXGAME; i++) { pthread_mutex_lock(&gameMutex[i]); //Regarde si la game est active et le nom if (game[i].active && strncmp(game[i].name, name, strlen(game[i].name)) == 0) { index = i; pthread_mutex_unlock(&gameMutex[i]); break; } pthread_mutex_unlock(&gameMutex[i]); } return index; } /** * Parse une postion en 2 entier X et Y * @param char* La position * @param int* Le resultat en X * @param int Le resultat en Y * @return Reussite */ boolean parse_pos(char* pos, int* x, int* y) { int index = 0; char* cy, * cx; //Cherche la separation while (pos[index] != '\0' && pos[index] != ',') { index++; } if (pos[index] == '\0') { return false; } //Separation chaine cx = malloc(sizeof (char) * index + 1); memset(cx, 0, index + 1); strncpy(cx, pos, index); cy = pos + index + 1; //Parse *x = atoi(cx); *y = atoi(cy); //Nettoyage free(cx); return true; } /* --- Fonctions publiques --- */ void ini_handler() { //Get add_handler("GET", "game/list", handler_game_list); //Post add_handler("POST", "client/end", handler_client_end); add_handler("POST", "game/create", handler_game_create); add_handler("POST", "game/join", handler_game_join); add_handler("POST", "game/quit", handler_game_quit); add_handler("POST", "player/move", handler_player_move); add_handler("POST", "object/new", handler_object_new); add_handler("POST", "attack/bomb", handler_attack_bomb); add_handler("POST", "attack/remote/go", handler_attack_remote_go); } int handler_client_end(int cliId, JsonParser* json) { //Verif que le client est toujours actif if (get_client(cliId) != NULL) { printf("Deconnexion du client %d\n", cliId); handler_game_quit(cliId, json); remove_client(cliId); } return SUCCESS; } int handler_game_list(int cliId, JsonParser* json) { JsonEncoder reponse; JsonArray game; JsonArray map; int nb; //Recup la liste des parties et des cartes ini_array_encoder(&game); ini_array_encoder(&map); nb = list_game(&game); list_map(&map); //Creation reponse ini_encoder(&reponse); add_string(&reponse, "action", "game/list"); add_integer(&reponse, "status", 200); add_string(&reponse, "message", "ok"); if (nb == 0) { add_integer(&reponse, "numberGameList", 0); add_value(&reponse, ", \"games\": []"); } else { add_integer(&reponse, "numberGameList", nb); add_array(&reponse, "games", &game); } add_array(&reponse, "maps", &map); //Envoi reponse au client if (!send_client(cliId, &reponse)) { adderror("Impossible de répondre au client"); return FAIL; } //Nettoyage clean_json_encoder(&reponse); clean_json_array(&map); clean_json_array(&game); 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"); //Initialisation reponse JSON ini_encoder(&reponse); add_string(&reponse, "action", "game/create"); //Verif nom non existant if (search_game(name) != ERR) { add_integer(&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); return FAIL; } //Creation index = create_game(name, map); if (index == ERR) { add_integer(&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); return FAIL; } //Ajout du joueur dans la partie joueur = add_player(&game[index], cliId); if (joueur == ERR) { stop_game(&game[index]); add_integer(&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); return FAIL; } //Recup info describe_game(&game[index], joueur, &reponse); //Ajout infos msg = new_string(25 + strlen(map)); sprintf(msg, "Game created with %s", map); add_integer(&reponse, "status", 201); add_string(&reponse, "message", msg); add_string(&reponse, "startPos", "1,1"); free(msg); //Envoi if (!send_client(cliId, &reponse)) { adderror("Impossible de répondre au client"); clean_json_encoder(&reponse); return FAIL; } //Nettoyage clean_json_encoder(&reponse); free(map); free(name); return SUCCESS; } int handler_game_join(int cliId, JsonParser* json) { char* name; 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; } //Recup valeur name = get_string(json, "name"); //Initialisation json reponse ini_encoder(&reponse); add_string(&reponse, "action", "game/join"); //Verif game existe index = search_game(name); if (index == ERR) { add_integer(&reponse, "status", 501); add_string(&reponse, "message", "Cannot join the game"); if (!send_client(cliId, &reponse)) { adderror("Impossible de répondre au client"); } clean_json_encoder(&reponse); free(name); return FAIL; } //Ajout du joueur dans la partie joueur = add_player(&game[index], cliId); if (joueur == ERR) { add_integer(&reponse, "status", 501); add_string(&reponse, "message", "Cannot join the game"); if (!send_client(cliId, &reponse)) { adderror("Impossible de répondre au client"); } clean_json_encoder(&reponse); free(name); return FAIL; } //Recup info describe_game(&game[index], joueur, &reponse); //Ajout infos add_integer(&reponse, "status", 201); add_string(&reponse, "startPos", "1,1"); //Envoi if (!send_client(cliId, &reponse)) { adderror("Impossible de répondre au client"); clean_json_encoder(&reponse); free(name); return FAIL; } //Nettoyage clean_json_encoder(&reponse); free(name); //Avertit les autres joueurs add_integer(&reponse, "id", cliId); add_string(&reponse, "pos", "1,1"); notify_player(&game[index], "POST", "game/newplayer", &reponse, cliId); //Nettoyage clean_json_encoder(&reponse); return SUCCESS; } int handler_game_quit(int cliId, JsonParser* json) { boolean find = false; JsonEncoder notif; //Cherche le client dans les parties for (int i = 0; i < MAXGAME; i++) { pthread_mutex_lock(&gameMutex[i]); if (game[i].active) { for (int j = 0; j < MAXPLAYER; j++) { pthread_mutex_lock(&playerMutex[(i * MAXPLAYER) + j]); if (game[i].player[j]->ini && game[i].player[j]->id == cliId) { pthread_mutex_unlock(&playerMutex[(i * MAXPLAYER) + j]); pthread_mutex_unlock(&gameMutex[i]); find = true; remove_player(&game[i], j); //Avertit les autres joueurs ini_encoder(¬if); add_integer(¬if, "player", cliId); notify_player(&game[i], "POST", "game/quit", ¬if, cliId); clean_json_encoder(¬if); break; } pthread_mutex_unlock(&playerMutex[(i * MAXPLAYER) + j]); } if (find) { break; } } pthread_mutex_unlock(&gameMutex[i]); } return SUCCESS; } int handler_player_move(int cliId, JsonParser* json) { char* move; int index, playerIndex = 0, x = 0, y = 0; Player* p = NULL; boolean ok = false, mine = false; JsonEncoder reponse; //Verification arguments if (get_pos(json, "move") == JSON_ERROR) { return FAIL; } //Recup valeur move = get_string(json, "move"); //Verif game existe index = search_client_game(cliId); if (index == ERR) { free(move); adderror("La game du client n'existe pas"); return FAIL; } //Recup le joueur pthread_mutex_lock(&gameMutex[index]); for (int i = 0; i < MAXPLAYER; i++) { pthread_mutex_lock(&playerMutex[(index * MAXPLAYER) + i]); if (game[index].player[i]->ini && game[index].player[i]->id == cliId) { playerIndex = i; p = game[index].player[i]; break; } pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + i]); } if (p == NULL) { pthread_mutex_unlock(&gameMutex[index]); free(move); adderror("Aucun joueur associé au client"); return FAIL; } //Regarde si le joueur peut bouger if (strncmp(move, "up", 2) == 0) { if (p->y > 0 && !player_collision(&game[index], p->x, p->y - 1) && (game[index].map[p->x][p->y - 1] == '_' /*Vide*/ || game[index].map[p->x][p->y - 1] == '2' /*Mine*/)) { ok = true; //Bouge le joueur sur la carte p->y--; //Si marche sur une mine if (game[index].map[p->x][p->y] == '2') { mine = true; x = p->x; y = p->y; } } } else if (strncmp(move, "down", 4) == 0) { if (p->y < game[index].height && !player_collision(&game[index], p->x, p->y + 1) && (game[index].map[p->x][p->y + 1] == '_' /*Vide*/ || game[index].map[p->x][p->y + 1] == '2' /*Mine*/)) { ok = true; //Bouge le joueur sur la carte p->y++; //Si marche sur une mine if (game[index].map[p->x][p->y] == '2') { mine = true; x = p->x; y = p->y; } } } else if (strncmp(move, "left", 4) == 0) { if (p->x > 0 && !player_collision(&game[index], p->x - 1, p->y) && (game[index].map[p->x - 1][p->y] == '_' /*Vide*/ || game[index].map[p->x - 1][p->y] == '2' /*Mine*/)) { ok = true; //Bouge le joueur sur la carte p->x--; //Si marche sur une mine if (game[index].map[p->x][p->y] == '2') { mine = true; x = p->x; y = p->y; } } } else if (strncmp(move, "right", 4) == 0) { if (p->x < game[index].width && !player_collision(&game[index], p->x + 1, p->y) && (game[index].map[p->x + 1][p->y] == '_' /*Vide*/ || game[index].map[p->x + 1][p->y] == '2' /*Mine*/)) { ok = true; //Bouge le joueur sur la carte p->x++; //Si marche sur une mine if (game[index].map[p->x][p->y] == '2') { mine = true; x = p->x; y = p->y; } } } else { pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); pthread_mutex_unlock(&gameMutex[index]); free(move); adderror("Le json du client est incorrect"); return FAIL; } pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); pthread_mutex_unlock(&gameMutex[index]); //Notifie les joueurs si mouvement ok if (ok) { ini_encoder(&reponse); add_integer(&reponse, "player", cliId); add_string(&reponse, "dir", move); notify_player(&game[index], "POST", "player/position/update", &reponse, -1); //Si marche sur une mine if (mine) { JsonEncoder notif; ini_encoder(¬if); if(!bomb_explode(&game[index], -1, x, y, ¬if)){ adderror("Erreur explosion mine"); } clean_json_encoder(¬if); } } //Nettoyage clean_json_encoder(&reponse); free(move); return SUCCESS; } int handler_object_new(int cliId, JsonParser* json) { char* class; int index, type, length, playerIndex = 0; Player* p = NULL; obj_node* objn; JsonEncoder reponse; //Verification arguments if (get_pos(json, "class") == JSON_ERROR) { send_err_client(cliId, EREQUEST); adderror("Le json du client est incorrect"); return FAIL; } //Recup valeur class = get_string(json, "class"); //Verif valeur class length = strlen(class); if (strncmp(class, "classic", length) == 0) { type = OBJ_BCLASSIC; } else if (strncmp(class, "mine", length) == 0) { type = OBJ_BMINE; } else if (strncmp(class, "remote", length) == 0) { type = OBJ_BREMOTE; } else if (strncmp(class, "bomb_up", length) == 0) { type = OBJ_BOMBUP; } else if (strncmp(class, "bomb_down", length) == 0) { type = OBJ_BOMBDOWN; } else if (strncmp(class, "fire_power", length) == 0) { type = OBJ_FIREPOWER; } else if (strncmp(class, "scooter", length) == 0) { type = OBJ_SCOOTER; } else if (strncmp(class, "broken_legs", length) == 0) { type = OBJ_BROKENLEG; } else if (strncmp(class, "life_up", length) == 0) { type = OBJ_LIFEUP; } else if (strncmp(class, "life_max", length) == 0) { type = OBJ_LIFEMAX; } else if (strncmp(class, "major", length) == 0) { type = OBJ_MAJOR; } else { free(class); send_err_client(cliId, EREQUEST); adderror("Le json du client est incorrect"); return FAIL; } //Verif game existe index = search_client_game(cliId); if (index == ERR) { free(class); send_err_client(cliId, EREQUEST); adderror("La game du client n'existe pas"); return FAIL; } //Recup le joueur pthread_mutex_lock(&gameMutex[index]); for (int i = 0; i < MAXPLAYER; i++) { pthread_mutex_lock(&playerMutex[(index * MAXPLAYER) + i]); if (game[index].player[i]->ini && game[index].player[i]->id == cliId) { playerIndex = i; p = game[index].player[i]; pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + i]); break; } pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + i]); } if (p == NULL) { pthread_mutex_unlock(&gameMutex[index]); free(class); send_err_client(cliId, EREQUEST); adderror("Aucun joueur associé au client"); return FAIL; } //Regarde si un objet correspond objn = object_search(game[index].object, type, p->x, p->y); if (objn == NULL) { ini_encoder(&reponse); add_string(&reponse, "action", "object/new"); add_integer(&reponse, "status", 501); add_string(&reponse, "message", "No object"); //Envoi if (!send_client(cliId, &reponse)) { adderror("Impossible de répondre au client"); clean_json_encoder(&reponse); free(class); return FAIL; } } else { //Ajoute l'objet au joueur pthread_mutex_lock(&playerMutex[(index * MAXPLAYER) + playerIndex]); add_player_object(p, type); //Creation reponse ini_encoder(&reponse); describe_player(p, &reponse); add_integer(&reponse, "status", 201); add_string(&reponse, "action", "object/new"); pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); //Envoi if (!send_client(cliId, &reponse)) { adderror("Impossible de répondre au client"); clean_json_encoder(&reponse); free(class); return FAIL; } //Si major lance le timer pour avertir de la fin if (type == OBJ_MAJOR) { delay(TIMEMAJOR, index, playerIndex, callback_major_end); } } //Nettoyage pthread_mutex_unlock(&gameMutex[index]); free(class); clean_json_encoder(&reponse); return SUCCESS; } int handler_attack_bomb(int cliId, JsonParser* json) { char* pos, * class; int x, y, length, index, playerIndex = 0; Player* p = NULL; boolean ok = false; JsonEncoder reponse, player; //Verification arguments if (get_pos(json, "class") == JSON_ERROR) { send_err_client(cliId, EREQUEST); adderror("Le json du client est incorrect"); return FAIL; } if (get_pos(json, "pos") == JSON_ERROR) { send_err_client(cliId, EREQUEST); adderror("Le json du client est incorrect"); return FAIL; } //Recup valeur class = get_string(json, "class"); pos = get_string(json, "pos"); //Parse les valeurs de pos if (!parse_pos(pos, &x, &y)) { free(class); free(pos); send_err_client(cliId, EREQUEST); adderror("Le json du client est incorrect"); return FAIL; } //Verif game existe index = search_client_game(cliId); if (index == ERR) { free(class); free(pos); send_err_client(cliId, EREQUEST); adderror("La game du client n'existe pas"); return FAIL; } //Recup le joueur pthread_mutex_lock(&gameMutex[index]); for (int i = 0; i < MAXPLAYER; i++) { pthread_mutex_lock(&playerMutex[(index * MAXPLAYER) + i]); if (game[index].player[i]->ini && game[index].player[i]->id == cliId) { playerIndex = i; p = game[index].player[i]; break; } pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + i]); } if (p == NULL) { pthread_mutex_unlock(&gameMutex[index]); free(class); free(pos); send_err_client(cliId, EREQUEST); adderror("Aucun joueur associé au client"); return FAIL; } //Verif si la bombe peut être posé et pose length = strlen(class); if (strncmp(class, "classic", length) == 0) { ok = p->classicBomb > 0 && p->nbBomb < p->maxBomb && game[index].map[x][y] == '_' && !player_collision(&game[index], x, y); if (ok) { p->nbBomb++; p->classicBomb--; game[index].map[x][y] = '1'; //Lance timer int* tab = malloc(sizeof(int) * 2); tab[0] = x; tab[1] = y; //Ajout bombe object_add(p->bomb, OBJ_BCLASSIC, x, y); //Timer delay_data(TIMEBOMB, index, playerIndex, (void*) tab, callback_bomb_explode); } } else if (strncmp(class, "mine", length) == 0) { ok = p->mine > 0 && p->nbBomb < p->maxBomb && game[index].map[x][y] == '_' && !player_collision(&game[index], x, y); if (ok) { p->nbBomb++; p->mine--; game[index].map[x][y] = '2'; //Ajout bombe object_add(p->bomb, OBJ_BMINE, x, y); } } else if (strncmp(class, "remote", length) == 0) { ok = p->remoteBomb > 0 && p->nbBomb < p->maxBomb && game[index].map[x][y] == '_' && !player_collision(&game[index], x, y); if (ok) { p->nbBomb++; p->remoteBomb--; game[index].map[x][y] = '3'; //Ajoute la bombe dans la liste du joueur object_add(p->bomb, OBJ_BREMOTE, x, y); } } else { pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); pthread_mutex_unlock(&gameMutex[index]); free(pos); free(class); send_err_client(cliId, EREQUEST); adderror("Le json du client est incorrect"); return FAIL; } //Initialisation json ini_encoder(&reponse); add_string(&reponse, "action", "attack/bomb"); //Reponse if (ok) { add_integer(&reponse, "status", 201); describe_player(p, &player); add_object(&reponse, "player", &player); clean_json_encoder(&player); //Envoi if (!send_client(cliId, &reponse)) { adderror("Impossible de répondre au client"); pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); pthread_mutex_unlock(&gameMutex[index]); clean_json_encoder(&reponse); free(pos); free(class); return FAIL; } //Notification des autre joeurs pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); pthread_mutex_unlock(&gameMutex[index]); clean_json_encoder(&reponse); add_string(&reponse, "pos", pos); add_string(&reponse, "class", class); notify_player(&game[index], "POST", "attack/newbomb", &reponse, -1); } else { add_integer(&reponse, "status", 403); add_string(&reponse, "message", "Forbidden action"); //Envoi if (!send_client(cliId, &reponse)) { adderror("Impossible de répondre au client"); pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); pthread_mutex_unlock(&gameMutex[index]); clean_json_encoder(&reponse); free(pos); free(class); return FAIL; } pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); pthread_mutex_unlock(&gameMutex[index]); } //Nettoyage clean_json_encoder(&reponse); free(pos); free(class); return SUCCESS; } int handler_attack_remote_go(int cliId, JsonParser* json){ int index, playerIndex; JsonEncoder notif; Player* p = NULL; obj_node* objn, *tmp; //Charche la game index = search_client_game(cliId); //Ini json ini_encoder(¬if); //Recup le joueur pthread_mutex_lock(&gameMutex[index]); for (int i = 0; i < MAXPLAYER; i++) { pthread_mutex_lock(&playerMutex[(index * MAXPLAYER) + i]); if (game[index].player[i]->ini && game[index].player[i]->id == cliId) { playerIndex = i; p = game[index].player[i]; break; } pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + i]); } if (p == NULL) { pthread_mutex_unlock(&gameMutex[index]); send_err_client(cliId, EREQUEST); adderror("Aucun joueur associé au client"); return FAIL; } //Explose toutes les remotes du joueur objn = p->bomb->first; while(objn != NULL){ tmp = objn->next; if(objn->type == OBJ_BREMOTE){ //Retire mutex gérés dans fonctions pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); pthread_mutex_unlock(&gameMutex[index]); if(bomb_explode(&game[index], playerIndex, objn->x, objn->y, ¬if)){ //Notification joueurs if(!notify_player(&game[index], "POST", "attack/explose", ¬if, -1)){ adderror("Impossible de notifier le joueur de l'explosion"); } clean_json_encoder(¬if); } //Remet mutex pthread_mutex_lock(&gameMutex[index]); pthread_mutex_lock(&playerMutex[(index * MAXPLAYER) + playerIndex]); } objn = tmp; } //Nettoyage pthread_mutex_unlock(&playerMutex[(index * MAXPLAYER) + playerIndex]); pthread_mutex_unlock(&gameMutex[index]); return SUCCESS; }