diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index d9e8de162..d4c8e124e 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -41,7 +41,7 @@ jobs:
fi
- name: Gradle Publish
- if: "${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/version/') }}"
+ if: "${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/feature/server-links' || startsWith(github.ref, 'refs/heads/version/') }}"
run: ./gradlew publish
- name: Gradle Release
diff --git a/api/src/main/java/com/lunarclient/apollo/common/icon/ResourceLocationIcon.java b/api/src/main/java/com/lunarclient/apollo/common/icon/ResourceLocationIcon.java
new file mode 100644
index 000000000..f4716c4a6
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/common/icon/ResourceLocationIcon.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.common.icon;
+
+import lombok.Builder;
+import lombok.Getter;
+
+/**
+ * Represents a resource location icon.
+ *
+ * @since 1.2.5
+ */
+@Getter
+@Builder
+public final class ResourceLocationIcon extends Icon {
+
+ /**
+ * Returns the icon {@link String} resource location.
+ *
+ *
Represents a path to an icon that will appear for the player.
+ *
+ * @return the icon resource location
+ * @since 1.2.5
+ */
+ String resourceLocation;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/module/serverlink/ServerLink.java b/api/src/main/java/com/lunarclient/apollo/module/serverlink/ServerLink.java
new file mode 100644
index 000000000..2c6d5abfe
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/module/serverlink/ServerLink.java
@@ -0,0 +1,63 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.module.serverlink;
+
+import lombok.Builder;
+import lombok.Getter;
+import net.kyori.adventure.text.Component;
+
+/**
+ * Represents a link entry displayed in the Server Links menu.
+ *
+ * @since 1.2.5
+ */
+@Getter
+@Builder
+public final class ServerLink {
+
+ /**
+ * Returns the server link {@link String} id.
+ *
+ * @return the server link id
+ * @since 1.2.5
+ */
+ String id;
+
+ /**
+ * Returns the server link {@link Component} display name.
+ *
+ * @return the server link display name
+ * @since 1.2.5
+ */
+ Component displayName;
+
+ /**
+ * Returns the server link {@link String} url.
+ *
+ * @return the server link url
+ * @since 1.2.5
+ */
+ String url;
+
+}
diff --git a/api/src/main/java/com/lunarclient/apollo/module/serverlink/ServerLinkModule.java b/api/src/main/java/com/lunarclient/apollo/module/serverlink/ServerLinkModule.java
new file mode 100644
index 000000000..f3010c971
--- /dev/null
+++ b/api/src/main/java/com/lunarclient/apollo/module/serverlink/ServerLinkModule.java
@@ -0,0 +1,130 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.module.serverlink;
+
+import com.lunarclient.apollo.common.icon.ResourceLocationIcon;
+import com.lunarclient.apollo.module.ApolloModule;
+import com.lunarclient.apollo.module.ModuleDefinition;
+import com.lunarclient.apollo.recipients.Recipients;
+import java.util.List;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * Represents the server links module.
+ *
+ * This module provides support for the Server Links feature introduced in
+ * Minecraft 1.21.0, with compatibility extending down to version 1.7.
+ *
+ * @since 1.2.5
+ */
+@ApiStatus.NonExtendable
+@ModuleDefinition(id = "server_link", name = "Server Link")
+public abstract class ServerLinkModule extends ApolloModule {
+
+ /**
+ * Overrides the server link menu title image for the {@link Recipients}.
+ *
+ * The provided {@link ResourceLocationIcon} will be displayed in place of the default
+ * menu title.
+ *
+ * The resource location must reference a valid client-side asset using the standard
+ * namespace format:
+ *
+ *
+ * minecraft:textures/item/apple.png
+ * lunar:logo/logo-100x100.png
+ *
+ *
+ * For optimal results, a square image (e.g. 128x128) is recommended.
+ *
+ * @param recipients the recipients that are receiving the packet
+ * @param icon the resource location icon
+ * @since 1.2.5
+ */
+ public abstract void overrideServerLinkResource(Recipients recipients, ResourceLocationIcon icon);
+
+ /**
+ * Resets the server link menu title image for the {@link Recipients}.
+ *
+ * Reverts back to displaying the default menu title.
+ *
+ * @param recipients the recipients that are receiving the packet
+ * @since 1.2.5
+ */
+ public abstract void resetServerLinkResource(Recipients recipients);
+
+ /**
+ * Adds or updates the {@link ServerLink} for the {@link Recipients}.
+ *
+ * @param recipients the recipients that are receiving the packet
+ * @param serverLink the server link
+ * @since 1.2.5
+ */
+ public abstract void addServerLink(Recipients recipients, ServerLink serverLink);
+
+ /**
+ * Adds or updates the {@link ServerLink}s for the {@link Recipients}.
+ *
+ * @param recipients the recipients that are receiving the packet
+ * @param serverLinks the server links
+ * @since 1.2.5
+ */
+ public abstract void addServerLink(Recipients recipients, List serverLinks);
+
+ /**
+ * Removes the {@link ServerLink} from the {@link Recipients}.
+ *
+ * @param recipients the recipients that are receiving the packet
+ * @param serverLinkId the server link id
+ * @since 1.2.5
+ */
+ public abstract void removeServerLink(Recipients recipients, String serverLinkId);
+
+ /**
+ * Removes the {@link ServerLink} from the {@link Recipients}.
+ *
+ * @param recipients the recipients that are receiving the packet
+ * @param serverLink the server link
+ * @since 1.2.5
+ */
+ public abstract void removeServerLink(Recipients recipients, ServerLink serverLink);
+
+ /**
+ * Removes the {@link ServerLink}s from the {@link Recipients}.
+ *
+ * @param recipients the recipients that are receiving the packet
+ * @param serverLinkIds the server link ids
+ * @since 1.2.5
+ */
+ public abstract void removeServerLink(Recipients recipients, List serverLinkIds);
+
+ /**
+ * Resets all {@link ServerLink}s for the {@link Recipients}.
+ *
+ * @param recipients the recipients that are receiving the packet
+ * @since 1.2.5
+ */
+ public abstract void resetServerLinks(Recipients recipients);
+
+}
diff --git a/common/src/main/java/com/lunarclient/apollo/module/serverlink/ServerLinkModuleImpl.java b/common/src/main/java/com/lunarclient/apollo/module/serverlink/ServerLinkModuleImpl.java
new file mode 100644
index 000000000..4427a9718
--- /dev/null
+++ b/common/src/main/java/com/lunarclient/apollo/module/serverlink/ServerLinkModuleImpl.java
@@ -0,0 +1,114 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.module.serverlink;
+
+import com.lunarclient.apollo.common.ApolloComponent;
+import com.lunarclient.apollo.common.icon.ResourceLocationIcon;
+import com.lunarclient.apollo.network.NetworkTypes;
+import com.lunarclient.apollo.player.AbstractApolloPlayer;
+import com.lunarclient.apollo.recipients.Recipients;
+import com.lunarclient.apollo.serverlink.v1.AddServerLinkMessage;
+import com.lunarclient.apollo.serverlink.v1.OverrideServerLinkResourceMessage;
+import com.lunarclient.apollo.serverlink.v1.RemoveServerLinkMessage;
+import com.lunarclient.apollo.serverlink.v1.ResetServerLinkResourceMessage;
+import com.lunarclient.apollo.serverlink.v1.ResetServerLinksMessage;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import lombok.NonNull;
+
+/**
+ * Provides the server link module.
+ *
+ * @since 1.2.5
+ */
+public final class ServerLinkModuleImpl extends ServerLinkModule {
+
+ @Override
+ public void overrideServerLinkResource(@NonNull Recipients recipients, @NonNull ResourceLocationIcon icon) {
+ OverrideServerLinkResourceMessage message = OverrideServerLinkResourceMessage.newBuilder()
+ .setIcon(NetworkTypes.toProtobuf(icon))
+ .build();
+
+ recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message));
+ }
+
+ @Override
+ public void resetServerLinkResource(@NonNull Recipients recipients) {
+ ResetServerLinkResourceMessage message = ResetServerLinkResourceMessage.getDefaultInstance();
+ recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message));
+ }
+
+ @Override
+ public void addServerLink(@NonNull Recipients recipients, @NonNull ServerLink serverLink) {
+ this.addServerLink(recipients, Collections.singletonList(serverLink));
+ }
+
+ @Override
+ public void addServerLink(@NonNull Recipients recipients, @NonNull List serverLinks) {
+ List serverLinksProto = serverLinks.stream()
+ .map(this::toProtobuf)
+ .collect(Collectors.toList());
+
+ AddServerLinkMessage message = AddServerLinkMessage.newBuilder()
+ .addAllServerLinks(serverLinksProto)
+ .build();
+
+ recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message));
+ }
+
+ @Override
+ public void removeServerLink(@NonNull Recipients recipients, @NonNull String serverLinkId) {
+ this.removeServerLink(recipients, Collections.singletonList(serverLinkId));
+ }
+
+ @Override
+ public void removeServerLink(@NonNull Recipients recipients, @NonNull ServerLink serverLink) {
+ this.removeServerLink(recipients, Collections.singletonList(serverLink.getId()));
+ }
+
+ @Override
+ public void removeServerLink(@NonNull Recipients recipients, @NonNull List serverLinkIds) {
+ RemoveServerLinkMessage message = RemoveServerLinkMessage.newBuilder()
+ .addAllServerLinkIds(serverLinkIds)
+ .build();
+
+ recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message));
+ }
+
+ @Override
+ public void resetServerLinks(@NonNull Recipients recipients) {
+ ResetServerLinksMessage message = ResetServerLinksMessage.getDefaultInstance();
+ recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message));
+ }
+
+ private com.lunarclient.apollo.serverlink.v1.ServerLink toProtobuf(ServerLink serverLink) {
+ return com.lunarclient.apollo.serverlink.v1.ServerLink.newBuilder()
+ .setId(serverLink.getId())
+ .setDisplayNameAdventureJsonLines(ApolloComponent.toJson(serverLink.getDisplayName()))
+ .setUrl(serverLink.getUrl())
+ .build();
+ }
+
+}
diff --git a/common/src/main/java/com/lunarclient/apollo/network/NetworkTypes.java b/common/src/main/java/com/lunarclient/apollo/network/NetworkTypes.java
index 0ecf1dcd8..6d20395f0 100644
--- a/common/src/main/java/com/lunarclient/apollo/network/NetworkTypes.java
+++ b/common/src/main/java/com/lunarclient/apollo/network/NetworkTypes.java
@@ -30,6 +30,7 @@
import com.lunarclient.apollo.common.icon.AdvancedResourceLocationIcon;
import com.lunarclient.apollo.common.icon.Icon;
import com.lunarclient.apollo.common.icon.ItemStackIcon;
+import com.lunarclient.apollo.common.icon.ResourceLocationIcon;
import com.lunarclient.apollo.common.icon.SimpleResourceLocationIcon;
import com.lunarclient.apollo.common.location.ApolloBlockLocation;
import com.lunarclient.apollo.common.location.ApolloLocation;
@@ -484,37 +485,15 @@ public static com.lunarclient.apollo.common.v1.Icon toProtobuf(Icon icon) {
com.lunarclient.apollo.common.v1.Icon.Builder builder = com.lunarclient.apollo.common.v1.Icon.newBuilder();
if (icon instanceof ItemStackIcon) {
- ItemStackIcon item = (ItemStackIcon) icon;
- String itemName = item.getItemName();
-
- com.lunarclient.apollo.common.v1.ItemStackIcon.Builder itemBuilder = com.lunarclient.apollo.common.v1.ItemStackIcon.newBuilder()
- .setItemId(item.getItemId())
- .setCustomModelData(item.getCustomModelData());
-
- if (itemName != null) {
- itemBuilder.setItemName(itemName);
- }
-
- builder.setItemStack(itemBuilder.build());
+ builder.setItemStack(NetworkTypes.toProtobuf((ItemStackIcon) icon));
+ } else if (icon instanceof ResourceLocationIcon) {
+ builder.setResourceLocation(NetworkTypes.toProtobuf((ResourceLocationIcon) icon));
} else if (icon instanceof SimpleResourceLocationIcon) {
- SimpleResourceLocationIcon simple = (SimpleResourceLocationIcon) icon;
-
- builder.setSimpleResourceLocation(com.lunarclient.apollo.common.v1.SimpleResourceLocationIcon.newBuilder()
- .setResourceLocation(simple.getResourceLocation())
- .setSize(checkPositive(simple.getSize(), "SimpleResourceLocationIcon#size"))
- .build());
+ builder.setSimpleResourceLocation(NetworkTypes.toProtobuf((SimpleResourceLocationIcon) icon));
} else if (icon instanceof AdvancedResourceLocationIcon) {
- AdvancedResourceLocationIcon advanced = (AdvancedResourceLocationIcon) icon;
-
- builder.setAdvancedResourceLocation(com.lunarclient.apollo.common.v1.AdvancedResourceLocationIcon.newBuilder()
- .setResourceLocation(advanced.getResourceLocation())
- .setWidth(checkPositive(advanced.getWidth(), "AdvancedResourceLocationIcon#width"))
- .setHeight(checkPositive(advanced.getHeight(), "AdvancedResourceLocationIcon#height"))
- .setMinU(checkRange(advanced.getMinU(), 0, 1, "AdvancedResourceLocationIcon#minU"))
- .setMaxU(checkRange(advanced.getMaxU(), 0, 1, "AdvancedResourceLocationIcon#maxU"))
- .setMinV(checkRange(advanced.getMinV(), 0, 1, "AdvancedResourceLocationIcon#minV"))
- .setMaxV(checkRange(advanced.getMaxV(), 0, 1, "AdvancedResourceLocationIcon#maxV"))
- .build());
+ builder.setAdvancedResourceLocation(NetworkTypes.toProtobuf((AdvancedResourceLocationIcon) icon));
+ } else {
+ throw new IllegalArgumentException("Unknown icon type: " + icon.getClass().getName());
}
return builder.build();
@@ -530,35 +509,150 @@ public static com.lunarclient.apollo.common.v1.Icon toProtobuf(Icon icon) {
*/
public static Icon fromProtobuf(com.lunarclient.apollo.common.v1.Icon icon) {
if (icon.hasItemStack()) {
- com.lunarclient.apollo.common.v1.ItemStackIcon item = icon.getItemStack();
-
- return ItemStackIcon.builder()
- .itemName(item.getItemName())
- .itemId(item.getItemId())
- .customModelData(item.getCustomModelData())
- .build();
+ return NetworkTypes.fromProtobuf(icon.getItemStack());
+ } else if (icon.hasResourceLocation()) {
+ return NetworkTypes.fromProtobuf(icon.getResourceLocation());
} else if (icon.hasSimpleResourceLocation()) {
- com.lunarclient.apollo.common.v1.SimpleResourceLocationIcon simple = icon.getSimpleResourceLocation();
-
- return SimpleResourceLocationIcon.builder()
- .resourceLocation(simple.getResourceLocation())
- .size(simple.getSize())
- .build();
+ return NetworkTypes.fromProtobuf(icon.getSimpleResourceLocation());
} else if (icon.hasAdvancedResourceLocation()) {
- com.lunarclient.apollo.common.v1.AdvancedResourceLocationIcon advanced = icon.getAdvancedResourceLocation();
-
- return AdvancedResourceLocationIcon.builder()
- .resourceLocation(advanced.getResourceLocation())
- .width(advanced.getWidth())
- .height(advanced.getHeight())
- .minU(advanced.getMinU())
- .maxU(advanced.getMaxU())
- .minV(advanced.getMinV())
- .maxV(advanced.getMaxV())
- .build();
+ return NetworkTypes.fromProtobuf(icon.getAdvancedResourceLocation());
}
- return null;
+ throw new IllegalArgumentException("Unknown icon proto type");
+ }
+
+ /**
+ * Converts an {@link ItemStackIcon} to a
+ * {@link com.lunarclient.apollo.common.v1.ItemStackIcon} proto message.
+ *
+ * @param icon the item stack icon
+ * @return the proto item stack icon message
+ * @since 1.2.5
+ */
+ public static com.lunarclient.apollo.common.v1.ItemStackIcon toProtobuf(ItemStackIcon icon) {
+ com.lunarclient.apollo.common.v1.ItemStackIcon.Builder builder = com.lunarclient.apollo.common.v1.ItemStackIcon.newBuilder()
+ .setItemId(icon.getItemId())
+ .setCustomModelData(icon.getCustomModelData());
+
+ if (icon.getItemName() != null) {
+ builder.setItemName(icon.getItemName());
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Converts a {@link com.lunarclient.apollo.common.v1.ItemStackIcon}
+ * proto message to an {@link ItemStackIcon}.
+ *
+ * @param icon the item stack icon message
+ * @return the item stack icon
+ * @since 1.2.5
+ */
+ public static ItemStackIcon fromProtobuf(com.lunarclient.apollo.common.v1.ItemStackIcon icon) {
+ return ItemStackIcon.builder()
+ .itemName(icon.getItemName())
+ .itemId(icon.getItemId())
+ .customModelData(icon.getCustomModelData())
+ .build();
+ }
+
+ /**
+ * Converts a {@link ResourceLocationIcon} to a
+ * {@link com.lunarclient.apollo.common.v1.ResourceLocationIcon} proto message.
+ *
+ * @param icon the resource location icon
+ * @return the proto resource location icon message
+ * @since 1.2.5
+ */
+ public static com.lunarclient.apollo.common.v1.ResourceLocationIcon toProtobuf(ResourceLocationIcon icon) {
+ return com.lunarclient.apollo.common.v1.ResourceLocationIcon.newBuilder()
+ .setResourceLocation(icon.getResourceLocation())
+ .build();
+ }
+
+ /**
+ * Converts a {@link com.lunarclient.apollo.common.v1.ResourceLocationIcon}
+ * proto message to a {@link ResourceLocationIcon}.
+ *
+ * @param icon the resource location icon message
+ * @return the resource location icon
+ * @since 1.2.5
+ */
+ public static ResourceLocationIcon fromProtobuf(com.lunarclient.apollo.common.v1.ResourceLocationIcon icon) {
+ return ResourceLocationIcon.builder()
+ .resourceLocation(icon.getResourceLocation())
+ .build();
+ }
+
+ /**
+ * Converts a {@link SimpleResourceLocationIcon} to a
+ * {@link com.lunarclient.apollo.common.v1.SimpleResourceLocationIcon} proto message.
+ *
+ * @param icon the simple resource location icon
+ * @return the proto simple resource location icon message
+ * @since 1.2.5
+ */
+ public static com.lunarclient.apollo.common.v1.SimpleResourceLocationIcon toProtobuf(SimpleResourceLocationIcon icon) {
+ return com.lunarclient.apollo.common.v1.SimpleResourceLocationIcon.newBuilder()
+ .setResourceLocation(icon.getResourceLocation())
+ .setSize(checkPositive(icon.getSize(), "SimpleResourceLocationIcon#size"))
+ .build();
+ }
+
+ /**
+ * Converts a {@link com.lunarclient.apollo.common.v1.SimpleResourceLocationIcon}
+ * proto message to a {@link SimpleResourceLocationIcon}.
+ *
+ * @param icon the simple resource location icon message
+ * @return the simple resource location icon
+ * @since 1.2.5
+ */
+ public static SimpleResourceLocationIcon fromProtobuf(com.lunarclient.apollo.common.v1.SimpleResourceLocationIcon icon) {
+ return SimpleResourceLocationIcon.builder()
+ .resourceLocation(icon.getResourceLocation())
+ .size(icon.getSize())
+ .build();
+ }
+
+ /**
+ * Converts an {@link AdvancedResourceLocationIcon} to an
+ * {@link com.lunarclient.apollo.common.v1.AdvancedResourceLocationIcon} proto message.
+ *
+ * @param icon the advanced resource location icon
+ * @return the proto advanced resource location icon message
+ * @since 1.2.5
+ */
+ public static com.lunarclient.apollo.common.v1.AdvancedResourceLocationIcon toProtobuf(AdvancedResourceLocationIcon icon) {
+ return com.lunarclient.apollo.common.v1.AdvancedResourceLocationIcon.newBuilder()
+ .setResourceLocation(icon.getResourceLocation())
+ .setWidth(checkPositive(icon.getWidth(), "AdvancedResourceLocationIcon#width"))
+ .setHeight(checkPositive(icon.getHeight(), "AdvancedResourceLocationIcon#height"))
+ .setMinU(checkRange(icon.getMinU(), 0, 1, "AdvancedResourceLocationIcon#minU"))
+ .setMaxU(checkRange(icon.getMaxU(), 0, 1, "AdvancedResourceLocationIcon#maxU"))
+ .setMinV(checkRange(icon.getMinV(), 0, 1, "AdvancedResourceLocationIcon#minV"))
+ .setMaxV(checkRange(icon.getMaxV(), 0, 1, "AdvancedResourceLocationIcon#maxV"))
+ .build();
+ }
+
+ /**
+ * Converts an {@link com.lunarclient.apollo.common.v1.AdvancedResourceLocationIcon}
+ * proto message to an {@link AdvancedResourceLocationIcon}.
+ *
+ * @param icon the advanced resource location icon message
+ * @return the advanced resource location icon
+ * @since 1.2.5
+ */
+ public static AdvancedResourceLocationIcon fromProtobuf(com.lunarclient.apollo.common.v1.AdvancedResourceLocationIcon icon) {
+ return AdvancedResourceLocationIcon.builder()
+ .resourceLocation(icon.getResourceLocation())
+ .width(icon.getWidth())
+ .height(icon.getHeight())
+ .minU(icon.getMinU())
+ .maxU(icon.getMaxU())
+ .minV(icon.getMinV())
+ .maxV(icon.getMaxV())
+ .build();
}
private NetworkTypes() {
diff --git a/docs/developers/lightweight/json/object-util.mdx b/docs/developers/lightweight/json/object-util.mdx
index 1a79ade80..de62365e5 100644
--- a/docs/developers/lightweight/json/object-util.mdx
+++ b/docs/developers/lightweight/json/object-util.mdx
@@ -99,6 +99,16 @@ public static JsonObject createItemStackIconObject(@Nullable String itemName, in
return iconObject;
}
+public static JsonObject createResourceLocationIconObject(@NotNull String resourceLocation) {
+ JsonObject resourceIconObject = new JsonObject();
+ resourceIconObject.addProperty("resource_location", resourceLocation);
+
+ JsonObject iconObject = new JsonObject();
+ iconObject.add("resource_location", resourceIconObject);
+
+ return iconObject;
+}
+
public static JsonObject createSimpleResourceLocationIconObject(@NotNull String resourceLocation, int size) {
JsonObject simpleIconObject = new JsonObject();
simpleIconObject.addProperty("resource_location", resourceLocation);
diff --git a/docs/developers/lightweight/protobuf/getting-started.mdx b/docs/developers/lightweight/protobuf/getting-started.mdx
index 467c0db9e..b3ce05011 100644
--- a/docs/developers/lightweight/protobuf/getting-started.mdx
+++ b/docs/developers/lightweight/protobuf/getting-started.mdx
@@ -26,7 +26,7 @@ Available fields for each message, including their types, are available on the B
com.lunarclient
apollo-protos
- 0.0.6
+ 0.0.8
```
@@ -41,7 +41,7 @@ Available fields for each message, including their types, are available on the B
}
dependencies {
- api 'com.lunarclient:apollo-protos:0.0.6'
+ api 'com.lunarclient:apollo-protos:0.0.8'
}
```
@@ -55,7 +55,7 @@ Available fields for each message, including their types, are available on the B
}
dependencies {
- api("com.lunarclient:apollo-protos:0.0.6")
+ api("com.lunarclient:apollo-protos:0.0.8")
}
```
diff --git a/docs/developers/lightweight/protobuf/object-util.mdx b/docs/developers/lightweight/protobuf/object-util.mdx
index b4111e5bf..bff0bbdeb 100644
--- a/docs/developers/lightweight/protobuf/object-util.mdx
+++ b/docs/developers/lightweight/protobuf/object-util.mdx
@@ -97,7 +97,7 @@ public static Location toBukkitPlayerLocation(JsonObject message) {
Icon-related methods
```java
-public static Icon createItemStackIconProto(@Nullable String itemName, int itemId, int customModelData) {
+public static ItemStackIcon createItemStackIconProto(@Nullable String itemName, int itemId, int customModelData) {
ItemStackIcon.Builder iconBuilder = ItemStackIcon.newBuilder()
.setItemId(itemId)
.setCustomModelData(customModelData);
@@ -106,21 +106,25 @@ public static Icon createItemStackIconProto(@Nullable String itemName, int itemI
iconBuilder.setItemName(itemName);
}
- return Icon.newBuilder().setItemStack(iconBuilder.build()).build();
+ return iconBuilder.build();
}
-public static Icon createSimpleResourceLocationIconProto(String resourceLocation, int size) {
- SimpleResourceLocationIcon icon = SimpleResourceLocationIcon.newBuilder()
+public static ResourceLocationIcon createResourceLocationIconProto(String resourceLocation) {
+ return ResourceLocationIcon.newBuilder()
.setResourceLocation(resourceLocation)
- .setSize(size)
.build();
+}
- return Icon.newBuilder().setSimpleResourceLocation(icon).build();
+public static SimpleResourceLocationIcon createSimpleResourceLocationIconProto(String resourceLocation, int size) {
+ return SimpleResourceLocationIcon.newBuilder()
+ .setResourceLocation(resourceLocation)
+ .setSize(size)
+ .build();
}
-public static Icon createAdvancedResourceLocationIconProto(String resourceLocation, float width, float height,
+public static AdvancedResourceLocationIcon createAdvancedResourceLocationIconProto(String resourceLocation, float width, float height,
float minU, float maxU, float minV, float maxV) {
- AdvancedResourceLocationIcon icon = AdvancedResourceLocationIcon.newBuilder()
+ return AdvancedResourceLocationIcon.newBuilder()
.setResourceLocation(resourceLocation)
.setWidth(width)
.setHeight(height)
@@ -129,7 +133,5 @@ public static Icon createAdvancedResourceLocationIconProto(String resourceLocati
.setMinV(minV)
.setMaxV(maxV)
.build();
-
- return Icon.newBuilder().setAdvancedResourceLocation(icon).build();
}
```
diff --git a/docs/developers/modules/_meta.json b/docs/developers/modules/_meta.json
index 02faf06d0..1f3fba9c1 100644
--- a/docs/developers/modules/_meta.json
+++ b/docs/developers/modules/_meta.json
@@ -20,6 +20,7 @@
"packetenrichment": "Packet Enrichment",
"richpresence": "Rich Presence",
"saturation": "Saturation",
+ "serverlink": "Server Link",
"serverrule": "Server Rule",
"staffmod": "Staff Mod",
"stopwatch": "Stopwatch",
diff --git a/docs/developers/modules/cooldown.mdx b/docs/developers/modules/cooldown.mdx
index 4264a826b..d04c75f97 100644
--- a/docs/developers/modules/cooldown.mdx
+++ b/docs/developers/modules/cooldown.mdx
@@ -121,7 +121,9 @@ public void displayCooldownItemExample(Player viewer) {
DisplayCooldownMessage message = DisplayCooldownMessage.newBuilder()
.setName("enderpearl-cooldown")
.setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(15)))
- .setIcon(ProtobufUtil.createItemStackIconProto("ENDER_PEARL", 0, 0))
+ .setIcon(Icon.newBuilder()
+ .setItemStack(ProtobufUtil.createItemStackIconProto("ENDER_PEARL", 0, 0))
+ .build())
.build();
ProtobufPacketUtil.sendPacket(viewer, message);
@@ -135,7 +137,9 @@ public void displayCooldownResourceExample(Player viewer) {
DisplayCooldownMessage message = DisplayCooldownMessage.newBuilder()
.setName("lunar-cooldown")
.setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(15)))
- .setIcon(ProtobufUtil.createSimpleResourceLocationIconProto("lunar:logo/logo-200x182.svg", 12))
+ .setIcon(Icon.newBuilder()
+ .setSimpleResourceLocation(ProtobufUtil.createSimpleResourceLocationIconProto("lunar:logo/logo-200x182.svg", 12))
+ .build())
.build();
ProtobufPacketUtil.sendPacket(viewer, message);
diff --git a/docs/developers/modules/hologram.mdx b/docs/developers/modules/hologram.mdx
index 5cc882065..db5ab913e 100644
--- a/docs/developers/modules/hologram.mdx
+++ b/docs/developers/modules/hologram.mdx
@@ -88,7 +88,7 @@ public void resetHologramsExample(Player viewer) {
)
```
-`.lines(Component)` should be a string, or an Adventure `Component`. See the [chat components](https://docs.advntr.dev/text.html) page for more.
+`.lines(Component)` is using the Adventure `Component`. See the [chat components](https://docs.advntr.dev/text.html) page for more.
```java
.lines(List.of(
diff --git a/docs/developers/modules/nametag.mdx b/docs/developers/modules/nametag.mdx
index 679cfcf22..5ca13ec02 100644
--- a/docs/developers/modules/nametag.mdx
+++ b/docs/developers/modules/nametag.mdx
@@ -81,7 +81,7 @@ public void resetNametagsExample(Player viewer) {
### `Nametag` Options
-`.lines(Component)` should be a string, or an Adventure `Component`. See the [chat components](https://docs.advntr.dev/text.html) page for more.
+`.lines(Component)` is using the Adventure `Component`. See the [chat components](https://docs.advntr.dev/text.html) page for more.
```java
.lines(List.of(
diff --git a/docs/developers/modules/notification.mdx b/docs/developers/modules/notification.mdx
index b88c2b6d3..f9b506b1b 100644
--- a/docs/developers/modules/notification.mdx
+++ b/docs/developers/modules/notification.mdx
@@ -41,7 +41,7 @@ public void displayNotificationExample(Player viewer) {
.append(Component.newline())
.append(Component.text("Good luck!", NamedTextColor.GOLD))
)
- .resourceLocation("icons/golden_apple.png") // This field is optional
+ .resourceLocation("textures/items/apple_golden.png") // This field is optional
.displayTime(Duration.ofSeconds(5))
.build());
});
@@ -78,7 +78,7 @@ public void resetNotificationsExample(Player viewer) {
`.resourceLocation(String)` is the resource location of the shown icon.
```java
-.resourceLocation("icons/golden_apple.png")
+.resourceLocation("textures/items/apple_golden.png")
```
`.displayTime(java.time.Duration)` is the duration you want to keep the notification on screen. See the [java.time.Duration Javadocs](https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html) for more.
@@ -125,7 +125,7 @@ public void displayNotificationExample(Player viewer) {
.append(Component.text("Good luck!", NamedTextColor.GOLD))
)
)
- .setResourceLocation("icons/golden_apple.png") // This field is optional
+ .setResourceLocation("textures/items/apple_golden.png") // This field is optional
.setDisplayTime(ProtobufUtil.createDurationProto(Duration.ofSeconds(5)))
.build();
@@ -164,7 +164,7 @@ public void displayNotificationExample(Player viewer) {
));
message.addProperty("display_time", JsonUtil.createDurationObject(Duration.ofSeconds(5)));
- message.addProperty("resource_location", "icons/golden_apple.png");
+ message.addProperty("resource_location", "textures/items/apple_golden.png");
JsonPacketUtil.sendPacket(viewer, message);
}
diff --git a/docs/developers/modules/serverlink.mdx b/docs/developers/modules/serverlink.mdx
new file mode 100644
index 000000000..96ca563d9
--- /dev/null
+++ b/docs/developers/modules/serverlink.mdx
@@ -0,0 +1,282 @@
+import { Tab, Tabs } from 'nextra-theme-docs'
+
+# Server Link Module
+
+## Overview
+
+The server link module provides enhancements and additional support to the vanilla Minecraft server link feature. Which allows you to display important links to players in a dedicated UI.
+
+- Adds improvements to the existing server link features
+ - Ability to set a custom image title resource for the server links menu
+ - Ability to use chat colors and formats for button names
+- Backported legacy version support for all versions below 1.21
+
+
+
+## Integration
+
+### Sample Code
+Explore each integration by cycling through each tab, to find the best fit for your requirements and needs.
+
+
+
+
+
+### Override the Server Link Menu resource
+
+```java
+public void overrideServerLinkResourceExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+
+ apolloPlayerOpt.ifPresent(apolloPlayer -> {
+ this.serverLinkModule.overrideServerLinkResource(apolloPlayer, ResourceLocationIcon.builder()
+ .resourceLocation("lunar:logo/logo-100x100.png")
+ .build());
+ });
+}
+```
+
+### Reset the Server Link Menu resource
+
+```java
+public void resetServerLinkResourceExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+ apolloPlayerOpt.ifPresent(this.serverLinkModule::resetServerLinkResource);
+}
+```
+
+### Add Server Link
+
+```java
+public void addServerLinkExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+
+ apolloPlayerOpt.ifPresent(apolloPlayer -> {
+ this.serverLinkModule.addServerLink(apolloPlayer, Lists.newArrayList(
+ ServerLink.builder()
+ .id("website")
+ .displayName(Component.text("Website", NamedTextColor.LIGHT_PURPLE))
+ .url("https://www.lunarclient.com/")
+ .build(),
+ ServerLink.builder()
+ .id("support")
+ .displayName(Component.text("Support", NamedTextColor.AQUA))
+ .url("https://support.lunarclient.com/")
+ .build(),
+ ServerLink.builder()
+ .id("status")
+ .displayName(Component.text("Status", NamedTextColor.RED))
+ .url("https://status.lunarclient.com/")
+ .build()
+ ));
+ });
+}
+```
+
+### Remove Server Link
+
+```java
+public void removeServerLinkExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+
+ apolloPlayerOpt.ifPresent(apolloPlayer -> {
+ this.serverLinkModule.removeServerLink(apolloPlayer, Lists.newArrayList(
+ "website", "support", "status"
+ ));
+ });
+}
+```
+
+### Resetting all Server Links
+
+```java
+public void resetServerLinksExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+ apolloPlayerOpt.ifPresent(this.serverLinkModule::resetServerLinks);
+}
+```
+
+### `ServerLink` Options
+
+`.id(String)` sets a unique identifier for the server link.
+
+```java
+.id("website")
+```
+
+`.displayName(Component)` is the public-facing name of the server link. It should contain an Adventure `Component`. See the [chat components](https://docs.advntr.dev/text.html) page for more.
+
+```java
+.displayName(Component.text("Website", NamedTextColor.LIGHT_PURPLE))
+```
+
+`.url(String)` should be a string that specifies the destination URL that will be opened from the server link.
+
+```java
+.url("https://www.lunarclient.com/")
+```
+
+
+
+
+
+
+**Override the Server Link Menu resource**
+
+```java
+public void overrideServerLinkResourceExample(Player viewer) {
+ OverrideServerLinkResourceMessage message = OverrideServerLinkResourceMessage.newBuilder()
+ .setIcon(ProtobufUtil.createResourceLocationIconProto("lunar:logo/logo-100x100.png"))
+ .build();
+
+ ProtobufPacketUtil.sendPacket(viewer, message);
+}
+```
+
+**Reset the Server Link Menu resource**
+
+```java
+public void resetServerLinkResourceExample(Player viewer) {
+ ResetServerLinkResourceMessage message = ResetServerLinkResourceMessage.getDefaultInstance();
+ ProtobufPacketUtil.sendPacket(viewer, message);
+}
+```
+
+**Add Server Link**
+
+```java
+public void addServerLinkExample(Player viewer) {
+ List serverLinks = Lists.newArrayList(
+ ServerLink.newBuilder()
+ .setId("website")
+ .setDisplayNameAdventureJsonLines(AdventureUtil.toJson(
+ Component.text("Website", NamedTextColor.LIGHT_PURPLE)))
+ .setUrl("https://www.lunarclient.com/")
+ .build(),
+ ServerLink.newBuilder()
+ .setId("support")
+ .setDisplayNameAdventureJsonLines(AdventureUtil.toJson(
+ Component.text("Support", NamedTextColor.AQUA)))
+ .setUrl("https://support.lunarclient.com/")
+ .build(),
+ ServerLink.newBuilder()
+ .setId("status")
+ .setDisplayNameAdventureJsonLines(AdventureUtil.toJson(
+ Component.text("Status", NamedTextColor.RED)))
+ .setUrl("https://status.lunarclient.com/")
+ .build()
+ );
+
+ AddServerLinkMessage message = AddServerLinkMessage.newBuilder()
+ .addAllServerLinks(serverLinks)
+ .build();
+
+ ProtobufPacketUtil.sendPacket(viewer, message);
+}
+```
+
+**Remove Server Link**
+
+```java
+public void removeServerLinkExample(Player viewer) {
+ List serverLinkIds = Lists.newArrayList("website", "support", "status");
+
+ RemoveServerLinkMessage message = RemoveServerLinkMessage.newBuilder()
+ .addAllServerLinkIds(serverLinkIds)
+ .build();
+
+ ProtobufPacketUtil.sendPacket(viewer, message);
+}
+```
+
+**Resetting all Server Links**
+
+```java
+public void resetServerLinksExample(Player viewer) {
+ ResetServerLinksMessage message = ResetServerLinksMessage.getDefaultInstance();
+ ProtobufPacketUtil.sendPacket(viewer, message);
+}
+```
+
+
+
+
+
+**Override the Server Link Menu resource**
+
+```java
+public void overrideServerLinkResourceExample(Player viewer) {
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.OverrideServerLinkResourceMessage");
+ message.add("icon", JsonUtil.createResourceLocationIconObject("lunar:logo/logo-100x100.png"));
+
+ JsonPacketUtil.sendPacket(viewer, message);
+}
+```
+
+**Reset the Server Link Menu resource**
+
+```java
+public void resetServerLinkResourceExample(Player viewer) {
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.ResetServerLinkResourceMessage");
+
+ JsonPacketUtil.sendPacket(viewer, message);
+}
+```
+
+**Add Server Link**
+
+```java
+public void addServerLinkExample(Player viewer) {
+ JsonArray serverLinks = Lists.newArrayList(
+ this.createServerLinkObject("website", Component.text("Website", NamedTextColor.LIGHT_PURPLE), "https://www.lunarclient.com/"),
+ this.createServerLinkObject("support", Component.text("Support", NamedTextColor.AQUA), "https://support.lunarclient.com/"),
+ this.createServerLinkObject("status", Component.text("Status", NamedTextColor.RED), "https://status.lunarclient.com/")
+ ).stream().collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
+
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.AddServerLinkMessage");
+ message.add("server_links", serverLinks);
+
+ JsonPacketUtil.sendPacket(viewer, message);
+}
+
+private JsonObject createServerLinkObject(String id, Component displayName, String url) {
+ JsonObject serverLinkObject = new JsonObject();
+ serverLinkObject.addProperty("id", id);
+ serverLinkObject.addProperty("display_name_adventure_json_lines", AdventureUtil.toJson(displayName));
+ serverLinkObject.addProperty("url", url);
+ return serverLinkObject;
+}
+```
+
+**Remove Server Link**
+
+```java
+public void removeServerLinkExample(Player viewer) {
+ JsonArray serverLinkIds = Lists.newArrayList("website", "support", "status")
+ .stream().collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
+
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.RemoveServerLinkMessage");
+ message.add("server_link_ids", serverLinkIds);
+
+ JsonPacketUtil.sendPacket(viewer, message);
+}
+```
+
+**Resetting all Server Links**
+
+```java
+public void resetServerLinksExample(Player viewer) {
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.ResetServerLinksMessage");
+
+ JsonPacketUtil.sendPacket(viewer, message);
+}
+```
+
+
+
+
diff --git a/docs/developers/utilities/icons.mdx b/docs/developers/utilities/icons.mdx
index 4c01099f9..449151b7c 100644
--- a/docs/developers/utilities/icons.mdx
+++ b/docs/developers/utilities/icons.mdx
@@ -61,6 +61,40 @@ public static ItemStackIcon itemStackNameIconExample() {
}
```
+## `ResourceLocationIcon` Builder
+
+If you're using a custom resource pack and want to create an icon, you can use the `ResourceLocationIcon` builder.
+
+
+ The supplied resource location should point to a texture in the resource pack, or the path to an `svg` file in the resource pack.
+
+
+```java
+public class ResourceLocationIcon extends Icon {
+
+ /**
+ * Returns the icon {@link String} resource location.
+ *
+ * Represents a path to an icon that will appear for the player.
+ *
+ * @return the icon resource location
+ * @since 1.2.5
+ */
+ String resourceLocation;
+
+}
+```
+
+### Sample Code
+
+```java
+public static ResourceLocationIcon resourceLocationExample() {
+ return ResourceLocationIcon.builder()
+ .resourceLocation("minecraft:textures/item/apple.png") // Resource path location
+ .build();
+}
+```
+
## `SimpleResourceLocation` Builder
If you're using a custom resource pack and want to create an icon, you can use the `SimpleResourceLocation` builder.
diff --git a/docs/public/modules/serverlink/overview.png b/docs/public/modules/serverlink/overview.png
new file mode 100644
index 000000000..512543fef
Binary files /dev/null and b/docs/public/modules/serverlink/overview.png differ
diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/ApolloApiExamplePlatform.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/ApolloApiExamplePlatform.java
index 6fc763dcf..8575bc784 100644
--- a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/ApolloApiExamplePlatform.java
+++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/ApolloApiExamplePlatform.java
@@ -47,6 +47,7 @@
import com.lunarclient.apollo.example.api.module.NotificationApiExample;
import com.lunarclient.apollo.example.api.module.PayNowApiExample;
import com.lunarclient.apollo.example.api.module.RichPresenceApiExample;
+import com.lunarclient.apollo.example.api.module.ServerLinkApiExample;
import com.lunarclient.apollo.example.api.module.ServerRuleApiExample;
import com.lunarclient.apollo.example.api.module.StaffModApiExample;
import com.lunarclient.apollo.example.api.module.StopwatchApiExample;
@@ -97,6 +98,7 @@ public void registerModuleExamples() {
this.setNotificationExample(new NotificationApiExample());
this.setPayNowExample(new PayNowApiExample());
this.setRichPresenceExample(new RichPresenceApiExample());
+ this.setServerLinkExample(new ServerLinkApiExample());
this.setServerRuleExample(new ServerRuleApiExample());
this.setStaffModExample(new StaffModApiExample());
this.setStopwatchExample(new StopwatchApiExample());
diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/listener/ApolloPlayerApiListener.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/listener/ApolloPlayerApiListener.java
index 837b525d4..3f1dc22e3 100644
--- a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/listener/ApolloPlayerApiListener.java
+++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/listener/ApolloPlayerApiListener.java
@@ -29,6 +29,7 @@
import com.lunarclient.apollo.event.player.ApolloRegisterPlayerEvent;
import com.lunarclient.apollo.example.ApolloExamplePlugin;
import com.lunarclient.apollo.example.api.module.TeamApiExample;
+import com.lunarclient.apollo.example.module.impl.ServerLinkExample;
import com.lunarclient.apollo.player.ApolloPlayer;
import org.bukkit.entity.Player;
@@ -61,6 +62,10 @@ private void onApolloRegister(ApolloRegisterPlayerEvent event) {
this.example.getCooldownExample().displayCooldownItemExample(player);
this.example.getNametagExample().overrideNametagExample(player);
this.example.getWaypointExample().displayWaypointExample(player);
+
+ ServerLinkExample serverLinkExample = this.example.getServerLinkExample();
+ serverLinkExample.overrideServerLinkResourceExample(player);
+ serverLinkExample.addServerLinkExample(player);
}
}
diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/NotificationApiExample.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/NotificationApiExample.java
index 528953801..512074740 100644
--- a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/NotificationApiExample.java
+++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/NotificationApiExample.java
@@ -51,7 +51,7 @@ public void displayNotificationExample(Player viewer) {
.append(Component.newline())
.append(Component.text("Good luck!", NamedTextColor.GOLD))
)
- .resourceLocation("icons/golden_apple.png") // This field is optional
+ .resourceLocation("textures/items/apple_golden.png") // This field is optional
.displayTime(Duration.ofSeconds(5))
.build());
});
diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/ServerLinkApiExample.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/ServerLinkApiExample.java
new file mode 100644
index 000000000..8c8f9e44c
--- /dev/null
+++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/ServerLinkApiExample.java
@@ -0,0 +1,101 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.example.api.module;
+
+import com.google.common.collect.Lists;
+import com.lunarclient.apollo.Apollo;
+import com.lunarclient.apollo.common.icon.ResourceLocationIcon;
+import com.lunarclient.apollo.example.module.impl.ServerLinkExample;
+import com.lunarclient.apollo.module.serverlink.ServerLink;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModule;
+import com.lunarclient.apollo.player.ApolloPlayer;
+import java.util.Optional;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.entity.Player;
+
+public class ServerLinkApiExample extends ServerLinkExample {
+
+ private final ServerLinkModule serverLinkModule = Apollo.getModuleManager().getModule(ServerLinkModule.class);
+
+ @Override
+ public void overrideServerLinkResourceExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+
+ apolloPlayerOpt.ifPresent(apolloPlayer -> {
+ this.serverLinkModule.overrideServerLinkResource(apolloPlayer, ResourceLocationIcon.builder()
+ .resourceLocation("lunar:logo/logo-100x100.png")
+ .build());
+ });
+ }
+
+ @Override
+ public void resetServerLinkResourceExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+ apolloPlayerOpt.ifPresent(this.serverLinkModule::resetServerLinkResource);
+ }
+
+ @Override
+ public void addServerLinkExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+
+ apolloPlayerOpt.ifPresent(apolloPlayer -> {
+ this.serverLinkModule.addServerLink(apolloPlayer, Lists.newArrayList(
+ ServerLink.builder()
+ .id("website")
+ .displayName(Component.text("Website", NamedTextColor.LIGHT_PURPLE))
+ .url("https://www.lunarclient.com/")
+ .build(),
+ ServerLink.builder()
+ .id("support")
+ .displayName(Component.text("Support", NamedTextColor.AQUA))
+ .url("https://support.lunarclient.com/")
+ .build(),
+ ServerLink.builder()
+ .id("status")
+ .displayName(Component.text("Status", NamedTextColor.RED))
+ .url("https://status.lunarclient.com/")
+ .build()
+ ));
+ });
+ }
+
+ @Override
+ public void removeServerLinkExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+
+ apolloPlayerOpt.ifPresent(apolloPlayer -> {
+ this.serverLinkModule.removeServerLink(apolloPlayer, Lists.newArrayList(
+ "website", "support", "status"
+ ));
+ });
+ }
+
+ @Override
+ public void resetServerLinksExample(Player viewer) {
+ Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
+ apolloPlayerOpt.ifPresent(this.serverLinkModule::resetServerLinks);
+ }
+
+}
diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/util/IconExample.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/util/IconExample.java
index ebd6f896a..426b63d1f 100644
--- a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/util/IconExample.java
+++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/util/IconExample.java
@@ -25,6 +25,7 @@
import com.lunarclient.apollo.common.icon.AdvancedResourceLocationIcon;
import com.lunarclient.apollo.common.icon.ItemStackIcon;
+import com.lunarclient.apollo.common.icon.ResourceLocationIcon;
import com.lunarclient.apollo.common.icon.SimpleResourceLocationIcon;
import org.bukkit.Material;
@@ -42,6 +43,12 @@ public static ItemStackIcon itemStackNameIconExample() {
.build();
}
+ public static ResourceLocationIcon resourceLocationExample() {
+ return ResourceLocationIcon.builder()
+ .resourceLocation("minecraft:textures/item/apple.png") // Resource path location
+ .build();
+ }
+
public static SimpleResourceLocationIcon simpleResourceLocationIconExample() {
return SimpleResourceLocationIcon.builder()
.resourceLocation("icons/server-logo.png") // Resource path location
diff --git a/example/bukkit/api/src/main/resources/plugin.yml b/example/bukkit/api/src/main/resources/plugin.yml
index fbe960bbe..042f5363f 100644
--- a/example/bukkit/api/src/main/resources/plugin.yml
+++ b/example/bukkit/api/src/main/resources/plugin.yml
@@ -49,6 +49,8 @@ commands:
description: "Pay Now!"
richpresence:
description: "Rich Presence!"
+ serverlink:
+ description: "Server Links!"
saturation:
description: "Saturation!"
serverrule:
diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/ApolloExamplePlugin.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/ApolloExamplePlugin.java
index f69da42ef..81d28966a 100644
--- a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/ApolloExamplePlugin.java
+++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/ApolloExamplePlugin.java
@@ -43,6 +43,7 @@
import com.lunarclient.apollo.example.command.PayNowCommand;
import com.lunarclient.apollo.example.command.RichPresenceCommand;
import com.lunarclient.apollo.example.command.SaturationCommand;
+import com.lunarclient.apollo.example.command.ServerLinkCommand;
import com.lunarclient.apollo.example.command.ServerRuleCommand;
import com.lunarclient.apollo.example.command.StaffModCommand;
import com.lunarclient.apollo.example.command.StopwatchCommand;
@@ -73,6 +74,7 @@
import com.lunarclient.apollo.example.module.impl.PayNowExample;
import com.lunarclient.apollo.example.module.impl.RichPresenceExample;
import com.lunarclient.apollo.example.module.impl.SaturationExample;
+import com.lunarclient.apollo.example.module.impl.ServerLinkExample;
import com.lunarclient.apollo.example.module.impl.ServerRuleExample;
import com.lunarclient.apollo.example.module.impl.StaffModExample;
import com.lunarclient.apollo.example.module.impl.StopwatchExample;
@@ -112,6 +114,7 @@ public abstract class ApolloExamplePlugin extends JavaPlugin {
private NotificationExample notificationExample;
private PayNowExample payNowExample;
private RichPresenceExample richPresenceExample;
+ private ServerLinkExample serverLinkExample;
private SaturationExample saturationExample;
private ServerRuleExample serverRuleExample;
private StaffModExample staffModExample;
@@ -163,6 +166,7 @@ private void registerCommonCommands() {
this.getCommand("paynow").setExecutor(new PayNowCommand());
this.getCommand("richpresence").setExecutor(new RichPresenceCommand());
this.getCommand("saturation").setExecutor(new SaturationCommand());
+ this.getCommand("serverlink").setExecutor(new ServerLinkCommand());
this.getCommand("serverrule").setExecutor(new ServerRuleCommand());
this.getCommand("staffmod").setExecutor(new StaffModCommand());
this.getCommand("stopwatch").setExecutor(new StopwatchCommand());
diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/ServerLinkCommand.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/ServerLinkCommand.java
new file mode 100644
index 000000000..d52802288
--- /dev/null
+++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/ServerLinkCommand.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.example.command;
+
+import com.lunarclient.apollo.example.ApolloExamplePlugin;
+import com.lunarclient.apollo.example.module.impl.ServerLinkExample;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+public class ServerLinkCommand implements CommandExecutor {
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
+ if (!(sender instanceof Player)) {
+ sender.sendMessage("Player only!");
+ return true;
+ }
+
+ Player player = (Player) sender;
+
+ if (args.length != 1) {
+ player.sendMessage("Usage: /serverlink ");
+ return true;
+ }
+
+ ServerLinkExample serverLinkExample = ApolloExamplePlugin.getInstance().getServerLinkExample();
+
+ switch (args[0].toLowerCase()) {
+ case "overrideresource": {
+ serverLinkExample.overrideServerLinkResourceExample(player);
+ player.sendMessage("Overriding server link resource...");
+ break;
+ }
+
+ case "resetresource": {
+ serverLinkExample.resetServerLinkResourceExample(player);
+ player.sendMessage("Resetting server link resource...");
+ break;
+ }
+
+ case "addserverlink": {
+ serverLinkExample.addServerLinkExample(player);
+ player.sendMessage("Adding server link...");
+ break;
+ }
+
+ case "removeserverlink": {
+ serverLinkExample.removeServerLinkExample(player);
+ player.sendMessage("Removing server link...");
+ break;
+ }
+
+ case "resetserverlinks": {
+ serverLinkExample.resetServerLinksExample(player);
+ player.sendMessage("Resetting server links...");
+ break;
+ }
+
+ default: {
+ player.sendMessage("Usage: /serverlink ");
+ break;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/ServerLinkExample.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/ServerLinkExample.java
new file mode 100644
index 000000000..26ad56fba
--- /dev/null
+++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/ServerLinkExample.java
@@ -0,0 +1,41 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.example.module.impl;
+
+import com.lunarclient.apollo.example.module.ApolloModuleExample;
+import org.bukkit.entity.Player;
+
+public abstract class ServerLinkExample extends ApolloModuleExample {
+
+ public abstract void overrideServerLinkResourceExample(Player viewer);
+
+ public abstract void resetServerLinkResourceExample(Player viewer);
+
+ public abstract void addServerLinkExample(Player viewer);
+
+ public abstract void removeServerLinkExample(Player viewer);
+
+ public abstract void resetServerLinksExample(Player viewer);
+
+}
diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/ApolloJsonExamplePlatform.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/ApolloJsonExamplePlatform.java
index 6ca73781a..196b6f81a 100644
--- a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/ApolloJsonExamplePlatform.java
+++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/ApolloJsonExamplePlatform.java
@@ -43,6 +43,7 @@
import com.lunarclient.apollo.example.json.module.NotificationJsonExample;
import com.lunarclient.apollo.example.json.module.PayNowJsonExample;
import com.lunarclient.apollo.example.json.module.RichPresenceJsonExample;
+import com.lunarclient.apollo.example.json.module.ServerLinkJsonExample;
import com.lunarclient.apollo.example.json.module.ServerRuleJsonExample;
import com.lunarclient.apollo.example.json.module.StaffModJsonExample;
import com.lunarclient.apollo.example.json.module.StopwatchJsonExample;
@@ -85,6 +86,7 @@ public void registerModuleExamples() {
this.setNotificationExample(new NotificationJsonExample());
this.setPayNowExample(new PayNowJsonExample());
this.setRichPresenceExample(new RichPresenceJsonExample());
+ this.setServerLinkExample(new ServerLinkJsonExample());
this.setServerRuleExample(new ServerRuleJsonExample());
this.setStaffModExample(new StaffModJsonExample());
this.setStopwatchExample(new StopwatchJsonExample());
diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/NotificationJsonExample.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/NotificationJsonExample.java
index c8f92e4a2..70c4b1bdc 100644
--- a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/NotificationJsonExample.java
+++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/NotificationJsonExample.java
@@ -51,7 +51,7 @@ public void displayNotificationExample(Player viewer) {
));
message.addProperty("display_time", JsonUtil.createDurationObject(Duration.ofSeconds(5)));
- message.addProperty("resource_location", "icons/golden_apple.png");
+ message.addProperty("resource_location", "textures/items/apple_golden.png");
JsonPacketUtil.sendPacket(viewer, message);
}
diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/ServerLinkJsonExample.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/ServerLinkJsonExample.java
new file mode 100644
index 000000000..6cd6f7cd4
--- /dev/null
+++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/ServerLinkJsonExample.java
@@ -0,0 +1,99 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.example.json.module;
+
+import com.google.common.collect.Lists;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.lunarclient.apollo.example.json.util.AdventureUtil;
+import com.lunarclient.apollo.example.json.util.JsonPacketUtil;
+import com.lunarclient.apollo.example.json.util.JsonUtil;
+import com.lunarclient.apollo.example.module.impl.ServerLinkExample;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.entity.Player;
+
+public class ServerLinkJsonExample extends ServerLinkExample {
+
+ @Override
+ public void overrideServerLinkResourceExample(Player viewer) {
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.OverrideServerLinkResourceMessage");
+ message.add("icon", JsonUtil.createResourceLocationIconObject("lunar:logo/logo-100x100.png"));
+
+ JsonPacketUtil.sendPacket(viewer, message);
+ }
+
+ @Override
+ public void resetServerLinkResourceExample(Player viewer) {
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.ResetServerLinkResourceMessage");
+
+ JsonPacketUtil.sendPacket(viewer, message);
+ }
+
+ @Override
+ public void addServerLinkExample(Player viewer) {
+ JsonArray serverLinks = Lists.newArrayList(
+ this.createServerLinkObject("website", Component.text("Website", NamedTextColor.LIGHT_PURPLE), "https://www.lunarclient.com/"),
+ this.createServerLinkObject("support", Component.text("Support", NamedTextColor.AQUA), "https://support.lunarclient.com/"),
+ this.createServerLinkObject("status", Component.text("Status", NamedTextColor.RED), "https://status.lunarclient.com/")
+ ).stream().collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
+
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.AddServerLinkMessage");
+ message.add("server_links", serverLinks);
+
+ JsonPacketUtil.sendPacket(viewer, message);
+ }
+
+ private JsonObject createServerLinkObject(String id, Component displayName, String url) {
+ JsonObject serverLinkObject = new JsonObject();
+ serverLinkObject.addProperty("id", id);
+ serverLinkObject.addProperty("display_name_adventure_json_lines", AdventureUtil.toJson(displayName));
+ serverLinkObject.addProperty("url", url);
+ return serverLinkObject;
+ }
+
+ @Override
+ public void removeServerLinkExample(Player viewer) {
+ JsonArray serverLinkIds = Lists.newArrayList("website", "support", "status")
+ .stream().collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
+
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.RemoveServerLinkMessage");
+ message.add("server_link_ids", serverLinkIds);
+
+ JsonPacketUtil.sendPacket(viewer, message);
+ }
+
+ @Override
+ public void resetServerLinksExample(Player viewer) {
+ JsonObject message = new JsonObject();
+ message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.serverlink.v1.ResetServerLinksMessage");
+
+ JsonPacketUtil.sendPacket(viewer, message);
+ }
+
+}
diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/util/JsonUtil.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/util/JsonUtil.java
index dce4fdf0d..841a96943 100644
--- a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/util/JsonUtil.java
+++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/util/JsonUtil.java
@@ -150,6 +150,16 @@ public static JsonObject createItemStackIconObject(@Nullable String itemName, in
return iconObject;
}
+ public static JsonObject createResourceLocationIconObject(@NotNull String resourceLocation) {
+ JsonObject resourceIconObject = new JsonObject();
+ resourceIconObject.addProperty("resource_location", resourceLocation);
+
+ JsonObject iconObject = new JsonObject();
+ iconObject.add("resource_location", resourceIconObject);
+
+ return iconObject;
+ }
+
public static JsonObject createSimpleResourceLocationIconObject(@NotNull String resourceLocation, int size) {
JsonObject simpleIconObject = new JsonObject();
simpleIconObject.addProperty("resource_location", resourceLocation);
diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/ApolloProtoExamplePlatform.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/ApolloProtoExamplePlatform.java
index 57c5a1930..f02c1bfd3 100644
--- a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/ApolloProtoExamplePlatform.java
+++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/ApolloProtoExamplePlatform.java
@@ -43,6 +43,7 @@
import com.lunarclient.apollo.example.proto.module.NotificationProtoExample;
import com.lunarclient.apollo.example.proto.module.PayNowProtoExample;
import com.lunarclient.apollo.example.proto.module.RichPresenceProtoExample;
+import com.lunarclient.apollo.example.proto.module.ServerLinkProtoExample;
import com.lunarclient.apollo.example.proto.module.ServerRuleProtoExample;
import com.lunarclient.apollo.example.proto.module.StaffModProtoExample;
import com.lunarclient.apollo.example.proto.module.StopwatchProtoExample;
@@ -85,6 +86,7 @@ public void registerModuleExamples() {
this.setNotificationExample(new NotificationProtoExample());
this.setPayNowExample(new PayNowProtoExample());
this.setRichPresenceExample(new RichPresenceProtoExample());
+ this.setServerLinkExample(new ServerLinkProtoExample());
this.setServerRuleExample(new ServerRuleProtoExample());
this.setStaffModExample(new StaffModProtoExample());
this.setStopwatchExample(new StopwatchProtoExample());
diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CooldownProtoExample.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CooldownProtoExample.java
index d8c6741ca..cd515b09b 100644
--- a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CooldownProtoExample.java
+++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CooldownProtoExample.java
@@ -23,6 +23,7 @@
*/
package com.lunarclient.apollo.example.proto.module;
+import com.lunarclient.apollo.common.v1.Icon;
import com.lunarclient.apollo.cooldown.v1.DisplayCooldownMessage;
import com.lunarclient.apollo.cooldown.v1.RemoveCooldownMessage;
import com.lunarclient.apollo.cooldown.v1.ResetCooldownsMessage;
@@ -39,7 +40,9 @@ public void displayCooldownItemExample(Player viewer) {
DisplayCooldownMessage message = DisplayCooldownMessage.newBuilder()
.setName("enderpearl-cooldown")
.setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(15)))
- .setIcon(ProtobufUtil.createItemStackIconProto("ENDER_PEARL", 0, 0))
+ .setIcon(Icon.newBuilder()
+ .setItemStack(ProtobufUtil.createItemStackIconProto("ENDER_PEARL", 0, 0))
+ .build())
.build();
ProtobufPacketUtil.sendPacket(viewer, message);
@@ -50,7 +53,9 @@ public void displayCooldownResourceExample(Player viewer) {
DisplayCooldownMessage message = DisplayCooldownMessage.newBuilder()
.setName("lunar-cooldown")
.setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(15)))
- .setIcon(ProtobufUtil.createSimpleResourceLocationIconProto("lunar:logo/logo-200x182.svg", 12))
+ .setIcon(Icon.newBuilder()
+ .setSimpleResourceLocation(ProtobufUtil.createSimpleResourceLocationIconProto("lunar:logo/logo-200x182.svg", 12))
+ .build())
.build();
ProtobufPacketUtil.sendPacket(viewer, message);
diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/NotificationProtoExample.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/NotificationProtoExample.java
index 3fcf905cb..0853dca56 100644
--- a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/NotificationProtoExample.java
+++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/NotificationProtoExample.java
@@ -50,7 +50,7 @@ public void displayNotificationExample(Player viewer) {
.append(Component.text("Good luck!", NamedTextColor.GOLD))
)
)
- .setResourceLocation("icons/golden_apple.png") // This field is optional
+ .setResourceLocation("textures/items/apple_golden.png") // This field is optional
.setDisplayTime(ProtobufUtil.createDurationProto(Duration.ofSeconds(5)))
.build();
diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/ServerLinkProtoExample.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/ServerLinkProtoExample.java
new file mode 100644
index 000000000..c39e53cc2
--- /dev/null
+++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/ServerLinkProtoExample.java
@@ -0,0 +1,106 @@
+/*
+ * This file is part of Apollo, licensed under the MIT License.
+ *
+ * Copyright (c) 2026 Moonsworth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.lunarclient.apollo.example.proto.module;
+
+import com.google.common.collect.Lists;
+import com.lunarclient.apollo.example.module.impl.ServerLinkExample;
+import com.lunarclient.apollo.example.proto.util.AdventureUtil;
+import com.lunarclient.apollo.example.proto.util.ProtobufPacketUtil;
+import com.lunarclient.apollo.example.proto.util.ProtobufUtil;
+import com.lunarclient.apollo.serverlink.v1.AddServerLinkMessage;
+import com.lunarclient.apollo.serverlink.v1.OverrideServerLinkResourceMessage;
+import com.lunarclient.apollo.serverlink.v1.RemoveServerLinkMessage;
+import com.lunarclient.apollo.serverlink.v1.ResetServerLinkResourceMessage;
+import com.lunarclient.apollo.serverlink.v1.ResetServerLinksMessage;
+import com.lunarclient.apollo.serverlink.v1.ServerLink;
+import java.util.List;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.entity.Player;
+
+public class ServerLinkProtoExample extends ServerLinkExample {
+
+ @Override
+ public void overrideServerLinkResourceExample(Player viewer) {
+ OverrideServerLinkResourceMessage message = OverrideServerLinkResourceMessage.newBuilder()
+ .setIcon(ProtobufUtil.createResourceLocationIconProto("lunar:logo/logo-100x100.png"))
+ .build();
+
+ ProtobufPacketUtil.sendPacket(viewer, message);
+ }
+
+ @Override
+ public void resetServerLinkResourceExample(Player viewer) {
+ ResetServerLinkResourceMessage message = ResetServerLinkResourceMessage.getDefaultInstance();
+ ProtobufPacketUtil.sendPacket(viewer, message);
+ }
+
+ @Override
+ public void addServerLinkExample(Player viewer) {
+ List serverLinks = Lists.newArrayList(
+ ServerLink.newBuilder()
+ .setId("website")
+ .setDisplayNameAdventureJsonLines(AdventureUtil.toJson(
+ Component.text("Website", NamedTextColor.LIGHT_PURPLE)))
+ .setUrl("https://www.lunarclient.com/")
+ .build(),
+ ServerLink.newBuilder()
+ .setId("support")
+ .setDisplayNameAdventureJsonLines(AdventureUtil.toJson(
+ Component.text("Support", NamedTextColor.AQUA)))
+ .setUrl("https://support.lunarclient.com/")
+ .build(),
+ ServerLink.newBuilder()
+ .setId("status")
+ .setDisplayNameAdventureJsonLines(AdventureUtil.toJson(
+ Component.text("Status", NamedTextColor.RED)))
+ .setUrl("https://status.lunarclient.com/")
+ .build()
+ );
+
+ AddServerLinkMessage message = AddServerLinkMessage.newBuilder()
+ .addAllServerLinks(serverLinks)
+ .build();
+
+ ProtobufPacketUtil.sendPacket(viewer, message);
+ }
+
+ @Override
+ public void removeServerLinkExample(Player viewer) {
+ List serverLinkIds = Lists.newArrayList("website", "support", "status");
+
+ RemoveServerLinkMessage message = RemoveServerLinkMessage.newBuilder()
+ .addAllServerLinkIds(serverLinkIds)
+ .build();
+
+ ProtobufPacketUtil.sendPacket(viewer, message);
+ }
+
+ @Override
+ public void resetServerLinksExample(Player viewer) {
+ ResetServerLinksMessage message = ResetServerLinksMessage.getDefaultInstance();
+ ProtobufPacketUtil.sendPacket(viewer, message);
+ }
+
+}
diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/util/ProtobufUtil.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/util/ProtobufUtil.java
index affc6a1f2..2c35b3c34 100644
--- a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/util/ProtobufUtil.java
+++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/util/ProtobufUtil.java
@@ -28,8 +28,8 @@
import com.lunarclient.apollo.common.v1.BlockLocation;
import com.lunarclient.apollo.common.v1.Cuboid2D;
import com.lunarclient.apollo.common.v1.EntityId;
-import com.lunarclient.apollo.common.v1.Icon;
import com.lunarclient.apollo.common.v1.ItemStackIcon;
+import com.lunarclient.apollo.common.v1.ResourceLocationIcon;
import com.lunarclient.apollo.common.v1.SimpleResourceLocationIcon;
import com.lunarclient.apollo.common.v1.Uuid;
import java.awt.Color;
@@ -114,7 +114,7 @@ public static Location toBukkitLocation(com.lunarclient.apollo.common.v1.PlayerL
return location;
}
- public static Icon createItemStackIconProto(@Nullable String itemName, int itemId, int customModelData) {
+ public static ItemStackIcon createItemStackIconProto(@Nullable String itemName, int itemId, int customModelData) {
ItemStackIcon.Builder iconBuilder = ItemStackIcon.newBuilder()
.setItemId(itemId)
.setCustomModelData(customModelData);
@@ -123,21 +123,25 @@ public static Icon createItemStackIconProto(@Nullable String itemName, int itemI
iconBuilder.setItemName(itemName);
}
- return Icon.newBuilder().setItemStack(iconBuilder.build()).build();
+ return iconBuilder.build();
}
- public static Icon createSimpleResourceLocationIconProto(String resourceLocation, int size) {
- SimpleResourceLocationIcon icon = SimpleResourceLocationIcon.newBuilder()
+ public static ResourceLocationIcon createResourceLocationIconProto(String resourceLocation) {
+ return ResourceLocationIcon.newBuilder()
.setResourceLocation(resourceLocation)
- .setSize(size)
.build();
+ }
- return Icon.newBuilder().setSimpleResourceLocation(icon).build();
+ public static SimpleResourceLocationIcon createSimpleResourceLocationIconProto(String resourceLocation, int size) {
+ return SimpleResourceLocationIcon.newBuilder()
+ .setResourceLocation(resourceLocation)
+ .setSize(size)
+ .build();
}
- public static Icon createAdvancedResourceLocationIconProto(String resourceLocation, float width, float height,
+ public static AdvancedResourceLocationIcon createAdvancedResourceLocationIconProto(String resourceLocation, float width, float height,
float minU, float maxU, float minV, float maxV) {
- AdvancedResourceLocationIcon icon = AdvancedResourceLocationIcon.newBuilder()
+ return AdvancedResourceLocationIcon.newBuilder()
.setResourceLocation(resourceLocation)
.setWidth(width)
.setHeight(height)
@@ -146,8 +150,6 @@ public static Icon createAdvancedResourceLocationIconProto(String resourceLocati
.setMinV(minV)
.setMaxV(maxV)
.build();
-
- return Icon.newBuilder().setAdvancedResourceLocation(icon).build();
}
private ProtobufUtil() {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 1a56e5582..08faec7e0 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -11,7 +11,7 @@ geantyref = "1.3.11"
idea = "1.1.7"
jetbrains = "24.0.1"
lombok = "1.18.38"
-protobuf = "0.0.6"
+protobuf = "0.0.8"
gson = "2.10.1"
shadow = "8.1.1"
spotless = "6.13.0"
diff --git a/platform/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java b/platform/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java
index fd5918685..8965682cf 100644
--- a/platform/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java
+++ b/platform/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java
@@ -68,6 +68,8 @@
import com.lunarclient.apollo.module.richpresence.RichPresenceModule;
import com.lunarclient.apollo.module.richpresence.RichPresenceModuleImpl;
import com.lunarclient.apollo.module.saturation.SaturationModule;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModule;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModuleImpl;
import com.lunarclient.apollo.module.serverrule.ServerRuleModule;
import com.lunarclient.apollo.module.staffmod.StaffModModule;
import com.lunarclient.apollo.module.staffmod.StaffModModuleImpl;
@@ -152,6 +154,7 @@ public void onEnable() {
.addModule(PacketEnrichmentModule.class, new PacketEnrichmentImpl())
.addModule(PayNowModule.class, new PayNowModuleImpl())
.addModule(RichPresenceModule.class, new RichPresenceModuleImpl())
+ .addModule(ServerLinkModule.class, new ServerLinkModuleImpl())
.addModule(SaturationModule.class)
.addModule(ServerRuleModule.class)
.addModule(StaffModModule.class, new StaffModModuleImpl())
diff --git a/platform/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java b/platform/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java
index 24d75c18c..7e958dc05 100644
--- a/platform/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java
+++ b/platform/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java
@@ -58,6 +58,8 @@
import com.lunarclient.apollo.module.paynow.PayNowModuleImpl;
import com.lunarclient.apollo.module.richpresence.RichPresenceModule;
import com.lunarclient.apollo.module.richpresence.RichPresenceModuleImpl;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModule;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModuleImpl;
import com.lunarclient.apollo.module.serverrule.ServerRuleModule;
import com.lunarclient.apollo.module.staffmod.StaffModModule;
import com.lunarclient.apollo.module.staffmod.StaffModModuleImpl;
@@ -131,6 +133,7 @@ public void onEnable() {
.addModule(NotificationModule.class, new NotificationModuleImpl())
.addModule(PayNowModule.class, new PayNowModuleImpl())
.addModule(RichPresenceModule.class, new RichPresenceModuleImpl())
+ .addModule(ServerLinkModule.class, new ServerLinkModuleImpl())
.addModule(ServerRuleModule.class)
.addModule(StaffModModule.class, new StaffModModuleImpl())
.addModule(StopwatchModule.class, new StopwatchModuleImpl())
diff --git a/platform/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java b/platform/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java
index dca57648c..e28530da7 100644
--- a/platform/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java
+++ b/platform/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java
@@ -64,6 +64,8 @@
import com.lunarclient.apollo.module.paynow.PayNowModuleImpl;
import com.lunarclient.apollo.module.richpresence.RichPresenceModule;
import com.lunarclient.apollo.module.richpresence.RichPresenceModuleImpl;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModule;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModuleImpl;
import com.lunarclient.apollo.module.serverrule.ServerRuleModule;
import com.lunarclient.apollo.module.staffmod.StaffModModule;
import com.lunarclient.apollo.module.staffmod.StaffModModuleImpl;
@@ -140,6 +142,7 @@ public void onEnable() {
.addModule(PacketEnrichmentModule.class, new PacketEnrichmentImpl())
.addModule(PayNowModule.class, new PayNowModuleImpl())
.addModule(RichPresenceModule.class, new RichPresenceModuleImpl())
+ .addModule(ServerLinkModule.class, new ServerLinkModuleImpl())
.addModule(ServerRuleModule.class)
.addModule(StaffModModule.class, new StaffModModuleImpl())
.addModule(StopwatchModule.class, new StopwatchModuleImpl())
diff --git a/platform/minestom/src/main/java/com/lunarclient/apollo/ApolloMinestomPlatform.java b/platform/minestom/src/main/java/com/lunarclient/apollo/ApolloMinestomPlatform.java
index fd1357a8d..559d4e5c4 100644
--- a/platform/minestom/src/main/java/com/lunarclient/apollo/ApolloMinestomPlatform.java
+++ b/platform/minestom/src/main/java/com/lunarclient/apollo/ApolloMinestomPlatform.java
@@ -67,6 +67,8 @@
import com.lunarclient.apollo.module.richpresence.RichPresenceModule;
import com.lunarclient.apollo.module.richpresence.RichPresenceModuleImpl;
import com.lunarclient.apollo.module.saturation.SaturationModule;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModule;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModuleImpl;
import com.lunarclient.apollo.module.serverrule.ServerRuleModule;
import com.lunarclient.apollo.module.staffmod.StaffModModule;
import com.lunarclient.apollo.module.staffmod.StaffModModuleImpl;
@@ -174,6 +176,7 @@ public static void init(ApolloMinestomProperties properties) {
.addModule(PacketEnrichmentModule.class, new PacketEnrichmentImpl())
.addModule(PayNowModule.class, new PayNowModuleImpl())
.addModule(RichPresenceModule.class, new RichPresenceModuleImpl())
+ .addModule(ServerLinkModule.class, new ServerLinkModuleImpl())
.addModule(SaturationModule.class)
.addModule(ServerRuleModule.class)
.addModule(StaffModModule.class, new StaffModModuleImpl())
diff --git a/platform/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java b/platform/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java
index e975fae09..1b8501ed5 100644
--- a/platform/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java
+++ b/platform/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java
@@ -58,6 +58,8 @@
import com.lunarclient.apollo.module.paynow.PayNowModuleImpl;
import com.lunarclient.apollo.module.richpresence.RichPresenceModule;
import com.lunarclient.apollo.module.richpresence.RichPresenceModuleImpl;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModule;
+import com.lunarclient.apollo.module.serverlink.ServerLinkModuleImpl;
import com.lunarclient.apollo.module.serverrule.ServerRuleModule;
import com.lunarclient.apollo.module.staffmod.StaffModModule;
import com.lunarclient.apollo.module.staffmod.StaffModModuleImpl;
@@ -196,6 +198,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) {
.addModule(NotificationModule.class, new NotificationModuleImpl())
.addModule(PayNowModule.class, new PayNowModuleImpl())
.addModule(RichPresenceModule.class, new RichPresenceModuleImpl())
+ .addModule(ServerLinkModule.class, new ServerLinkModuleImpl())
.addModule(ServerRuleModule.class)
.addModule(StaffModModule.class, new StaffModModuleImpl())
.addModule(StopwatchModule.class, new StopwatchModuleImpl())