G-Rom a écrit :le client recevra un message de taille inconnu , il n'aura qu'a lire le premier byte pour connaître la taille du message puisque c'est toi qui le défini en créant ton protocole.
Merci pour ton exemple, je pense que j'ai à peu près compris

.
J'utilise déjà le premier byte du message comme entête de type de message :
Code : Tout sélectionner
#MESSAGE_CONNEXION = $0
#MESSAGE_LOGIN_MDP = $1
#MESSAGE_CHOIX_PERSONNAGE = $2
#MESSAGE_COORDONNEES = $50
#MESSAGE_DECONNEXION = $FE
; etc...
J'imagine qu'en fonction 1er byte (l 'entête de message), l'allocation memoire ressemble à ça :
Code : Tout sélectionner
*Message = AllocateMemory( 1 + (3 * 4) ); pour message coordonnées
*Message = AllocateMemory( 1 +2+1+2+2 ); pour message type new perso : id.w/peuple.a/x./y.w
; etc..
Mais pour un pseudo ou un message de tchat par exemple, je dois faire ceci (car c'est du texte, donc je décide moi-même de sa taille)?
Code : Tout sélectionner
; tchat :
*Message = AllocateMemory(100); on suppose qu'on interdit plus de 100 caractères pour du tchat
; pseudo :
*Message = AllocateMemory(25); pas plus de 25 caractères par pseudo.
; On poke....
PokeA( *Message , #MESSAGE_CORRECTION_COORDONNE)
PokeF( *Message+1 , PosX)
PokeF( *Message+1+4 , PosY)
PokeF( *Message+1+4+4 , PosZ)
ce n'est pas ça plutôt ? :
; On poke....
PokeA( *Message , #MESSAGE_CORRECTION_COORDONNE)
PokeF( *Message+4 , PosX)
PokeF( *Message+4+4 , PosY)
PokeF( *Message+4+4+4 , PosZ)
En tout cas, merci beaucoup pour ton explication

.
il faut utiliser ça pour l'envoyer, j'imagine ?
Code : Tout sélectionner
SendNetworkData(ClientID,*Message,1+4*3) ; pour le message coordonnées
Pour un truc fluide et costaud , opte déjà pour le serveur dédié, pas d'affichage pour lui , juste une console d'admin.
Server dédié, tu veux dire un server en dehors du client ?
Si oui, c'est déjà le cas (et en console)

.
n’envoie pas en temps réel les données , envois avec une latence de 25/30ms max tout les paquets préalablement fabriqué.
là, ça m'intéresserait d'avoir un peu de précision, car c'est là-dessus que je bloque un peu :
- Créer la "pile" de paquet
- on envoie tous les paquets à tous les clients en même temps (enfin, à la suite) ? ou on met un léger délai ?
Voilà un exemple de ce que je pensais faire, est-ce la méthode pour gérer un thread d'envoi de paquet et la réception de ces paquets (message des clients)?
Code : Tout sélectionner
;{ les entêtes de messages
#MESSAGE_CONNEXION = $0
#MESSAGE_LOGIN_MDP = $1
#MESSAGE_CHOIX_PERSONNAGE = $2
#MESSAGE_COORDONNEES = $50
#MESSAGE_TCHAT = $64 ; 100
#MESSAGE_DECONNEXION = $FE
;}
;{ variables, globales
Global timer.w,ClientID.l,*buffer
;}
;{ structures
Structure StPaquet
txt$
size.w ; la taille memoire du paquet
*paquet
EndStructure
Global NewList paquet.StPaquet()
Structure stClient
id.i
nom$ : pass$
status.a; en attente, connecté...
NumMap.w
x.w : y.w
peuple.a
EndStructure
Global NewMap Client.StClient()
;}
;{ init
If InitNetwork()=0 : End : EndIf
*buffer =AllocateMemory(300); pour le tchat au cas où
;}
;{ procedure
Procedure ReceiveMessageFromClient(*var)
packet.s = PeekS(*buffer,MemoryStringLength(*buffer))
AddElement(paquet())
Select entete
Case #MESSAGE_CONNEXION
paquet()\Paquet= AllocateMemory( 1 + 2); on lui renverra son id.w
; comment enlever le #MESSAGE_CONNEXION ou décaler pour ne lire que le message ?
PokeW(paquet()\Paquet,#MESSAGE_CONNEXION)
Case #MESSAGE_LOGIN_MDP
paquet()\Paquet = AllocateMemory( 1 + Len(Login$)+Len(Mdp$))
;paquet()\txt$ = PeekS(*buffer)
Case #MESSAGE_CHOIX_PERSONNAGE
paquet()\Paquet = AllocateMemory( 1 + 1); le choix du personnage est un .a
PokeA(paquet()\Paquet,#MESSAGE_CHOIX_PERSONNAGE)
; etc...
EndSelect
FreeMemory(*buffer)
*buffer =AllocateMemory(300); 300 c'est pour la longueur max (le tchat)
EndProcedure
Procedure SendPaquet(*var); thread pour gérer les paquets à envoyer
Shared mutex
LockMutex(mutex)
If timer >0
timer - 1
Else
timer = 250 ; 25ms
ForEach paquet()
ForEach client()
SendNetworkData(ClientID,@paquet()\paquet,paquet()\size)
Next
DeleteElement(paquet(),1)
;Break
Next
EndIf
UnlockMutex(mutex)
EndProcedure
;}
If OpenConsole() = 0 : End :EndIf ; on ouvre la console
If CreateNetworkServer(0,6832)=0 : End :EndIf ;on crée la connexion server
PrintN("Server Connecte sur le port 6832")
mutex = CreateMutex()
Repeat
KeyPressed$ = Inkey()
CreateThread(@SendPaquet() , 0)
SEvent = NetworkServerEvent()
If SEvent
ClientID = EventClient() ; on reçoit l'id du client
Select SEvent
Case #PB_NetworkEvent_Connect ; connexion d'un nouveau client
Case #PB_NetworkEvent_Data; réception et/ou envoi de donnée (client->server->clients)
ReceiveNetworkData(ClientID,*Buffer,300)
ReceiveMessageFromClient(ClientID)
Case #PB_NetworkEvent_Disconnect ; deconnexion d'un client
EndSelect
EndIf
CreateThread(@SendPaquet(),0)
Until KeyPressed$ = Chr(27); Echap
n'envoie pas des coordonnée brut pour effectué un mouvement sur les clients, mais des actions :
- Client XXX à appuyer sur avancer
- Serveur reçois l'info du de l’appui du Client
- Le Serveur avance le joueur
- Le Serveur renvois l'info à tout les clients , y compris celui qui a appuyé la touche.
- Les Clients reçoivent l'info du serveur et met à jour l'affichage.
ok, je comprends tout à fait le principe, c'est d'ailleurs comme cela que je pensais faire, mais il faut que je mette ça en pratique

.
Faut bien que tu comprennent que ce n'est pas le client qui décide de sa position , mais le serveur.
tu n'as plus qu'a faire une correction de coordonnée ( à cause de la latence ) tout les 100ms par exemple. (serveur->clients) et pas l'inverse la correction , sinon faille pour la triche

. si je n'ai pas été clair , dis le moi.
tu as été très clair, j'ai tout compris je t'en remercie

.
Après, il faut juste que j'arrive à coder ce que j'ai compris, et là, des fois, ça se corse

.
Concernant les mouvements, je pensais faire ça :
- le joueur appuie sur une touche (right). On envoie soit l'appuie de la touche
- le server reçoit l'info
- 2 possibilités : soit le server renvoie l'info à tous les clients, soit le server crée une "cible" : la case d'à coté en fonction de la ou des touche(s) appuyée(s) : case à droite si on a appuyé sur key_right, etc.., puis il renvoie l'info (la cible) à tout les clients , y compris celui qui a appuyé la touche.
- Les Clients reçoivent l'info du serveur et mettent à jour l'affichage : autrement dit ils bougent le joueur vers cette case (qui est à coté) ou alors ils bougent simplement le client en fonction du message envoyé par le server.
J'ai testé avec des mobs et ça marche super bien, c'est excellent

. Les mobs ont un avantage par rapport aux clients, leur mouvements est définis par le serveur.
stombretrooper a écrit :La solution c'est sans doute les mutex. Avec les détails que tu donnes, on dirait que ta map est utilisé simultanément sur deux thread différent. Pour éviter ce genre de problème, les mutex sont là !
yep, j'ai commencé à regarder un peu les mutex. Je pense aussi que la map est utilisée sur 2 thread : le thread des mobs et le thread des clients (thread principal).
Du coup, j'ai ajouté un mutex dans le thred de déplacement des mobs et ça marche nickel, plus de lag ni de plantage, c'est magique

.
Un mutex c'est un élément qui va permettre de bloquer un thread tant qu'un autre thread n'a pas libéré le mutex.
Le seul truc que tu as à faire c'est partager la variable mutex_id, pour ça, je te conseils de faire une structure, la mettre en 'global', et mettre tes mutex dedans. Essaye de faire un mutex pour chaque liste, tableau, map, variables partagées.
j'ai crée un mutex en shared dans la procédure des mobs.
Mais je testerai ta méthode pour voir

.
Tu prépares un mode co-op sur Arkeos ?
Disons que je fais des tests pour ça, on verra si j'y arrive

. En regardant Steam, j'ai vu que sur le formulaire de submission, on devait préciser si le jeu pouvait être en LAN (mode co-op) ou multi-joueurs.
Visiblement, les jeux sur lesquels il y a de la co-op ou des possibilités de LAN marchent vraiment bien, mieux que les autres semble-t-il.
Du coup, pour Arkeos, j'aimerai bien mettre un petit mode LAN , un truc très simple, genre :
- créer un server (local uniquement ou via internet ? je ne sais pas encore)
- server avec maximum 5 à 20 joueurs (enfin, je testera avec plus de joueurs pour être sûr que ça passe avec le minimum).
- pour les autres clients du réseau : rejoindre la partie/ quitter la partie, etc...
Ensuite, j'aimerai ajouter ça :
- création des mobs et gestion des mobs (apparition, déplacement, combat (perte de vie), mort, re-pop, depuis le client qui fait server) : ça c'est ok à priori.
- gestion des joueurs : apparition, déplacement, combat, mort, déconnexion, etc.. : à priori, ça semble fonctionner. Mais je dois revoir les threads car je n'ai pas encore fait de thread pour les clients ^^.
- gestion des fx : ça devrait être ok, car c'est comme les joueurs et les mobs.
Et c'est tout, à priori, pas de BDD (enfin, je vais quand même en ajouter une pour tester), car le reste serait normalement stocké en local, sauf si Steam est intéressé par le jeu, et demande une gestion plutôt type en ligne multi-joueurs que local (c'est à dire un server "dédié" gérant une BDD)
Mais là, ce serait tout autre chose, je ne sais pas si je le ferai du coup.
je pense qu'un petit mode co-op/LAN pourrait être un gros plus pour une version du jeu, avec une gestion d'une dizaine de joueurs par exemple.
La lib network n'est pas threadsafe non ? J'ai des problèmes de connections de client plutôt gênant, il y a une méthode pour utiliser directement les sockets et/ou avoir la lib en threadsafe ?
j'ai cherché pas mal d'info sur les thread, et j'ai pas vu de message permettant d'avoir du thread safe. Les mutex ne règlent pas le problème ?
Sinon, pour les sockets, j'ai trouvé ça, je pense que ça peut être intéressant (c'est un exemple client\server) :
http://www.purebasic.fr/english/viewtop ... ket+server
Mon principal problème, c'est quand un client se déconnecte, et qu'un thread était en train de dialoguer avec ce dernier, il arrive souvent le plantage 'le client spécifié n'existe pas' (une erreur du genre quand on envoie des données sur un client inexistant), c'est surtout qu'il n'existe plus en faite... Ça se joue à quelques dixièmes de secondes.
J'y avais déjà réfléchi, et je pense qu'une des solutions serait de faire comme le jeu MIxmaster ou d'autres jeux :
- un délai de déconnexion.
Dans Mixmaster, lorsque tu cliques sur déconnexion, le jeu mets 10 secondes pour te déconnecter. Tu ne peux plus rien faire pendant ce temps-là (tu peux bouger la souris et tu vois un compteur qui décompte la déconnexion).
Je pense que le client envoie à ce moment-là un message au server pour le prévenir qu'il souhaite se déconnecter et lui laisse le temps de "fermer" ou arrêter ce dont il se sert avec ce client.
En fait, tu pourrais même faire un truc de déconnexion du coté du server, comme lorsque tu éteins le server.
Voici la manière dont tu pourrais faire la déconnexion :
- le client envoie un message comme quoi le joueur a cliqué sur "déconnexion", mais il ne se déconnecte pas.
- le server reçoit ce message, il renvoie un message au client comme quoi c'est ok (deco_ok), il supprime le client de la liste des clients.
- le client reçoit le message du server : "deco_ok" et peut se fermer.
Si tu fais ça, ça devrait résoudre ton problème pour la plupart des cas j'imagine (sauf si on ferme le jeu avec ctr+alt+sup..).
On m'a conseillé pour ce genre de grosse appli serveur, de créer un thread par 'socket' (et donc par client), mais avec la lib network, justement on travaille pas avec des sockets, c'est quasiment impossible de séparer un lien serveur-client sur un thread, à part en connectant un seul client par port, et en faisant une centaine de serveurs, mais je penses pas que ce soit une méthode très propre.
Visiblement, on peut utiliser les sockets avec le pure, regarde le lien que j'ai mis plus haut

.
Sinon, ici, une explication très bien détaillée pour l'utilisation :
http://www.purebasic.fr/french/viewtopi ... ver+client
Dobro : ton système me fait penser au système de case que je voulais mettre en place

.
En tout cas, un très grand merci à vous pour toutes ces informations très précieuses, car grâce à vous, mon test server/client commence à être assez sympa

.