- Tham gia
- 18/09/2016
- Bài viết
- 3,165
Hôm nay, mình làm tut giới thiệu + hưỡng dẫn về một con framework khá ngon, giúp tạo command một cách, nhanh, gọn, lẹ 
Như các bạn đã biết, thì ờ command là một thứ quan trọng trong mỗi plugin
nhưng ngặt nỗi là quản lí command rất mất thời gian vì phải code dài dòng, check này check nọ.... vậy tại sao chúng ta ko dùng một cách nào đó nhanh + gọn hơn để tạo command? 
- Về con lib này, do Aikar phát triển (người tạo ra server Paper đấy)
- Hoàn toàn open-source, k phải trả phí gì để sử dụng
- Hỗ trợ trên nhiều platform server như Spigot, Paper, Bungeecord, Velocity, Sponge
- Các tính năng:
+ Tạo command dựa trên method + annotation: Bạn cho một hàm gì đó vd giveItem#(Player player, int amount) thì cái command của bạn sẽ có dạng /xxx <player> <amount>
chạy command tức là code trong hàm đó cũng dc chạy luôn! quá ngon phải k?
+ Hỗ trợ gợi ý command
qua lệnh /help chẳng hạn
+ Hỗ trợ hoàn thành command (phím tab): Rất tiện cho những ai lười gõ
+ Hỗ trợ kiểm tra các tham số: vd tham số abc, xyz là số nguyên, số thực, r là 1 player đang online hay offline,...
+ vvv.... còn nhiều lắm
Về cách lib này hoạt động:
Nó sẽ đọc method + các annotation từ code của bạn (thông qua Java Reflection), sau đó xử lí bla bla gì đấy, khi member hoặc console chạy lệnh thì hàm sẽ dc gọi & code bên trong hàm sẽ chạy.
Hưỡng dẫn cơ bản:
Đầu tiên, chúng ta sẽ học cách tạo command trước:
Ngay trên đầu class, bạn cần thêm annotation CommandAlias, là chỗ để ghi tên của command, nếu có nhiều tên thì cách nhau bởi dấu "|"
Sau đó bn phải thêm khúc "extends BaseCommand" (giống cái main class cũng có "extends JavaPlugin" đấy)
Tiếp theo, xác định các hàm nào sẽ dc chạy khi thực hiện lệnh và thêm ở trên annotation Subcommand
- Cái annotation này sẽ có 1 biến value để bn ghi tên subcommand, có thể có khoảng cách; nếu có nhiều tên thì ngăn bởi "|"
vd @ SubCommand("give|gave|given item|it") thì bn có thể có tới 6 cách nhập
/<tên command> give item
/<tên command> give it
/<tên command> gave item
/<tên command> gave it
/<tên command> given item
/<tên command> given it
- Tên hàm không quan trọng, nó ko liên quan tới command
- Nếu hàm ko có annotation này thì nó sẽ là 1 hàm bth, ko liên quan tới command
- Mọi hàm command phải có ít nhất 1 tham số, với tham số đầu tiên luôn là người chạy command (ví dụ Player, OnlinePlayer, CommandSender,..), cái này trong lib dc gọi là context.
vd cái hàm giveItem(Player player, Material material, int amount) ở trên đi; đừng lầm tưởng lệnh là /testpl give <player> <material> <amount> nhé! Cái tham số đầu tiên sẽ k dc tính trong command; lệnh đúng phải là /testpl give <material> <amount>; còn cái player kia là để lưu lại ng` chạy command thôi, vd bn muốn give ng ta item thì phải có cái Player để give chứ
- Với mỗi hàm dc gắn SubCommand thì bạn có thể thêm các tham số , với các kiểu dữ liệu sau:

Nhưng chú ý rằng: không phải toàn bộ kiểu dữ liệu đều hỗ trợ, vd plugin bạn có cái object là ABC thì cái này ko dc hỗ trợ mặc định mà bạn phải register nó với lib.
Một ví dụ nữa:
Trên là code tạo 2 lệnh /testpl spawn entity và /testpl spawn particle (class mình vẫn giữ nguyên như ở trên)
Một số cái mới:
- Annotation Optional: đặt trước tham số của command, để đánh dấu rằng tham số này ko bị ràng buộc, có hay k đều dc, nếu ko có thì cái object sẽ bị null. Đó là lý do tại sao có cái hàng code để mình check xem nó có bị null k, nếu null sẽ dùng world của cái người nhập command
- Annotation CommandPermission: đặt ra quyền cho command, nếu k có quyền thì khi nhập sẽ báo lỗi
- Annotation Description: mô tả command
Cả 3 cái trên đều k bắt buộc phải có trong code.
Rồi giờ chúng ta sẽ học cách tạo lệnh trợ giúp, dc built-in trong lib này luôn
CommandHelp cũng là 1 context, vì thế nó là tham số đầu tiên
Reg lệnh command thì bn k cần SubCommand gì cả, annotation HelpCommand sẽ tự hiểu lệnh này là /testpl help.
Chú ý: cái trợ giúp này là 1 api chưa chính thức, vì thế bn phải unlock cái tính năng này (xem tiếp đi r chỉ)
Ví dụ, mình muốn show help nếu nhập lệnh ko tồn tại thì sao? Thêm annotation CatchUnknown thoi:
Bây giờ chúng ta sẽ register command:
Đừng để ý cái PaperCommandManager, cái này là theo khuyến nghị từ chủ lib này: nếu sv dùng paper thì lệnh sẽ hđ tốt hơn, hỗ trợ async, còn server spigot vẫn dùng bình thường.
Nếu đây là plugin bungeecord thì bn cũng lm tương tự, thêm nó vào onEnable, rồi sửa PaperCommandManager thành BungeeCommandManager.
Hết
Chúc mừng bn đã tạo dc cái command thành công
Thôi ngừng xem bài này dc rồi...
Hưỡng dẫn nâng cao
Ok, chúng ta đã học dc cách tạo cơ bản rồi, giờ học sâu thêm chút nữa.
1. Annotation Private: đặt ở đầu hàm, để ngăn tab-completion (hoàn thành lệnh bằng phím tab) và ngăn hiện trong lệnh trợ giúp (dùng chính api của ACF mới dc nhé)
2. Annotation Default: đặt ở tham số, tương tự như Optional, nó dùng để chỉ tham số này ko bắt buộc, nếu ko dc nhập thì nó sẽ trả về giá trị mặc định (ko phải null giống như cái Optional)
3. Annotation Single: đặt ở tham số cuối cùng
công dụng của nó thì hơi khó nói chút, th để minh hoạ cho dễ vậy:
vd plugin bn có lệnh là /message <player> <text> đi & một lần nọ member nhập lệnh /message noob e solo daxua k
nếu có cái annotation này thì cái tham số text á, sẽ là full đoạn "e solo daxua k", còn nếu k có thì chỉ có chữ "e" thôi
do sau đó có dấu cách
4. Annotation CommandCompletion: đặt ở đầu hàm, để bật tính năng tự động hoàn thành cho sub command, cách viết thì tương tự như cái SubCommand thôi, có thể dùng khoảng cách và nếu có nhiều cách viết thì ngăn cách bởi "|"
vd "mlem|mdem ngon|mup vai|qua"
nếu gõ mle rồi tab sẽ ra mlem, gõ md sẽ ra mdem
gõ "mlem ng" sẽ ra "mlem ngon"
gõ "mlem mup v" sẽ ra "mlem mup vai"
Tks ae đã đọc

Như các bạn đã biết, thì ờ command là một thứ quan trọng trong mỗi plugin
nhưng ngặt nỗi là quản lí command rất mất thời gian vì phải code dài dòng, check này check nọ.... vậy tại sao chúng ta ko dùng một cách nào đó nhanh + gọn hơn để tạo command? 
- Về con lib này, do Aikar phát triển (người tạo ra server Paper đấy)
- Hoàn toàn open-source, k phải trả phí gì để sử dụng
- Hỗ trợ trên nhiều platform server như Spigot, Paper, Bungeecord, Velocity, Sponge
- Các tính năng:
+ Tạo command dựa trên method + annotation: Bạn cho một hàm gì đó vd giveItem#(Player player, int amount) thì cái command của bạn sẽ có dạng /xxx <player> <amount>
chạy command tức là code trong hàm đó cũng dc chạy luôn! quá ngon phải k?+ Hỗ trợ gợi ý command
qua lệnh /help chẳng hạn+ Hỗ trợ hoàn thành command (phím tab): Rất tiện cho những ai lười gõ
+ Hỗ trợ kiểm tra các tham số: vd tham số abc, xyz là số nguyên, số thực, r là 1 player đang online hay offline,...
+ vvv.... còn nhiều lắm
Về cách lib này hoạt động:
Nó sẽ đọc method + các annotation từ code của bạn (thông qua Java Reflection), sau đó xử lí bla bla gì đấy, khi member hoặc console chạy lệnh thì hàm sẽ dc gọi & code bên trong hàm sẽ chạy.
Hưỡng dẫn cơ bản:
Đầu tiên, chúng ta sẽ học cách tạo command trước:
Java:
@CommandAlias("testplugin|testpl")
public class cmd extends BaseCommand {
@Subcommand("give")
public void giveItem(Player player, Material material, int amount){
player.getInventory().addItem(new ItemStack(material, amount));
}
}
Sau đó bn phải thêm khúc "extends BaseCommand" (giống cái main class cũng có "extends JavaPlugin" đấy)
Tiếp theo, xác định các hàm nào sẽ dc chạy khi thực hiện lệnh và thêm ở trên annotation Subcommand
- Cái annotation này sẽ có 1 biến value để bn ghi tên subcommand, có thể có khoảng cách; nếu có nhiều tên thì ngăn bởi "|"
vd @ SubCommand("give|gave|given item|it") thì bn có thể có tới 6 cách nhập
/<tên command> give item
/<tên command> give it
/<tên command> gave item
/<tên command> gave it
/<tên command> given item
/<tên command> given it
- Tên hàm không quan trọng, nó ko liên quan tới command
- Nếu hàm ko có annotation này thì nó sẽ là 1 hàm bth, ko liên quan tới command
- Mọi hàm command phải có ít nhất 1 tham số, với tham số đầu tiên luôn là người chạy command (ví dụ Player, OnlinePlayer, CommandSender,..), cái này trong lib dc gọi là context.
vd cái hàm giveItem(Player player, Material material, int amount) ở trên đi; đừng lầm tưởng lệnh là /testpl give <player> <material> <amount> nhé! Cái tham số đầu tiên sẽ k dc tính trong command; lệnh đúng phải là /testpl give <material> <amount>; còn cái player kia là để lưu lại ng` chạy command thôi, vd bn muốn give ng ta item thì phải có cái Player để give chứ

- Với mỗi hàm dc gắn SubCommand thì bạn có thể thêm các tham số , với các kiểu dữ liệu sau:
+ Player (cho plugin Bukkit): người chơi hiện đang online, nếu member nhập tên ai đó ko on, lệnh sẽ k dc chạy mà thay vào đó sẽ có lỗi xảy ra
+ OnlinePlayer (cho plugin Bukkit): tương tự với Player
+ World (cho plugin Bukkit): world nào đó đã được load
+ Location (cho plugin Bukkit): vị trí; hình như là dc phép dùng kí hiệu "~" như trong vanilla (ko nhớ rõ lắm)
+ ChatColor (cho plugin Bukkit): tên màu
+ CommandSender (cho plugin Bukkit): người đã chạy lệnh, có thể là player hoặc console
+ ProxiedPlayer (cho plugin Bungeecord): như cái Player, nhưng ở bên Bungeecord thì tên đúng là ProxiedPlayer
+ CommandIssuer: đây là 1 object chung dành cho mọi platform. Chẳng hạn, plugin của bạn có cả 2 phiên bản cho spigot và bungeecord, thay vì viết hai đoạn code y hệt nhau, một cái dùng Player, cái kia thì ProxiedPlayer, thì bạn có thể dùng chung CommandIssuer
(tuỳ mục đích)
(tuỳ mục đích)+ CommandSender (nhưng mà dành cho plugin Bungeecord)
+ ChatColor (cũng là ở bên Bungeecord)
+ Các kiểu dữ liệu nguyên thuỷ: vd chuỗi, số, boolean, vvv; tất cả trừ chuỗi sẽ dc check tự động, vd nếu là số thực thì phải nhập đúng số thực, số nguyên phải nhập đúng số nguyên, nhập sai sẽ báo lỗi + ko chạy lệnh
+ Enum: mọi enum đều được hỗ trợ (một số enum tiêu biểu như Material, Sound, Particle, EntityType, vvv)
Còn nữa nhưng mình cũng chưa tìm hiểu thêm 
Nhưng chú ý rằng: không phải toàn bộ kiểu dữ liệu đều hỗ trợ, vd plugin bạn có cái object là ABC thì cái này ko dc hỗ trợ mặc định mà bạn phải register nó với lib.
Một ví dụ nữa:
Java:
@Subcommand("spawn entity")
@CommandPermission("testpl.spawn.entity")
@Description("Sinh ra mot mob nao do")
public void spawnEntity(Player player, EntityType entityType, int amount) {
World world = player.getWorld();
Location location = player.getLocation();
for(int i = 0; i < amount; i++){
world.spawnEntity(location, entityType);
}
}
@Subcommand("spawn particle")
@CommandPermission("testpl.spawn.particle")
public void spawnParticle(Player player, Particle particle, @Optional World world) {
world = (world == null) ? player.getWorld() : world;
for(Player p : world.getPlayers()){
world.spawnParticle(particle, p.getLocation(), 100);
}
}
Một số cái mới:
- Annotation Optional: đặt trước tham số của command, để đánh dấu rằng tham số này ko bị ràng buộc, có hay k đều dc, nếu ko có thì cái object sẽ bị null. Đó là lý do tại sao có cái hàng code để mình check xem nó có bị null k, nếu null sẽ dùng world của cái người nhập command
- Annotation CommandPermission: đặt ra quyền cho command, nếu k có quyền thì khi nhập sẽ báo lỗi
- Annotation Description: mô tả command
Cả 3 cái trên đều k bắt buộc phải có trong code.
Rồi giờ chúng ta sẽ học cách tạo lệnh trợ giúp, dc built-in trong lib này luôn

Java:
@HelpCommand
public void help(CommandHelp commandHelp) {
commandHelp.showHelp();
}
Reg lệnh command thì bn k cần SubCommand gì cả, annotation HelpCommand sẽ tự hiểu lệnh này là /testpl help.
Chú ý: cái trợ giúp này là 1 api chưa chính thức, vì thế bn phải unlock cái tính năng này (xem tiếp đi r chỉ)
Ví dụ, mình muốn show help nếu nhập lệnh ko tồn tại thì sao? Thêm annotation CatchUnknown thoi:
Java:
@HelpCommand
@CatchUnknown
public void help(CommandHelp commandHelp) {
commandHelp.showHelp();
}
Bây giờ chúng ta sẽ register command:
Java:
public class pl extends JavaPlugin {
@Override
public void onEnable() {
PaperCommandManager commandManager = new PaperCommandManager(this);
commandManager.registerCommand(new cmd());
// dòng này chỉ cần nếu dùng api trợ giúp command của ACF.
// nếu bn k có, hoặc có lệnh help tự tạo (dùng sendMessage hay gì ấy) thì k cần.
commandManager.enableUnstableAPI("help");
}
}
Nếu đây là plugin bungeecord thì bn cũng lm tương tự, thêm nó vào onEnable, rồi sửa PaperCommandManager thành BungeeCommandManager.
Hết
Chúc mừng bn đã tạo dc cái command thành công
Thôi ngừng xem bài này dc rồi...Hưỡng dẫn nâng cao
Ok, chúng ta đã học dc cách tạo cơ bản rồi, giờ học sâu thêm chút nữa.
1. Annotation Private: đặt ở đầu hàm, để ngăn tab-completion (hoàn thành lệnh bằng phím tab) và ngăn hiện trong lệnh trợ giúp (dùng chính api của ACF mới dc nhé)
2. Annotation Default: đặt ở tham số, tương tự như Optional, nó dùng để chỉ tham số này ko bắt buộc, nếu ko dc nhập thì nó sẽ trả về giá trị mặc định (ko phải null giống như cái Optional)
3. Annotation Single: đặt ở tham số cuối cùng
công dụng của nó thì hơi khó nói chút, th để minh hoạ cho dễ vậy:vd plugin bn có lệnh là /message <player> <text> đi & một lần nọ member nhập lệnh /message noob e solo daxua k
nếu có cái annotation này thì cái tham số text á, sẽ là full đoạn "e solo daxua k", còn nếu k có thì chỉ có chữ "e" thôi
do sau đó có dấu cách4. Annotation CommandCompletion: đặt ở đầu hàm, để bật tính năng tự động hoàn thành cho sub command, cách viết thì tương tự như cái SubCommand thôi, có thể dùng khoảng cách và nếu có nhiều cách viết thì ngăn cách bởi "|"
vd "mlem|mdem ngon|mup vai|qua"
nếu gõ mle rồi tab sẽ ra mlem, gõ md sẽ ra mdem
gõ "mlem ng" sẽ ra "mlem ngon"
gõ "mlem mup v" sẽ ra "mlem mup vai"
Tks ae đã đọc

K




