### Create BrigadierCommand with Arguments Source: https://docs.papermc.io/velocity/dev/command-api Example of creating a BrigadierCommand with a literal argument and a subcommand that accepts a string argument. Includes permission checks and message sending. ```java package com.example.velocityplugin; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.Command; import com.mojang.brigadier.tree.LiteralCommandNode; import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.VelocityBrigadierMessage; import com.velocitypowered.api.proxy.ProxyServer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; public final class TestBrigadierCommand { public static BrigadierCommand createBrigadierCommand(final ProxyServer proxy) { LiteralCommandNode helloNode = BrigadierCommand.literalArgumentBuilder("test") // Here you can filter the subjects that can execute the command. // This is the ideal place to do "hasPermission" checks .requires(source -> source.hasPermission("test.permission")) // Here you can add the logic that will be used in // the execution of the "/test" command without any argument .executes(context -> { // Here you get the subject that executed the command CommandSource source = context.getSource(); Component message = Component.text("Hello World", NamedTextColor.AQUA); source.sendMessage(message); // Returning Command.SINGLE_SUCCESS means that the execution was successful // Returning BrigadierCommand.FORWARD will send the command to the server return Command.SINGLE_SUCCESS; }) // Using the "then" method, you can add sub-arguments to the command. // For example, this subcommand will be executed when using the command "/test " // A RequiredArgumentBuilder is a type of argument in which you can enter some undefined data // of some kind. For example, this example uses a StringArgumentType.word() that requires // a single word to be entered, but you can also use different ArgumentTypes provided by Brigadier // that return data of type Boolean, Integer, Float, other String types, etc .then(BrigadierCommand.requiredArgumentBuilder("argument", StringArgumentType.word()) // Here you can define the hints to be provided in case the ArgumentType does not provide them. // In this example, the names of all connected players are provided .suggests((ctx, builder) -> { // Here we provide the names of the players along with a tooltip, // which can be used as an explanation of a specific argument or as a simple decoration proxy.getAllPlayers().forEach(player -> builder.suggest( player.getUsername(), // A VelocityBrigadierMessage takes a component. // In this case, the player's name is provided with a rainbow // gradient created using MiniMessage. VelocityBrigadierMessage.tooltip( MiniMessage.miniMessage().deserialize("" + player.getUsername()) ) )); // If you do not need to add a tooltip to the hint // or your command is intended only for versions lower than Minecraft 1.13, // you can omit adding the tooltip, since for older clients, // the tooltip will not be displayed. builder.suggest("all"); return builder.buildFuture(); }) // Here the logic of the command "/test " is executed .executes(context -> { // Here you get the argument that the CommandSource has entered. // You must enter exactly the name as you have named the argument // and you must provide the class of the argument you expect, in this case... a String String argumentProvided = context.getArgument("argument", String.class); // This method will check if the given string corresponds to a // player's name and if it does, it will send a message to that player proxy.getPlayer(argumentProvided).ifPresent(player -> player.sendMessage(Component.text("Hello!")) ); ``` -------------------------------- ### Setting Velocity Packet Decode Logging Source: https://docs.papermc.io/velocity/reference/system-properties Example of how to set a system property to enable extensive packet decoding error logging when starting the Velocity server. This is done by adding the -D flag before the -jar command. ```bash java -Dvelocity.packet-decode-logging=true -jar velocity.jar ``` -------------------------------- ### Update Velocity and Install CrossStitch for Fabric 1.16+ Source: https://docs.papermc.io/velocity/faq If you are running a modded server with Fabric 1.16+ and encounter 'Argument type identifier unknown', update Velocity to at least 1.1.2 and install CrossStitch. ```text Argument type identifier : unknown. ``` -------------------------------- ### Get and Cancel Plugin Tasks Source: https://docs.papermc.io/velocity/dev/scheduler-api Retrieve all tasks scheduled by a specific plugin and cancel them individually. ```java Collection tasks = server.getScheduler().tasksByPlugin(plugin); // then you can control them, for example, cancel all task scheduled by a plugin for (ScheduledTask task : tasks) { task.cancel(); } ``` -------------------------------- ### Server Did Not Send Forwarding Request Source: https://docs.papermc.io/velocity/faq This error indicates that your backend server is not correctly configured to send forwarding requests to the proxy. Verify your player information forwarding setup. ```text Can't connect to server lobby: Your server did not send a forwarding request to the proxy. Is it set up correctly? ``` -------------------------------- ### Velocity Initial Launch Output Source: https://docs.papermc.io/velocity/getting-started This is a sample of the console output when Velocity successfully launches. ```log [05:41:13 INFO]: Booting up Velocity 3.5.0-SNAPSHOT (git-74c932e5-b363)... [05:41:13 INFO]: Loading localizations... [05:41:13 INFO]: Connections will use epoll channels, libdeflate (Linux aarch64) compression, OpenSSL (Linux aarch64) ciphers [05:41:13 INFO]: Loading plugins... [05:41:13 INFO]: Loaded 0 plugins [05:41:13 INFO]: Listening on /[0:0:0:0:0:0:0:0%0]:25565 [05:41:13 INFO]: Done (0.36s)! ``` -------------------------------- ### Recommended JVM Startup Flags for Velocity Source: https://docs.papermc.io/velocity/tuning Apply these flags to your `java` command before the `-jar` parameter to optimize Velocity's performance. These settings are recommended for improved garbage collection and execution. ```bash java -XX:+UseG1GC -XX:G1HeapRegionSize=4M -XX:+UnlockExperimentalVMOptions -XX:+ParallelRefProcEnabled -XX:+AlwaysPreTouch -XX:MaxInlineLevel=15 -jar velocity.jar ``` -------------------------------- ### Launch Velocity on Windows Source: https://docs.papermc.io/velocity/getting-started Use this batch script to launch Velocity on Windows. Ensure 'velocity.jar' is in the same directory. ```batch @echo off java -Xms1G -Xmx1G -XX:+UseG1GC -XX:G1HeapRegionSize=4M -XX:+UnlockExperimentalVMOptions -XX:+ParallelRefProcEnabled -XX:+AlwaysPreTouch -XX:MaxInlineLevel=15 -jar velocity.jar pause ``` -------------------------------- ### Echo RawCommand Implementation Source: https://docs.papermc.io/velocity/dev/command-api Implements the RawCommand interface to pass the command alias and arguments directly to the proxy without further processing. Useful for commands like /say or when using external command frameworks. ```java package com.example.velocityplugin; import com.velocitypowered.api.command.RawCommand; import net.kyori.adventure.text.Component; public final class EchoCommand implements RawCommand { @Override public void execute(final Invocation invocation) { invocation.source().sendMessage(Component.text(invocation.arguments())); } @Override public boolean hasPermission(final Invocation invocation) { return invocation.source().hasPermission("command.echo"); } } ``` -------------------------------- ### Minecraft 1.19.1 Chat System Change Source: https://docs.papermc.io/velocity/faq Starting with Minecraft 1.19.1, chat messages are encrypted. Plugins can no longer cancel or modify these messages directly. Consider using SignedVelocity for compatibility. ```text A plugin tried to cancel a signed chat message. This is no longer possible in 1.19.1 and newer. Disconnecting player ``` -------------------------------- ### SimpleCommand Implementation Source: https://docs.papermc.io/velocity/dev/command-api Implements the SimpleCommand interface for command execution, permission checks, and tab completion suggestions. The invocation.arguments() method provides arguments excluding the command alias. ```java package com.example.velocityplugin; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.SimpleCommand; import java.util.concurrent.CompletableFuture; import java.util.List; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; public final class TestCommand implements SimpleCommand { @Override public void execute(final Invocation invocation) { CommandSource source = invocation.source(); // Get the arguments after the command alias String[] args = invocation.arguments(); source.sendMessage(Component.text("Hello World!", NamedTextColor.AQUA)); } // This method allows you to control who can execute the command. // If the executor does not have the required permission, // the execution of the command and the control of its autocompletion // will be sent directly to the server on which the sender is located @Override public boolean hasPermission(final Invocation invocation) { return invocation.source().hasPermission("command.test"); } // With this method you can control the suggestions to send // to the CommandSource according to the arguments // it has already written or other requirements you need @Override public List suggest(final Invocation invocation) { return List.of(); } // Here you can offer argument suggestions in the same way as the previous method, // but asynchronously. It is recommended to use this method instead of the previous one // especially in cases where you make a more extensive logic to provide the suggestions @Override public CompletableFuture> suggestAsync(final Invocation invocation) { return CompletableFuture.completedFuture(List.of()); } } ``` -------------------------------- ### Build Brigadier Command Source: https://docs.papermc.io/velocity/dev/command-api Builds a Brigadier command with a root node. Returning Command.SINGLE_SUCCESS indicates successful execution, while BrigadierCommand.FORWARD sends the command to the server. ```java return new BrigadierCommand(helloNode); } } ``` -------------------------------- ### Velocity Server Configuration Source: https://docs.papermc.io/velocity/getting-started Configure your backend servers in the '[servers]' section of 'velocity.toml'. Each key is the server name and the value is its IP address. ```toml [servers] # Configure your servers here. Each key represents the server's name, and the value # represents the IP address of the server to connect to. lobby = "127.0.0.1:30066" factions = "127.0.0.1:30067" minigames = "127.0.0.1:30068" ``` -------------------------------- ### Register and Receive Plugin Messages from Backend Source: https://docs.papermc.io/velocity/dev/plugin-messaging Register a channel identifier and subscribe to plugin messages from backend servers. Handles incoming messages by marking them as 'handled' to prevent forwarding. ```java public static final MinecraftChannelIdentifier IDENTIFIER = MinecraftChannelIdentifier.from("custom:main"); @Subscribe public void onProxyInitialization(ProxyInitializeEvent event) { proxyServer.getChannelRegistrar().register(IDENTIFIER); } @Subscribe public void onPluginMessageFromBackend(PluginMessageEvent event) { // Check if the identifier matches first, no matter the source. // this allows setting all messages to IDENTIFIER as handled, // preventing any client-originating messages from being forwarded. if (!IDENTIFIER.equals(event.getIdentifier())) { return; } // mark PluginMessage as handled, indicating that the contents // should not be forwarding to their original destination. event.setResult(PluginMessageEvent.ForwardResult.handled()); // Alternatively: // mark PluginMessage as forwarded, indicating that the contents // should be passed through, as if Velocity is not present. // // this should be used with extreme caution, // as any client can freely send whatever it wants, pretending to be the proxy //event.setResult(PluginMessageEvent.ForwardResult.forward()); // only attempt parsing the data if the source is a backend server if (!(event.getSource() instanceof ServerConnection backend)) { return; } ByteArrayDataInput in = ByteStreams.newDataInput(event.getData()); // handle packet data } ``` -------------------------------- ### Define Server Login Order Source: https://docs.papermc.io/velocity/getting-started Specify the order of servers Velocity should attempt to connect players to upon login or server kick. Ensure server names match your configuration. ```yaml try = [ "lobby", "factions" ] ``` -------------------------------- ### Launch Velocity on macOS/Linux Source: https://docs.papermc.io/velocity/getting-started Use this shell script to launch Velocity on macOS or Linux. Make sure to make it executable with 'chmod u+x start.sh'. ```shell #!/bin/sh java -Xms1G -Xmx1G -XX:+UseG1GC -XX:G1HeapRegionSize=4M -XX:+UnlockExperimentalVMOptions -XX:+ParallelRefProcEnabled -XX:+AlwaysPreTouch -XX:MaxInlineLevel=15 -jar velocity*.jar ``` -------------------------------- ### Forge Server Read Timeout Startup Flag Source: https://docs.papermc.io/velocity/faq When configuring the read timeout for Forge servers, use the Java startup flag `-Dfml.readTimeout` followed by the desired timeout in seconds. ```bash -Dfml.readTimeout=120 ``` -------------------------------- ### Register and Receive Plugin Message from Player Source: https://docs.papermc.io/velocity/dev/plugin-messaging Register a channel identifier and handle incoming plugin messages from players. Ensure the message is marked as handled to prevent forwarding. Only process data if the source is a player. ```java public static final MinecraftChannelIdentifier IDENTIFIER = MinecraftChannelIdentifier.from("custom:main"); @Subscribe public void onProxyInitialization(ProxyInitializeEvent event) { proxyServer.getChannelRegistrar().register(IDENTIFIER); } @Subscribe public void onPluginMessageFromPlayer(PluginMessageEvent event) { // Check if the identifier matches first, no matter the source. if (!IDENTIFIER.equals(event.getIdentifier())) { return; } // mark PluginMessage as handled, indicating that the contents // should not be forwarding to their original destination. event.setResult(PluginMessageEvent.ForwardResult.handled()); // Alternatively: // mark PluginMessage as forwarded, indicating that the contents // should be passed through, as if Velocity is not present. //event.setResult(PluginMessageEvent.ForwardResult.forward()); // only attempt parsing the data if the source is a player if (!(event.getSource() instanceof Player player)) { return; } ByteArrayDataInput in = ByteStreams.newDataInput(event.getData()); // handle packet data } ``` -------------------------------- ### Send Plugin Message to Backend Server Source: https://docs.papermc.io/velocity/dev/plugin-messaging Send a plugin message to a backend server using any connected player. This method is useful for broadcasting information to the entire server. ```java public boolean sendPluginMessageToBackend(RegisteredServer server, ChannelIdentifier identifier, byte[] data) { // On success, returns true return server.sendPluginMessage(identifier, data); } ``` -------------------------------- ### Schedule a Repeating Task Source: https://docs.papermc.io/velocity/dev/scheduler-api Schedule a task to repeat at a fixed interval. Use TimeUnit for specifying the repeat duration. ```java server.getScheduler() .buildTask(plugin, () -> { // do stuff here }) .repeat(5L, TimeUnit.MINUTES) .schedule(); ``` -------------------------------- ### Create a basic event listener method Source: https://docs.papermc.io/velocity/dev/event-api Use the @Subscribe annotation to mark a method that should be called when a specific event is fired. Ensure you import Subscribe from com.velocitypowered.api.event. ```java @Subscribe public void onPlayerChat(PlayerChatEvent event) { // do stuff } ``` -------------------------------- ### Registering a BrigadierCommand Source: https://docs.papermc.io/velocity/dev/command-api Register a BrigadierCommand with custom aliases and associate it with the plugin. Ensure the CommandManager is obtained either through injection or by calling proxyServer.getCommandManager(). ```java package com.example.velocityplugin; import com.google.inject.Inject; import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.proxy.ProxyServer; @Plugin(id = "helloworld") public final class HelloWorldPlugin { private final ProxyServer proxy; @Inject public HelloWorldPlugin(ProxyServer proxy) { this.proxy = proxy; } @Subscribe public void onProxyInitialize(ProxyInitializeEvent event) { CommandManager commandManager = proxy.getCommandManager(); // Here you can add meta for the command, as aliases and the plugin to which it belongs (RECOMMENDED) CommandMeta commandMeta = commandManager.metaBuilder("test") // This will create a new alias for the command "/test" // with the same arguments and functionality .aliases("otherAlias", "anotherAlias") .plugin(this) .build(); // You can replace this with "new EchoCommand()" or "new TestCommand()" // SimpleCommand simpleCommand = new TestCommand(); // RawCommand rawCommand = new EchoCommand(); // The registration is done in the same way, since all 3 interfaces implement "Command" BrigadierCommand commandToRegister = TestBrigadierCommand.createBrigadierCommand(proxy); // Finally, you can register the command commandManager.register(commandMeta, commandToRegister); } } ``` -------------------------------- ### Run a Task Immediately Source: https://docs.papermc.io/velocity/dev/scheduler-api Execute a task asynchronously using the scheduler's cached thread pool by omitting delay and repeat methods. ```java server.getScheduler() .buildTask(plugin, () -> { // do stuff here }) .schedule(); ``` -------------------------------- ### Schedule a Delayed Task Source: https://docs.papermc.io/velocity/dev/scheduler-api Schedule a task to run after a specified delay. Requires the plugin instance and a TimeUnit for precision. ```java server.getScheduler() .buildTask(plugin, () -> { // do stuff here }) .delay(2L, TimeUnit.SECONDS) .schedule(); ``` -------------------------------- ### Send Plugin Message to Backend Server via Specific Player Source: https://docs.papermc.io/velocity/dev/plugin-messaging Send a plugin message to a backend server through a specific player's connection. This is useful for communicating player-specific information to their current backend server. ```java public boolean sendPluginMessageToBackendUsingPlayer(Player player, ChannelIdentifier identifier, byte[] data) { Optional connection = player.getCurrentServer(); if (connection.isPresent()) { // On success, returns true return connection.get().sendPluginMessage(identifier, data); } return false; } ``` -------------------------------- ### Asynchronous Event Handling with Continuation Source: https://docs.papermc.io/velocity/dev/event-api Use Continuation for annotation-based listeners to perform asynchronous operations and resume event processing. Requires a second 'Continuation' parameter in the listener method. ```java @Subscribe(priority = 100) public void onLogin(LoginEvent event, Continuation continuation) { doSomeAsyncProcessing().whenComplete((_, throwable) -> { if (throwable != null) { continuation.resumeWithException(throwable); } else { continuation.resume(); } }); } private CompletableFuture doSomeAsyncProcessing() { //... } ``` -------------------------------- ### Create Velocity Plugin Class Source: https://docs.papermc.io/velocity/dev/api-basics This is the main class for your Velocity plugin. It uses dependency injection to receive instances of ProxyServer and Logger. ```java package com.example.velocityplugin; import com.google.inject.Inject; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.proxy.ProxyServer; import org.slf4j.Logger; @Plugin(id = "myfirstplugin", name = "My First Plugin", version = "0.1.0-SNAPSHOT", url = "https://example.org", description = "I did it!", authors = {"Me"}) public class VelocityTest { private final ProxyServer server; private final Logger logger; @Inject public VelocityTest(ProxyServer server, Logger logger) { this.server = server; this.logger = logger; logger.info("Hello there! I made my first plugin with Velocity."); } } ``` -------------------------------- ### Configure Forced Hosts in Velocity Source: https://docs.papermc.io/velocity/faq Use this configuration in velocity.toml to direct players to specific servers based on the hostname they connect to, especially when using non-default ports. ```toml [forced-hosts] "forced1.example.com" = ["survival"] # survival.example.com ``` -------------------------------- ### Gradle Kotlin DSL Build Script Configuration Source: https://docs.papermc.io/velocity/dev/creating-your-first-plugin Configure your Gradle build script in Kotlin DSL to include the Papermc Maven repository and the Velocity API dependency. Ensure your project JDK is Java 21 or later. ```gradle repositories { maven { name = "papermc" url = uri("https://repo.papermc.io/repository/maven-public/") } } dependencies { compileOnly("com.velocitypowered:velocity-api:3.5.0-SNAPSHOT") annotationProcessor("com.velocitypowered:velocity-api:3.5.0-SNAPSHOT") } ``` -------------------------------- ### Enable IP Forwarding in BungeeCord Config Source: https://docs.papermc.io/velocity/faq If you encounter connection issues related to IP forwarding, ensure it is enabled in your BungeeCord configuration file. ```text Can't connect to server lobby: If you wish to use IP forwarding, please enable it in your Bungeecord config as well! ``` -------------------------------- ### Configure Read Timeout for Forge Servers Source: https://docs.papermc.io/velocity/faq To prevent connection timeouts when switching to large mod packs on Forge servers, adjust the `read-timeout` in `velocity.toml` and set the `-Dfml.readTimeout` startup flag on the Forge server. ```toml read-timeout = 120000 ``` -------------------------------- ### Gradle Groovy DSL Build Script Configuration Source: https://docs.papermc.io/velocity/dev/creating-your-first-plugin Configure your Gradle build script in Groovy DSL to include the Papermc Maven repository and the Velocity API dependency. Ensure your project JDK is Java 21 or later. ```gradle repositories { maven { name = 'papermc' url = 'https://repo.papermc.io/repository/maven-public/' } } dependencies { compileOnly 'com.velocitypowered:velocity-api:3.5.0-SNAPSHOT' annotationProcessor 'com.velocitypowered:velocity-api:3.5.0-SNAPSHOT' } ``` -------------------------------- ### Register Event Listener on Initialization Source: https://docs.papermc.io/velocity/dev/pitfalls Access the Velocity API safely during plugin initialization by subscribing to ProxyInitializeEvent. Avoid registering listeners or performing API operations during the plugin's constructor. ```java @Subscribe public void onProxyInitialization(ProxyInitializeEvent event) { // Do some operation demanding access to the Velocity API here. // For instance, we could register an event: server.getEventManager().register(this, new PluginListener()); } ``` -------------------------------- ### Maven POM XML Configuration with Annotation Processor Source: https://docs.papermc.io/velocity/dev/creating-your-first-plugin Configure your Maven pom.xml to include the Papermc repository and the Velocity API dependency, including the annotation processor. This is required for JDK 23 or later. ```xml papermc https://repo.papermc.io/repository/maven-public/ com.velocitypowered velocity-api 3.5.0-SNAPSHOT provided com.velocitypowered velocity-api 3.5.0-SNAPSHOT processor ``` -------------------------------- ### Firing a Custom Event Source: https://docs.papermc.io/velocity/dev/event-api Fire a custom event using the server's event manager. The `fire` method returns a CompletableFuture, allowing for subsequent logic execution after the event has been handled by all listeners. ```java server.getEventManager().fire(new PrivateMessageEvent(sender, recipient, message)).thenAccept((event) -> { // event has finished firing // do some logic dependent on the result }); ``` -------------------------------- ### Register an object as an event listener Source: https://docs.papermc.io/velocity/dev/event-api Register a separate object containing event handler methods with the EventManager. This is useful for organizing listeners in larger plugins. Both the plugin instance and the listener object are passed as arguments. ```java server.getEventManager().register(plugin, listener); ``` ```java @Plugin(id = "myfirstplugin", name = "My Plugin", version = "0.1.0", dependencies = {@Dependency(id = "wonderplugin")}) public class VelocityTest { private final ProxyServer server; private final Logger logger; @Inject public VelocityTest(ProxyServer server, Logger logger) { this.server = server; this.logger = logger; } @Subscribe public void onInitialize(ProxyInitializeEvent event) { server.getEventManager().register(this, new MyListener()); } } public class MyListener { @Subscribe public void onPlayerChat(PlayerChatEvent event) { // do something here } } ``` -------------------------------- ### Send Plugin Message to Player Source: https://docs.papermc.io/velocity/dev/plugin-messaging Sends a plugin message to a specific player. Returns true if the message was sent successfully. ```java public boolean sendPluginMessageToPlayer(Player player, ChannelIdentifier identifier, byte[] data) { // On success, returns true return player.sendPluginMessage(identifier, data); } ``` -------------------------------- ### Access Plugin Directory with @DataDirectory Source: https://docs.papermc.io/velocity/dev/api-basics Inject a Path object representing your plugin's data directory into your plugin's constructor using the @DataDirectory annotation. Velocity typically works with Path objects, but you can convert to a File using Path#toFile() if necessary. ```java private final Path dataDirectory; @Inject public VelocityTest(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) { this.server = server; this.logger = logger; this.dataDirectory = dataDirectory; } ``` -------------------------------- ### Creating a Custom Event Class Source: https://docs.papermc.io/velocity/dev/event-api Define a custom event class with necessary fields and a constructor. Custom events in Velocity do not need to extend or implement any specific interfaces. ```java public class PrivateMessageEvent { private final Player sender; private final Player recipient; private final String message; public PrivateMessageEvent(Player sender, Player recipient, String message) { this.sender = sender; this.recipient = recipient; this.message = message; } public Player sender() { return sender; } public Player recipient() { return recipient; } public String message() { return message; } // toString, equals, and hashCode may be added as needed } ``` -------------------------------- ### Dependency Injection in Plugin Constructor Source: https://docs.papermc.io/velocity/dev/api-basics Velocity uses dependency injection to provide instances of core services like ProxyServer and Logger to your plugin's constructor. This allows you to interact with the Velocity API. ```java @Inject public VelocityTest(ProxyServer server, Logger logger) { this.server = server; this.logger = logger; logger.info("Hello there, it's a test plugin I made!"); } ``` -------------------------------- ### Schedule a Self-Cancelling Task Source: https://docs.papermc.io/velocity/dev/scheduler-api Create a task that can cancel itself based on a condition within its execution logic, using a Consumer. ```java AtomicInteger integer = new AtomicInteger(0); ScheduledTask task = server.getScheduler() .buildTask(plugin, (selfTask) -> { // do stuff here, for example... if (integer.addAndGet(1) > 10) { selfTask.cancel(); } }) .repeat(Duration.ofSeconds(4L)) .schedule(); ``` -------------------------------- ### Maven POM XML Configuration without Annotation Processor Source: https://docs.papermc.io/velocity/dev/creating-your-first-plugin Configure your Maven pom.xml to include the Papermc repository and the Velocity API dependency. This configuration is suitable if not explicitly adding the annotation processor. ```xml papermc https://repo.papermc.io/repository/maven-public/ com.velocitypowered velocity-api 3.5.0-SNAPSHOT provided org.apache.maven.plugins maven-compiler-plugin com.velocitypowered velocity-api 3.5.0-SNAPSHOT ``` -------------------------------- ### Declaring a Plugin Dependency Source: https://docs.papermc.io/velocity/dev/dependency-management Add a dependency on another plugin by including the @Dependency annotation within the 'dependencies' array of the @Plugin annotation. The plugin will load after the specified dependency. ```java @Plugin( id = "myfirstplugin", name = "My Plugin", version = "0.1.0", dependencies = { @Dependency(id = "wonderplugin") } ) public class VelocityTest { // ... } ``` -------------------------------- ### Register a functional-style event listener Source: https://docs.papermc.io/velocity/dev/event-api Register an event listener using a functional interface (lambda expression) with the EventManager. This approach is concise for simple event handling logic. ```java server.getEventManager().register(this, PlayerChatEvent.class, event -> { // do something here }); ``` -------------------------------- ### Implement PrivateMessageEvent with GenericResult Source: https://docs.papermc.io/velocity/dev/event-api This class implements ResultedEvent to handle private messages. Listeners can deny the event using GenericResult.denied(). ```java public class PrivateMessageEvent implements ResultedEvent { private final Player sender; private final Player recipient; private final String message; private GenericResult result = GenericResult.allowed(); // Allowed by default public PrivateMessageEvent(Player sender, Player recipient, String message) { this.sender = sender; this.recipient = recipient; this.message = message; } public Player sender() { return sender; } public Player recipient() { return recipient; } public String message() { return message; } @Override public GenericResult result() { return result; } @Override public void setResult(GenericResult result) { this.result = Objects.requireNonNull(result); } } ``` -------------------------------- ### Functional Listener with EventTask Source: https://docs.papermc.io/velocity/dev/event-api Implement AwaitingEventExecutor for functional listeners to return an EventTask for asynchronous event handling. Use EventTask.async() for simple asynchronous execution. ```java server.getEventManager().register(this, PlayerChatEvent.class, (AwaitingEventExecutor) event -> { if (mustFurtherProcess(event)) { return EventTask.async(() => ...); } return null; }); ```