BukkitWiki

Welcome to the BukkitWiki!

This Wiki is home to Bukkit's documentation and regulations surrounding the Bukkit Project and it's services. Want to help out? We would love to have you! Signup to get started!

READ MORE

BukkitWiki
Register
Advertisement

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[]

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:

NewJavaProject

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:

BuildPathPic

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:

Bukkitjavadocs

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:

MakePackage

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_bukkit (ź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}
Lightbulb Note: Nazwa pakietu często zawiera nazwę pluginu, więc nie zdziw się jeśli zobaczysz <NazwaPluginu>.<NazwaPluginu> na końcu drugiej linii!
Lightbulb 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ą.

Lightbulb 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:

commands:
   basic:
      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();
     }
  }
}
Lightbulb 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:

Exportwindow

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());
        }
    }


Language   EnglishбеларускаяDeutschespañolsuomifrançaisitaliano한국어Nederlandsnorskpolskiportuguêsрусскийlietuviųčeština
Advertisement