Tout commence par le réseau
Introduction
Je vais vous parler de la partie communication entre le serveur et le client.
Il s'agit d'une analyse de l'existant, de ce que j'ai compris, des compromis faits, mais aussi des améliorations possibles et de mes suggestions.
Quelle est la communication entre le serveur et les clients ? Comment sont définis la position, les zones, comment fait-on pour changer de zone, la communication (chat), quel sont les états ?
Pourquoi écrire ce document ? Premièrement pour se repérer dans le code, comprendre comment cela marche, et surtout mettre à plat tout ce petit monde afin de recadrer et voir vers où l'on va.
Les grandes lignes
Quand on parle de communication, on parle en fait de deux étapes.
- Étape 1 : connexion & authentification
- Étape 2 : communication continue (ici commence réellement le jeu)
Pourquoi deux étapes?
La première étape concerne la vérification de l’utilisateur et de son mot de passe, s'ils sont correct, vous pouvez vous enfin communiquer (en permanence) avec le serveur. Ce dernier envoie une clef/token afin de pouvoir s'authentifier dans l'étape 2. Ensuite la connexion est maintenue entre le serveur et le client de jeu.
Vue d'ensemble
+---------+ | | | +-------+ +------------------+ | | 443 | <---HTTPS/TCP-----> | Login / Register | | +-------+ +------------------+ | Serveur | | +-------+ +----------------------------+ | | xxxxx | <---UDP-----> | Communication avec le jeu | | +-------+ +----------------------------+ | | +---------+
Étape 0 : Enregistrer un nouveau compte
Cette étape est exécutée une seule fois et permet de créer un compte.
Dans les faits, on utilise le protocole https
[TCP/IP]1)
Ce qui suit n'est pas clair. Je propose une reformulation, mais je n'ai peut-être pas tout compris.
— zatalyz 2020/08/21 12:58
Mise à jour afin de voir si cela convient — aleajactaest 2021/01/12 20:45
On lance une requette WEB avec la forme suivante
Le lien dans l'interface web sera de la forme suivante :
[url]/ams/index.php?page=register&Username=[username]&Password=[password]&ConfirmPass=[password]&Email=[email]&TaC=[tac]&function=add_user</nowiki>
Avec:
username
: il doit être uniquepassword
: le mot de passeemail
: un emailtac
: toujourson
(pour dire que la licence est acceptée)
Exemple:
https://khaganat.net/ams/index.php?page=register&Username=joueurdetest&Password=jenetedonnepasmonmotdepasse&Email=joueurdetest@khaganat.net&TaC=on&function=add_user
Le serveur retourne une ligne:
- [Status]:[commentaire]
si status
est égale à 1, la création est effectuée. Sinon, une erreur est survenue, il faut regarder le commentaire.
Étape 1 : Connexion & authentification
Cette étape permet de s'assurer que l'utilisateur est bien la bonne personne, via le couple utilisateur/mot de passe.
On utilise le protocole https
[TCP/IP]2)
On envoie les informations suivantes :
On peut remarquer qu'ici on communique directement avec l'interface (en php) qui s'occupe de faire la vérification avec la base de donnée puis renvoye la clef d'authentification.
1. Client → Serveur
Demande un mot clef pour permettre le salage du mot de passe.3)
URL formatée de la façon suivante :
[url]/login/r2_login.php?cmd=ask&cp=2&login=[login]&lg=[LanguageCode]
2. Serveur → Client
Le serveur retourne une ligne:
- [Status]:[commentaire ou sel]
Si status
est égal à 1, le deuxième paramètre est le sel, une suite de chiffres utilisés avec le mot de passe pour générer une chaîne de hachage.
Si le status
n'est pas égale à 1, le deuxième paramètre est un commentaire sur l'erreur.
3. Client → Serveur
Renvoie le compte & une clef permettant de vérifier que l'on connaît le mot de passe.
Cette clef est en réalité un calcul réalisé avec le sel et le mot de passe [sha512_crypt]. Cela évite d'avoir à transmettre le mot de passe, donc plus sécurisé.
Sachant que le serveur connaît aussi le sel, il exécute aussi le même calcul afin de vérifier que vous connaissez le mot de passe.
L'URL pour se connecter :
[url]/login/r2_login.php?cmd=login&login=[login]&password=[password]&clientApplication=[ClientApplication]&cp=[cp]&lg=[lg]
4. Serveur → Client
Le serveur retourne deux lignes:
- state#cookie#fsaddr#ringmainurl#fartp
- r2serverversion]#r2backuppatchurl#r2patchurl
Définition de certain paramètre:
- fsaddr = khaganat_host:khaganat_port
- cookie = user_addr|user_key|user_id
Ici, le serveur renvoie trois clefs à utiliser pour se connecter, ainsi que l'adresse du serveur à contacter.
khaganat_host, khaganat_port
: adresse à utiliser pour se connecter au serveur (en UDP)user_addr
: une chaîne de caractère qui traduit le code hexa (32 bits)user_key
: une chaîne de caractère qui traduit le code hexa (32 bits)user_id
: une chaîne de caractère qui traduit le code hexa (32 bits)
Ces clefs seront utiles dans la première phase de communication avec le serveur.
Étape 2 : Communication avec le jeu
Vue générale
Première chose, on remarque que le protocole est UDP.
Il n'y a donc pas de synchronisation avec le serveur, on envoie les messages, ils arrivent dans n'importe quel ordre et c'est à l'application de faire le tri.
L’intérêt est que la communication est plus rapide, et que sur le principe d'un jeu “temps réel”, les actions passées ne sont pas importantes, seul compte l'état actuel.
Pour faire une analogie, il faut voir le réseau comme une multitude d'autoroute et de péage qui s’entremêlent. Suivant votre chauffeur, vous irez plus ou moins vite, avec des raccourcis plus au moins chanceux (et parfois des accidents).
Au départ, on souhaite envoyer une message du point A vers le point B (serveur → client ou client → serveur).
Les messages sortent les uns après les autres.
Arrivé au premier croisement (switch), certains partent à droite, d'autres à gauche.
Arrivé au péage (firewall), le message est bloqué pour analyse, par contre d'autres poursuivent leur chemin.
On continue sur le même principe.
Mais des fois, nous perdons un paquet suite à un accident (collision avec un autre message).
Et enfin tous les messages restants arrivent à destination (pas forcément dans l'ordre envoyé).
Ce mode de fonctionnement implique une gestion de la couche réseau côté applicatif, au niveau du serveur et du client.
La communication est en fonction de l'état de la connexion
La communication s'effectue suivant différents état.
NotInitialised
: État initialeNotConnected
: État non utilisé, réception du cookieAuthenticate
: État non utilisé, authentificationLogin
: État loginSynchronize
: État synchronisationConnected
: État connecté, ici, on est dans la phase communication du jeu,Probe
: État approbation, on demande de vérifier que le client est bien connectéStalled
: État bloquéDisconnect
: État déconnecté- Quit : État quitter
- ForceSynchronize : État forçant la synchronisation (non existant côté serveur)
Dans le code, l'idéal serait de gérer l'état dans des classes différentes, afin de ne gérer que l'état en cours et de ne pas polluer le code de if
/switch
/case
.
Paquets envoyés par le serveur
Maintenant, regardons d'un peu plus près chaque message envoyé par le serveur.
Corps du message
+-------------------+------------+--- | CurrentSendNumber | SystemMode | . . . | 32 bits | 1 bit | +-------------------+------------+--- 1 -> system mode 0 -> normal mode
- CurrentSendNumber : un compteur qui s'incrémente tout le temps (permet d'ignorer les messages passés). Attention: quand le compteur aura atteint le maximum, le client va refuser tous les messages.
- SystemMode : Informe que le type de message est dit “système” ou “normal”.
Message système
+-------------------+------------+---------+--- | CurrentSendNumber | SystemMode | Message | . . . | 32 bits | 1 bit | 8 Bits | +-------------------+------------+---------+--- | xxxxxxxxxxxxxxxx | 1 |
Suivant la valeur du message, nous avons des paramètres spécifiques.
Notez qu'ici nous utilisons 8 bits. On aurait pu réduire la taille afin d'être en adéquation avec les messages envoyés.
À savoir qu'il y a 5 types de messages provenant du client & 5 types de messages provenant du serveur, soit une taille de 3 bits.
Message 1 : SYSTEM_SYNC_CODE
Message émis par le serveur.
Message utilisé pour synchroniser le client et vérifier que nous avons la même base de définition XML (msg.xml
& database.xml
)
+-------------------+------------+---------+-------------+---------+------------+-------------------+------------------------+ | CurrentSendNumber | SystemMode | Message | Synchronize | stime | LatestSync | MsgData | DatabaseData | | 32 bits | 1 bit | 8 Bits | 32 bits | 64 bits | 32 bits | 16 * 8 bits | 16 * 8 bits | | | | | unsigned | signed | unsigned | md5sum of msg.xml | md5sum of database.xml | +-------------------+------------+---------+-------------+---------+------------+-------------------+------------------------+ | xxxxxxxxxxxxxxxx | 1 | 1 | xxx | xxx | xxx | xxx | xxx |
Message 3 : SYSTEM_PROBE_CODE
Message émis par le serveur.
Message pour vérifier que le client reçoit bien les messages (le client va renvoyer un SYSTEM_ACK_PROBE_CODE
, avec tous les PROBE
envoyés).
+-------------------+------------+---------+-------------+ | CurrentSendNumber | SystemMode | Message | LatestProbe | | 32 bits | 1 bit | 8 Bits | 32 bits | | | | | unsigned | +-------------------+------------+---------+-------------+ | xxxxxxxxxxxxxxxx | 1 | 3 | xxx |
Message 6 : SYSTEM_STALLED_CODE
Message émis par le serveur.
+-------------------+------------+---------+ | CurrentSendNumber | SystemMode | Message | | 32 bits | 1 bit | 8 Bits | | | | | +-------------------+------------+---------+ | xxxxxxxxxxxxxxxx | 1 | 6 |
Message 7 : SYSTEM_SERVER_DOWN_CODE
Message émis par le serveur.
+-------------------+------------+---------+ | CurrentSendNumber | SystemMode | Message | | 32 bits | 1 bit | 8 Bits | | | | | +-------------------+------------+---------+ | xxxxxxxxxxxxxxxx | 1 | 7 |
Message 9 : SYSTEM_ACK_QUIT_CODE
Message émis par le serveur.
+-------------------+------------+---------+ | CurrentSendNumber | SystemMode | Message | | 32 bits | 1 bit | 8 Bits | | | | | +-------------------+------------+---------+ | xxxxxxxxxxxxxxxx | 1 | 7 |
Message normal
+-------------------+------------+-------------------+---------+-----------------+ | CurrentSendNumber | SystemMode | last received ack | Factory | Visual Property | | 32 bits | 1 bit | 32 Bits | X bits | Z bits | | | | unsigned | | | +-------------------+------------+-------------------+---------+-----------------+ | xxxxxxxxxxxxxxxx | 0 |
Factory
Ici nous avons tout un système de boîtes afin de savoir dans quel rayon on lit le message. Utilisation à voir, peut-être une méthode pour différencier les différents services du serveur.
A l'origine
On stocke les paquets suivant le paramètre CurrentSendNumber
. Mais pour chaque paquet, on doit d'abord lire un bit pour identifier si on a des données ou pas.
Les différents niveaux suivant la valeur de CurrentSendNumber
:
+----------+ +----------+ +----------+ | Packet A | | Packet B | | Packet C | | (0) | | (0) | | (0) | +----------+ +----------+ +----------+ +----------+ +----------+ | Packet B | | Packet C | | (1) | | (1) | +----------+ +----------+ +----------+ | Packet C | | (2) | +----------+ +----------+ | Packet C | | (3) | +----------+
Si modulo CurrentSendNumber 4 == 0
:
+---------+ +----------+----------+----------+ | Factory | | Packet A | Packet B | Packet C | | | ===> | pos:0 | pos:0 | pos:0 | | X bits | | X bits | X bits | X bits | +---------+ +----------+----------+----------+
Si modulo CurrentSendNumber 4 == 1
:
+---------+ +----------+----------+----------+ | Factory | | Packet A | Packet B | Packet C | | | ===> | pos:0 | pos:1 | pos:1 | | X bits | | X bits | X bits | X bits | +---------+ +----------+----------+----------+
Si modulo CurrentSendNumber 4 == 2
:
+---------+ +----------+----------+----------+ | Factory | | Packet A | Packet B | Packet C | | | ===> | pos:0 | pos:0 | pos:2 | | X bits | | X bits | X bits | X bits | +---------+ +----------+----------+----------+
Si modulo CurrentSendNumber 4 == 3
:
+---------+ +----------+----------+----------+ | Factory | | Packet A | Packet B | Packet C | | | ===> | pos:0 | pos:1 | pos:3 | | X bits | | X bits | X bits | X bits | +---------+ +----------+----------+----------+
Pour le packet A
: tant que next
est égale à 1, on lit le paquet (Level A) :
+----------+ +-------+-----------+-------+-----------+-------+ | Packet A | | next | Factory A | next | Factory A | next | . . . | X bits | --> | 1 bit | (0) | 1 bit | (0) | 1 bit | | | | | | | | | +----------+ +-------+-----------+-------+-----------+-------+
Pour le packet B :
- Tant que
next
est égal à 1, on lit le paquet (Level B) - La position
x
dépend deCurrentSendNumber
+----------+ +-------+-----------+-------+-----------+-------+ | Packet B | | next | Factory B | next | Factory B | next | . . . | X bits | --> | 1 bit | (x) | 1 bit | (x) | 1 bit | | | | | | | | | +----------+ +-------+-----------+-------+-----------+-------+
Pour le packet C:
- Tant que next est égal à 1, on lit le paquet (Level C)
- La position
y
dépend deCurrentSendNumber
+----------+ +-------+-----------+-------+-----------+-------+ | Packet C | | next | Factory C | next | Factory C | next | . . . | X bits | --> | 1 bit | (y) | 1 bit | (y) | 1 bit | | | | | | | | | +----------+ +-------+-----------+-------+-----------+-------+
Exemple avec CurrentSendNumber
égal à 7 (donc CurrentSendNumber
% 4 = 3)
+---------+ +-------+-----------+-------+-----------+-------+-------+-----------+-------+-------+-----------+-------+-----------+-------+ | | | | Action | | Action | | | Action | | | Action | | Action | | | Factory | | next | Factory A | next | Factory A | next | next | Factory B | next | next | Factory C | next | Factory C | next | | | ===> | | (0) | | (0) | | | (1) | | | (3) | | (3) | | | X bits | | 1 bit | X bits | 1 bit | X bits | 1 bit | 1 bit | X bits | 1 bit | 1 bit | X bits | 1 bit | X bits | 1 bit | +---------+ +-------+-----------+-------+-----------+-------+-------+-----------+-------+-------+-----------+-------+-----------+-------+ | 1 | xxx | 1 | xxxx | 0 | 1 | xxx | 0 | 1 | xxx | 1 | xxx | 0 | | ---- Packet A ---- | ----- Packet B ---- | ----- Packet C ---- |
Et maintenant
N'ayant pas la signification exacte, je considère que tout a le même niveau (bref on lit au fil de l'eau).
On lit les trois paquets (A, B & C, que j'ai converti/nommé Z), car je ne vois pas très bien l'interet d'avoir trois niveaux, sachant qu'après on remet tout au même niveau, peut-etre un optimisation pour les reprises suite à la perte de paquet)
+---------+ +----------+----------+----------+ | Factory | | Packet Z | Packet Z | Packet Z | | X bits | | X bits | X bits | X bits | +---------+ +----------+----------+----------+
Puis pour chaque paquet Z on lit le champ next
et si ert à '1' on lit ActionFactory X, puis on lit next
suivant:
+----------+ +-------+-----------------+-------+-----------------+-------+ | Packet Z | | next | ActionFactory X | next | ActionFactory X | next | . . . | X bits | --> | 1 bit | X bits | 1 bit | X bits | 1 bit | +----------+ +-------+-----------------+-------+-----------------+-------+
ActionFactory
Dans chaque ActionFactory
, nous avons la structure suivante:
+-----------------+ +-------+--------+
| ActionFactory X | --> | Code | Action |
| | | X bit | X Bit |
+-----------------+ +-------+--------+
On y découvre un code (qui identifie l'action à décoder) et l'action4).
Code
Ici, nous avons une petite structure pour définir ActionCode
. En gros, on décode ce chiffre en 3 bits ou 9 bits. Sachant que le plus souvent, on aura à décoder seulement 3 bits.
+------+ +-----------+---------------------------+
| Code | -> | ShortCode | ActionCode |
| | | 1 bit | 2 bits, si ShortCode = 1 |
| | | | 8 bits, si ShortCode = 0 |
+------+ +-----------+---------------------------+
Voir la section Action pour ActionCode.
Paquet envoyé par le client
Il faut remarqué ici, que la taille d'un paquet envoyé par le client est limité, si on dépasse, le serveur ignore le message.
Corps du message
+-------------------+------------+--- | CurrentSendNumber | SystemMode | . . . | 32 bits | 1 bit | +-------------------+------------+--- 1 -> system mode 0 -> normal mode
- CurrentSendNumber : un compteur qui s'incrémente tout le temps (permet d'ignorer les messages passés). Attention: quand le compteur aura atteint le maximum, le client va refuser tous les messages.
- SystemMode : Informe que le type de message est dit “système” ou “normal”.
Message système
+-------------------+------------+---------+--- | CurrentSendNumber | SystemMode | Message | . . . | 32 bits | 1 bit | 8 Bits | +-------------------+------------+---------+--- | xxxxxxxxxxxxxxxx | 1 |
Suivant la valeur du message, nous avons des paramètres spécifiques.
Notez qu'ici nous utilisons 8 bits, on aurait pu réduire la taille afin d'être en adéquation avec les messages envoyés.
À savoir qu'il y a 5 types de messages provenant du client & 5 types de messages provenant du serveur, soit une taille de 3 bits.
Message 0 : SYSTEM_LOGIN_CODE
Message émis par le client.
+-------------------+------------+---------+-----------+----------+----------+-------- / ---------+ | CurrentSendNumber | SystemMode | Message | User Addr | User Key | User ID | Langue | | 32 bits | 1 bit | 8 Bits | 32 bits | 32 bits | 32 bits | String (x bits) | | | | | unsigned | unsigned | unsigned | | +-------------------+------------+---------+-----------+----------+----------+-------- / ---------+ | xxxxxxxxxxxxxxxx | 1 | 0 | xxx | xxx | xxx | xxx |
Message 2 : SYSTEM_ACK_SYNC_CODE
Message émis par le client. Message utilisé pour acquitter d'une demande de synchronisation.
+-------------------+------------+---------+-----------+----------+-----------------+----------+ | CurrentSendNumber | SystemMode | Message | frontack | backack | longackbitfield | syncCode | | 32 bits | 1 bit | 8 Bits | 32 bits | 32 bits | 1024 bits | 32 bits | | | | | unsigned | unsigned | unsigned | unsigned | +-------------------+------------+---------+-----------+----------+-----------------+----------+ | xxxxxxxxxxxxxxxx | 1 | 2 | xxx | xxx | xxx | xxx |
frontack
: dernier message reçu (last_received_number
)backack
:last_ack_in_long_ack
longackbitfield
: suite de bit qui indique les messages normaux reçus (permettra de faire une synchronisation avec le serveur)syncCode
:_server_sync
Message 4 : SYSTEM_ACK_PROBE_CODE
Message émis par le client.
Acquitte des messages PROBE envoyés.
+-------------------+------------+---------+-------------+---------+---------+-- / --+---------+ | CurrentSendNumber | SystemMode | Message | Taille : X | Probe 1 | Probe 2 | | Probe X | | 32 bits | 1 bit | 8 Bits | 32 bits | 32 bits | 32 bits | | 32 bits | | | | | signed | signed | signed | | signed | +-------------------+------------+---------+-------------+---------+---------+-- / --+---------+ | xxxxxxxxxxxxxxxx | 1 | 4 |
Message 5 : SYSTEM_DISCONNECTION_CODE
Message émis par le client.
+-------------------+------------+---------+ | CurrentSendNumber | SystemMode | Message | | 32 bits | 1 bit | 8 Bits | | | | | +-------------------+------------+---------+ | xxxxxxxxxxxxxxxx | 1 | 5 |
Message 8 : SYSTEM_QUIT_CODE
Message émis par le client.
+-------------------+------------+---------+---------+ | CurrentSendNumber | SystemMode | Message | Quit Id | | 32 bits | 1 bit | 8 Bits | 32 bits | | | | | signed | +-------------------+------------+---------+---------+ | xxxxxxxxxxxxxxxx | 1 | 8 |
Message normal
+-------------------+------------+---- | CurrentSendNumber | SystemMode | . . . | 32 bits | 1 bit | +-------------------+------------+---- | xxxxxxxxxxxxxxxx | 0 |
À remplir.
Les différents groupes de données
Action
Nous avons différents type Action
:
ACTION_POSITION_CODE = 0
: Disponible en modeShortCode = 1
ou0
ACTION_GENERIC_CODE = 1
: Disponible en modeShortCode = 1
ou0
ACTION_GENERIC_MULTI_PART_CODE = 2
: Disponible en modeShortCode = 1
ou0
ACTION_SINT64 = 3
: Disponible en modeShortCode = 1
ou0
ACTION_SYNC_CODE = 10
: Disponible en modeShortCode = 0
ACTION_DISCONNECTION_CODE = 11
: Disponible en modeShortCode = 0
ACTION_ASSOCIATION_CODE = 12
: Disponible en modeShortCode = 0
ACTION_LOGIN_CODE = 13
: Disponible en modeShortCode = 0
ACTION_TARGET_SLOT_CODE = 40
: Disponible en modeShortCode = 0
ACTION_DUMMY_CODE = 99
: Disponible en modeShortCode = 0
ACTION_POSITION_CODE
+----------+----------+----------+ | px | py | pz | | 16 bits | 16 bits | 16 bits | | unsigned | unsigned | unsigned | +----------+----------+----------+
+-------------------------------+ | pz | | 14 bit | | | | | | | | | | | | | | | | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Interior + IsRelative
ACTION_GENERIC_CODE
On récupère une chaîne contenant le code impulse
.
+----------+------+------+-- / --+--------+ +---------+ | size | char | char | | char | | | | 32 bits | a a+1 a+size | ==> | Impulse | | unsigned | | | | +----------+------+------+-- / --+--------+ +---------+
size
: nombre de caractères dans ce blocchar
: les données
Impulse
est la concaténation de toutes les chaines : char [a, a+1, … , a+size].
ACTION_GENERIC_MULTI_PART_CODE
On ne récupère qu'une partie du code impulse
. Il faudra attendre de recevoir toutes les parties pour pouvoir décoder ce code impulse
.
+----------+----------+----------+----------+------+------+-- / --+--------+ | Number | Part | NbBlock | size | char | char | | char | | 8 bits | 16 bits | 16 bits | 32 bits | a a+1 a+size | | unsigned | unsigned | unsigned | unsigned | | +----------+----------+----------+----------+------+------+-- / --+--------+
Number
: Numéro de paquet (doit être unique du moins pendant une courte période)Part
: position du blocNbBlock
: Nombre de bloc (total)size
: nombre de caractère dans ce blocchar
: les données
Pour décoder, il faut recevoir pour le même Number
toutes les Part
(blocs), les remettre dans l'ordre et lancer le décodage impulse
.
Exemple :
- Number : 1
- NbBlock : 3
+---------+---------+---------+ +---------+ | Part 1 | Part 2 | Part 3 | ===> | Impulse | | | | | | | | | | | | | | | | | | | +---------+---------+---------+ +---------+
ACTION_SINT64
Voilà un format/type qu'il faut retenir. Il est utilisé dans la partie visual decode
, mais pas comme son nom l'indique. En fait il s'agit d'une structure à taille variable suivant sa destination. Bref, on sent la réutilisation d'un code pour faire autre chose, dommage car vu la capacité, pourquoi ne pas avoir ajouter d'autres format/type ?
ACTION_SYNC_CODE
Structure utilisée pour la synchronisation.
+----------+------------+ | Sync | BKEntityId | | 32 Bits | 64 bits | | unsigned | unsigned | +----------+------------+
ACTION_DISCONNECTION_CODE
Code pour lancer une déconnexion. Il n'y a pas de paramètre.
ACTION_ASSOCIATION_CODE
Certainement pour associer une nouvelle zone.
+----------+---------+ | SheetId | Replace | | 32 Bits | 1 bit | | unsigned | | +----------+---------+
ACTION_LOGIN_CODE
Un code qui aurait du être utilisé pour l’authentification. Mais on ne l'utilise jamais ! On utilise la commande système SYSTEM_LOGIN_CODE. Bref les mystères de l'évolution du code.
+-----------+----------+----------+ | user addr | user key | user id | | 32 Bits | 32 bits | 32 bits | | unsigned | unsigned | unsigned | +-----------+----------+----------+
ACTION_TARGET_SLOT_CODE
Un nouveau slot ?
+----------+------------------+ | Slot | Target Or Pickup | | 8 Bits | 2 bits | | unsigned | unsigned | +----------+------------------+
ACTION_DUMMY_CODE
Un code de test.
+-----------+----------+ | Dummy 1 | Dummy 2 | | 32 Bits | 32 bits | | unsigned | unsigned | +-----------+----------+
Impulse
Comment ça marche
Ici, nous allons utilisé la structure msg.xml
pour pouvoir décoder le message. Le décodage demande d'abord de comprendre comment lire le fichier msg.xml
.
Lecture du fichier msg.xml
Le fichier xml a une structure du style :
<client_messages_description> <branch name=x1 param_a=y1 param_b=z1 position=0> <leaf name=x2 param_aa=y2 param_bb=z2 position=0/> </branch> <branch name=x3 param_a=y3 param_b=z3 position=1> <leaf name=x4 param_aa=y4 param_bb=z4 position=0/> <leaf name=x5 param_aa=y5 param_bb=z5 position=1/> </branch> <branch name=x6 param_a=y6 param_b=z6 position=2> <branch name=x7 param_a=y7 param_b=z7 position=0> <leaf name=x8 param_aa=y8 param_bb=z8 position=0/> <leaf name=x9 param_aa=y9 param_bb=z9 position=1/> <leaf name=x10 param_aa=y10 param_bb=z10 position=2/> </branch> <leaf name=x11 param_aa=y11 param_bb=z11 position=1/> <leaf name=x12 param_aa=y12 param_bb=z12 position=2/> <leaf name=x13 param_aa=y13 param_bb=z13 position=3/> <leaf name=x14 param_aa=y14 param_bb=z14 position=4/> </branch> </client_messages_description>
Pour msg.xml
, la première clef est client_messages_description
.
Ensuite nous avons deux types de clefs : branch
(branche) ou leaf
(feuille).
- La clef
branch
permet d'indiquer qu'elle contient d'autres clefs à l’intérieur. Ces clefs à l'intérieur debranch
peuvent être du typebranch
ouleaf
). - La clef
leaf
indique qu'elle ne contient qu'elle : pas de sous-branche ou de sous-feuille.
Pour décoder nous avons le code impulse
. Nous avons besoin dans chaque branche de compter le nombre de sous-branches et sous-feuilles.
Par exemple, ici
- la branche racine s'appelle
client_messages_description
, nous comptons 3 sous-branches (appelées x1, x3 & x6). Chaque branche/feuille a une position : on commence par zéro, et on incrémente à chaque fois. Ici j'ai ajouté le paramètre position juste pour suivre, il n'existe pas en réalité dans le codemsg.xml
. - La branche x1 (en position 0), nous comptons une seule sous-branche/feuille (appelé x2) ;
- La branche x3 (en position 1), nous comptons 2 sous-branches/feuilles (appelé x4 & x5) ;
- La branch x6 (en position 2), nous comptons 5 sous-branches/feuilles (appelé x7, x11, x12, x13 & x14) ;
- La branche x7 (en position 0), nous comptons 3 sous-branches/feuilles (appelé x8, x9 & x10).
Ce résultat nous permettra d'avoir le nombre de possibilités et aussi connaitre le nombre de bit accordé pour la branche en question. On répète le calcul jusqu’à arriver à une feuille qui termine et donne la fonction complète.
Exemple (ici je ne mets que les bits) :
- code: 010
- on compte le nombre de branche/sous-branche sur la racine (ici on a en 3, donc le code tient sur 2 bits)
- on décode les deux premier bit (ici 01, correspond à la position 1, qui équivaut à x3)
- puis dans la branche x3, on compte le nombre de branche/sous branche, il y en a 1.
- on décode un bit (ici 0) qui correspond à la position 0, qui équivaut à x4
- en résumé : 010 ⇒ 01.0 ⇒ 01 → x3, 0 → x4
- cela donne x3:x4
- code: 1000001
- on compte le nombre de branche/sous-branche sur la racine (ici on a en 3, donc le code tient sur 2 bits)
- on décode les deux premier bit (ici 10, correspond à la position 2, qui équivaut à x6)
- puis dans la branche x6, on compte le nombre de branche/sous branche, il y en à 5 (donc le code tient sur 3 bits).
- on décode un bit (ici 000) qui correspond à la position 0, qui équivaut à x7
- puis dans la branche x7, on compte le nombre de branche/sous branche, il y en à 3 (donc le code tient sur 2 bits).
- en résumé : 1000001 ⇒ 10.000.01 ⇒ 10 → x6, 000 → x7, 01 → x9
- cela donne x6:x7:x9
Maintenant, vous savez comment décoder l'entête des messages impulse
. Attention, le reste du codage dans msg.xml
ne sert pas pour le client (mais certainement pour la communication entre les différents services du serveur).
Lecture du fichier database.xml
En complément des messages provenant de msg.xml, nous avons une autre référence database.xml qui a un mode de fonctionnement un peu différent. Certainne commande Impulse (msg.xml), auront un message encodé via database.xml
Structure du fichier:
<database_description> <bank_superclass> <!-- dans cette zone existe des informations non utilisés par le client --> </bank_superclass> <branch name=x1 bank="PLR" clientonly="1"> <leaf name=x2 type=t1/> </branch> <branch name=x3 bank="PLR" count="c3"> <leaf name=x4 type=t4/> <leaf name=x5 type=t5/> </branch> <branch name=x6 bank="PLR" atom="1"> <branch name=x7 atom="1" count="c7"> <leaf name=x8 type=t8/> <leaf name=x9 type=t9/> <leaf name=x10 type=t10/> </branch> <leaf name=x11 type=t11/> <leaf name=x12 type=t12/> <leaf name=x13 type=t13/> <leaf name=x14 type=t14/> </branch> </database_description>
Nous trouvons le mot de clef branch, que l'on va traduire par branche, et le mot de clef leaf que l'on traduit par feuille.
Ici le premier niveau qui nous interrese est database_description, puis ensuite on ne garde que les branches ayant le parametre bank égale “PLR”. Pour la partie branche, cet élément peut contenir d'autres branches (zero, une, …, X) et/ou des feuilles (de 0, 1 ou X). Dans sa structure interne, ces deux entités (branches & feuilles) sont séparées, et on regardera d'abord dans les branches puis ensuite dans les feuilles.
Si une branch contient le mot de clef “count”, cela implique une duplication de cette branche X fois (avec pour nom le numéro de position commençant par 0)
Ensuite, nous avons la possibilité d'avoir le mot clef 'atom', s'il est à 1, cela implique une gestion différente de l'agragation des données. (dans ce cas, on considère que l'on aura une structure pouvant envoyer toutes les données [dans en premier temps on envoie un booléen pour chaque feuille disponible dans la branche/sous-branche, puis pour chaque booléen ayant la valeur 1/Vrai, on lira la donnée de la feuille correspondante)
L'association de count & atom m'a particulièrement posé des problèmes, dans les fait 'count' est prioritaire, ensuite on gère le cas de 'atom'.
Un petit exemple:
<database_description> <branch name="Entities" bank="PLR" clientonly="1"> <branch name="E" count="2"> <leaf name="P" count="3" type="I8" /> </branch> </branch> <branch name="USER" bank="PLR" atom="1"> <leaf name="HAIR_TYPE" type="I8" /> <branch name="SKILL_POINTS_" count="4"> <leaf name="VALUE" type="I32" /> </branch> <branch name="FACTION_POINTS_" count="7"> <leaf name="VALUE" type="I32" /> </branch> </branch> <branch name="GROUP" bank="PLR"> <branch name="" count="5" atom="1"> <leaf name="PRESENT" type="I1" /> <leaf name="UID" type="I20" /> </branch> </branch> </database_description>
* Entities * E0 * P0 (type : I8) * P1 (type : I8) * P2 (type : I8) * E1 * P0 (type : I8) * P1 (type : I8) * P2 (type : I8) * USER (Atom) * SKILL_POINTS_0 * VALUE (type : I32) * SKILL_POINTS_1 * VALUE (type : I32) * SKILL_POINTS_2 * VALUE (type : I32) * SKILL_POINTS_3 * VALUE (type : I32) * FACTION_POINTS_0 * VALUE (type : I32) * FACTION_POINTS_1 * VALUE (type : I32) * FACTION_POINTS_2 * VALUE (type : I32) * FACTION_POINTS_3 * VALUE (type : I32) * FACTION_POINTS_4 * VALUE (type : I32) * FACTION_POINTS_5 * VALUE (type : I32) * FACTION_POINTS_6 * VALUE (type : I32) * GROUP * 0 (Atom) * PRESENT (Type : I1 == Booléen) * UID (Type I20) * 1 (Atom) * PRESENT (Type : I1 == Booléen) * UID (Type I20) * 2 (Atom) * PRESENT (Type : I1 == Booléen) * UID (Type I20) * 3 (Atom) * PRESENT (Type : I1 == Booléen) * UID (Type I20) * 4 (Atom) * PRESENT (Type : I1 == Booléen) * UID (Type I20)
Par exemple le message suivant : 00.0.10.11111111
- 00 : <Entities> le 1er nieveau est sur 2 bit (on a trois elements 0,1 & 2) ⇒ 2 bit pour sélectionner celui que l'on veut.
- 0 : <E0> le 2ème niveau, nous avons ici le choix entre E0 & E1, (1 bit suffit pour trouver l'élément)
- 10 : <P1> le 3ème niveau, nous avons ici le choix entre P0, P1 & P2 (2 bits suffisent pour trouver l'élément)
- 11111111 : <I8> le 4ème niveau, la valeur de la branche
- Résumé :
- Entities:E0:P1 = 255
2ème exemple : 01.0010100.11111111111111111111111111111111.01111111111111111111111111111111
- 01 : <USER> le 1er nieveau est sur 2 bit (on a trois elements 0,1 & 2) ⇒ 2 bit pour sélectionner celui que l'on veut.
- 0010100 : <SKILL_POINTS_2,SKILL_POINTS_4> USER est du type atom, donc on compte le nombre de tous les feuilles ici 7, et pour chaque bit à 1, on doit récupèrer la valeur
- 11111111111111111111111111111111 : <VALUE> 4294967295
- 01111111111111111111111111111111 : <VALUE> 2147483647
- Résumé:
- USER.SKILL_POINTS_2 = 4294967295
- USER.SKILL_POINTS_4 = 2147483647
3ème exemple : 10.010.0.1
- 10 : <GROUP> le 1er nieveau est sur 2 bit (on a trois elements 0,1 & 2) ⇒ 2 bit pour sélectionner celui que l'on veut.
- 010 : <2> La branche suivante à count à 5, donc on doit lire 3 bits pour savoir qu'elle est l'element
- 0 : <PRESENT>
- 1 : VALUE = 1
Les types :
- Ixxx : Entier non signé ou xxx et le nombre de bit à récupèrer
- Sxxx : Entier signé, ou xxx et le nombre de bit à récupèrer
- TEXT : Entier non-signé de 32 bit (dont le bit de poids fort est forcé à zero (jamais négatif - si signé)
Décodage Impulse
Les messages impulses sont construits suivant le format suivant :
* premier champ contenant le code impulse
* champs suivants : les paramètres du code impulse
Cela donne le schéma suivant :
+--------------+---- / -----+
| code msg.xml | paramètres |
| x bits | x bits |
| unsigned | [
+--------------+---- / -----+
Commande impulse
TARGET:PARTY
TARGET:INVENTORY
TARGET:FOLLOW
TARGET:NO_FOLLOW
CONNECTION:USER_CHAR
Le serveur répond à un CONNECTION:SELECT_CHAR, et renvoie les informations sur le personnage.
Code actuel : CONNECTION:USER_CHAR ⇒ 000001.00000
.
Attention : dépend de la version que vous avez de msg.xml, susceptible de changer.
+----------------+--------------+------------------+------------------+------------------+------------------+--------+----------+--------------------------+--------------------+------------------+ | CONNECTION | USER_CHAR | X | Y | Z | Heading | season | userRole | highestMainlandSessionId | firstConnectedTime | playedTime | | 6 bits | 5 bits | 32 bits | 32 bits | 32 bits | 32 bits | 3 bits | 3 bits | 32 bits | 32 bits | 32 bits | | 000001 | 00000 | 32 bits unsigned | 32 bits unsigned | 32 bits unsigned | float | | | 32 bits unsigned | 32 bits unsigned | 32 bits unsigned | +----------------+--------------+------------------+------------------+------------------+------------------+--------+----------+--------------------------+--------------------+------------------+
- X (Sint32) : position X
- Y (Sint32) : position Y
- Z (Sint32) : position Z
- Heading (Float) : Angle (plan X/Y) du personnage (vers ou regarde le perso)
- season (3 bits) : type de saison
- userRole (3 bits)
- highestMainlandSessionId (Uint32)
- firstConnectedTime (Uint32)
- playedTime (Uint32)
CONNECTION:NO_USER_CHAR
CONNECTION:USER_CHARS
Liste tous les paramètres de l'utilisateur, les personnages, les domaines, …
Code actuel : CONNECTION:USER_CHARS ⇒ 000001.00010
.
Attention : dépend de la version que vous avez de msg.xml, susceptible de changer.
+------------+------------+--------------------+ | CONNECTION | USER_CHARS | ServerPeopleActive | | 6 bits | 5 bits | uint8 | . . . | 000001 | 00010 | unsigned | +------------+------------+--------------------+
- ServerPeopleActive (uint8)
- ServerPeopleActive (uint8)
- CharacterSummaries_Len (Uint32) - les paramètres suivants dépendent de la taille définie ici
- Version (Uint8)
- Mainland (Uint32)
- Name (String-UTF-16)
- People (Sint32)
- Location (Uint32)
- VisualPropA (Uint64) : en réalité on utilise moins de bit (mais ici on prend les 64 bits)
- VisualPropB (Uint64) : en réalité on utilise moins de bit (mais ici on prend les 64 bits)
- VisualPropC (Uint64) : en réalité on utilise moins de bit (mais ici on prend les 64 bits)
- SheetId (Uint32)
- Title (Sint32)
- CharacterSlot (Uint8)
- InRingSession (Bool)
- HasEditSession (Bool)
- InNewbieland (Bool)
- shard_names_Len (Uint32)
- session_id (String)
- display_name (String)
- short_name (String)
- privileges (String)
- free_trial (Bool)
- mainlands_len (Uint32)
- id (Uint32)
- name (String-UTF-16)
- description (String-UTF-16)
- language_code (String)
- online (Bool)
CONNECTION:CREATE_CHAR
Création d'un nouveau personnage.
Code actuel : CONNECTION:CREATE_CHAR ⇒ 000001.00011
.
Attention : dépend de la version que vous avez de msg.xml, susceptible de changer.
+------------+-------------+--------------------+ | CONNECTION | CREATE_CHAR | Slot | | 6 bits | 5 bits | uint8 | . . . | 000001 | 00011 | unsigned | +------------+-------------+--------------------+
- Slot (Uint8)
- SheetId (Uint32)
- CSessionId (Uint32)
- name (String-UTF-16) - Attention il n'accepte qu'un nombre restreint de caractère [A-Za-z0-9]
- People (Uint8)
- Sex (Uint8)
- NbPointFighter (Uint8)
- NbPointCaster (Uint8)
- NbPointCrafter (Uint8)
- NbPointHarvester (Uint8)
- StartPoint (Sint32)
- HairType (Sint8)
- HairColor (Sint8)
- GabaritHeight (Sint8)
- GabaritArmsWidth (Sint8)
- GabaritLegsWidth (Sint8)
- GabaritBreastSize (Sint8)
- MorphTarget1 (Sint8)
- MorphTarget2 (Sint8)
- MorphTarget3 (Sint8)
- MorphTarget4 (Sint8)
- MorphTarget5 (Sint8)
- MorphTarget6 (Sint8)
- MorphTarget7 (Sint8)
- MorphTarget8 (Sint8)
- EyesColor (Sint8)
- Tattoo (Sint8)
- JacketColor (Sint8)
- TrousersColor (Sint8)
- HatColor (Sint8)
- HandsColor (Sint8)
- FeetColor (Sint8)
On remarquera ici que le type des différentes caractéristiques est différent dans CONNECTION:USER_CHAR
. Ces propriétés sont définies dans VisualPropA
, VisualPropB
& VisualPropC
, et n'ont bien sûr pas la même taille.
CONNECTION:SELECT_CHAR
Le client sélectionne un personnage.
Code actuel : CONNECTION:SELECT_CHAR ⇒ 000001.00100
.
Attention: dépend de la version que vous avez de msg.xml, susceptible de changer.
+----------------+--------------+-----------------+ | CONNECTION | SELECT_CHAR | SelectCharMsg | | 6 bits | 5 bits | 8 bits | | 000001 | 00100 | 8 bits unsigned | +----------------+--------------+-----------------+
- SelectCharMsg (Uint8) : id du personnage (entre 0 et 4, actuellement on ne peut avoir que 5 personnages maximum)
En retour de cette commande, le serveur va répondre CONNECTION:USER_CHAR
.
CONNECTION:DELETE_CHAR
CONNECTION:RENAME_CHAR
CONNECTION:ENTER
CONNECTION:READY
Le serveur informe au client qu'il est prêt, puis le client informe ensuite au serveur qu'il est prêt.
Code actuel : CONNECTION:READY ⇒ 000001.01000
.
Attention : dépend de la version que vous avez de msg.xml, susceptible de changer.
+------------+------------+--------------+ | CONNECTION | READY | LanguageCode | | 6 bits | 5 bits | String | | 000001 | 01000 | | +------------+------------+--------------+
- LanguageCode (String)
CONNECTION:TIME_DATE_SYNCHRO
CONNECTION:ASK_NAME
Le client donne le nom et son ID (zone ?) et demande une vérification côté serveur.
Code actuel : CONNECTION:ASK_NAME ⇒ 000001.01010
.
Attention : dépend de la version que vous avez de msg.xml, suceptible de changer.
+----------------+--------------+------------------+------------------+ | CONNECTION | ASK_NAME | Name | HomeSessionId | | 6 bits | 5 bits | X bits | Uint32 | | 000001 | 01010 | String-UTF-16 | 32 bits unsigned | +----------------+--------------+------------------+------------------+
- Name (String-UTF-16)
- HomeSessionId (Uint32)
Retour attendu : CONNECTION:VALID_NAME
.
CONNECTION:VALID_NAME
Le serveur répond à une requête CONNECTION:ASK_NAME
.
Code actuel : CONNECTION:VALID_NAME ⇒ 000001.01011
Attention: dépend de la version que vous avez de msg.xml, susceptible de changer.
+----------------+--------------+-----------------+ | CONNECTION | VALID_NAME | valide | | 6 bits | 5 bits | 8 bits | | 000001 | 01011 | 8 bits unsigned | +----------------+--------------+-----------------+
- valide (Uint8) : 1 ⇒ valide, sinon non valide
CONNECTION:CREATE_CHAR_ERROR
CONNECTION:RECONNECT
CONNECTION:SERVER_RECONNECT_OK
CONNECTION:SHARD_ID
CONNECTION:SERVER_QUIT_OK
CONNECTION:SERVER_QUIT_ABORT
CONNECTION:CLIENT_QUIT_REQUEST
CONNECTION:MAIL_AVAILABLE
CONNECTION:GUILD_MESSAGE_AVAILABLE
CONNECTION:PERMANENT_BAN
CONNECTION:UNBAN
CONNECTION:FAR_TP
CONNECTION:RET_MAINLAND
DB_INIT:PLR
DB_INIT:INV
DB_GROUP:UPDATE_BANK
DB_GROUP:INIT_BANK
DB_GROUP:RESET_BANK
HARVEST:DEPOSIT
HARVEST:CORPSE
HARVEST:INTERRUPT
HARVEST:CLOSE_TEMP_INVENTORY
JOB:SET_JOB_STATUS
JOB:SWAP_JOB_STATUS
CASTING:BEGIN
CASTING:INTERRUPT
FABER:OPEN
FABER:CLOSE
FABER:EXECUTE
FABER:SET_NUM_ITEM
FABER:START_CREATE
FABER:START_REPAIR
FABER:START_REFINE
FABER:SET_MP_QUALITY
FABER:SET_MP_REFINE
FABER:SET_TOOL
FABER:RESET_TOOL
FABER:INTERRUPT
COMBAT:ENGAGE
COMBAT:DISENGAGE
COMBAT:DEFAULT_ATTACK
COMBAT:ENGAGE_FAILED
COMBAT:VALIDATE_MELEE
COMBAT:PARRY
COMBAT:DODGE
COMBAT:PROTECTED_SLOT
COMBAT:FLYING_ChaScore1_DELTA
COMBAT:FLYING_TEXT_ISE
COMBAT:FLYING_TEXT
STRING:TELL
STRING:FAR_TELL
STRING:CHAT
STRING:CHAT_TEAM
STRING:ADD_DYN_STR
STRING:FILTER
STRING:CHAT_MODE
STRING:CHAT2
STRING:TELL2
STRING:DYN_STRING
STRING:DYN_STRING_GROUP
STRING:AFK_TXT
STUN:STUN
STUN:WAKE_UP
SENTENCE:EXECUTE
SENTENCE:CANCEL
SENTENCE:CANCEL_CURRENT
SENTENCE:CANCEL_ALL
SENTENCE:CLEAR
SENTENCE:MEMORIZE
SENTENCE:FORGET
SENTENCE:EVALUATE
SENTENCE:ADD_BRICK
SENTENCE:REMOVE_BRICK
SENTENCE:START
SENTENCE:STOP
TEAM:JOIN
TEAM:LEAVE
TEAM:INVITATION
TEAM:JOIN_PROPOSAL
TEAM:JOIN_PROPOSAL_DECLINE
TEAM:KICK
TEAM:SET_SUCCESSOR
TEAM:SHARE_OPEN
TEAM:SHARE_VALID_ITEM
TEAM:SHARE_INVALID_ITEM
TEAM:SHARE_VALID
TEAM:SHARE_INVALID
TEAM:SHARE_CLOSE
TEAM:CONTACT_INIT
TEAM:CONTACT_ADD
TEAM:CONTACT_DEL
TEAM:CONTACT_MOVE
TEAM:CONTACT_CREATE
TEAM:CONTACT_STATUS
TEAM:CONTACT_REMOVE
ITEM:DROP
ITEM:PICK_UP
ITEM:PICK_UP_CLOSE
ITEM:SWAP
ITEM:ACTIVATE_SHEATH
ITEM:HARVEST
ITEM:HARVEST_CLOSE
ITEM:GIVE
ITEM:DESTROY
ITEM:EQUIP
ITEM:UNEQUIP
ITEM:TEMP_TO_BAG
ITEM:ALL_TEMP
ITEM:NO_TEMP
ITEM:ENCHANT
ITEM:OPEN_ROOM_INVENTORY
ITEM:CLOSE_ROOM_INVENTORY
ITEM:USE_ITEM
ITEM:STOP_USE_XP_CAT
TP:RESPAWN
TP:BOT
TP:WANTED
TP:DEST
TP:DEST_WITH_SEASON
TP:ACK
TP:CORRECT
DEATH:RESPAWN_POINT
DEATH:ASK_RESPAWN
DEATH:RESPAWN
ANIMALS:BEAST
ANIMALS:MOUNT_ABORT
EXCHANGE:INVITATION
EXCHANGE:CLOSE_INVITATION
EXCHANGE:PROPOSAL
EXCHANGE:ACCEPT_INVITATION
EXCHANGE:DECLINE_INVITATION
EXCHANGE:VALIDATE
EXCHANGE:INVALIDATE
EXCHANGE:END
EXCHANGE:SEEDS
EXCHANGE:ADD
EXCHANGE:REMOVE
DEBUG:WHERE
DEBUG:WHO
DEBUG:REPLY_WHERE
DEBUG:SERVICES
DEBUG:CMD
DEBUG:PING
DEBUG:COUNTER
COMMAND:EMOTE
COMMAND:CUSTOM_EMOTE
COMMAND:WHERE
COMMAND:ADMIN
COMMAND:ADMIN_OFFLINE
COMMAND:REMOTE_ADMIN
COMMAND:REMOTE_ADMIN_ANSWER
COMMAND:SIT
COMMAND:AFK
COMMAND:RANDOM
COMMAND:GUILDMOTD
MP_EVAL:SET_MP
MP_EVAL:SET_MP_LEVEL
MP_EVAL:SET_MARKET
MP_EVAL:EXECUTE
BOTCHAT:NEXT_PAGE_ITEM
BOTCHAT:NEXT_PAGE_MISSION
BOTCHAT:START_TRADE_ITEM
BOTCHAT:START_TRADE_TELEPORT
BOTCHAT:START_TRADE_FACTION
BOTCHAT:START_TRADE_SKILL
BOTCHAT:START_TRADE_PACT
BOTCHAT:START_TRADE_ACTION
BOTCHAT:BUY
BOTCHAT:SELL
BOTCHAT:DESTROY_ITEM
BOTCHAT:REFRESH_TRADE_LIST
BOTCHAT:SET_FILTERS
Le client informe de certains filtres.
À quoi servent-ils ?
Il s'agit peut-être d'outils de modération, permettant de remplacer certains mots (comme les insultes) par des étoiles.
Il pourrait aussi s'agir de filtres pour préciser sur quels canaux le message de chat est envoyé.
Bref, pour en savoir plus, il va falloir faire des tests ! S'il y a des exemples de filtres, demander à des joueuses de Ryzom (il y en a sur le canal XMPP Khaganat) si ça leur évoque quelque chose. S'il n'y a aucun exemple, donner la piste pour les trouver, et on demandera à quelqu'un ayant accès au code sous NDA de Ryzom pour trouver plus de détail. — zatalyz 2020/08/21 13:46
En fait, tout ce qui est 'botchat' c'est les échanges avec les PNJ. Les filtres des botchat sont donc les filtres des fenêtres d'achats/vente avec les PNJ. — siela1915 2020/08/22 00:15
Code actuel : BOTCHAT:SET_FILTERS ⇒ 011001.001100
.
Attention : dépend de la version que vous avez de msg.xml, susceptible de changer.
+------------+-------------------+------------------+ | BOTCHAT | SET_FILTERS | qualityMin | | 6 bits | 5 bits | 32 bits | . . . | 011001 | 001100 | 32 bits unsigned | +------------+-------------------+------------------+
- qualityMin (Uint32)
- qualityMax (Uint32)
- priceMin (Uint32)
- priceMax (Uint32)
- classMin (Uint8)
- classMax (Uint8)
- itemPart (Uint8)
- itemType (Uint8)
BOTCHAT:START_CHOOSE_MISSION
BOTCHAT:START_DYNAMIC_MISSION
BOTCHAT:CONTINUE_MISSION
BOTCHAT:VALIDATE_PLAYER_GIFT
BOTCHAT:PICK_MISSION
BOTCHAT:DM_CHOICE
BOTCHAT:DM_ACCEPT
BOTCHAT:START_NEWS
BOTCHAT:START_CREATE_GUILD
BOTCHAT:END
BOTCHAT:FORCE_END
BOTCHAT:START_CHOOSE_DUTY
BOTCHAT:NEXT_PAGE_DUTY
BOTCHAT:DUTY_APPLY
BOTCHAT:DUTY_CANCEL_APPLY
BOTCHAT:START_TRADE_GUILD_OPTIONS
BOTCHAT:BUY_GUILD_OPTION
BOTCHAT:START_GUILD_RESEARCH
BOTCHAT:DESTROY_BUILDING
BOTCHAT:DYNCHAT_OPEN
BOTCHAT:DYNCHAT_CLOSE
BOTCHAT:DYNCHAT_SEND
JOURNAL:INIT_COMPLETED_MISSIONS
JOURNAL:UPDATE_COMPLETED_MISSIONS
JOURNAL:MISSION_ABANDON
JOURNAL:GROUP_MISSION_ABANDON
JOURNAL:ADD_COMPASS
JOURNAL:ADD_COMPASS_BOT
JOURNAL:REMOVE_COMPASS
JOURNAL:REMOVE_COMPASS_BOT
STRING_MANAGER:SET_LANGUAGE
STRING_MANAGER:PHRASE_SEND
Le serveur envoie un message, plus précisément le pointeur sur le message.
Code actuel : STRING_MANAGER:PHRASE_SEND ⇒ 011011.001
Attention : dépend de la version que vous avez de msg.xml
, susceptible de changer !
+----------------+--------------+------------------+------------------+ | STRING_MANAGER | PHRASE_SEND | dyn_id | string_id | string_id . . . | 6 bits | 3 bits | uint32 | uint32 | tant que l'on a au moins 32 bits, on lit un autre string_id | 011011 | 001 | 32 bits unsigned | 32 bits unsigned | +----------------+--------------+------------------+------------------+
dyn_id
(Uint32) : l'ID du groupe de messagestring_id
(Uint32) : le pointeur sur le message
STRING_MANAGER:STRING_RQ
Le client demande le message complet en fonction du pointeur.
Code actuel : STRING_MANAGER:STRING_RQ ⇒ 011011.010
.
+----------------+--------------+------------------+ | STRING_MANAGER | STRING_RQ | string_id | | 6 bits | 3 bits | uint32 | | 011011 | 010 | 32 bits unsigned | +----------------+--------------+------------------+
string_id
(Uint32) : le pointeur sur le message
Le serveur va répondre à ce message par STRING_MANAGER:STRING_RESP
.
STRING_MANAGER:STRING_RESP
Le serveur renvoie le message complet.
Code actuel : STRING_MANAGER:STRING_RESP ⇒ 011011.011
.
+----------------+--------------+------------------+--------------+ | STRING_MANAGER | STRING_RESP | string_id | MessageUtf8 | | 6 bits | 3 bits | uint32 | String-UTF-8 | | 011011 | 011 | 32 bits unsigned | X bits | +----------------+--------------+------------------+--------------+
string_id
(Uint32) : le pointeur sur le message.MessageUtf8
(String-UTF-8) : la chaîne de caractère en UTF-8.
On remarque ici l'utilisation de la chaîne de caractère au format UTF-8 (alors qu’ailleurs nous avons UTF-16).
STRING_MANAGER:RELOAD_CACHE
Commande utilisée pour initialiser le tableau de message.
Ici, on vérifie que notre tableau interne au client a la même date que le tableau du serveur. Sinon, on efface le tableau interne du client, et à chaque nouveau message le client demandera à quoi il correspond.
Code actuel : STRING_MANAGER:RELOAD_CACHE ⇒ 011011.100
Attention : dépend de la version que vous avez de msg.xml
, susceptible de changer !
+----------------+--------------+------------------+ | STRING_MANAGER | RELOAD_CACHE | timestamp | | 6 bits | 3 bits | uint32 | | 011011 | 100 | 32 bits unsigned | +----------------+--------------+------------------+
timestamp (uint32)
: date du tableau de message
GUILD:CREATE
GUILD:ABORT_CREATION
GUILD:OPEN_GUILD_WINDOW
GUILD:INVITATION
GUILD:ACCEPT_INVITATION
GUILD:REFUSE_INVITATION
GUILD:JOIN_PROPOSAL
GUILD:SET_GRADE
GUILD:SET_LEADER
GUILD:KICK_MEMBER
GUILD:TELEPORT
GUILD:ASCENSOR
GUILD:FIRST_ASCENSOR_PAGE
GUILD:NEXT_ASCENSOR_PAGE
GUILD:LEAVE_ASCENSOR
GUILD:QUIT
GUILD:SET_PLAYER_TITLE
GUILD:UPDATE_PLAYER_TITLE
GUILD:USE_FEMALE_TITLES
Le serveur signale que la guilde5) utilise le féminin dans les titres.
Code actuel : GUILD:USE_FEMALE_TITLES ⇒ 011100.10010
Attention : dépend de la version que vous avez de msg.xml, susceptible de changer.
+------------+-------------------+--------------------+ | GUILD | USE_FEMALE_TITLES | UseFemaleTitles | | 6 bits | 5 bits | bool | | 011100 | 10010 | 1 bit | +------------+-------------------+--------------------+
- UseFemaleTitles (bool) : 1 = Oui, 0 = Non
GUILD:PUT_MONEY
GUILD:TAKE_MONEY
GUILD:OPEN_INVENTORY
GUILD:CLOSE_INVENTORY
OUTPOST:GIVEUP_OUTPOST
OUTPOST:SELECT
OUTPOST:UNSELECT
OUTPOST:DECLARE_WAR_START
OUTPOST:DECLARE_WAR_ACK
OUTPOST:DECLARE_WAR_VALIDATE
OUTPOST:SET_DEF_PERIOD
OUTPOST:SET_SQUAD
OUTPOST:SET_SQUAD_SPAWN
OUTPOST:INSERT_SQUAD
OUTPOST:REMOVE_SQUAD
OUTPOST:SET_SQUAD_CAPITAL
OUTPOST:CHOOSE_SIDE
OUTPOST:SIDE_CHOSEN
OUTPOST:BANISH_PLAYER
OUTPOST:BANISH_GUILD
OUTPOST:BUY_BUILDING
OUTPOST:DESTROY_BUILDING
PHRASE:DELETE
PHRASE:LEARN
PHRASE:MEMORIZE
PHRASE:FORGET
PHRASE:EXECUTE
PHRASE:EXECUTE_CYCLIC
PHRASE:EXECUTE_FABER
PHRASE:DOWNLOAD
PHRASE:BUY
PHRASE:CONFIRM_BUY
PHRASE:BUY_SHEET
PHRASE:CANCEL_LINK
PHRASE:CANCEL_TOP
PHRASE:CANCEL_ALL
PHRASE:CRISTALIZE
PHRASE:EXEC_CYCLIC_ACK
PHRASE:EXEC_NEXT_ACK
ITEM_INFO:GET
ITEM_INFO:SET
ITEM_INFO:REFRESH_VERSION
MISSION_PREREQ:GET
MISSION_PREREQ:SET
MISSION:ENTER_CRITICAL
MISSION:ASK_ENTER_CRITICAL
MISSION:CLOSE_ENTER_CRITICAL
MISSION:WAKE
MISSION:GROUP_WAKE
DUEL:ASK
DUEL:ACCEPT
DUEL:REFUSE
DUEL:ABANDON
DUEL:INVITATION
DUEL:CANCEL_INVITATION
PVP_CHALLENGE:ASK
PVP_CHALLENGE:ACCEPT
PVP_CHALLENGE:REFUSE
PVP_CHALLENGE:ABANDON
PVP_CHALLENGE:INVITATION
PVP_CHALLENGE:CANCEL_INVITATION
PVP:PVP_TAG
PVP:SET_NEUTRAL_ALLEGIANCE
PVP:SET_NEUTRAL_ALLEGIANCE_GUILD
PVP_FACTION:PUSH_FACTION_WAR
PVP_FACTION:POP_FACTION_WAR
PVP_FACTION:FACTION_WARS
ENCYCLOPEDIA:UPDATE
ENCYCLOPEDIA:INIT
USER:BARS
USER:POPUP
EVENT:SET_ITEM_CUSTOM_TEXT
EVENT:DUMMY
TOTEM:BUILD
TOTEM:DUMMY
MODULE_GATEWAY:FEOPEN
MODULE_GATEWAY:GATEWAY_MSG
MODULE_GATEWAY:FECLOSE
SEASON:SET
SEASON:DUMMY
DM_GIFT:BEGIN
DM_GIFT:VALIDATE
RING_MISSION:MISSION_RING_SELECT
RING_MISSION:DSS_DOWN
NPC_ICON:GET_DESC
NPC_ICON:SET_DESC
NPC_ICON:SVR_EVENT_MIS_AVL
NPC_ICON:SET_TIMER
Visual Property
Et oui, nous avons encore une autre structure pour envoyer des informations du serveur vers le client.
Ici le codage/cryptage est différent, on utilise un arbre (avec des feuilles) pour savoir quelles sont les données à décrypter.
Pourquoi cette structure?
- Peut-etre pour réduire la taille des info, mais on perd la modularité de msg.xml
- l'autre hypothèse est que ces informations sont spécifiques pour les effets visuels (et qu'ils sont suceptibles d'etre tronqué, mais aussi de ne pas bloquer le jeux pour ces effets, car on ne gère pas le renvoie de ces données, enfin je crois)
Visiblement, le serveur gère les mêmes données est il envoie les mises à jour pour certain d'entre eux.
Le début de la structure:
+----------+-----------------+------------------+----------------+------------------+ | Slot | associationBits | timestampIsThere | timestampDelta | VisualProperties | | 8 Bits | 2 bit | 1 bit | 4 bit | X bits | | unsigned | | | (optionnel) | | +----------+-----------------+------------------+----------------+------------------+
- slot : position/slot des données
- associationBits : un indicateur (sorte de clef de hachage) pour savoir si on parle de la même donnée, sinon l'ancienne donnée sera effacé.
- timestampIsThere : active le delta sur le timestamp (0: non, 1:oui)
- timestampDelta : permet d'ajouter un delta au timestamp (optionnel dépend de timestampIsThere)
- VisualProperties : ici nous avons une nouvelle structure qui fonctionne par branche (et feuille pour les terminaison)
VisualProperties
Il s'agit d'une liste sans fin de VisualProperty
+----------------+----------------+ | VisualProperty | VisualProperty | . . . +----------------+----------------+
VisualProperty
Il s'agit d'un arbre qui se sépare à chaque fois en deux branch VPA et VPB.
Pour chacune des branches (on commence par VPA, puis VPB), on lit un bit (0: pas de donnée, 1: des données), si un branch contient une autre branche, on refait de comme précédement (lecture VPA puis VPB). puis on continue jusqu'à atteindre la feille, là suivant le type de propriété on va lire un certain nombre debit.
Dans les faits, ils utilisent ACTION_SINT64 pour toutes les valeurs, mais avec une taille différents pour chaque éléments (et cerise sur le gâteau, PROPERTY_ORIENTATION n'est pas un entier mais un flotant)
Les différents Property
POSITION_CODE
+----------+----------+----------+ | px | py | pz | | 16 Bits | 16 Bits | 16 Bits | | unsigned | unsigned | unsigned | +----------+----------+----------+
- px : position x
- py : position y
- pz : position z (isRelative: 0x1, Interior: 0x2)
+-------------------------------+ | pz | | 14 bit | | | | | | | | | | | | | | | | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Interior + IsRelative
PROPERTY_ORIENTATION
Indique la rotation (par rapport à quoi ?, le nord ?)
+-------------+ | Orientation | | 32 Bits | | float | +-------------+
PROPERTY_BEHAVIOUR
+-------------+ | Behaviour | | 48 Bits | | Unsigned | +-------------+
PROPERTY_OWNER_PEOPLE
+--------------+ | Owner People | | 3 Bits | | Unsigned | +--------------+
PROPERTY_NAME_STRING_ID
+----------------+ | Name String Id | | 32 Bits | | Unsigned | +----------------+
PROPERTY_CONTEXTUAL
+------------+ | Contextual | | 16 Bits | | Unsigned | +------------+
PROPERTY_TARGET_LIST
+-------------+ | Target List | | 32 Bits | | Unsigned | +-------------+
PROPERTY_TARGET_ID
+-----------+ | Target ID | | 8 Bits | | Unsigned | +-----------+
PROPERTY_MODE
+-----------+ | Mode | | 44 Bits | | Unsigned | +-----------+
PROPERTY_VPA
+-----------+ | VPA | | 64 Bits | | Unsigned | +-----------+
PROPERTY_BARS
+-----------+ | BARS | | 32 Bits | | Unsigned | +-----------+
Avec le petit commentaire du code source : please do not lower it (or tell Olivier: used for forage sources)
PROPERTY_VISUAL_FX
+-----------+ | VISUAL FX | | 11 Bits | | Unsigned | +-----------+
Avec le petit commentaire du code source : please do not lower it (or tell Olivier: used for forage sources)
PROPERTY_VPB
+-----------+ | VPB | | 47 Bits | | Unsigned | +-----------+
PROPERTY_VPC
+-----------+ | VPC | | 58 Bits | | Unsigned | +-----------+
PROPERTY_EVENT_FACTION_ID
+------------------+ | Event Faction ID | | 32 Bits | | Unsigned | +------------------+
PROPERTY_PVP_MODE
+----------+ | PvP Mode | | 10 Bits | | Unsigned | +----------+
PROPERTY_PVP_CLAN
+----------+ | PvP Clan | | 32 Bits | | Unsigned | +----------+
PROPERTY_ENTITY_MOUNTED_ID
+-------------------+ | Entity Mounted ID | | 8 Bits | | Unsigned | +-------------------+
PROPERTY_RIDER_ENTITY_ID
+-----------------+ | Rider Entity ID | | 8 Bits | | Unsigned | +-----------------+
PROPERTY_OUTPOST_INFOS
+---------------+ | Outpost Infos | | 16 Bits | | Unsigned | +---------------+
PROPERTY_GUILD_SYMBOL
+--------------+ | Guild Symbol | | 60 Bits | | Unsigned | +--------------+
PROPERTY_GUILD_NAME_ID
+---------------+ | Guild Name ID | | 32 Bits | | Unsigned | +---------------+
Exemple
A faire …
Annexe : Format des messages
bool : 1 bit
Juste un bit, vrai ou faux (true [1]/false [0]).
+-------+ | 1 bit | +-------+
uint8 : Unsigned int 8 bits
+--------+ | 8 bits | +--------+
uint16 : Unsigned int 16 bits
On écrit le chiffre en commençant par la partie la plus faible jusqu'au plus fort. Format little endian.
Ex. Valeur : 0x1234
+--------+--------+ | low | high | | 8 Bits | 8 Bits | +--------+--------+ | 0x34 | 0x12 |
uint32 : Unsigned int 32 bits
On écrit le chiffre en commençant par la partie la plus faible jusqu'au plus fort. Format little endian.
Ex. Valeur : 0x12345678
+--------+--------+--------+--------+ | root | low | medium | high | | 8 Bits | 8 Bits | 8 Bits | 8 Bits | +--------+--------+--------+--------+ | 0x78 | 0x56 | Ox34 | Ox12 |
sint32 : int 32 bits
On écrit le chiffre en commençant par la partie la plus faible jusqu'au plus fort. Format little endian.
Ex. Valeur : 0x12345678
+--------+--------+--------+--------+ | root | low | medium | high | | 8 Bits | 8 Bits | 8 Bits | 8 Bits | +--------+--------+--------+--------+ | 0x78 | 0x56 | Ox34 | Ox12 |
uint64 : Unsigned int 64 bits
On écrit le chiffre en commençant par la partie la plus faible jusqu'au plus fort. Format little endian.
Ex. Valeur : 0x0123456789ABCDEF
+--------+--------+--------+--------+--------+--------+--------+--------+ | low | low | medium | medium | medium | high | high | high | | 8 Bits | 8 Bits | 8 Bits | 8 Bits | 8 Bits | 8 Bits | 8 Bits | 8 Bits | +--------+--------+--------+--------+--------+--------+--------+--------+ | 0xEF | 0xCD | OxAB | Ox89 | Ox67 | Ox45 | Ox23 | Ox01 |
String : Chaîne de caractère
Une chaîne de caractères (on définit la taille, puis chaque caractère)
+------------+---------+--------+------------+--------+ | Taille : X | 1 char | 2 char | . . . | X char | | uint32 | 8 bits | 8 Bits | | 8 bits | +------------+---------+--------+------------+--------+ 1 2 X == Taille
String-UTF-8 : Chaîne de caractère
Une chaîne de caractères (on définit la taille, puis chaque caractère)
+------------+---------+--------+------------+--------+ | Taille : X | 1 char | 2 char | . . . | X char | | uint32 | 8 bits | 8 Bits | | 8 bits | +------------+---------+--------+------------+--------+ 1 2 X == Taille
String-UTF-16 : chaîne de caractère au format UTF-16
Une chaîne de caractères (on définit la taille, puis chaque caractère - ici un caractère tient sur 16 bits).
Ce format est utilisé sous Windows, nous préférons plus UTF-8 dans Godot, mais une simple conversion suffit pour passer d'un format à l'autre.
+------------+-------------+-------------+------------+-------------+ | Taille : X | char utf 16 | char utf 16 | . . . | char utf 16 | | uint32 | 16 bits | 16 Bits | | 16 bits | +------------+-------------+-------------+------------+-------------+ 1 2 X == Taille
Annexe : Exemple de communication
Exemple d'échange de message entre le serveur et client après l'authentification
+-------------------+--------------------------------+-------------- | Source -> Dest. | Message | Description +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/USER_CHARS | Liste tous les personnages du joueur + quelque info sur le système (liste des shard, ...) +-------------------+--------------------------------+-------------- | Client -> Serveur | CONNECTION/ASK_NAME | Demande si on nom est disponible +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/VALID_NAME | Réponse sur la validité du nom +-------------------+--------------------------------+-------------- | Client -> Serveur | CONNECTION/CREATE_CHAR | Création d'un personnage +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/USER_CHARS | Liste tous les personnages du joueur + quelque info sur le système (liste des shard, ...) +-------------------+--------------------------------+-------------- | Client -> Serveur | CONNECTION/SELECT_CHAR | Selection le personnage +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/USER_CHAR | Confirmation du personnage + renvoie des infos +-------------------+--------------------------------+-------------- | Client -> Serveur | CONNECTION/READY | Joueur confirme qu'il est pret +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/SHARD_ID | Information sur le shard connecté +-------------------+--------------------------------+-------------- | Serveur -> Client | TEAM/CONTACT_INIT | Liste les contacts du joueur +-------------------+--------------------------------+-------------- | Serveur -> Client | PHRASE/DOWNLOAD | +-------------------+--------------------------------+-------------- | Serveur -> Client | GUILD/UPDATE_PLAYER_TITLE | Mise a jour pour le joueur +-------------------+--------------------------------+-------------- | Client -> Serveur | BOTCHAT/SET_FILTERS | +-------------------+--------------------------------+-------------- | Client -> Serveur | POSITION (X, Y, Z, Heading) | Definition de la position actuelle du personnage +-------------------+--------------------------------+-------------- | Client -> Serveur | DEBUG/PING | Debug message with time +-------------------+--------------------------------+-------------- | Serveur -> Client | DEATH/RESPAWN_POINT | Position pour renaitre (apres une mort) +-------------------+--------------------------------+-------------- | Serveur -> Client | STRING/DYN_STRING | Envoie une message (ici on envoie uniquement un ID, si besoin on interroge le serveur pour avoir le détail du message) +-------------------+--------------------------------+-------------- | Serveur -> Client | PHRASE/DOWNLOAD | message +-------------------+--------------------------------+-------------- | Serveur -> Client | ENCYCLOPEDIA/INIT | +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_INIT/INV | Initialise l'inventaire +-------------------+--------------------------------+-------------- | Client -> Serveur | NPC_ICON/GET_DESC | +-------------------+--------------------------------+-------------- | Serveur -> Client | STRING_MANAGER/PHRASE_SEND | Envoie une série de message (toujours via l'ID) +-------------------+--------------------------------+-------------- | Serveur -> Client | USER/BARS | +-------------------+--------------------------------+-------------- | Serveur -> Client | NPC_ICON/SET_DESC | +-------------------+--------------------------------+-------------- | Serveur -> Client | VisualProperty/Slot | Description de l'environnement (autres joueurs/personnages +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_INIT/PLR/DatabaseXML | Initialise la base de donnée - ici on trouve toutes les descriptions de l'environnement (personnage, groupe, pack animal et peut-être plus) +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_GROUP/INIT_BANK | +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_UPD_PLR | +-------------------+--------------------------------+-------------- | Serveur -> Client | PVP_EFFECTS/PVP_FACTION_POINTS | +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_UPD_PLR/DatabaseXML | Mise à jour la base de donnée - ici on trouve toutes les descriptions de l'environnement (personnage, groupe, pack animal et peut-être plus) +-------------------+--------------------------------+--------------
Exemple d'échange de message texte envoyé par le serveur
Dans les faits, le serveur envoie des identifiant pour le message, si le client ne connais pas l'identifiant, il demande au serveur qui lui répond.
+——————-+——————————–+————–
| Source -> Dest. | Message | Description +-------------------+--------------------------------+-------------- | Serveur -> Client | STRING_MANAGER/PHRASE_SEND | Envoie d'une phase à afficher (pouvant contenir des paramètres à remplacer) - envoie une liste d'ID (chaque ID correspond à un identifiant présent dans un tableau) +-------------------+--------------------------------+-------------- | Client -> Serveur | STRING_MANAGER/STRING_RQ | Interrogation sur le contenu d'un identifiant +-------------------+--------------------------------+-------------- | Serveur -> Client | STRING_MANAGER/STRING_RESP | Retour du serveur pour une chaine de texte demande +-------------------+--------------------------------+--------------
Ex.:
Le serveur demande d'afficher le message 2264924217 (qui est la somme des messages 7705 avec comme paramètre 7849 + 7859) 7705 : &ZON&Vous êtes dans la région %s sur le continent %s. 7849 : Dispensaire 7859 : Terres Contrôlées Cela donne le message : &ZON&Vous êtes dans la région Dispensaire sur le continent Terres Contrôlées.
Annexe : Exemple de communication
Exemple d'échange de message entre le serveur et client après l'authentification
+-------------------+--------------------------------+-------------- | Source -> Dest. | Type de message | Description +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/USER_CHARS | Liste tous les personnages du joueur + quelque info sur le système (liste des shard, ...) +-------------------+--------------------------------+-------------- | Client -> Serveur | CONNECTION/ASK_NAME | Demande si on nom est disponible +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/VALID_NAME | Réponse sur la validité du nom +-------------------+--------------------------------+-------------- | Client -> Serveur | CONNECTION/CREATE_CHAR | Création d'un personnage +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/USER_CHARS | Liste tous les personnages du joueur + quelque info sur le système (liste des shard, ...) +-------------------+--------------------------------+-------------- | Client -> Serveur | CONNECTION/SELECT_CHAR | Selection le personnage +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/USER_CHAR | Confirmation du personnage + renvoie des infos +-------------------+--------------------------------+-------------- | Client -> Serveur | CONNECTION/READY | Joueur confirme qu'il est pret +-------------------+--------------------------------+-------------- | Serveur -> Client | CONNECTION/SHARD_ID | Information sur le shard connecté +-------------------+--------------------------------+-------------- | Serveur -> Client | TEAM/CONTACT_INIT | Liste les contacts du joueur +-------------------+--------------------------------+-------------- | Serveur -> Client | PHRASE/DOWNLOAD | +-------------------+--------------------------------+-------------- | Serveur -> Client | GUILD/UPDATE_PLAYER_TITLE | Mise a jour pour le joueur +-------------------+--------------------------------+-------------- | Client -> Serveur | BOTCHAT/SET_FILTERS | +-------------------+--------------------------------+-------------- | Client -> Serveur | POSITION (X, Y, Z, Heading) | Definition de la position actuelle du personnage +-------------------+--------------------------------+-------------- | Client -> Serveur | DEBUG/PING | Debug message with time +-------------------+--------------------------------+-------------- | Serveur -> Client | DEATH/RESPAWN_POINT | Position pour renaitre (apres une mort) +-------------------+--------------------------------+-------------- | Serveur -> Client | STRING/DYN_STRING | Envoie une message (ici on envoie uniquement un ID, si besoin on interroge le serveur pour avoir le détail du message) +-------------------+--------------------------------+-------------- | Serveur -> Client | PHRASE/DOWNLOAD | message +-------------------+--------------------------------+-------------- | Serveur -> Client | ENCYCLOPEDIA/INIT | +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_INIT/INV | Initialise l'inventaire +-------------------+--------------------------------+-------------- | Client -> Serveur | NPC_ICON/GET_DESC | +-------------------+--------------------------------+-------------- | Serveur -> Client | STRING_MANAGER/PHRASE_SEND | Envoie une série de message (toujours via l'ID) +-------------------+--------------------------------+-------------- | Serveur -> Client | USER/BARS | +-------------------+--------------------------------+-------------- | Serveur -> Client | NPC_ICON/SET_DESC | +-------------------+--------------------------------+-------------- | Serveur -> Client | VisualProperty/Slot | Description de l'environnement (autres joueurs/personnages +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_INIT/PLR/DatabaseXML | Initialise la base de donnée - ici on trouve toutes les descriptions de l'environnement (personnage, groupe, pack animal et peut-être plus) +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_GROUP/INIT_BANK | +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_UPD_PLR | +-------------------+--------------------------------+-------------- | Serveur -> Client | PVP_EFFECTS/PVP_FACTION_POINTS | +-------------------+--------------------------------+-------------- | Serveur -> Client | DB_UPD_PLR/DatabaseXML | Mise à jour la base de donnée - ici on trouve toutes les descriptions de l'environnement (personnage, groupe, pack animal et peut-être plus) +-------------------+--------------------------------+--------------
Exemple d'échange de message texte envoyé par le serveur
Dans les faits, le serveur envoie des identifiant pour le message, si le client ne connais pas l'identifiant, il demande au serveur qui lui répond.
+-------------------+--------------------------------+-------------- | Source -> Dest. | Type de message | Description +-------------------+--------------------------------+-------------- | Serveur -> Client | STRING_MANAGER/PHRASE_SEND | Envoie d'une phase à afficher (pouvant contenir des paramètres à remplacer) - envoie une liste d'ID (chaque ID correspond à un identifiant présent dans un tableau) +-------------------+--------------------------------+-------------- | Client -> Serveur | STRING_MANAGER/STRING_RQ | Interrogation sur le contenu d'un identifiant +-------------------+--------------------------------+-------------- | Serveur -> Client | STRING_MANAGER/STRING_RESP | Retour du serveur pour une chaine de texte demande +-------------------+--------------------------------+--------------
Ex.:
Le serveur demande d'afficher le message 2264924217 (qui est la somme des messages 7705 avec comme paramètres 7849 + 7859) 7705 : &ZON&Vous êtes dans la région %s sur le continent %s. 7849 : Dispensaire 7859 : Terres Contrôlées Cela donne le message : &ZON&Vous êtes dans la région Dispensaire sur le continent Terres Contrôlées.
Annexe : Analyse des structures
Structure du personnage
La structure du personnage est différente entre la création et l'envoie les caractèristiques vers les autres joueurs. Il en résulte une contrainte sur les paramètres pour faire coincidé les deux structures. Dans un premier cas, on retrouve toutes les propriétés sur un mutltiple de 8 bits, and le deuxième cas, on contracte les données en trois entier de 64 bits. (bref moins de place donc certain paramètres on pas la possibilité d'utiliser toutes les valeurs pouvant etre mise dans le premier cas. De plus quand on recharge les personnages, on retrouve la réduction appliqué.
- * Liste à puce
Structure création du personnage
Structure création du personnage
Type de message : CONNECTION/CREATE_CHAR
- (Uint8) Slot
- (Uint32) SheetId
- (Uint32) CSessionId
- (Uint32) name:size
- (UString) name:ustring (si la size > 0, attention, il y a des fortes contraintes uniquement [A-Za-z]+)
- (Uint8) People
- (Uint8) Sex
- (Uint8) NbPointFighter
- (Uint8) NbPointCaster
- (Uint8) NbPointCrafter
- (Uint8) NbPointHarvester
- (Sint32) StartPoint
- (Sint8) HairType
- (Sint8) HairColor
- (Sint8) GabaritHeight
- (Sint8) GabaritTorsoWidth
- (Sint8) GabaritArmsWidth
- (Sint8) GabaritLegsWidth
- (Sint8) GabaritBreastSize
- (Sint8) MorphTarget1
- (Sint8) MorphTarget2
- (Sint8) MorphTarget3
- (Sint8) MorphTarget4
- (Sint8) MorphTarget5
- (Sint8) MorphTarget6
- (Sint8) MorphTarget7
- (Sint8) MorphTarget8
- (Sint8) EyesColor
- (Sint8) Tattoo
- (Sint8) JacketColor
- (Sint8) TrousersColor
- (Sint8) HatColor
- (Sint8) ArmsColor
- (Sint8) HandsColor
- (Sint8) FeetColor
Source :
khanat-opennel-code/code/ryzom/common/src/game_share/msg_client_server.h:114 class CCreateCharMsg khanat-opennel-code/code/ryzom/common/src/game_share/character_summary.h:35 struct CCharacterSummary khanat-opennel-code/code/ryzom/server/src/pd_support_service/stat_character.h:844 CStatsScanCharacter()
Structure reception pour les autres joueurs
Ils existent plusieurs messages qui receptionne les caractèristiques.
- Les messages du type CONNECTION/USER_CHARS (qui les tous les personnages du joueur)
- Utilise la structure VPA, VPB & VPC
- Les messages du type VisualProperty (on réception les caractèristiques des différentes entités [personnages d'autres joueurs, annimaux, …])
- Utilise la structure VPA & VPC (pas la VPB - étrange)
Structure VisualPropA
VPA: VisualPropA est de type Uint64 (unsigned int 64 bits).
[Source: khanat-opennel-code/code/ryzom/common/src/game_share/player_visual_properties.h:32 struct SPropVisualA]
- Sex : 1 bit/booléan (max 1)
- JacketModel : 8 bits (max: 255)
- JacketColor : 3 bits (max: 7)
- TrouserModel : 8 bits (max 255)
- TrouserColor : 3 bits (max: 7)
- WeaponRightHand : 10 bits (max: 1023)
- WeaponLeftHand : 8 bits (max: 255)
- ArmModel : 8 bits (max: 255)
- ArmColor : 3 bits (max: 7)
- HatModel : 9 bits (max: 511)
- HatColor : 3 bits (max: 7)
Remarque concernant le sexe : On peut noter qu'il n'y a que 2 possibilités pour le sexe, mâle ou femelle. (on peut dire adieu aux autres formes de reproduction (asexuée, clonnage, changement de sexe suivant l'alimentation/age/température/…)
Structure VisualPropB
VPB: VisualPropB est de type Uint64 (unsigned int 64 bits).
[Source: khanat-opennel-code/code/ryzom/common/src/game_share/player_visual_properties.h:105 struct SPropVisualB]
- Name : 16 bits (max: 65535) → ID qui permettrant de retrouver dans la liste des mots stockés
- HandsModel : 9 bits (max: 511)
- HandsColor : 3 bits (max: 7)
- FeetModel : 9 bits (max: 511)
- FeetColor : 3 bits (max: 7)
- RTrail : 4 bits (max: 15)
- LTrail : 3 bits (max: 7)
- Reste : 17 bits non utilisée
Structure VisualPropC
VPC: VisualPropC est de type Uint64 (unsigned int 64 bits).
[Source: khanat-opennel-code/code/ryzom/common/src/game_share/player_visual_properties.h:163 struct SPropVisualC]
- MorphTarget1 : 3 bits (max: 7)
- MorphTarget2 : 3 bits (max: 7)
- MorphTarget3 : 3 bits (max: 7)
- MorphTarget4 : 3 bits (max: 7)
- MorphTarget5 : 3 bits (max: 7)
- MorphTarget6 : 3 bits (max: 7)
- MorphTarget7 : 3 bits (max: 7)
- MorphTarget8 : 3 bits (max: 7)
- EyesColor : 3 bits (max: 7)
- Tattoo : 7 bits (max: 127)
- CharacterHeight : 4 bits (max: 15)
- TorsoWidth : 4 bits (max: 15)
- ArmsWidth : 4 bits (max: 15)
- LegsWidth : 4 bits (max: 15)
- BreastSize : 4 bits (max: 15)
- Reste : 10 bits non utilisée
http
, oui c'est possible suivant la configuration du serveur, mais je ne conseille pas. Préférez https
afin d'avoir des connexion chiffrées et de réduire la surface des attaques.http
, même commentaire qu'au dessus : ne pas utiliser http sauf cas particulier.