Plugin Impossible de faire fonctionner Gson..

LaForêt

Bucheron
11 Septembre 2022
8
0
11
34
Salut,

j'essaye d'utiliser Gson pour la sérialisation et donc la save des données dans un fichier mais je ne comprend pas pourquoi mon truc fonctionne pas

Erreur

Je veux enregistrer dans mon fichier l'objet "Arena" qui est composé d'un string et de deux Location
Code:
public class Arena {

    private String type;
    private Location p1;
    private Location p2;

    public Arena(String type, Location p1, Location p2)
    {
        this.type = type;
        this.p1 = p1;
        this.p2 = p2;
    }

Code de serialization
Code:
    public String SerializeArenaFile(ArenaFile arenaFile)
    {
        return this.gson.toJson(arenaFile);
    }

Class ArenaFile
Code:
public class ArenaFile {

    public List<Arena> listArena = new ArrayList<>();

(Les valeurs type p1 p2 ne peuvent jamais être nulle)

Code:
                Arena arena = new Arena(type, p1, p2);
                Main.getInstance().getArenaFile().setArena(arena);
                FileManager.saveFileArena(file);

Code:
    public static void saveFileArena(File file)
    {
        final FileWriter fw;

        try {
            fw = new FileWriter(file);
            String t = Main.getInstance().getGsonSerialization().SerializeArenaFile(Main.getInstance().getArenaFile());
            fw.write(t);
            fw.flush();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Dans la class main il y a un simple getter de "FileArena" basique

Le problème semble être dans la class "ArenaFile" quand je remplace la list de type "Arena" par une liste de type "String" ça fonctionne, cependant il me faut absolument une list de type arena
 

ShE3py

Enbogueuse
Support
26 Septembre 2015
4 518
198
499
247
21
Mìlhüsa
Bonsoir,

En sauvegardant une Arena tu sauvegardes deux Location, en sauvegardant une Location tu sauvegardes son World, et sauvegarder deux mondes entiers avec toutes les entités et chunks dedans ça commence à faire cher, en particulier avec les références cycliques (en sauvegardant un monde tu sauvegardes ses entités, mais en sauvegardant une entité tu sauvegardes son monde).

Passe par l'API de configuration de Bukkit :
https://bukkit.fandom.com/wiki/Configuration_API_Reference


Java:
public class Arena {
    private String type;
    private Location p1;
    private Location p2;

    public Arena(String type, Location p1, Location p2) {
        this.type = type;
        this.p1 = p1;
        this.p2 = p2;
    }
}
Je suis mais absolument incapable de savoir ce que sont censées représentées ces trois variables.

(Les valeurs type p1 p2 ne peuvent jamais être nulle)
Faux.
Java:
Arena arena = new Arena("idk", null, null);

Si tu fais des assomptions, c'est mieux de les vérifier dans le constructeur :
Java:
public Arena(String a, Location b, Location c) {
    if(a == null || a.isBlank()) {
        throw new IllegalArgumentException("`a` must not be blank");
    }
   
    if(b == null || b.getWorld() == null) {
        throw new IllegalArgumentException("`b` must have a world");
    }
   
    if(c == null || c.getWorld() == null) {
        throw new IllegalArgumentException("`c` must have a world");
    }
   
    // généralement on créer une classe utilitaire pour faire ça en une seule ligne,
    // style Arguments.requirePositionWithWorld("b", b);
   
    this.a = a;
    this.b = b;
    this.c = c;
}

Java:
public class ArenaFile {
    public List<Arena> listArena = new ArrayList<>();
}

Ce n'est pas un fichier mais une liste d'arènes donc Arenas, ArenaList, etc. serait plus approprié, et à moins que tu veuilles avoir plusieurs groupes d’arènes, je trouve que c'est mieux de mettre la liste directement dans la classe :
Java:
public class Arena {
    private static final List<Arena> ARENAS = new ArrayList<>();
   
    public static void loadAll() {
        // [...]
    }
   
    public static void saveAll() {
        // [...]
    }
   
    @UnmodifiableView
    public static List<Arena> list() {
        return Collections.unmodifiableList(ARENAS);
    }
}

Car bon,
Java:
Main.getInstance().getArenaFile().setArena(arena);
« Définir l'arène du "fichier arène" de l'instance de la classe principale sur arena », ça ne veut pas dire grand-chose.

Des objets bien nommés rendraient le code beaucoup plus compréhensible :
Java:
Arena.register(arena);
Arenas.add(arena);
MyPlugin.instance().arenas().add(arena);

Et nomme ta classe « principale » avec le nom de ton plugin, car si tu veux utiliser WorldGuard, tu veux avoir un truc du style
Java:
WorldGuard wg = this.getPlugin("wordguard");

Pas de
Java:
Main wg = this.getPlugin("worldguard");

Cordialement,
ShE3py
 
  • J'aime
Reactions: LaForêt

LaForêt

Bucheron
11 Septembre 2022
8
0
11
34
Bonsoir,

En sauvegardant une Arena tu sauvegardes deux Location, en sauvegardant une Location tu sauvegardes son World, et sauvegarder deux mondes entiers avec toutes les entités et chunks dedans ça commence à faire cher, en particulier avec les références cycliques (en sauvegardant un monde tu sauvegardes ses entités, mais en sauvegardant une entité tu sauvegardes son monde).

Passe par l'API de configuration de Bukkit :
https://bukkit.fandom.com/wiki/Configuration_API_Reference


Java:
public class Arena {
    private String type;
    private Location p1;
    private Location p2;

    public Arena(String type, Location p1, Location p2) {
        this.type = type;
        this.p1 = p1;
        this.p2 = p2;
    }
}
Je suis mais absolument incapable de savoir ce que sont censées représentées ces trois variables.


Faux.
Java:
Arena arena = new Arena("idk", null, null);

Si tu fais des assomptions, c'est mieux de les vérifier dans le constructeur :
Java:
public Arena(String a, Location b, Location c) {
    if(a == null || a.isBlank()) {
        throw new IllegalArgumentException("`a` must not be blank");
    }
 
    if(b == null || b.getWorld() == null) {
        throw new IllegalArgumentException("`b` must have a world");
    }
 
    if(c == null || c.getWorld() == null) {
        throw new IllegalArgumentException("`c` must have a world");
    }
 
    // généralement on créer une classe utilitaire pour faire ça en une seule ligne,
    // style Arguments.requirePositionWithWorld("b", b);
 
    this.a = a;
    this.b = b;
    this.c = c;
}

Java:
public class ArenaFile {
    public List<Arena> listArena = new ArrayList<>();
}

Ce n'est pas un fichier mais une liste d'arènes donc Arenas, ArenaList, etc. serait plus approprié, et à moins que tu veuilles avoir plusieurs groupes d’arènes, je trouve que c'est mieux de mettre la liste directement dans la classe :
Java:
public class Arena {
    private static final List<Arena> ARENAS = new ArrayList<>();
 
    public static void loadAll() {
        // [...]
    }
 
    public static void saveAll() {
        // [...]
    }
 
    @UnmodifiableView
    public static List<Arena> list() {
        return Collections.unmodifiableList(ARENAS);
    }
}

Car bon,
Java:
Main.getInstance().getArenaFile().setArena(arena);
« Définir l'arène du "fichier arène" de l'instance de la classe principale sur arena », ça ne veut pas dire grand-chose.

Des objets bien nommés rendraient le code beaucoup plus compréhensible :
Java:
Arena.register(arena);
Arenas.add(arena);
MyPlugin.instance().arenas().add(arena);

Et nomme ta classe « principale » avec le nom de ton plugin, car si tu veux utiliser WorldGuard, tu veux avoir un truc du style
Java:
WorldGuard wg = this.getPlugin("wordguard");

Pas de
Java:
Main wg = this.getPlugin("worldguard");

Cordialement,
ShE3py
Salut merci de ton retour,

Enfaite je cherche simplement a sauvegarder deux positions, (X Y Z WORLD YAW PITCH), le "Type" et un indicatif simple pour le joueur (grande petit moyenne difficile simple..) et servira aussi a d'autres choses par la suite, tout ceci est fait via des commandes, donc le staff effectue par exemple /setarena <type> p1 (puis je get la location du sender) puis il va faire la même commande pour p2(get location sender encore), une fois les deux points de spawn définit le tout doit être save dans un fichier.
Au moment souhaité je souhaite donc téléporter 2 joueurs aux Location p1 et p2. "L'arène" est sélectionnée au hasard, donc j'ai une liste d'arène, avec Math j'en prend une au hasard et les joueurs doivent y être téléporté..

Le problème étant ici la "Serialization" des données, je ne parviens pas a sauvegarder les arènes définits

Concernant le Main merci du conseil je vais effectuer le changement :)

EDIT concernant le type:

Comme expliqué plus haut je compte prendre une arène au hasard pour téléporter les joueurs, cependant si les joueurs ont décidés d'une arène "petite" alors je dois les téléporter a une arène de type "petite" parmi la liste des arènes mais je n'ai pas encore trouvé le meilleur moyen d'y parvenir

PS:

Je dev depuis quelques mois mais c'est le premier projet "d'envergure" que je tente de réaliser afin d'approfondir mes connaissances et d'évoluer rapidement désolé du spam dernièrement et merci de ton aide
 

ShE3py

Enbogueuse
Support
26 Septembre 2015
4 518
198
499
247
21
Mìlhüsa
le "Type" et un indicatif simple pour le joueur (grande petit moyenne difficile simple..)
Donc deux variable, une pour la taille et une pour la difficulté.
Java:
public enum ArenaSize {
    SMALL,
    MEDIUM,
    LARGE
}

public enum ArenaDifficulty {
    EASY,
    NORMAL,
    HARD
}

ArenaSize size = ArenaSize.MEDIUM;
ArenaDifficulty diffficulty = ArenaDifficulty.NORMAL;
Car avec une chaîne de caractères tu peux mettre ce que tu veux, ce qui est une très mauvaise idée.

Le problème étant ici la "Serialization" des données, je ne parviens pas a sauvegarder les arènes définits
Java:
public class Arena implements ConfigurationSerializable {
    public final ArenaSize size;
    public final ArenaDifficulty difficulty;
    private final Location spawnA;
    private final Location spawnB;
    
    public Arena(ArenaSize size, ArenaDifficulty difficulty, Location spawnA, Location spawnB) {
        if(size == null)                                 throw new NullPointerException("size must not be null");
        if(difficulty == null)                           throw new NullPointerException("difficulty must not be null");
        if(spawnA == null || spawnA.getWorld() == null)  throw new NullPointerException("spawnA's world must not be null");
        if(spawnB == null || spawnB.getWorld() == null)  throw new NullPointerException("spawnB's world must not be null");
        
        this.size = size;
        this.difficulty = difficulty;
        this.spawnA = spawnA;
        this.spawnB = spawnB;
    }
    
    @Override
    public Map<String, Object> serialize() {
        Map<String, Object> data = new HashMap<>();
        data.put("size", this.size.name().toLowerCase(Locale.ROOT));
        data.put("difficulty", this.difficulty.name().toLowerCase(Locale.ROOT));
        data.put("spawnA", this.spawnA); // Vu que c'est l'API de Bukkit, il sauvegarde correctement ses objets
        data.put("spawnB", this.spawnB);
        
        return data;
    }
    
    public static Arena deserialize(Map<String, Object> data) {
        ArenaSize size = ArenaSize.valueOf(((String) data.get("size")).toUpperCase(Locale.ROOT));
        ArenaDifficulty difficulty = ArenaDifficulty.valueOf(((String) data.get("difficulty")).toUpperCase(Locale.ROOT));
        Location spawnA = (Location) data.get("spawnA");
        Location spawnB = (Location) data.get("spawnB");
        
        return new Arena(size, difficulty, spawnA, spawnB);
    }
}
Java:
public class MyPlugin extends JavaPlugin {
    private Arena arena;
    
    @Override
    public void onEnable() {
        ConfigurationSerialization.registerClass(Arena.class);
        
        final Arena ifAbsentValue = new Arena(ArenaSize.MEDIUM, etc.);
        
        this.saveDefaultConfig();
        this.arena = (Arena) this.getConfig().get("arena0", ifAbsentValue);
    }
    
    @Override
    public void onDisable() {
        this.getConfig().set("arena0", this.arena);
        this.saveConfig();
    }
}
https://bukkit.fandom.com/wiki/Configuration_API_Reference

je compte prendre une arène au hasard pour téléporter les joueurs, cependant si les joueurs ont décidés d'une arène "petite" alors je dois les téléporter a une arène de type "petite" parmi la liste des arènes mais je n'ai pas encore trouvé le meilleur moyen d'y parvenir
Java:
private static final List<Arena> ARENAS = new ArrayList<>();

@UnmodifiableView
public static List<Arena> list() {
    return Collections.unmodifiableList(ARENAS);
}

public static List<Arena> filter(@Nullable ArenaSize size, @Nullable ArenaDifficulty difficulty) {
    return ARENAS.stream()
                 .filter(arena -> (size == null || arena.size == size) && (difficulty == null || arena.difficulty == difficulty))
                 .collect(Collectors.toList());
}

public static List<Arena> filterEquivalant(@Nullable ArenaSize size, @Nullable ArenaDifficulty difficulty) {
    List<Arena> result = new ArrayList<>();
    for(Arena arena : ARENAS) {
        if((size == null || arena.size == size) && (difficulty == null || arena.difficulty == difficulty)) {
            result.add(arena);
        }
    }
    
    return result;
}

List<Arena> filtered = filter(ArenaSize.LARGE, null);
Arena random = filtered.get(new Random().nextInt(filtered.size()));