Pygame multijoueur

a marqué ce sujet comme résolu.

Salut,

J’ai déplacé le sujet dans un forum plus adapté (tu n’es apparement pas en train de rédiger du contenu pour ZdS, mais tu cherches à développer/programmer quelque chose).

N’hésite pas à préciser ta demande pour obtenir des réponses plus précises.

Par exemple :

  • Où en es-tu de ta recherche ?
  • Qu’as-tu déjà fait ou compris ?
  • Quels sont tes objectifs précisément ?

Salut,

Déjà est-ce que tu as déjà une version en simple joueur du jeu que tu voudrais passer en multi ?

Ensuite la réponse dépendre du type de multijoueur que tu souhaites, est-ce qu’il s’agit de plusieurs personnes sur un même programme ou d’une communication réseau ? Est-ce qu’il y aura des actions simultanées ou est-ce un jeu au tour par tour ?

Si le jeu nécessite du réseau il te faudra t’orienter vers des libs pour cela.

Bonjour,

J’imagine créer, pour commencer, un petit jeu 2D pour deux joueurs (un genre de pong multijoueur, par exemple). Du coup, je suppose que les deux joueurs seront sur le même réseau. Je pense même qu’en théorie, il doit être possible de faire une version pour aller jusqu’à 4 joueurs…

Je pense que les actions devront pouvoir être simultanées pour que le jeu soit fluide.

Pourriez-vous m’aiguiller ?

Merci d’avance,

@flopy78

Bonjour,

J’ai fait quelques essais avec le module "socket" (un simple échange de messages).

Voici le code du serveur (situé sur un premier ordi) :

import socket

host = 'localhost'
port = 5001

my_socket = socket.socket()

my_socket.connect((host,port))

my_socket.send('hello'.encode())

msg_in = mySocket.recv(1024).decode()

print(msg_in)

my_socket.close()

Et voici mon code côté client (sur un autre ordi) :

import socket

host = "localhost"
port = 6678

my_socket = socket.socket()
my_socket.bind((host,port))

my_socket.listen(5)

while True:
    client, client_addr = my_socket.accept()
    msg = client.recv(1024).decode()
    print(msg)
    msg_out = "message recieved".encode()
    client.send(msg_out)
    client.close()
    break

Malheureusement, IDLE s’obstine à me répondre l’erreur suivante à chaque fois que j’essaye de lancer mon code côté client (le code serveur tourne pendant ce temps-là sur l’autre ordi, et les deux ordis sont normalement connectés) :

Traceback (most recent call last): File "/Users/florian/Desktop/Client Socket.py", line 8, in <module> my_socket.connect((host,port)) ConnectionRefusedError: [Errno 61] Connection refused

J’ai fouillé sur internet, sans trouver de solution à mon problème.

Pourriez-vous m’aider ?

Merci d’avance,

@flopy78

Salut,

(Tu as inversé le code du client et celui du serveur dans ton exemple, et tu as un mySocket au lieu de my_socket qui traîne dans le code client.)

Là, tu as un serveur qui se branche sur le port 6678 du "localhost", autrement dit lui-même, et un client qui se branche sur le port 5001 du "localhost", autrement dit lui-même. Ça ne peut pas marcher, le client et le serveur se parlent juste à eux-même mais pas entre eux. Si le client et le serveur étaient la même machine, ça ne marcherait pas non plus parce qu’ils ne sont pas branchés sur le même port.

Dans un premier temps, essaie déjà avec le client et le serveur sur la même machine. Si tes deux scripts se branchent sur le même port, ça devrait marcher. Si déjà tu as du mal avec cette étape, dis nous parce que si ça marche déjà pas en local, ça marchera pas en réseau.

Ensuite, pour faire communiquer deux machines différentes sur le réseau local, tu peux avoir le serveur qui se branche sur my_socket.bind(("", 6678)) (pour accepter n’importe qui du réseau local), et le client doit se brancher sur le port 6678 du serveur. Par exemple my_socket.connect(("192.168.1.5", 6678)), en remplaçant 192.168.1.5 par l’IP locale du serveur. Pour connaitre l’IP locale du serveur, tu peux par exemple utiliser la commande ip addr sur le serveur, ou aller voir dans ton serveur DHCP (typiquement dans l’interface d’admin de ton routeur) qui va lister les machines présentes sur le réseau et leur IP(s) locale(s).

+1 -0

J’imagine créer, pour commencer, un petit jeu 2D pour deux joueurs (un genre de pong multijoueur, par exemple). Du coup, je suppose que les deux joueurs seront sur le même réseau. Je pense même qu’en théorie, il doit être possible de faire une version pour aller jusqu’à 4 joueurs…

Je pense que les actions devront pouvoir être simultanées pour que le jeu soit fluide.

Pourriez-vous m’aiguiller ?

flopy78

Il y a d’autres questions d’architecture qui pourront encore se poser (est-ce que le serveur est décorrélé du client ou est-ce qu’un client fait office de serveur) mais auxquelles tu n’as pas besoin de répondre dans l’immédiat.

Le mieux est comme tu l’as fait de commencer par t’intéresser à la programmation réseau, et le module socket est un bon début pour ça. Peut-être que par la suite tu voudras tester d’autres libs de réseau, mais socket fait bien le boulot.

Je te conseille aussi de commencer par faire tourner tes deux programmes sur la même machine, tu évites ainsi tous les problèmes liés au fait que les deux machines n’arrivent pas à communiquer ensemble.

Bonjour,

Finalement, j’ai réussi à faire fonctionner mon affaire en déclenchant un de mes programmes depuis le shell ILDE et l’autre depuis le terminal !

Maintenant, il faut arriver à faire fonctionner mes programmes depuis deux ordis différents…

Merci infiniment !

Bonne journée,

@flopy78

Bonjour,

J’ai finalement réussi à faire fonctionner mon programme sur deux ordis différents !

j’ai utilisé comme "host" le retour de la fonction "socket.gethostname()", exécutée sur le serveur.

Maintenant, la prochaine étape c’est de réussir à faire un jeu avec ça…

Merci infiniment !

Bonne journée,

@flopy78

Pour faire un jeu il va falloir faire attention à la boucle événementielle : le client du jeu doit pouvoir continuer à récupérer les événements utilisateur (clavier, souris) tout en écoutant les événements réseau. Je ne sais pas si pygame prévoit de base des outils qui s’intègrent à sa boucle pour gérer le réseau ou s’il faudra que tu le fasses de ton côté pour que ça ne soit pas bloquant (sélecteurs, threads).

Bonjour,

Finalement, j’envisage de concevoir une bataille navale (l’aspect "tour par tour" rendra sûrement les choses plus faciles pour commencer, et pour le coup, il y a un vrai intérêt à utiliser deux ordis !).

Pour ça, j’envisagerai bien une configuration où le programme, (qui est le même sur les deux ordis), jouerai à la fois le rôle de serveur et le rôle de client.

Du coup, j’ai mis au point le code suivant (c’est une ébauche, je vous préviens), mais il n’a pas l’air de fonctionner comme je veux…

import pygame
import socket

my_socket = socket.socket()

host_this_player = socket.gethostname()

host_other_player = ''

#meant to be changed

if "imac" in host_this_player:
    host_other_player = 'pluton-1.home'
else:
    host_other_player = 'imac-de-christian-mayeux.home'
    

port = 6666

my_socket.bind((host_this_player,port))

my_socket.listen(5)

connected = False

while not connected:
    connected = True
    try:
        print(host_other_player)
        my_socket.connect((host_other_player,port))
    except:
        connected = False 

other_player, adr = my_socket.accept()

other_player.send((host_this_player+" is connected").encode())

Pourriez-vous m’aider ?

Merci d’avance,

@flopy78

Salut,

La bataille navale est en effet un bon exercice pour cela :

  • Le concept du jeu est simple à appréhender.
  • Pas d’actions simultanées à gérer.
  • Le protocole est plutôt facile (envoyer les coordonnées d’une case, recevoir un état).

Tu choisis d’avoir un client qui héberge la partie et fait office de serveur, ce n’est là pas forcément la solution la plus évidente à développer puisque tu dois mélanger code client et serveur dans un même programme.
Ton programme doit donc savoir faire la part des choses entre qui est l’hôte et qui est le client distant, avec des conditions pour gérer ces différents cas.

Ici ton code mélange un peu tout et les deux initialisent un serveur sur lequel ils attendent une connexion, tout en tentant de se connecter à l’autre.
Tu n’as pas besoin que chaque programme ait un serveur (une socket est bidirectionnelle) et ta conception fait que le programme est bloqué par sa tentative de connexion avant de pouvoir accepter des clients. Il te faudra utiliser des mécanismes de programmation concurrente (sélecteurs, threads ou coroutines) pour éviter ces blocages dans le code du serveur.

Aparté

Sinon la bataille navale sans autorité centrale (serveur) est une architecture intéressante, chaque joueur devant faire confiance à l’autre (si on considère qu’il n’y a pas l’un des deux qui fait autorité et donc qui connaît le placement exact de l’autre).
Ça amène à développer des mécanismes pour éviter la triche : vérifier au fur et à mesure (ainsi qu’en fin de partie) la grille du joueur adverse pour s’assurer qu’elle est conforme aux règles.
Car les techniques de triche vont de la plus simple (renvoyer toujours "dans l’eau") aux plus sophistiquées (replacer les bateaux en fonction de là où l’adversaire vise) et demandent donc aussi de d’assurer que la grille en face ne bouge pas entre le début et la fin de la partie.

Bonjour,

Les deux joueurs auraient un code légèrement différent : un aurait le socket "serveur", qui se chargerait juste d’accepter la connexion, et l’autre le socket "client", qui se chargerait de la demander. A partir de là, chacun des joueurs peut envoyer et recevoir des messages, avec send() et recv(). Du coup, ça me parait possible…

A terme, la perfection serait qu’il n’y ait pas de serveur et de client prédéfini : les joueurs ont le même code, et lorsqu’il se lance, il vérifie si l’autre programme est déjà actif (est-ce que c’est possible ?). Si oui, le code "serveur" serait exécuté, et sinon le code "client" (une simple condition if … else, en fait). Sur ce point là (qui n’est pas indispensable), le problème est de détecter l’activité de l’autre programme… Ce dispositif aurait l’avantage de ne pas avoir besoin de s’embêter à devoir faire attention à lancer un programme avant l’autre…

Après, peut-être que quelque chose m’a échappé…

Merci beaucoup !

Bonne journée,

@flopy78

En effet il y aura des similitudes dans le traitement des messages, mais attention à ne pas tout mélanger et se rappeler qu’il s’agit d’un jeu au tour par tour (chaque joueur est tantôt en position d’envoyer un message ou d’en recevoir un).

Le fait de ne pas avoir de serveur prédéfini est envisageable : il « suffit » d’un menu et d’une option « héberger une partie » comme on en trouve dans certains jeux, puis de communiquer les informations de connexion à l’autre joueur. Mais c’est toujours l’ordre de lancement des deux (d’abord démarrer le serveur avant d’y connecter le client) qui fait que ça fonctionne bien.
Vouloir que le client détecte automatiquement le serveur me semble casse gueule et demandera à ce que la configuration soit entrée en dur des deux côtés.

Bonjour,

Merci beaucoup pour les conseils !

Pour l’instant, voici mon code (j’en suis au tout début, et ce code fonctionne avec 2 ordis précis, pour commencer) :

import pygame as pg
import socket

#################################SETUP PYGAME##################################

pg.init()

screen = pg.display.set_mode((640,640))


###################SET THE CONNEXION WITH THE OTHER PLAYER#####################

def setup_serveur_connexion():
    my_socket = socket.socket()

    host_this_player = socket.gethostname()

    host_other_player = ''

    #meant to be changed

    if "imac" in host_this_player:
        host_other_player = 'pluton-1.home'
    else:
        host_other_player = 'imac-de-christian-mayeux.home'
        

    port = 6667

    my_socket.bind((host_this_player,port))

    my_socket.listen(5)

    print("done")
    connexion, adr = my_socket.accept()

    connexion.send((host_this_player+" is connected").encode())

    print(connexion.recv(1024).decode())
    return connexion

def setup_client_connexion():
    my_socket = socket.socket()

    host_this_player = socket.gethostname()

    host_other_player = ''

    #meant to be changed

    if "imac" in host_this_player:
        host_other_player = 'pluton-1.home'
    else:
        host_other_player = 'imac-de-christian-mayeux.home'
        

    port = 6667


    my_socket.connect((host_other_player,port))

    print("done")

    my_socket.send((host_this_player+" is connected").encode())

    print(my_socket.recv(1024).decode())

    return my_socket

if 'imac' in socket.gethostname():
    cxn =  setup_serveur_connexion()
else:
    cxn = setup_client_connexion()


###############################DEFINE CLASSES####################################

class Grille:
    def __init__(self,w,h):
        self.data = []
        for i in range(h):
            self.data.append([])
            for j in range(w):
                self.data[-1].append(None)
        for ligne in self.data:
            print(ligne)

Grille(4,3)

###################################MAIN LOOP##############################s######

done = False

tour = 0

ready = False

while not done:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            done = True
    screen.fill("white")
    

    pg.display.update()

pg.quit()
cxn.close()

Du coup, le code est censé être le même pour le serveur et client, et le serveur est toujours le même.

Effectivement, je crois que je vais créer, comme vous le dites, une option "héberger la partie"…

Merci beaucoup !

@flopy78

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte