Résolu Problème d'utilisation d'une ressource

Bizu

Bucheron
21 Juillet 2020
22
0
13
24
Coucou, alors j'ai rencontré un petit souci lors de l'utilisation de cette ressource : https://www.spigotmc.org/threads/infinite-inventory-with-pages.178964/

en effet j'ai bien tout suivi, mais lorsque j'ajoute via une commande un ItemStack dans l'ArrayList puis que j'ouvre l'inventaire via une autre commande, et bien cela n'affiche rien au lieu de normalement afficher un ItemStack papier... Voici mon code ici : https://pastebin.com/qy3LbGAC
Si quelqu'un passe par là et a la solution, je dirais un grand merci !
 

Detobel36

Créateur de plugins (PhoenixRebirth)
Support
17 Août 2012
10 531
24
2 247
347
27
Bruxelles - Belgique
www.phoenix-rebirth.fr
Salut,

Bon, pleins de trucs à dire...
Déjà en java les class ont (par convention) des majuscules. Donc "main" devrais s'écrire "Main":
Java:
    private Main main;

    public Report(Main test) {
        this.main = test;
    }

Ne soit pas "radin" dans le nom de tes variables. C'est pas parce que tes variables sont plus petites que tu va "économiser" du temps ou de la performance. Lire un code où on joue avec des "c", des "cs", ... c'est vite incompréhensible et on a vite fait d'oublier une lettre (et donc de faire une erreur). Utilise un IDE qui va auto complété tes variables ;)
Typiquement:
Java:
public boolean onC ommand(CommandSender cs, Command c, String s, String[] args) {
Peut devenir:
Java:
public boolean onC ommand(CommandSender commandSender, Command command, String label, String[] args) {

Bon après on peut un peu optimiser tes conditions...
Java:
        if (cs instanceof Player) {
            Player player = (Player) cs;
 
            if (c.getName().equalsIgnoreCase("report")) {

                if (args.length <= 1) { // ça correspond bien à == 0 ou == 1
                    player.sendMessage("/report (joueur) (raison)");
                    return true;
                } else { // Si on est pas inférieur à 1 (compris), on est d'office suppérieur à 2 (compris)
                    Player selectedPlayer = Bukkit.getPlayer(args[0]); // Pourquoi avoir mis la variable "t" ? Oo
 
                    if (selectedPlayer == null) {
                        player.sendMessage("Tu ne peux pas report un joueur Offline");
                        return true;
                    }

                    // Tu pourrais juste utiliser un "join"
                    StringBuilder messageBuilder = new StringBuilder();
                    for (String part : args) {
                        messageBuilder.append(part);
                    }
 
                    items.add(getItem(Material.PAPER, player.getName(), messageBuilder.toString() + " Joueur ciblé : " + selectedPlayer.getName()));
                }
            } else if (c.getName().equalsIgnoreCase("reports")) { // Pourquoi mettre une majuscule alors que tu fais un "ignoreCase"...
                if (player.hasPermission("test.modo")) { // En général les permissions n'ont pas de majuscules
                    new ScrollerInventory(items, ChatColor.RED + "Reports", player);
                } else {
                    player.sendMessage("Tu n'as pas la permission de faire ceci !");
                }
            }
        }

Bon, d'autres optimisations pourraient être faites dans ton code (comme rajouter des final ou un "private static" à ton ArrayList). Mais tout cela ne devrait pas influencer le fait que ça ne marche pas.

Pour comprendre pourquoi ça ne marche pas, je t'invite à rajouter des messages de débug :)
Si tu veux qu'on trouve le solution, il faudrait que tu nous donne ton "Main", histoire qu'on comprenne comment ta class "Report" est instancié.

Pour les messages de débug, voila ce que j'aurais mis:
Java:
public class Report {
    private final Main main;

    private static final ArrayList<ItemStack> items = new ArrayList<ItemStack>();

    public Report(final Main main) {
        this.main = main;
    }

    @Override
    public boolean onC ommand(final CommandSender commandSender, final Command command, final String label, final String[] args) {
        if (commandSender instanceof Player) {
            final Player player = (Player) commandSender;
 
            if (command.getName().equalsIgnoreCase("report")) {

                if (args.length <= 1) {
                    player.sendMessage("/report (joueur) (raison)");
                    return true;
                } else {
                    final Player selectedPlayer = Bukkit.getPlayer(args[0]); // Pourquoi avoir mis la variable "t" ? Oo
 
                    if (selectedPlayer == null) {
                        player.sendMessage("Tu ne peux pas report un joueur Offline");
                        return true;
                    }

                    // Tu pourrais juste utiliser un "join"
                    StringBuilder messageBuilder = new StringBuilder();
                    for (final String part : args) {
                        messageBuilder.append(part);
                    }
 
                    items.add(getItem(Material.PAPER, player.getName(), messageBuilder.toString() + " Joueur ciblé : " + selectedPlayer.getName()));
                    Bukkit.getLogger().info("Ajout d'un item. Total de la liste: " + items.size());
                }
            } else if (command.getName().equalsIgnoreCase("reports")) {
                if (player.hasPermission("test.modo")) {
                    Bukkit.getLogger().info("Ouverture de ScrollerInventory avec " + items.size() + " items");
                    new ScrollerInventory(items, ChatColor.RED + "Reports", player);
                } else {
                    player.sendMessage("Tu n'as pas la permission de faire ceci !");
                }
            }
        }
        return false; 
   }
}


Cordialement,
Detobel36
 

Bizu

Bucheron
21 Juillet 2020
22
0
13
24
Coucou, alors pour commencer merci de la réponse et des conseils, en effet il y a la convention de la class Main en majuscules mais j'oublie souvent si c'est en majuscule ou en miniscule x) ensuite, au niveau de l'optimisation même si ce n'est pas la version " finale " de mon plugin c'est sympa de m'avoir montré des petites optimisations :) pour parler de comment est instancié mes commandes je ne comprend pas trop l'utilité mais voici :
@Override
public void onEnable() {
saveDefaultConfig();
getCommand("report").setExecutor(new Report(this));
getCommand("reports").setExecutor(new Report(this));
}

Ensuite, lorsque je débug cela m'affiche 1 puis lorsque je fait un autre report cela affiche 2 mais ça n'affiche toujours rien dans l'inventaire...

Merci de ton/votre aide !
 

Detobel36

Créateur de plugins (PhoenixRebirth)
Support
17 Août 2012
10 531
24
2 247
347
27
Bruxelles - Belgique
www.phoenix-rebirth.fr
Salut,

Wait wait:
Java:
   getCommand("report").setExecutor(new Report(this));
   getCommand("reports").setExecutor(new Report(this));
Tu instencie deux fois la class "Report". Tu as donc 2 fois la liste "items" (à moins que tu la passe en static).

Il te faut faire ceci:
Java:
   Report reportCmd = new Report(this);
   getCommand("report").setExecutor(reportCmd);
   getCommand("reports").setExecutor(reportCmd);
Cela devrait résoudre ton problème.

ensuite, au niveau de l'optimisation même si ce n'est pas la version " finale " de mon plugin
Y a pas de "brouillon" quand on développe ;) Sinon tu dis toujours "ce sera pour plus tard", tu oublies et tu reviens jamais dessus... Si tu veux faire des "test" avant, tu fais juste un script en une class. Mais faut bien comprend ce qu'on fait. Le mieux c'est vraiment d'essayer de directement faire les choses bien. De ne pas tout développé. De ne faire qu'une partie. Mais la partie que tu fais, tu l'as fait bien ;)


Cordialement,
Detobel36
 

Bizu

Bucheron
21 Juillet 2020
22
0
13
24
Re-Coucou, juste pour dire que ça a marché, honnêtement je n'aurai jamais été cherché de ce côté étant donné que dans un autre pl j'avais instancié plusieurs fois des commandes dans la même class, comme quoi j'ai encore des choses à apprendre, et merci je vais continuer à apprendre :)
 

ShE3py

Enbogueuse
Support
26 Septembre 2015
4 129
162
461
247
21
Mìlhüsa
Bonjour,

Certaines parties ont déjà été dites par Detobel, mais bon vu que c'est dit autrement je ne me suis pas amusé à les supprimer.
Sur ce bonne lecture :3

Java:
ArrayList<ItemStack> items = new ArrayList<ItemStack>();

Tu n'as pas spécifié de niveau d'accès pour ta variable, donc là est elle en package — dans ton cas elle devrait sans doute être privée :
Java:
private ArrayList<ItemStack> items = new ArrayList<ItemStack>();

Ensuite, elle n'est pas statique — c'est à dire que chaque instance de Report a sa propre variable items. Or tu fais :
Java:
@Override
public void onEnable() {
    this.saveDefaultConfig();

    this.getCommand("report").setExecutor(new Report(this));
    this.getCommand("reports").setExecutor(new Report(this));
}

Ce qui équivaut à faire :
Java:
@Override
public void onEnable() {
    this.saveDefaultConfig();

    Report reportA = new Report(this);
    Report reportB = new Report(this);

    this.getCommand("report").setExecutor(reportA);
    this.getCommand("reports").setExecutor(reportB);
}
Donc la commande /report modifie uniquement reportA.items ; et /reports lit uniquement reportB.items.
Mais ce sont deux variables différentes, donc oui, ton inventaire est vide puis que reportB.items n'est jamais modifiée.

Si tu veux que tes deux commandes utilisent la même variable items, il faut que tu leurs donne le même exécuteur :
Java:
@Override
public void onEnable() {
    this.saveDefaultConfig();

    Report report = new Report(this);

    this.getCommand("report").setExecutor(report);
    this.getCommand("reports").setExecutor(report);
}
Comme ça, /report modifiera report.items et /reports lira report.items, donc la même variable.

C'est pour ça que Detobel dans son spoiler a mit :
Java:
private static final ArrayList<ItemStack> items = new ArrayList<ItemStack>();

Le mot-clef static indique que la variable est liée à la classe et non à l'objet ; chaque instance de Report utilisera la même variable items, et tu peux accéder à la variable sans passer par une instance :
Java:
Report.items.add(...);

Ensuite, derniers points ;
  • Tu n'es pas obligé d'écrire deux fois le type de la classe générique :
    Java:
    private static final ArrayList<ItemStack> items = new ArrayList<>();
    C'est plus lisible, notamment avec des List<Foo<Bar<Baz>>>.

  • C'est généralement mieux d'utiliser l'interface :
    Java:
    private static final List<ItemStack> items = new ArrayList<>();
    https://stackoverflow.com/questions/2279030/type-list-vs-type-arraylist-in-java

  • Vu le contexte et que tu n'as pas besoin d'un accès aléatoire, une liste chaînée serait plus rapide :
    Java:
    private static final List<ItemStack> items = new LinkedList<>();
Y a pas de "brouillon" quand on développe ;) Sinon tu dis toujours "ce sera pour plus tard", tu oublies et tu reviens jamais dessus... Si tu veux faire des "test" avant, tu fais juste un script en une class. Mais faut bien comprend ce qu'on fait. Le mieux c'est vraiment d'essayer de directement faire les choses bien. De ne pas tout développé. De ne faire qu'une partie. Mais la partie que tu fais, tu l'as fait bien
Bizu parlait optimisation mais de ce côté-là il faut s'attaquer avec parcimonie, parce qu'en soit il ne faut pas vraiment tout optimiser — mieux vaut un code lisible qu'un code illisible mais qui prend une nanoseconde de moins.
La règle est plutôt de regarder quelles fonctions prennent le plus de temps cumulé et d'optimiser celles-ci ; c'est plus intéressant de gagner une microseconde sur une fonction appelée 50 000 fois par seconde que de gagner une milliseconde sur une fonction qui n'est appelée qu'une fois au démarrage.

Bon après il y a toujours des lignes qui détruisent les yeux — while(i < len(L)) L.get(i++) sur une liste chaînée — mais je ne pense pas que le final change grand-chose dans les paramètres ou aux variables locales, sauf pour éventuellement les littéraux. Et le compilateur doit être assez intelligent pour savoir que la variable est effectivement final même sans le mot-clef.
IntelliJ affiche par défaut un avertissement lorsqu'un field peut être final parce qu'il dit que c'est prit en compte par l'optimisateur, et il y a aussi un autre avertissement pour justement les paramètres ou les variables locales qui peuvent être final, mais qui lui est désactivé par défaut et la description ne cite pas d'optimisation, donc je pense que c'est plutôt pour le code style.
Et de la même manière aucun paramètre des fonctions de Java n'est final, alors ça ne doit pas changer grand-chose.

Bon après c'est que pour le mot-clef final dans certains cas, personellement je ne l'utilise que dans les fields parce que je trouve ça un peu trop lourd dans les paramètres/variables locales — surtout que les objets ne sont pas constants, seul le pointeur l'est.

Bref pour le reste du code :
Java:
// `Report` sous-entend que command est un signalement d'un seul joueur,
// or il y en a plusieurs ;
//
// un meilleur nom serait `Reports` ou `CommandReport`
public class CommandReport implements CommandExecutor {
    // convention: les classes commencent par une majuscule, donc `Main`
    // + main veut dire tout et n'importe quoi, préfère mettre le nom de ton plugin
    private MyPlugin plugin;

    private static final List<ItemStack> items = new LinkedList<>();

    public CommandReport(MyPlugin plugin) {
        // le nom du paramètre `test` n'a aucun sens
        this.plugin = plugin;
    }


    @Override
    public boolean onKommand(CommandSender sender, Command command, String label, String[] args) {
        // c'est plus élégant de `return` tout de suite que 15km après
        if(!(sender instanceof Player)) {
            sender.sendMessage("§cVous devez être un joueur pour pouvoir exécuter cette commande.");
          
            return true; // `return false` affiche l'usage, or ce n'est pas le problème
        }
      
        Player author = (Player) sender;

        // optimisation: le paramètre `label` indique déjà la commande ou l'alias utilisé
        // optimisation: le nom du `label` ne peux pas être autre chose que ce que tu as écrit dans ton `plugin.yml`,
        //   on est sûr de la casse
        if(label.equals("report")) {
            // optimisation: équivaut à < 2
            if(args.length < 2) {
                // convention: les commandes sont en anglais
                // convention: les paramètres requis sont entre chevrons
                // + c'est mieux de commencer avec `Usage:` ou équivalent
                author.sendMessage("Usage: /report <who> <reason>");
              
                return true;
            }
          
            // optimisation: aucun autre cas possible, `else`
            //   or comme nous avons `return` dans le `if`,
            //   pas besoin de `else`
          
            // ¿t?
            Player target = Bukkit.getPlayer(args[0]);
          
            // plus lisible de faire le check juste après que l'on ait récupéré `args[0]`
            if(target == null) {
                // français: “to report” se traduit par « signaler »
                // anglais: “offline” ne prend pas de majuscule
                // français: “offline” se traduit par « hors-ligne »
                // convention: les messages d'erreurs sont en rouge
                author.sendMessage(ChatColor.RED + "Tu ne peux pas signaler un joueur hors-ligne");
              
                return true;
            }
          
            // ¿bc?
            StringBuilder sb = new StringBuilder();
            sb.append("Le joueur « ")
              .append(author.getName())
              .append(" » a signalé « ")
              .append(target.getName())
              .append(" » pour: ");
          
            // args contient le nom du joueur signalé `args[0]`
            for(int i = 1; i < args.length; ++i) {
                sb.append(parts[i]);
            }
          
            // convention: si `getItem` créer un item, ce n'est pas un get
            items.add(getItem(Material.PAPER, author.getName() /* ??? */, sb.toString() /* inutile d'utiliser un `StringBuilder` si tu + après */));
            return true;
        }


        if(label.equals("reports" /* ¿majuscule? */)) {
            if(author.hasPermission("rest.modo" /* ¿majuscule? */)) {
                // ¿new?
                new ScrollerInventory(items, ChatColor.RED + "Reports", author);

            } else {
                // rouge
                author.sendMessage("§cTu n'as pas la permission de faire ceci !");
            }
            
            return true;
        }
      
        //  ni `/report`, ni `/reports`
        throw new RuntimeException("entered unreachable code: " + label); // ne devrait pas se produire
    }
}

Ensuite, tu sauvegardes les signalements sous la forme d'un item ; NON. Tu dois créer une classe Report qui représentera un signalement avec :

Variable
Type
Description
reportedPlayer​
UUID​
Le joueur signalé​
authorPlayer​
UUID​
Le joueur qui a fait le signalement​
reason​
String​
La raison du signalement​
date​
long​
La date du signalement​

Puis tu rajouteras ensuite une fonction createRenderItem() pour créer un item à partir de ces informations.
Cela te permettra de mieux sauvegarder les signalements, et que si tu veux changer les items que tu utilises dans /reports, tu auras juste à modifier la fonction sans à avoir besoin de modifier les données sauvegardées.

De plus, travailler avec des joueurs sous forme d'UUIDs et non d'entités Player te permettra de supporter les joueurs hors-lignes OfflinePlayer.

Cordialement,
ShE3py
 
  • J'aime
Reactions: Detobel36