Introduction to the New Configuration/de

Die Konfigurations-API ist ein Bündel von Tools die Entwicklern hilft, schnell Konfigurationsdatein zu laden und zu teilen, die von Menschen gelesen und bearbeitet werden können. Aktuell unterstüzt das Sytem nur Dateien die das YAML Schema nutzen. Mit der API können neue Formate hinzugefügt werden, da die übergeordnete API vom Schema unabhängig ist.

Diese Einführung erfordert Wissen über Java, Pluginentwicklung mit Bukkit und objektorientierte Programmierung.

## Themen

### Das Konfigurations Objekt

Dein Plugin erweitert JavaPlugin und erbt dabei dessen Methoden und Felder. Die geerbte Methode *getConfig()* gibt ein Objekt vom Typorg.bukkit.configuration.file.FileConfiguration zurück. Dieses Objekt repräsentiert die *config.yml* innerhalb deines Pluginordners.

Das erste Mal, wenn *getConfig()* innerhalb deines Plugins aufgrufen wird, wird die *config.yml* von deiner Festplatte, sowie Standardwerte aus deiner JAR, geladen. Von nun an wird *getConfig()* das *FileConfiguration* Objekt zurückgeben, dass sich im Speicher befindet. Alle Operationen werden auf dieses Objekt angewandt und beeinflussen nicht die Datei auf der Festplatte. Wenn die *config.yml* nicht existiert, wird dies mit einer leeren Datei gleichgesetzt und hat auch ein leeres *FileConfiguration* Objekt zur Folge.

 **Warnung**:  Wenn du das zurückgegebende Objekt von *getConfig(*) zuweist, weise es nicht einem statischen Feld zu

 **Warnung**:  Wenn du obiges tust, weise *getConfig()* der Variable nach dem Neuladen der Konfiguration erneut zu.

 **Note**: Es ist besser nur *getConfig()* zu nutzen, anstatt es zu speichern
### Standardwerte

Standardwerte für deine config.yml sollten für den Benutzer bereitsgestellt werden. Wenn die Konfigurationsdatei deines Pluginordners nicht existiert, leer ist, oder ihr Schlüssel fehlen, werden die Werte aus deiner JAR gelesen. Standardwerte sollten in einer YAML-Datei bereitgestellt werden, die exakt dieselbe Struktur hat, wie du es von der *config.yml* erwartest. Die Datei muss *config.yml* heißen und im selben Verzeichnis wie deine  plugin.yml liegen. Wenn du die Standardwerte kopieren und überschreiben willst, dann musst du die Methode *copyDefaultConfig()* des JavaPlugin aufrufen. Wenn du existierende Dateien nicht überschreiben möchtest, setze *copyDefaults* auf true.

Wenn du standardmäßig dynamische Werte als Standardwert verwendest, können diese mit der Methode*addDefault(String, Object)* und *addDefaults(Map<String,Object>)* hinzugefügt.

### Werte auslesen

Werte aus der Konfiguration lesen erfordert das Nutzen einer der vielen Getter-Methoden. Eine kompllete Liste aller Getter-Methoden kannst du hier finden. Ein paar typische Getter-Methoden sind die Folgenden:

* getBoolean(String)
* getInt(String)
* getString(String)
* getList(String)
* getStringList(String)

#### Schlüssel

Das Auslesen von Schlüsseln, die keine Werte darstellen, funktioniert anders als bei den anderen Getter-Methoden. Der Aufruf der Methode liefert eine Liste aller Schlüssel der aktuellen *FileConfigurationSection* zurück. Um einen speziellen Abschnitt zu erhalten benutzt du die Methode *getConfigurationSection(String)* bevor du die Methode *getKeys(boolean)* aufrufst. Der boolsche Wert legt fest, ob die zurückgelieferte Liste rekursiv ist. Bei true werden die Schlüssel und alle Kindschlüssel des Abschnittes zurückgegeben. Bei false lediglich die Schlüssel.

* getKeys(boolean)

 **Note**: Dies gibt jetzt ein Set von Strings zurück!
### Werte setzen

Werte schreiben erfordert das Aufrufen der Methode *set(String, Object)* einer Instanz von *Configuration*. Im Gegensatz zu den verschiedenen Getter-Methoden des *FileConfiguration* Objekts, gibt es nur eine Setter-Methode. Nicht alle Objekte können gesetzt werden, lediglich einfache Typen, Strings, Listen und Typen die von *ConfigurationSerializable* implementiert werden. Dazu zählen z.B. *Vector* und *ItemStack*. Um einen Wert zu löschen, setze ihn auf null. Alle Änderungen wirken sich lediglich auf die Kopie der Konfiguration im Speicher aus und sind nicht dauerhaft. Bevor du den Server neustartest, speichere die Konfiguration.

Hier ein paar Beispiele:

```
// setting a boolean value
this.getConfig().set("path.to.boolean", true);

// setting a String
String string = "Hello World!";
this.getConfig().set("path.to.string", stringValue);

// Setting a List of Strings
// The List of Strings is first defined in this array
String[] listOfStrings = {"Hello World", "Welcome to Bukkit", "Have a Good Day!"};
this.getConfig().set("path.to.list", Arrays.asList(listOfStrings);

// Setting a vector
// event is assumed to be an existing event inside an "onEvent" method.
Vector vector = event.getPlayer().getLocation().toVector();
this.getConfig().set("path.to.vector", vector);

// Erasing a value
this.getConfig().set("path.to.value", null);

```

#### HashMaps

Wenn du eine *HashMap* schreiben willst, musst du eine *ConfigurationSection* erstellen. Du kannst eine HashMap nur speichern, wenn der Schlüssel ein String und der Wert ein bereits ein *ConfigurationSerializable* Objekt ist.

```
createSection (String path, Map< String, Object > map);

```

### Die Datei speichern

Wenn du Änderungen am *FileConfiguration* Objekt vornimmst und diese behalten möchtest, musst du diese auf die Festplatte speichern. Dafür rufst du die Methode *saveConfig()* auf. Ist bereits eine Datei vorhanden wird diese überschrieben*.*

```
this.saveConfig();

```

### Von der Festplatte neuladen

Wenn du glaubst das der Nutzer die config.yml verändert hat, werden diese nicht automatisch im Speicher übernommen. Die Methode *reloadConfig()* lädt die Konfiguration erneut von der Festplatte. Diese Operation zerstört alle Änderungen im Speicher.

```
this.reloadConfig();

```

## Fortgeschrittene Themen

Nachfolgend einige fortgeschrittene Themen für fortgeschrittene Plugins.

### Optionen

Jedes *FileConfiguration*-Objekt ist mit einem FileConfigurationOptions-Objekt assoziert. Dieses kontrolliert das Verhalten der *FileConfiguration*. Die Methode *FileConfiguration.options()* gibt das verantwortliche  *FileConfigurationOptions*-Objekt zurück. Damit kannst du Optionen überprüfen und setzen. Aktuell gibt es vier Optionen. Bedenke, dass bestimmte Methoden überladen sind. *copyDefaults()* zum Beispiel gibt einen booleschen Wert zurück und *copyDefaults(boolean)* gibt self zurück, hat jedoch Nebeneffekte, die den Status verändern.

#### copyDefaults

Die *copyDefaults*-Option verändert das Verhalten der "Speichern"-Methode einer Konfiguration. Standardmäßig werden die Standardwerte nicht in die Zieldatei geschrieben. Wenn auf true gesetzt, werden auch Standardwerte in die Zieldatei geschrieben. Einmal gespeichert wirst du nicht mehr in der Lage sein, zwischen Standardwert und "echtem" Wert zu unterscheiden.

#### pathSeperator

Die Option pathSeperator verändert den Char, welcher genutzt wird, um beim Setzen des Pfades mit *addDefault(String path, Object value)* die Ebenen der Konfiguration zu abzugrenzen. Standardmäßig ist es "." (Punkt), aber dies kann zu jedem anderen Char geändert werden.

#### header

Der Header ist der Kommentarblock am Anfang der YAML-Datei. Die Option erhält den String, der in die Datei geschrieben wird. Er ist der einzige Kommentar, welcher von der Configuration API kopiert werden kann.

#### copyHeader

Wenn *copyHeader()* true zurückliefert, wird der Header beim Speichern von der Standardquelle kopiert.

### Methoden um eigene Konfigurationen zu Laden, Aktualisieren und Speichern

Wenn du zusätzliche YAML-Dateien benötigst, um Konfigurationsinfos oder statische zusätzliche Spielinformationen zu speichern, musst du eigene Methoden schreiben, um auf diese zusätzlichen Konfigurationsdateien zu greifen zu können. Den Model Prinzipien des JavaPlugin folgend, hier ein Beispiel, wie du deine eigenen Methoden zum Lesen und Schreiben eigener Konfigurationsdateien schreiben könntest. Da diese Dateien zu deinem Plugin gehören, kannst du diese Methode in deine Grund-Klasse packen. Du muss die folgenden Methoden für jede YAML-Datei schreiben. Der Vorteil besteht darin, dass du diese genauso nutzen kannst, wie die für die config.yml bereitgestellten Methoden.

Zuerst musst du zwei Felder deklarieren und diese für die benutzerdefinierte Konfiugration initialisieren. Eine um die *FileConfiguration* zu speichern und eine um die Datei zu speichern. Das File Objekt repräsentiert die Datei auf der Festplatte, die FileConfiguration repräsentiert den Inhalt der Konfiguration.

```
private FileConfiguration customConfig = null;
private File customConfigurationFile = null;

```

Dannach schreibe die Methode, die für das Laden der Konfiguration von der Platte, zu ständig ist. Diese wird die Datei laden und die JAR nach der standardmäßigen customConfig.yml durchsuchen.

```
public void reloadCustomConfig() {
 if (customConfigFile == null) {
 customConfigFile = new File(getDataFolder(), "customConfig.yml");
 }
 customConfig = YamlConfiguration.loadConfiguration(customConfigFile);

 // Schaut nach den Standardwerten in der jar
 InputStream defConfigStream = getResource("customConfig.yml");
 if (defConfigStream != null) {
 YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
 customConfig.setDefaults(defConfig);
 }
}

```

Als nächstes musst du eine Getter-Methode schreiben. Prüfe ob *customConfig* null ist, wenn sie von der Platte geladen wurde.

```
public FileConfiguration getCustomConfig() {
 if (customConfig == null) {
 reloadCustomConfig();
 }
 return customConfig;
}

```

Abschließen schreibe die Methode zum Abspeichern. Diese speichert die Änderungen und überschreibt die Datei auf der Festplatte.

```
public void saveCustomConfig() {
 if (customConfig == null || customConfigFile == null) {
 return;
 }
 try {
 customConfig.save(customConfigFile);
 } catch (IOException ex) {
 Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Konfiguration konnte nicht nach " + customConfigFile + " geschrieben werden.", ex);
 }
}

```

Nachdem du diese Methoden in die Hauptklasse deines Plugins eingefügt hast, kannst du sie auf die selbe Art und Weise nutzen, wie die geerbten Methoden *getConfig()*, *reloadConfig()* und *saveConfig()*.

### Serialisieren und Deserialisieren von Objekten

Jedes Objekt das du serialisieren möchtest muss das Interface *ConfigurationSerializable* implentieren. Serialisierte Objkete erlauben
es dem Entwickler schnell Spielinformationen zu speichern und ermöglichen das einfache Laden. Es vereinfacht stark Aufgaben, wie z.B., das Speichern einer *Location* in YAML. Ein Entwickler kann eine Wrapper-Klasse serialisieren, die Methoden bereitstellt, um eine *Location* zu laden.

 Zusätzlich zur Implementation des Interfaces musst du eines der Folgenden implementieren:

* Ein Konstruktor der eine einzelne *Map* implementiert.
* Eine statische Methode "deserialize" die eine einzelne *Map* akzeptiert und eine Klasse zurück gibt.
* Eine statische Methode "valueOf" die eine einzelne *Map* akzeptiert und eine Klasse zurück gibt.

Du musst außerdem die Klasse mit *ConfigurationSerialization* registrieren, bevor du versucht ein Objekt zu deserialisieren. Dieser Ausdruck kann in einem statischen, initialisierenden Block deiner Klasse abgelegt werden.

```
ConfigurationSerialization.registerClass(Class<? extends ConfigurationSerializable> class)

```

 **Note**: ein verzweigtes *ConfigurationSerializable Objekt* wird als Typ *Map* zurückgegeben und muss als solches gecasted werden. Weiterhin muss es an eine der Deserialisierungs-Methoden gegeben werden, wenn die Eltern-Klasse deserialisiert wird.
#### Aliase

Du kannst deiner Klasse einen Alias bereitstellen, sodass sie nicht mit dem vollwertigen Namen der Klasse serialisiert wird. Das tust do mit der *SerializeAs* Annotation in der Klasse die *ConfigurationSerializable* implementiert.

```
@SerilizeAs(String alias)

```
 Wenn du eine Klasse mit Alias registrierst, dann muss der Alias bereits zur Registrierung bereitgestellt werden.

```
ConfigurationSerialization.registerClass(Class<? extends ConfigurationSerializable> class, String alias)

```

## Beispiele

Unten findest du ein Beispiel für ein Plugin, welches die neue Konfigurations API nutzt, um Nachrichten zu speichern, die als MOTD angezeigt werden, wenn ein Spieler beitritt. Es folgt nicht dem gewünschten Stil und Layout, da es in 50 Zeilen Platz finden sollte.

```
import java.util.\*;
import org.bukkit.command.\*;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.Event.\*;
import org.bukkit.event.player.PlayerListener;

public class SimpleMOTD extends JavaPlugin {

 public void onDisable() {
 System.out.println(this.toString() + " disabled");
 }

 public void onEnable() {
 this.getServer().getPluginManager().registerEvent(Type.PLAYER\_JOIN,
 new PlayerListener() {

 @Override
 public void onPlayerJoin(PlayerJoinEvent event) {
 event.getPlayer().sendMessage(getConfig().getString("message"));
 }
 },
 Priority.Normal,
 this);

 this.getCommand("rules").setExecutor(new CommandExecutor() {

 public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
 List<String> rules = getConfig().getList("rules");
 Iterator<String> iter = rules.iterator();
 while (iter.hasNext()) {
 sender.sendMessage(iter.next());
 }
 return true;
 }
 });

 this.getConfig().options().copyDefaults(true);
 saveConfig();
 System.out.println(this.toString() + " enabled");
 }
}

```
 The default config.yml

```
# default config.yml
message: Hello World and Welcome! :)
rules:
- Play Nice
- Respect others
- Have Fun

```