Arguments
Argument types are datatypes that we can use instead of strings.
Paper's command system is still experimental and may change in the future.
Basic usage of arguments
You can add arguments to a command by doing the following:
public class YourPluginClass extends JavaPlugin {
@Override
public void onEnable() {
final LifecycleEventManager<Plugin> manager = this.getLifecycleManager();
manager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
final Commands commands = event.registrar();
commands.register(
Commands.literal("enchantmentargumentcommand")
.then(
Commands.argument("enchantmentargument", ArgumentTypes.resource(RegistryKey.ENCHANTMENT))
.executes(ctx -> {
ctx.getSource().getSender().sendPlainMessage(
ctx.getArgument("enchantmentargument", Enchantment.class).getKey().toString()
);
return Command.SINGLE_SUCCESS;
})
).build()
);
});
}
}
This command has one argument of the Enchantment
datatype. When the command is executed, the command
sender will get a message containing the key of the enchantment they selected.
Advantages over string-based arguments
- Direct conversion to usable type
- Client-side error handling
- Custom types
- Non alphanumerical sorting
Enchantment types
By default, you can use the registry API to get simple argument types like blocks, items, potions and many more. In the example above, we used the Enchantment Argument type, but there are many others:
Predefined types (Registry)
Registry key name | Return datatype class | Description |
---|---|---|
GAME_EVENT | GameEvent | Game events |
STRUCTURE_TYPE | StructureType | Structure types |
INSTRUMENT | MusicInstrument | Goat horns |
MOB_EFFECT | PotionEffectType | Potion effect |
BLOCK | BlockType | Block type |
ITEM | ItemType | Item type |
CAT_VARIANT | Cat.Type | Cat variants |
FROG_VARIANT | Frog.Variant | Frog variants |
VILLAGER_PROFESSION | Villager.Profession | Villager professions |
VILLAGER_TYPE | Villager.Type | Villager biome specific type |
MAP_DECORATION_TYPE | MapCursor.Type | Types of sprites displayed on a map |
MENU | MenuType | Menu type |
STRUCTURE | Structure | Structures |
TRIM_MATERIAL | TrimMaterial | Materials used to trim armor |
TRIM_PATTERN | TrimPattern | Trim patterns |
DAMAGE_TYPE | DamageType | All types of damage dealt to an entity |
ENCHANTMENT | Enchantment | Enchantment type |
JUKEBOX_SONG | JukeboxSong | Music disc songs |
WOLF_VARIANT | Wolf.Variant | Wolf variants |
BANNER_PATTERN | PatternType | Banner patterns |
BIOME | Biome | Biome type |
PAINTING_VARIANT | Art | Painting variants |
ATTRIBUTE | Attribute | Entity attribute |
ENTITY_TYPE | EntityType | Every entity type |
PARTICLE_TYPE | Particle | Every particle type |
POTION | PotionType | Every potion type |
SOUND_EVENT | Sound | Events that trigger sound effects |
MEMORY_MODULE_TYPE | MemoryKey | Keys for saving per-entity data |
FLUID | Fluid | Fluid types |
Paper specifies many argument types. For more information on them, see ArgumentTypes
Custom types
Custom arguments can be created by implementing the CustomArgumentType interface.
Now, let's say that we want to implement a command which lets you order ice cream. For that, we add an enum that specifies all available values for our custom type.
public enum IceCreamType {
VANILLA,
CHOCOLATE,
STRAWBERRY,
MINT,
COOKIES
}
Now, we have to define the argument itself. We do this by implementing the CustomArgumentType.Converted interface:
public class IceCreamTypeArgument implements CustomArgumentType.Converted<IceCreamType, String> {
@Override
public @NotNull IceCreamType convert(String nativeType) throws CommandSyntaxException {
try {
return IceCreamType.valueOf(nativeType.toUpperCase(Locale.ENGLISH));
} catch (IllegalArgumentException ignored) {
Message message = MessageComponentSerializer.message().serialize(Component.text("Invalid flavor %s!".formatted(nativeType), NamedTextColor.RED));
throw new CommandSyntaxException(new SimpleCommandExceptionType(message), message);
}
}
@Override
public @NotNull ArgumentType<String> getNativeType() {
return StringArgumentType.word();
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
for (IceCreamType flavor : IceCreamType.values()) {
builder.suggest(flavor.name(), MessageComponentSerializer.message().serialize(Component.text("look at this cool green tooltip!", NamedTextColor.GREEN)));
}
return builder.buildFuture();
}
}
We implemented the CustomArgumentType.Converted interface. This interface takes two type arguments: our custom enum, T, and a native Java type called N.
-
convert()
converts the native type (in this caseString
) into our custom type. -
getNativeType()
returns the type of string that our command argument uses. This uses a single word, so we returnStringArgumentType.word()
. -
listSuggestions()
returnsCompletableFuture<Suggestions>
so that the client can suggest all available options. We can even add tooltips to the suggestions to explain them in greater detail.
We then need to register the command:
public void onEnable() {
final LifecycleEventManager<Plugin> manager = this.getLifecycleManager();
manager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
final Commands commands = event.registrar();
commands.register(Commands.literal("ordericecream")
.then(
Commands.argument("flavor", new IceCreamTypeArgument())
.executes(commandContext -> {
IceCreamType argumentResponse = commandContext.getArgument("flavor", IceCreamType.class);
commandContext.getSource().getSender().sendMessage(Component.text("You ordered: " + argumentResponse));
return Command.SINGLE_SUCCESS;
})
).build()
);
});
}
Now that we have registered the command, we can execute it ingame:
Look, we can even see our tooltip and if we execute the command, we get the message we specified