Ce document explique en detail tout le chemin reseau du projet:
- quels binaires sont lances
- quels fichiers font quoi
- comment un client se connecte
- comment les paquets transitent
- comment la position d un joueur arrive jusqu au rendu des autres joueurs
- quelles sont les limites actuelles de l implementation
Le but est de pouvoir debugger, faire evoluer, ou refactor le reseau sans zones floues.
Le projet compile deux executables:
wolf3d: le client jeu (rendu + input + reseau client)server: le serveur TCP standalone
Le reseau est base sur SFML Network (TCP), avec un schema simple:
- Le client ouvre une socket TCP vers le serveur.
- Le serveur accepte la connexion et assigne un
player_id. - Le serveur envoie un paquet
PKT_CONNECTau nouveau client. - En jeu, chaque client envoie periodiquement sa position (
PKT_PLAYER_POS). - Le serveur fait un relay (broadcast) vers les autres clients.
- Chaque client met a jour
others[]et dessine les joueurs distants.
include/network.h
Contient:
- constantes (
MAX_PLAYERS,PORT) - enum des types de paquets (
packet_type_t) - format des paquets (
network_packet_t) - structures serveur/client (
server_t,client_t) - prototypes des fonctions client et serveur
src/network/client/client.csrc/main.c(integration reseau dans la boucle de jeu)
Role:
- etablir la connexion TCP
- envoyer les paquets de position
- recevoir les paquets serveur
- mettre a jour les autres joueurs en local
src/network/server/server.c(primitives serveur)src/network/server/server_close.c(fermeture propre)src/network_server.c(main du serveur et boucle d orchestration)
Role:
- ecouter un port TCP
- accepter les clients
- relayer les paquets recus vers les autres clients
Makefile
Partie importante:
SRC_GAMEinclut le code jeu + client reseauSRC_SERVERinclutnetwork_server.c,server.c,server_close.c,client.c
MAX_PLAYERS = 4PORT = 8080
packet_type_t:
PKT_CONNECTPKT_DISCONNECTPKT_PLAYER_POSPKT_SHOOTPKT_GAME_STATE
Dans l etat actuel, le flux actif utilise surtout:
PKT_CONNECTPKT_PLAYER_POS
network_packet_t contient:
type(uint8_t)player_id(uint32_t)timestamp(uint32_t)x,y,angle(float)data[512](payload brut reserve pour extensions)
Point cle: le code envoie la structure binaire brute telle quelle (sfTcpSocket_send sur le struct). Il n y a pas encore de serialisation explicite (endianness/version/packing).
Dans src/network_server.c:
main()appelleblock_sigint()server_init(&server, PORT):- cree un listener TCP
- bind/listen sur le port
- cree un selector SFML
- ajoute le listener au selector
- reset la table
client_sockets[]
run_server_loop(&server)demarre la boucle reseau
Dans src/main.c, fonction program():
client_init(&wolf->net, "127.0.0.1", PORT)- Dans
client_init(src/network/client/client.c):- conversion IP string ->
sfIpAddress - creation socket TCP
- tentative de connexion (timeout 500 ms)
- passage en non bloquant (
sfFalse) - init
player_id = MAX_PLAYERS(sentinel: id non assigne)
- conversion IP string ->
Dans run_server_loop:
- attente
sfSocketSelector_wait(..., 50 ms) - si listener pret:
handle_new_connection() server_accept_client():- accepte la socket
- prend le slot
nb_players - ajoute la socket au selector
- incremente
nb_players
- creation d un paquet
PKT_CONNECTavec:player_id = slottimestamp = time(NULL)
- envoi du paquet au nouveau client (
server_send_to_client) - broadcast du meme paquet aux autres (
server_broadcast_to_others)
Dans network_update() (src/main.c):
- boucle
while (client_recv_packet(...) > 0) - dispatch:
handle_recv_packet()traitePKT_CONNECThandle_recv_position()traitePKT_PLAYER_POS
handle_recv_packet():
- ignore tout sauf
PKT_CONNECT - si le client n a pas encore d id (
MAX_PLAYERS), prendpkt->player_id - applique un spawn special si
player_id == 1
Chemin exact d une position locale vers les autres clients:
- Le joueur local bouge (input + simulation locale).
- A chaque frame,
network_update()est appelee. - Si le client est connecte, en mode
GAME, et avec id valide,network_send()peut envoyer. network_send()est throttle a 50 ms viasend_clock(environ 20 envois/s).- Le paquet est rempli:
type = PKT_PLAYER_POSx,y,angledepuiswolf->player
client_send_packet()forcepacket->player_id = client->player_idpuis envoie.- Le serveur detecte la socket prete et appelle
handle_client_data(). server_recv_from_client()lit le paquet.- Si lecture OK,
server_broadcast_to_others(server, &pkt, i). - Tous les clients sauf emetteur recoivent le paquet.
- Chez chaque client recepteur:
handle_recv_position()met a jourwolf->others[player_id]- met a jour
wolf->nb_otherssi besoin
- Pendant le rendu (
stage()):draw_other_players(wolf)projette et dessineothers[]
run_server_loop(server):
- tant que
!should_stop() - attend des evenements reseau (timeout 50 ms)
- traite connexion entrante eventuelle
- traite chaque client actif pret en lecture
Details de stop:
block_sigint()bloqueSIGINTshould_stop()teste siSIGINTest en attente viasigpending- le serveur peut sortir de boucle puis appeler
server_close()proprement
client_initretourne-1si creation/connect echoueclient_send_packet:sfSocketDoneetsfSocketNotReadysont consideres non fatals- sinon retourne
-1
network_sendmetwolf->connected = 0si envoi echoue
server_recv_from_clientnettoie la socket surDisconnectedouError- le pointeur socket du slot passe a
NULL
- Allocation de slot:
- le slot est base sur
nb_playersqui s incremente - pas de decrement quand un client quitte
- risque de ne plus accepter de nouveaux joueurs apres des deconnexions
- le slot est base sur
- Protocole brut:
- envoi direct du struct C
- pas de normalisation reseau (endianness, version, checksum)
- Types non exploites:
PKT_DISCONNECT,PKT_SHOOT,PKT_GAME_STATEnon implementes bout a bout
- Adresse serveur hardcodee:
- client connecte a
127.0.0.1
- client connecte a
- Fiabilite temporelle:
- pas d interpolation/extrapolation des positions distantes
sequenceDiagram
participant C1 as Client A (wolf3d)
participant S as Server
participant C2 as Client B (wolf3d)
C1->>S: TCP connect
S-->>C1: PKT_CONNECT(player_id=A)
C2->>S: TCP connect
S-->>C2: PKT_CONNECT(player_id=B)
S-->>C1: PKT_CONNECT(player_id=B)
loop Toutes les ~50ms
C1->>S: PKT_PLAYER_POS(x,y,angle,player_id=A)
S-->>C2: PKT_PLAYER_POS(...)
end
C2->>C2: handle_recv_position()
C2->>C2: update others[A]
C2->>C2: draw_other_players()
Si un joueur ne voit pas les autres:
- verifier que
servertourne et ecoute le port 8080 - verifier que le client est bien
connected - verifier reception de
PKT_CONNECT(id assigne) - verifier que le client est en state
GAME - verifier emission periodique de
PKT_PLAYER_POS - verifier
server_recv_from_client> 0 - verifier
server_broadcast_to_othersexecute - verifier
handle_recv_positionmet a jourothers[] - verifier
draw_other_playersappele pendant le rendu
Le reseau actuel est un relay TCP simple:
- Client -> envoi position
- Serveur -> redistribue aux autres
- Client recepteur -> met a jour tableau
others[]et rend les joueurs distants
Il est fonctionnel pour synchroniser position/angle, mais il reste des evolutions a faire pour robustesse production (gestion de slots, protocole serialize, gestion de deconnexion explicite, interpolation, securite).