Erreur de synchronisation Client/Serveur



  • Bonjour à vous !

    Je viens vers vous pour un problème concernant les capabilities et les packets.

    L'idée est simple, j'ai ma capability pour la Mana et je souhaite synchroniser les informations du joueur entre le client et le serveur.

    En gros il faut que chaque joueur débute avec 0 de Mana et que ce chiffre soit différents selon les joueurs et leurs niveau de mana... logique, me direz vous.

    Seulement quand je défini 50 de mana au Player80 par exemple.
    Que je sauvegarde le monde que je quitte le jeu et que je relance le jeu avec un nouveau joueur, le Player54 par exemple et bien celui-ci se retrouve avec le niveau de mana du Player80.

    Alors y'a forcément un problème avec mon code, et je vous le partage ci-dessous.

    IMana.java
    :::

    /**
     * Mana storage capability
     */
    public interface IMana {
    	public void consume(float points);
    
    	public void regen(float points);
    
    	public void set(float points);
    
    	public float getMana();
    
    	public void synchronize();
    
    }
    

    :::

    Mana.java
    :::

    /**
     * Default implementation of IMana
     */
    public class Mana implements IMana {
    	public float mana = 0F;
    	public float max_mana = 250.0F;
    
    	private EntityPlayer player;
    
    	public Mana() {}
    
    	public Mana(EntityPlayer playerIn) {
    		this.player = playerIn;
    	}
    
    	@Override
    	public void consume(float points) {
    		this.mana -= points;
    
    		if (this.mana < 0.0F)
    			this.mana = 0.0F;
    		this.synchronize();
    	}
    
    	@Override
    	public void regen(float points) {
    		if (this.mana <= this.max_mana)
    			this.mana += points;
    
    		this.synchronize();
    	}
    
    	@Override
    	public void set(float points) {
    		this.mana = points;
    		this.synchronize();
    	}
    
    	@Override
    	public float getMana() {
    		return this.mana;
    	}
    
    	@Override
    	public void synchronize() {
    		if (this.player != null && !this.player.getEntityWorld().isRemote)
    			Caminelot.getNetwork().sendTo(new PacketMana(this.getMana()), (EntityPlayerMP) this.player);
    	}
    }
    

    :::

    ManaProvider.java
    :::

    
    /**
     * Mana provider
     */
    public class ManaProvider implements ICapabilitySerializable<NBTBase> {
    	@CapabilityInject(IMana.class)
    	public static final Capability<IMana> MANA_CAP = null;
    
    	private IMana instance = MANA_CAP.getDefaultInstance();
    
    	/**
    	 * Format mana value.
    	 *
    	 * @param manaIn
    	 *            The mana value
    	 * @return The formatted text.
    	 */
    	public static String formatMana(final float manaIn) {
    		return ItemStack.DECIMALFORMAT.format(manaIn);
    	}
    
    	@Override
    	public boolean hasCapability(Capability<?> capability, EnumFacing facing) {
    		return capability == MANA_CAP;
    	}
    
    	@Override
    	public <T> T getCapability(Capability<T> capability, EnumFacing facing) {
    		return capability == MANA_CAP ? MANA_CAP.<T>cast(this.instance) : null;
    	}
    
    	@Override
    	public NBTBase serializeNBT() {
    		return MANA_CAP.getStorage().writeNBT(MANA_CAP, this.instance, null);
    	}
    
    	@Override
    	public void deserializeNBT(NBTBase nbt) {
    		MANA_CAP.getStorage().readNBT(MANA_CAP, this.instance, null, nbt);
    	}
    }
    

    :::

    ManaStorage.java
    :::

    /**
     * This class is responsible for saving and reading mana data from or to server
     */
    public class ManaStorage implements IStorage<IMana> {
    	@Override
    	public NBTBase writeNBT(Capability<IMana> capability, IMana instance, EnumFacing side) {
    		return new NBTTagFloat(instance.getMana());
    	}
    
    	@Override
    	public void readNBT(Capability<IMana> capability, IMana instance, EnumFacing side, NBTBase nbt) {
    		instance.set(((NBTTagFloat) nbt).getFloat());
    	}
    }
    

    :::

    EventHandler
    :::

    public class EventHandler
    {
        @SubscribeEvent
        public void onPlayerLogsIn(PlayerLoggedInEvent event)
        {
            EntityPlayer player = event.player;
            IMana mana = player.getCapability(ManaProvider.MANA_CAP, null);
    
            String message = String.format("Hello there, you have §7%d§r mana left.", (int) mana.getMana());
            player.addChatMessage(new TextComponentString(message));
        }
    
        @SubscribeEvent
        public void onPlayerSleep(PlayerSleepInBedEvent event)
        {
            EntityPlayer player = event.getEntityPlayer();
    
            if (player.worldObj.isRemote) return;
    
            IMana mana = player.getCapability(ManaProvider.MANA_CAP, null);
    
            mana.fill(50);
    
            String message = String.format("You refreshed yourself in the bed. You received 50 mana, you have §7%d§r mana left.", (int) mana.getMana());
            player.addChatMessage(new TextComponentString(message));
        }
    
        @SubscribeEvent
        public void onPlayerFalls(LivingFallEvent event)
        {
            Entity entity = event.getEntity();
    
            if (entity.worldObj.isRemote || !(entity instanceof EntityPlayerMP) || event.getDistance() < 3) return;
    
            EntityPlayer player = (EntityPlayer) entity;
            IMana mana = player.getCapability(ManaProvider.MANA_CAP, null);
    
            float points = mana.getMana();
            float cost = event.getDistance() * 3;
    
            if (points > cost)
            {
                mana.consume(cost);
    
                String message = String.format("You absorbed fall damage. It costed §7%d§r mana, you have §7%d§r mana left.", (int) cost, (int) mana.getMana());
                player.addChatMessage(new TextComponentString(message));
    
                event.setCanceled(true);
            }
        }
    
        /**
         * Copy data from dead player to the new player
         */
        @SubscribeEvent
        public void onPlayerClone(PlayerEvent.Clone event)
        {
            EntityPlayer player = event.getEntityPlayer();
            IMana mana = player.getCapability(ManaProvider.MANA_CAP, null);
            IMana oldMana = event.getOriginal().getCapability(ManaProvider.MANA_CAP, null);
    
            mana.set(oldMana.getMana());
        }
    }
    

    :::

    PacketMana.java
    :::

    public class PacketMana implements IMessage {
    
    	private float value;
    
    	public PacketMana() {}
    
    	public PacketMana(float valueIn) {
    		this.value = valueIn;
    	}
    
    	@Override
    	public void fromBytes(ByteBuf buf) {
    		value = buf.readFloat();
    	}
    
    	@Override
    	public void toBytes(ByteBuf buf) {
    		buf.writeFloat(value);
    	}
    
    	public static class Handler implements IMessageHandler<PacketMana, IMessage> {
    
    		@Override
    		public IMessage onMessage(PacketMana message, MessageContext ctx) {
    			Caminelot.getProxy().getThreadListener(ctx).addScheduledTask(() -> {
    				final EntityPlayer player = Caminelot.getProxy().getPlayer(ctx);
    				IMana mana = player.getCapability(ManaProvider.MANA_CAP, null);
    				mana.set(message.value);
    			});
    			return null;
    		}
    	}
    }
    

    :::

    Enregistrement de mes packets
    :::

    registerMessage(PacketMana.Handler.class, PacketMana.class, Side.CLIENT, 0);
    registerMessage(PacketMana.Handler.class, PacketMana.class, Side.SERVER, 1);
    

    :::

    Enregistrement de ma capability

    :::

    /**
     * Capability handler
     * 
     * This class is responsible for attaching our capabilities
     */
    @EventBusSubscriber(modid = Caminelot.MODID)
    public class CapabilityHandler {
    	public static final ResourceLocation MANA_CAP = new ResourceLocation(Caminelot.MODID, "mana");
    
    	@SubscribeEvent
    	public static void attachCapability(AttachCapabilitiesEvent<Entity> event) {
    		if (!(event.getObject() instanceof EntityPlayer))
    			return;
    
    		event.addCapability(MANA_CAP, new ManaProvider());
    	}
    
    }
    

    :::



  • Bonjour,
    Déjà, il y a un problème de sécurité avec ton Packet car le player peut envoyer au serveur la quantité de mana qu'il veux et le serveur va l'accepter. Le packet n'a que besoin d'aller du serveur vers le client.
    Ensuite, tu n'as pas mis l'event où tu attache ta capability au joueur. C'est peut-être là qu'il y a un problème.



  • Effectivement, j'ai rajouté le code manquant.
    Mais je pense pas que cela vienne de là.


  • Rédacteurs

    Et oui ! De la même manière que tu as le stuff de Player80, car c'est un monde solo 😉



  • Du coup ce qui veut dire que le code est correct ?



  • Lance un serveur et deux clients pour test, tout simplements



  • Comme il l'a été dit plus haut, tu rencontre ce problème a cause d'un monde solo.
    Cependant, il se peut que tu rencontre un second problème lors de la synchronisation des packets après la mort de ton joueur/changement de dimension.

    D'apres ce que j'ai pu remarquer, les packets n'ont pas l'air de s'envoyer correctement lors du PlayerEvent.Clone, dans ce cas, délaye les de 1 ou 2 ticks avant de l'envoyer.



  • Bien, effectivement après tests sur serveur cela semble fonctionner.

    Autre petit point.

    Dans la lignée de ma Mana ici présente de défini un nouveau "pseudo" rp au joueur donc je stocke ma capability, jusque là tout va bien, mais j'ai aussi modifier le système de rendu du joueur, mais impossible d'afficher la valeur de la capability (le pseudo rp donc), tout passe par packets je suppose, mais impossible de faire en sorte que le joueur A voit le pseudo RP du joueur B.

    Une idée ?

    Pour l'exemple prenons ma Mana actuelle, j'ai juste fait un c/c sauf que c'est un String et non un Float ^^



  • @ama a dit dans Erreur de synchronisation Client/Serveur :

    Bien, effectivement après tests sur serveur cela semble fonctionner.

    Autre petit point.

    Dans la lignée de ma Mana ici présente de défini un nouveau "pseudo" rp au joueur donc je stocke ma capability, jusque là tout va bien, mais j'ai aussi modifier le système de rendu du joueur, mais impossible d'afficher la valeur de la capability (le pseudo rp donc), tout passe par packets je suppose, mais impossible de faire en sorte que le joueur A voit le pseudo RP du joueur B.

    Une idée ?

    Pour l'exemple prenons ma Mana actuelle, j'ai juste fait un c/c sauf que c'est un String et non un Float ^^

    Tu veut dire stocker un String dans un packet ? tu peut faire

    @Override
    public void fromBytes(ByteBuf buf) {
    	this.type = ByteBufUtils.readUTF8String(buf);
    }
    
    @Override
    public void toBytes(ByteBuf buf) {
    	ByteBufUtils.writeUTF8String(buf, this.type);
    }

  • Rédacteurs

    Au lieu de synchroniser les données seulement avec le joueur possédant la capability (comme c'est le cas pour la mana), il te faut envoyer à tous les clients cette donnée



  • Cette ressource pourrait potentiellement t'etre utile, bien qu'elle n'utilise pas les capabilities
    https://github.com/Tschipp/fakename



  • Très bien, j'ai essayé donc de send le packet à tout le monde
    via un sendToAll seulement, j'ai se problème lors de l'affichage.

    text alternatif

    On voit ici que je défini le nom "LEL "au Player614, mais le "LEL" j'affiche à tous les clients mais pas à celui qui a cette valeur.
    Le test à été réalisé en serveur et non pas sur un monde local, ou en solo bien évidemment.

    Voilà mon code.

    Code de rendu du pseudo.
    :::

    @SideOnly(Side.CLIENT)
    	@SubscribeEvent
    	public static void onPrePlayerRender(RenderPlayerEvent.Pre event) {
    		EntityPlayer player = event.getEntityPlayer();
    		RenderManager renderManager = event.getRenderer().getRenderManager();
    		double d0 = player.getDistanceSq(renderManager.renderViewEntity);
    		double maxDistance = 64D;
    
    		IRPName rpName = player.getCapability(RPNameProvider.RPNAME_CAP, null);
    
    		if (d0 <= (maxDistance * maxDistance)) {
    			boolean flag = player.isSneaking();
    			float f = renderManager.playerViewY;
    			float f1 = renderManager.playerViewX;
    			boolean flag1 = renderManager.options.thirdPersonView == 2;
    			float f2 = player.height + 0.5F - (flag ? 0.25F : 0.0F);
    			EntityRenderer.drawNameplate(renderManager.getFontRenderer(), rpName.getName(), (float) event.getX(), (float) event.getY() + f2 + 0.25F, (float) event.getZ(), 0, f, f1, flag1, flag);
    		}
    	}
    

    :::

    PacketRPName.java
    :::

    public class PacketRPName implements IMessage {
    
    	private String name;
    	private int playerID;
    
    	public PacketRPName() {}
    
    	public PacketRPName(EntityPlayer playerIn, String valueIn) {
    		this.name = valueIn;
    		this.playerID = playerIn.getEntityId();
    	}
    
    	@Override
    	public void fromBytes(ByteBuf buf) {
    		this.name = ByteBufUtils.readUTF8String(buf);
    		this.playerID = buf.readInt();
    	}
    
    	@Override
    	public void toBytes(ByteBuf buf) {
    		ByteBufUtils.writeUTF8String(buf, this.name);
    		buf.writeInt(this.playerID);
    	}
    
    	public static class Handler implements IMessageHandler<PacketRPName, IMessage> {
    
    		@Override
    		public IMessage onMessage(PacketRPName message, MessageContext ctx) {
    			Caminelot.getProxy().getThreadListener(ctx).addScheduledTask(() -> {
    				final EntityPlayer player = Caminelot.getProxy().getPlayer(ctx);
    				IRPName rpName = player.getCapability(RPNameProvider.RPNAME_CAP, null);
    				rpName.set(player, message.playerID, message.name);
    			});
    			return null;
    		}
    	}
    }
    

    :::

    RPNameCommande.java (la commande qui assigne le nom RP)
    :::

    
    public class RPNameCommand extends CommandBase {
    	public RPNameCommand() {
    
    	}
    
    	@Override
    	public String getName() {
    		return "rpname";
    	}
    
    	@Override
    	public String getUsage(ICommandSender sender) {
    		return "/rpname <assign|remove> <player> <title>";
    	}
    
    	@Override
    	public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException {
    		if (args.length >= 2 && args[0].length() > 0 && args[1].length() > 0) {
    			if (args[0].equalsIgnoreCase("assign") && args.length >= 3) {
    				if (args[2].length() > 0) {
    					// NAKConfig.addTitles(args[1], args[2]);
    					EntityPlayer player;
    					player = getPlayer(server, sender, args[1]);
    					IRPName rpname = player.getCapability(RPNameProvider.RPNAME_CAP, null);
    					String title = "";
    					for (int i = 2; i < args.length; i++) {
    						title = title + args[i];
    					}
    					rpname.set(player, player.getEntityId(), title);
    					rpname.synchronize();
    					TextComponentTranslation textComponantTranslation1 = new TextComponentTranslation("Title " + title + " assigned to " + args[1] + " with success!", "");
    					textComponantTranslation1.getStyle().setColor(TextFormatting.GREEN);
    					sender.sendMessage(textComponantTranslation1);
    				} else {
    					wrongUsage(sender);
    				}
    			} else if (args[0].equalsIgnoreCase("remove")) {
    				EntityPlayer player;
    				player = getPlayer(server, sender, args[1]);
    				IRPName rpname = player.getCapability(RPNameProvider.RPNAME_CAP, null);
    				String title = "undefined";
    				rpname.set(player, player.getEntityId(), title);
    				rpname.synchronize();
    				TextComponentTranslation textComponantTranslation1 = new TextComponentTranslation("Title removed to " + args[1] + " with success!", "");
    				textComponantTranslation1.getStyle().setColor(TextFormatting.GREEN);
    				sender.sendMessage(textComponantTranslation1);
    			} else {
    				wrongUsage(sender);
    			}
    		} else {
    			wrongUsage(sender);
    		}
    	}
    
    	private static void wrongUsage(ICommandSender sender) {
    		TextComponentTranslation textComponantTranslation1 = new TextComponentTranslation("Error, usage : /rpname <assign|remove> <player> <rpname>", "");
    		textComponantTranslation1.getStyle().setColor(TextFormatting.RED);
    		sender.sendMessage(textComponantTranslation1);
    	}
    
    	public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) {
    		if (args.length == 1) {
    			return getListOfStringsMatchingLastWord(args, new String[] { "assign", "remove" });
    		} else if (args.length == 2) {
    			return getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames());
    		} else {
    			return Collections.<String>emptyList();
    		}
    	}
    
    }
    

    :::

    RPname.java qui implémente IRPName
    :::

    /**
     * Default implementation of IRPName
     */
    public class RPName implements IRPName {
    	public String name = "undefined";
    	private int playerID;
    	private EntityPlayer player;
    
    	public RPName() {}
    
    	@Override
    	public void set(EntityPlayer playerIn, int playerIDIn, String nameIn) {
    		this.player = playerIn;
    		this.playerID = playerIDIn;
    		this.name = nameIn;
    		this.synchronize();
    	}
    
    	@Override
    	public String getName() {
    		return this.name;
    	}
    
    	@Override
    	public void synchronize() {
    		if (this.player != null && !this.player.getEntityWorld().isRemote)
    			Caminelot.getNetwork().sendToAll(new PacketRPName(this.player, this.getName()));
    	}
    }
    

    :::

    Je pense que mon erreur viens du rendu, ou de la commande.
    Seulement j'ai vraiment du mal avec cette notion de packets et de quelles informations doivent transiter ou non.
    Et encore plus pour récupérer la dite information apparemment.

    Encore merci de votre aide !



  • Petit up ^^'



  • Le probleme vient de ton packet handler.

    La capability a beau etre attachée a tous les joueurs, lors de ton Caminelot.getProxy().getPlayer(ctx), tu n'obtiens que le joueur du client. Cependant, la capability doit etre présente chez tous les joueurs.

    Essaie d'envoyer les données de tous les joueurs a tous les joueurs (c'est tres moche, mais en attendant que tu vois l'effet voulu, ca suffira), en envoyant chaun des pseudos RP a tous les joueurs et en le donnant au joueur concerné.

    Petit conseil, utilise getEntityId(); pour identifier une entité dans le monde (les ids sont le memes client/serveur)


  • Rédacteurs



  • J'ai pas accès à ton repo @BrokenSwing 😢


  • Rédacteurs

    Pas grave, en gros, tu dois utiliser sendToAllTracking plutôt que sendToAll pour envoyer ton paquet.



  • Toujours pareil, ça change rien.

    Dois-je vous renvoyer mon code ?


  • Rédacteurs

    Vas-y, renvoie le tout 😉



  • Bon,

    Je suis pas bon dans ce foutoir de packets et de capability.

    Voilà les classes qui t'interesse.

    La Commande
    :::

    
    public class RPNameCommand extends CommandBase {
    	public RPNameCommand() {
    
    	}
    
    	@Override
    	public String getName() {
    		return "rpname";
    	}
    
    	@Override
    	public String getUsage(ICommandSender sender) {
    		return "/rpname <assign|remove> <player> <title>";
    	}
    
    	@Override
    	public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException {
    		if (args.length >= 2 && args[0].length() > 0 && args[1].length() > 0) {
    			if (args[0].equalsIgnoreCase("assign") && args.length >= 3) {
    				if (args[2].length() > 0) {
    					// NAKConfig.addTitles(args[1], args[2]);
    					EntityPlayer player;
    					player = getPlayer(server, sender, args[1]);
    					IRPName rpname = player.getCapability(RPNameProvider.RPNAME_CAP, null);
    					String title = "";
    					for (int i = 2; i < args.length; i++) {
    						title = title + args[i];
    					}
    					rpname.set(player.getUniqueID().toString(), title);
    
    					// TODO Sending Packet Command #1
    					Caminelot.getNetwork().sendToAllTracking(new PacketRPName(player.getUniqueID().toString(), title), player);
    
    					TextComponentTranslation textComponantTranslation1 = new TextComponentTranslation("Title " + title + " assigned to " + args[1] + " with success!", "");
    					textComponantTranslation1.getStyle().setColor(TextFormatting.GREEN);
    					sender.sendMessage(textComponantTranslation1);
    				} else {
    					wrongUsage(sender);
    				}
    			} else if (args[0].equalsIgnoreCase("remove")) {
    				EntityPlayer player;
    				player = getPlayer(server, sender, args[1]);
    				IRPName rpname = player.getCapability(RPNameProvider.RPNAME_CAP, null);
    				String title = "undefined";
    				rpname.set(player.getUniqueID().toString(), title);
    
    				// TODO Sending Packet Command #2
    				Caminelot.getNetwork().sendToAllTracking(new PacketRPName(player.getUniqueID().toString(), title), player);
    
    				TextComponentTranslation textComponantTranslation1 = new TextComponentTranslation("Title removed to " + args[1] + " with success!", "");
    				textComponantTranslation1.getStyle().setColor(TextFormatting.GREEN);
    				sender.sendMessage(textComponantTranslation1);
    			} else {
    				wrongUsage(sender);
    			}
    		} else {
    			wrongUsage(sender);
    		}
    	}
    
    	private static void wrongUsage(ICommandSender sender) {
    		TextComponentTranslation textComponantTranslation1 = new TextComponentTranslation("Error, usage : /rpname <assign|remove> <player> <rpname>", "");
    		textComponantTranslation1.getStyle().setColor(TextFormatting.RED);
    		sender.sendMessage(textComponantTranslation1);
    	}
    
    	public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) {
    		if (args.length == 1) {
    			return getListOfStringsMatchingLastWord(args, new String[] { "assign", "remove" });
    		} else if (args.length == 2) {
    			return getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames());
    		} else {
    			return Collections.<String>emptyList();
    		}
    	}
    
    }
    
    

    :::

    La Classe du Packet

    :::

    
    public class PacketRPName implements IMessage {
    
    	private String name;
    	private String playerID;
    
    	public PacketRPName() {}
    
    	public PacketRPName(String playerIDIn, String valueIn) {
    		this.name = valueIn;
    		this.playerID = playerIDIn;
    	}
    
    	@Override
    	public void fromBytes(ByteBuf buf) {
    		this.name = ByteBufUtils.readUTF8String(buf);
    		this.playerID = ByteBufUtils.readUTF8String(buf);
    	}
    
    	@Override
    	public void toBytes(ByteBuf buf) {
    		ByteBufUtils.writeUTF8String(buf, this.name);
    		ByteBufUtils.writeUTF8String(buf, this.playerID);
    	}
    
    	public static class Handler implements IMessageHandler<PacketRPName, IMessage> {
    
    		@Override
    		public IMessage onMessage(PacketRPName message, MessageContext ctx) {
    			EntityPlayerMP serverPlayer = ctx.getServerHandler().player;
    
    			serverPlayer.getServerWorld().addScheduledTask(() -> {
    				IRPName rpName = serverPlayer.getCapability(RPNameProvider.RPNAME_CAP, null);
    				rpName.set(message.playerID, message.name);
    			});
    			return null;
    		}
    	}
    }
    

    :::

    Le NetworkHandler

    :::

    public class NetworkHandler {
    
    	public static void registerPacket() {
    		registerMessage(PacketRPName.Handler.class, PacketRPName.class, Side.CLIENT, 2);
    		registerMessage(PacketRPName.Handler.class, PacketRPName.class, Side.SERVER, 3);
    	}
    
    	private static <REQ extends IMessage, REPLY extends IMessage> void registerMessage(final Class<? extends IMessageHandler<REQ, REPLY>> messageHandler, final Class<REQ> requestMessageType,
    			final Side receivingSide, int desc) {
    		Caminelot.getNetwork().registerMessage(messageHandler, requestMessageType, desc, receivingSide);
    	}
    }
    

    :::

    Les Events pour la Capability

    :::

    package fr.caminelot.common.capability.player;
    
    import fr.caminelot.common.Caminelot;
    import fr.caminelot.common.capability.player.mana.IMana;
    import fr.caminelot.common.capability.player.mana.ManaProvider;
    import fr.caminelot.common.capability.player.rpname.IRPName;
    import fr.caminelot.common.capability.player.rpname.RPNameProvider;
    import fr.caminelot.common.network.packet.rpname.PacketRPName;
    import net.minecraft.client.renderer.EntityRenderer;
    import net.minecraft.client.renderer.entity.RenderManager;
    import net.minecraft.entity.Entity;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.entity.player.EntityPlayer.SleepResult;
    import net.minecraft.entity.player.EntityPlayerMP;
    import net.minecraft.util.text.TextComponentString;
    import net.minecraftforge.client.event.RenderPlayerEvent;
    import net.minecraftforge.event.entity.living.LivingFallEvent;
    import net.minecraftforge.event.entity.player.PlayerEvent;
    import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent;
    import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
    import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
    import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;
    import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent;
    import net.minecraftforge.fml.relauncher.Side;
    import net.minecraftforge.fml.relauncher.SideOnly;
    
    @EventBusSubscriber(modid = Caminelot.MODID)
    public class CapabilityEventHandler {
    
    	@SubscribeEvent
    	public static void onPlayerLogsIn(PlayerLoggedInEvent event) {
    		EntityPlayer player = event.player;
    		IRPName rpName = player.getCapability(RPNameProvider.RPNAME_CAP, null);
    
    		String message = String.format("Hello there, your RP Name is %s.", rpName.getName());
    		player.sendMessage(new TextComponentString(message));
    	}
    
    
    	/**
    	 * Copy data from dead player to the new player
    	 */
    	@SubscribeEvent
    	public static void onPlayerClone(PlayerEvent.Clone event) {
    		EntityPlayer player = event.getEntityPlayer();
    		// ====RPName
    		IRPName rpName = player.getCapability(RPNameProvider.RPNAME_CAP, null);
    		IRPName oldRpName = event.getOriginal().getCapability(RPNameProvider.RPNAME_CAP, null);
    		rpName.set(player.getUniqueID().toString(), oldRpName.getName());
    	}
    
    	@SideOnly(Side.CLIENT)
    	@SubscribeEvent
    	public static void onPrePlayerRender(RenderPlayerEvent.Pre event) {
    		EntityPlayer player = event.getEntityPlayer();
    		RenderManager renderManager = event.getRenderer().getRenderManager();
    		double d0 = player.getDistanceSq(renderManager.renderViewEntity);
    		double maxDistance = 64D;
    
    		IRPName rpName = player.getCapability(RPNameProvider.RPNAME_CAP, null);
    
    		if (d0 <= (maxDistance * maxDistance)) {
    			boolean flag = player.isSneaking();
    			float f = renderManager.playerViewY;
    			float f1 = renderManager.playerViewX;
    			boolean flag1 = renderManager.options.thirdPersonView == 2;
    			float f2 = player.height + 0.5F - (flag ? 0.25F : 0.0F);
    			EntityRenderer.drawNameplate(renderManager.getFontRenderer(), rpName.getName(), (float) event.getX(), (float) event.getY() + f2 + 0.25F, (float) event.getZ(), 0, f, f1, flag1, flag);
    		}
    	}
    
    }
    
    

    :::

    Classe RPName

    :::

    
    public class RPName implements IRPName {
    	public String name = "undefined";
    	private String playerID = "none";
    
    	public RPName() {}
    
    	@Override
    	public void set(String playerIDIn, String nameIn) {
    		this.playerID = playerIDIn;
    		this.name = nameIn;
    	}
    
    	@Override
    	public String getName() {
    		return this.name;
    	}
    
    }
    

    :::

    Classe Storage de la Capability

    :::

    
    public class RPNameStorage implements IStorage<IRPName> {
    	@Override
    	public NBTBase writeNBT(Capability<IRPName> capability, IRPName instance, EnumFacing side) {
    		return new NBTTagString(instance.getName());
    	}
    
    	@Override
    	public void readNBT(Capability<IRPName> capability, IRPName instance, EnumFacing side, NBTBase nbt) {
    		instance.set("none", ((NBTTagString) nbt).getString());
    	}
    }
    

    :::

    Classe Provider de la Capability

    :::

    
    public class RPNameProvider implements ICapabilitySerializable<NBTBase> {
    	@CapabilityInject(IRPName.class)
    	public static final Capability<IRPName> RPNAME_CAP = null;
    
    	private IRPName instance = RPNAME_CAP.getDefaultInstance();
    
    	@Override
    	public boolean hasCapability(Capability<?> capability, EnumFacing facing) {
    		return capability == RPNAME_CAP;
    	}
    
    	@Override
    	public <T> T getCapability(Capability<T> capability, EnumFacing facing) {
    		return capability == RPNAME_CAP ? RPNAME_CAP.<T>cast(this.instance) : null;
    	}
    
    	@Override
    	public NBTBase serializeNBT() {
    		return RPNAME_CAP.getStorage().writeNBT(RPNAME_CAP, this.instance, null);
    	}
    
    	@Override
    	public void deserializeNBT(NBTBase nbt) {
    		RPNAME_CAP.getStorage().readNBT(RPNAME_CAP, this.instance, null, nbt);
    	}
    }
    

    :::

    Enregistrement de la capability

    • Dans le pre-init du CommonProxy

    :::

    CapabilityManager.INSTANCE.register(IRPName.class, new RPNameStorage(), RPName::new);
    

    :::