warps¶
Named warps: /warp set <name> saves where you stand, /warp <name> teleports you there, and
/warps lists them. The example shows Paper commands:
one Brigadier tree and one BasicCommand, both registered by the bundled paper:commands
module. The full source lives in
examples/paper/warps.
The plugin main¶
WarpsPlugin is just the registry hook. Its paper-plugin.yml declares no commands.
package org.weftkit.examples.warps;
import org.weftkit.wiring.Registry;
import org.weftkit.wiring.bukkit.WeftPlugin;
import org.weftkit.wiring.registry.ComponentRegistry;
@Registry
public final class WarpsPlugin extends WeftPlugin {
@Override
protected ComponentRegistry registry() {
return WeftWiring.INSTANCE;
}
}
name: Warps
version: 1.0.0
main: org.weftkit.examples.warps.WarpsPlugin
api-version: "1.21"
description: Named warps with /warp and /warps, registered as Paper commands by weftkit.
The store¶
WarpStore holds the warps in memory, sorted by name.
package org.weftkit.examples.warps.storage;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.bukkit.Location;
import org.weftkit.wiring.Singleton;
import org.weftkit.wiring.Wired;
@Wired
@Singleton
public final class WarpStore {
private final Map<String, Location> warps = new TreeMap<>();
public WarpStore() {}
public void set(String name, Location location) {
warps.put(name, location.clone());
}
public Location find(String name) {
Location warp = warps.get(name);
return warp == null ? null : warp.clone();
}
public Set<String> names() {
return new TreeSet<>(warps.keySet());
}
}
The tree command¶
WarpCommand is a CommandTree: a set subcommand behind a permission, and a name argument
that suggests the existing warps. Brigadier prefers a literal over an argument at the same level,
so /warp set could never teleport to a warp named set, and the subcommand refuses that name. The root requires a player, so the console never sees it.
package org.weftkit.examples.warps.commands;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.weftkit.examples.warps.storage.WarpStore;
import org.weftkit.wiring.Singleton;
import org.weftkit.wiring.Wired;
import org.weftkit.wiring.paper.commands.CommandTree;
import org.weftkit.wiring.paper.commands.PaperCommand;
@Wired
@Singleton
@PaperCommand(value = "warp", description = "Teleport to or set a named warp")
public final class WarpCommand implements CommandTree {
private final WarpStore warps;
public WarpCommand(WarpStore warps) {
this.warps = warps;
}
@Override
public LiteralArgumentBuilder<CommandSourceStack> tree(String label) {
return Commands.literal(label)
.requires(source -> source.getExecutor() instanceof Player)
.then(Commands.literal("set")
.requires(source -> source.getSender().hasPermission("warps.set"))
.then(Commands.argument("name", StringArgumentType.word())
.executes(this::set)))
.then(Commands.argument("name", StringArgumentType.word())
.suggests((context, builder) -> {
warps.names().forEach(builder::suggest);
return builder.buildFuture();
})
.executes(this::teleport));
}
private int set(CommandContext<CommandSourceStack> context) {
Player player = (Player) context.getSource().getExecutor();
String name = StringArgumentType.getString(context, "name");
if (name.equals("set")) {
player.sendMessage(Component.text("A warp cannot be named set.", NamedTextColor.RED));
return 0;
}
warps.set(name, player.getLocation());
player.sendMessage(Component.text("Warp " + name + " set.", NamedTextColor.YELLOW));
return Command.SINGLE_SUCCESS;
}
private int teleport(CommandContext<CommandSourceStack> context) {
Player player = (Player) context.getSource().getExecutor();
String name = StringArgumentType.getString(context, "name");
Location warp = warps.find(name);
if (warp == null) {
player.sendMessage(Component.text("No warp named " + name + ".", NamedTextColor.RED));
return 0;
}
player.teleport(warp);
return Command.SINGLE_SUCCESS;
}
}
The basic command¶
WarpsCommand is a plain BasicCommand with an alias.
package org.weftkit.examples.warps.commands;
import io.papermc.paper.command.brigadier.BasicCommand;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import java.util.Set;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.weftkit.examples.warps.storage.WarpStore;
import org.weftkit.wiring.Singleton;
import org.weftkit.wiring.Wired;
import org.weftkit.wiring.paper.commands.PaperCommand;
@Wired
@Singleton
@PaperCommand(value = "warps", description = "List every warp", aliases = "warplist")
public final class WarpsCommand implements BasicCommand {
private final WarpStore warps;
public WarpsCommand(WarpStore warps) {
this.warps = warps;
}
@Override
public void execute(CommandSourceStack source, String[] args) {
Set<String> names = warps.names();
if (names.isEmpty()) {
source.getSender().sendMessage(Component.text("No warps yet.", NamedTextColor.GRAY));
return;
}
source.getSender().sendMessage(Component.text("Warps: " + String.join(", ", names), NamedTextColor.YELLOW));
}
}
Run it¶
Build the example as described in the overview, drop the jar into the plugins/
folder of a Paper 1.21.1 or newer server, run /warp set spawn, wander off, and /warp spawn
brings you back.