Create an offline player command
This example shows how to execute a command for both online and offline players.
Offline player commands are useful when you need to access player data even if the player is not currently online.
Deep dive: [[DataLoader|Core-API/Player/DataLoader]]
Steps
- Try to get the player (online)
- If not found, load the player using a
DataLoader - Execute logic after loading (sync)
- Handle errors properly
Implementation
public class InfoCommand extends PixelCommand<ExamplePlugin> {
@CommandDefinition(
label = "info",
usage = "(Player)",
rank = PixelRank.SUPPORTER
)
public InfoCommand(ExamplePlugin plugin) {
super(plugin);
}
@Override
public void onPlayerCommand(BukkitPixelPlayer player, String label, String[] args) {
if (args.length != 1) {
String playerUsage = getPlayerUsage();
player.sendMessage(playerUsage);
return;
}
String targetName = args[0];
PlayerHandler playerHandler = BukkitCoreLibrary.getPlayerHandler();
// try to get online player first
BukkitPixelPlayer target = playerHandler.getPlayer(targetName);
if (target != null) {
sendInformation(player, target);
return;
}
ExamplePlugin plugin = getPlugin();
DataLoader dataLoader = DataLoaderBuilder.create(DataLoaderBuilderType.SINGLE_PLAYER)
.name("InfoCommand")
.addRequireds(
// base player data is ALWAYS needed!
// rank data for colored name display
BasePlayerData.class,
RankPlayerData.class,
ExamplePlayerData.class
)
.build();
Logger logger = plugin.getLogger();
// loading player asynchronously
BukkitExecutor.runAsync(plugin, () -> {
BukkitPixelPlayer loadedTarget;
try {
loadedTarget = playerHandler.getPlayer(targetName, dataLoader);
} catch (IOException | ClassNotFoundException | SQLException exception) {
logger.log(Level.WARNING, "Failed to load player " + targetName + "!", exception);
BukkitExecutor.runSync(plugin, () -> {
if (player.isOnline()) player.sendMessage(DATA_LOAD_FAILED);
});
return;
}
// switch back to main thread
BukkitExecutor.runSync(plugin, () -> {
if (!player.isOnline()) return;
// can be null if required data is missing
if (loadedTarget == null) {
player.sendMessage(PLAYER_IS_NOT_REGISTERED);
return;
}
sendInformation(player, loadedTarget);
});
});
}
private void sendInformation(@NotNull BukkitPixelPlayer player, @NotNull BukkitPixelPlayer target) {
String coloredName = target.getColoredName();
ExamplePlayerData data = target.getData(ExamplePlayerData.class);
Timestamp firstJoin = data.getFirstJoin();
player.sendMessage(coloredName + " first join: " + TimeUtil.getTimeMoment(firstJoin));
}
}
Tab completion
@Override
public Collection<String> onPlayerTabComplete(BukkitPixelPlayer player, String label, String[] args) {
if (args.length != 1) return null;
// returning all online players across the network
ServerHandler serverHandler = BukkitCoreLibrary.getServerHandler();
return serverHandler.getOnlinePlayers(player);
}
Explanation
getPlayer(name)tries to get an online playerDataLoaderis used to load offline player data.addRequireds(...)defines which data must be loaded- Loading must happen asynchronously
- Logic must run back on the main thread
Notes
- Always try online lookup first (faster)
- Always use async loading for offline players
- Always switch back to the main thread before interacting with Bukkit
loadedTargetcan benullif required data is missing- Only request the data you actually need
Next step
[[Create a jedis packet & listener|Core-API/Build-your-first-feature/Systems/Create-a-jedis-packet-&-listener]]