Commande qui joueur à proximité

Sala85

Massacreur de Mouton
12 Octobre 2020
36
2
18
22
Bonsoir,
J'ai créé un plugin en utilisant spigot 1.8.8, et je cherche à savoir si une commande est possible pour enlever des coeurs à tous les joueurs proche du joueur ayant utiliser la commande (et pas que sur la même couche Y).
Auparavant j'utilisais ça :
1642884729638.png

Cependant ça ne marche que sur la couche exact du joueur...
Merci d'avance de votre réponse,
Sala.
 
Bonsoir,

Tu as trois problèmes de logique avec ton algorithme actuel :
  1. Tu souhaites sélectionner tous les joueurs dans un rayon de 10 mètres mais tu break; la boucle à la fin de toutes tes itérations, donc ta boucle ne sera exécutée uniquement que zéro ou une seule fois.


  2. Tu ne vérifies pas si l'entité à proximité est effectivement un joueur ; tenter de (cast) une entité non-joueur en un joueur lancera une ClassCastException.


  3. Si le joueur à proximité a deux points de vie, 2 - 6 = -4, et définir la vie d'un joueur à un nombre négatif lancera une IllegalArgumentException.

    De plus, tu as une fonction spéciale pour appliquer des dégâts bruts, qui permet en plus de marquer l'entité qui a causé les dégâts ; donc typiquement si l'entité source des dégâts en zone doit prendre les kills il faut indiquer l'attaquant aux attaquées, mais si le joueur a fait des dégâts malgré lui (malédiction ou que sais-je), dans ce cas-là ce serait mieux de ne pas lui donner les kills et donc de ne pas indiquer l'attaquant aux attaquées.

    Dernier point, si tu marques les dégâts comme étant des dégâts avec la fonction appropriée, les plugins de protection peuvent les annuler (EntityDamageByEntityEvent), ce qui permet par exemple à World Guard de désactiver l'effet de ta commande au spawn sans à ce que tu doives rajouter toi-même une condition ; il me semble qu'aucun plugin ne peut annuler un Entity#setHealth(double).
Ensuite pour la forme :
  • Le compilateur optimise les expressions directement calculables telles que 10 * 10, donc que tu mettes ça ou directement 100 reviendra au même. Dans ces cas-là, c'est toi qui choisie la forme qui te convient le mieux ; là c'est pas très compliqué de remarquer que 100 = 10², par contre voir que 156.25 = 12.5² c'est une autre paire de manches.

  • De la même manière tu peux créer des variables constantes avec le mot-clef final (fonctionne uniquement sur les primitives), donc tu peux aussi écrire des expressions telles que 2 * Math.PI * r qui sera automatiquement remplacé par 6.2832 * r, avec évidemment beaucoup plus de décimales dans le premier facteur.
Exemple du code qui mettra tout cela en pratique :
Java:
// avoir des varibles pour les constantes permet de les nommer,
// ce qui évite de chercher que 20 = 2 * 10,
// de plus avec le mot-clef `final` le compilateur sait qu'il
// peut complètement zigouiller la varible parce qu'il peut
// tout simplement la remplacer par sa valeur à chaque fois
// qu'il l'a voit, donc pas de perte en performances
// + si tu veux changer le rayon, tu risques pas d'oublier un
//   2 * 10 quelque part
final int radius = 10;
final double damage = 6.0;

for(Entity nearby : source.getNearbyEntities(radius * 2, radius * 2, radius * 2)) {
    if(!(nearby instanceof Player) || source.getLocation().distanceSquared(nearby.getLocation()) > (radius * radius)) {
        // si l'entité à proximité n'est pas un joueur ou n'est pas à
        // `radius` mètres de `source`, on ne fait rien pour cette entité
        continue;
    }
    
    // même pas besoin de convertir le joueur en Player, donc tu peux
    // généraliser les dégâts à toutes les entités à proximité si besoin
    nearby.damage(damage, source);
}

Cordialement,
ShE3py
 
Bonsoir,

Tu as trois problèmes de logique avec ton algorithme actuel :
  1. Tu souhaites sélectionner tous les joueurs dans un rayon de 10 mètres mais tu break; la boucle à la fin de toutes tes itérations, donc ta boucle ne sera exécutée uniquement que zéro ou une seule fois.


  2. Tu ne vérifies pas si l'entité à proximité est effectivement un joueur ; tenter de (cast) une entité non-joueur en un joueur lancera une ClassCastException.


  3. Si le joueur à proximité a deux points de vie, 2 - 6 = -4, et définir la vie d'un joueur à un nombre négatif lancera une IllegalArgumentException.

    De plus, tu as une fonction spéciale pour appliquer des dégâts bruts, qui permet en plus de marquer l'entité qui a causé les dégâts ; donc typiquement si l'entité source des dégâts en zone doit prendre les kills il faut indiquer l'attaquant aux attaquées, mais si le joueur a fait des dégâts malgré lui (malédiction ou que sais-je), dans ce cas-là ce serait mieux de ne pas lui donner les kills et donc de ne pas indiquer l'attaquant aux attaquées.

    Dernier point, si tu marques les dégâts comme étant des dégâts avec la fonction appropriée, les plugins de protection peuvent les annuler (EntityDamageByEntityEvent), ce qui permet par exemple à World Guard de désactiver l'effet de ta commande au spawn sans à ce que tu doives rajouter toi-même une condition ; il me semble qu'aucun plugin ne peut annuler un Entity#setHealth(double).
Ensuite pour la forme :
  • Le compilateur optimise les expressions directement calculables telles que 10 * 10, donc que tu mettes ça ou directement 100 reviendra au même. Dans ces cas-là, c'est toi qui choisie la forme qui te convient le mieux ; là c'est pas très compliqué de remarquer que 100 = 10², par contre voir que 156.25 = 12.5² c'est une autre paire de manches.

  • De la même manière tu peux créer des variables constantes avec le mot-clef final (fonctionne uniquement sur les primitives), donc tu peux aussi écrire des expressions telles que 2 * Math.PI * r qui sera automatiquement remplacé par 6.2832 * r, avec évidemment beaucoup plus de décimales dans le premier facteur.
Exemple du code qui mettra tout cela en pratique :
Java:
// avoir des varibles pour les constantes permet de les nommer,
// ce qui évite de chercher que 20 = 2 * 10,
// de plus avec le mot-clef `final` le compilateur sait qu'il
// peut complètement zigouiller la varible parce qu'il peut
// tout simplement la remplacer par sa valeur à chaque fois
// qu'il l'a voit, donc pas de perte en performances
// + si tu veux changer le rayon, tu risques pas d'oublier un
//   2 * 10 quelque part
final int radius = 10;
final double damage = 6.0;

for(Entity nearby : source.getNearbyEntities(radius * 2, radius * 2, radius * 2)) {
    if(!(nearby instanceof Player) || source.getLocation().distanceSquared(nearby.getLocation()) > (radius * radius)) {
        // si l'entité à proximité n'est pas un joueur ou n'est pas à
        // `radius` mètres de `source`, on ne fait rien pour cette entité
        continue;
    }
   
    // même pas besoin de convertir le joueur en Player, donc tu peux
    // généraliser les dégâts à toutes les entités à proximité si besoin
    nearby.damage(damage, source);
}

Cordialement,
ShE3py
Hello !

J'aurai une petite question :
Pour ce qui est des constantes, il a pas meilleur temps de les mettre en attribut de la classe avec les mots clés private static final ?
Genre :

Java:
public class PluginClass extends JavaPlugin {

    private static final int RADIUS = 10;
    private static final double damage = 6.0;
 
    @Override
    public void onEnable() {

    }
}
Puisqu'il s'agit de constantes, autant qu'elles soient statiques pour ne pas les redéfinir à chaque fois que la commande est tapée ?
 
Puisqu'il s'agit de constantes, autant qu'elles soient statiques pour ne pas les redéfinir à chaque fois que la commande est tapée ?
C'est un peu plus compliqué que cela, prenons un premier exemple :
Java:
int f() {
    final Random random = new Random();

    final int x = random.nextInt(6);
    final int y = x + 5;
    final int z = x - 2;

    return y * z;
}

Ici, « x », « y », « z » et le produit des deux derniers sont tous quatre constants pendant toute leur durée de vie, c'est-à-dire de leur création à la fin d'exécution de la fonction « f ».
La meilleure chose que le compilateur puisse faire, c'est zigouiller les variables intermédiaires :
Java:
int f() {
    final int x = new Random().nextInt(6);
   
    return (x + 5) * (x - 2);
}

Ce qui évite au processeur de faire des allers-retours dans la mémoire vive pour stocker et lire « y » et « z » alors qu'il pourrait tout faire chez lui et simplement renvoyer uniquement le résultat final.

Maintenant, deuxième exemple :
Java:
boolean g(int 六) {
    final int 三 = 3;
    final int 九 = 三 * 三;
   
    return (三 + 六) == 九;
}

Les deux variables « 三 » et « 九 » sont constantes pendant toute la durée de l'univers, tu peux donc les transformer en littéraux :
Java:
boolean g(int 六) {
    return (3 + 六) == 9;
}

Donc, nos deux fonctions après simplification :
Java:
int f() {
    final int x = new Random().nextInt(6);
   
    // développer nous ferait faire faire plus d'opérations, donc gardons la forme factorisée
    return (x + 5) * (x - 2);
}

boolean g(int 六) {
    return 六 == 6;
}

Dans les deux cas nous sommes partis d'un nombre constant, avons définis d'autres variables constantes intermédiaires qui ont disparue, or dans le premier cas, il nous reste la constante initiale « x ».

Où est donc passé notre zigouillage ?
Et bien la différence fondamentale est que :
  • Dans notre premier cas, la constante « x » subit une affectation.
  • Dans notre second cas, la constante « 三 » est définie comme étant une égalité mathématique.
Je rappelle que a et b sont égaux entre eux si et seulement si l'on peut remplacer a par b et b par a.
Dans notre 2e cas, l'on peut écrire « 三 × 三 = 3 × 3 ».
Or dans notre 1er cas, « x + x ≠ random.nextInt(6) + random.nextInt(6) ».

Maintenant, pour en revenir à ta constante statique :
Java:
private static final int RADIUS = 10;

Est-elle constante pour la durée de vie de l'univers ?
Alors en théorie non parce Java est réflexif, mais en pratique oui, j'ai compilé puis décompilé les fonctions pour voir (OpenJDK 17) :
Java:
public class Temp {
    private static final float A = 2.72F;
    private static final float B = 0.45F;

    public Temp() {}

    int f() {
        Random random = new Random();
        int x = random.nextInt(6);
        int y = x + 5;
        int z = x - 2;
        return y * z;
    }

    boolean g(int 六) {
        int 三 = 3;
        int 九 = 9;
        return 3 + 六 == 9;
    }

    boolean h(float x) {
//      return A * 2 + x == B / 3;        
        return 5.44F + x == 0.14999999F;
    }
}

On peut voir qu'il a un peu optimisé comme un saugrenu, mais Java optimise aussi à l'exécution en transformant le bytecode en code natif, et on peut toujours espérer qu'il optimise mieux un jour.

Donc en théorie il n'y a aucune différence entre mettre la constante dans la classe en static, en pratique il optimise comme un pied donc ça te permet d'éviter d'allouer des variables qu'il n'utilisera au final même pas, cependant il y a de fortes chances que le Java de celui qui exécute ton programme refasse une étape d'optimisation par derrière, conclusion la différence est négligeable, c'est comme tu préfères.

J'avais mis la constante dans la fonction par habitude de réduire au plus possible la portée de la variable (si une seule fonction l'utilise, ça ne sert à rien de la rendre accessible pour toutes les autres fonctions), après on peut voir qu'au final il n'a pas enlevé les variables même si elles sont inutilisées, mais bon il a quand même calculé 九 = 9 au lieu de laisser 九 = 3 × 3, et deux allocations de huit octets est un peu cher prix à payer en échange qu'il ne me propose pas des constantes d'autres fonctions dans l'autocomplétion, de plus ça permet d'avoir plusieurs constantes du même nom dans plusieurs fonctions.

P.S.: Les caractères 三, 六 et 九 représentent respectivement les nombres 3, 6 et 9 dans la numération chinoise.
 
  • J'aime
Reactions: Niz
Bonsoir, j'avais pas vu tout ça xD, je verrais si ça règle mon problème,
Merci pour les réponses !