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 @Override8 protected void setup() {9 getLogger().at(Level.INFO).log("WelcomePlugin loading...");10 }11 12 @Override13 protected void start() {14 getEventRegistry().register(PlayerConnectEvent.class, this::onPlayerJoin);15 getLogger().at(Level.INFO).log("WelcomePlugin started!");16 }17 18 @Override19 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 join33 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, // bold42 MaybeBool.False, // italic43 MaybeBool.False, // monospace44 MaybeBool.False, // underlined45 null, false46 );47 48 FormattedMessage subtitle = new FormattedMessage(49 player.getDisplayName(),50 null, null, null, null,51 "#AAAAAA",52 MaybeBool.False, // bold53 MaybeBool.True, // italic54 MaybeBool.False, // monospace55 MaybeBool.False, // underlined56 null, false57 );58 59 ShowEventTitle packet = new ShowEventTitle(60 0.5f, 0.5f, 3.0f, null, true, title, subtitle61 );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 null76 );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 second6 7 public ChatFilter() {8 bannedWords.add("badword1");9 bannedWords.add("badword2");10 }11 12 // Note: PlayerChatEvent is async and keyed by world name13 public void handleChat(PlayerChatEvent event) {14 PlayerRef sender = event.getSender();15 String message = event.getContent().toLowerCase();16 UUID uuid = sender.getUuid();17 18 // Check spam19 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 words28 for (String word : bannedWords) {29 if (message.contains(word)) {30 event.setCancelled(true);31 return;32 }33 }34 35 // Update last message time36 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.DOUBLE11 );12 }13 14 @Override15 @Nullable16 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.