Envoyer des packets pendant l'handshaking



  • Bonjour,

    Je tente actuellement de faire, qu'à la connexion au serveur, celui-ci envoi des données au client. Le joueur distant devra alors choisir un profile (les données représentant les profiles disponibles), puis envoyer la réponse au serveur.

    Rien de bien compliqué jusque-là, sauf que le serveur doit attendre que le client réponde, et ne pas le faire déjà apparaître dans le monde. J'utilise l'évènement PlayerLoggedIn, qui envoie les données au client. Le problème est que cette même fonction va déconnecter le joueur après, car il n'a pas encore envoyé la réponse, et je ne peux pas faire attendre le serveur pour une réponse qui ne pourra jamais arrivée.
    Le code actuel est (il marche si je remplace les données qui sont censées êtres échangées par des données statiques) :

    	@SubscribeEvent
    	public static void onPlayerLoggin(PlayerEvent.PlayerLoggedInEvent e) {
    		// si le joueur a déjà la capability de créée
    		if(e.player.hasCapability(PlayerProfileCapatibilityProvider.CAPATIBILITY, null)) {
    			// on la récupère
    			final IPlayerProfileCapability c = e.player.getCapability(PlayerProfileCapatibilityProvider.CAPATIBILITY, null);
    			
    			// puis on envoie les profiles au client
    			networkWrapper.sendTo(new ProfilesListPacket(c.profiles()), (EntityPlayerMP) e.player);
    		}
    		else
    			// sinon on envoie une liste vide
    			networkWrapper.sendTo(new ProfilesListPacket(), (EntityPlayerMP) e.player);
    		
    		// on récupère la capability
    		final IPlayerProfileCapability c = e.player.getCapability(PlayerProfileCapatibilityProvider.CAPATIBILITY, null);
    		if(c.activeProfile() == null)
    			// si nous n'avons pas de profile actif, nous déconnectons le joueur
    			((EntityPlayerMP) e.player).connection.disconnect(new TextComponentTranslation("multiplayer.disconnect.profileNull"));
    	}
    

    Le client reçoit bien les données, mais le serveur le déconnecte juste après. J'ai tenté avec l'évènement FMLNetworkEvent.ClientConnectedToServerEvent, mais je ne peux pas utiliser mon networkWrapper car la connexion moddé n'est pas encore établie. Je pense qu'il faudrait passer par la connexion avec FML, mais je ne sais pas comment faire.

    Pour résumé, je souhaite faire un handshake entre le client et le serveur avant que le joueur ait apparu dans le monde.
    Le mod devra permettre de switch sur des profils pour avoir différents inventaires/permissions sur un même compte, par exemple pour switcher entre un compte admin et un compte joueur.

    Merci d'avoir pris le temps pour me lire,
    Cordialement,
    ShE3py.



  • A mon avis, tu devrais connecter le joueur sur un monde d'attente le temps qu'il choisis son profil



  • J'y ai pensé, mais pour moi ce n'est pas très propre. Il faudrait créer un monde, empêcher les joueurs de se déplacer et de tout détruire, les rendre invisibles entre eux...

    L'évènement ClientConnectedToServerEvent permet de récupérer le NetworkManager et l'handler, et la classe mère contient un évènement CustomPacketEvent, n'y aurait-il pas moyen d'enregistrer un packet des deux côtés et de les traités ?



  • Bon au final après avoir regardé du côté des packets du FML j'ai trouvé un truc assez simple pour envoyer des packets avant que le joueur soit entièrement connecté, il m'a suffit d'enregistrer de nouveaux packets.

    J’envoie la classe (imparfaite) pour ceux à qui ça pourrait intéresser :

    package fr.she3py.mods.zeptopia.network;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Map;
    
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelFutureListener;
    import net.minecraft.client.network.NetHandlerPlayClient;
    import net.minecraft.network.EnumConnectionState;
    import net.minecraft.network.EnumPacketDirection;
    import net.minecraft.network.INetHandler;
    import net.minecraft.network.NetHandlerPlayServer;
    import net.minecraft.network.Packet;
    import net.minecraftforge.fml.common.network.FMLNetworkEvent;
    import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
    import net.minecraftforge.fml.relauncher.ReflectionHelper;
    import net.minecraftforge.fml.relauncher.ReflectionHelper.UnableToFindMethodException;
    
    public class DirectNetworkWrapper {
    	private static final Method REGISTERER;
    	
    	static {
    		try {
    			REGISTERER = ReflectionHelper.findMethod(EnumConnectionState.class, "registerPacket", "a", EnumPacketDirection.class, Class.class);
    			REGISTERER.setAccessible(true);
    		}
    		catch(UnableToFindMethodException | SecurityException e) {
    			throw new RuntimeException("Unable to retrieve the method to register packets", e);
    		}
    	}
    	
    	public static void registerPacket(final EnumPacketDirection direction, final Class <? extends Packet<? >> packetClass) {
    		try {
    			REGISTERER.invoke(EnumConnectionState.PLAY, direction, packetClass);
    		} catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
    			throw new RuntimeException("Unable to register a packet", e);
    		}
    	}
    	
    	public static void sendThrough(final Channel channel, final Packet<?> packet) {
    		channel.writeAndFlush(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
    	}
    	
    	public static <E extends FMLNetworkEvent<? extends INetHandler>> void sendToRemote(final E event, final Packet<?> packet) {
    		sendThrough(event.getManager().channel(), packet);
    	}
    	
    	public static <H extends INetHandler> void sendToRemote(final H handler, final Packet<?> packet) {
    		if(handler instanceof NetHandlerPlayServer)
    			sendThrough(((NetHandlerPlayServer) handler).netManager.channel(), packet);
    		else
    			sendThrough(((NetHandlerPlayClient) handler).getNetworkManager().channel(), packet);
    	}
    }
    

    Il faut juste enregistrer les packets customs dans le CommonProxy via registerPacket(direction, packetClass), puis dans un évènement FMLNetworkEvent faire sendToRemote(event, packet), ou sendToRemote(handler, packet) dans un packet.

    Cordialement,
    ShE3py.


Log in to reply