### Request All Installed Addons Source: https://dev.labymod.net/pages/server/labymod/moderation/installed-addons Request all installed addons from a player and process the response containing a list of all installed addons. This method is not recommended due to potential traffic and processing overhead. ```java // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Request all installed addons and optionally handle the response labyModPlayer.requestInstalledAddons(response -> { List installedAddons = response.getInstalledAddons(); // Handle the response }); ``` -------------------------------- ### Create Discord Rich Presence with Game Mode and Start Time Source: https://dev.labymod.net/pages/server/labymod/features/discord Use `DiscordRPC.createWithStart` to initialize a Discord Rich Presence with the current game mode and a specified start time. Useful for tracking session duration. ```java DiscordRPC discordRPC = DiscordRPC.createWithStart("Example Game Mode", System.currentTimeMillis()); ``` -------------------------------- ### Request Specific Installed Addons Source: https://dev.labymod.net/pages/server/labymod/moderation/installed-addons Request specific installed addons from a player and handle their enabled/disabled/installed state in the callback. An empty list requests all addons, which is not recommended. ```java List addonsToRequest = new ArrayList<>(); // Add the namespace of all addons that you want to request // Keeping the List empty requests all addons (not recommended) addonsToRequest.add("voicechat"); // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Request the installed addons and optionally handle the response labyModPlayer.requestInstalledAddons(addonsToRequest, response -> { if (response.isEnabled("voicechat")) { // VoiceChat is enabled } else if (response.isDisabled("voicechat")) { // VoiceChat is disabled } else { // VoiceChat is not installed } }); ``` -------------------------------- ### Example Configuration Class Source: https://dev.labymod.net/pages/addon/features/config Defines various configuration properties including boolean switches, text fields, sub-settings, dropdowns, and a button action. Use this as a base for your addon's configuration. ```java @ConfigName("settings") @SpriteTexture("example_sprite.png") public class ExampleConfiguration extends AddonConfig { @SpriteSlot(x = 1, y = 1) @SwitchSetting private ConfigProperty enabled = new ConfigProperty<>(true); @SettingSection("print") @SpriteSlot(x = 6) @TextFieldSetting private ConfigProperty text = new ConfigProperty<>("Hello World!"); @SettingSection("miscellaneous") @SpriteSlot(x = 1, y = 3) private ExampleSubSettings subSettings = new ExampleSubSettings(); @SpriteSlot(y = 3) @DropdownSetting private ConfigProperty type = new ConfigProperty<>(ExampleEnum.SCALENE_TRIANGLE); @MethodOrder(after = "text") @SpriteSlot(x = 2, y = 6) @ButtonSetting public void print(Setting setting) { Notification.Builder builder = Notification.builder() .title(Component.text("INFO")) .text(Component.text(setting.getId() + " was clicked! " + this.text)) .type(Type.SYSTEM); Laby.labyAPI().notificationController().push(builder.build()); } @Override public ConfigProperty enabled() { return this.enabled; } } ``` -------------------------------- ### Example Command Implementation Source: https://dev.labymod.net/pages/addon/features/commands This snippet shows a basic command implementation with a primary command and an alias. It registers a notification and handles different argument counts. Returns true to consume the command. ```java public class ExampleCommand extends Command { private final NotificationController notificationController; public ExampleCommand() { super("notify", "alias"); this.notificationController = Laby.references().notificationController(); this.withSubCommand(new ExampleSubCommand()); } @Override public boolean execute(String prefix, String[] arguments) { if (prefix.equalsIgnoreCase("alias")) { this.displayMessage(Component.text("You used an Alias!", NamedTextColor.AQUA)); return false; } String title; String text; if (arguments.length < 2) { title = "Title"; text = "Text"; } else { title = arguments[0]; text = arguments[1]; } notificationController.push( Notification.builder() .title(Component.text(title)) .text(Component.text(text)) .build() ); return true; } } ``` -------------------------------- ### Example Configuration JSON Source: https://dev.labymod.net/pages/addon/features/config JSON representation of the configuration structure, mapping setting names and properties. This is used for localization and defining display names for settings. ```json { "example": { "settings": { "name": "ExampleAddon", "enabled": { "name": "Enabled" }, "text": { "name": "Text to be Printed" }, "print": { "name": "Click Me to Print the Text", "text": "Print!" }, "subSettings": { "name": "Miscellaneous Sub Settings", "labyModColor": { "name": "The LabyMod Color" }, "keyBind": { "name": "Key Bind" } }, "type": { "name": "Display Type", "entries": { "heart": "Heart", "circle": "Circle", "rectangle": "Rectangle", "triangle": "Triangle", "scaleneTriangle": "Weird Shape" } }, "header": { "print": { "name": "Print!" }, "miscellaneous": { "name": "Miscellaneous" } } } } } ``` -------------------------------- ### Example Integration Class Source: https://dev.labymod.net/pages/server/integrations Implement the LabyModProtocolIntegration interface to create a new integration. The initialize method is required and is called when the integration is loaded. The createIntegrationPlayer method is optional and called when a player connects. ```java import net.labymod.serverapi.core.AbstractLabyModProtocolService; import net.labymod.serverapi.core.integration.LabyModProtocolIntegration; public class ExampleIntegration implements LabyModProtocolIntegration { private AbstractLabyModProtocolService protocolService; @Override public void initialize(AbstractLabyModProtocolService protocolService) { // Check if the field is already set, if so throw an exception. This is a good practice to // prevent the integration from being initialized multiple times. if (this.protocolService != null) { throw new IllegalStateException("VoiceChatIntegration is already initialized"); } // Store the protocol service for later use this.protocolService = protocolService; } } ``` -------------------------------- ### Example Sub-Settings Class Source: https://dev.labymod.net/pages/addon/features/config Defines nested configuration options, including a switch, color picker, and key bind. Use this to group related settings within a parent configuration. ```java public class ExampleSubSettings extends Config { @ShowSettingInParent @SpriteSlot(x = 7) @SwitchSetting private ConfigProperty enabled = new ConfigProperty<>(true); @SpriteSlot(x = 1, y = 6) @ColorPickerSetting private ConfigProperty labyModColor = new ConfigProperty<>( new Color(10, 85, 165).getRGB()); @SpriteSlot(x = 2) @KeyBindSetting private ConfigProperty keyBind = new ConfigProperty<>(Key.F); } ``` -------------------------------- ### Example Addon Configuration Class Source: https://dev.labymod.net/pages/addon/features/config Defines the configuration structure for an addon, including various setting types like switches, text fields, and sliders. Use `@IntroducedIn` to specify version compatibility. ```java @ConfigName("settings") public class ExampleConfiguration extends AddonConfig { @SwitchSetting private final ConfigProperty enabled = new ConfigProperty<>(true); @TextFieldSetting private final ConfigProperty oldConfigOption = new ConfigProperty<>(""); @IntroducedIn(namespace = "example", value = "1.0.1") @SliderSetting(min = 1, max = 10) private final ConfigProperty brandNewConfigOption = new ConfigProperty<>(7); public ConfigProperty enabled() { return this.enabled; } } ``` -------------------------------- ### Send Installed Addons Request Packet with Response Handler Source: https://dev.labymod.net/pages/server/labymod/moderation/installed-addons Sends a request for installed addons and includes a direct response handler for the InstalledAddonsResponsePacket. The handler processes the list of installed addons. ```java // Create or get a List of addons to request (array is also possible) List addonsToRequest = new ArrayList<>(); // Add the namespace of all addons that you want to request // Keeping the List empty requests all addons (not recommended) addonsToRequest.add("voicechat"); // Get the LabyModProtocol LabyModProtocol labyModProtocol = LabyModProtocolService.get().labyModProtocol(); // Send the packet and handle the response labyModProtocol.sendPacket( uniqueId, new InstalledAddonsRequestPacket(recommendations), InstalledAddonsResponsePacket.class, response -> { List installedAddons = response.getInstalledAddons(); // Handle the response return false; // return false because no other response is expected } ); ``` -------------------------------- ### Example Ping Command Source: https://dev.labymod.net/pages/addon/features/version-dependent A command that utilizes the ExampleChatExecutor to display a 'Pong!' message in the chat. It requires an instance of the ExampleAddon to access the chat executor. ```java public class ExamplePingCommand extends Command { private final ExampleChatExecutor chatExecutor; public ExamplePingCommand(ExampleAddon addon) { ``` -------------------------------- ### Example Subcommand Implementation Source: https://dev.labymod.net/pages/addon/features/commands This snippet demonstrates how to implement a subcommand for a LabyMod 4 command. It pushes a notification upon execution and returns true to consume the subcommand. ```java public class ExampleSubCommand extends SubCommand { private final NotificationController notificationController; public ExampleSubCommand() { super("sub"); this.notificationController = Laby.references().notificationController(); } @Override public boolean execute(String prefix, String[] arguments) { notificationController.push( Notification.builder() .title(Component.text("Success!")) .text(Component.text("You used a subcommand!")) .build() ); return true; } } ``` -------------------------------- ### BeaconSnapshot Example Source: https://dev.labymod.net/pages/addon/rendering/world-objects Extends AbstractWorldObjectSnapshot to capture render-relevant state for a beacon. Snapshots must be immutable to prevent race conditions between game and render threads. ```java package org.example.myaddon.object; import net.labymod.api.client.world.object.snapshot.AbstractWorldObjectSnapshot; import net.labymod.api.laby3d.renderer.snapshot.Extras; public final class BeaconSnapshot extends AbstractWorldObjectSnapshot { private final int color; private final float cameraYaw; public BeaconSnapshot( double x, double y, double z, int lightCoords, int color, float cameraYaw ) { super(x, y, z, lightCoords, Extras.empty()); this.color = color; this.cameraYaw = cameraYaw; } public int color() { return this.color; } public float cameraYaw() { return this.cameraYaw; } } ``` -------------------------------- ### Send Addon Recommendations via LabyModPlayer with Handler Source: https://dev.labymod.net/pages/server/labymod/moderation/addon-recommendation Sends a list of recommended addons to a player using LabyModPlayer and includes a callback to handle the response. The callback receives the initial response status and whether all addons are installed. ```java // Create or get a List of recommended addons List recommendations = new ArrayList<>(); // Add all recommended addons you want to send to the player recommendations.add(recommendedAddon); // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Send the recommended addons and handle the response labyModPlayer.sendAddonRecommendations(recommendations, response -> { boolean initial = response.isInitial(); // Whether the response is the initial response boolean allInstalled = response.isAllInstalled(); // Whether all recommended addons are installed // Directly handle the response }); ``` -------------------------------- ### Addon Configuration Class with SwitchSetting Source: https://dev.labymod.net/pages/addon/setup/setup Defines the addon's configuration, inheriting `AddonConfig`. This example includes an 'enabled' switch setting using `SwitchSetting` annotation. ```java public class ExampleConfiguration extends AddonConfig { @SwitchSetting( name = "Enable My Addon", description = "Enables or disables my addon", parent = "My Settings" ) public ConfigProperty enabled = new ConfigProperty<>(true); } ``` -------------------------------- ### Mixin Package Structure Example Source: https://dev.labymod.net/pages/addon/features/version-dependent Illustrates the required directory structure for placing mixin files within the `game-runner` module of a LabyMod addon. The `mixins` package must be inside the automatically generated versioned package. ```text org/example/{version}/mixins ``` -------------------------------- ### Send Addon Recommendation Packet via LabyModProtocol with Handler Source: https://dev.labymod.net/pages/server/labymod/moderation/addon-recommendation Sends an AddonRecommendationPacket using LabyModProtocol and includes a callback to handle the response. The callback processes the initial response status and addon installation status, returning true if it's the initial response. ```java // Create or get a List of recommended addons (array is also possible) List recommendations = new ArrayList<>(); // Add all recommended addons you want to send to the player recommendations.add(recommendedAddon); // Get the LabyModProtocol LabyModProtocol labyModProtocol = LabyModProtocolService.get().labyModProtocol(); // Send the packet and handle the response labyModProtocol.sendPacket( uniqueId, new AddonRecommendationPacket(recommendations), AddonRecommendationResponsePacket.class, response -> { boolean initial = response.isInitial(); // Whether the response is the initial response boolean allInstalled = response.isAllInstalled(); // Whether all recommended addons are installed // Handle the response packet return initial; // return #isInitial, as another response is expected if it is the initial response } ); ``` -------------------------------- ### Main Addon Class with LabyAddon Superclass Source: https://dev.labymod.net/pages/addon/setup/setup Example of a main addon class extending `LabyAddon` for simplified development. It demonstrates registering settings, commands, listeners, and using the integrated logger. ```java @AddonMain public class ExampleAddon extends LabyAddon { public static void main(String[] args) { // Method for command line execution } @Override public void enable() { // Register settings registerSettingCategory(new SettingsCategory("My Settings", "My Addon Settings", Optional.empty())); // Register commands registerCommand(new MyCommand()); // Register listeners registerListener(new MyListener()); // Use the logger this.logger().info("Enabled"); this.logger().warn("Something might be wrong"); this.logger().error("This is an error"); super.enable(); } @Override public Class configurationClass() { return ExampleConfiguration.class; } } ``` -------------------------------- ### Example Chat Executor Interface Source: https://dev.labymod.net/pages/addon/features/version-dependent Defines methods for displaying messages in the chat. Use this interface to abstract chat functionality. ```java @Referenceable public interface ExampleChatExecutor { void displayMessageInChat(String message); void displayMessageInChat(Component labyComponent); } ``` -------------------------------- ### Example Addon Class Source: https://dev.labymod.net/pages/addon/features/version-dependent The main class for the LabyMod addon. It enables features, registers commands, and provides access to version-dependent implementations like the chat executor. ```java public class ExampleAddon extends LabyAddon { private ExampleChatExecutor chatExecutor; @Override protected void enable() { # Make sure to build your addon first (after creating the interface and it's implementations) and to import your own generated ReferenceStorage class this.chatExecutor = ((ReferenceStorage) this.referenceStorageAccessor()).exampleChatExecutor(); this.registerCommand(new ExamplePingCommand(this)); } @Override protected Class configurationClass() { return ExampleConfiguration.class; } public ExampleChatExecutor chatExecutor() { return this.chatExecutor; } } ``` -------------------------------- ### DropdownListWidget Generic Example Source: https://dev.labymod.net/pages/addon/activities/custom-widgets Demonstrates the use of generic types in widget inheritance for defining child types. ```java public class DropdownListWidget extends VerticalListWidget> {} ``` -------------------------------- ### Example TextHudWidget Implementation Source: https://dev.labymod.net/pages/addon/features/hud-widgets Implement a TextHudWidget to display dynamic text. It supports binding to a category, setting an icon, and updating text content based on game state. ```java public class ExampleHudWidget extends TextHudWidget { private TextLine textLine; public ExampleHudWidget(ExampleAddon addon) { super("example_id"); // Bind the Widget to our created category in our main class this.bindCategory(addon.widgetCategory()); // Optional - set an icon for the Widget Editor in the Constructor; you can also annotate the icon via a SpriteSlot at the top of the class this.setIcon(ResourceLocation.create("minecraft", "textures/item/name_tag.png")); } @Override public void load(TextHudWidgetConfig config) { super.load(config); this.textLine = createLine("Own Username", ""); } @Override public void onTick(boolean isEditorContext) { String value = null; if(Laby.labyAPI().serverController().isConnected()) { value = Laby.labyAPI().getName(); } // Update the text line an flush it this.textLine.updateAndFlush(value); // Set the state of the text line // DISABLED - text line is fully disabled also not available in the Widget Editor // HIDDEN - text line is hidden ingame, but is displayed in the Widget Editor // VISIBLE - text line is visible ingame this.textLine.setState(value != null ? State.VISIBLE : State.HIDDEN); } } ``` -------------------------------- ### Initialize LabyMod API in Minestom Source: https://dev.labymod.net/pages/server Call this method before starting your Minestom server to enable LabyMod 4 Server API functionality. ```java LabyModProtocolService.initialize(); ``` -------------------------------- ### Send Installed Addons Request Packet Source: https://dev.labymod.net/pages/server/labymod/moderation/installed-addons Sends a request for a specific list of addons using the LabyModProtocol. Requires manual registration of a response handler. ```java // Create or get a List of addons to request (array is also possible) List addonsToRequest = new ArrayList<>(); // Add the namespace of all addons that you want to request // Keeping the List empty requests all addons (not recommended) addonsToRequest.add("voicechat"); // Get the LabyModProtocol LabyModProtocol labyModProtocol = LabyModProtocolService.get().labyModProtocol(); // Send the packet labyModProtocol.sendPacket(uniqueId, new InstalledAddonsRequestPacket(addonsToRequest)); // To handle the response, you need to register a PacketHandler for the // InstalledAddonsResponsePacket yourself! ``` -------------------------------- ### Example Packet Implementation Source: https://dev.labymod.net/pages/server/packets This class implements the Packet interface for LabyMod 4 Server API. It defines fields for a nullable component, an integer, and a list of custom objects. The constructor ensures the list of objects is not null. Includes getter methods and an overridden toString method for representation. Also defines a nested record 'ExamplePacketObject' for the list items. ```java import net.labymod.serverapi.api.model.component.ServerAPIComponent; import net.labymod.serverapi.api.packet.Packet; import net.labymod.serverapi.api.payload.io.PayloadReader; import net.labymod.serverapi.api.payload.io.PayloadWriter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Objects; public class ExamplePacket implements Packet { private ServerAPIComponent component; private int color; private List objects; public ExamplePacket( @Nullable ServerAPIComponent component, @NotNull List objects, int color ) { // Throw an exception if the list is null Objects.requireNonNull(objects, "Objects list cannot be null"); // Assign the values to the fields this.component = component; this.objects = objects; this.color = color; } public @Nullable ServerAPIComponent getComponent() { return this.component; } public int getColor() { return this.color; } /** * This can be annotated with {@link NotNull}, as the list is never null. * We know that because our constructor is throwing an exception if no list * is provided. And if for some reason the list is null upon reading, it * will never be handed over to the PacketHandler. */ public @NotNull List getObjects() { return this.objects; } /** * It's always a good idea to override the {@link Object#toString()} method to * provide a human-readable representation of the object. */ @Override public String toString() { return "ExamplePacket{" + "component=" + component + ", color=" + color + '}'; } /** * The record is a simple data class that is used to store the data of * the objects list. */ public record ExamplePacketObject( String name, boolean editable, boolean createdByUser, boolean global ) { /** * It's always a good idea to override the {@link Object#toString()} method * to provide a human-readable representation of the object. */ @Override public String toString() { return "ExamplePacketObject{" + "name='" + name + '\'' + ", editable=" + editable + ", createdByUser=" + createdByUser + ", global=" + global + '}'; } } } ``` -------------------------------- ### Logging Every Tick with Mixin Source: https://dev.labymod.net/pages/addon/features/version-dependent An example of a Mixin class that injects a method to log the tick count every game tick. Ensure your `rootProject.name` matches your addon namespace for external use. ```java @Mixin(Minecraft.class) public class MixinMinecraft { @Unique private int example$tickCount = 0; @Inject(method = "tick", at = @At("HEAD")) private void onTick(CallbackInfo ci) { System.out.println("[Mixin] Tick log " + this.example$tickCount++); } } ``` -------------------------------- ### Example LSS Activity with LSS Styling Source: https://dev.labymod.net/pages/addon/activities/lss This Java code demonstrates how to create an Activity that uses an LSS stylesheet for its UI components. It includes adding an ID to a widget and linking the LSS file. ```java @AutoActivity @Link("example.lss") public class ExampleLssActivity extends SimpleActivity { @Override public void initialize(Parent parent) { super.initialize(parent); ComponentWidget componentWidget = ComponentWidget.text( "I am an example text rendered with a ComponentWidget set via LSS" ); componentWidget.addId("test-widget"); this.document().addChild(componentWidget); } } ``` -------------------------------- ### Implement Custom Name Tag Logic Source: https://dev.labymod.net/pages/addon/rendering/entity-tags Implement the buildComponents method to define the logic for displaying custom name tags. This example shows how to display a 'Reports' count based on snapshot data. ```java package org.example.myaddon.tag; import org.example.myaddon.api.snapshot.ExampleTagExtraKeys; import org.example.myaddon.api.snapshot.ExampleTagUserSnapshot; import java.util.List; import net.labymod.api.client.component.Component; import net.labymod.api.client.entity.player.tag.tags.ComponentNameTag; import net.labymod.api.client.render.state.entity.EntitySnapshot; import org.jetbrains.annotations.NotNull; public class MyCustomNameTag extends ComponentNameTag { @Override protected @NotNull List buildComponents(EntitySnapshot snapshot) { if (!snapshot.has(ExampleTagExtraKeys.EXAMPLE_USER_TAG)) { return super.buildComponents(snapshot); } ExampleTagUserSnapshot userSnapshot = snapshot.get(ExampleTagExtraKeys.EXAMPLE_USER_TAG); int reports = userSnapshot.reports(); if (reports > 0) { return List.of(Component.text("Reports: " + reports)); } return List.of(); } // You can override this method to include your own custom logic, but make sure // to include the call to super.isVisible(); // The NameTag is always hidden as long as buildComponents() returns an empty list // @Override // public boolean isVisible() { // return super.isVisible() && ; // } // You can override this method to change the scale of your NameTag, default is 1.0 // @Override // public float getScale() { // return 0.5F; // } } ``` -------------------------------- ### Query Current Server Feature State Source: https://dev.labymod.net/pages/addon/features/server-features Use `ServerFeatureService` to get the current state of features outside of event listeners. This example checks if the fancy font is enabled, providing a default value if the server hasn't sent the feature state yet. ```java import net.labymod.api.Laby; import net.labymod.api.user.serverfeature.ServerFeature; import net.labymod.api.user.serverfeature.ServerFeatureService; import net.labymod.serverapi.core.model.feature.Feature; ServerFeatureService service = Laby.references().serverFeatureService(); ServerFeature serverFeature = service.serverFeature(); // Check if a feature is enabled, returns the default if the server hasn't sent it boolean fancyFontEnabled = serverFeature.isFeatureEnabled(Feature.FANCY_FONT, false); ``` -------------------------------- ### Get Non-Nullable Translated String Source: https://dev.labymod.net/pages/addon/features/internationalization Use I18n.translate to get a translated string. This method returns the translation key if the key is not found in any internationalization file. ```java I18n.translate("translation.key", arg1, arg2) ``` -------------------------------- ### Create a Recommended Addon Source: https://dev.labymod.net/pages/server/labymod/moderation/addon-recommendation Creates a recommended addon object. Use `.require()` to mark it as mandatory. ```java RecommendedAddon recommendedAddon = RecommendedAddon.of("example"); // If the addon is required, execute the following line recommendedAddon.require(); ``` -------------------------------- ### Create Input Prompt using create() Source: https://dev.labymod.net/pages/server/labymod/supplements/input-prompt Use the InputPrompt.create() method for a more concise way to create an input prompt with title, placeholder, default value, and maximum length. ```java InputPrompt inputPrompt = InputPrompt.create( ServerAPIComponent.text("Example Input Switch Prompt") .color(ServerAPITextColor.AQUA) .decorate(ServerAPITextDecoration.BOLD), ServerAPIComponent.text("Example placeholder"), "value", 12 ); ``` -------------------------------- ### Example Enum for Dropdown Source: https://dev.labymod.net/pages/addon/features/config Defines an enumeration for use with dropdown settings. Each enum constant represents an option in the dropdown. ```java public enum ExampleEnum { HEART, CIRCLE, RECTANGLE, TRIANGLE, SCALENE_TRIANGLE; } ``` -------------------------------- ### Get Cash Economy Display Source: https://dev.labymod.net/pages/server/labymod/displays/economy Retrieves the cash economy display for a LabyMod player. This method is part of the LabyModPlayer management system. ```java // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Get the Cash Economy Display EconomyDisplay cashEconomy = labyModPlayer.cashEconomy(); ``` -------------------------------- ### Creating and Rendering a Bare Activity Source: https://dev.labymod.net/pages/addon/activities/activity This snippet demonstrates how to create a basic Activity by extending `SimpleActivity` and rendering centered text. Ensure the `super.render` call is at the beginning of the method. The text can be customized with position, centering, shadow, and color. ```Java import net.labymod.api.activity.SimpleActivity; import net.labymymod.api.render.text.TextRenderer; import net.labymod.api.util.Bounds; import net.labymod.core.labyclient.gui.hud.Stack; import net.labymod.core.labyclient.gui.hud.mouse.MutableMouse; import net.labymod.api.AutoActivity; import java.awt.Color; @AutoActivity public class ExampleBareActivity extends SimpleActivity { @Override public void render(Stack stack, MutableMouse mouse, float partialTicks) { super.render(stack, mouse, partialTicks); Bounds bounds = this.bounds(); TextRenderer textRenderer = this.labyAPI.renderPipeline().textRenderer(); textRenderer.text("I am a bare rendered example text") .pos(bounds.getCenterX(), bounds.getCenterY()) .centered(true) .shadow(false) .color(Color.ORANGE.getRGB()) .render(stack); } } ``` -------------------------------- ### BeaconObject Example Source: https://dev.labymod.net/pages/addon/rendering/world-objects Extends AbstractWorldObject to define a custom world object, like a beacon. Implements lifecycle methods such as cullVolume() and shouldRemove(). ```java package org.example.myaddon.object; import net.labymod.api.client.world.object.AbstractWorldObject; import net.labymod.api.client.world.object.CullVolume; import net.labymod.api.util.math.vector.DoubleVector3; import org.jetbrains.annotations.NotNull; public class BeaconObject extends AbstractWorldObject { private static final float HALF_WIDTH = 0.5F; private static final float HEIGHT = 3.0F; private final int color; private boolean removed; public BeaconObject(@NotNull DoubleVector3 position, int color) { super(position); this.color = color; } @Override @NotNull public CullVolume cullVolume() { DoubleVector3 pos = this.position(); return CullVolume.box( pos.getX() - HALF_WIDTH, pos.getY(), pos.getZ() - HALF_WIDTH, pos.getX() + HALF_WIDTH, pos.getY() + HEIGHT, pos.getZ() + HALF_WIDTH ); } @Override public boolean shouldRemove() { return this.removed; } @Override public void onRemove() { // Cleanup logic when removed from the registry } public int getColor() { return this.color; } public void markRemoved() { this.removed = true; } } ``` -------------------------------- ### Example LSS Stylesheet Source: https://dev.labymod.net/pages/addon/activities/lss This LSS code defines the styling for a widget with the ID 'test-widget'. It centers the widget and sets its text color. ```lss .test-widget { left: 50%; top: 50%; alignment-x: center; alignment-y: center; text-color: gold; } ``` -------------------------------- ### ExampleWidget with Custom Rendering and Interaction Source: https://dev.labymod.net/pages/addon/activities/custom-widgets A basic custom widget demonstrating overridden methods for rendering, mouse clicks, and key presses. Extend this class to create interactive widgets. ```java @AutoWidget public class ExampleWidget extends AbstractWidget { @Override public void renderWidget(ScreenContext context) { if (this.isVisible()) { // optional: custom rendering } super.renderWidget(context); } @Override public boolean mouseClicked(MutableMouse mouse, MouseButton mouseButton) { // optional: handle mouse interactions return super.mouseClicked(mouse, mouseButton); } @Override public boolean keyPressed(Key key, InputType type) { // optional: handle key press return super.keyPressed(key, type); } /* There are more methods that you can override for user input like: - mouseDragged(...) - mouseScrolled(...) - mouseReleased(...) - keyReleased(...) - fileDropped(...) -> there may be limitations in older Minecraft versions, for file interactions you should always provide an optional file chooser (e.g. FileChooserWidget) */ } ``` -------------------------------- ### Get Bank Economy Display Source: https://dev.labymod.net/pages/server/labymod/displays/economy Retrieves the bank economy display associated with a LabyMod player. This is part of the recommended management approach via LabyModPlayer. ```java // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Get the Bank Economy Display EconomyDisplay bankEconomy = labyModPlayer.bankEconomy(); ``` -------------------------------- ### Send Addon Recommendations via LabyModPlayer without Handler Source: https://dev.labymod.net/pages/server/labymod/moderation/addon-recommendation Sends a list of recommended addons to a player using LabyModPlayer. Response handling requires manual registration of a PacketHandler for AddonRecommendationResponsePacket. ```java // Create or get a List of recommended addons List recommendations = new ArrayList<>(); // Add all recommended addons you want to send to the player recommendations.add(recommendedAddon); // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Send the recommended addons labyModPlayer.sendAddonRecommendations(serverSwitchPrompt); // To handle the response, you need to register a PacketHandler for the // AddonRecommendationResponsePacket yourself! ``` -------------------------------- ### Open Server Switch Prompt via LabyModPlayer with Response Handling Source: https://dev.labymod.net/pages/server/labymod/supplements/server-switch Opens a server switch prompt for a player using their LabyModPlayer object and provides a callback to handle the response directly. ```java // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Open the server switch prompt and handle the response labyModPlayer.openServerSwitchPrompt(serverSwitchPrompt, response -> { boolean accepted = response.wasAccepted(); // Whether the player accepted the server switch // Directly handle the response }); ``` -------------------------------- ### Listen for Server Feature Updates Source: https://dev.labymod.net/pages/addon/features/server-features Register a listener to receive `ServerFeatureUpdateEvent` when servers toggle features. This example shows how to check if the fancy font feature is enabled or disabled. ```java import net.labymod.api.event.Subscribe; import net.labymod.api.event.labymod.serverapi.ServerFeatureUpdateEvent; import net.labymod.serverapi.core.model.feature.Feature; import net.labymod.serverapi.core.model.feature.Feature.StatedFeature; public class FeatureListener { @Subscribe public void onServerFeatureUpdate(ServerFeatureUpdateEvent event) { StatedFeature fancyFont = event.get(Feature.FANCY_FONT); if (fancyFont == null) { return; } if (fancyFont.isEnabled()) { // The server enabled fancy font } else { // The server disabled fancy font } } } ``` -------------------------------- ### Creating and Registering a Custom Protocol Source: https://dev.labymod.net/pages/server/protocols This snippet outlines the steps to create a new custom addon protocol using the LabyModProtocolService and then registering it with the ProtocolRegistry. ```java // Get the Protocol Service LabyModProtocolService protocolService = LabyModProtocolService.get(); // Create a new AddonProtocol AddonProtocol protocol = new AddonProtocol(protocolService, "example"); // Register the protocol protocolService.registry().registerProtocol(protocol); ``` -------------------------------- ### Create Discord Rich Presence with Game Mode Source: https://dev.labymod.net/pages/server/labymod/features/discord Use `DiscordRPC.create` to initialize a Discord Rich Presence with the current game mode. This is the basic method for setting up presence. ```java DiscordRPC discordRPC = DiscordRPC.create("Example Game Mode"); ``` -------------------------------- ### Get Custom Economy Display Source: https://dev.labymod.net/pages/server/labymod/displays/economy Retrieves a custom economy display by its key from the LabyModPlayer object. Returns null if the economy does not exist. Remember to replace 'custom' with your economy's key. ```java // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Get the Custom Economy Display. Replace "custom" with the key of your custom economy EconomyDisplay customEconomy = labyModPlayer.getEconomy("custom"); // Nullable! ``` -------------------------------- ### Configure Addon Information in build.gradle.kts Source: https://dev.labymod.net/pages/addon/setup/setup Modify the `build.gradle.kts` file to set your addon's package name, namespace, display name, author, description, and Minecraft version compatibility. ```gradle labyMod { defaultPackageName = "net.labymod.addons.voicechat" addonInfo { namespace = "your_unique_namespace" displayName = "Your Addon Name" author = "Your Name" description = "A description for your addon" minecraftVersion = "1.8.9" } } ``` -------------------------------- ### Initialize LabyMod Server API (Bukkit/BungeeCord Shaded) Source: https://dev.labymod.net/pages/server Initialize the LabyMod Server API manually in your plugin's onEnable method when shading the dependency. ```java @Override public void onEnable() { LabyModProtocolService.initialize(this); } ``` -------------------------------- ### Registering a VersionLoginPacket Handler Source: https://dev.labymod.net/pages/server/protocols This snippet shows how to create a class that implements the PacketHandler interface to handle VersionLoginPackets. It includes the necessary imports and the structure for the handle method. ```java import net.labymod.serverapi.api.packet.PacketHandler; import net.labymod.serverapi.core.packet.serverbound.login.VersionLoginPacket; import org.jetbrains.annotations.NotNull; import java.util.UUID; public class VersionLoginPacketHandler implements PacketHandler { /** * Handles the VersionLoginPacket * * @param sender the unique id of the sender of the packet * @param packet the packet that was sent */ @Override public void handle(@NotNull UUID sender, @NotNull VersionLoginPacket packet) { // Perform logic to handle the packet } } ``` -------------------------------- ### Send Addon Recommendation Packet via LabyModProtocol Source: https://dev.labymod.net/pages/server/labymod/moderation/addon-recommendation Sends an AddonRecommendationPacket directly using LabyModProtocol. Response handling requires manual registration of a PacketHandler for AddonRecommendationResponsePacket. ```java // Create or get a List of recommended addons (array is also possible) List recommendations = new ArrayList<>(); // Add all recommended addons you want to send to the player recommendations.add(recommendedAddon); // Get the LabyModProtocol LabyModProtocol labyModProtocol = LabyModProtocolService.get().labyModProtocol(); // Send the packet labyModProtocol.sendPacket(uniqueId, new AddonRecommendationPacket(recommendations)); // To handle the response, you need to register a PacketHandler for the // AddonRecommendationResponsePacket yourself! ``` -------------------------------- ### Open Input Prompt via LabyModPlayer with Handler Source: https://dev.labymod.net/pages/server/labymod/supplements/input-prompt Send an input prompt to a player using LabyModPlayer and provide a Consumer to directly handle the player's input response. ```java // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Open the input prompt and handle the response labyModPlayer.openInputPrompt(inputPrompt, input -> { // Directly handle the input }); ``` -------------------------------- ### Create Server Switch Prompt Source: https://dev.labymod.net/pages/server/labymod/supplements/server-switch Creates a server switch prompt with custom text and a target server address. Uses the Server API's Component model for text formatting. ```java ServerSwitchPrompt serverSwitchPrompt = ServerSwitchPrompt.create( ServerAPIComponent.text("Example Server Switch Prompt") .color(ServerAPITextColor.GOLD) .decorate(ServerAPITextDecoration.BOLD), "hypixel.net" ); ``` -------------------------------- ### Open Input Prompt via LabyModPlayer without Handler Source: https://dev.labymod.net/pages/server/labymod/supplements/input-prompt Open an input prompt for a player using LabyModPlayer. Response handling requires manual registration of an InputPromptResponsePacket handler. ```java // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Open the input prompt labyModPlayer.openInputPrompt(inputPrompt); // To handle the response, you need to register a PacketHandler for the // InputPromptResponsePacket yourself! ``` -------------------------------- ### Registering World Objects and Submitters Source: https://dev.labymod.net/pages/addon/rendering/world-objects Demonstrates how to register a custom WorldObjectSubmitter and place world objects within the LabyMod addon. ```java package org.example.myaddon; import net.labymod.api.Laby; import net.labymod.api.addon.LabyAddon; import net.labymod.api.client.world.object.WorldObjectDispatcher; import net.labymod.api.client.world.object.WorldObjectRegistry; import net.labymod.api.util.math.vector.DoubleVector3; import org.example.myaddon.object.BeaconObject; import org.example.myaddon.object.BeaconSubmitter; @AddonMain public class ExampleAddon extends LabyAddon { @Override protected void enable() { // Register the submitter for your world object type WorldObjectDispatcher dispatcher = Laby.references().worldObjectDispatcher(); dispatcher.registerSubmitter(BeaconObject.class, new BeaconSubmitter()); } // Call this whenever you want to place an object in the world. // The id should be unique per object instance. public void placeBeacon(double x, double y, double z, int color) { WorldObjectRegistry registry = Laby.references().worldObjectRegistry(); registry.register( "my_beacon_" + x + "_" + y + "_" + z, new BeaconObject(new DoubleVector3(x, y, z), color) ); } } ``` -------------------------------- ### Open Server Switch Prompt via LabyModPlayer without Response Handling Source: https://dev.labymod.net/pages/server/labymod/supplements/server-switch Opens a server switch prompt for a player using their LabyModPlayer object. Response handling must be registered separately. ```java // Get the LabyModPlayer LabyModPlayer labyModPlayer = LabyModProtocolService.get().getPlayer(uniqueId); // Open the server switch prompt labyModPlayer.openServerSwitchPrompt(serverSwitchPrompt); // To handle the response, you need to register a PacketHandler for the // ServerSwitchPromptResponsePacket yourself! ``` -------------------------------- ### Registering a Simple Revision Source: https://dev.labymod.net/pages/addon/features/config Register a new revision for your addon configuration using `SimpleRevision`. This is typically done in the `preConfigurationLoad` method. ```java public class ExampleAddon extends LabyAddon { @Override protected void preConfigurationLoad() { Laby.references().revisionRegistry().register(new SimpleRevision( "example", new SemanticVersion("1.0.1"), "2025-10-06" )); } } ``` -------------------------------- ### Create Input Prompt using Builder Source: https://dev.labymod.net/pages/server/labymod/supplements/input-prompt Use the InputPrompt.builder() method to construct an input prompt with a title, placeholder, default value, and maximum length. ```java InputPrompt inputPrompt = InputPrompt.builder() .title(ServerAPIComponent.text("Example Input Switch Prompt") .color(ServerAPITextColor.AQUA) .decorate(ServerAPITextDecoration.BOLD)) .placeholder(ServerAPIComponent.text("Example placeholder")) .defaultValue("value") .maxLength(12) .build(); ``` -------------------------------- ### Custom Setting Widget Implementation Source: https://dev.labymod.net/pages/addon/features/custom-settings This Java code defines a custom setting widget with a dropdown and a button. It demonstrates how to initialize the widget, handle user interactions, and integrate with the addon's configuration system using annotations and factories. Use this for creating complex, interactive settings. ```java @AutoWidget @SettingWidget // Important to be able to use this widget in the settings @Link("custom-setting-widget.lss") public class CustomSettingWidget extends HorizontalListWidget { private final String customText; private final String initialValue; private Consumer customUpdateListener; public CustomSettingWidget(String customText, String initialValue) { this.customText = customText; this.initialValue = initialValue; } public void setCustomUpdateListener(Consumer customUpdateListener) { this.customUpdateListener = customUpdateListener; } @Override public void initialize(Parent parent) { super.initialize(parent); // Define your custom widget // You can also use other methods like mouseClicked(), fileDropped(), renderWidget(), etc. // as described earlier DropdownWidget dropdown = new DropdownWidget<>(); dropdown.addAll(new String[]{"first", "second", "third"}); dropdown.addId("custom-dropdown"); dropdown.setChangeListener(value -> { if (this.customUpdateListener != null) { this.customUpdateListener.accept(value); } }); if (this.initialValue != null) { dropdown.setSelected(this.initialValue); } this.addEntry(dropdown); ButtonWidget button = ButtonWidget.text(this.customText); button.setPressListener(() -> { System.out.println("Button pressed"); return true; }); button.addId("custom-button"); this.addEntry(button); } @SettingElement( // extended is optional: moves your widgets below the title of the settings // instead of right to it, giving you more space extended = false ) @Target({ElementType.FIELD}) // may also be METHOD if your setting should be used on methods @Retention(RetentionPolicy.RUNTIME) public @interface MyCustomSetting { // Define your custom values here, // or define none if you don't need any String customText() default "Refresh"; } /** * Factory for CustomSettingWidgets, can be used in configurations via @MyCustomSetting */ @SettingFactory public static class Factory implements WidgetFactory { @Override public Class[] types() { // The types of config values that this setting supports. // For example in a slider this could be all numbers: PrimitiveHelper.NUMBER_PRIMITIVES // It can also be an empty array if you don't care about the type (e.g. on a method instead of a field) return new Class[]{String.class}; } @Override public CustomSettingWidget[] create(Setting setting, MyCustomSetting annotation, SettingInfo info, SettingAccessor accessor) { CustomSettingWidget customWidget = new CustomSettingWidget( annotation.customText(), // Custom text defined in the annotation in the config accessor.get() // Get the current value from the config ); // When the user selects a custom value, pass it to the SettingAccessor to write it into the config customWidget.setCustomUpdateListener(value -> accessor.set(value)); // You can also return multiple widgets here that will be placed next to each other return new CustomSettingWidget[]{customWidget}; } } } ``` -------------------------------- ### Create a Basic Configuration Field Source: https://dev.labymod.net/pages/addon/features/config Demonstrates how to declare an 'enabled' configuration property of type Boolean with a default value of true. ```java ConfigProperty enabled = new ConfigProperty<>(true); ``` -------------------------------- ### Send Input Prompt Packet via LabyModProtocol without Handler Source: https://dev.labymod.net/pages/server/labymod/supplements/input-prompt Send an input prompt packet to a player using LabyModProtocol. Response handling requires manual registration of an InputPromptResponsePacket handler. ```java // Get the LabyModProtocol LabyModProtocol labyModProtocol = LabyModProtocolService.get().labyModProtocol(); // Send the packet labyModProtocol.sendPacket(uniqueId, new InputPromptPacket(inputPrompt)); // To handle the response, you need to register a PacketHandler for the // InputPromptResponsePacket yourself! ``` -------------------------------- ### Send Server Switch Prompt via LabyModProtocol with Response Handling Source: https://dev.labymod.net/pages/server/labymod/supplements/server-switch Sends a server switch prompt packet to a player using the LabyModProtocol and provides a callback to handle the response directly. ```java // Get the LabyModProtocol LabyModProtocol labyModProtocol = LabyModProtocolService.get().labyModProtocol(); // Send the packet and handle the response labyModProtocol.sendPacket( uniqueId, new ServerSwitchPromptPacket(inputPrompt), ServerSwitchPromptResponsePacket.class, response -> { boolean accepted = response.wasAccepted(); // Whether the player accepted the server switch // Handle the response packet return false; // Return false, as only one response is expected } ); ```