In der letzten Episode haben wir gelernt, wie man auf Events horchen und reagieren kann. Heute werden wir das anwenden indem wir unser Getafix-Plugin erweitern, sodass man Spielern einen Zaubertrank verabreichen kann.
/magicpotion WorkUser
Mit diesem Kommando (das natürlich wieder nur von ops verwendet werden darf) ist der Spieler WorkUser unbesiegbar, das heißt, er verliert keine Gesundheit mehr. Natürlich soll der Sender des Kommandos eine Rückmeldung bekommen, dass er den Spieler erfolgreich Zaubertrank verabreicht hat und auch der Spieler, der den Trank bekommen hat soll eine Information bekommen.
Außerdem werden wir noch ein Kommando /listpotiondrinkers
implementieren, in welchem ops alle Spieler auflisten können, welche bereits einen Zaubertrank bekommen haben:
Wir werden uns, wie bisher auch, schön langsam an das Thema herantasten. Der Fahrplan für diese Unit ist daher so:
EntityDamageEvent
horcht./magicpotion
implementieren und dabei lernen, wie wir bequem eine Menge an Spielern abspeichern können/listpotiondrinkers
.Wir beginnen mit dem Bauen des Event-Listeners, das dir schon aus der letzten Episode bekannt ist. Ich sage dir nur den Namen des Events, damit du weißt, welchen Parameter du verwenden sollst: EntityDamageEvent
. Damit kannst du mit der Zusammenfassung für Profis aus der letzten Episode den Event-Handler aufbauen. Bau ihn einfach so, dass beim Auftreten des Events auf der Konsole eine Info “Outch, something got damaged” oder so ähnlich ausgegeben wird (Sieh nach in der Episode 2: Bukkit.getLogger().info
, falls du unsicher bist, wie du etwas in der Konsole ausgeben kannst). Falls dir bei den Namen die Ideen ausgehen, hier meine Vorschläge:
DamageListener
onPlayerDamage
Dann kannst du das Projekt gleich mal bauen und testen. Versuche heraus, wann der Handler aufgerufen wird. Der heißt nicht umsonst sehr allgemein EntityDamageEvent
.
EntityDamageEvent
Durch unsere Experimente haben wir gesehen, dass der Handler immer aufgerufen wird, sobald irgendetwas kaputt geht. Wir sind aber nur dann an einem Damage interessiert, wenn er einen Spieler betrifft. Beachte, dass es uns jetzt noch egal ist, ob der Spieler einen Zaubertrank getrunken hat oder nicht. Wir bauen sozusagen gerade lauter Obelixe ;-).
Also, wie finden wir heraus, ob der aktuelle Damage-Event sich gerade auf einen Spieler bezieht oder nicht. Als Profi wirst du schon ahnen, dass das irgendwo in unserer Rohrpost, also dem event-Objekt gespeichert ist. Die Methode getEntityType
liefert dir den Wert zurück, der dir sagt, was gerade beschädigt wurde. Wenn du in der Methode onPlayerDamage
einmal
EntityType.
eintippst (notfalls wieder mit Ctrl
+ Space
nachhelfen), dann siehst du, wieviele verschiedene Dinge (Entities) beschädigt werden können. Und wenn du dann bis zum Buchstaben P hinunterscrollst, siehst du auch den PLAYER
.
Wir handeln jetzt im Beamten-Modus: Wenns uns nix angeht, lass ma gleich die Finger davon. Also: Wenn das betroffene Entity kein PLAYER
ist, dann verlassen wir die Methode sofort. Auf Gut-Java:
public void onDamageEvent(EntityDamageEvent event) {
if (event.getEntityType() != EntityType.PLAYER) {
return;
}
Bukkit.getLogger().info("Outch, someone got damaged");
}
Das probieren wir jetzt gleich wieder aus. Also bauen und testen. Du solltest jetzt sehen, dass die Meldung nur mehr ausgegeben wird, wenn ein Spieler zu Schaden kommt.
Jetzt wird es an der Zeit zu überlegen, was wir tun können, damit wir, falls der Spieler einen Schaden erleidet, diesen wieder gut machen. Eine erste Idee könnte sein, dass wir die bereits bekannte Methode setHealth
verwenden und im Fall, dass ein Damage auftritt, einfach setHealth(20)
aufrufen. Probier das einfach mal aus. Was siehst du?
Also bei mir hat es so mittelgut funktioniert. Wenn du leichte Schäden erleidest (zum Beispiel wenn du unter Wasser keine Luft mehr bekommst und so langsam deine Herzen verschwinden)geht es einigermaßen. Aber hast du mal probiert von großer Höhe runterzuspringen? Da ist dann ganz schnell Schluss mit Lustig. Aber warum ist das so?
Da müssen wir uns schnell an eine Sache aus der letzten Episode erinnern. Ziemlich zum Schluss habe ich dir vorgestellt, wie Ereignisse auf dem Bukkit-Server ablaufen. Hier nochmals zur Erinnerung:
Das heißt, dass dein Listener immer vor dem eigentlichen Event aufgerufen wird. Das heißt auch, dass du bevor der Damage wirklich passiert, deinem Spieler die maximale Gesundheit zukommen lässt. Wenn jetzt ein wirklich schlimmer Schaden passiert, wie z. B. der Fall aus ein er großen Höhe, dann nutzt das alles nix, weil der Spieler dann mehr als 20 Punkte Gesundheit verliert.
Also brauchen wir eine andere Strategie, und die ist, dass wir das Event einfach abbrechen. Ja das geht. Wir können dem Server also sagen, dass er das Event nicht ausführen soll und das geht mit der Methode setCancelled
des Event-Objekts. Wenn wir setCancelled
mit true
aufrufen, dann bekommen zwar alle Plugins das Event noch zugesandt aber der Server selbst führt es nicht mehr aus. Das ganze sieht dann so aus:
public void onDamageEvent(EntityDamageEvent event) {
if (event.getEntityType() != EntityType.PLAYER) {
return;
}
event.setCancelled(true)
// remove line: Bukkit.getLogger().info("Outch, something got damaged");
}
Der auskommentierte Text // remove line: ...
soll heißen, dass du diese Zeile in deine Code einfach löschen kannst. Probiere dein Plugin jetzt aus. Jetzt sollten alle Spieler auf deinem Server unverwundbar sein.
Natürlich muss dein plugin.yml
vorhanden sein und der DamageListener
muss im Plugin in der Methode onEnable()
registiert sein.
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(new DamageListener(), this);
}
Jetzt können wir den Event des Damage abfangen. Das passiert aber immer und für alle Spieler. Natürlich willst du, dass nur die Spieler, welche den Zaubertrank bekommen haben, unverwundbar sind. Und das gehen wir jetzt an.
Die Spieler werden in einem TreeSet
gespeichert. Dazu fügst du in der Hauptklasse, in der du auch die Kommandos abfragst, eine Variable potionDrinkers
ein, in der die Spielernamen gespeichert werden. Außerdem muss der DamageListener
Zugriff auf das Plugin bekommen, um überprüfen zu können, ob der Spieler unverwundbar ist.
Das sieht dann so aus:
public class GetafixPlugin extends JavaPlugin {
private TreeSet<String> potionDrinkers = new TreeSet<String>();
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(new DamageListener(this), this); //Übergabe des Plugins an den DamageListener
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
...
if (label.equalsIgnoreCase("magicpotion")) {
Bukkit.getLogger().info("/magicpotion for " + player.getName() + ": successfully added to list of potion drinkers");
potionDrinkers.add(player.getName());
}
...
return true;
}
//Überprüfung, ob ein Spieler einen Zaubertrank getrunken hat.
public boolean isPotionDrinkerAllowed(String playerName) {
return potionDrinkers.contains(playerName);
}
}
Der DamageListener
sieht dann so aus:
public class DamageListener implements Listener {
private GetafixPlugin plugin;
public DamageListener(GetafixPlugin plugin) {
this.plugin = plugin;
}
@EventHandler
public void onDamageEvent(EntityDamageEvent event) {
if (event.getEntityType() != EntityType.PLAYER) {
return;
}
Player player = (Player)event.getEntity();
if (plugin.isPotionDrinkerAllowed(player.getName())) {
event.setCancelled(true);
}
}
}
Alles was du jetzt machen musst, ist ein weiteres Kommando in der plugin.yml
Datei hinzufügen, dieses Kommando in der onCommand()
Methode abfragen, und die Liste der Potion Drinkers ausgeben.
plugin.yml
## YAML Template.
---
name: Getafix
main: io.coderdojo.lisi.minecraft.firstplugin.GetafixPlugin
version: 0.1.0
author: E. Rosemann
description: A miraculous healing plugin.
commands:
gethealth:
description: Displays health level of player.
usage: /gethealth player-name
heal:
description: Brings your health to the top level.
usage: /heal me | player-name
magicpotion:
description: Adds user to the list of potion holders.
usage: /magicpotion me | player-name
listpotiondrinkers:
description: Lists all current potion holders.
usage: /listpotiondrinkers
GetafixPlugin.java
public class GetafixPlugin extends JavaPlugin {
private TreeSet<String> potionDrinkers = new TreeSet<String>();
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(new DamageListener(this), this);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
...
if (label.equalsIgnoreCase("listpotiondrinkers")) {
Bukkit.getLogger().info("/listpotiondrinkers: " + potionDrinkers.size() + " potion drinkers");
sender.sendMessage(printCurrentPotionDrinkers());
}
...
return true;
}
private String printCurrentPotionDrinkers() {
StringBuilder sb = new StringBuilder("List of potion drinkers: ");
if (potionDrinkers.size() > 0) {
for (String playerName : potionDrinkers) {
sb.append(playerName);
sb.append(" ");
}
}
return sb.toString();
}
}
GetafixPlugin
, oder du erstellst ein neues Projekt (siehe auch hier)DamageListener
und stelle sicher, dass nach dem Klassennamen implements Listener
steht.onDamageEvent
mit der Annotation @EventHandler
hinzu, mit dem event
Parameter vom Typ EntityDamageEvent
.@EventHandler
public void onDamageEvent(EntityDamageEvent event) {
if (event.getEntityType() != EntityType.PLAYER) {
return;
}
Bukkit.getLogger().info("Outch, someone got damaged");
event.setCancelled(true);
}
dist
(siehe Pfad im Output) in das Minecraft Server Plugin-Verzeichnis.reload
in die Server Konsole ein.