10 min

Examples & Recipes

Complete code examples for common modding tasks. Copy, adapt, and learn.

Welcome Plugin

A complete plugin that welcomes players with a title and particle effect:

WelcomePlugin.javajava
1public class WelcomePlugin extends JavaPlugin {
2
3 public WelcomePlugin(JavaPluginInit init) {
4 super(init);
5 }
6
7 @Override
8 protected void setup() {
9 getLogger().at(Level.INFO).log("WelcomePlugin loading...");
10 }
11
12 @Override
13 protected void start() {
14 getEventRegistry().register(PlayerConnectEvent.class, this::onPlayerJoin);
15 getLogger().at(Level.INFO).log("WelcomePlugin started!");
16 }
17
18 @Override
19 protected void shutdown() {
20 getLogger().at(Level.INFO).log("WelcomePlugin stopped.");
21 }
22
23 private void onPlayerJoin(PlayerConnectEvent event) {
24 Player player = event.getPlayer();
25
26 // Show welcome title (thread-safe)
27 showWelcomeTitle(player);
28
29 // Spawn particles (thread-safe)
30 spawnWelcomeParticles(player);
31
32 // Log the join
33 getLogger().at(Level.INFO).log("%s joined the server", player.getDisplayName());
34 }
35
36 private void showWelcomeTitle(Player player) {
37 FormattedMessage title = new FormattedMessage(
38 "WELCOME",
39 null, null, null, null,
40 "#00FF88",
41 MaybeBool.True, // bold
42 MaybeBool.False, // italic
43 MaybeBool.False, // monospace
44 MaybeBool.False, // underlined
45 null, false
46 );
47
48 FormattedMessage subtitle = new FormattedMessage(
49 player.getDisplayName(),
50 null, null, null, null,
51 "#AAAAAA",
52 MaybeBool.False, // bold
53 MaybeBool.True, // italic
54 MaybeBool.False, // monospace
55 MaybeBool.False, // underlined
56 null, false
57 );
58
59 ShowEventTitle packet = new ShowEventTitle(
60 0.5f, 0.5f, 3.0f, null, true, title, subtitle
61 );
62
63 player.getPlayerConnection().write(packet);
64 }
65
66 private void spawnWelcomeParticles(Player player) {
67 TransformComponent transform = player.getTransformComponent();
68 Vector3d pos = transform.getPosition();
69
70 SpawnParticleSystem packet = new SpawnParticleSystem(
71 "Sparkle",
72 new Position(pos.x, pos.y + 2, pos.z),
73 new Direction(0f, 0f, 0f),
74 2.0f,
75 null
76 );
77
78 for (Player p : player.getWorld().getPlayers()) {
79 p.getPlayerConnection().write(packet);
80 }
81 }
82}

Chat Filter

async event
PlayerChatEvent is an async event. Be careful with thread safety.
ChatFilter.javajava
1public class ChatFilter {
2
3 private final Set<String> bannedWords = new HashSet<>();
4 private final Map lastMessage = new ConcurrentHashMap<>();
5 private final long COOLDOWN_MS = 1000; // 1 second
6
7 public ChatFilter() {
8 bannedWords.add("badword1");
9 bannedWords.add("badword2");
10 }
11
12 // Note: PlayerChatEvent is async and keyed by world name
13 public void handleChat(PlayerChatEvent event) {
14 PlayerRef sender = event.getSender();
15 String message = event.getContent().toLowerCase();
16 UUID uuid = sender.getUuid();
17
18 // Check spam
19 long now = System.currentTimeMillis();
20 Long last = lastMessage.get(uuid);
21
22 if (last != null && (now - last) < COOLDOWN_MS) {
23 event.setCancelled(true);
24 return;
25 }
26
27 // Check banned words
28 for (String word : bannedWords) {
29 if (message.contains(word)) {
30 event.setCancelled(true);
31 return;
32 }
33 }
34
35 // Update last message time
36 lastMessage.put(uuid, now);
37 }
38}

Custom Command

commands run on forkjoinpool
Command execute() runs on ForkJoinPool.commonPool(), not the world thread. Use world.execute() for entity modifications.
SpeedCommand.javajava
1public class SpeedCommand extends AbstractCommand {
2
3 private final RequiredArg multiplierArg;
4
5 public SpeedCommand() {
6 super("speed", "Set your movement speed");
7 this.multiplierArg = withRequiredArg(
8 "multiplier",
9 "Speed multiplier (0.1 - 5.0)",
10 ArgTypes.DOUBLE
11 );
12 }
13
14 @Override
15 @Nullable
16 protected CompletableFuture execute(@Nonnull CommandContext ctx) {
17 if (!ctx.isPlayer()) {
18 ctx.sendMessage(Message.raw("Only players can use this command!"));
19 return null;
20 }
21
22 double multiplier = ctx.get(multiplierArg);
23
24 if (multiplier < 0.1 || multiplier > 5.0) {
25 ctx.sendMessage(Message.raw("Multiplier must be between 0.1 and 5.0"));
26 return null;
27 }
28
29 Player player = ctx.senderAs(Player.class);
30 MovementSettings settings = MovementConfig.DEFAULT_MOVEMENT.toPacket();
31
32 settings.baseSpeed = settings.baseSpeed * (float) multiplier;
33 settings.forwardWalkSpeedMultiplier *= (float) multiplier;
34 settings.forwardRunSpeedMultiplier *= (float) multiplier;
35 settings.forwardSprintSpeedMultiplier *= (float) multiplier;
36
37 player.getPlayerConnection().write(new UpdateMovementSettings(settings));
38 ctx.sendMessage(Message.raw("Speed set to " + multiplier + "x"));
39
40 return null;
41 }
42}
more examples
These examples demonstrate common patterns. Combine them to create more complex functionality. Always refer to the API reference pages for details on specific methods.