Plugin Tutorial/pl

# Wprowadzenie

Ten raczej bardzo duży poradnik ma na celu wprowadzenie Cię w tajniki programowania dla Bukkit'a. Nie jest to kompletny poradnik wszelkich możliwości jakie daje Bukkit, lecz raczej mały przegląd podstawowych funkcji. Zaczyna się od sprawdzenia Twojej znajomości Javy, następnie przechodzi do konfiguracji IDE, a skończy się na zasadniczych funkcjach programowania pluginów.

# Nauka Javy

*Poradniki w dalszej części artykułu wymagają podstawowej znajomości Javy, jako języka programowania.* Jeśli nie masz tej znajomości, lub wiesz zbyt mało, polecam Ci sprawdzenie poradników poniżej, mogą Cię wiele nauczyć!

### Poradniki wideo

* iTechRemix - bardzo podstawowe poradniki pisania pluginów.
* Thenweboston - wiele świetnych filmików.
* JavaVideoTutes.com - pokrywają wiele tematów.

### Poradniki pisane

* Dokumentacja Oracle (firma aktualnie zajmująca się Javą)
* Java2s.com - chyba prawie wszystko co można zrobić z Javą
* Java 101 - dogłębne pisemnne poradniki
* JavaStart.pl - bardzo fajna polska stronka z kursem Javy. Spora część artykułów ma nagrane filmy.

# Środowisko Deweloperskie

Przed rozpoczęciem pisania pluginów (lub nauką Javy) będziesz musiał/a skonfigurować swoje środowisko. Oznacza to, ale się tylko do tego nie ogranicza, instalację IDE. Ten poradnik będzie opisany na podstawie Eclipse.

*Więcej informacji o IDE jest w Artykule Przystosowanie miejsca pracy*
# Tworzenie nowego projektu

### Stwórz projekt

Zanim zaczniesz, będziesz musiał/a ustawić swoją przestrzeń roboczą i dodatkowe pliki w Eclipse. Uruchom IDE, następnie utwórz nowy projekt poprzez wybranie *File > New > Java Project:*

Nazwij projekt jak chcesz, następnie przebrnij przez kreatora, wykonując wskazówki wyświetlane na ekranie. Nowy folder pojawi się w Panelu Eksploratora po lewej; kliknij LPM na małej strzałce koło niego aby wyświetlić zawartość Twojego projektu.

### Dodaj Bukkit API

Zanim zaczniesz pisać swój plugin musisz zaimportować Bukkit API do swojego projektu jako zewnętrzny plik JAR, możesz w ten sposób dodawać jakiekolwiek inne API, które chcesz wykorzystać w swoim pluginie.

Najnowsza kompilacja Bukkit API może zostać pobrana stąd: Bukkit API - wersja rozwojowa

Kliknij PPM na folder z nazwą Twojego projektu w Eklsploratorze na panelu po lewej stronie ekranu i wybierz *Properties*. Kliknij na *Java Build Path* na liście opcji po lewej i okno na środku powinno teraz wyglądać podobnie do tego poniżej:

Kliknij na *Add External JARs* i przejdź do lokalizacji, do której wcześniej pobrałeś/aś Bukkit API. Po tym kroku nasze okna powinny wyglądać identycznie.

### Dokumentacja Bukkita

Jeśli masz trochę doświadczenia z Eclipse oraz Javą, zapewnie wiesz, że po przytrzymaniu wskaźnika myszki nad jakąś klasą lub metodą, pojawi się żółte okienko zawierające fragment dokumentacji dotyczący tej klasy/metody. Znane jest to powszechnie pod nazwą Javadocs i jest dostępne online także na stronie Oracle. Bukkit także ma swoją dokumentację, która często zawiera użyteczne opisy metod i klas dostarczanych przez API, dostępną pod tym adresem. Aby te informacje były dostępne także w Eclipse w opisany wcześniej sposób, kliknij na *"Bukkit.jar"* (plik dodany w poprzednim kroku) w zakładce *"Referenced Libraries"* w Eksploratorze i kliknij *"Properties"*. Wybierz zakładkę *"Javadoc Location"* po lewej i wklej tam ten adres:

```
http://jd.bukkit.org/apidocs/

```

Gdy to zrobisz, całe okienko powinno wyglądać tak:

Jeśli tak jest, kliknij na *"Validate"*, a następnie na *"OK"*. Zrobione! Dokumentacja Bukkita jest teraz podłączona do Eclipse i możesz ją przeglądać bezpośredni w oknie swojego IDE.

### Tworzenie pakietu

Kolejnym krokiem na naszej drodze tworzenia własnego pluginu jest utworzenie pakietu (ang. *package*). Będzie on przechowywał wszystkie pliki klas Java których będziemy używać. Kliknij PPM na folder w Eksploratorze podpisany *"src"* i wybierz *New > Package*:

Nazwij swój pakiet w następujący sposób:

* Jeśli masz swoją domenę, nazwij pakiet odwróconą domeną.
	+ Na przykład: *jestem-deweloperem-bukkit.com*, to nazwij pakiet tak: *com.jestem\_deweloperem\_bukki*t (źródło).
	+ Unikaj posługiwania się domeną, właścicielem której nie jesteś.
* Nie masz domeny? Tu masz kilka popularnych rozwiązań tej sytuacji:
	1. Stwórz konto na stronie do zarządzania kodem źródłowym, np. Git Hub lub SourceForge
		+ Dla GitHuba, postępuj za wskazówkami opisanymi tutaj w celu uzyskania własnej subdomeny. Twój pakiet będzie się wtedy nazywał *com.github.<nazwa\_subdomeny>*
	2. Użyj swojego adresu email. Np. *<nazwa\_użytkownika>@gmail.com* będzie jako *com.gmail.<nazwa\_użytkownika>*
	3. To ostatnie wyjście. Nazwij pakiet jak chcesz, lecz, ponownie, niech będzie to **ostatnie wyjście**.

Istnieje jednak kilka rzeczy, od których Twój pakiet **nie może** się zaczynać. Są to:

* org.bukkit
* net.bukkit
* com.bukkit
* net.minecraft

Teraz, gdy nasz pakiet ma już swoją bazową nazwę, zakończ go nazwą swojego pluginu. Pokażmy to na przykładzie GitHuba. Jeśli utworzymy plugin *"TestPlugin"*, pełna nazwa pakietu powinna wyglądać tak: *"com.github.<nazwa\_subdomeny>.testplugin"*

### Tworzenie klasy głównej pluginu

Teraz, gdy nasz projekt jest już ustawiony, możemy zacząć dodawać pliki klas i zacząć tworzyć właściwą część pluginu. Główną klasą pluginu jest ta, która rozszerza klasę JavaPlugin. Niezależnie od rodzaju pluginu, tylko *jedna* klasa może rozszerzać klasę JavaPlugin bezpośrednio lub pośrednio. Dobrą praktyką jest utworzenie głównej klasy jako pierwszej i nazwanie jej tak samo jak plugin. Kliknij PPM na pakiecie który wcześniej utworzyliśmy i wybierz *New > Class*. Powinieneś/aś mieć nową klasę podobną do tej:

```
package {$TopLevelDomain}.{$Domain}.{$PluginName};

import org.bukkit.plugin.java.JavaPlugin;

public final class {$PluginName} extends JavaPlugin { }
```

**Uwaga**: Pluginy nigdy nie powinny odwoływać się do swojego konstruktora ani tworzyć nowych instancji

### Tworzenie pliku plugin.yml

Mamy już gotowy projekt oraz główną klasę naszego pluginu. Do uruchomienia pluginu wymagany jest jeszcze plik *plugin.yml*. Plik ten będzie zawierał zasadnicze informacje o pluginie i bez niego plugin NIE będzie działał. Kliknij PPM tym razem na projekt, NIE *"src"*. Wybierz *New > File*. Nazwij plik *plugin.yml* i kliknij *OK*. Eclipse otworzy teraz ten plik w domyślnym systemowym edytorze tekstu (z zasady jest to Notatnik). *(Wskazówka: Jeśli chcesz zachować porządek w swojej przestrzeni roboczej, zamknij edytor i przeciągnij plik plugin.yml z Eksploratora do głównego okna Eclipse, dzięki czemu będziesz mógł edytować go bezpośrednio w Eclipse)*

Istnieją trzy podstawowe atrybuty, które muszą zostać zadeklarowane w pliku *plugin.yml*.

`name:` prosta nazwa pluginu.

`main:` pełna nazwa głównej klasy pluginu.

`version:` wersja pluginu.
Najprostszy plik *plugin.yml* będzie więc wyglądał następująco:

```
name: {$NazwaPluginu}
main: {$NazwaPakietu}.{$GłównaKlasa}
version: {$NumerWersji}
```

 **Note**: Nazwa pakietu często zawiera nazwę pluginu, więc nie zdziw się jeśli zobaczysz <NazwaPluginu>.<NazwaPluginu> na końcu drugiej linii!
 **Note**: Klasa główna może, ale nie musi nazywać się tak samo jak plugin, zależy to od tego, jak ją wcześniej nazwałeś/aś. Pamiętaj, że wielkość znaków ma znaczenie.
***Więcej przykładów*** znajdziesz tutaj: #Przykładowe\_pliki\_i\_szablony

Na tym etapie Twój plugin może zostać wczytany przez Bukkita i będą temu towarzyszyły wiadomości w konsoli. Lecz pamiętaj, że to, jak na razie, szczyt możliwości Twojego pluginu!

# onEnable() oraz onDisable()

Te metody są używane kiedy twój plugin jest włączany lub wyłączany. Domyślnie plugin automatycznie się włączy przy wczytywaniu serwera żebyś mógł zarejestrować zdarzenia i wyświetlić kilka przydatnych informacji. onEnable() uruchamia się kiedy plugin jest włączany i powinien zawierać wartość logiczną konfigurującą plugin gdy jest włączony. onDisable() uruchamia się kiedy plugin jest wyłączany i powinien zawierać wartość logiczną czyszczącą dane pluginu. Dodatkowo pluginy mogą nadpisywać metodę onLoad() w celu wykonania innych czynności.

### Wprowadzenie do onEnable() i onDisable()

Utwórz onEnable() i onDisable() w głównej klasie. Powinny wyglądać podobnie jak to:

```
package {$TopLevelDomain}.{$Domain}.{$PluginName};

import org.bukkit.plugin.java.JavaPlugin;

public final class {$PluginName} extends JavaPlugin {

    @Override
    public void onEnable(){
        // Wprowadź tutaj kod, który ma zostać wykonany przy włączeniu się pluginu
    }

    @Override
    public void onDisable() {
        // Wprowadź tutaj kod, który ma zostać wykonany przy wyłączeniu się pluginu
    }
}
```

Metody zostały już utworzone, ale jak na razie nic nie robią.

 **Note**: Nie ma powodu wyświetlać wiadomości typu"{$PluginName} został włączony!" ponieważ bukkit zrobi to sam.
### Rejestrowanie wiadomości

Plugin może wysłać wiadomość do konsoli. Można to zrobić odwołując się do obiektu Logger. Jako, że nasza klasa dziedziczy po klasie JavaPlugin, możemy użyć metody zwracającej tenże obiekt poprzez *this.getLogger()*
Zapiszemy w logu wiadomość kiedy metoda onEnable() się uruchomi. Możemy to zrobić przepisując poniższy kod.

```
this.getLogger().info("Rozpoczynam wlaczanie pluginu..");
```

To samo można zrobić w onDisable(), upewniając się żeby zmienić treść wiadomości.

Twoja główna klasa powinna teraz wyglądać następująco:

```
package {$TopLevelDomain}.{$Domain}.{$PluginName};

import org.bukkit.plugin.java.JavaPlugin;

public final class {$PluginName} extends JavaPlugin {

 public void onEnable() {
 this.getLogger().log(Level.INFO, "Wlaczanie pluginu..");
 }

 public void onDisable() {
 this.getLogger().log(Level.INFO, "Wylaczanie pluginu..");
 }
}

```
Jest jeszcze kilka innych sposobów na wysłanie wiadomości do konsoli. Można to zrobić poprzez metodę z czystej Javy, której nie zaleca się używać, albo poprzez jedną z metod obiektu ConsoleCommandSender.

Przykładowa linijka kodu przy użyciu czystej Javy wygląda mniej więcej tak:

```
System.out.println("Wlaczanie pluginu..");
```

Używając następnego sposobu kod powinien wyglądać tak:

```
Bukkit.getConsoleSender().sendMessage("Wlaczanie pluginu..");

```

# Detektory

Detektory (ang. *listeners*) to klasy, których metody są uaktywniane poprzez zdarzenie (ang. *event*). Wszystkie detektory implementują *org.bukkit.event.Listener*.

*Więcej informacji o detektorach znajdziesz w artykule: Dokumentacja API zdarzeń*
# Komendy

### Metoda onCommand()

A więc wiesz już jak rejestrować zdarzenia oraz robić *coś* gdy mają one miejsce, lecz co, gdy chcesz aby *coś* się działo tylko po wpisaniu komendy? Używamy metod `onCommand`. Kod ten jest wykonywany za każdym razem, gdy gracz wpisze komendę poprzedzoną znakiem */*. Np. wpisanie *"/wykonaj cos"* wywołałoby metodę `onCommand`. W tym wypadku nic by się nie stało, ponieważ żadna reakcja nie została zaprogramowana.

Unikaj używanie nazw komend takich samych jak te zawarte domyślnie w Bukkicie oraz rozważ jak unikalna jest Twoja nazwa. Np. komenda *"give"* już jest wykorzystywana przez kilka pluginów i jeśli i Ty ją zaimplementujesz, Twój plugin stanie się niekompatybilny z pozostałymi pluginami także korzystającymi z komendy *"give"*. Musisz zarejestrować komendy w pliku *plugin.yml*, ponieważ bez tego metoda nie zostanie aktywowana.

Metoda `onCommand` musi zawsze zwracać wartość typu *boolean* - albo *true*, albo *false*. Jeśli zwracane jest *true*, nie zauważysz nic szczególnego. Jeśli zwrócone zostanie *false* wtedy plugin przejdzie do plików i wyświetli użytkownikowi wiadomość *`usage: wartość`*, gdzie wartość to tekst wpisany przez Ciebie w pliku *plugin.yml*.

Przy używaniu metody `onCommand` powinieneś/aś zawsze zarejestrować te cztery parametry:

* `CommandSender sender` - kto użył komendy
* `Command cmd` - wykonana komenda
* `String commandLabel` - wywołany alias komendy
* `String[] args` - tablica dodatkowych argumentów, np. wpisanie */witaj abc def* umieści *abc* w *args[0]*, a *def* w *args[1]*

#### Ustawianie komendy

```
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){
    	if(cmd.getName().equalsIgnoreCase("basic")){ // Jeśli gracz wpisał /basic, wykonaj to...
    		//wykonaj Coś
    		return true;
    	} //Jeśli Coś się stało, funkcja zwróci wartość 'true'.
            //Jeśli Coś się nie stało, funkcja zwróci wartość 'false'.
    	return false;
    }

```

Podczas pisania funkcji `onCommand` dobrą praktyką jest zwracanie wartości *"false"* na samym końcu. Zwrócenie *"false"* poskutkuje wyświetleniem informacji o użyciu komendy (*`usage:`* w pliku *plugin.yml*). Dzięki temu, jeśli cokolwiek pójdzie nie tak, wiadomość pomocy zostanie wyświetlona. Po zwróceniu wartości funkcja się zakończy, więc jeśli zwrócisz *"true"*, kod wpisany poniżej nie wykona się dopóki stwierdzenie *return* nie jest zagnieżdżone w *if* lub podobnym.

*`.equalsIgnoreCase("basic")`* oznacza, że wielkość znaków w komendzie nie będzie miała znaczenia, np. wyrażenie *"BaSic"* i *"bASiC"* znaczą *"basic"* i ten sam kod zostanie wykonany.

### Dodawanie komendy do pliku plugin.yml

Będziesz też potrzebował dodać swoje komendy do pliku *plugin.yml*. Dodaj następujący kod na końcu *plugin.yml*:

```
      description: To jest opis testowej komendy.
      usage: /<komenda> [gracz]
      permission: <nazwaPluginu>.basic
      permission-message: You don't have <permission>
```

* `basic` - nazwa komendy.
* `description` - opis komendy.
* `usage` - wiadomość jaka ukaże się jeśli onCommand zwróci wartość *false*.
* `permission` - uprawnienie wymagane do wykonania komendy.
* `permission-message` - ta wiadomość się pojawi kiedy gracz wykona komendę, lecz nie ma wystarczających uprawnień (ang. *permission*)

Zauważ że w pliku yml tabulatory występują jako dwie spacje, a używanie "tab" może spowodować błędy.

### Komendy konsolowe vs. te w grze

Na pewno zauważyłeś wcześniej parametr CommandSender sender. CommandSender jest częścią Bukkita która zawiera dwie użyteczne(dla programistów pluginów) podklasy: Player i ConsoleCommandSender.
Kiedy piszesz swój plugin, bardzo dobrym pomysłem jest żeby zdecydować które komendy mogą być uruchamiane z konsoli, a które tylko przez gracza.
Można to zrobić tak:

```
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
	if (cmd.getName().equalsIgnoreCase("basic")){ // If the player typed /basic then do the following...
		// do something...
		return true;
	} else if (cmd.getName().equalsIgnoreCase("basic2")) {
		if (!(sender instanceof Player)) {
			sender.sendMessage("This command can only be run by a player.");
		} else {
			Player player = (Player) sender;
			// do something
		}
		return true;
	}
	return false;
}
```

W tym przykładzie komenda *basic* może zostać uruchomiona przez każdego - zalogowanego gracza, operatora, a nawet z konsoli. Komenda *basic2* zaś może zostać uruchomiona tylko przez gracza na serwerze.
Zazwyczaj, powinieneś pozwalać większości komend działać i z konsoli, i dla graczy. Komendy które potrzebują zalogowanego gracza mogą używać mechanizmu z przykładu powyżej, czyli sprawdzać czy CommandSender jest graczem przed przejściem do wykonywania swojego kodu. Niektóre komendy są dostępne tylko dla graczy z powodu wymaganych argumentów, na przykład teleportacja potrzebuje gracza do teleportacji, a komenda dająca przedmioty potrzebuje gracza któremu da przedmiot (tylko jeżeli komenda jest wykonywana dla siebie samego).

Jeżeli chcesz być bardziej zaawansowany, możesz użyć więcej warunków do sprawdzenia podczas uruchamiania komendy, na przykład teleportacja może zostać użyta z z konsoli jeżeli nick gracza też jest podany.

### Osobna klasa wykonująca komendy

Przykłady wcześniej umieszczają metodę onCommand() w głównej klasie pluginu. Dla małych pluginów jest dobrze, lecz jeżeli piszesz plugin bardziej rozwinięty najlepiej jest wstawić metodę onCommand() we własnej klasie. Na szczęście nie jest to nic trudnego:

* Utwórz nową klase, nazwij ją na przykład MyPluginCommandExecutor (oczywiście zmień "MyPlugin" na nazwę twojego pluginu). Ta klasa musi zawierać interfejs Bukkita o nazwie CommandExecutor.
* W głównej klasie w metodzie onEnable() musisz utworzyć instancję twojej klasy onCommand(), a wygląda to tak:
```
getCommand("basic").setExecutor(myExecutor);
```
, gdzie "basic" to komenda którą chcemy użyć a "myExecutor" to instancja którą utworzyliśmy.

Najlepiej można to zobaczyć na przykładzie:
MyPlugin.java (główna klasa):

```
@Override
public void onEnable() {
	// ...

	// Zwróci NullPointException jeżeli nie masz zdefiniowanej komendy w twoim pliku plugin.yml!
	getCommand("basic").setExecutor(new MyPluginCommandExecutor(this));

	// ...
}
```

MyPluginCommandExecutor.java:

```
public class MyPluginCommandExecutor implements CommandExecutor {

	private MyPlugin plugin; // wskaźnik twojej głównej klasy, nie przydatne jeżeli nie chcesz używać metod z głównej klasy

	public MyPluginCommandExecutor(MyPlugin plugin) {
		this.plugin = plugin;
	}

	@Override
	public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
		// implementacja komend jak w sekcji wcześniej
	}
}
```

Zauważ jak przyłączyliśmy instancję głównej klasy do konstruktora dla MyPluginCommandExecutor. Umożliwia to łatwy dostęp do metod z głównej klasy jeżeli tego potrzebujemy.
Robiąc to wszystko, lepiej zorganizujemy swój kod - jezeli metoda onCommand() jest duża i rozwinięta możemy rozdzielić ją na pod metody bez zaśmiecania głównej klasy twojego pluginu.
Zauważ że jeżeli twój plugin ma więcej komand, to będziesz musiał utworzyć więcej instancji "command executor" dla każdej komedndy.

### Bezpieczna metoda onCommand

Kiedy piszesz metodę `onCommand`, ważne jest abyś nie zakładał żadnej informacji, np. że wysyłający komendę jest graczem. Ważne rzeczy warte zapamiętania:

#### Upewnij się, że wysyłający jest graczem

Z pomocą poniższego kodu jest to możliwe:

```
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){
    	if (sender instanceof Player) {
               Player player = (Player) sender;
               // do something
            } else {
               sender.sendMessage("You must be a player!");
               return false;
            }
            // do something
            return false;
    }

```

#### Sprawdź liczbę argumentów

Nigdy nie zakładaj, że użytkownik podał właściwą liczbę argumentów:

```
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){
    	if (args.length > 4) {
           sender.sendMessage("Zbyt dużo argumentów!");
           return false;
        }
        if (args.length < 2) {
           sender.sendMessage("Za mało argumentów!");
           return false;
        }
    }

```

#### Kiedy wskazujesz na gracza wg. nazwy, sprawdź czy jest online

Czasami chciałbyś otrzymać uchwyt do gracza posiadając tylko jego nazwę. Zawsze sprawdź, czy jest on online!

```
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){
    	Player target = (Bukkit.getServer().getPlayer(args[0]));
        if (target == null) {
           sender.sendMessage(args[0] + " is not online!");
           return false;
        }
        return false;
    }

```

Jeśli musisz modyfikować gracza, który jest offline, klasa `OfflinePlayer` jest do Twojej dyspozycji.

# Konfiguracja/ustawienia pluginu

Bukkit API zapewnia pluginom wygodny sposób na zarządzanie ustawieniami użytkownika. Dodatkowo jest to łatwy sposób dla programisty na przechowywanie danych.

Zobacz: Dokumentacja API Konfiguracji
# Uprawnienia

Z nowym API Bukkita dla permissions nie może być łatwiej. Żeby sprawdzić czy gracz ma jakieś pozwolenie trzeba użyc:

```
if(player.hasPermission("some.pointless.permission")) {
   //Coś robi
}else{
   //Robi coś innego
}
```

Możesz też sprawdzić czy permission zostało ustawione lub nie:

```
boolean isPermissionSet(String name)
```

Być może zastanawiasz się czemu nie ma tutaj żadnych grup. Odpowiedź jest prosta, nie są one tak naprawdę potrzebne. Poprzednio jednym z głównych użyć grup było formatowanie czatu. To też może zostać zrobione łatwo z permissions. W pliku konfiguracyjnym twojego pluginu od czatu możesz zdefiniować połączenia pomiędzy pozwoleniem a prefixem. Na przykład pozwolenie "someChat.prefix.admin" może się odwoływać do prefixu [Admin]. Każdy gracz który napisze coś z tym permission dostanie prefix [Admin].
Kolejnym sposobem może być wysłanie wiadomości do wszystkich graczy w grupie. Ponownie może to zostać zrobione z permission:

```
for(Player player: getServer().getOnlinePlayers()) {

    if(player.hasPermission("send.recieve.message")) {
        player.sendMessage("You were sent a message");
    }

}
```

Na koniec możesz się zastanawiać czemu ustawiałem i organizowałem pozwolenia dla graczy jeżeli nie ma żadnych grup. Bukkit API nie zapewnia grup, musisz zainstalować odpowiedni plugin do pozwoleń typu permissionsBukkit który będzie zarządzał grupami za ciebie. To API zapewnia interfejs nie implementacje.

### Konfiguracja własnych uprawnień

Jeżeli chcesz sprawować większą kontrolę nad pozwoleniami, na przykład nad dziedziczeniem to powinieneś dodać je do twojego pliku plugin.yml. Jest to całkowicie nie wymagane, lecz polecane. Poniżej jest przykład skonfigurowanych pozwoleń:

```
permissions:
    doorman.*:
        description: Gives access to all doorman commands
        children:
            doorman.kick: true
            doorman.ban: true
            doorman.knock: true
            doorman.denied: false
    doorman.kick:
        description: Allows you to kick a user
        default: op
    doorman.ban:
        description: Allows you to ban a user
        default: op
    doorman.knock:
        description: Knocks on the door!
        default: true
    doorman.denied:
        description: Prevents this user from entering the door
```

Każde pozwolenie może posiadać opis(description) oraz domyślną wartość(default) lub "dziecko"(children).

# Działania w tle oraz działania planowane

Obecnie serwer Minecraft liczy całą logikę w jednym wątku, więc każde wykonywane zadanie musi być bardzo krótkie. Skomplikowany fragment kodu mógłby powodować ogromne zacięcia i opóźnienia, o ile nie zostałby odpowiednio wykonany.

Na szczęście Bukkit wspiera odkładanie kodu w pluginach. Możesz ustawić zadanie tak, aby miało miejsce raz w przyszłości lub wykonywało się automatycznie co jakiś czas. Masz też możliwość ustanowienia osobnego wątku, który wykonywać będzie długie i absorbujące zadania niezależnie od głównego wątku serwera.

Istnieje na ten temat oddzielny artykuł (Zaplanowane akcje), który wprowadzi Cię w tajniki harmonogramów, odkładania zadań synchronicznych i wyłączania asynchronicznych w Bukkicie.

# Manipulacja blokami

Najprostszym sposobem na tworzenie bloków jest dostanie się do istniejącego i jego modyfikacja. Dla przykładu, jeśli chciałbyś zmodyfikować blok znajdujący się 5 klocków nad Twoją pozycją, najpierw chciałbyś utrzymać uchwyt do tego bloku, zaś następnie byś go zmodyfikował. Obejrzyj teraz ten kod reagujący na zdarzenie ruchu gracza:

```
public void onPlayerMove(PlayerMoveEvent evt) {
	Location loc = evt.getPlayer().getLocation();
	World w = loc.getWorld();
	loc.setY(loc.getY() + 5);
	Block b = w.getBlockAt(loc);
	b.setTypeId(1);
}

```

Skanując go linia po linii możemy zobaczyć, że zmieni blok pięć klocków nad naszą głową w kamień przy każdym wywołaniu zdarzenia playerMove(). Na początku pobieramy pozycję gracza, następnie z lokacji wyciągamy świat w którym ona jest. Kolejnym krokiem jest modyfikacja koordynatów poprzez dodanie wartości 5 do pozycji *y*. Posiadając świat i lokalizację możemy przejść do stworzenia zmiennej bloku w której będziemy przechowywać klocek znajdujący się w daneym miejscu. Zrobimy to przy wykorzystaniu wyrażenia *w.getBlockAt(loc);*. W końcu posiadając zmienną bloku, możemy rozpocząć zabawę - możemy zmienić jego ID lub nawet dane klocka. Dane klocka są opisywane przez *byte*, więc jakąkolwiek wartość musiałbyś rzutować na ten typ. Dla przykładu do powyższego kodu możnaby dodać *b.setData((byte)3);*

# Manipulacja ekwipunkiem

Ta sekcja opisuje manipulowanie ekwipunkiem gracza, lecz te same zasady stosuje się do manipulacji wyposażeniem skrzyń (o ile uda Ci się dojść do tego, jak uzyskać wyposażenie skrzyni :P). Czas na prosty przykład manipulacji ekwipunkiem:

```
public void onPlayerJoin(PlayerJoinEvent evt) {
    Player player = evt.getPlayer(); //Gracz który dołączył
    PlayerInventory inventory = player.getInventory(); //Ekwipunek gracza
    ItemStack itemstack = new ItemStack(Material.DIAMOND, 64); //Stack diamentów

    if (inventory.contains(itemstack)) {
        inventory.addItem(itemstack); //Dodaje stack diamentów do ekwipunku gracza
        player.sendMessage("Witaj! Wyglądasz na bogatego gracza, więc dajemy Ci jeszcze więcej diamentów!");
    }
}
```

We wnętrzu *onPlayerJoin* tworzymy kilka zmiennych w celu ułatwienia późniejszej pracy; są to: gracz, ekwipunek oraz stack(diamentów).

# Manipulacja przedmiotami

Gdy operujesz na przedmiotach, używasz klasy *ItemStack* w celu wyszukania oraz ustawienia wszelkich opcji danego stosu.

### Zaklinanie

Aby zakląć przedmiot musisz znać jego ID oraz ID enchantu. Enchant jako enchant nie może zostać zinstancjonowany (*new Enchantement()* nie zadziała) ponieważ jest typem abstrakcyjnym; z tego powodu musisz wykorzytsać *EnchantmentWrapper*. Nie możesz zaklinać przedmiotów, które nie są normalnie możliwe do zaklęcia, ponieważ serwery Bukkit nie są zdolne do wysłanie informacji zaklinania na nie zaklinalnych przedmiotach. A więc zapomnij o ognistych patykach ;)

```
int itemCode = 280;  //tu wpisz id przedmiotu
int effectId = 20;  //tutaj podaj id enchantu
int enchantmentLevel = 100;

ItemStack myItem = new ItemStack(itemCode);  //nowy przedmiot o danym id
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  //nowy enchant o podanym id enchantu
myItem.addEnchantment(myEnchantment, enchantmentLevel);  //zaklnij przedmiot

```

# Metadane

### Czemu używać metadanych

* Metadane są obsługiwane przez Bukkita, co sprawia, że są dobrą alternatywą dla HashMap.
* Metadane mogą być wykorzystane do współdzielenia danych pomiędzy pluginami.

### Czemu nie używać metadanych

* Trochę trudniej uzyskać wartość.
* Nie są zapisywane podczas wyłączenia serwera.

### Pobieranie oraz ustawianie metadanych

```
/* If you're having these methods in your plugin's main class (which extends JavaPlugin), you can remove parameters plugin from them,
 * and in the FixedMetadataValue constructor and getMetadata method, use "this" instead*/
public void setMetadata(Player player, String key, Object value, Plugin plugin){
  player.setMetadata(key,new FixedMetadataValue(plugin,value));
}
public Object getMetadata(Player player, String key, Plugin plugin){
  List<MetadataValue> values = player.getMetadata(key);
  for(MetadataValue value : values){
     if(value.getOwningPlugin().getDescription().getName().equals(plugin.getDescription().getName())){
        return value.value();
     }
  }
}
```

 **Note**: Jeśli manipulujesz liczbami, danymi typu *boolean* lub *String*, używaj wygodniejszej metody do uzyskania wartości. Dla przykładu, możesz użyć *asInt*, *asString* lub *asBoolean* zamiast *value* aby uzyskać wartość
# Bazy danych

### MySQL

Innym popularnym silnikiem baz SQL jest MySQL. Bliżej mu do klasy serwerowej niż SQLite, wiele poważnych firm oraz stron odwiedzanych miliony razy dziennie polega na nim. Sekcja zabezpieczeń jest także o wiele bardziej rozbudowana w MySQLu.

Programowanie pluginów korzystających z MySQL wygląda prawie tak samo jak w przypadku malutkiego SQLite i wielkiego dziecka Oracle, występują jedynie drobne różnice w składni. Zarządzanie ma wiele przestrzeni do rozwoju. Możliwe jest ustawienie osobnych kont oraz uprawnień wewnątrz bazy. Możesz nawet ustawić skrypty SQL, które będą zarządzały kopiami zapasowymi i przywracaniem bazy do stanu wcześniejszego.

# Wdrażanie swojego pluginu

Gdy mamy już gotowy plugin, jak przerobić garść plików źródłowych w jeden, działający plik JAR, który możemy zainstalować na serwerze? Po pierwsze, uruchom sobie serwer CraftBukkit na swoim komputerze. Instrukcję, jak to zrobić możesz znaleźć w artykule Tworzenie serwera. Następnie musisz wyeksportować plugin tak, że można go będzie uruchomić na serwerze. Aby to zrobić w Eclipse, kliknij na *File > Export*. W oknie które się pojawi, pod opcją *"Java"* wybierz *"Jar file"* i kliknij *Next*. Teraz powinieneś/aś zobaczyć okno podobne do tego:

Z lewej, upewnij się, że folder *src* jest zaznaczony. Z prawej, dwa pliki z kropką na początku nazwy są wewnętrznymi plikami Eclipse, ponieważ nie są niezbędne do działania pluginu możesz je odznaczyć. Niezbędnym do działania jest natomiast plik *plugin.yml*, więc musisz go zaznaczyć. Teraz wyeksportuj plugin dokąd chcesz, tylko tak, żebyś później pamiętał/a tą lokalizację.

Plik *jar* który właśnie wyeksportowałeś/aś powinien być już pełnoprawnym pluginem! Oczywiście, o ile nie wystąpiły żadne błędy w kodzie ani w pliku *plugin.yml*. Możesz teraz przenieść swój plugin do folderu *plugins* na wcześniej postawionym serwerze, a po restarcie serwera przejść do praktycznego testowania swojego pluginu! Aby połączyć się z serwerem działającym na Twoim komputerze, jako adres serwera wpisz *"localhost"*. Jeśli wystąpiły jakieś błędy, z którymi sam/a nie dajesz sobie rady, zasięgnij pomocy na forum deweloperów, zapytaj na kanale IRC bukkitdev lub przeczytaj ten artykuł ponownie. Gdy będziesz miał użyteczny, działajacy plugin pomyśl nad udostępnieniem go dla społeczności na BukkitDev.

# Wskazówki i sztuczki

### Podpalanie gracza

API Craftbukkita ma bardzo szerokie możliwości. Poniżej znajdziesz kilka wycinków kodu, które powodują naprawdę fajne efekty!

Jak podpalić gracza, w kontekście komendy:

```
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){
    if(cmd.getName().equalsIgnoreCase("ignite")){
        Player s = (Player)sender;
        Player target = Bukkit.getPlayerExact(args[0]);
          if (target == null)
          {
            return false;
          }
        // Dla przykładu, jeśli komenda była "/ignite Notch", gracz będzie po prostu "notch".
        // Uwaga: numerowanie argumentów rozpoczyna się od [0], nie od [1]. Z tego powodu to arg[0] będzie wpisanym nickiem
        target.setFireTicks(10000);
        return true;
    }
    return false;
}
```

O ile gracz Notch będzie online, wpisanie */ignite Notch* spowoduje podpalenie *Notch'*a na 500 sekund.

### Zabijanie gracza

Aby nie psuć nastroju wprowadzonego poprzednim fragmentem kodu, tutaj pokażę jak łatwo zabić gracza.

Wykorzystajmy to w metodzie *onCommand()*:

```
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){
    if(cmd.getName().equalsIgnoreCase("KillPlayer")){
        Player target = Bukkit.getPlayerExact(args[0]);
          if (target == null)
          {
            return false;
          }
        target.setHealth(0);
    }
    return false;
}
```

Czemu by nie dodać do tego małej eksplozji? Zobacz ten fragment kodu!

```
float explosionPower = 4F; //To jest moc eksplozji, TNT także wybucha domyślnie z mocą 4F
Player target = sender.getWorld().getPlayer(args[0]);
target.getWorld().createExplosion(target.getLocation(), explosionPower);
target.setHealth(0);
```

### Stwarzanie eksplozji

Poniższy fragment tworzy efekt audiowizualne efekty eksplozji TNT/creepera. Może to być użyteczne w wypadku anulowania zdarzenia wybuchu, ale woli zachowania efektów audiowizualnych.

```
public void onExplosionPrime(ExplosionPrimeEvent event){

		Entity entity = event.getEntity();

		if (entity instanceof TNTPrimed){
			TNTPrimed tnt = (TNTPrimed) entity;
			event.getEntity().getWorld().createExplosion(tnt.getLocation(), 0);

		}
	}
```

### Ukrywanie gracza przed pozostałymi graczami

Ten fragment ukryje wykonującego komendę przed podanym graczem. Warto zauważyć, że ukryje go TYLKO przed podanym graczem!

```
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){
    if(cmd.getName().equalsIgnoreCase("HideMe") && args.length == 1){
        Player s = (Player)sender; //Gets the Sender
        Player target = sender.getServer().getPlayer(args[0]);  //Gets the player who is supposed not to see the sender
        target.hidePlayer(s);
        return true;
    }
    return false;
}
```

### Strzelanie piorunami w miejsce kliknięcia gracza

Jeśli klikniesz blok za pośrednictwem wędki, piorun uderzy w tamto miejsce. Jest to prosta a zarazem fajna sztuczka.

```
@EventHandler
    public void onPlayerInteractBlock(PlayerInteractEvent evt){
        if(evt.getPlayer().getItemInHand().getTypeId() == Material.FISHING_ROD.getId()){
            //maximal distance between player and thunder is 200 blocks
            evt.getPlayer().getWorld().strikeLightning(evt.getPlayer().getTargetBlock(null, 200).getLocation());
        }
    }
```