Scheduler Programming/de

## Scheduler Tutorial - Scheduler Erstellen und Infos.

Dies ist die deutsche Erklärung meines Schedulers Tutorials.
Ein Scheduler ist sowas wie, wie ein Countdown.

Diesen kann man aufrufen, indem man ".. void onSchedulerStart(new Runnable)", oder : "run() { }" als Methode wählt.

```
Bukkit.getScheduler().runTaskTimer(this, new Runnable() {
    @Override
    public void run() {
        /* Code */
    }
}, 20l, 20l);

```

 **Für die etwas mehr erfahrenden:**

Ein Scheduler unterscheidet sich DERMAßEN von einer "For" Schleife die Integer beibehält:

Beispiel:

```
for(int i = 0; i != 10; i++){
 /* Code */
}

```

```
for(teil1; teil2; teil3){}

```

Wird vor dem Ausführen aufgerufen. In diesem Beispiel wird die Variable i vom Typ Integer erstellt.

teil2:

Wenn dieser Wert auf false (Falsch) springt wird die for-Schleife gestoppt.

teil3:

Wird nach jedem durchlauf ausgeführt. Hier: i wird um 1 erhöht.

Alles was zwischen den geschwungenen Klammern steht {, } wird solange wiederholt, bis i nicht mehr nicht 10 entspricht ( also, bis i 10 ergibt).

Die Variable i wird gewählt, da es eine "Tradition" von Software-Entwickler ist.

**WICHTIG:**

- Schedulers sind jenachdem wann und wie man sie einsetzt rechen-aufwendig.

- Schedulers kann man PERFEKT für Countdowns nutzen.

## Wichtig

Zwei wichtige Dinge, die man beachten sollte, sind:

* Rufe keine API Methoden von anderen Threads auf (außer in ein paar Ausnahmen)
* Lasse auf keinen Fall den Haupt-Thread einfrieren bzw. schlafen

## Tutorial

Der Scheduler bietet die folgenden Optionen:

Drei Aufgabentypen:

* Verzögerte Aufgaben
* Wiederholende Aufgaben
* Aufgaben, die einen Wert zurückgeben

Diese Aufgaben können von zwei Thread-Typen ausgeführt werden

* Haupt-Server Thread (Sync - Synchron)
* -> z.B.: getScheduler#runTask
* Dedizierter Thread (Async - Asynchron)
* -> z.B.: getScheduler#runTaskAsynchronously

#### **Thread-Typen**

Die wichtigste Frage die man sich stellen muss, wenn man den Scheduler benutzt, ist welchen Thread-Typ man benutzen sollte. Den falschen zu benutzen kann schwere Probleme verursachen. Diese Probleme treten nur auf stark belasteten Servern und nicht auf einem Test-Server auf.

Der Minecraft Server nutzt viele einzelne Threads, es gibt aber nur einen Haupt-Thread und viele andere Helfer-Threads.

Auf viele Datenstrukturen kann nur von dem Haupt-Thread aus zugegriffen werden. Wenn man versucht eine davon von einem anderen Thread aus zu verändern, kann sie das Beschädigen, da dann zwei Threads gleichzeitig auf sie zugreifen. (Es wird meistens sowieso ein Fehler geworfen sollte man solche Methoden aufrufen (Mach es trotzdem nicht))

Der Haupt-Thread benützt ein Tick-System. Es tickt 20 Mal pro Sekunde und schläft dann für den Rest des Taktzyklus.

Nur sehr wenige Bukkit API Aufrufe sind Thread-sicher. Sofern man nicht überprüft hat, dass der API-Aufruf den man ausführt Thread-sicher ist, ist es das Beste anzunehmen, dass er es nicht ist.

#### **Synchrone Aufgaben**

Das sind Aufgaben, die von Haupt-Thread ausgeführt werden.

Wichtig ist: Wenn so ein Thread stoppt wird der ganze Server eingefroren.

Der Haupt-Thread KANN genutzt werden für

* Scheduler Methoden-Aufrufe
	+ Vorsicht, wenn es um zukünftige Objekte geht, die durch den Scheduler zurückgegeben werden (siehe unten)

* Andere Aufgaben, die nichts blockieren

 Der Haupt-Thread MUSS genutzt werden für

* Aufgaben, die Bukkit Methoden aufrufen  (außer Thread-sichere Methoden, siehe unten)

 Der Haupt-Thread DARF NICHT genutzt werden für

* Aufgaben, die den aktuellen Thread einfrieren bzw. schlafen lassen

	+ Dazu gehören Aufgaben, die blockieren. z.B. Netzwerk Leseaufgaben

 Es ist Ok, mit dem Haupt-Thread zu synchronisieren, aber nur, wenn man sicher ist, dass es nur für sehr Zeit kurze blockiert.

#### Asynchrone Aufgaben

Diese Aufgaben werden von anderen Threads als dem Haupt-Thread ausgeführt. Diese Aufgaben können gestoppt werden ohne dass Probleme auftreten, da der Thread für die Aufgabe da ist. Die Regeln für diese Threads sind genau das Gegenteil, von denen der synchronen Aufgaben.

 Ein asynchroner Thread KANN genutzt werden für

* Scheduler Methoden Aufrufe
* Andere Aufgaben, die nicht auf das Plugin API zugreifen

 Ein asynchroner Thread MUSS genutzt werden für

* Aufgaben, die den aktuellen Thread einfrieren bzw. schlafen lassen
	+ Dazu gehören Aufgaben, die blockieren. z.B. Netzwerk Leseaufgaben

Ein asynchroner Thread DARF NICHT genutzt werden für

* Aufgaben, die das Bukkit API aufrufen (außer Thread-sichere Methoden, siehe unten)

## Aufgaben

Es gibt 3 Arten von Aufgaben: Sich wiederholdende (Repeating Task), verzögerte (Delayed Tasks) und Aufgaben, die einen Wert zurückgeben.

#### Verzögerte Aufgaben

Diese Aufgaben können genutzt werden, um etwas nach einer gewissen Dauer zu tun. Diese Verzögerung kann auch auf 0 gesetzt werden.

###### Beispiel

Dies ist ein synchroner Thread, der nach 3 Sekunden eine Aufgabe ausführt.

```
myPlugin.getServer().getScheduler().scheduleSyncDelayedTask(myPlugin, new Runnable() {

public void run() {
getServer().broadcastMessage("Diese Nachricht wird nach 3 Sekunden gesendet!");
}
}, 20*3L);

```

Die Verzögerung beträgt 60 und sie wird in Ticks gezählt. Es gibt 20 Ticks pro Sekunde, also beträgt die Verzögerung 60/20 = 3 Sekunden.

Dieser Code kann von einem anderen Thread ausgeführt werden. Wenn der Code

```
 getServer().broadcastMessage("Diese Nachricht wird von einem asynchronen Thread übertragen");

```

von einem anderen Thread ausgeführt wurde, könnte das zu Problemen führen, da die broadcastMessage Methode von einem anderen Thread ausgeführt wird.

#### Wiederholende Aufgaben

Aufgaben können wiederholt ausgeführt werden. Auch hier muss eine Verzögerung gesetzt werden.

###### Beispiel

Dies ist ein asynchroner Thread, der nach einer initialen Verzögerung von 3 Sekunden, alle 10 Sekunden eine Nachricht ausgibt.

```
myPlugin.getServer().getScheduler().scheduleAsyncRepeatingTask(myPlugin, new Runnable() {

public void run() {
System.out.println("Ich bin eine Nachricht.");
}
}, 20*3L, 20*10);

```

Beide Werte werden in Ticks gezählt. Die initiale Verzögerung beträgt 60/20 = 3, also 3 Sekunden und das Intervall beträgt 200/20 = 10, also 10 Sekunden.

Der obere Code gibt alle 10 Sekunden die Nachricht "Ich bin eine Nachricht." aus.

#### Aufgaben, die einen Wert zurückgeben

Es ist möglich Aufgaben zu erstellen, die von dem Scheduler einen Wert zurückgegeben bekommen. Das funktioniert allerdings nur durch den Haupt-Thread.

Der Zweck dieses Systems ist es, API-Methoden, die Werte, die von anderen Threads aufgerufen werden zurückgeben zu können.

Man benutzt dazu ein "Zukunfts"-Objekt, das benutzt wird, damit man ein Ergebnis erhalten kann.

###### Beispiel

```
Future<String> returnFuture = myPlugin.getServer().getScheduler().callSyncMethod(myPlugin, new Callable<String>() {

public String call() {
return "Dieser String wird zurückgegeben!";
}

});

try {
//Das wird den aktuellen Thread blockieren
String returnValue = returnFuture.get();
System.out.println(returnValue);
} catch (InterruptedException e) {
System.out.println("Es wird noch ein Wert zum zurückgeben benötigt!");
} catch (ExecutionException e) {
System.out.println("Es gab eine unerwartete Ausnahme:");
ee.getCause().printStackTrace();
}

```

Dies gibt den Wert mit der Methode Future.get() zurück. Diese Methode schläft, bis es einen Wert zum zurückgeben gibt.

###### Nutzung von dem Haupt-Thread aus

Das ist **NICHT** empfohlen.

Diese Methode ist nicht dazu da, im Haupt-Thread benutzt zu werden.

Wenn man die .get() Methode von dem "Zukunfts"-Objekt benutzt, schläft der aktuelle Thread ein (also der Haupt-Thread), bis der Haupt-Thread die Aufgabe erfüllt hat.

Wenn mal unbedingt der Haupt-Thread dafür benutzt werden muss, MUSS man die .isDone() Methode abchecken, bevor man die .get() Methode nutzt. Ansonsten wird der ganze Haupt-Thread schlafen, bis die Aufgabe erledigt wurde (von dem Haupt-Thread).

## Thread-sichere API Methoden

Bukkit API Methoden, die Thread-sicher sind, sind:

* Alle Scheduler Methoden
* Bukkit.getServer()
* Server.getBukkitVersion()
* World.getUID()
* World.getMaxHeight()
* World.getSeed()
* World.getBlockTypeIdAt(int x, int y, int z)
* Entity.getEntityId()
* Entity.getUniqueId()
* Player.sendMessage()
	+ Diese Liste ist noch nicht fertig.