Skip to main content

Arguments

Argument types are datatypes that we can use instead of strings.

Experimental

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:

YourPluginClass.java
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

Argument 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 nameReturn datatype classDescription
GAME_EVENTGameEventGame events
STRUCTURE_TYPEStructureTypeStructure types
MOB_EFFECTPotionEffectTypePotion effect
BLOCKBlockTypeBlock type
ITEMItemTypeItem type
CAT_VARIANTCat.TypeCat variants
FROG_VARIANTFrog.VariantFrog variants
VILLAGER_PROFESSIONVillager.ProfessionVillager professions
VILLAGER_TYPEVillager.TypeVillager biome specific type
MAP_DECORATION_TYPEMapCursor.TypeTypes of sprites displayed on a map
MENUMenuTypeMenu type
ATTRIBUTEAttributeEntity attribute
FLUIDFluidFluid types
SOUND_EVENTSoundEvents that trigger sound effects
BIOMEBiomeBiome type
STRUCTUREStructureStructures
TRIM_MATERIALTrimMaterialMaterials used to trim armor
TRIM_PATTERNTrimPatternTrim patterns
DAMAGE_TYPEDamageTypeAll types of damage dealt to an entity
WOLF_VARIANTWolf.VariantWolf variants
ENCHANTMENTEnchantmentEnchantment type
JUKEBOX_SONGJukeboxSongMusic disc songs
BANNER_PATTERNPatternTypeBanner patterns
PAINTING_VARIANTArtPainting variants
INSTRUMENTMusicInstrumentGoat horns
ENTITY_TYPEEntityTypeEvery entity type
PARTICLE_TYPEParticleEvery particle type
POTIONPotionTypeEvery potion type
MEMORY_MODULE_TYPEMemoryKeyKeys for saving per-entity data

Paper specifies many more 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.

IceCreamType.java
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:

IceCreamTypeArgument.java
public class IceCreamTypeArgument implements CustomArgumentType.Converted<IceCreamType, String> {

@Override
public 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 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 case String) into our custom type.

  • getNativeType() returns the type of string that our command argument uses. This uses a single word, so we return StringArgumentType.word().

  • listSuggestions() returns CompletableFuture<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:

TestPlugin.java
@Override
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(ctx -> {
IceCreamType argumentResponse = ctx.getArgument("flavor", IceCreamType.class);
ctx.getSource().getSender().sendMessage(Component.text("You ordered: " + argumentResponse));
return Command.SINGLE_SUCCESS;
})
).build()
);
});
}

Now that we have registered the command, we can execute it in-game:

command with suggestions Look, we can even see our tooltip and if we execute the command, we get the message we specified:

executed command