1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen
  2. Hallo Gast,

    um unseren Lesern und Nutzern das best mögliche Seitenerlebnis bieten zu können nutzen wir Google Analytics.
    Um mehr über unsere Nutzung von Google Analytics zu erfahren, kannst du in unserer Datenschutzerklärung unter dem Punkt "Datenschutzerklärung für die Nutzung von Google Analytics" mehr erfahren und, wenn gewünscht, die Nutzung von Google Analytics via Klick auf den Link "Google Analytics deaktivieren" unterbinden.

    Solltest du diese Benachrichtigung ausblenden, akzeptierst du unsere Nutzung von Google Analytics. (Das Deaktivieren steht dir allerdings immer zur Verfügung)
    Information ausblenden

Tutorial Verwendung von ConfigurationSerializable

Dieses Thema im Forum "Minecraft" wurde erstellt von SirYwell, 26. Januar 2018.

Schlagworte:
  1. SirYwell

    SirYwell Moderator Moderator Spender

    Registriert seit:
    2. November 2017
    Beiträge:
    5
    Zustimmungen:
    5
    Geschlecht:
    männlich
    Warum gibt es dieses Tutorial?
    Ich habe mich entschlossen, dieses Tutorial zu erstellen, da ich oft sehe, dass Bukkit/Spigot-Plugins irgendwo eine Location in eine YAML-Konfigurationsdatei schreiben oder herauslesen. Meist wird dabei wie folgt vorgegangen:
    Code:
    public Location loadLocation(String path) {
        World world = Bukkit.getWorld(getConfig().getString(path + ".world"));
        int x = getConfig().getInt(path + ".x");
        int y = getConfig().getInt(path + ".y");
        int z = getConfig().getInt(path + ".z");
        return new Location(world, x, y, z);
    }
    
    public void saveLocation(Location location, String path) {
        getConfig().set(path + ".world", location.getWorld().getName());
        getConfig().set(path + ".x", location.getBlockX());
        getConfig().set(path + ".y", location.getBlockY());
        getConfig().set(path + ".z", location.getBlockZ());
        saveConfig();
    }
    
    Sieht soweit richtig aus, funktioniert auch gut. Allerdings stellt uns Bukkit etwas bereit, was uns hier eine Menge Arbeit abnimmt: Das Interface ConfigurationSerializable. Objekte, die von diesem Interface erben, kann man nämlich direkt in die Konfiguration schreiben bzw. auslesen.

    Aber was ist die Alternative?
    Mehrere Klassen von Bukkit sind Subklassen oder Subinterfaces von ConfigurationSerialiable. Eine Liste findet ihr in den oben verlinkten Javadocs zu dem Interface. Wie ihr mit diesen Klassen umgeht, zeige ich euch hier:
    Code:
    public Location loadLocation(String path) {
        return (Location) getConfig().get(path);
    }
    
    public void saveLocation(Location location, String path) {
        getConfig().set(path, location);
        saveConfig();
    }
    
    Diese Methoden erfüllen die gleiche Aufgabe. Zugegeben, im ersten Codeabschnitt speichern wir nur Integer-Werte für x, y und z und mit der zweiten Variante speichern wir diese als Double-Werte und außerdem zwei Float-Werte für pitch und yaw. Hierbei kommt es aber auch vollkommen darauf an, wie man natürlich das Location-Objekt an die Konfiguration übergibt.
    Beispielsweise könnten die Unterschiede so in der gepeicherten YAML-Datei aussehen:
    Code:
    serialized:
      ==: org.bukkit.Location
      world: world
      x: -123.48872766645316
      y: 90.0
      z: 567.1446789517951
      pitch: 22.50058
      yaw: 82.34863
    not-serialized:
      world: world
      x: -124
      y: 90
      z: 567
    
    Abgesehen davon lassen sich aber auch Objekte wie ItemStacks und vieles mehr auf diese einfache Art speichern. Für ItemStacks stellt die Konfiguration sogar eine Methode bereit, damit man nicht casten muss:
    Code:
    ItemStack itemStack = getConfig().getItemStack(path);
    
    (Fragt mich nicht, warum es das nicht für Locations gibt.)

    Was kann man damit noch so anstellen?
    Nun wollen wir einen Schritt weitergehen und unsere eigene Klasse erstellen, die genauso praktisch verwendet werden kann:

    Code:
    @SerializableAs("example")
    public class MySerializableObject implements ConfigurationSerializable {
     
        private final int number;
        private final String name;
        private final List<String> list;
    
        public MySerializableObject(int number, String name, List<String> list) {
            this.number = number;
            this.name = name;
            this.list = list;
        }
    
        @Override
        public Map<String, Object> serialize() {
            Map<String, Object> result = new LinkedHashMap<>();
            result.put("number", this.number);
            result.put("name", this.name);
            result.put("list", this.list);
            return result;
        }
    }
    
    Die Annotation @SerializableAs definiert ein Alias, unter welchem diese Klasse in der Konfiguration gespeichert werden soll. Später mehr noch dazu.

    Die Methode, die wir überschreiben müssen, gibt eine Map zurück, die alle Werte mit einem von uns zugegebenen Key beinhaltet, die abgespeichert werden sollen.

    Theoretisch könnten wir ein solches Objekt nun schon speichern, allerdings würde das bei einem erneuten Laden der Konfigurationsdatei eine Fehlermeldung generieren. Bukkit muss nämlich erst wissen, wie es dieses Objekt wieder aus der Datei auslesen soll. Hierbei gibt es zwei mögliche Vorgehensweisen.
    Konstruktor:
    Code:
    public MySerializableObject(Map<String, Object> result) {
        this.number = (int) result.get("number");
        this.name = (String) result.get("name");
        this.list = (List<String>) result.get("list");
    }
    
    Wenn die Klasse einen Konstruktor enthält, der nur den Parameter Map<String, Object> enthält, kann Bukkit diesen verwenden.
    Methode:
    Code:
    public static MySerializableObject deserialize(Map<String, Object> result) {
        int number = (int) result.get("number");
        String name = (String) result.get("name");
        List<String> list = (List<String>) result.get("list");
        return new MySerializableObject(number, name, list);
    }
    

    Alternativ kann Bukkit auch die Methode public static YourObject valueOf(Map<String, Object> result)
    verwenden, um ein Objekt aus der Datei zu lesen. Die Funktionsweise bleibt dabei gleich.
    Ein letzter Schritt ist jedoch noch notwendig, damit Bukkit wirklich etwas mit der Klasse anfangen kann:
    Beim Aktivieren des Plugins, bzw vor dem ersten Zugriff auf die Datei, muss die Klasse noch registriert werden. Dies geht wie folgt:
    Code:
    ConfigurationSerialization.registerClass(MySerializableObject.class, "example");
    
    Hier taucht auch wieder unser Alias auf, welches Bukkit benötigt, um von diesem aus wieder die dazugehörige Klasse zu finden.
    Beachte: ConfigurationSerialization ist nicht das Interface, das wir zuvor verwendet haben ;)

    Endergebnis
    Wenn wir nun ein beispielhaftes Objekt erstellen und in eine YAML-Datei speichern, sieht das wie folgt aus:
    Code:
    my-own-object:
      ==: example
      number: 1337
      name: This is CONFIGURATIONSERIALIZABLE
      list:
      - One
      - Two
      - Three
    
    Ich hoffe, ich konnte dem ein oder anderen damit weiterhelfen und viel Arbeit ersparen.
     
    MBK, GasGas, RiotSeb und einer weiteren Person gefällt das.