Skocz do zawartości
  • 👋 Witaj na MPCForum!

    Przeglądasz forum jako gość, co oznacza, że wiele świetnych funkcji jest jeszcze przed Tobą! 😎

    • Pełny dostęp do działów i ukrytych treści
    • Możliwość pisania i odpowiadania w tematach
    • System prywatnych wiadomości
    • Zbieranie reputacji i rozwijanie swojego profilu
    • Członkostwo w jednej z największych społeczności graczy

    👉 Dołączenie zajmie Ci mniej niż minutę – a zyskasz znacznie więcej!

    Zarejestruj się teraz

Tutorial #Java - Łatwe tworzenie i rejestrowanie komend! | własny Executor


AdamGrzegorz

Rekomendowane odpowiedzi

Opublikowano

Dzień dobry.

 

Od razu ostrzegam że oprawa graficzna jest c*****a słaba, bo nie chciało mi się jej robić, po prostu skupcie się na przekazie poradnika niż na wyglądzie :D
 
 # SPIS TREŚCI
    [----------------------------------------------------------------]

  • Znaczenie słów używanych w poradniku.          |  []              
  • Czym jest executor? Jest wiele sposobów?       |  []
  • Wyjaśnienie i praktyczne napisanie kodu          |  [][][][]
  • Używanie executora w komendach                   |  [][]                                                  < POZIOM TRUDNOŚCI >
  • Podstawa rejestrowania komend refleksjami    |  [][][]                     < MIERZONY NA PODSTAWIE WYTYCZNYCH SPOŁECZNOŚCI >
  • Szybsze rejestrowanie komend.                       |  [][][][][][]
  • Podsumowanie i podziękowania                      |  []

   [----------------------------------------------------------------]
 
 
 # Znaczenie słów używanych w poradniku.
     [----------------------------------------------------------------]

  • Executor | Kod wykonujący, ładujący lub rejestrujący daną czynność
  • Plugin | Wtyczka używana do różnych serwerów lub programów < tj. do serwera gry Minecraft >
  • Java | Język programowania < tj. w tym wypadku używany do pluginów >
  • Komenda | Wpisany na czacie gry lub w konsoli czy stronie ciąg znaków wykonujący daną funkcję. 

    [----------------------------------------------------------------]
 
 # Czym jest executor? Jest wiele sposobów?
Opisany w tym poradniku executor służy do prostszego wykonywania i rejestrowania komend, co wiąże się z szybszym i łatwiejszym tworzeniem własnych komend.
Szczególnie przydatne jest to w przypadku większych pluginów. Jest wiele pluginów które używają własnych executorów, na przykład FunnyGuilds, czy qEssentials dajmy na to.
 
Jest to mój sposób na executor, jest wiele innych, szybszych bądź nie, ale ten sposób jest moim zdaniem wygodny i nie sprawia żadnych problemów.

 
 # Wyjaśnienie i praktyczne napisanie kodu.
Tak więc, zaczynajmy!
Na początek potrzebna nam będzie klasa główna Main.java i klasa Executor.java (mam nadzieje że znacie podstawy, i wiecie jak to zrobić).
 
Klasa Main.java wygląda następująco, nie będę jej opisywał, albowiem nie ma to większego sensu, zakładam że wiecie do czego wszystko służy.

package me.twojnick.plugin;

import org.bukkit.plugin.java.JavaPlugin;

public class Main extends JavaPlugin{
	
	private static Main instance;
	
	public static Main getInstance()
	{
		return instance;
	}
	
	public void onLoad()
	{
		instance = this;
	}
	
	public void onEnable()
	{
	       // tutaj jeszcze nic nie ma, będzie dopisywane w dalszych częściach.
	}

} 

No i oczywiście klasa Executor.java w której na razie nic nie ma.

package me.twojnick.plugin;

public class Executor {

}  

Przygotujmy sobie od razu paczkę (me.twojnick.plugin.commands) w której będą przechowywane wszystkie komendy.

Możemy od razu jedną przygotować.

package me.twojnick.plugin.commands;

public class TestCommand {

}
 

Okej, czas wziąć się za Executor. Zakładam że zaimportowaliście już wasz silnik bukkita/spigota, bo bez tego ani rusz!

Na początku musimy zmienić trochę wygląd klasy, potrzebujemy żeby była ona abstrakcyjna, czyli niezgodna z właściwymi wymaganiami rozszerzenia.

public abstract class Executor extends Command{ 

Extends czyli rozszerzenie, w tym wypadku Command z bukkita. 

Teraz musimy stworzyć parę prywatnych zmiennych (nie statycznych!), które będą dla każdej komendy przydzielane.

	private String permission;
	private boolean onlyplayer;

Na początku mamy uprawnienie, które będzie przydzielane do każdej komendy osobno!

No i oczywiście metoda boolean (true/false) sprawdzająca czy komenda jest dozwolona dla konsoli, czy tylko dla graczy.

 

Teraz trzeba stworzyć własne rozszerzenie dla każdej z naszych komend, dzięki czemu będziemy mogli dla niej ustawić wiele ciekawych funkcji.

public Executor(String name, String description, String usage, String permission, List<String> aliases, boolean onlyPlayer)
	{
		super(name, description, usage, aliases);
		this.permission = "plugin." + permission;
		this.onlyplayer = onlyPlayer;
	}

Wiem, że wydaje się to trudne do zrozumienia, szczególnie dla początkującego, ale wyjaśnię to wszystko po kolei.

 

Metoda, w której zawarte jest pole 'super', jest ono wymagane do rozszerzenia Command w bukkicie, dzięki temu umożliwiamy bukkitowi zainicjowanie i rozpoznanie komendy.

Ustawiamy własne zmienne, wybranymi opcjami dla danej komendy, dajmy na to permission, możemy również dać tak, abyśmy nie musieli za każdym razem wpisywać 'plugin.uprawnienie', tylko samo uprawnienie docelowe :)

 

Następną rzeczą będzie wykonywanie całej komendy, czyli to co teraz napiszemy, będzie się wykonywało przy wpisaniu wymaganego tekstu w konsoli lub czacie. (zależy jak ustawiliśmy)

public final boolean execute(CommandSender sender, String label, String[] args)
	{
		if(!sender.hasPermission(this.permission))
		{
			sender.sendMessage("Nie masz uprawnien"); 
			return true;
		}
		if(this.onlyplayer)
		{
			if(!(sender instanceof Player))
			{
				sender.sendMessage("Nie jestes graczem"); 
				return true;
			}
		}
		try {
			onExecute(sender, args); 
		} catch(Exception e) {
			e.printStackTrace();
		}
		return true;
	}

Teraz po kolei.

Jest to metoda finalna 'execute', czyli główna rzecz (jest wykonywana jako pierwsza) gdy executor zostanie uruchomiony i zainicjowany przez gracza lub konsolę.

 

Na początku jest standardowe sprawdzanie czy gracz ma ustawione uprawnienie, lecz jeżeli nie - wywal tekst i zwróć wartość taką, aby komenda nie została dalej wykonywana.

Potem sprawdzanie czy jest graczem (jeżeli ustawiliśmy że tylko dla gracza ma być komenda, czyli wartość true)

 

I jeżeli wszystkie warunki zostały spełnione, wykonuje się metoda onExecute, czyli metoda, którą juz napiszemy w komendzie, czyli to co ma się wykonywać głównie gdy komenda zostanie wpisana przez konsolę/gracza.

 

Ale zaraz, takiej metody tu nie ma, więc napiszmy ją.

public abstract void onExecute(CommandSender sender, String[] args);

Gotowe!

 

Cały kod powinien wyglądać następująco:

package me.twojnick.plugin;

import java.util.List;

import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import pl.za.xvacuum.guilds.lang.Messages;
import pl.za.xvacuum.guilds.utils.Logger;
import pl.za.xvacuum.guilds.utils.Logger.LogType;
import pl.za.xvacuum.guilds.utils.Util;

public abstract class Executor extends Command{
	
	private String permission;
	private boolean onlyplayer = false;
	
	public Executor(String name, String description, String usage, String permission, List<String> aliases, boolean onlyPlayer)
	{
		super(name, description, usage, aliases);
		this.permission = "plugin." + permission;
		this.onlyplayer = onlyPlayer;
	}
	
	public final boolean execute(CommandSender sender, String label, String[] args)
	{
		if(!sender.hasPermission(this.permission))
		{
			sender.sendMessage("Nie masz uprawnien"); 
			return true;
		}
		if(this.onlyplayer)
		{
			if(!(sender instanceof Player))
			{
				sender.sendMessage("Nie jestes graczem"); 
				return true;
			}
		}
		try {
			onExecute(sender, args);
		} catch(Exception e) {
			e.printStackTrace();
		}
		return true;
	}

	public abstract void onExecute(CommandSender sender,  String[] args);

}

 # Używanie executora w komendach.

Czas na używanie tego co napisaliśmy, w komendach. Let's do it!

 

Przechodzimy w klasę TestCommand.java

Jest ona pusta, dodajmy do niej rozszerzenie naszego Executora.

public class TestCommand extends Executor{

Pokazuje nam błąd na tekscie 'TestCommand', musimy kliknąć pierwszą opcje (add constructor)

 

Całość powinna wyglądać tak:

package me.twojnick.plugin.commands;

import java.util.List;

import me.twojnick.plugin.Executor;

public class TestCommand extends Executor{

	public TestCommand(String name, String description, String usage,
			String permission, List<String> aliases, boolean onlyPlayer) {
		super(name, description, usage, permission, aliases, onlyPlayer);
		// TODO Auto-generated constructor stub
	}

}

Po usunięciu zbędnych rzeczy, musi wyglądać tak:

package me.twojnick.plugin.commands;

import java.util.List;

import me.twojnick.plugin.Executor;

public class TestCommand extends Executor{

	public TestCommand() {
		super(name, description, usage, permission, aliases, onlyPlayer);
	}

}

Teraz czas na uzupełnienie!

Uzupełnię komendę tak jak mi się podoba, wy możecie zrobić to inaczej.

public TestCommand() {
	super("test", "Testowa komenda", "/test", "test", Arrays.asList("alias", "alias2"), true);
}

Po takim uzupełnieniu, wszystko będzie działać poprawnie. 

ps: Arrays.asList to metoda która zamienia wpisane teksty na listę, czyli na poprawną formę.

ps2: w polu permission wpisałem tylko "test" bo w Executorze automatycznie dodaje się "plugin." czyli całość wygląda tak: "plugin.test".

 

Ale zaraz, pokazuje nam się jeszcze jeden błąd, znowu musimy kliknąć pierwszą opcje, całość powinna wyglądać w ten sposób:

package me.twojnick.plugin.commands;

import java.util.List;

import me.twojnick.plugin.Executor;

public class TestCommand extends Executor{

	public TestCommand() {
		super("test", "Testowa komenda", "/test", "test", Arrays.asList("alias", "alias2"), true);
	}

	@Override
	public void onExecute(CommandSender sender, String[] args) {
		// TODO Auto-generated method stub
		
	}
}

Teraz wystarczy w polu onExecute wykonać naszą, dowolną komendę.

Ja zrobię w ten sposób:

	@Override
	public void onExecute(CommandSender sender, String[] args) {
		Player p = (Player)sender;
                p.sendMessage("czesc!");
                return;
	} 

Po wykonaniu komendy powinien wyświetlić się tekst "czesc!" :)

ps: return zamyka komendę i nie pozwala dalej się wykonywać, warto tego używać.

 
 # Podstawa rejestrowania komend refleksjami.

Pamiętacie, w CommandExecutor jest funkcja getCommand("komenda").setExecutor(new Komenda());

Tutaj będziemy musieli użyć zupełnie czegoś innego. 

 

Wchodzimy do głównej klasy Main.java i tworzymy zmienną (na samej górze).

private static CommandMap cmdMap;

Jest to zmienna przechowująca mapę komend z bukkita.

 

Dalej, musimy napisać dość spory kod refleksji rejestrujący daną komendę.

        public static boolean registerCommand(Command command)
	{
		if (cmdMap == null) {
			try {
				Field field = SimplePluginManager.class.getDeclaredField("commandMap");
				field.setAccessible(true);
				cmdMap = (CommandMap)field.get(Bukkit.getServer().getPluginManager());
			}
			catch (Exception e) {
				e.printStackTrace();
				return false;
			}
		}
		cmdMap.register("plugin:", command);
		return true;
	}

Spokojnie, nie bój się, zaraz to wszystko wytłumaczę.

Jeżeli mapa komendy nie jest ustawiona, kod ją ustawia poprzez refleksje.

Najpierw ustawia fielda pobierającego command mapę z SimplePluginManager'a (z bukkita) i ustawia ją jako dostępną, no a potem ustawia naszą mapę na to, co pobrał z plugin managera.

(Mam nadzieje że trochę rozumiecie, nie umiem tłumaczyć, sam dopiero uczę się refleksji).

"plugin:" oznacza fallback-prefix (dzięki za przypomnienie, @nieznany1234567 ) czyli /plugin:twojakomenda

 

Gdy wszystko pójdzie z planem, zarejestruje komendę i zwróci wartość true.

 

Przechodzimy do onEnable w obecnej klasie, i wpisujemy:

registerCommand(new TestCommand()); 

Wszystko powinno wyglądać tak:

package me.twojnick.plugin;

import java.lang.reflect.Field;

import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import org.bukkit.plugin.SimplePluginManager;
import org.bukkit.plugin.java.JavaPlugin;

import me.twojnick.plugin.commands.TestCommand;

public class Main extends JavaPlugin{
	
	private static Main instance;
	private static CommandMap cmdMap;
	
	public static Main getInstance()
	{
		return instance;
	}
	
	public void onLoad()
	{
		instance = this;
	}
	
	public void onEnable()
	{
		registerCommand(new TestCommand());
	}
	
	public static boolean registerCommand(Command command)
	{
		if (cmdMap == null) {
			try {
				Field field = SimplePluginManager.class.getDeclaredField("commandMap");
				field.setAccessible(true);
				cmdMap = (CommandMap)field.get(Bukkit.getServer().getPluginManager());
			}
			catch (Exception e) {
				e.printStackTrace();
				return false;
			}
		}
		cmdMap.register("plugin:", command);
		return true;
	}

}
 

 # Szybsze rejestrowanie komend.     

Jeżeli chcemy szybciej rejestrować komendy bez potrzeby dodawania registerCommand do onEnable, musisz stworzyć rejestrację po klasach z paczki .commands.

 

Teraz, oprócz wszystkiego w Main.java, musisz dodać jeszcze jedną metodę.

	public static void registerCommands()
			throws IllegalArgumentException, InvocationTargetException,
			SecurityException {
		try {
			ImmutableSet<ClassInfo> classes = ClassPath.from(classLoader)
					.getTopLevelClasses("me.twojnick.plugin.commands");

			for (ClassInfo clsInfo : classes) {
				Class<?> commandClass = clsInfo.load();
				Object obj = commandClass.getDeclaredConstructor()
						.newInstance();
				if (!Main.getThis().registerCommand((Executor) obj)) {
					//error
				}
			}
		} catch (IOException | InstantiationException | NoSuchMethodException
				| IllegalAccessException ex) {
			ex.printStackTrace();
			Bukkit.getPluginManager().disablePlugin(Main.getThis());
		}
	} 

Na początku próbuje stworzyć immutable set'a z listą klas z paczki .commands, i dla każdej z komendy tworzy obiekt instancji zadeklarowanego konstruktora (rozszerzenie executor'a) i rejestruje wartość, jeżeli mu się to nie uda (bo pamiętajcie, jeżeli się nie uda, zwraca wartość false, czyli !<cos> oznacza jeżeli coś zwróci false) to plugin zwróci jakiś błąd, to już należy do was co tam wstawicie.

 

Jeżeli nie uda się stworzyć immutable set'a ani nic innego w tej metodzie, zwróci jedno z powyższych błędów.

  IOException - błąd związany z plikami, również z klasami.

  InstantationException - Jeżeli nie uda się utworzyć instancji klasy.

  NoSuchMethodException - Jeżeli nie udało się znaleźć metody Executora (w komendach).

  IllegalAccessException - Jeżeli nie udało się uzyskać dostępu do klasy.

 

 

 

 # Podsumowanie i podziękowania.

 

Podsumowywując, nauczyłeś się:

  • Tworzenia własnych executorów, i konstruktory używane w innych klasach
  • Rejestrowania komend za pomocą refleksji i CommandMapy
  • Pobierania klas z paczek i robienia coś z nimi.
  • Dowiedziałeś się co to jest abstract, try/catch.
  • Nauczyłeś się podstaw refleksji i zastosowałeś je w praktyce.

Podziękowania kieruje do ntsw i shooly'iego i paru innym osobom które również pomogły w przygotowaniu metod i nauczyły mnie podstaw refleksji, i dzięki nim mogę podzielić się z wami moją dotychczasową wiedzą, jeszcze raz dziękuje!

 

 

Mam nadzieje że się przydało.

Pozdrawiam.

Moja oficjalna strona! :)

http://socketbyte.pl/ 

Opublikowano

Tam przy rejestracji komendy refleksją jako pierwszy parametr do register() trzeba podać fallback-prefix. Czyli np. /bukkit:plugins (bukkit to ten prefix). Gdy komenda jest normalnie rejestrowana przez bukkita to prefix jest nazwą pluginu pisaną małymi literami

 

 

EDIT:

Żeby nie było, że głupoty gadam: https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bukkit/command/CommandMap.java#L66(fallback prefix jest po to żeby każda komenda była unikalna więc powinien on być nazwą pluginu)

Opublikowano

Tutorial o komendach, wystarczająco bogaty w wyjaśnienia, krótko skromnie i na temat. 

Leci +  za prace :D.

Opublikowano

­Poooradnik wspaniały! W końcu coś nowego na MPC.

Mam nadzieję że będziesz kontynuował ten tut.

Opublikowano

Tam przy rejestracji komendy refleksją jako pierwszy parametr do register() trzeba podać fallback-prefix. Czyli np. /bukkit:plugins (bukkit to ten prefix). Gdy komenda jest normalnie rejestrowana przez bukkita to prefix jest nazwą pluginu pisaną małymi literami

Dziękuje za przypomnienie, już dodano.

 

 

Tutorial o komendach, wystarczająco bogaty w wyjaśnienia, krótko skromnie i na temat. 

Leci +  za prace :D.

Dzięki!

 

 

­Poooradnik wspaniały! W końcu coś nowego na MPC.

Mam nadzieję że będziesz kontynuował ten tut.

Tak, powinienem to jeszcze rozbudowywać, bo to nie wszystko. :)

 

 

 

­

Moja oficjalna strona! :)

http://socketbyte.pl/ 

Opublikowano

Jedno zasadnicze pytanie: po co.

Po co łamać wszystkie zasady, ryzykować tym, że kiedyś coś zmiennią i plugin się popsuje, ryzykować że inne pluginy się posypią jak zobaczą że plugin ma więcej komend niż powinien.

Na co to?

 

Zrozumiałbym jak ktoś by tworzył plugin na dynamiczne komendy i niechciał używać eventów, tylko robić to bezpośrednio.

Albo jak ktoś by chciał nadpisać cały system, by np dodać aliasy komend za pomocą reg-exa.

Ale tutaj sensu nie widzę, używajcie normalnie plugin.yml tak jak zostało to stworzone...

a jak chcecie tylko szybszej rejestracji, to można adnotacjami to zrobić.

 

 

PS: reflekcje powinno się cachować. Tutaj można wybaczyć bo i tak uzywamy tylko raz na starcie, ale jak ktoś chce zrobić dynamiczne komendy to powinien zapisac te fieldy

1438614356923701010629.png

 

Opublikowano

Dziękuje za przypomnienie, już dodano.

 

 

Dzięki!

 

 

Tak, powinienem to jeszcze rozbudowywać, bo to nie wszystko. :)

 

 

 

­

ciekawy poradnik, napewno sie poducze :) ps: czemu mi nie odpisujesz na p/w ciagle?

Opublikowano

Genialny poradnik! Dobrze, że nie zawiera zbędnej grafiki - która po roku wygaśnię, a poradnik będzie nie do zrozumienia.

Na pewno przyda się innym programistom javy!

 

  • 3 tygodnie później...
  • 1 miesiąc temu...

Zarchiwizowany

Ten temat przebywa obecnie w archiwum. Dodawanie nowych odpowiedzi zostało zablokowane.

×
×
  • Dodaj nową pozycję...