/
.
$
[
{
=

NOUS LES DEVS

Rejoindre une partie

A la conquête du jeu vidéo partie 9

Niveau : expert(e)
</> </> </>

Afficher les parties

Lorsqu’un joueur crée une partie, tous les joueurs disponibles sont prévenus par le serveur et la méthode update_list_parties est directement appelée pour afficher les nouvelles parties dans l’interface.

update_list_parties(list_parties){
	this.list_parties = list_parties;
	this.refresh_list_parties();
}

refresh_list_parties(){
	//nettoyage de la liste
	$("#list_of_parties").html("");
	
	//remplissage de la liste des parties
	this.list_parties.forEach( partie => {
		$('#list_of_parties').append(`<li data-id="${partie.id}">${partie.map_name} - ${partie.id} (${partie.nbr_players}/${partie.max_players})</li>`);
	});
}

Menu

De la même manière que pour la création de partie, j’ajoute un système d’événements qui me permet de cliquer sur une partie.

$("#list_of_parties").off("click.join_partie").on("click.join_partie", "li", event => this.event_join_partie(event) );
			

Rejoindre côté client

Lorsqu’un joueur clique sur une partie pour la rejoindre, une requête est envoyée au serveur avec l’identifiant de la partie.

event_join_partie(event){	
	let partie_id = $(event.currentTarget).attr("data-id");
	if( partie_id != "" && this.connected ){
		//joindre la partie
		this.send_to_server("message",{ action : "request_join_partie" , params : { token : this.token , partie_id : partie_id } });
		
		//unbind de click
		$("#list_of_parties").off("click.join_partie");
	}
}
interface_multijoueur_list_parties

Rejoindre côté serveur

La méthode request_join_partie() est la plus intéressante car elle communique directement avec les autres joueurs.

Il y a beaucoup de tests à faire, vérifier si la partie existe bien, si le joueur n’est pas déjà dans la liste, si la partie n’est pas pleine, etc… Il y a trois méthodes qui sont appelées en fonction des cas, update_list_parties() qui va permettre de mettre à jour la partie et surtout d’afficher le nombre de places restantes auprès de tous les autres joueurs (free), update_request_join_partie() qui va permettre d’informer l’arrivée d’un nouveau joueur aux autres joueurs de la partie, ça se traduit par l’ajout d’une vignette avec l’affichage d’un CO, et enfin response_prepare_partie() lorsque la partie est complète, elle passera donc en préparation, c’est une sorte d’étape où il faut valider sont CO.

request_join_partie(user_socket, params){
	
	if( this.parties[params.partie_id] != undefined ){
		
		//si la partie n'est pas pleine
		if( Object.keys( this.parties[ params.partie_id ].list_players ).length < this.parties[ params.partie_id ].map.nbr_joueur && this.parties[ params.partie_id ].state == "waiting" ){
			//si le joueur n'est pas encore dedans
			if( this.parties[ params.partie_id ].list_players[ params.token ] == undefined && this.users[ params.token ].state == "free"){

				e(`:: Joueur ${params.token} rejoins la partie ${params.partie_id}`);
				this.parties[ params.partie_id ].list_players[ params.token ] = {
					name : params.token,
					co : this.list_co[ Math.floor( Math.random() * (this.list_co.length-1) + 1 ) ]
				}

				//this.parties[ params.partie_id ].list_players.push( params.token );
				this.users[ params.token ].state 			= "join";
				this.users[ params.token ].current_partie 	= params.partie_id;
				
				//envoyer la réponse à ce client
				//this.send_to_client( user_socket, "message" , { action : "response_request_join_partie" , params : { result : true, nbr_joueur : this.parties[ params.partie_id ].list_players.length , place : this.parties[ params.partie_id ].map.nbr_joueur } });
				this.send_to_client( user_socket, "message" , { 
					action : "response_request_join_partie" , 
					params : {
						result 			: true,
						id				: params.partie_id,
						players_info 	: this.get_list_players_for_preparation( this.parties[ params.partie_id ] ),
						list_co			: this.list_co
					}
				});
		
				//si la partie est pleine alors go preparation
				if( Object.keys( this.parties[ params.partie_id ].list_players ).length == this.parties[ params.partie_id ].map.nbr_joueur ){
					e(`:: Partie ${params.partie_id} passe en préparation`);
					this.parties[ params.partie_id ].state = "prepare";

					for(let token in this.parties[ params.partie_id ].list_players ){
						//envois à tous les autre users
						this.send_to_specific_client(this.users[token].socket_id, "message", { 
							action : "response_prepare_partie", 
							params :  { 
								partie_id 		: params.partie_id , 
								players_info 	: this.get_list_players_for_preparation( this.parties[ params.partie_id ] )
							}
						});	
					}
				}else{
					//envois un message au utilisateur de la partie
					for(let token in this.parties[ params.partie_id ].list_players ){
						if( user_socket.id != this.users[token].socket_id ){
							this.send_to_specific_client(this.users[token].socket_id, "message", { 
								action : "update_request_join_partie", 
								params :  this.get_list_players_for_preparation( this.parties[ params.partie_id ] )  
							});
						}
					}
				}
				
				//mettre à jour toute la liste des utilisateurs free
				for(let token in this.users){
					if( this.users[ token ].state == "free" ){
						this.send_to_all_other_clients( user_socket, "message" , { action : "update_list_parties" , params : this.get_client_list_parties() });
					}
				}	
			}else{
				e(":: Joueur déja présent ou pas libre");
			}
		}else{
			e("erreur partie complete ou déja en cours");
			//rebalancer la nouvelle liste au client
			this.send_to_client( user_socket, "message" , { action : "refresh_list_parties" , params : this.get_client_list_parties() });
		}
	}
}

Mise à jour

Quand un joueur rejoint une partie, si la partie est pleine, alors il ne faut plus afficher la partie auprès de tout les autres joueurs, et au contraire, si la partie n’est pas pleine, il faut mettre à jour le nombre de joueurs de la partie, c’est ce que fait la méthode update_list_parties qui est affichée au début de l’article.

Réponse

Si un joueur réussi à rejoindre une partie alors c’est la méthode response_request_join_partie qui est appelée. Beaucoup plus simple que la partie serveur, ici il s’agit simplement d’afficher la bonne interface et de rafraîchir certaines données, d’enregistrer quelques variables, de donner la possibilité au joueur de quitter la partie et d’afficher les slots !

response_request_join_partie(params){
	if( params.result ){
		$("#menu .step").hide();
		$("#menu .wait").show(300);
		
		this.list_co         = params.list_co;
		this.current_co      = params.players_info.list_players[ this.token ].co;
		this.current_partie  = params.id;
		
		//ajouter le bouton quitter
		$(".wait .sub-menu").html(`<button class="quit_partie">Quitter la partie</button>`);
		
		//afficher nombre de place
		$(".wait .content .place").html(`(${params.players_info.nbr_players}/${params.players_info.max_players})`);
		
		this.display_slot(params.players_info);
		
	}else{
		$("#list_of_parties li").off("click.join_partie").on("click.join_partie", "li", event => this.event_join_partie(event) );			
	}
}

Les slots

Alors le slot c’est quoi ? C’est la vignette qui représente le CO lorsqu’un joueur vient de créer ou de rejoindre une partie.

interface_multijoueur_slots

Dans le précédent article et celui-ci j’ai appelé plusieurs fois la fonction display_slot qui permet d’afficher et de mettre à jour les fameux slots des joueurs.

display_slot(data){
	//change graphiquement l'etat de validation
	$("#list-players .picture").removeClass("valide");
	
	let slots = $("#list-players.list-co");
	slots.html('');
	for(let index in data.list_players){
		if( this.token == index ){
			slots.prepend(`<li id="player-${index}"><span class="change_co before">&lt;</span><div class="picture ${data.list_players[index].co}"></div><span class="next change_co">&gt;</span></li>`);
		}else{
			slots.prepend(`<li id="player-${index}"><div class="picture ${data.list_players[index].co}"></div></li>`);
		}
	}
	
	//si slot change on rebind le bouton
	$("#list-players").off("click.change_co").on("click.change_co", "span", event => this.event_change_co(event) );
	
}

Et si tu as bien fait attention, le CO est toujours généré de manière aléatoire, dans la méthode request_join_partie par exemple, lorsque j’ajoute un nouveau joueur dans la liste, je renseigne deux choses, la première c’est le token du joueur et ensuite je fais un random sur la liste des CO disponibles.

this.parties[ params.partie_id ].list_players[ params.token ] = {
	name : params.token,
	co : this.list_co[ Math.floor( Math.random() * (this.list_co.length-1) + 1 ) ]
}

Et dans la méthode display_slot, j’affiche directement le nom du CO dans l’attribut « class » de l’élément HTML qui doit afficher l’image, grâce à une syntaxe ES6.

<div class="picture ${data.list_players[index].co}"></div>

Je modifie aussi mon code SASS pour changer le background-position du sprite CO.

//largeur d'une vignette
$factor_co:130;

$co: (
	"andy" 		1,
	"nell" 		2,
	"sami" 		3,
	"max" 		4,
	"olaf" 		5,
	"eagle" 	6,
	"grit" 		7,
	"kanbei" 	8,
	"sonja" 	9,
	"colin" 	10,
	"drake" 	11,
	"jess" 		12,
	"sensei" 	13,
	"hachi" 	14,
	"helmut" 	15,
	"kat" 		16,
	"maverick" 	17,
	"ader" 		18,
	"sturm" 	19
);

//set co bg
@each $current_co in $co {
	&.#{nth($current_co, 1)}{
		background-position: ( nth($current_co, 2) * $factor_co * -1px)  0px; 
	}
}

18/06/2016

Yann Vangampelaere - nouslesdevs -

NOUS LES DEVS

Vous aimez ce que je fais ? Vous voulez que j'en fasse plus ? dans le développement du blog.