Bonjour, tout d'abord je tiens à remercier skeezr qui m'a beaucoup aidé dans l’implantation de solution fonctionnel au sein de son émulateur (past). En effet, après plusieurs test j'ai été en mesure (grâce à skeezr) d'implanter le GameServer dans Past.
Pour information, la version 0.0.0.1 de Past, partagé sur le GitHub n'intègre que le LoginServer, qui ne permet la connexion qu'au serveur.
Afin d'entrer dans le jeu (world) il faut intégré le GameServer, c'est-à-dire le serveur qui permet de gérer les paquets relatif au world.
Concernant le Login, il est déjà géré par Past dans Past/Network/Login/LoginClient.cs à savoir :
- A la connexion d'un client en socket (Login_OnClientSocketConnected) on envoi 2 paquet : le ProtocolRequired et le HelloConnectMessage.
- Lorsque l'on reçoit le paquet Id 4 (IdentificationMessage) on envoi l' "IdentificationSuccessMessage" permettant la connexion au serveur (avec les login et pswd rentrée dans le Client, tous les login fonctionne puisque l'émulateur ne gère pas encore les bases de données mysql). Puis on envoi le paquet ServersListMessage permettant d'afficher la liste des serveurs en fonction de la communauté défini dans l' "IdentificationSuccessMessage".
- Lorsque l'on reçoit le paquet Id 40 (ServerSelectionMessage) qui correspond à la sélection d'un serveur (double clic sur un serveur dans le Client) on envoi le "SelectedServerDataMessage" qui contiens les informations relative au serveur séléctionné sur le client avec le ticket, l'ID du serveur, le port du serveur etc... Puis on ferme le LoginServer, la suite du processus se passant dans le GameClient.
Le processus illustré dans la console :
LoginServerProcess
Cliquez pour révéler
Cliquez pour masquer
Loading Image
Voilà donc ce qui se passe lorsque dans l'ordre je : me connecte avec des identifiant sur le client, double-clic sur un serveur de jeu.
Le LoginClient est donc 100% fonctionnel et gère toute les connexions (voir le code) : plusieurs personnes peuvent se connecter simultanément sur le serveur (cf. illustration).
La connexion au serveur étant parfaitement géré, nous n'avons plus besoin de revenir dessus, tout se passera dorénavant dans le GameServer et le GameClient.
Le GameServer est une copie conforme du LoginServer et se trouve dans Past/Network/Game/GameServer.cs, vous trouverez son code ici
Passons au GameClient, là où les paquets vont être envoyé et reçu. Voici mon code :
GameClient.cs
Cliquez pour révéler
Cliquez pour masquer
using Past.Protocol;
using Past.Protocol.IO;
using Past.Protocol.Messages;
using Past.Protocol.Types;
using Past.Utils;
using System;
using System.Net.Sockets;
using Past.Network;
namespace Past.Network.Login
{
public class GameClient
{
private Client Game { get; set; }
public GameClient(Client client)
{
Game = client;
Game_OnClientSocketConnected();
Game.OnClientSocketClosed += Login_OnClientSocketClosed;
Game.OnClientReceivedData += Game_OnClientReceivedData;
}
private void Game_OnClientSocketConnected() // Quand quelqu'un se connecte au Game
{
Send(new HelloGameMessage());
}
private void Login_OnClientSocketClosed()
{
Past.Network.Game.GameServer.Clients.Remove(this);
Game.Close();
ConsoleUtils.Write(ConsoleUtils.type.INFO, "Client disconnected from GameServer ...");
}
private void Game_OnClientReceivedData(byte[] data)
{
{
int Header;
int Id;
int Size = 0;
using (BigEndianReader reader = new BigEndianReader(data))
{
Header = reader.ReadShort(); //header
Id = Header >> 2;
var typeLen = Functions.ComputeTypeLen((uint)data.Length);
if (typeLen == 1)
{
Size = reader.ReadByte();
}
else if (typeLen == 2)
{
Size = reader.ReadShort();
}
else if (typeLen == 3)
{
Size = ((reader.ReadByte() & 255) >> 16) + ((reader.ReadByte() & 255) >> 8) + (reader.ReadByte() & 255);
}
while (data.Length - 3 <= Size)
{
ConsoleUtils.Write(ConsoleUtils.type.RECEIV, "Receiv Packet ID {0} Header {1} {2}", Id, Header, Size);
if (Id == 110) //AuthenticationTicketMessage
{
Send(new AuthenticationTicketAcceptedMessage());
}
EntityLook look = new EntityLook(1, new short[] { 10 }, new int[0], new short[] { 125 }, new SubEntity[0]);
if (Id == 150) //CharacterListRequestMessage
{
Send(new CharactersListMessage(false, false, new CharacterBaseInformations[] { new CharacterBaseInformations(1, "Unknown", 1, look, 1, false)}));
}
if (Id == 152) //
{
Send(new CharacterSelectedSuccessMessage(new CharacterBaseInformations(1, "Unknown", 1, look, 1, false)));
}
if (data.Length - 3 == Size)
{
ConsoleUtils.Write(ConsoleUtils.type.RECEIV, "{0} ...", Functions.ByteArrayToString(data));
return;
}
}
}
}
}
public void Send(NetworkMessage message)
{
try
{
using (BigEndianWriter writer = new BigEndianWriter())
{
message.Pack(writer);
Game.Send(writer.Data);
}
ConsoleUtils.Write(ConsoleUtils.type.SEND, "{0} to client {1}:{2} ...", message.ToString(), Game.Ip, Game.Port);
}
catch (Exception ex)
{
ConsoleUtils.Write(ConsoleUtils.type.ERROR, ex.ToString());
}
}
}
}
Ce que fais ce code :
- A la connexion d'un client en socket (Game_OnClientSocketConnected), on envoi le HelloConnectMessage conformément à tout processus de connexion
- Lorsque l'on reçois le paquet Id 110 (AuthenticationTicketMessage), on envoi le AuthenticationTicketAcceptedMessage
Et c'est à cet endroit que ça merde. En effet, lorsque j'arrive à ce stade, le serveur me kick et j'obtient une erreur "connexion interrompu" sur mon client comme ci-dessous :
GameClientResult
Cliquez pour révéler
Cliquez pour masquer
Loading Image
Donc je reçois le paquet Id 110 (AuthenticationTicketMessage), j'envoi le AuthenticationTicketAcceptedMessage puis je re-reçois le paquet Id 110 et à ce moment là je suis kick du GameServer.
Donc la j'ai plusieurs théories :
1 - Je renvoi pas le bon paquet
2 - Je renvoi pas les bons arguments avec le bon paquet
3 - Probleme dans le protocole de l'émulateur ( AuthenticationTicketAcceptedMessage vide)
Merci de votre aide !
Unknown.