diff --git a/.checkstyle/suppressions.xml b/.checkstyle/suppressions.xml index 530d6e64c..12f09771b 100644 --- a/.checkstyle/suppressions.xml +++ b/.checkstyle/suppressions.xml @@ -2,7 +2,7 @@ - + diff --git a/.github/README.md b/.github/README.md index 86531795a..d5e3fa06f 100644 --- a/.github/README.md +++ b/.github/README.md @@ -24,12 +24,12 @@ Our **Lightweight integration** allows you to use Apollo features **without runn This is useful for developers who want Apollo functionality but prefer a more minimal approach. There are two supported methods: -- [Lightweight JSON](https://lunarclient.dev/apollo/developers/lightweight/json/getting-started) -- [Lightweight Protobuf](https://lunarclient.dev/apollo/developers/lightweight/protobuf/getting-started) +- [Lightweight JSON](https://lunarclient.dev/apollo/developers/lightweight/json) +- [Lightweight Protobuf](https://lunarclient.dev/apollo/developers/lightweight/protobuf) Both approaches achieve the same goal, but with different trade-offs in terms of **complexity and flexibility**. -Read the [Lightweight introduction](https://lunarclient.dev/apollo/developers/lightweight/introduction) documentation to get started. +Read the [Lightweight introduction](https://lunarclient.dev/apollo/developers/lightweight) documentation to get started. ## Examples diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d9e8de162..4c3e26d2e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,7 +24,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: "zulu" - java-version: 8 + java-version: 21 - name: Gradle Build run: ./gradlew build diff --git a/api/src/main/java/com/lunarclient/apollo/common/icon/ItemStackIcon.java b/api/src/main/java/com/lunarclient/apollo/common/icon/ItemStackIcon.java index 97ca66ab5..2df8cb7fe 100644 --- a/api/src/main/java/com/lunarclient/apollo/common/icon/ItemStackIcon.java +++ b/api/src/main/java/com/lunarclient/apollo/common/icon/ItemStackIcon.java @@ -23,8 +23,10 @@ */ package com.lunarclient.apollo.common.icon; +import com.lunarclient.apollo.common.profile.Profile; import lombok.Builder; import lombok.Getter; +import org.jetbrains.annotations.Nullable; /** * Represents an item stack icon. @@ -59,4 +61,12 @@ public final class ItemStackIcon extends Icon { */ int customModelData; + /** + * Returns the icon {@link Profile}. + * + * @return the icon profile + * @since 1.2.6 + */ + @Nullable Profile profile; + } diff --git a/api/src/main/java/com/lunarclient/apollo/common/location/HudPosition.java b/api/src/main/java/com/lunarclient/apollo/common/location/HudPosition.java new file mode 100644 index 000000000..c6f7f4fad --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/common/location/HudPosition.java @@ -0,0 +1,54 @@ +/* + * 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.location; + +import lombok.Builder; +import lombok.Getter; + +/** + * Represents a HUD element position on the client screen. + * + * @since 1.2.6 + */ +@Getter +@Builder +public final class HudPosition { + + /** + * Returns the {@code float} X coordinate for this HUD position. + * + * @return the x coordinate + * @since 1.2.6 + */ + float x; + + /** + * Returns the {@code float} Y coordinate for this HUD position. + * + * @return the y coordinate + * @since 1.2.6 + */ + float y; + +} diff --git a/api/src/main/java/com/lunarclient/apollo/common/profile/Profile.java b/api/src/main/java/com/lunarclient/apollo/common/profile/Profile.java new file mode 100644 index 000000000..2420a46b7 --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/common/profile/Profile.java @@ -0,0 +1,64 @@ +/* + * 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.profile; + +import java.util.UUID; +import lombok.Builder; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a profile attached to the {@link com.lunarclient.apollo.common.icon.ItemStackIcon}. + * + * @since 1.2.6 + */ +@Getter +@Builder +public final class Profile { + + /** + * Returns the profile {@link UUID} id. + * + * @return the profile id + * @since 1.2.6 + */ + @Nullable UUID id; + + /** + * Returns the profile {@link String} texture. + * + * @return the profile texture + * @since 1.2.6 + */ + String texture; + + /** + * Returns the profile {@link String} signature. + * + * @return the profile signature + * @since 1.2.6 + */ + String signature; + +} diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModArmorstatus.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModArmorstatus.java index cf54d8b4d..eaea46708 100644 --- a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModArmorstatus.java +++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModArmorstatus.java @@ -76,7 +76,7 @@ public final class ModArmorstatus { */ public static final SimpleOption HIDE_UNBREAKABLE_DURABILITY = SimpleOption.builder() .node("armorstatus", "hide-unbreakable-durability").type(TypeToken.get(Boolean.class)) - .defaultValue(false) + .defaultValue(true) .notifyClient() .build(); diff --git a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModParticleChanger.java b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModParticleChanger.java index dc998ce9e..06cd6a0da 100644 --- a/api/src/main/java/com/lunarclient/apollo/mods/impl/ModParticleChanger.java +++ b/api/src/main/java/com/lunarclient/apollo/mods/impl/ModParticleChanger.java @@ -49,56 +49,70 @@ public final class ModParticleChanger { /** * No documentation available. * - * @since 1.2.2 + * @since 1.0.0 */ - public static final SimpleOption SHOW_BLOOD_PARTICLES = SimpleOption.builder() - .node("particle-changer", "show-blood-particles").type(TypeToken.get(Boolean.class)) + public static final SimpleOption ALWAYS_ENCHANT_STRIKES = SimpleOption.builder() + .node("particle-changer", "always-enchant-strikes").type(TypeToken.get(Boolean.class)) .defaultValue(false) .notifyClient() .build(); /** - * No documentation available. + * Hide particles which are emitted in first person (e.g. potion effects). * - * @since 1.2.2 + * @since 1.0.0 */ - public static final NumberOption BLOOD_MULTIPLIER = NumberOption.number() - .node("particle-changer", "blood-multiplier").type(TypeToken.get(Integer.class)) - .min(1).max(10) - .defaultValue(1) + public static final SimpleOption HIDE_FIRST_PERSON_PARTICLES = SimpleOption.builder() + .comment("Hide particles which are emitted in first person (e.g. potion effects)") + .node("particle-changer", "hide-first-person-particles").type(TypeToken.get(Boolean.class)) + .defaultValue(false) .notifyClient() .build(); /** * No documentation available. * + * @since 1.2.6 + */ + public static final SimpleOption HIDE_BLOCK_BREAK_PARTICLES = SimpleOption.builder() + .node("particle-changer", "hide-block-break-particles").type(TypeToken.get(Boolean.class)) + .defaultValue(false) + .notifyClient() + .build(); + + /** + * Simple toggle for clean game rendering. This may cause issues with certain server mechanics being more challenging to see!. + * * @since 1.2.2 */ - public static final SimpleOption PLAYER_BLOOD_PARTICLES = SimpleOption.builder() - .node("particle-changer", "player-blood-particles").type(TypeToken.get(Boolean.class)) - .defaultValue(true) + public static final SimpleOption HIDE_ALL_PARTICLES = SimpleOption.builder() + .comment("Simple toggle for clean game rendering. This may cause issues with certain server mechanics being more challenging to see!") + .node("particle-changer", "hide-all-particles").type(TypeToken.get(Boolean.class)) + .defaultValue(false) .notifyClient() .build(); /** * No documentation available. * - * @since 1.2.2 + * @since 1.0.0 */ - public static final SimpleOption ENTITY_BLOOD_PARTICLES = SimpleOption.builder() - .node("particle-changer", "entity-blood-particles").type(TypeToken.get(Boolean.class)) - .defaultValue(true) + public static final NumberOption SCALE = NumberOption.number() + .node("particle-changer", "scale").type(TypeToken.get(Float.class)) + .min(0.25F).max(2.0F) + .defaultValue(1.0F) .notifyClient() .build(); /** * No documentation available. * - * @since 1.2.2 + * @since 1.0.0 */ - public static final SimpleOption PLAY_BLOOD_SOUND = SimpleOption.builder() - .node("particle-changer", "play-blood-sound").type(TypeToken.get(Boolean.class)) - .defaultValue(false) + public static final NumberOption PARTICLE_MULTIPLIER = NumberOption.number() + .node("particle-changer", "particle-multiplier").type(TypeToken.get(Float.class)) + .min(0.25F).max(10.0F) + .defaultValue(1.0F) .notifyClient() .build(); @@ -107,8 +121,8 @@ public final class ModParticleChanger { * * @since 1.0.0 */ - public static final SimpleOption ALWAYS_ENCHANT_STRIKES = SimpleOption.builder() - .node("particle-changer", "always-enchant-strikes").type(TypeToken.get(Boolean.class)) + public static final SimpleOption HIDE_PARTICLE = SimpleOption.builder() + .node("particle-changer", "hide-particle").type(TypeToken.get(Boolean.class)) .defaultValue(false) .notifyClient() .build(); @@ -118,32 +132,31 @@ public final class ModParticleChanger { * * @since 1.0.0 */ - public static final SimpleOption AFFECT_ENCHANTED_WEAPONS = SimpleOption.builder() - .node("particle-changer", "affect-enchanted-weapons").type(TypeToken.get(Boolean.class)) - .defaultValue(true) + public static final SimpleOption OVERLAY_COLOR = SimpleOption.builder() + .node("particle-changer", "overlay-color").type(TypeToken.get(Boolean.class)) + .defaultValue(false) .notifyClient() .build(); /** - * Hide your players potion effect particles when you're in first person. + * No documentation available. * * @since 1.0.0 */ - public static final SimpleOption HIDE_FIRST_PERSON_PARTICLES = SimpleOption.builder() - .comment("Hide your players potion effect particles when you're in first person") - .node("particle-changer", "hide-first-person-particles").type(TypeToken.get(Boolean.class)) - .defaultValue(false) + public static final SimpleOption COLOR = SimpleOption.builder() + .node("particle-changer", "color").type(TypeToken.get(Color.class)) + .defaultValue(new Color(255, 255, 255)) .notifyClient() .build(); /** - * Simple toggle for clean game rendering. This may cause issues with certain server mechanics being more challenging to see!. + * No documentation available. * * @since 1.2.2 */ - public static final SimpleOption HIDE_ALL_PARTICLES = SimpleOption.builder() - .comment("Simple toggle for clean game rendering. This may cause issues with certain server mechanics being more challenging to see!") - .node("particle-changer", "hide-all-particles").type(TypeToken.get(Boolean.class)) + @Deprecated + public static final SimpleOption SHOW_BLOOD_PARTICLES = SimpleOption.builder() + .node("particle-changer", "show-blood-particles").type(TypeToken.get(Boolean.class)) .defaultValue(false) .notifyClient() .build(); @@ -151,45 +164,48 @@ public final class ModParticleChanger { /** * No documentation available. * - * @since 1.0.0 + * @since 1.2.2 */ - public static final SimpleOption COLOR = SimpleOption.builder() - .node("particle-changer", "color").type(TypeToken.get(Color.class)) - .defaultValue(new Color(255, 255, 255)) + @Deprecated + public static final NumberOption BLOOD_MULTIPLIER = NumberOption.number() + .node("particle-changer", "blood-multiplier").type(TypeToken.get(Integer.class)) + .min(1).max(10) + .defaultValue(1) .notifyClient() .build(); /** * No documentation available. * - * @since 1.0.0 + * @since 1.2.2 */ - public static final NumberOption SCALE = NumberOption.number() - .node("particle-changer", "scale").type(TypeToken.get(Float.class)) - .min(0.5F).max(2.0F) - .defaultValue(1.0F) + @Deprecated + public static final SimpleOption PLAYER_BLOOD_PARTICLES = SimpleOption.builder() + .node("particle-changer", "player-blood-particles").type(TypeToken.get(Boolean.class)) + .defaultValue(true) .notifyClient() .build(); /** * No documentation available. * - * @since 1.0.0 + * @since 1.2.2 */ - public static final NumberOption PARTICLE_MULTIPLIER = NumberOption.number() - .node("particle-changer", "particle-multiplier").type(TypeToken.get(Integer.class)) - .min(1).max(10) - .defaultValue(1) + @Deprecated + public static final SimpleOption ENTITY_BLOOD_PARTICLES = SimpleOption.builder() + .node("particle-changer", "entity-blood-particles").type(TypeToken.get(Boolean.class)) + .defaultValue(true) .notifyClient() .build(); /** * No documentation available. * - * @since 1.0.0 + * @since 1.2.2 */ - public static final SimpleOption HIDE_PARTICLE = SimpleOption.builder() - .node("particle-changer", "hide-particle").type(TypeToken.get(Boolean.class)) + @Deprecated + public static final SimpleOption PLAY_BLOOD_SOUND = SimpleOption.builder() + .node("particle-changer", "play-blood-sound").type(TypeToken.get(Boolean.class)) .defaultValue(false) .notifyClient() .build(); @@ -199,9 +215,10 @@ public final class ModParticleChanger { * * @since 1.0.0 */ - public static final SimpleOption OVERLAY_COLOR = SimpleOption.builder() - .node("particle-changer", "overlay-color").type(TypeToken.get(Boolean.class)) - .defaultValue(false) + @Deprecated + public static final SimpleOption AFFECT_ENCHANTED_WEAPONS = SimpleOption.builder() + .node("particle-changer", "affect-enchanted-weapons").type(TypeToken.get(Boolean.class)) + .defaultValue(true) .notifyClient() .build(); diff --git a/api/src/main/java/com/lunarclient/apollo/module/cooldown/CooldownStyle.java b/api/src/main/java/com/lunarclient/apollo/module/cooldown/CooldownStyle.java index 2641e5a3b..9886e19ff 100644 --- a/api/src/main/java/com/lunarclient/apollo/module/cooldown/CooldownStyle.java +++ b/api/src/main/java/com/lunarclient/apollo/module/cooldown/CooldownStyle.java @@ -29,7 +29,7 @@ import org.jetbrains.annotations.Nullable; /** - * Represents the {@link Cooldown} style, allowing customization of the circle start, end, edge & text color. + * Represents the {@link Cooldown} style, allowing customization of the circle start, end, edge and text color. * * @since 1.2.5 */ diff --git a/api/src/main/java/com/lunarclient/apollo/module/cosmetic/Cosmetic.java b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/Cosmetic.java new file mode 100644 index 000000000..36e2cf121 --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/Cosmetic.java @@ -0,0 +1,66 @@ +/* + * 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.cosmetic; + +import com.lunarclient.apollo.module.cosmetic.options.BodyOptions; +import com.lunarclient.apollo.module.cosmetic.options.CloakOptions; +import com.lunarclient.apollo.module.cosmetic.options.CosmeticOptions; +import com.lunarclient.apollo.module.cosmetic.options.HatOptions; +import com.lunarclient.apollo.module.cosmetic.options.PetOptions; +import lombok.Builder; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +/** + * Represents a single cosmetic with optional per-type display settings. + * + * @since 1.2.6 + */ +@Getter +@Builder +public final class Cosmetic { + + /** + * Returns the Lunar Client cosmetic id for this entry. + * + *

The value must be greater than 0.

+ * + * @return the cosmetic id + * @since 1.2.6 + */ + @Range(from = 1, to = Integer.MAX_VALUE) int id; + + /** + * Returns optional cosmetic display options for this cosmetic id. + * + *

Expected concrete types are {@link HatOptions}, {@link CloakOptions}, {@link PetOptions}, or + * {@link BodyOptions}.

+ * + * @return cosmetic options, or {@code null} + * @since 1.2.6 + */ + @Nullable CosmeticOptions options; + +} diff --git a/api/src/main/java/com/lunarclient/apollo/module/cosmetic/CosmeticModule.java b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/CosmeticModule.java new file mode 100644 index 000000000..c7a77f337 --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/CosmeticModule.java @@ -0,0 +1,121 @@ +/* + * 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.cosmetic; + +import com.lunarclient.apollo.common.location.ApolloBlockLocation; +import com.lunarclient.apollo.module.ApolloModule; +import com.lunarclient.apollo.module.ModuleDefinition; +import com.lunarclient.apollo.module.cosmetic.options.BodyOptions; +import com.lunarclient.apollo.module.cosmetic.options.CloakOptions; +import com.lunarclient.apollo.module.cosmetic.options.CosmeticOptions; +import com.lunarclient.apollo.module.cosmetic.options.HatOptions; +import com.lunarclient.apollo.module.cosmetic.options.PetOptions; +import com.lunarclient.apollo.recipients.Recipients; +import java.util.List; +import java.util.UUID; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +/** + * Represents the cosmetic module. + * + * @since 1.2.6 + */ +@ApiStatus.NonExtendable +@ModuleDefinition(id = "cosmetic", name = "Cosmetic") +public abstract class CosmeticModule extends ApolloModule { + + /** + * Equips the provided cosmetics on an NPC for the given {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param npcUuid the {@link UUID} of the NPC to equip the cosmetics on + * @param cosmetics the cosmetics to equip, including optional {@link CosmeticOptions} per entry + * ({@link HatOptions}, {@link CloakOptions}, {@link PetOptions}, or {@link BodyOptions}) + * @since 1.2.6 + */ + public abstract void equipNpcCosmetics(Recipients recipients, UUID npcUuid, List cosmetics); + + /** + * Unequips the provided cosmetics from an NPC for the given {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param npcUuid the {@link UUID} of the NPC to unequip the cosmetics from + * @param cosmeticIds the list of cosmetic ids to unequip + * @since 1.2.6 + */ + public abstract void unequipNpcCosmetics(Recipients recipients, UUID npcUuid, List cosmeticIds); + + /** + * Resets all cosmetics on an NPC for the given {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param npcUuid the {@link UUID} of the NPC to reset the cosmetics on + * @since 1.2.6 + */ + public abstract void resetNpcCosmetics(Recipients recipients, UUID npcUuid); + + /** + * Displays a spray for the given {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param spray the spray to display (durations under one second are raised to one second before sending) + * @since 1.2.6 + */ + public abstract void displaySpray(Recipients recipients, Spray spray); + + /** + * Removes every instance of a spray id for the given {@link Recipients}. + * + *

The spray id must be greater than 0.

+ * + * @param recipients the recipients that are receiving the packet + * @param sprayId the spray cosmetic id + * @since 1.2.6 + */ + public abstract void removeSpray(Recipients recipients, @Range(from = 1, to = Integer.MAX_VALUE) int sprayId); + + /** + * Removes every instance of a spray id at a specific block for the given {@link Recipients}. + * + *

The spray id must be greater than 0. If {@code location} is {@code null}, every + * instance of the spray id is removed regardless of position.

+ * + * @param recipients the recipients that are receiving the packet + * @param sprayId the spray cosmetic id + * @param location the block location of the spray to remove, or {@code null} to remove all + * @since 1.2.6 + */ + public abstract void removeSpray(Recipients recipients, @Range(from = 1, to = Integer.MAX_VALUE) int sprayId, @Nullable ApolloBlockLocation location); + + /** + * Resets all server sprays for the given {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @since 1.2.6 + */ + public abstract void resetSprays(Recipients recipients); + +} diff --git a/api/src/main/java/com/lunarclient/apollo/module/cosmetic/Spray.java b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/Spray.java new file mode 100644 index 000000000..4366cc769 --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/Spray.java @@ -0,0 +1,90 @@ +/* + * 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.cosmetic; + +import com.lunarclient.apollo.common.location.ApolloBlockLocation; +import com.lunarclient.apollo.module.packetenrichment.raytrace.Direction; +import java.time.Duration; +import lombok.Builder; +import lombok.Getter; +import org.jetbrains.annotations.Range; + +/** + * Represents a spray. + * + *

Sprays are client-local and validated against loaded chunks; + * they're removed when their backing chunk unloads and won't reappear + * unless the server resends the spray packet.

+ * + * @since 1.2.6 + */ +@Getter +@Builder +public final class Spray { + + /** + * Returns the Lunar Client spray cosmetic id. + * + *

The value must be greater than 0.

+ * + * @return the spray cosmetic id + * @since 1.2.6 + */ + @Range(from = 1, to = Integer.MAX_VALUE) int sprayId; + + /** + * Returns the {@link ApolloBlockLocation} of the block the spray is placed on. + * + * @return the block location + * @since 1.2.6 + */ + ApolloBlockLocation location; + + /** + * Returns the {@link Direction} indicating which side the spray faces. + * + * @return the facing direction + * @since 1.2.6 + */ + Direction facing; + + /** + * Returns the spray rotation in degrees on the client. + * + * @return the rotation in degrees + * @since 1.2.6 + */ + @Builder.Default + float rotation = 0f; + + /** + * Returns the {@link Duration} for how long the spray remains visible on the client. + * + * @return the display duration + * @since 1.2.6 + */ + @Builder.Default + Duration duration = Duration.ofSeconds(30); + +} diff --git a/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/BodyOptions.java b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/BodyOptions.java new file mode 100644 index 000000000..c3472923d --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/BodyOptions.java @@ -0,0 +1,66 @@ +/* + * 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.cosmetic.options; + +import com.lunarclient.apollo.module.cosmetic.Cosmetic; +import lombok.Builder; +import lombok.Getter; + +/** + * Represents body cosmetic display settings for use with {@link Cosmetic}. + * + * @since 1.2.6 + */ +@Getter +@Builder +public final class BodyOptions extends CosmeticOptions { + + /** + * Returns whether the body cosmetic should render on top of an equipped chestplate when present. + * + * @return {@code true} to draw over the chestplate, {@code false} otherwise + * @since 1.2.6 + */ + @Builder.Default + boolean showOverChestplate = true; + + /** + * Returns whether the body cosmetic should render on top of equipped leggings when present. + * + * @return {@code true} to draw over the leggings, {@code false} otherwise + * @since 1.2.6 + */ + @Builder.Default + boolean showOverLeggings = true; + + /** + * Returns whether the body cosmetic should render on top of equipped boots when present. + * + * @return {@code true} to draw over the boots, {@code false} otherwise + * @since 1.2.6 + */ + @Builder.Default + boolean showOverBoots = true; + +} diff --git a/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/CloakOptions.java b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/CloakOptions.java new file mode 100644 index 000000000..888b07db7 --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/CloakOptions.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.module.cosmetic.options; + +import com.lunarclient.apollo.module.cosmetic.Cosmetic; +import lombok.Builder; +import lombok.Getter; + +/** + * Represents cloak cosmetic display settings for use with {@link Cosmetic}. + * + * @since 1.2.6 + */ +@Getter +@Builder +public final class CloakOptions extends CosmeticOptions { + + /** + * Returns whether the cloak should use cloth-style physics on the client. + * + * @return {@code true} to enable cloth physics, {@code false} otherwise + * @since 1.2.6 + */ + @Builder.Default + boolean useClothPhysics = false; + +} diff --git a/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/CosmeticOptions.java b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/CosmeticOptions.java new file mode 100644 index 000000000..b80f2029e --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/CosmeticOptions.java @@ -0,0 +1,37 @@ +/* + * 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.cosmetic.options; + +import com.lunarclient.apollo.module.cosmetic.Cosmetic; + +/** + * The abstract base class for cosmetic display options used with {@link Cosmetic}. + * + *

Concrete types are {@link HatOptions}, {@link CloakOptions}, {@link PetOptions}, and {@link BodyOptions}.

+ * + * @since 1.2.6 + */ +public abstract class CosmeticOptions { + +} diff --git a/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/HatOptions.java b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/HatOptions.java new file mode 100644 index 000000000..4f79b9260 --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/HatOptions.java @@ -0,0 +1,66 @@ +/* + * 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.cosmetic.options; + +import com.lunarclient.apollo.module.cosmetic.Cosmetic; +import lombok.Builder; +import lombok.Getter; + +/** + * Represents hat cosmetic display settings for use with {@link Cosmetic}. + * + * @since 1.2.6 + */ +@Getter +@Builder +public final class HatOptions extends CosmeticOptions { + + /** + * Returns whether the hat should render on top of an equipped helmet when present. + * + * @return {@code true} to draw over the helmet, {@code false} otherwise + * @since 1.2.6 + */ + @Builder.Default + boolean showOverHelmet = true; + + /** + * Returns whether the hat should render on top of the player's outer skin layer when present. + * + * @return {@code true} to draw over the skin layer, {@code false} otherwise + * @since 1.2.6 + */ + @Builder.Default + boolean showOverSkinLayer = true; + + /** + * Returns the vertical height offset applied to this hat on the client, in world units. + * + * @return the height offset + * @since 1.2.6 + */ + @Builder.Default + float heightOffset = 0f; + +} diff --git a/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/PetOptions.java b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/PetOptions.java new file mode 100644 index 000000000..314eb3913 --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/cosmetic/options/PetOptions.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.module.cosmetic.options; + +import com.lunarclient.apollo.module.cosmetic.Cosmetic; +import lombok.Builder; +import lombok.Getter; + +/** + * Represents shoulder-pet cosmetic display settings for use with {@link Cosmetic}. + * + * @since 1.2.6 + */ +@Getter +@Builder +public final class PetOptions extends CosmeticOptions { + + /** + * Returns whether the shoulder pet should appear on the opposite shoulder from the default side. + * + * @return {@code true} to mirror the pet to the other shoulder, {@code false} for default placement + * @since 1.2.6 + */ + @Builder.Default + boolean flipShoulder = false; + +} diff --git a/api/src/main/java/com/lunarclient/apollo/module/stopwatch/Stopwatch.java b/api/src/main/java/com/lunarclient/apollo/module/stopwatch/Stopwatch.java new file mode 100644 index 000000000..3afd1df2c --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/stopwatch/Stopwatch.java @@ -0,0 +1,119 @@ +/* + * 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.stopwatch; + +import com.lunarclient.apollo.common.location.HudPosition; +import java.awt.Color; +import lombok.Builder; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a stopwatch which can be displayed on the client HUD. + * + * @since 1.2.6 + */ +@Getter +@Builder +public final class Stopwatch { + + /** + * Returns the stopwatch {@link String} id. + * + * @return the stopwatch id + * @since 1.2.6 + */ + String id; + + /** + * Returns the stopwatch {@link String} name. + * + * @return the stopwatch name + * @since 1.2.6 + */ + String name; + + /** + * Returns the stopwatch {@code boolean} reset on start. + * + *

If {@code true}, elapsed time is reset on each start.

+ * + * @return whether to reset on start + * @since 1.2.6 + */ + boolean resetOnStart; + + /** + * Returns the stopwatch {@code boolean} prevent modification. + * + *

If {@code true}, the user cannot modify the options for this + * stopwatch on the client side.

+ * + * @return whether modification is prevented + * @since 1.2.6 + */ + boolean preventModification; + + /** + * Returns the stopwatch {@code boolean} hide when stopped. + * + *

If {@code true}, the stopwatch is hidden from the HUD when stopped.

+ * + * @return whether to hide when stopped + * @since 1.2.6 + */ + boolean hideWhenStopped; + + /** + * Returns the stopwatch {@link String} display format. + * + *

A format string (e.g. {@code "mm:ss"}), or {@code null} + * for the default display format.

+ * + * @return the display format string + * @since 1.2.6 + */ + @Nullable String displayFormat; + + /** + * Returns the stopwatch {@link Color} text color. + * + *

If {@code null}, the default color (white) is used.

+ * + * @return the text color + * @since 1.2.6 + */ + @Nullable Color textColor; + + /** + * Returns the stopwatch {@link HudPosition} HUD position. + * + *

If {@code null}, the stopwatch is auto-stacked at the default position.

+ * + * @return the HUD position + * @since 1.2.6 + */ + @Nullable HudPosition hudPosition; + +} diff --git a/api/src/main/java/com/lunarclient/apollo/module/stopwatch/StopwatchModule.java b/api/src/main/java/com/lunarclient/apollo/module/stopwatch/StopwatchModule.java index bec983169..f39af5792 100644 --- a/api/src/main/java/com/lunarclient/apollo/module/stopwatch/StopwatchModule.java +++ b/api/src/main/java/com/lunarclient/apollo/module/stopwatch/StopwatchModule.java @@ -45,28 +45,141 @@ public Collection getSupportedPlatforms() { return Arrays.asList(ApolloPlatform.Kind.SERVER, ApolloPlatform.Kind.PROXY); } + /** + * Adds a {@link Stopwatch} to the HUD for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param stopwatch the stopwatch to add + * @since 1.2.6 + */ + public abstract void addStopwatch(Recipients recipients, Stopwatch stopwatch); + + /** + * Removes the {@link Stopwatch} with the given id for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param id the stopwatch id + * @since 1.2.6 + */ + public abstract void removeStopwatch(Recipients recipients, String id); + + /** + * Starts the {@link Stopwatch} with the given id for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param id the stopwatch id + * @since 1.2.6 + */ + public abstract void startStopwatch(Recipients recipients, String id); + + /** + * Stops the {@link Stopwatch} with the given id for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param id the stopwatch id + * @since 1.2.6 + */ + public abstract void stopStopwatch(Recipients recipients, String id); + + /** + * Resets the {@link Stopwatch} with the given id for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param id the stopwatch id + * @since 1.2.6 + */ + public abstract void resetStopwatch(Recipients recipients, String id); + + /** + * Resets all {@link Stopwatch}es for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @since 1.2.6 + */ + public abstract void resetStopwatches(Recipients recipients); + + /** + * Adds a {@link Timer} to the HUD for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param timer the timer to add + * @since 1.2.6 + */ + public abstract void addTimer(Recipients recipients, Timer timer); + + /** + * Removes the {@link Timer} with the given id for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param id the timer id + * @since 1.2.6 + */ + public abstract void removeTimer(Recipients recipients, String id); + + /** + * Starts the {@link Timer} with the given id for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param id the timer id + * @since 1.2.6 + */ + public abstract void startTimer(Recipients recipients, String id); + + /** + * Stops the {@link Timer} with the given id for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param id the timer id + * @since 1.2.6 + */ + public abstract void stopTimer(Recipients recipients, String id); + + /** + * Resets the {@link Timer} with the given id for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @param id the timer id + * @since 1.2.6 + */ + public abstract void resetTimer(Recipients recipients, String id); + + /** + * Resets all {@link Timer}s for the {@link Recipients}. + * + * @param recipients the recipients that are receiving the packet + * @since 1.2.6 + */ + public abstract void resetTimers(Recipients recipients); + /** * Starts the stopwatch for the {@link Recipients}. * * @param recipients the recipients that are receiving the packet + * @deprecated for removal since 1.2.6, use {@link #addStopwatch(Recipients, Stopwatch)} + * and {@link #startStopwatch(Recipients, String)} instead. * @since 1.0.0 */ + @Deprecated public abstract void startStopwatch(Recipients recipients); /** * Stops the stopwatch for the {@link Recipients}. * * @param recipients the recipients that are receiving the packet + * @deprecated for removal since 1.2.6, use {@link #stopStopwatch(Recipients, String)} instead. * @since 1.0.0 */ + @Deprecated public abstract void stopStopwatch(Recipients recipients); /** * Resets the stopwatch for the {@link Recipients}. * * @param recipients the recipients that are receiving the packet + * @deprecated for removal since 1.2.6, use {@link #resetStopwatch(Recipients, String)} instead. * @since 1.0.0 */ + @Deprecated public abstract void resetStopwatch(Recipients recipients); } diff --git a/api/src/main/java/com/lunarclient/apollo/module/stopwatch/Timer.java b/api/src/main/java/com/lunarclient/apollo/module/stopwatch/Timer.java new file mode 100644 index 000000000..99d71861a --- /dev/null +++ b/api/src/main/java/com/lunarclient/apollo/module/stopwatch/Timer.java @@ -0,0 +1,150 @@ +/* + * 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.stopwatch; + +import com.lunarclient.apollo.common.location.HudPosition; +import java.awt.Color; +import java.time.Duration; +import lombok.Builder; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a timer which can be displayed on the client HUD. + * + * @since 1.2.6 + */ +@Getter +@Builder +public final class Timer { + + /** + * Returns the timer {@link String} id. + * + * @return the timer id + * @since 1.2.6 + */ + String id; + + /** + * Returns the timer {@link String} name. + * + * @return the timer name + * @since 1.2.6 + */ + String name; + + /** + * Returns the timer {@link Duration}. + * + * @return the timer duration + * @since 1.2.6 + */ + Duration duration; + + /** + * Returns the timer {@code boolean} loop. + * + *

If {@code true}, the timer restarts automatically when finished.

+ * + * @return whether the timer loops + * @since 1.2.6 + */ + boolean loop; + + /** + * Returns the timer {@code boolean} prevent modification. + * + *

If {@code true}, the user cannot modify the options for this + * timer on the client side.

+ * + * @return whether modification is prevented + * @since 1.2.6 + */ + boolean preventModification; + + /** + * Returns the timer {@code boolean} hide when stopped. + * + *

If {@code true}, the timer is hidden from the HUD when stopped.

+ * + * @return whether to hide when stopped + * @since 1.2.6 + */ + boolean hideWhenStopped; + + /** + * Returns the timer {@link String} display format. + * + *

A format string (e.g. {@code "mm:ss"}), or {@code null} + * for the default display format.

+ * + * @return the display format string + * @since 1.2.6 + */ + @Nullable String displayFormat; + + /** + * Returns the timer {@link Component} title text. + * + *

The on-screen title shown when the timer finishes, + * or {@code null} to skip.

+ * + * @return the title text component + * @since 1.2.6 + */ + @Nullable Component titleText; + + /** + * Returns the timer {@code boolean} in-game notification. + * + *

If {@code true}, an in-game popup is shown when the timer finishes.

+ * + * @return whether an in-game notification is shown + * @since 1.2.6 + */ + boolean inGameNotification; + + /** + * Returns the timer {@link Color} text color. + * + *

If {@code null}, the default color (white) is used.

+ * + * @return the text color + * @since 1.2.6 + */ + @Nullable Color textColor; + + /** + * Returns the timer {@link HudPosition} HUD position. + * + *

If {@code null}, the timer is auto-stacked at the default position.

+ * + * @return the HUD position + * @since 1.2.6 + */ + @Nullable HudPosition hudPosition; + +} diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 819e383a9..cba24b36f 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -15,16 +15,12 @@ dependencies { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlin { - target { - compilations.configureEach { - kotlinOptions { - jvmTarget = "1.8" - } - } + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) } } diff --git a/build-logic/src/main/kotlin/apollo.base-conventions.gradle.kts b/build-logic/src/main/kotlin/apollo.base-conventions.gradle.kts index d87f820ee..362d3d5ff 100644 --- a/build-logic/src/main/kotlin/apollo.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/apollo.base-conventions.gradle.kts @@ -36,7 +36,7 @@ spotless { fun FormatExtension.applyCommon() { trimTrailingWhitespace() endWithNewline() - indentWithSpaces(4) + leadingTabsToSpaces(4) targetExclude("**/build/generated/source/proto/**/*.*") targetExclude("**/org/spongepowered/configurate/yaml/**/*.*") } diff --git a/build-logic/src/main/kotlin/apollo.shadow-conventions.gradle.kts b/build-logic/src/main/kotlin/apollo.shadow-conventions.gradle.kts index 38b138f8e..424a87f7d 100644 --- a/build-logic/src/main/kotlin/apollo.shadow-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/apollo.shadow-conventions.gradle.kts @@ -2,7 +2,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { id("apollo.base-conventions") - id("com.github.johnrengelman.shadow") + id("com.gradleup.shadow") } tasks { diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt index 375c85d0e..7f57c7f93 100644 --- a/build-logic/src/main/kotlin/extensions.kt +++ b/build-logic/src/main/kotlin/extensions.kt @@ -147,7 +147,7 @@ fun Project.setupDynamicLoader() { archiveClassifier.set("all") } - val shadowJarLoader by tasks.creating(ShadowJar::class) { + val shadowJarLoader by tasks.registering(ShadowJar::class) { archiveClassifier.set("") configurations = listOf(loaderImplementationConfig.get()) diff --git a/common/src/main/java/com/lunarclient/apollo/module/cosmetic/CosmeticModuleImpl.java b/common/src/main/java/com/lunarclient/apollo/module/cosmetic/CosmeticModuleImpl.java new file mode 100644 index 000000000..2189d3a75 --- /dev/null +++ b/common/src/main/java/com/lunarclient/apollo/module/cosmetic/CosmeticModuleImpl.java @@ -0,0 +1,172 @@ +/* + * 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.cosmetic; + +import com.lunarclient.apollo.common.location.ApolloBlockLocation; +import com.lunarclient.apollo.cosmetic.v1.DisplaySprayMessage; +import com.lunarclient.apollo.cosmetic.v1.EquipNpcCosmeticsMessage; +import com.lunarclient.apollo.cosmetic.v1.RemoveSprayMessage; +import com.lunarclient.apollo.cosmetic.v1.ResetNpcCosmeticsMessage; +import com.lunarclient.apollo.cosmetic.v1.ResetSpraysMessage; +import com.lunarclient.apollo.cosmetic.v1.UnequipNpcCosmeticsMessage; +import com.lunarclient.apollo.module.cosmetic.options.BodyOptions; +import com.lunarclient.apollo.module.cosmetic.options.CloakOptions; +import com.lunarclient.apollo.module.cosmetic.options.CosmeticOptions; +import com.lunarclient.apollo.module.cosmetic.options.HatOptions; +import com.lunarclient.apollo.module.cosmetic.options.PetOptions; +import com.lunarclient.apollo.network.NetworkTypes; +import com.lunarclient.apollo.player.AbstractApolloPlayer; +import com.lunarclient.apollo.recipients.Recipients; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import lombok.NonNull; +import org.jetbrains.annotations.Nullable; + +import static com.lunarclient.apollo.util.Ranges.checkStrictlyPositive; + +/** + * Provides the cosmetic module. + * + * @since 1.2.6 + */ +public final class CosmeticModuleImpl extends CosmeticModule { + + @Override + public void equipNpcCosmetics(@NonNull Recipients recipients, @NonNull UUID npcUuid, @NonNull List cosmetics) { + List cosmeticsProto = cosmetics.stream() + .map(this::toProtobuf) + .collect(Collectors.toList()); + + EquipNpcCosmeticsMessage message = EquipNpcCosmeticsMessage.newBuilder() + .setNpcUuid(NetworkTypes.toProtobuf(npcUuid)) + .addAllCosmetics(cosmeticsProto) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void unequipNpcCosmetics(@NonNull Recipients recipients, @NonNull UUID npcUuid, @NonNull List cosmeticIds) { + List validatedIds = cosmeticIds.stream() + .map(id -> checkStrictlyPositive(id, "Cosmetic#id")) + .collect(Collectors.toList()); + + UnequipNpcCosmeticsMessage message = UnequipNpcCosmeticsMessage.newBuilder() + .setNpcUuid(NetworkTypes.toProtobuf(npcUuid)) + .addAllCosmeticIds(validatedIds) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void resetNpcCosmetics(@NonNull Recipients recipients, @NonNull UUID npcUuid) { + ResetNpcCosmeticsMessage message = ResetNpcCosmeticsMessage.newBuilder() + .setNpcUuid(NetworkTypes.toProtobuf(npcUuid)) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void displaySpray(@NonNull Recipients recipients, @NonNull Spray spray) { + DisplaySprayMessage message = DisplaySprayMessage.newBuilder() + .setSprayId(checkStrictlyPositive(spray.getSprayId(), "Spray#sprayId")) + .setLocation(NetworkTypes.toProtobuf(spray.getLocation())) + .setFacing(NetworkTypes.toProtobuf(spray.getFacing())) + .setRotation(spray.getRotation()) + .setDuration(NetworkTypes.toProtobuf(spray.getDuration())) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void removeSpray(@NonNull Recipients recipients, int sprayId) { + RemoveSprayMessage message = RemoveSprayMessage.newBuilder() + .setSprayId(checkStrictlyPositive(sprayId, "Spray#sprayId")) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void removeSpray(@NonNull Recipients recipients, int sprayId, @Nullable ApolloBlockLocation location) { + RemoveSprayMessage.Builder builder = RemoveSprayMessage.newBuilder() + .setSprayId(checkStrictlyPositive(sprayId, "Spray#sprayId")); + + if (location != null) { + builder.setLocation(NetworkTypes.toProtobuf(location)); + } + + RemoveSprayMessage message = builder.build(); + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void resetSprays(@NonNull Recipients recipients) { + ResetSpraysMessage message = ResetSpraysMessage.getDefaultInstance(); + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + private com.lunarclient.apollo.cosmetic.v1.Cosmetic toProtobuf(Cosmetic cosmetic) { + com.lunarclient.apollo.cosmetic.v1.Cosmetic.Builder builder = com.lunarclient.apollo.cosmetic.v1.Cosmetic.newBuilder() + .setId(checkStrictlyPositive(cosmetic.getId(), "Cosmetic#id")); + + CosmeticOptions options = cosmetic.getOptions(); + if (options == null) { + return builder.build(); + } + + if (options instanceof HatOptions) { + HatOptions hatOptions = (HatOptions) options; + builder.setHatOptions(com.lunarclient.apollo.cosmetic.v1.HatOptions.newBuilder() + .setShowOverHelmet(hatOptions.isShowOverHelmet()) + .setShowOverSkinLayer(hatOptions.isShowOverSkinLayer()) + .setHeightOffset(hatOptions.getHeightOffset()) + .build()); + } else if (options instanceof CloakOptions) { + CloakOptions cloakOptions = (CloakOptions) options; + builder.setCloakOptions(com.lunarclient.apollo.cosmetic.v1.CloakOptions.newBuilder() + .setUseClothPhysics(cloakOptions.isUseClothPhysics()) + .build()); + } else if (options instanceof PetOptions) { + PetOptions petOptions = (PetOptions) options; + builder.setPetOptions(com.lunarclient.apollo.cosmetic.v1.PetOptions.newBuilder() + .setFlipShoulder(petOptions.isFlipShoulder()) + .build()); + } else if (options instanceof BodyOptions) { + BodyOptions bodyOptions = (BodyOptions) options; + builder.setBodyOptions(com.lunarclient.apollo.cosmetic.v1.BodyOptions.newBuilder() + .setShowOverChestplate(bodyOptions.isShowOverChestplate()) + .setShowOverLeggings(bodyOptions.isShowOverLeggings()) + .setShowOverBoots(bodyOptions.isShowOverBoots()) + .build()); + } + + return builder.build(); + } + +} diff --git a/common/src/main/java/com/lunarclient/apollo/module/stopwatch/StopwatchModuleImpl.java b/common/src/main/java/com/lunarclient/apollo/module/stopwatch/StopwatchModuleImpl.java index efba37df3..ded30e4b3 100644 --- a/common/src/main/java/com/lunarclient/apollo/module/stopwatch/StopwatchModuleImpl.java +++ b/common/src/main/java/com/lunarclient/apollo/module/stopwatch/StopwatchModuleImpl.java @@ -23,12 +23,26 @@ */ package com.lunarclient.apollo.module.stopwatch; +import com.lunarclient.apollo.common.ApolloComponent; +import com.lunarclient.apollo.common.location.HudPosition; +import com.lunarclient.apollo.network.NetworkTypes; import com.lunarclient.apollo.player.AbstractApolloPlayer; import com.lunarclient.apollo.recipients.Recipients; +import com.lunarclient.apollo.stopwatch.v1.AddStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.AddTimerMessage; +import com.lunarclient.apollo.stopwatch.v1.RemoveStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.RemoveTimerMessage; import com.lunarclient.apollo.stopwatch.v1.ResetStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.ResetStopwatchesMessage; +import com.lunarclient.apollo.stopwatch.v1.ResetTimerMessage; +import com.lunarclient.apollo.stopwatch.v1.ResetTimersMessage; import com.lunarclient.apollo.stopwatch.v1.StartStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.StartTimerMessage; import com.lunarclient.apollo.stopwatch.v1.StopStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.StopTimerMessage; +import java.awt.Color; import lombok.NonNull; +import net.kyori.adventure.text.Component; /** * Provides the stopwatch module. @@ -55,4 +69,151 @@ public void resetStopwatch(@NonNull Recipients recipients) { recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); } + @Override + public void addStopwatch(@NonNull Recipients recipients, @NonNull Stopwatch stopwatch) { + AddStopwatchMessage.Builder builder = AddStopwatchMessage.newBuilder() + .setId(stopwatch.getId()) + .setName(stopwatch.getName()) + .setResetOnStart(stopwatch.isResetOnStart()) + .setPreventModification(stopwatch.isPreventModification()) + .setHideWhenStopped(stopwatch.isHideWhenStopped()); + + String displayFormat = stopwatch.getDisplayFormat(); + if (displayFormat != null) { + builder.setDisplayFormat(displayFormat); + } + + Color textColor = stopwatch.getTextColor(); + if (textColor != null) { + builder.setTextColor(NetworkTypes.toProtobuf(textColor)); + } + + HudPosition hudPosition = stopwatch.getHudPosition(); + if (hudPosition != null) { + builder.setHudPosition(NetworkTypes.toProtobuf(hudPosition)); + } + + AddStopwatchMessage message = builder.build(); + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void removeStopwatch(@NonNull Recipients recipients, @NonNull String id) { + RemoveStopwatchMessage message = RemoveStopwatchMessage.newBuilder() + .setId(id) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void startStopwatch(@NonNull Recipients recipients, @NonNull String id) { + StartStopwatchMessage message = StartStopwatchMessage.newBuilder() + .setId(id) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void stopStopwatch(@NonNull Recipients recipients, @NonNull String id) { + StopStopwatchMessage message = StopStopwatchMessage.newBuilder() + .setId(id) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void resetStopwatch(@NonNull Recipients recipients, @NonNull String id) { + ResetStopwatchMessage message = ResetStopwatchMessage.newBuilder() + .setId(id) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void resetStopwatches(@NonNull Recipients recipients) { + ResetStopwatchesMessage message = ResetStopwatchesMessage.getDefaultInstance(); + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void addTimer(@NonNull Recipients recipients, @NonNull Timer timer) { + AddTimerMessage.Builder builder = AddTimerMessage.newBuilder() + .setId(timer.getId()) + .setName(timer.getName()) + .setDuration(NetworkTypes.toProtobuf(timer.getDuration())) + .setLoop(timer.isLoop()) + .setPreventModification(timer.isPreventModification()) + .setHideWhenStopped(timer.isHideWhenStopped()) + .setInGameNotification(timer.isInGameNotification()); + + String displayFormat = timer.getDisplayFormat(); + if (displayFormat != null) { + builder.setDisplayFormat(displayFormat); + } + + Component titleText = timer.getTitleText(); + if (titleText != null) { + builder.setTitleTextAdventureJsonLines(ApolloComponent.toJson(titleText)); + } + + Color textColor = timer.getTextColor(); + if (textColor != null) { + builder.setTextColor(NetworkTypes.toProtobuf(textColor)); + } + + HudPosition hudPosition = timer.getHudPosition(); + if (hudPosition != null) { + builder.setHudPosition(NetworkTypes.toProtobuf(hudPosition)); + } + + AddTimerMessage message = builder.build(); + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void removeTimer(@NonNull Recipients recipients, @NonNull String id) { + RemoveTimerMessage message = RemoveTimerMessage.newBuilder() + .setId(id) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void startTimer(@NonNull Recipients recipients, @NonNull String id) { + StartTimerMessage message = StartTimerMessage.newBuilder() + .setId(id) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void stopTimer(@NonNull Recipients recipients, @NonNull String id) { + StopTimerMessage message = StopTimerMessage.newBuilder() + .setId(id) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void resetTimer(@NonNull Recipients recipients, @NonNull String id) { + ResetTimerMessage message = ResetTimerMessage.newBuilder() + .setId(id) + .build(); + + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + + @Override + public void resetTimers(@NonNull Recipients recipients) { + ResetTimersMessage message = ResetTimersMessage.getDefaultInstance(); + recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message)); + } + } 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 6d20395f0..1bc392648 100644 --- a/common/src/main/java/com/lunarclient/apollo/network/NetworkTypes.java +++ b/common/src/main/java/com/lunarclient/apollo/network/NetworkTypes.java @@ -35,6 +35,8 @@ import com.lunarclient.apollo.common.location.ApolloBlockLocation; import com.lunarclient.apollo.common.location.ApolloLocation; import com.lunarclient.apollo.common.location.ApolloPlayerLocation; +import com.lunarclient.apollo.common.location.HudPosition; +import com.lunarclient.apollo.common.profile.Profile; import com.lunarclient.apollo.common.v1.EntityId; import com.lunarclient.apollo.common.v1.Uuid; import com.lunarclient.apollo.module.packetenrichment.PlayerInfo; @@ -141,6 +143,36 @@ public static Color fromProtobuf(com.lunarclient.apollo.common.v1.Color message) return new Color(message.getColor()); } + /** + * Converts a {@link HudPosition} object to a + * {@link com.lunarclient.apollo.hud.v1.HudPosition} proto message. + * + * @param object the hud position + * @return the proto hud position message + * @since 1.2.6 + */ + public static com.lunarclient.apollo.hud.v1.HudPosition toProtobuf(HudPosition object) { + return com.lunarclient.apollo.hud.v1.HudPosition.newBuilder() + .setX(object.getX()) + .setY(object.getY()) + .build(); + } + + /** + * Converts a {@link com.lunarclient.apollo.hud.v1.HudPosition} + * proto message to a {@link HudPosition} object. + * + * @param message the hud position message + * @return the hud position object + * @since 1.2.6 + */ + public static HudPosition fromProtobuf(com.lunarclient.apollo.hud.v1.HudPosition message) { + return HudPosition.builder() + .x(message.getX()) + .y(message.getY()) + .build(); + } + /** * Converts an {@link Duration} object to an * {@link com.google.protobuf.Duration} proto message. @@ -252,7 +284,7 @@ public static com.lunarclient.apollo.packetenrichment.v1.RayTraceResult toProtob BlockHit blockHit = BlockHit.newBuilder() .setHitLocation(NetworkTypes.toProtobuf(result.getHitLocation())) .setBlockLocation(NetworkTypes.toProtobuf(result.getBlockLocation())) - .setDirection(com.lunarclient.apollo.packetenrichment.v1.Direction.forNumber(result.getDirection().ordinal() + 1)) + .setDirection(NetworkTypes.toProtobuf(result.getDirection())) .build(); builder.setBlock(blockHit); @@ -301,6 +333,17 @@ public static RayTraceResult fromProtobuf(com.lunarclient.apollo.packetenrichmen return new MissResult(); } + /** + * Converts a {@link Direction} enum to a {@link com.lunarclient.apollo.packetenrichment.v1.Direction} proto message. + * + * @param direction the apollo direction + * @return the proto direction enum message + * @since 1.2.6 + */ + public static com.lunarclient.apollo.packetenrichment.v1.Direction toProtobuf(Direction direction) { + return com.lunarclient.apollo.packetenrichment.v1.Direction.forNumber(direction.ordinal() + 1); + } + /** * Converts an {@link ApolloLocation} object to an * {@link com.lunarclient.apollo.common.v1.Location} proto message. @@ -538,6 +581,10 @@ public static com.lunarclient.apollo.common.v1.ItemStackIcon toProtobuf(ItemStac builder.setItemName(icon.getItemName()); } + if (icon.getProfile() != null) { + builder.setProfile(NetworkTypes.toProtobuf(icon.getProfile())); + } + return builder.build(); } @@ -550,11 +597,56 @@ public static com.lunarclient.apollo.common.v1.ItemStackIcon toProtobuf(ItemStac * @since 1.2.5 */ public static ItemStackIcon fromProtobuf(com.lunarclient.apollo.common.v1.ItemStackIcon icon) { - return ItemStackIcon.builder() + ItemStackIcon.ItemStackIconBuilder builder = ItemStackIcon.builder() .itemName(icon.getItemName()) .itemId(icon.getItemId()) - .customModelData(icon.getCustomModelData()) - .build(); + .customModelData(icon.getCustomModelData()); + + if (icon.hasProfile()) { + builder.profile(NetworkTypes.fromProtobuf(icon.getProfile())); + } + + return builder.build(); + } + + /** + * Converts a {@link Profile} object to a + * {@link com.lunarclient.apollo.common.v1.Profile} proto message. + * + * @param object the profile + * @return the proto profile message + * @since 1.2.6 + */ + public static com.lunarclient.apollo.common.v1.Profile toProtobuf(Profile object) { + com.lunarclient.apollo.common.v1.Profile.Builder builder = com.lunarclient.apollo.common.v1.Profile.newBuilder() + .setTexture(object.getTexture()) + .setSignature(object.getSignature()); + + if (object.getId() != null) { + builder.setId(NetworkTypes.toProtobuf(object.getId())); + } + + return builder.build(); + } + + /** + * Converts a {@link com.lunarclient.apollo.common.v1.Profile} + * proto message to a {@link Profile} object. + * + * @param message the profile message + * @return the profile object + * @since 1.2.6 + */ + public static Profile fromProtobuf(com.lunarclient.apollo.common.v1.Profile message) { + Profile.ProfileBuilder builder = Profile.builder() + .texture(message.getTexture()) + .signature(message.getSignature()); + + if (message.hasId()) { + builder.id(NetworkTypes.fromProtobuf(message.getId())); + } + + return builder.build(); } /** diff --git a/common/src/main/java/com/lunarclient/apollo/util/Ranges.java b/common/src/main/java/com/lunarclient/apollo/util/Ranges.java index bf7bc7541..655726a6c 100644 --- a/common/src/main/java/com/lunarclient/apollo/util/Ranges.java +++ b/common/src/main/java/com/lunarclient/apollo/util/Ranges.java @@ -62,6 +62,21 @@ public static int checkPositive(int value, String name) { return value; } + /** + * Returns the value if it is strictly positive (greater than 0), + * otherwise throws an {@link IllegalArgumentException}. + * + * @param value the value to check + * @param name the name of the value + * @return the value + * @throws IllegalArgumentException if the value is not strictly positive + * @since 1.2.6 + */ + public static int checkStrictlyPositive(int value, String name) { + if(value <= 0) throw new IllegalArgumentException(name + " must be greater than 0"); + return value; + } + /** * Returns the value if it is positive, otherwise throws an * {@link IllegalArgumentException}. diff --git a/docs/acceptable-use.mdx b/docs/acceptable-use.mdx index 1ddf215c2..49226e1cd 100644 --- a/docs/acceptable-use.mdx +++ b/docs/acceptable-use.mdx @@ -7,20 +7,22 @@ import { Callout } from 'nextra-theme-docs' any experiences you create must still be compliant with the Minecraft EULA and Lunar Client Terms of Service. -## Current modules -All current Apollo modules are available for use by all servers, without charge. The intent of -Apollo is to help servers create amazing experiences for Lunar Client players. +## Public Modules +Apollo includes a wide range of publicly available modules that can be used by all servers at no cost. -## Future modules -We _may_ release future Apollo modules / features that require an approval process for servers -to use. This would be done if a new module / feature is: +The intent of Apollo is to encourage servers to integrate directly with Lunar Client to create better player experiences. + +## Private & Future Modules +Apollo includes [private modules](/apollo/developers/private-modules) that require additional approval before use. + +We typically reserve private modules for features that: * Something that requires backing infrastructure that we're unable to provide for free. * Something we're required to keep exclusive for a certain period of time. * Something that directly competes with our [store](https://store.lunarclient.com). ## Questions -If you have further questions about acceptable use, you can join the [Lunar Client Developers Discord](https://lunarclient.dev/discord). +If you have further questions about acceptable use, you can join the [Lunar Client Developers Discord](https://lunarclient.dev/discord). Alternatively, you can request additional verification by contacting us directly via email: **apollo@lunarclient.com**. _Disclaimer_: Our acceptable-use polices may be updated at any time to reflect changes in technology or legal requirements. Modules and features, both current and future, may require approval by Moonsworth, LLC for any reason. diff --git a/docs/developers.mdx b/docs/developers.mdx new file mode 100644 index 000000000..97dc082f4 --- /dev/null +++ b/docs/developers.mdx @@ -0,0 +1,37 @@ +import { Callout } from 'nextra-theme-docs' + +# Introduction + +Apollo is a powerful tool that allows developers to create custom integrations with Lunar Client. + +Apollo enables server owners and developers to tweak the capabilities of Lunar Client on their server, create custom experiences not possible in vanilla Minecraft, and add quality-of-life features. + +We have a lot planned for Apollo, and are very open to suggestions and contributions. + +This documentation will provide you with all information you'll need to know to start integrating Apollo into your server or plugins! +We'll cover the basics of Apollo, from changing the configuration file to integrating your own features using each module. +Inside the module breakdowns, for developers, we'll include code snippets along with photos, videos, and GIFs related to the module to ensure you get the most out of Apollo. + + + Need just the essentials? [Lightweight](/apollo/developers/lightweight) allows you to use Apollo features without the full plugin! + + +## Useful Links + +🔗 [Lunar Client Website](https://www.lunarclient.com/)
+🔗 [Lunar Client Developers Discord](https://lunarclient.dev/discord)
+🔗 [Apollo GitHub Repository](https://github.com/LunarClient/Apollo)
+ + +## Where to start + +🔗 [FAQ](/apollo/developers/faq)
+🔗 [General](/apollo/developers/general)
+🔗 [Events](/apollo/developers/events)
+🔗 [Options](/apollo/developers/options)
+🔗 [Modules](/apollo/developers/modules)
+🔗 [Private Modules](/apollo/developers/private-modules)
+🔗 [Mod Options](/apollo/developers/mods/2ditems)
+🔗 [Utilities](/apollo/developers/utilities/colors)
+🔗 [Minestom](/apollo/developers/minestom)
+🔗 [Lightweight](/apollo/developers/lightweight) diff --git a/docs/developers/_meta.json b/docs/developers/_meta.json index f6f66360c..efe255c44 100644 --- a/docs/developers/_meta.json +++ b/docs/developers/_meta.json @@ -4,10 +4,9 @@ "events": "Events", "options": "Options", "modules": "Modules", - "mods": "Mods", + "private-modules": "Private Modules", + "mods": "Mod Options", "utilities": "Utilities", - "platform-utilities": "Platform Utilities", - "adventure": "Adventure", "minestom": "Minestom", "lightweight": "Lightweight" } diff --git a/docs/developers/events.mdx b/docs/developers/events.mdx index e3cb1f792..a8899eee9 100644 --- a/docs/developers/events.mdx +++ b/docs/developers/events.mdx @@ -300,77 +300,3 @@ public class GeneralExample2 implements ApolloListener { } ``` -## Creating Apollo events - -### Creating the event class - -```java -// Normal event -public class CoolApolloEvent implements Event { - - private final ApolloPlayer player; - - public CoolApolloEvent(ApolloPlayer player) { - this.player = player; - } - - public ApolloPlayer getPlayer() { - return this.player; - } -} - -// Cancellable event -public class CoolApolloCancellableEvent implements EventCancellable { - - private final ApolloPlayer player; - - private boolean cancelled; - - public CoolApolloCancellableEvent(ApolloPlayer player) { - this.player = player; - } - - public ApolloPlayer getPlayer() { - return this.player; - } - - @Override - public boolean isCancelled() { - return this.cancelled; - } - - @Override - public void setCancelled(boolean cancel) { - this.cancelled = cancel; - } -} -``` - -### Calling the created event - -```java -// Calling a normal event -public void callCoolApolloEvent(ApolloPlayer player) { - CoolApolloEvent event = new CoolApolloEvent(player); - EventBus.EventResult result = EventBus.getBus().post(event); - - for (Throwable throwable : result.getThrowing()) { - throwable.printStackTrace(); - } -} - -// Calling a cancellable event -public void callCoolApolloCancellableEvent(ApolloPlayer player) { - CoolApolloCancellableEvent event = new CoolApolloCancellableEvent(player); - EventBus.EventResult result = EventBus.getBus().post(event); - - if (!result.getEvent().isCancelled()) { - // Do some action if the event is not cancelled - } - - for (Throwable throwable : result.getThrowing()) { - throwable.printStackTrace(); - } -} -``` - diff --git a/docs/developers/faq.mdx b/docs/developers/faq.mdx index 3ffa32b4a..2b46f8586 100644 --- a/docs/developers/faq.mdx +++ b/docs/developers/faq.mdx @@ -1,63 +1,22 @@ -# FAQs - -
-Will the legacy API still be supported and maintained? - -As of now, the legacy API will remain functional, but will **not** be improved, extended, or patched further. -We highly recommend switching to the updated version of Apollo. -As of the first release, all features inside the legacy API are available on Apollo. - -
- -
-Where can I view, fork or download the source code to Apollo? - -The source code can be viewed, forked and downloaded on the Lunar Client [GitHub](https://github.com/LunarClient/Apollo). -Feel free to download the source and fork it anyway you can imagine, as long as it's within the ToS and follows the license. - -
- -
-Where can I view the documentation for Apollo? - -You can view the documentation for Apollo, Lunar Client's API, on our Lunar Client [Developers website](https://www.lunarclient.dev/). - -
+import { Callout } from 'nextra-theme-docs' -
-Do I have to run Apollo to interact with Lunar Client? - -You do not need to run the Apollo plugin to interact with Lunar Client, however you do need to follow the protocols found within Apollo. Features might not function properly if you use an alternate implementation that does not implement the Apollo protocols properly. - -
- -
-Where can I suggest new features or enhancements for Apollo? - -You can join the Lunar Client Developers [Discord](https://www.lunarclient.dev/discord) and create a suggestion under `#suggestions`. - -Alternatively, if you have the skills and knowledge, you can create the feature yourself and open a pull request on the [Apollo repository](https://github.com/LunarClient/Apollo) on GitHub. If your feature requires new changes or enhancements to Lunar Client to function, we recommend connecting with us on the [Lunar Client Developers](https://www.lunarclient.dev/discord) Discord server. - -
- -
-Where can I report issues with Apollo or Lunar Client? - -You can report issues on the [Apollo repository](https://github.com/LunarClient/Apollo) issues page, on GitHub. -If you're having an issue with Lunar Client that doesn't involve Apollo, you can create a [support ticket](https://support.lunarclient.com/). +# FAQs -
+ +Have a question not currently covered here? Join our [Developer Discord](lunarclient.dev/discord) to ask! + -
-Where can I contribute to Apollo? +## Do I have to run Apollo to use Apollo features? +No, you do not need to run the Apollo plugin directly. However, you do need to follow the protocols found within Apollo for features to function properly. -You can open a pull request on the [Apollo repository](https://github.com/LunarClient/Apollo) page, on GitHub. +That said, for full compatibility, reliability, and broad integration, we recommend using Apollo directly. -
+## Where can I suggest features or improvements? Where can I report issues? +You are able to share your suggestions in the Lunar Client [developer discord](https://www.lunarclient.dev/discord) under `#suggestions`. -
-Where can I view the Apollo licensing and acceptable use policy? +If you've found an issue with Apollo, you can report it on our [GitHub repository](https://www.github.com/LunarClient/Apollo). For issues unrelated to Apollo, please use the proper channels to report them. -We've listed our [Acceptable Use policy](https://www.lunarclient.dev/apollo/acceptable-use) and [Licensing](https://www.lunarclient.dev/apollo/license) information on the Lunar Client developers website. +## What are Apollo's licensing and acceptable use policies? +Apollo has [licensing](https://www.lunarclient.dev/apollo/license) terms and an [acceptable use](https://www.lunarclient.dev/apollo/acceptable-use) policy that define how it can be used. These are subject to change, and should be reviewed accordingly. -
+You can find both on the Lunar Client documentation website at all times. Make sure your usage complies with these policies, or you may be prevented from using Apollo features in the future. If you have further questions, ask! diff --git a/docs/developers/general.mdx b/docs/developers/general.mdx index 5a2e05f5a..806d05270 100644 --- a/docs/developers/general.mdx +++ b/docs/developers/general.mdx @@ -7,6 +7,12 @@ You will see these options appear throughout Apollo and while reading the docume We recommend you use and understand these general features. +### Sending Apollo data after a player joins + +Do **not** rely on the **`PlayerJoinEvent`** as the moment to send Apollo module packets to a player. At join time the client has often **not** completed the Apollo registration handshake yet, so your packets can be dropped or have no effect. + +Listen for **[ApolloRegisterPlayerEvent](/apollo/developers/events#apollo-register-player-event)** instead. It fires once Lunar Client has registered with the server after the client completes the registration flow Apollo expects. That is the right place to apply initial module state, waypoints, and similar setup. + ### Player #### Checking if player is running Lunar Client diff --git a/docs/developers/lightweight/introduction.mdx b/docs/developers/lightweight.mdx similarity index 89% rename from docs/developers/lightweight/introduction.mdx rename to docs/developers/lightweight.mdx index b581efbe9..38a85c7fd 100644 --- a/docs/developers/lightweight/introduction.mdx +++ b/docs/developers/lightweight.mdx @@ -24,5 +24,5 @@ The message format used to communicate, follows a loosely structured JSON schema ### Usage Methods -🔗 [Protobuf](/apollo/developers/lightweight/protobuf/getting-started)
-🔗 [JSON](/apollo/developers/lightweight/json/getting-started)
+🔗 [Protobuf](/apollo/developers/lightweight/protobuf)
+🔗 [JSON](/apollo/developers/lightweight/json)
diff --git a/docs/developers/lightweight/_meta.json b/docs/developers/lightweight/_meta.json index 88c256cf9..2bd8f341a 100644 --- a/docs/developers/lightweight/_meta.json +++ b/docs/developers/lightweight/_meta.json @@ -1,5 +1,4 @@ { - "introduction": "Introduction", "protobuf": "Protobuf", "json": "JSON" } diff --git a/docs/developers/lightweight/json/getting-started.mdx b/docs/developers/lightweight/json.mdx similarity index 100% rename from docs/developers/lightweight/json/getting-started.mdx rename to docs/developers/lightweight/json.mdx diff --git a/docs/developers/lightweight/json/_meta.json b/docs/developers/lightweight/json/_meta.json index 83eb3d8b5..0959a1f2b 100644 --- a/docs/developers/lightweight/json/_meta.json +++ b/docs/developers/lightweight/json/_meta.json @@ -1,5 +1,4 @@ { - "getting-started": "Getting Started", "player-detection": "Player Detection", "serverbound-packets": "Serverbound Packets", "roundtrip-packets": "Roundtrip Packets", diff --git a/docs/developers/lightweight/json/object-util.mdx b/docs/developers/lightweight/json/object-util.mdx index de62365e5..f60dbbfdf 100644 --- a/docs/developers/lightweight/json/object-util.mdx +++ b/docs/developers/lightweight/json/object-util.mdx @@ -52,9 +52,13 @@ public static JsonObject createCuboid2DObject(double minX, double minZ, double m } public static JsonObject createEntityIdObject(@NotNull Entity entity) { + return JsonUtil.createEntityIdObject(entity.getEntityId(), entity.getUniqueId()); +} + +public static JsonObject createEntityIdObject(int entityId, @NotNull UUID uuid) { JsonObject entityIdObject = new JsonObject(); - entityIdObject.addProperty("entity_id", entity.getEntityId()); - entityIdObject.add("entity_uuid", JsonUtil.createUuidObject(entity.getUniqueId())); + entityIdObject.addProperty("entity_id", entityId); + entityIdObject.add("entity_uuid", JsonUtil.createUuidObject(uuid)); return entityIdObject; } ``` @@ -85,6 +89,10 @@ Icon-related methods ```java public static JsonObject createItemStackIconObject(@Nullable String itemName, int itemId, int customModelData) { + return JsonUtil.createItemStackIconObject(itemName, itemId, customModelData, null); +} + +public static JsonObject createItemStackIconObject(@Nullable String itemName, int itemId, int customModelData, @Nullable JsonObject profile) { JsonObject itemIconObject = new JsonObject(); if (itemName != null) { itemIconObject.addProperty("item_name", itemName); @@ -94,11 +102,25 @@ public static JsonObject createItemStackIconObject(@Nullable String itemName, in itemIconObject.addProperty("custom_model_data", customModelData); + if (profile != null) { + itemIconObject.add("profile", profile); + } + JsonObject iconObject = new JsonObject(); iconObject.add("item_stack", itemIconObject); return iconObject; } +public static JsonObject createProfileObject(@Nullable UUID id, @NotNull String texture, @NotNull String signature) { + JsonObject profileObject = new JsonObject(); + if (id != null) { + profileObject.add("id", JsonUtil.createUuidObject(id)); + } + profileObject.addProperty("texture", texture); + profileObject.addProperty("signature", signature); + return profileObject; +} + public static JsonObject createResourceLocationIconObject(@NotNull String resourceLocation) { JsonObject resourceIconObject = new JsonObject(); resourceIconObject.addProperty("resource_location", resourceLocation); diff --git a/docs/developers/lightweight/protobuf/getting-started.mdx b/docs/developers/lightweight/protobuf.mdx similarity index 95% rename from docs/developers/lightweight/protobuf/getting-started.mdx rename to docs/developers/lightweight/protobuf.mdx index a90ec0747..c1129b9d6 100644 --- a/docs/developers/lightweight/protobuf/getting-started.mdx +++ b/docs/developers/lightweight/protobuf.mdx @@ -26,7 +26,7 @@ Available fields for each message, including their types, are available on the B com.lunarclient apollo-protos - 0.1.0 + 0.1.6 ``` @@ -41,7 +41,7 @@ Available fields for each message, including their types, are available on the B } dependencies { - api 'com.lunarclient:apollo-protos:0.1.0' + api 'com.lunarclient:apollo-protos:0.1.6' } ``` @@ -55,7 +55,7 @@ Available fields for each message, including their types, are available on the B } dependencies { - api("com.lunarclient:apollo-protos:0.1.0") + api("com.lunarclient:apollo-protos:0.1.6") } ``` diff --git a/docs/developers/lightweight/protobuf/_meta.json b/docs/developers/lightweight/protobuf/_meta.json index 83eb3d8b5..0959a1f2b 100644 --- a/docs/developers/lightweight/protobuf/_meta.json +++ b/docs/developers/lightweight/protobuf/_meta.json @@ -1,5 +1,4 @@ { - "getting-started": "Getting Started", "player-detection": "Player Detection", "serverbound-packets": "Serverbound Packets", "roundtrip-packets": "Roundtrip Packets", diff --git a/docs/developers/lightweight/protobuf/object-util.mdx b/docs/developers/lightweight/protobuf/object-util.mdx index bff0bbdeb..7f5817b79 100644 --- a/docs/developers/lightweight/protobuf/object-util.mdx +++ b/docs/developers/lightweight/protobuf/object-util.mdx @@ -98,6 +98,10 @@ Icon-related methods ```java public static ItemStackIcon createItemStackIconProto(@Nullable String itemName, int itemId, int customModelData) { + return ProtobufUtil.createItemStackIconProto(itemName, itemId, customModelData, null); +} + +public static ItemStackIcon createItemStackIconProto(@Nullable String itemName, int itemId, int customModelData, @Nullable Profile profile) { ItemStackIcon.Builder iconBuilder = ItemStackIcon.newBuilder() .setItemId(itemId) .setCustomModelData(customModelData); @@ -106,9 +110,25 @@ public static ItemStackIcon createItemStackIconProto(@Nullable String itemName, iconBuilder.setItemName(itemName); } + if (profile != null) { + iconBuilder.setProfile(profile); + } + return iconBuilder.build(); } +public static Profile createProfileProto(@Nullable UUID id, String texture, String signature) { + Profile.Builder builder = Profile.newBuilder() + .setTexture(texture) + .setSignature(signature); + + if (id != null) { + builder.setId(ProtobufUtil.createUuidProto(id)); + } + + return builder.build(); +} + public static ResourceLocationIcon createResourceLocationIconProto(String resourceLocation) { return ResourceLocationIcon.newBuilder() .setResourceLocation(resourceLocation) diff --git a/docs/developers/minestom.mdx b/docs/developers/minestom.mdx index 5bbccfea8..c1b779d67 100644 --- a/docs/developers/minestom.mdx +++ b/docs/developers/minestom.mdx @@ -52,14 +52,14 @@ Next, add the `apollo-minestom` dependency to your project. ```kotlin filename="build.gradle.kts" dependencies { - implementation("com.lunarclient:apollo-minestom:1.2.5") + implementation("com.lunarclient:apollo-minestom:1.2.6") } ``` ```groovy filename="build.gradle" dependencies { - implementation 'com.lunarclient:apollo-minestom:1.2.5' + implementation 'com.lunarclient:apollo-minestom:1.2.6' } ``` @@ -69,7 +69,7 @@ Next, add the `apollo-minestom` dependency to your project. com.lunarclient apollo-minestom - 1.2.5 + 1.2.6 compile diff --git a/docs/developers/mods/armorstatus.mdx b/docs/developers/mods/armorstatus.mdx index 1c2a8556b..8f1865051 100644 --- a/docs/developers/mods/armorstatus.mdx +++ b/docs/developers/mods/armorstatus.mdx @@ -39,7 +39,7 @@ public void toggleArmorStatusExample(Player viewer, boolean value) { - Config Key: `hide-unbreakable-durability` - Values - Type: `Boolean` - - Default: `false` + - Default: `true` - __`ITEM_NAME`__ - Config Key: `item-name` diff --git a/docs/developers/mods/particlechanger.mdx b/docs/developers/mods/particlechanger.mdx index 81a233921..f403aef93 100644 --- a/docs/developers/mods/particlechanger.mdx +++ b/docs/developers/mods/particlechanger.mdx @@ -21,53 +21,21 @@ public void toggleParticleChangerExample(Player viewer, boolean value) { - Type: `Boolean` - Default: `false` -- __`SHOW_BLOOD_PARTICLES`__ - - Config Key: `show-blood-particles` - - Values - - Type: `Boolean` - - Default: `false` - -- __`BLOOD_MULTIPLIER`__ - - Config Key: `blood-multiplier` - - Values - - Type: `Integer` - - Default: `1` - - Minimum: `1` - - Maximum: `10` - -- __`PLAYER_BLOOD_PARTICLES`__ - - Config Key: `player-blood-particles` - - Values - - Type: `Boolean` - - Default: `true` - -- __`ENTITY_BLOOD_PARTICLES`__ - - Config Key: `entity-blood-particles` - - Values - - Type: `Boolean` - - Default: `true` - -- __`PLAY_BLOOD_SOUND`__ - - Config Key: `play-blood-sound` - - Values - - Type: `Boolean` - - Default: `false` - - __`ALWAYS_ENCHANT_STRIKES`__ - Config Key: `always-enchant-strikes` - Values - Type: `Boolean` - Default: `false` -- __`AFFECT_ENCHANTED_WEAPONS`__ - - Config Key: `affect-enchanted-weapons` +- __`HIDE_FIRST_PERSON_PARTICLES`__ + - Hide particles which are emitted in first person (e.g. potion effects) + - Config Key: `hide-first-person-particles` - Values - Type: `Boolean` - - Default: `true` + - Default: `false` -- __`HIDE_FIRST_PERSON_PARTICLES`__ - - Hide your players potion effect particles when you're in first person - - Config Key: `hide-first-person-particles` +- __`HIDE_BLOCK_BREAK_PARTICLES`__ + - Config Key: `hide-block-break-particles` - Values - Type: `Boolean` - Default: `false` @@ -79,27 +47,21 @@ public void toggleParticleChangerExample(Player viewer, boolean value) { - Type: `Boolean` - Default: `false` -- __`COLOR`__ - - Config Key: `color` - - Values - - Type: `String` - - Default: `#FFFFFFFF` - - __`SCALE`__ - Config Key: `scale` - Values - Type: `Float` - Default: `1.0F` - - Minimum: `0.5F` + - Minimum: `0.25F` - Maximum: `2.0F` - __`PARTICLE_MULTIPLIER`__ - Config Key: `particle-multiplier` - Values - - Type: `Integer` - - Default: `1` - - Minimum: `1` - - Maximum: `10` + - Type: `Float` + - Default: `1.0F` + - Minimum: `0.25F` + - Maximum: `10.0F` - __`HIDE_PARTICLE`__ - Config Key: `hide-particle` @@ -113,3 +75,9 @@ public void toggleParticleChangerExample(Player viewer, boolean value) { - Type: `Boolean` - Default: `false` +- __`COLOR`__ + - Config Key: `color` + - Values + - Type: `String` + - Default: `#FFFFFFFF` + diff --git a/docs/developers/modules.mdx b/docs/developers/modules.mdx new file mode 100644 index 000000000..16803d54d --- /dev/null +++ b/docs/developers/modules.mdx @@ -0,0 +1,40 @@ +# Public Modules + +Apollo provides a wide-range of public modules that allow you to integrate direct with Lunar Client. + +These modules are available to all servers using Apollo and do not require any pre-approval. You are still expected to follow our [Acceptable Use](/apollo/acceptable-use) guidelines. + +### Available Modules + +🔗 [Auto Text Hotkey](/apollo/developers/modules/autotexthotkey)
+🔗 [Beam](/apollo/developers/modules/beam)
+🔗 [Border](/apollo/developers/modules/border)
+🔗 [Chat](/apollo/developers/modules/chat)
+🔗 [Colored Fire](/apollo/developers/modules/coloredfire)
+🔗 [Combat](/apollo/developers/modules/combat)
+🔗 [Cooldown](/apollo/developers/modules/cooldown)
+🔗 [Entity](/apollo/developers/modules/entity)
+🔗 [Glint](/apollo/developers/modules/glint)
+🔗 [Glow](/apollo/developers/modules/glow)
+🔗 [Hologram](/apollo/developers/modules/hologram)
+🔗 [Inventory](/apollo/developers/modules/inventory)
+🔗 [Limb](/apollo/developers/modules/limb)
+🔗 [Mod Setting](/apollo/developers/modules/modsetting)
+🔗 [Nametag](/apollo/developers/modules/nametag)
+🔗 [Nick Hider](/apollo/developers/modules/nickhider)
+🔗 [Notification](/apollo/developers/modules/notification)
+🔗 [PayNow](/apollo/developers/modules/paynow)
+🔗 [Packet Enrichment](/apollo/developers/modules/packetenrichment)
+🔗 [Rich Presence](/apollo/developers/modules/richpresence)
+🔗 [Saturation](/apollo/developers/modules/saturation)
+🔗 [Server Link](/apollo/developers/modules/serverlink)
+🔗 [Server Rule](/apollo/developers/modules/serverrule)
+🔗 [Staff Mod](/apollo/developers/modules/staffmod)
+🔗 [Stopwatch](/apollo/developers/modules/stopwatch)
+🔗 [Team](/apollo/developers/modules/team)
+🔗 [Tebex](/apollo/developers/modules/tebex)
+🔗 [Title](/apollo/developers/modules/title)
+🔗 [TNT Countdown](/apollo/developers/modules/tntcountdown)
+🔗 [Transfer](/apollo/developers/modules/transfer)
+🔗 [Vignette](/apollo/developers/modules/vignette)
+🔗 [Waypoint](/apollo/developers/modules/waypoint)
diff --git a/docs/developers/modules/autotexthotkey.mdx b/docs/developers/modules/autotexthotkey.mdx index c154f013e..712a745b1 100644 --- a/docs/developers/modules/autotexthotkey.mdx +++ b/docs/developers/modules/autotexthotkey.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Auto Text Hotkey Module @@ -16,6 +16,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Set Blocked Text Inputs ```java @@ -38,6 +42,10 @@ public void setBlockChatMesssageTextInputs(boolean value) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + ### Set Blocked Text Inputs ```java @@ -73,6 +81,10 @@ public void setBlockChatMesssageTextInputs(boolean value) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + ### Set Blocked Text Inputs ```java diff --git a/docs/developers/modules/beam.mdx b/docs/developers/modules/beam.mdx index 57253cbb4..22bc0cf61 100644 --- a/docs/developers/modules/beam.mdx +++ b/docs/developers/modules/beam.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Beam Module @@ -28,6 +28,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Displaying a Beam ```java @@ -129,6 +133,10 @@ Custom colors can be created from any RGB values using `new Color(int red, int g + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Displaying a Beam** ```java @@ -168,6 +176,10 @@ public void resetBeamsExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Displaying a Beam** ```java diff --git a/docs/developers/modules/border.mdx b/docs/developers/modules/border.mdx index 3532abea8..9e708cabc 100644 --- a/docs/developers/modules/border.mdx +++ b/docs/developers/modules/border.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Border Module @@ -26,6 +25,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Displaying a Border ```java @@ -162,6 +165,10 @@ Custom colors can be created from any RGB values using `new Color(int red, int g + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Displaying a Border** ```java @@ -206,6 +213,10 @@ public void resetBordersExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Displaying a Border** ```java diff --git a/docs/developers/modules/chat.mdx b/docs/developers/modules/chat.mdx index 99cbe3876..6065ed978 100644 --- a/docs/developers/modules/chat.mdx +++ b/docs/developers/modules/chat.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Chat Module @@ -24,6 +24,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Displaying a Live Chat Message ```java @@ -99,6 +103,10 @@ public void removeLiveChatMessageExample() { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Displaying a Live Chat Message** ```java @@ -185,6 +193,10 @@ public void removeLiveChatMessageExample() { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Displaying a Live Chat Message** ```java diff --git a/docs/developers/modules/coloredfire.mdx b/docs/developers/modules/coloredfire.mdx index d4256840f..b3d876fbd 100644 --- a/docs/developers/modules/coloredfire.mdx +++ b/docs/developers/modules/coloredfire.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Colored Fire Module @@ -21,6 +21,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Override a Fire Color ```java @@ -62,6 +66,10 @@ public void resetColoredFiresExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Override a Fire Color** ```java @@ -100,6 +108,10 @@ public void resetColoredFiresExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Override a Fire Color** ```java diff --git a/docs/developers/modules/combat.mdx b/docs/developers/modules/combat.mdx index 06e6382c1..22803ec52 100644 --- a/docs/developers/modules/combat.mdx +++ b/docs/developers/modules/combat.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Combat Module @@ -15,6 +15,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Toggle Miss Penalty ```java @@ -27,6 +31,10 @@ public void setDisableMissPenalty(boolean value) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + ### Toggle Miss Penalty ```java @@ -43,6 +51,10 @@ public void setDisableMissPenalty(boolean value) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + ### Toggle Miss Penalty ```java diff --git a/docs/developers/modules/cooldown.mdx b/docs/developers/modules/cooldown.mdx index 16c48fe49..6c695fea4 100644 --- a/docs/developers/modules/cooldown.mdx +++ b/docs/developers/modules/cooldown.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Cooldown Module @@ -25,6 +25,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Displaying a Cooldown with an item ```java @@ -70,6 +74,31 @@ public void displayCooldownWithStyleExample(Player viewer) { } ``` +### Displaying a Cooldown with a player texture + +```java +public void displayCooldownWithPlayerTextureExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + + apolloPlayerOpt.ifPresent(apolloPlayer -> { + this.cooldownModule.displayCooldown(apolloPlayer, Cooldown.builder() + .name("player-head-cooldown") + .duration(Duration.ofSeconds(15)) + .icon(ItemStackIcon.builder() + .itemName("PLAYER_HEAD") // use "skull" for legacy with customModelData set to 3 + .profile(Profile.builder() + .id(UUID.fromString("f17627d8-1a97-487b-92ea-c04f413394bd")) + .texture("e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQ4MjUwNWJjZjNiYTU5YzJiZTdlMmQzNmY0ZTJiZGE4MzZmMmZkMTk0YjYyMTJhMmExYzRiNGEyYTQ3MWUifX19") + .signature("") + .build()) + .build() + ) + .build() + ); + }); +} +``` + ### Displaying a Cooldown with a resource ```java @@ -100,6 +129,7 @@ public void removeCooldownExample(Player viewer) { apolloPlayerOpt.ifPresent(apolloPlayer -> { this.cooldownModule.removeCooldown(apolloPlayer, "enderpearl-cooldown"); this.cooldownModule.removeCooldown(apolloPlayer, "book-cooldown"); + this.cooldownModule.removeCooldown(apolloPlayer, "player-head-cooldown"); this.cooldownModule.removeCooldown(apolloPlayer, "lunar-cooldown"); }); } @@ -174,6 +204,10 @@ All `CooldownStyle` fields are optional; any field left unset will fall back to + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Displaying a Cooldown with an item** ```java @@ -212,6 +246,29 @@ public void displayCooldownWithStyleExample(Player viewer) { } ``` +**Displaying a Cooldown with a player texture** + +```java +public void displayCooldownWithPlayerTextureExample(Player viewer) { + DisplayCooldownMessage message = DisplayCooldownMessage.newBuilder() + .setName("player-head-cooldown") + .setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(15))) + .setIcon(Icon.newBuilder() + .setItemStack(ProtobufUtil.createItemStackIconProto( + "PLAYER_HEAD", 0, 0, + ProtobufUtil.createProfileProto( + UUID.fromString("f17627d8-1a97-487b-92ea-c04f413394bd"), + "e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQ4MjUwNWJjZjNiYTU5YzJiZTdlMmQzNmY0ZTJiZGE4MzZmMmZkMTk0YjYyMTJhMmExYzRiNGEyYTQ3MWUifX19", + "" + ) + )) + .build()) + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + **Displaying a Cooldown with a resource** ```java @@ -253,6 +310,10 @@ public void resetCooldownsExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Displaying a Cooldown with an item** ```java @@ -288,6 +349,27 @@ public void displayCooldownWithStyleExample(Player viewer) { } ``` +**Displaying a Cooldown with a player texture** + +```java +public void displayCooldownWithPlayerTextureExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cooldown.v1.DisplayCooldownMessage"); + message.addProperty("name", "player-head-cooldown"); + message.addProperty("duration", JsonUtil.createDurationObject(Duration.ofSeconds(15))); + message.add("icon", JsonUtil.createItemStackIconObject( + "PLAYER_HEAD", 0, 0, + JsonUtil.createProfileObject( + UUID.fromString("f17627d8-1a97-487b-92ea-c04f413394bd"), + "e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQ4MjUwNWJjZjNiYTU5YzJiZTdlMmQzNmY0ZTJiZGE4MzZmMmZkMTk0YjYyMTJhMmExYzRiNGEyYTQ3MWUifX19", + "" + ) + )); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + **Displaying a Cooldown with a resource** ```java diff --git a/docs/developers/modules/entity.mdx b/docs/developers/modules/entity.mdx index 0bbeaff17..9361f0dbf 100644 --- a/docs/developers/modules/entity.mdx +++ b/docs/developers/modules/entity.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Entity Module @@ -18,6 +18,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Override Sheep rainbow state ```java @@ -100,6 +104,10 @@ public void resetFlippedEntityExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Override Sheep rainbow state** ```java @@ -172,6 +180,10 @@ public void resetFlippedEntityExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Override Sheep rainbow state** ```java diff --git a/docs/developers/modules/glow.mdx b/docs/developers/modules/glow.mdx index 992d6c046..cfd31d39e 100644 --- a/docs/developers/modules/glow.mdx +++ b/docs/developers/modules/glow.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Glow Module @@ -34,6 +33,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Override a Glow Effect ```java @@ -76,6 +79,10 @@ public void resetGlowEffectsExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Override a Glow Effect** ```java @@ -114,6 +121,10 @@ public void resetGlowEffectsExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Override a Glow Effect** ```java diff --git a/docs/developers/modules/hologram.mdx b/docs/developers/modules/hologram.mdx index db5ab913e..92c868986 100644 --- a/docs/developers/modules/hologram.mdx +++ b/docs/developers/modules/hologram.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Hologram Module @@ -21,6 +21,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Displaying a Hologram ```java @@ -125,6 +129,10 @@ public void resetHologramsExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Displaying a Hologram** ```java @@ -182,6 +190,10 @@ public void resetHologramsExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Displaying a Hologram** ```java diff --git a/docs/developers/modules/limb.mdx b/docs/developers/modules/limb.mdx index 106c6a2dc..c642a9f88 100644 --- a/docs/developers/modules/limb.mdx +++ b/docs/developers/modules/limb.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Limb Module @@ -24,6 +24,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Hide Armor Pieces ```java @@ -122,6 +126,10 @@ public void resetBodyExample(Player viewer, Player target) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Hide Armor Pieces** ```java @@ -178,6 +186,10 @@ public void resetBodyExample(Player viewer, Player target) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Hide Armor Pieces** ```java diff --git a/docs/developers/modules/modsetting.mdx b/docs/developers/modules/modsetting.mdx index a4bf847ad..127b46e36 100644 --- a/docs/developers/modules/modsetting.mdx +++ b/docs/developers/modules/modsetting.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Mod Setting Module @@ -91,6 +91,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + **Disable Lighting Mod** ```java @@ -148,6 +152,10 @@ public void requestInstalledModsExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Disable Lighting Mod** ```java @@ -213,6 +221,10 @@ public void requestInstalledModsExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Disable Lighting Mod** ```java diff --git a/docs/developers/modules/nametag.mdx b/docs/developers/modules/nametag.mdx index 5ca13ec02..081f8acdd 100644 --- a/docs/developers/modules/nametag.mdx +++ b/docs/developers/modules/nametag.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Nametag Module @@ -31,6 +30,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Override a Nametag ```java @@ -101,6 +104,10 @@ public void resetNametagsExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Override a Nametag** ```java @@ -153,6 +160,10 @@ public void resetNametagsExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Override a Nametag** ```java diff --git a/docs/developers/modules/nickhider.mdx b/docs/developers/modules/nickhider.mdx index 3b0cbe7c6..f4ac0496c 100644 --- a/docs/developers/modules/nickhider.mdx +++ b/docs/developers/modules/nickhider.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Nick Hider Module @@ -17,6 +17,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Override a Nick ```java @@ -39,6 +43,10 @@ public void resetNickExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Override a Nick** ```java @@ -64,6 +72,10 @@ public void resetNickExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Override a Nick** ```java diff --git a/docs/developers/modules/notification.mdx b/docs/developers/modules/notification.mdx index f9b506b1b..9732920a8 100644 --- a/docs/developers/modules/notification.mdx +++ b/docs/developers/modules/notification.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Notification Module @@ -26,6 +25,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Displaying a Notification ```java @@ -109,6 +112,10 @@ If this field is left empty (null) it'll display a generic info icon, as display + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Displaying a Notification** ```java @@ -146,6 +153,10 @@ public void resetNotificationsExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Displaying a Notification** ```java diff --git a/docs/developers/modules/packetenrichment.mdx b/docs/developers/modules/packetenrichment.mdx index 5edf9a5a5..3aba0e1cb 100644 --- a/docs/developers/modules/packetenrichment.mdx +++ b/docs/developers/modules/packetenrichment.mdx @@ -1,5 +1,4 @@ -import { Callout } from 'nextra-theme-docs' -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Packet Enrichment Module @@ -22,6 +21,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + The majority of this module is handled through Apollo's [event system](/apollo/developers/events). The following events are related to the packet enrichment module, you can find more information about each event on the [events page](/apollo/developers/events). @@ -35,6 +38,10 @@ The following events are related to the packet enrichment module, you can find m + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + Visit [Apollo Serverbound packets](/apollo/developers/lightweight/protobuf/serverbound-packets) diff --git a/docs/developers/modules/paynow.mdx b/docs/developers/modules/paynow.mdx index ab731fe23..d3f8120dc 100644 --- a/docs/developers/modules/paynow.mdx +++ b/docs/developers/modules/paynow.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # PayNow Module @@ -69,6 +68,10 @@ Explore each integration by cycling through each tab to find the best fit for yo + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + **Display PayNow Embedded Checkout** ```java @@ -102,6 +105,10 @@ public void displayPayNowEmbeddedCheckoutExample(Player viewer, String checkoutT + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Display PayNow Embedded Checkout** @@ -122,6 +129,10 @@ public void displayPayNowEmbeddedCheckoutExample(Player viewer, String checkoutT + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Display PayNow Embedded Checkout** ```java diff --git a/docs/developers/modules/richpresence.mdx b/docs/developers/modules/richpresence.mdx index e6a23f76b..17f40429e 100644 --- a/docs/developers/modules/richpresence.mdx +++ b/docs/developers/modules/richpresence.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Rich Presence Module @@ -35,6 +34,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Override Rich Presence ```java @@ -120,6 +123,10 @@ public void resetServerRichPresenceExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Override Rich Presence** ```java @@ -152,6 +159,10 @@ public void resetServerRichPresenceExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Override Rich Presence** ```java diff --git a/docs/developers/modules/serverlink.mdx b/docs/developers/modules/serverlink.mdx index 081a2dec8..9b6ff87f1 100644 --- a/docs/developers/modules/serverlink.mdx +++ b/docs/developers/modules/serverlink.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Server Link Module @@ -22,6 +22,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Override the Server Link Menu resource ```java @@ -120,6 +124,10 @@ public void resetServerLinksExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Override the Server Link Menu resource** ```java @@ -201,6 +209,10 @@ public void resetServerLinksExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Override the Server Link Menu resource** ```java diff --git a/docs/developers/modules/serverrule.mdx b/docs/developers/modules/serverrule.mdx index 16133b808..0be83b204 100644 --- a/docs/developers/modules/serverrule.mdx +++ b/docs/developers/modules/serverrule.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Server Rule Module @@ -24,6 +23,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Toggle Anti Portal Traps ```java @@ -56,6 +59,10 @@ public void setNametagRenderDistanceExample(int value) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Toggle Anti Portal Traps** ```java @@ -96,6 +103,10 @@ public void setNametagRenderDistanceExample(int value) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Toggle Anti Portal Traps** ```java diff --git a/docs/developers/modules/staffmod.mdx b/docs/developers/modules/staffmod.mdx index 534c75246..92f021013 100644 --- a/docs/developers/modules/staffmod.mdx +++ b/docs/developers/modules/staffmod.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Staff Mod Module @@ -24,6 +24,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Enable Staff Mods ```java @@ -61,6 +65,10 @@ public void disableStaffModsExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + ### Enable Staff Mods ```java @@ -93,6 +101,10 @@ public void disableStaffModsExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Enable Staff Mods** ```java diff --git a/docs/developers/modules/stopwatch.mdx b/docs/developers/modules/stopwatch.mdx index 37f16039e..7d48472be 100644 --- a/docs/developers/modules/stopwatch.mdx +++ b/docs/developers/modules/stopwatch.mdx @@ -1,15 +1,20 @@ import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout } from 'nextra-theme-docs' # Stopwatch Module ## Overview -The stopwatch module allows you to control the stopwatch mod inside Lunar Client. +The stopwatch module allows you to add custom Stopwatch and Timers directly to player's screens in any HUD position you want. -- Ability to fully control a player's stopwatch mod. - - Start a player's stopwatch - - Stop a player's stopwatch - - Reset a player's stopwatch +- Ability to create unique Stopwatch counters for a player. + - Control when it starts, stops, and when it resets. + - Customize the name, display color, and display format. + - Display the stopwatch anywhere on the player's screen. +- Ability to create and control unique Timers for a player. + - Control when the timer starts, stops, and when it resets. + - Customize the duration, name, looping, display format, and display color of the timer. + - Display the timer HUD in the position you'd like on the player's screen. ![Stopwatch Module Example](/modules/stopwatch/overview.gif#center) @@ -26,60 +31,440 @@ Explore each integration by cycling through each tab, to find the best fit for y -### Start Stopwatch + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + +### Adding a Stopwatch + +```java +public void addStopwatchExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + + apolloPlayerOpt.ifPresent(apolloPlayer -> { + this.stopwatchModule.addStopwatch(apolloPlayer, Stopwatch.builder() + .id("parkour-stopwatch") + .name("Parkour") + .resetOnStart(true) + .preventModification(true) + .hideWhenStopped(false) + .displayFormat("mm:ss") + .textColor(ApolloColors.DARK_AQUA) + .hudPosition(HudPosition.builder() + .x(-30) + .y(30) + .build() + ) + .build()); + }); +} +``` + +### Removing a Stopwatch + +```java +public void removeStopwatchExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.removeStopwatch(apolloPlayer, "parkour-stopwatch")); +} +``` + +### Starting a Stopwatch ```java public void startStopwatchExample(Player viewer) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); - apolloPlayerOpt.ifPresent(this.stopwatchModule::startStopwatch); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.startStopwatch(apolloPlayer, "parkour-stopwatch")); } ``` -### Stop Stopwatch +### Stopping a Stopwatch ```java public void stopStopwatchExample(Player viewer) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); - apolloPlayerOpt.ifPresent(this.stopwatchModule::stopStopwatch); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.stopStopwatch(apolloPlayer, "parkour-stopwatch")); } ``` -### Reset Stopwatch +### Resetting a Stopwatch ```java public void resetStopwatchExample(Player viewer) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); - apolloPlayerOpt.ifPresent(this.stopwatchModule::resetStopwatch); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.resetStopwatch(apolloPlayer, "parkour-stopwatch")); } ``` +### Resetting all Stopwatches + +```java +public void resetStopwatchesExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(this.stopwatchModule::resetStopwatches); +} +``` + +### Adding a Timer + +```java +public void addTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + + apolloPlayerOpt.ifPresent(apolloPlayer -> { + this.stopwatchModule.addTimer(apolloPlayer, Timer.builder() + .id("game-timer") + .name("Countdown") + .duration(Duration.ofSeconds(45)) + .loop(false) + .preventModification(true) + .hideWhenStopped(false) + .displayFormat("mm:ss") + .titleText(Component.text("Time's up!", NamedTextColor.RED)) + .inGameNotification(true) + .textColor(ApolloColors.LIGHT_PURPLE) + .hudPosition(HudPosition.builder() + .x(-10) + .y(30) + .build() + ) + .build()); + }); +} +``` + +### Removing a Timer + +```java +public void removeTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.removeTimer(apolloPlayer, "game-timer")); +} +``` + +### Starting a Timer + +```java +public void startTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.startTimer(apolloPlayer, "game-timer")); +} +``` + +### Stopping a Timer + +```java +public void stopTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.stopTimer(apolloPlayer, "game-timer")); +} +``` + +### Resetting a Timer + +```java +public void resetTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.resetTimer(apolloPlayer, "game-timer")); +} +``` + +### Resetting all Timers + +```java +public void resetTimersExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(this.stopwatchModule::resetTimers); +} +``` + +#### `Stopwatch` Options + +`.id(String)` is the unique server-assigned id for the stopwatch. + +```java +.id("parkour-stopwatch") +``` + +`.name(String)` is the HUD display name. + +```java +.name("Parkour") +``` + +`.resetOnStart(boolean)` determines whether to reset elapsed time on each start. + +```java +.resetOnStart(true) +``` + +`.preventModification(boolean)` prevents the user from modifying the options for this stopwatch on the client side. + +```java +.preventModification(true) +``` + +`.hideWhenStopped(boolean)` determines whether the stopwatch is hidden from the HUD when stopped. + +```java +.hideWhenStopped(false) +``` + +`.displayFormat(String)` is a format string (e.g. `"mm:ss"`). Uses the default display format if not provided. + +```java +.displayFormat("mm:ss") +``` + +`.textColor(java.awt.Color)` is the text color. Defaults to white if not provided. See the [colors page](/apollo/developers/utilities/colors) for more. + +```java +.textColor(ApolloColors.DARK_AQUA) +``` + +`.hudPosition(HudPosition)` is the HUD position on the client screen. If not provided, the stopwatch is auto-stacked. + +```java +.hudPosition(HudPosition.builder().x(-30).y(30).build()) +``` + +#### `Timer` Options + +`.id(String)` is the unique server-assigned id for the timer. + +```java +.id("game-timer") +``` + +`.name(String)` is the HUD display name. + +```java +.name("Countdown") +``` + +`.duration(java.time.Duration)` is the countdown duration. + +```java +.duration(Duration.ofSeconds(45)) +``` + +`.loop(boolean)` determines whether the timer restarts automatically when finished. + +```java +.loop(false) +``` + +`.preventModification(boolean)` prevents the user from modifying the options for this timer on the client side. + +```java +.preventModification(true) +``` + +`.hideWhenStopped(boolean)` determines whether the timer is hidden from the HUD when stopped. + +```java +.hideWhenStopped(false) +``` + +`.displayFormat(String)` is a format string. Uses the default display format if not provided. + +```java +.displayFormat("mm:ss") +``` + +`.titleText(Component)` is the on-screen title shown when the timer finishes. Set to `null` or omit to skip. See the [chat components](https://docs.advntr.dev/text.html) page for more. + +```java +.titleText(Component.text("Time's up!", NamedTextColor.RED)) +``` + +`.inGameNotification(boolean)` determines whether to show an in-game popup when the timer finishes. + +```java +.inGameNotification(true) +``` + +`.textColor(java.awt.Color)` is the text color. Defaults to white if not provided. See the [colors page](/apollo/developers/utilities/colors) for more. + +```java +.textColor(ApolloColors.LIGHT_PURPLE) +``` + +`.hudPosition(HudPosition)` is the HUD position on the client screen. If not provided, the timer is auto-stacked. + +```java +.hudPosition(HudPosition.builder().x(-10).y(30).build()) +``` + -**Start Stopwatch** +### Adding a Stopwatch + + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + +```java +public void addStopwatchExample(Player viewer) { + AddStopwatchMessage message = AddStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .setName("Parkour") + .setResetOnStart(true) + .setPreventModification(true) + .setHideWhenStopped(false) + .setDisplayFormat("mm:ss") + .setTextColor(ProtobufUtil.createColorProto(new Color(0, 170, 170))) + .setHudPosition(HudPosition.newBuilder() + .setX(-30) + .setY(30) + .build() + ) + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +### Removing a Stopwatch + +```java +public void removeStopwatchExample(Player viewer) { + RemoveStopwatchMessage message = RemoveStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +### Starting a Stopwatch ```java public void startStopwatchExample(Player viewer) { - StartStopwatchMessage message = StartStopwatchMessage.getDefaultInstance(); + StartStopwatchMessage message = StartStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .build(); + ProtobufPacketUtil.sendPacket(viewer, message); } ``` -**Stop Stopwatch** +### Stopping a Stopwatch ```java public void stopStopwatchExample(Player viewer) { - StopStopwatchMessage message = StopStopwatchMessage.getDefaultInstance(); + StopStopwatchMessage message = StopStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .build(); + ProtobufPacketUtil.sendPacket(viewer, message); } ``` -**Reset Stopwatch** +### Resetting a Stopwatch ```java public void resetStopwatchExample(Player viewer) { - ResetStopwatchMessage message = ResetStopwatchMessage.getDefaultInstance(); + ResetStopwatchMessage message = ResetStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +### Resetting all Stopwatches + +```java +public void resetStopwatchesExample(Player viewer) { + ResetStopwatchesMessage message = ResetStopwatchesMessage.getDefaultInstance(); + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +### Adding a Timer + +```java +public void addTimerExample(Player viewer) { + AddTimerMessage message = AddTimerMessage.newBuilder() + .setId("game-timer") + .setName("Countdown") + .setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(45))) + .setLoop(false) + .setPreventModification(true) + .setHideWhenStopped(false) + .setDisplayFormat("mm:ss") + .setTitleTextAdventureJsonLines(AdventureUtil.toJson( + Component.text("Time's up!", NamedTextColor.RED) + )) + .setInGameNotification(true) + .setTextColor(ProtobufUtil.createColorProto(new Color(255, 85, 255))) + .setHudPosition(HudPosition.newBuilder() + .setX(-10) + .setY(30) + .build() + ) + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +### Removing a Timer + +```java +public void removeTimerExample(Player viewer) { + RemoveTimerMessage message = RemoveTimerMessage.newBuilder() + .setId("game-timer") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +### Starting a Timer + +```java +public void startTimerExample(Player viewer) { + StartTimerMessage message = StartTimerMessage.newBuilder() + .setId("game-timer") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +### Stopping a Timer + +```java +public void stopTimerExample(Player viewer) { + StopTimerMessage message = StopTimerMessage.newBuilder() + .setId("game-timer") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +### Resetting a Timer + +```java +public void resetTimerExample(Player viewer) { + ResetTimerMessage message = ResetTimerMessage.newBuilder() + .setId("game-timer") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +### Resetting all Timers + +```java +public void resetTimersExample(Player viewer) { + ResetTimersMessage message = ResetTimersMessage.getDefaultInstance(); ProtobufPacketUtil.sendPacket(viewer, message); } ``` @@ -88,34 +473,174 @@ public void resetStopwatchExample(Player viewer) { -**Start Stopwatch** + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + +### Adding a Stopwatch + +```java +public void addStopwatchExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.AddStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); + message.addProperty("name", "Parkour"); + message.addProperty("reset_on_start", true); + message.addProperty("prevent_modification", true); + message.addProperty("hide_when_stopped", false); + message.addProperty("display_format", "mm:ss"); + message.add("text_color", JsonUtil.createColorObject(new Color(0, 170, 170))); + + JsonObject stopwatchPosition = new JsonObject(); + stopwatchPosition.addProperty("x", -30); + stopwatchPosition.addProperty("y", 30); + message.add("hud_position", stopwatchPosition); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +### Removing a Stopwatch + +```java +public void removeStopwatchExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.RemoveStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +### Starting a Stopwatch ```java public void startStopwatchExample(Player viewer) { JsonObject message = new JsonObject(); message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.StartStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); JsonPacketUtil.sendPacket(viewer, message); } ``` -**Stop Stopwatch** +### Stopping a Stopwatch ```java public void stopStopwatchExample(Player viewer) { JsonObject message = new JsonObject(); message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.StopStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); JsonPacketUtil.sendPacket(viewer, message); } ``` -**Reset Stopwatch** +### Resetting a Stopwatch ```java public void resetStopwatchExample(Player viewer) { JsonObject message = new JsonObject(); message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.ResetStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +### Resetting all Stopwatches + +```java +public void resetStopwatchesExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.ResetStopwatchesMessage"); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +### Adding a Timer + +```java +public void addTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.AddTimerMessage"); + message.addProperty("id", "game-timer"); + message.addProperty("name", "Countdown"); + message.addProperty("duration", JsonUtil.createDurationObject(Duration.ofSeconds(45))); + message.addProperty("loop", false); + message.addProperty("prevent_modification", true); + message.addProperty("hide_when_stopped", false); + message.addProperty("display_format", "mm:ss"); + message.addProperty("title_text_adventure_json_lines", AdventureUtil.toJson( + Component.text("Time's up!", NamedTextColor.RED) + )); + message.addProperty("in_game_notification", true); + message.add("text_color", JsonUtil.createColorObject(new Color(255, 85, 255))); + + JsonObject timerPosition = new JsonObject(); + timerPosition.addProperty("x", -10); + timerPosition.addProperty("y", 30); + message.add("hud_position", timerPosition); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +### Removing a Timer + +```java +public void removeTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.RemoveTimerMessage"); + message.addProperty("id", "game-timer"); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +### Starting a Timer + +```java +public void startTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.StartTimerMessage"); + message.addProperty("id", "game-timer"); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +### Stopping a Timer + +```java +public void stopTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.StopTimerMessage"); + message.addProperty("id", "game-timer"); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +### Resetting a Timer + +```java +public void resetTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.ResetTimerMessage"); + message.addProperty("id", "game-timer"); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +### Resetting all Timers + +```java +public void resetTimersExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.ResetTimersMessage"); JsonPacketUtil.sendPacket(viewer, message); } diff --git a/docs/developers/modules/team.mdx b/docs/developers/modules/team.mdx index e587e3155..0b80896a3 100644 --- a/docs/developers/modules/team.mdx +++ b/docs/developers/modules/team.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Team Module @@ -26,6 +26,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ```java private final Map teamsByTeamId = new ConcurrentHashMap<>(); private final Map teamsByPlayerUuid = new ConcurrentHashMap<>(); @@ -279,6 +283,10 @@ Custom colors can be created from any RGB values using `new Color(int red, int g + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + ```java private final Map teamsByTeamId = new ConcurrentHashMap<>(); private final Map teamsByPlayerUuid = new ConcurrentHashMap<>(); @@ -463,6 +471,10 @@ public class Team { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + ```java private final Map teamsByTeamId = new ConcurrentHashMap<>(); private final Map teamsByPlayerUuid = new ConcurrentHashMap<>(); diff --git a/docs/developers/modules/tebex.mdx b/docs/developers/modules/tebex.mdx index c622b8804..4a736e6fa 100644 --- a/docs/developers/modules/tebex.mdx +++ b/docs/developers/modules/tebex.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Tebex Module @@ -79,6 +78,10 @@ Explore each integration by cycling through each tab to find the best fit for yo + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + **Display Tebex Embedded Checkout** ```java @@ -112,6 +115,10 @@ public void displayTebexEmbeddedCheckoutExample(Player viewer, String basketIden + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Display Tebex Embedded Checkout** @@ -136,6 +143,10 @@ public void displayTebexEmbeddedCheckoutExample(Player viewer, String basketIden + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Display Tebex Embedded Checkout** ```java diff --git a/docs/developers/modules/title.mdx b/docs/developers/modules/title.mdx index f8b71cff5..4d8bb03db 100644 --- a/docs/developers/modules/title.mdx +++ b/docs/developers/modules/title.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Title Module @@ -19,6 +19,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Displaying a Title ```java @@ -141,6 +145,10 @@ public void setClearTitleOnServerSwitch(boolean value) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Displaying a Title** ```java @@ -214,6 +222,10 @@ public void setClearTitleOnServerSwitch(boolean value) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Displaying a Title** ```java diff --git a/docs/developers/modules/tntcountdown.mdx b/docs/developers/modules/tntcountdown.mdx index c1d9ebfee..3e527f4fa 100644 --- a/docs/developers/modules/tntcountdown.mdx +++ b/docs/developers/modules/tntcountdown.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # TNT Countdown Module @@ -22,6 +21,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Set TNT Countdown Ticks Option ```java @@ -91,6 +94,10 @@ private void onTntSpawn(EntitySpawnEvent event) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Set TNT Countdown Ticks Option** ```java @@ -167,6 +174,10 @@ private void onTntSpawn(EntitySpawnEvent event) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Create TNT Countdown Message** ```java diff --git a/docs/developers/modules/transfer.mdx b/docs/developers/modules/transfer.mdx index 51d252ce0..b2ea55b07 100644 --- a/docs/developers/modules/transfer.mdx +++ b/docs/developers/modules/transfer.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Transfer Module @@ -75,6 +74,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Transfer Player ```java @@ -162,6 +165,10 @@ public void pingExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Transfer Player** ```java @@ -246,6 +253,10 @@ public void pingExample(Player player) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Transfer Player** ```java diff --git a/docs/developers/modules/vignette.mdx b/docs/developers/modules/vignette.mdx index cda4e17b9..b39b41837 100644 --- a/docs/developers/modules/vignette.mdx +++ b/docs/developers/modules/vignette.mdx @@ -1,4 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Vignette Module @@ -17,6 +17,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Display Vignette ```java @@ -60,6 +64,10 @@ public void resetVignetteExample(Player viewer) { + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + **Display Vignette** ```java @@ -86,6 +94,10 @@ public void resetVignetteExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + **Display Vignette** ```java diff --git a/docs/developers/modules/waypoint.mdx b/docs/developers/modules/waypoint.mdx index 57cedba46..6a541b601 100644 --- a/docs/developers/modules/waypoint.mdx +++ b/docs/developers/modules/waypoint.mdx @@ -1,5 +1,4 @@ -import { Tab, Tabs } from 'nextra-theme-docs' -import { Callout } from 'nextra-theme-docs' +import { Callout, Tab, Tabs } from 'nextra-theme-docs' # Waypoint Module @@ -29,6 +28,10 @@ Explore each integration by cycling through each tab, to find the best fit for y + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + ### Displaying a Waypoint ```java @@ -144,6 +147,10 @@ Custom colors can be created from any RGB values using `new Color(int red, int g + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + Make sure the server is sending the world name to the client as show in the [Player Detection](/apollo/developers/lightweight/protobuf/player-detection) example. @@ -189,6 +196,10 @@ public void resetWaypointsExample(Player viewer) { + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + Make sure the server is sending the world name to the client as show in the [Player Detection](/apollo/developers/lightweight/json/player-detection) example. diff --git a/docs/developers/private-modules.mdx b/docs/developers/private-modules.mdx new file mode 100644 index 000000000..bf57d20fe --- /dev/null +++ b/docs/developers/private-modules.mdx @@ -0,0 +1,18 @@ +# Private Modules + +Apollo has private modules, that are access restricted extensions of the API. + +These modules are only available on pre-approved servers and will not work on servers that have not received approval. These private modules are still subject to our [Acceptable Use](/apollo/acceptable-use) guidelines. + +### How to Request Access + +We are not currently granting broad access to private modules. However, if you have a specific use-case in mind, you may request access by contacting **apollo@lunarclient.com**. + +Please include: +- Your server name & domain +- A description of your use-case +- Any relevant code snippets (if applicable) + +### Available Modules + +🔗 [Cosmetic](/apollo/developers/private-modules/cosmetic)
diff --git a/docs/developers/private-modules/_meta.json b/docs/developers/private-modules/_meta.json new file mode 100644 index 000000000..482e70cb6 --- /dev/null +++ b/docs/developers/private-modules/_meta.json @@ -0,0 +1,3 @@ +{ + "cosmetic": "Cosmetic" +} diff --git a/docs/developers/private-modules/cosmetic.mdx b/docs/developers/private-modules/cosmetic.mdx new file mode 100644 index 000000000..6b18d76fa --- /dev/null +++ b/docs/developers/private-modules/cosmetic.mdx @@ -0,0 +1,483 @@ +import { Callout, Tab, Tabs } from 'nextra-theme-docs' + +# Cosmetic Module + +## Overview + +The cosmetic module allows you to interact and utilize all the various cosmetic types found in Lunar Client. + +- Add wearable cosmetics to NPC entities + - Grants the ability to put any cosmetic on an NPC + - Uniquely customize various cosmetic options +- Add sprays to any location + - Grants the ability to put any spray on a block + - Customize the rotation, direction options, and duration + + + The cosmetic module is a **private module** and not publicly available. + To use this module, you must be granted access by the Lunar Client team. [Learn More](/apollo/developers/private-modules) + + +## Integration + +### Sample Code + +Explore each integration by cycling through each tab to find the best fit for your requirements and needs. + + + NPC UUIDs **must** have their least significant bits set to `0` (i.e. `new UUID(msb, 0L)`). + Lunar Client rejects cosmetic packets for UUIDs that do not follow this format. + + + + + + +**Apollo API examples.** See [General](/apollo/developers/general) for common patterns and helpers. + + +**Equip cosmetics on an NPC** + +```java +public void equipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + List cosmetics = Lists.newArrayList( + Cosmetic.builder() + .id(434) + .build(), + Cosmetic.builder() + .id(3654) + .build(), + Cosmetic.builder() + .id(5095) + .options(PetOptions.builder() + .flipShoulder(true) + .build()) + .build(), + Cosmetic.builder() + .id(3) + .options(CloakOptions.builder() + .useClothPhysics(true) + .build()) + .build(), + Cosmetic.builder() + .id(3977) + .build() + ); + + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.cosmeticModule.equipNpcCosmetics(apolloPlayer, npcUuid, cosmetics)); +} +``` + +**Unequip cosmetics from an NPC** + +```java +public void unequipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> { + List cosmeticIds = Lists.newArrayList(434, 3654, 5095, 3, 3977); + this.cosmeticModule.unequipNpcCosmetics(apolloPlayer, npcUuid, cosmeticIds); + }); +} +``` + +**Reset NPC cosmetics** + +```java +public void resetNpcCosmeticsExample(UUID npcUuid) { + this.cosmeticModule.resetNpcCosmetics(Recipients.ofEveryone(), npcUuid); +} +``` + +**Display spray** + +```java +public void displaySprayExample(Player viewer, int sprayId) { + Block block = viewer.getTargetBlockExact(10); + if (block == null) { + return; + } + + Material material = block.getType(); + if (material.isAir() || !material.isSolid()) { + return; + } + + ApolloBlockLocation location = ApolloBlockLocation.builder() + .world(block.getWorld().getName()) + .x(block.getX()) + .y(block.getY()) + .z(block.getZ()) + .build(); + + Spray spray = Spray.builder() + .sprayId(sprayId) + .location(location) + .facing(Direction.UP) + .rotation(0f) + .duration(Duration.ofSeconds(60)) + .build(); + + this.cosmeticModule.displaySpray(Recipients.ofEveryone(), spray); +} +``` + + + **Sprays** are client-local and validated against loaded chunks. When the backing chunk unloads, sprays are removed and **do not** come back until the server sends a display spray packet again. + + +**Remove sprays** + +```java +public void removeSprayExample(int sprayId) { + this.cosmeticModule.removeSpray(Recipients.ofEveryone(), sprayId); +} +``` + +**Reset sprays** + +```java +public void resetSpraysExample() { + this.cosmeticModule.resetSprays(Recipients.ofEveryone()); +} +``` + +### `Cosmetic` Options + +`.id(int)` is the Lunar Client cosmetic id for this entry. + +```java +.id(434) +``` + +`.options(CosmeticOptions)` is optional. Pass one concrete subtype such as `HatOptions`, `CloakOptions`, `PetOptions`, or `BodyOptions` (omit or pass `null` for id-only cosmetics). + +**`HatOptions`** + +```java +.options(HatOptions.builder() + .showOverHelmet(true) + .showOverSkinLayer(true) + .heightOffset(0.05f) + .build()) +``` + +**`CloakOptions`** + +```java +.options(CloakOptions.builder() + .useClothPhysics(true) + .build()) +``` + +**`PetOptions`** + +```java +.options(PetOptions.builder() + .flipShoulder(true) + .build()) +``` + +**`BodyOptions`** + +```java +.options(BodyOptions.builder() + .showOverChestplate(true) + .showOverLeggings(true) + .showOverBoots(true) + .build()) +``` + +### `Spray` Options + +`.sprayId(int)` is the Lunar Client spray cosmetic id. + +```java +.sprayId(sprayId) +``` + +`.location(ApolloBlockLocation)` is the block the spray is placed on. See the [locations page](/apollo/developers/utilities/locations) for more. + +```java +.location(ApolloBlockLocation.builder() + .world(block.getWorld().getName()) + .x(block.getX()) + .y(block.getY()) + .z(block.getZ()) + .build()) +``` + +`.facing(Direction)` selects which face of the block the spray uses. + +```java +.facing(Direction.UP) +``` + +`.rotation(float)` is rotation in degrees on the client (default `0f`). + +```java +.rotation(0f) +``` + +`.duration(java.time.Duration)` is how long the spray stays visible. See the [java.time.Duration Javadocs](https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html) for more. + +```java +.duration(Duration.ofSeconds(60)) +``` + + + + + + +**Lightweight Protobuf examples.** See [Lightweight Protobuf](/apollo/developers/lightweight/protobuf) for setup. + + +**Equip cosmetics on an NPC** + +```java +public void equipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + List cosmetics = Lists.newArrayList( + Cosmetic.newBuilder() + .setId(434) + .build(), + Cosmetic.newBuilder() + .setId(3654) + .build(), + Cosmetic.newBuilder() + .setId(5095) + .setPetOptions(PetOptions.newBuilder() + .setFlipShoulder(true) + .build()) + .build(), + Cosmetic.newBuilder() + .setId(3) + .setCloakOptions(CloakOptions.newBuilder() + .setUseClothPhysics(true) + .build()) + .build(), + Cosmetic.newBuilder() + .setId(3977) + .build() + ); + + EquipNpcCosmeticsMessage message = EquipNpcCosmeticsMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .addAllCosmetics(cosmetics) + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + +**Unequip cosmetics from an NPC** + +```java +public void unequipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + List cosmeticIds = Lists.newArrayList(434, 3654, 5095, 3, 3977); + + UnequipNpcCosmeticsMessage message = UnequipNpcCosmeticsMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .addAllCosmeticIds(cosmeticIds) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); +} +``` + +**Reset NPC cosmetics** + +```java +public void resetNpcCosmeticsExample(Player viewer, UUID npcUuid) { + ResetNpcCosmeticsMessage message = ResetNpcCosmeticsMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); +} +``` + +**Display spray** + +```java +public void displaySprayExample(Player viewer, int sprayId) { + Block block = viewer.getTargetBlockExact(10); + if (block == null) { + return; + } + + Material material = block.getType(); + if (material.isAir() || !material.isSolid()) { + return; + } + + DisplaySprayMessage message = DisplaySprayMessage.newBuilder() + .setSprayId(sprayId) + .setLocation(ProtobufUtil.createBlockLocationProto(block.getLocation())) + .setFacing(Direction.DIRECTION_UP) + .setRotation(0f) + .setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(60))) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); +} +``` + + + **Sprays** are client-local and validated against loaded chunks. When the backing chunk unloads, sprays are removed and **do not** come back until the server sends a display spray packet again. + + +**Remove sprays** + +```java +public void removeSprayExample(int sprayId) { + RemoveSprayMessage message = RemoveSprayMessage.newBuilder() + .setSprayId(sprayId) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); +} +``` + +**Reset sprays** + +```java +public void resetSpraysExample() { + ProtobufPacketUtil.broadcastPacket(ResetSpraysMessage.getDefaultInstance()); +} +``` + + + + + +**Lightweight JSON examples.** See [Lightweight JSON](/apollo/developers/lightweight/json) for setup. + + +**Equip cosmetics on an NPC** + +```java +public void equipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + JsonArray cosmeticsArray = new JsonArray(); + + JsonObject first = new JsonObject(); + first.addProperty("id", 434); + cosmeticsArray.add(first); + + JsonObject second = new JsonObject(); + second.addProperty("id", 3654); + cosmeticsArray.add(second); + + JsonObject third = new JsonObject(); + third.addProperty("id", 5095); + JsonObject petOptions = new JsonObject(); + petOptions.addProperty("flip_shoulder", true); + third.add("pet_options", petOptions); + cosmeticsArray.add(third); + + JsonObject fourth = new JsonObject(); + fourth.addProperty("id", 3); + JsonObject cloakOptions = new JsonObject(); + cloakOptions.addProperty("use_cloth_physics", true); + fourth.add("cloak_options", cloakOptions); + cosmeticsArray.add(fourth); + + JsonObject fifth = new JsonObject(); + fifth.addProperty("id", 3977); + cosmeticsArray.add(fifth); + + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.EquipNpcCosmeticsMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + message.add("cosmetics", cosmeticsArray); + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + +**Unequip cosmetics from an NPC** + +```java +public void unequipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + List cosmeticIds = Lists.newArrayList(434, 3654, 5095, 3, 3977); + + JsonArray cosmeticIdsArray = new JsonArray(); + cosmeticIds.forEach(cosmeticIdsArray::add); + + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.UnequipNpcCosmeticsMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + message.add("cosmetic_ids", cosmeticIdsArray); + + JsonPacketUtil.broadcastPacket(message); +} +``` + +**Reset NPC cosmetics** + +```java +public void resetNpcCosmeticsExample(Player viewer, UUID npcUuid) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.ResetNpcCosmeticsMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + + JsonPacketUtil.broadcastPacket(message); +} +``` + +**Display spray** + +```java +public void displaySprayExample(Player viewer, int sprayId) { + Block block = viewer.getTargetBlockExact(10); + if (block == null) { + return; + } + + Material material = block.getType(); + if (material.isAir() || !material.isSolid()) { + return; + } + + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.DisplaySprayMessage"); + message.addProperty("spray_id", sprayId); + message.add("location", JsonUtil.createBlockLocationObject(block.getLocation())); + message.addProperty("facing", "UP"); + message.addProperty("rotation", 0f); + message.addProperty("duration", JsonUtil.createDurationObject(Duration.ofSeconds(60))); + + JsonPacketUtil.broadcastPacket(message); +} +``` + + + **Sprays** are client-local and validated against loaded chunks. When the backing chunk unloads, sprays are removed and **do not** come back until the server sends a display spray packet again. + + +**Remove sprays** + +```java +public void removeSprayExample(int sprayId) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.RemoveSprayMessage"); + message.addProperty("spray_id", sprayId); + + JsonPacketUtil.broadcastPacket(message); +} +``` + +**Reset sprays** + +```java +public void resetSpraysExample() { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.ResetSpraysMessage"); + + JsonPacketUtil.broadcastPacket(message); +} +``` + + + + diff --git a/docs/developers/utilities/_meta.json b/docs/developers/utilities/_meta.json new file mode 100644 index 000000000..9007de2bd --- /dev/null +++ b/docs/developers/utilities/_meta.json @@ -0,0 +1,8 @@ +{ + "colors": "Colors", + "cuboids": "Cuboids", + "icons": "Icons", + "locations": "Locations", + "adventure": "Adventure", + "platform-utilities": "Platform Utilities" +} diff --git a/docs/developers/adventure.mdx b/docs/developers/utilities/adventure.mdx similarity index 100% rename from docs/developers/adventure.mdx rename to docs/developers/utilities/adventure.mdx diff --git a/docs/developers/utilities/icons.mdx b/docs/developers/utilities/icons.mdx index 449151b7c..1b1b488a0 100644 --- a/docs/developers/utilities/icons.mdx +++ b/docs/developers/utilities/icons.mdx @@ -42,6 +42,14 @@ public final class ItemStackIcon extends Icon { */ int customModelData; + /** + * Returns the icon {@link Profile}. + * + * @return the icon profile + * @since 1.2.6 + */ + @Nullable Profile profile; + } ``` @@ -59,6 +67,63 @@ public static ItemStackIcon itemStackNameIconExample() { .itemName("ENDER_PEARL") .build(); } + +public static ItemStackIcon itemStackPlayerTextureIconExample() { + return ItemStackIcon.builder() + .itemName("PLAYER_HEAD") // use "skull" for legacy with customModelData set to 3 + .profile(Profile.builder() + .id(UUID.fromString("f17627d8-1a97-487b-92ea-c04f413394bd")) + .texture("e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQ4MjUwNWJjZjNiYTU5YzJiZTdlMmQzNmY0ZTJiZGE4MzZmMmZkMTk0YjYyMTJhMmExYzRiNGEyYTQ3MWUifX19") + .signature("") + .build()) + .build(); +} +``` + +## `Profile` Builder + +The `Profile` builder is used to apply a player skin texture to a player head `ItemStackIcon`. + +```java +public final class Profile { + + /** + * Returns the profile {@link UUID} id. + * + * @return the profile id + * @since 1.2.6 + */ + @Nullable UUID id; + + /** + * Returns the profile {@link String} texture. + * + * @return the profile texture + * @since 1.2.6 + */ + String texture; + + /** + * Returns the profile {@link String} signature. + * + * @return the profile signature + * @since 1.2.6 + */ + String signature; + +} +``` + +### Sample Code + +```java +public static Profile profileExample() { + return Profile.builder() + .id(UUID.fromString("f17627d8-1a97-487b-92ea-c04f413394bd")) + .texture("e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQ4MjUwNWJjZjNiYTU5YzJiZTdlMmQzNmY0ZTJiZGE4MzZmMmZkMTk0YjYyMTJhMmExYzRiNGEyYTQ3MWUifX19") + .signature("") + .build(); +} ``` ## `ResourceLocationIcon` Builder diff --git a/docs/developers/platform-utilities.mdx b/docs/developers/utilities/platform-utilities.mdx similarity index 100% rename from docs/developers/platform-utilities.mdx rename to docs/developers/utilities/platform-utilities.mdx diff --git a/docs/introduction.mdx b/docs/introduction.mdx index 3c167a99c..3dad62b75 100644 --- a/docs/introduction.mdx +++ b/docs/introduction.mdx @@ -13,7 +13,7 @@ We'll cover the basics of Apollo, from changing the configuration file to integr Inside the module breakdowns, for developers, we'll include code snippets along with photos, videos, and GIFs related to the module to ensure you get the most out of Apollo. - Need just the essentials? [Lightweight](/apollo/developers/lightweight/introduction) allows you to use Apollo features without the full plugin! + Need just the essentials? [Lightweight](/apollo/developers/lightweight) allows you to use Apollo features without the full plugin! ## Useful Links diff --git a/docs/server-owners.mdx b/docs/server-owners.mdx new file mode 100644 index 000000000..33bcb94ab --- /dev/null +++ b/docs/server-owners.mdx @@ -0,0 +1,9 @@ +# Server Owners + +Use this section if you run a server and want Apollo’s **configuration** and **commands**, without writing custom plugin code. + +## Topics + +🔗 [Configuration](/apollo/server-owners/config) `config.yml`, `mods.yml`, and examples.
+🔗 [Commands](/apollo/server-owners/commands) operator commands exposed by the Apollo plugin.
+🔗 [FAQ](/apollo/server-owners/faq) downloads, setup, and common questions. diff --git a/docs/server-owners/config.mdx b/docs/server-owners/config.mdx index 4fdd7459a..e9952fad7 100644 --- a/docs/server-owners/config.mdx +++ b/docs/server-owners/config.mdx @@ -8,14 +8,14 @@ The Apollo configurations are split into two parts: The settings for the `config.yml` are generated with examples and comments, which you can modify to your liking. -The `mods.yml` is not generated with its settings or comments, which will require you to refer to each mods documentation ([example](/apollo/developers/mods/2ditems)) for the keys and value(s) you can set. +The `mods.yml` is not generated with its settings or comments, which will require you to refer to each mod’s documentation under [Mod Options](/apollo/developers/mods/2ditems) for the keys and value(s) you can set. The settings in the `mods.yml` will override the mod options for players on Lunar Client while they are on your server and revert them back after they leave. ## Mod Configuration Example The following example is not a complete list of mod settings or guaranteed to be up-to-date. - You should refer to the mods documentation ([example](/apollo/developers/mods/2ditems)) for the latest mod settings. + You should refer to the [Mod Settings](/apollo/developers/mods/2ditems) documentation for the latest mod settings. ```yaml diff --git a/docs/server-owners/faq.mdx b/docs/server-owners/faq.mdx index db322deb8..05d951bf7 100644 --- a/docs/server-owners/faq.mdx +++ b/docs/server-owners/faq.mdx @@ -1,63 +1,27 @@ -# FAQs - -
-What is the difference between the legacy Lunar Client API and Apollo API? - -The legacy API was hastily created specifically for features Lunar Network needed. -Whereas, Apollo was created with the intention to add more general functionality for all types of servers and gamemodes. - -
- -
-Can I continue to use the legacy Lunar Client API? - -Yes, as of now you're able to keep using the legacy Lunar Client API. -However, we no longer recommend the legacy API, and you won't be able to use the newest features added to Apollo. -We will eventually discontinue support for the legacy API, so it's highly recommended updating to use Apollo. - -
- -
-Why should I use Apollo? What benefits does Apollo have for my server? +import { Callout } from 'nextra-theme-docs' -Apollo offers you the chance to implement features that aren't possible in your current version of Minecraft and provide quality of life features to your players. -In addition, we have plans to add multiple server-sided improvements such as chuck caching, client-side rendering, and much more. - -
- -
-What Minecraft Versions does Apollo support? - -Apollo is currently set up to work with all versions that Lunar Client supports, with a single installation. - -
- -
-Where can I download a compiled version of Apollo? - -You can download a compiled version of Apollo and the official Apollo implementation plugin on the [downloads page](https://www.lunarclient.dev/apollo/downloads). - -
+# FAQs -
-Can I use Apollo without any programming knowledge? + +Have a question not currently covered here? Join our [Developer Discord](lunarclient.dev/discord) to ask! + -Yes! -You can [download](https://www.lunarclient.dev/apollo/downloads) the Apollo implementation plugin, and drop it into your plugins' folder. -The Apollo implementation plugin has a prebuilt configuration file, which can be edited very easily. +## Where can I download Apollo? +You can find direct downloads to Apollo on our official [download page](https://www.lunarclient.dev/apollo/downloads). -
+## Why should I use Apollo? What benefits does it provide? +Apollo unlocks features that go beyond what standard Minecraft supports, while also adding quality-of-life improvements for your players. -
-Can I use Apollo for free? +## What Minecraft versions does Apollo support? +Apollo has been fully tested and confirmed to work on all versions supported by Lunar Client. -Yes! Lunar Client has no intentions of ever charging for usage of Apollo. +## Can I use Apollo without programming experience? +Yes! You can download the official Apollo plugin and then place it into your `plugins` folder. It includes a pre-built configuration file that can be easily modified without developer experience required. -
+That said, there is far more potential when you have a developer work on custom integrations. -
-How can I get my server name to appear on the Lunar Client friend menu, launcher and other places? +## How can I get my server listed on Discord & Lunar Client rich presence placements? +To have your server appear in places like the friend menu, friend activity, and Discord rich presence, you can submit your server to the Lunar Client [ServerMappings](https://www.lunarclient.dev/server-mappings/introduction). -As of now, you can submit your server to the [Lunar Client ServerMappings](https://github.com/LunarClient/ServerMappings) repository on GitHub, where you'll need to provide information and branding around your server. +This requires you to provide relevant information and branding for your server, so we're able to promote your server accordingly. -
diff --git a/example/bukkit/api/build.gradle.kts b/example/bukkit/api/build.gradle.kts index 901f2477c..60846287e 100644 --- a/example/bukkit/api/build.gradle.kts +++ b/example/bukkit/api/build.gradle.kts @@ -15,3 +15,9 @@ dependencies { compileOnly(libs.folia) implementation(project(":example:bukkit:apollo-example-bukkit-common")) } + +tasks.shadowJar { + manifest { + attributes["paperweight-mappings-namespace"] = "mojang+yarn" + } +} 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 8575bc784..5b26d6f80 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 @@ -37,6 +37,7 @@ import com.lunarclient.apollo.example.api.module.ColoredFireApiExample; import com.lunarclient.apollo.example.api.module.CombatApiExample; import com.lunarclient.apollo.example.api.module.CooldownApiExample; +import com.lunarclient.apollo.example.api.module.CosmeticApiExample; import com.lunarclient.apollo.example.api.module.EntityApiExample; import com.lunarclient.apollo.example.api.module.GlowApiExample; import com.lunarclient.apollo.example.api.module.HologramApiExample; @@ -85,6 +86,7 @@ public void registerModuleExamples() { this.setBeamExample(new BeamApiExample()); this.setBorderExample(new BorderApiExample()); this.setChatExample(new ChatApiExample()); + this.setCosmeticExample(new CosmeticApiExample()); this.setColoredFireExample(new ColoredFireApiExample()); this.setCombatExample(new CombatApiExample()); this.setCooldownExample(new CooldownApiExample()); diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/debug/impl/ModSettingsTest.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/debug/impl/ModSettingsTest.java index 8601a6a23..1f6d15687 100644 --- a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/debug/impl/ModSettingsTest.java +++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/debug/impl/ModSettingsTest.java @@ -165,7 +165,7 @@ private void changeOptions(ApolloPlayer player) { } private void changeOption(ApolloPlayer player, Option option, Object value) { - this.modSettingModule.getOptions().set(option, value); + this.modSettingModule.getOptions().set(player, option, value); this.changes.computeIfAbsent(player.getUniqueId(), k -> new ArrayList<>()) .add(new OptionChange(option.getKey(), value)); 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 0913c1e29..0949ac904 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 @@ -30,8 +30,12 @@ import com.lunarclient.apollo.example.ApolloExamplePlugin; import com.lunarclient.apollo.example.api.module.TeamApiExample; import com.lunarclient.apollo.example.module.impl.CooldownExample; +import com.lunarclient.apollo.example.module.impl.CosmeticExample; import com.lunarclient.apollo.example.module.impl.ServerLinkExample; +import com.lunarclient.apollo.example.nms.NpcManager; +import com.lunarclient.apollo.example.nms.PlayerNpc; import com.lunarclient.apollo.player.ApolloPlayer; +import java.util.Optional; import org.bukkit.entity.Player; public class ApolloPlayerApiListener implements ApolloListener { @@ -60,17 +64,23 @@ private void onApolloRegister(ApolloRegisterPlayerEvent event) { this.example.getBeamExample().displayBeamExample(player); this.example.getBorderExample().displayBorderExample(player); - this.example.getNametagExample().overrideNametagExample(player); this.example.getWaypointExample().displayWaypointExample(player); CooldownExample cooldownExample = this.example.getCooldownExample(); cooldownExample.displayCooldownItemExample(player); cooldownExample.displayCooldownWithStyleExample(player); + cooldownExample.displayCooldownWithPlayerTextureExample(player); cooldownExample.displayCooldownResourceExample(player); ServerLinkExample serverLinkExample = this.example.getServerLinkExample(); serverLinkExample.overrideServerLinkResourceExample(player); serverLinkExample.addServerLinkExample(player); + + CosmeticExample cosmeticExample = this.example.getCosmeticExample(); + NpcManager npcManager = this.example.getNpcManager(); + + Optional npc = npcManager.findByName("Apollo"); + npc.ifPresent(playerNpc -> cosmeticExample.equipNpcCosmeticsExample(player, playerNpc.getUuid())); } } diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CooldownApiExample.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CooldownApiExample.java index 121eb27e1..99eb25961 100644 --- a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CooldownApiExample.java +++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CooldownApiExample.java @@ -27,6 +27,7 @@ import com.lunarclient.apollo.common.ApolloColors; import com.lunarclient.apollo.common.icon.ItemStackIcon; import com.lunarclient.apollo.common.icon.SimpleResourceLocationIcon; +import com.lunarclient.apollo.common.profile.Profile; import com.lunarclient.apollo.example.module.impl.CooldownExample; import com.lunarclient.apollo.module.cooldown.Cooldown; import com.lunarclient.apollo.module.cooldown.CooldownModule; @@ -34,6 +35,7 @@ import com.lunarclient.apollo.player.ApolloPlayer; import java.time.Duration; import java.util.Optional; +import java.util.UUID; import org.bukkit.entity.Player; public class CooldownApiExample extends CooldownExample { @@ -79,6 +81,28 @@ public void displayCooldownWithStyleExample(Player viewer) { }); } + @Override + public void displayCooldownWithPlayerTextureExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + + apolloPlayerOpt.ifPresent(apolloPlayer -> { + this.cooldownModule.displayCooldown(apolloPlayer, Cooldown.builder() + .name("player-head-cooldown") + .duration(Duration.ofSeconds(15)) + .icon(ItemStackIcon.builder() + .itemName("PLAYER_HEAD") // use "skull" for legacy with customModelData set to 3 + .profile(Profile.builder() + .id(UUID.fromString("f17627d8-1a97-487b-92ea-c04f413394bd")) + .texture("e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQ4MjUwNWJjZjNiYTU5YzJiZTdlMmQzNmY0ZTJiZGE4MzZmMmZkMTk0YjYyMTJhMmExYzRiNGEyYTQ3MWUifX19") + .signature("") + .build()) + .build() + ) + .build() + ); + }); + } + @Override public void displayCooldownResourceExample(Player viewer) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); @@ -104,6 +128,7 @@ public void removeCooldownExample(Player viewer) { apolloPlayerOpt.ifPresent(apolloPlayer -> { this.cooldownModule.removeCooldown(apolloPlayer, "enderpearl-cooldown"); this.cooldownModule.removeCooldown(apolloPlayer, "book-cooldown"); + this.cooldownModule.removeCooldown(apolloPlayer, "player-head-cooldown"); this.cooldownModule.removeCooldown(apolloPlayer, "lunar-cooldown"); }); } diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CosmeticApiExample.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CosmeticApiExample.java new file mode 100644 index 000000000..df3f8c38e --- /dev/null +++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/CosmeticApiExample.java @@ -0,0 +1,149 @@ +/* + * 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.location.ApolloBlockLocation; +import com.lunarclient.apollo.example.module.impl.CosmeticExample; +import com.lunarclient.apollo.module.cosmetic.Cosmetic; +import com.lunarclient.apollo.module.cosmetic.CosmeticModule; +import com.lunarclient.apollo.module.cosmetic.Spray; +import com.lunarclient.apollo.module.cosmetic.options.CloakOptions; +import com.lunarclient.apollo.module.cosmetic.options.PetOptions; +import com.lunarclient.apollo.module.packetenrichment.raytrace.Direction; +import com.lunarclient.apollo.player.ApolloPlayer; +import com.lunarclient.apollo.recipients.Recipients; +import java.time.Duration; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +public class CosmeticApiExample extends CosmeticExample { + + private final CosmeticModule cosmeticModule = Apollo.getModuleManager().getModule(CosmeticModule.class); + + @Override + public void equipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + List cosmetics = Lists.newArrayList( + Cosmetic.builder() + .id(434) + .build(), + Cosmetic.builder() + .id(3654) + .build(), + Cosmetic.builder() + .id(5095) + .options(PetOptions.builder() + .flipShoulder(true) + .build()) + .build(), + Cosmetic.builder() + .id(3) + .options(CloakOptions.builder() + .useClothPhysics(true) + .build()) + .build(), + Cosmetic.builder() + .id(3977) + .build() + ); + + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.cosmeticModule.equipNpcCosmetics(apolloPlayer, npcUuid, cosmetics)); + } + + @Override + public void equipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List cosmeticIds) { + List cosmetics = cosmeticIds.stream() + .map(id -> Cosmetic.builder().id(id).build()) + .collect(Collectors.toList()); + + this.cosmeticModule.equipNpcCosmetics(Recipients.ofEveryone(), npcUuid, cosmetics); + } + + @Override + public void unequipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> { + List cosmeticIds = Lists.newArrayList(434, 3654, 5095, 3, 3977); + this.cosmeticModule.unequipNpcCosmetics(apolloPlayer, npcUuid, cosmeticIds); + }); + } + + @Override + public void unequipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List cosmeticIds) { + this.cosmeticModule.unequipNpcCosmetics(Recipients.ofEveryone(), npcUuid, cosmeticIds); + } + + @Override + public void resetNpcCosmeticsExample(Player viewer, UUID npcUuid) { + this.cosmeticModule.resetNpcCosmetics(Recipients.ofEveryone(), npcUuid); + } + + @Override + public void displaySprayExample(Player viewer, int sprayId) { + Block block = viewer.getTargetBlockExact(10); + if (block == null) { + return; + } + + Material material = block.getType(); + if (material.isAir() || !material.isSolid()) { + return; + } + + ApolloBlockLocation location = ApolloBlockLocation.builder() + .world(block.getWorld().getName()) + .x(block.getX()) + .y(block.getY()) + .z(block.getZ()) + .build(); + + Spray spray = Spray.builder() + .sprayId(sprayId) + .location(location) + .facing(Direction.UP) + .rotation(0f) + .duration(Duration.ofSeconds(60)) + .build(); + + this.cosmeticModule.displaySpray(Recipients.ofEveryone(), spray); + } + + @Override + public void removeSprayExample(int sprayId) { + this.cosmeticModule.removeSpray(Recipients.ofEveryone(), sprayId); + } + + @Override + public void resetSpraysExample() { + this.cosmeticModule.resetSprays(Recipients.ofEveryone()); + } + +} diff --git a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/StopwatchApiExample.java b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/StopwatchApiExample.java index 620bb4fb7..2ff0d6c4f 100644 --- a/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/StopwatchApiExample.java +++ b/example/bukkit/api/src/main/java/com/lunarclient/apollo/example/api/module/StopwatchApiExample.java @@ -24,32 +24,128 @@ package com.lunarclient.apollo.example.api.module; import com.lunarclient.apollo.Apollo; +import com.lunarclient.apollo.common.ApolloColors; +import com.lunarclient.apollo.common.location.HudPosition; import com.lunarclient.apollo.example.module.impl.StopwatchExample; +import com.lunarclient.apollo.module.stopwatch.Stopwatch; import com.lunarclient.apollo.module.stopwatch.StopwatchModule; +import com.lunarclient.apollo.module.stopwatch.Timer; import com.lunarclient.apollo.player.ApolloPlayer; +import java.time.Duration; import java.util.Optional; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.entity.Player; public class StopwatchApiExample extends StopwatchExample { private final StopwatchModule stopwatchModule = Apollo.getModuleManager().getModule(StopwatchModule.class); + @Override + public void addStopwatchExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + + apolloPlayerOpt.ifPresent(apolloPlayer -> { + this.stopwatchModule.addStopwatch(apolloPlayer, Stopwatch.builder() + .id("parkour-stopwatch") + .name("Parkour") + .resetOnStart(true) + .preventModification(true) + .hideWhenStopped(false) + .displayFormat("mm:ss") + .textColor(ApolloColors.DARK_AQUA) + .hudPosition(HudPosition.builder() + .x(-30) + .y(30) + .build() + ) + .build()); + }); + } + + @Override + public void removeStopwatchExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.removeStopwatch(apolloPlayer, "parkour-stopwatch")); + } + @Override public void startStopwatchExample(Player viewer) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); - apolloPlayerOpt.ifPresent(this.stopwatchModule::startStopwatch); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.startStopwatch(apolloPlayer, "parkour-stopwatch")); } @Override public void stopStopwatchExample(Player viewer) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); - apolloPlayerOpt.ifPresent(this.stopwatchModule::stopStopwatch); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.stopStopwatch(apolloPlayer, "parkour-stopwatch")); } @Override public void resetStopwatchExample(Player viewer) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); - apolloPlayerOpt.ifPresent(this.stopwatchModule::resetStopwatch); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.resetStopwatch(apolloPlayer, "parkour-stopwatch")); + } + + @Override + public void resetStopwatchesExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(this.stopwatchModule::resetStopwatches); + } + + @Override + public void addTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + + apolloPlayerOpt.ifPresent(apolloPlayer -> { + this.stopwatchModule.addTimer(apolloPlayer, Timer.builder() + .id("game-timer") + .name("Countdown") + .duration(Duration.ofSeconds(45)) + .loop(false) + .preventModification(true) + .hideWhenStopped(false) + .displayFormat("mm:ss") + .titleText(Component.text("Time's up!", NamedTextColor.RED)) + .inGameNotification(true) + .textColor(ApolloColors.LIGHT_PURPLE) + .hudPosition(HudPosition.builder() + .x(-10) + .y(30) + .build() + ) + .build()); + }); + } + + @Override + public void removeTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.removeTimer(apolloPlayer, "game-timer")); + } + + @Override + public void startTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.startTimer(apolloPlayer, "game-timer")); + } + + @Override + public void stopTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.stopTimer(apolloPlayer, "game-timer")); + } + + @Override + public void resetTimerExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(apolloPlayer -> this.stopwatchModule.resetTimer(apolloPlayer, "game-timer")); + } + + @Override + public void resetTimersExample(Player viewer) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + apolloPlayerOpt.ifPresent(this.stopwatchModule::resetTimers); } } diff --git a/example/bukkit/api/src/main/resources/plugin.yml b/example/bukkit/api/src/main/resources/plugin.yml index e2d9ee4ca..2549361de 100644 --- a/example/bukkit/api/src/main/resources/plugin.yml +++ b/example/bukkit/api/src/main/resources/plugin.yml @@ -1,12 +1,14 @@ name: Apollo-API-Example main: com.lunarclient.apollo.example.api.ApolloApiExamplePlatform -version: 1.2.5 +version: 1.2.6 author: Moonsworth softdepend: [ Apollo-Bukkit, Apollo-Folia ] api-version: 1.13 folia-supported: true commands: + npc: + description: "NPCs!" apollodebug: description: "Apollo Debug!" modstatus: @@ -25,6 +27,8 @@ commands: description: "Combat!" cooldown: description: "Cooldowns!" + cosmetic: + description: "Cosmetics!" entity: description: "Entity!" glint: diff --git a/example/bukkit/common/build.gradle.kts b/example/bukkit/common/build.gradle.kts index 5c643f062..61b202e03 100644 --- a/example/bukkit/common/build.gradle.kts +++ b/example/bukkit/common/build.gradle.kts @@ -9,4 +9,6 @@ java { dependencies { compileOnly(libs.bukkit.api) + + api(project(":example:bukkit:apollo-example-bukkit-nms")) } 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 81d28966a..b524b6051 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 @@ -30,6 +30,7 @@ import com.lunarclient.apollo.example.command.ColoredFireCommand; import com.lunarclient.apollo.example.command.CombatCommand; import com.lunarclient.apollo.example.command.CooldownCommand; +import com.lunarclient.apollo.example.command.CosmeticCommand; import com.lunarclient.apollo.example.command.EntityCommand; import com.lunarclient.apollo.example.command.GlintCommand; import com.lunarclient.apollo.example.command.GlowCommand; @@ -40,6 +41,7 @@ import com.lunarclient.apollo.example.command.NametagCommand; import com.lunarclient.apollo.example.command.NickHiderCommand; import com.lunarclient.apollo.example.command.NotificationCommand; +import com.lunarclient.apollo.example.command.NpcCommand; import com.lunarclient.apollo.example.command.PayNowCommand; import com.lunarclient.apollo.example.command.RichPresenceCommand; import com.lunarclient.apollo.example.command.SaturationCommand; @@ -61,6 +63,7 @@ import com.lunarclient.apollo.example.module.impl.ColoredFireExample; import com.lunarclient.apollo.example.module.impl.CombatExample; import com.lunarclient.apollo.example.module.impl.CooldownExample; +import com.lunarclient.apollo.example.module.impl.CosmeticExample; import com.lunarclient.apollo.example.module.impl.EntityExample; import com.lunarclient.apollo.example.module.impl.GlintExample; import com.lunarclient.apollo.example.module.impl.GlowExample; @@ -85,6 +88,7 @@ import com.lunarclient.apollo.example.module.impl.TransferExample; import com.lunarclient.apollo.example.module.impl.VignetteExample; import com.lunarclient.apollo.example.module.impl.WaypointExample; +import com.lunarclient.apollo.example.nms.NpcManager; import lombok.Getter; import lombok.Setter; import org.bukkit.plugin.java.JavaPlugin; @@ -95,10 +99,13 @@ public abstract class ApolloExamplePlugin extends JavaPlugin { @Getter private static ApolloExamplePlugin instance; + private NpcManager npcManager; + private AutoTextHotkeyExample autoTextHotkeyExample; private BeamExample beamExample; private BorderExample borderExample; private ChatExample chatExample; + private CosmeticExample cosmeticExample; private ColoredFireExample coloredFireExample; private CombatExample combatExample; private CooldownExample cooldownExample; @@ -131,6 +138,8 @@ public abstract class ApolloExamplePlugin extends JavaPlugin { public void onEnable() { instance = this; + this.npcManager = new NpcManager(this); + this.registerCommonCommands(); this.registerCommonModulesExamples(); @@ -142,10 +151,14 @@ public void onEnable() { @Override public void onDisable() { - + if (this.npcManager != null) { + this.npcManager.removeAll(); + } } private void registerCommonCommands() { + this.getCommand("npc").setExecutor(new NpcCommand()); + this.getCommand("autotexthotkey").setExecutor(new AutoTextHotkeyCommand()); this.getCommand("beam").setExecutor(new BeamCommand()); this.getCommand("border").setExecutor(new BorderCommand()); @@ -153,6 +166,7 @@ private void registerCommonCommands() { this.getCommand("coloredfire").setExecutor(new ColoredFireCommand()); this.getCommand("combat").setExecutor(new CombatCommand()); this.getCommand("cooldown").setExecutor(new CooldownCommand()); + this.getCommand("cosmetic").setExecutor(new CosmeticCommand()); this.getCommand("entity").setExecutor(new EntityCommand()); this.getCommand("glint").setExecutor(new GlintCommand()); this.getCommand("glow").setExecutor(new GlowCommand()); diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CooldownCommand.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CooldownCommand.java index bb1223125..ceefef912 100644 --- a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CooldownCommand.java +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CooldownCommand.java @@ -43,7 +43,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Player player = (Player) sender; if (args.length != 1) { - player.sendMessage("Usage: /cooldown "); + player.sendMessage("Usage: /cooldown "); return true; } @@ -62,6 +62,12 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command break; } + case "displaywithplayertexture": { + cooldownExample.displayCooldownWithPlayerTextureExample(player); + player.sendMessage("Displaying cooldown with player texture...."); + break; + } + case "displayresource": { cooldownExample.displayCooldownResourceExample(player); player.sendMessage("Displaying cooldown resource...."); @@ -81,7 +87,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command } default: { - player.sendMessage("Usage: /cooldown "); + player.sendMessage("Usage: /cooldown "); break; } } diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CosmeticCommand.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CosmeticCommand.java new file mode 100644 index 000000000..fb88c1b52 --- /dev/null +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/CosmeticCommand.java @@ -0,0 +1,198 @@ +/* + * 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.CosmeticExample; +import com.lunarclient.apollo.example.nms.PlayerNpc; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import org.bukkit.ChatColor; +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 CosmeticCommand 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) { + this.sendUsage(player); + return true; + } + + CosmeticExample example = ApolloExamplePlugin.getInstance().getCosmeticExample(); + + if ("spray".equalsIgnoreCase(args[0])) { + return this.handleSpray(player, example, args); + } + + if (args.length < 2) { + this.sendUsage(player); + return true; + } + + String npcName = args[1]; + boolean uuidParam = npcName.contains("-"); + + Optional npcOpt = ApolloExamplePlugin.getInstance().getNpcManager().findByName(npcName); + if (!npcOpt.isPresent() && !uuidParam) { + player.sendMessage(ChatColor.RED + "No NPC found with name: " + npcName); + return true; + } + + UUID uuid; + if (uuidParam) { + uuid = UUID.fromString(npcName); + } else { + uuid = npcOpt.get().getUuid(); + } + + switch (args[0].toLowerCase()) { + case "equip": { + List cosmeticIds = this.parseCosmeticIds(args); + example.equipNpcCosmeticsInternal(player, uuid, cosmeticIds); + player.sendMessage(ChatColor.GREEN + "Equipped cosmetics " + cosmeticIds + " on NPC " + npcName); + break; + } + + case "unequip": { + List cosmeticIds = this.parseCosmeticIds(args); + example.unequipNpcCosmeticsInternal(player, uuid, cosmeticIds); + player.sendMessage(ChatColor.GREEN + "Unequipped cosmetics " + cosmeticIds + " from NPC " + npcName); + break; + } + + case "reset": { + example.resetNpcCosmeticsExample(player, uuid); + player.sendMessage(ChatColor.GREEN + "Reset all cosmetics on NPC " + npcName); + break; + } + + default: { + this.sendUsage(player); + break; + } + } + + return true; + } + + private boolean handleSpray(Player player, CosmeticExample example, String[] args) { + if (args.length < 2) { + this.sendSprayUsage(player); + return true; + } + + switch (args[1].toLowerCase()) { + case "display": { + if (args.length < 3) { + this.sendSprayUsage(player); + return true; + } + + int sprayId; + try { + sprayId = Integer.parseInt(args[2]); + } catch (NumberFormatException e) { + player.sendMessage("Spray id must be an integer."); + return true; + } + + example.displaySprayExample(player, sprayId); + player.sendMessage(ChatColor.GREEN + "Displayed spray " + sprayId + " at your target block"); + break; + } + + case "remove": { + if (args.length < 3) { + this.sendSprayUsage(player); + return true; + } + + int sprayId; + try { + sprayId = Integer.parseInt(args[2]); + } catch (NumberFormatException e) { + player.sendMessage("Spray id must be an integer."); + return true; + } + + example.removeSprayExample(sprayId); + player.sendMessage(ChatColor.GREEN + "Removed all sprays with id " + sprayId); + break; + } + + case "reset": { + example.resetSpraysExample(); + player.sendMessage(ChatColor.GREEN + "Reset all server sprays"); + break; + } + + default: { + this.sendSprayUsage(player); + break; + } + } + + return true; + } + + private List parseCosmeticIds(String[] args) { + List ids = new ArrayList<>(); + for (int i = 2; i < args.length; i++) { + try { + ids.add(Integer.parseInt(args[i])); + } catch (NumberFormatException ignored) { + } + } + return ids; + } + + private void sendUsage(Player player) { + player.sendMessage("Usage:"); + player.sendMessage(" - /cosmetic equip [cosmeticIds]"); + player.sendMessage(" - /cosmetic unequip [cosmeticIds]"); + player.sendMessage(" - /cosmetic reset "); + this.sendSprayUsage(player); + } + + private void sendSprayUsage(Player player) { + player.sendMessage(" - /cosmetic spray display "); + player.sendMessage(" - /cosmetic spray remove "); + player.sendMessage(" - /cosmetic spray reset"); + } + +} diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/NpcCommand.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/NpcCommand.java new file mode 100644 index 000000000..1f03492ad --- /dev/null +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/NpcCommand.java @@ -0,0 +1,115 @@ +/* + * 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.nms.NpcManager; +import com.lunarclient.apollo.example.nms.PlayerNpc; +import java.util.Optional; +import org.bukkit.ChatColor; +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 NpcCommand 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) { + this.sendUsage(player); + return true; + } + + NpcManager npcManager = ApolloExamplePlugin.getInstance().getNpcManager(); + + switch (args[0].toLowerCase()) { + case "spawn": { + if (args.length < 2) { + this.sendUsage(player); + break; + } + + PlayerNpc npc = npcManager.spawnNpc(args[1], player.getLocation()); + if (npc == null) { + player.sendMessage(ChatColor.RED + "Failed to spawn NPC."); + break; + } + + player.sendMessage(ChatColor.GREEN + "Spawned NPC " + npc.getName() + " (" + npc.getUuid() + ")"); + break; + } + + case "remove": { + if (args.length < 2) { + this.sendUsage(player); + break; + } + + Optional byName = npcManager.findByName(args[1]); + if (!byName.isPresent()) { + player.sendMessage(ChatColor.RED + "No NPC found with name: " + args[1]); + break; + } + + npcManager.removeNpc(byName.get().getUuid()); + player.sendMessage(ChatColor.GREEN + "Removed NPC " + args[1]); + break; + } + + case "reset": { + if (npcManager.getNpcs().isEmpty()) { + player.sendMessage(ChatColor.YELLOW + "There are no NPCs to remove."); + break; + } + + npcManager.removeAll(); + player.sendMessage(ChatColor.GREEN + "Removed all tracked NPCs."); + break; + } + + default: { + this.sendUsage(player); + break; + } + } + + return true; + } + + private void sendUsage(Player player) { + player.sendMessage("Usage: /npc spawn "); + player.sendMessage("Usage: /npc remove "); + player.sendMessage("Usage: /npc reset"); + } + +} diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/StopwatchCommand.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/StopwatchCommand.java index a4f081a12..a4bbbc7e6 100644 --- a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/StopwatchCommand.java +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/command/StopwatchCommand.java @@ -42,38 +42,124 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command Player player = (Player) sender; - if (args.length != 1) { - player.sendMessage("Usage: /stopwatch "); + if (args.length < 2) { + player.sendMessage("Usage: /stopwatch stopwatch "); + player.sendMessage("Usage: /stopwatch timer "); return true; } StopwatchExample stopwatchExample = ApolloExamplePlugin.getInstance().getStopwatchExample(); + String type = args[0].toLowerCase(); + String action = args[1].toLowerCase(); + + switch (type) { + case "stopwatch": { + this.handleStopwatch(player, stopwatchExample, action); + break; + } + + case "timer": { + this.handleTimer(player, stopwatchExample, action); + break; + } + + default: { + player.sendMessage("Usage: /stopwatch stopwatch "); + player.sendMessage("Usage: /stopwatch timer "); + break; + } + } + + return true; + } + + private void handleStopwatch(Player player, StopwatchExample example, String action) { + switch (action) { + case "add": { + example.addStopwatchExample(player); + player.sendMessage("Adding stopwatch..."); + break; + } + + case "remove": { + example.removeStopwatchExample(player); + player.sendMessage("Removing stopwatch..."); + break; + } - switch (args[0].toLowerCase()) { case "start": { - stopwatchExample.startStopwatchExample(player); - player.sendMessage("Starting stopwatch...."); + example.startStopwatchExample(player); + player.sendMessage("Starting stopwatch..."); break; } case "stop": { - stopwatchExample.stopStopwatchExample(player); - player.sendMessage("Stopping stopwatch...."); + example.stopStopwatchExample(player); + player.sendMessage("Stopping stopwatch..."); break; } case "reset": { - stopwatchExample.resetStopwatchExample(player); - player.sendMessage("Resetting stopwatch...."); + example.resetStopwatchExample(player); + player.sendMessage("Resetting stopwatch..."); + break; + } + + case "resetall": { + example.resetStopwatchesExample(player); + player.sendMessage("Resetting all stopwatches..."); break; } default: { - player.sendMessage("Usage: /stopwatch "); + player.sendMessage("Usage: /stopwatch stopwatch "); break; } } + } - return true; + private void handleTimer(Player player, StopwatchExample example, String action) { + switch (action) { + case "add": { + example.addTimerExample(player); + player.sendMessage("Adding timer..."); + break; + } + + case "remove": { + example.removeTimerExample(player); + player.sendMessage("Removing timer..."); + break; + } + + case "start": { + example.startTimerExample(player); + player.sendMessage("Starting timer..."); + break; + } + + case "stop": { + example.stopTimerExample(player); + player.sendMessage("Stopping timer..."); + break; + } + + case "reset": { + example.resetTimerExample(player); + player.sendMessage("Resetting timer..."); + break; + } + + case "resetall": { + example.resetTimersExample(player); + player.sendMessage("Resetting all timers..."); + break; + } + + default: { + player.sendMessage("Usage: /stopwatch timer "); + break; + } + } } } diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CooldownExample.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CooldownExample.java index e0c8ebc81..a7d6a8870 100644 --- a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CooldownExample.java +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CooldownExample.java @@ -32,6 +32,8 @@ public abstract class CooldownExample extends ApolloModuleExample { public abstract void displayCooldownWithStyleExample(Player viewer); + public abstract void displayCooldownWithPlayerTextureExample(Player viewer); + public abstract void displayCooldownResourceExample(Player viewer); public abstract void removeCooldownExample(Player viewer); diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CosmeticExample.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CosmeticExample.java new file mode 100644 index 000000000..d997de773 --- /dev/null +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/CosmeticExample.java @@ -0,0 +1,49 @@ +/* + * 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 java.util.List; +import java.util.UUID; +import org.bukkit.entity.Player; + +public abstract class CosmeticExample extends ApolloModuleExample { + + public abstract void equipNpcCosmeticsExample(Player viewer, UUID npcUuid); + + public abstract void equipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List cosmeticIds); + + public abstract void unequipNpcCosmeticsExample(Player viewer, UUID npcUuid); + + public abstract void unequipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List cosmeticIds); + + public abstract void resetNpcCosmeticsExample(Player viewer, UUID npcUuid); + + public abstract void displaySprayExample(Player viewer, int sprayId); + + public abstract void removeSprayExample(int sprayId); + + public abstract void resetSpraysExample(); + +} diff --git a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/StopwatchExample.java b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/StopwatchExample.java index 3994bbd4a..57d707107 100644 --- a/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/StopwatchExample.java +++ b/example/bukkit/common/src/main/java/com/lunarclient/apollo/example/module/impl/StopwatchExample.java @@ -28,10 +28,28 @@ public abstract class StopwatchExample extends ApolloModuleExample { + public abstract void addStopwatchExample(Player viewer); + + public abstract void removeStopwatchExample(Player viewer); + public abstract void startStopwatchExample(Player viewer); public abstract void stopStopwatchExample(Player viewer); public abstract void resetStopwatchExample(Player viewer); + public abstract void resetStopwatchesExample(Player viewer); + + public abstract void addTimerExample(Player viewer); + + public abstract void removeTimerExample(Player viewer); + + public abstract void startTimerExample(Player viewer); + + public abstract void stopTimerExample(Player viewer); + + public abstract void resetTimerExample(Player viewer); + + public abstract void resetTimersExample(Player viewer); + } diff --git a/example/bukkit/json/build.gradle.kts b/example/bukkit/json/build.gradle.kts index c37f0cbfa..797fa87cd 100644 --- a/example/bukkit/json/build.gradle.kts +++ b/example/bukkit/json/build.gradle.kts @@ -18,3 +18,9 @@ dependencies { compileOnly(libs.folia) implementation(project(":example:bukkit:apollo-example-bukkit-common")) } + +tasks.shadowJar { + manifest { + attributes["paperweight-mappings-namespace"] = "mojang+yarn" + } +} 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 196b6f81a..7240c067d 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 @@ -33,6 +33,7 @@ import com.lunarclient.apollo.example.json.module.ColoredFireJsonExample; import com.lunarclient.apollo.example.json.module.CombatJsonExample; import com.lunarclient.apollo.example.json.module.CooldownJsonExample; +import com.lunarclient.apollo.example.json.module.CosmeticJsonExample; import com.lunarclient.apollo.example.json.module.EntityJsonExample; import com.lunarclient.apollo.example.json.module.GlowJsonExample; import com.lunarclient.apollo.example.json.module.HologramJsonExample; @@ -73,6 +74,7 @@ public void registerModuleExamples() { this.setBeamExample(new BeamJsonExample()); this.setBorderExample(new BorderJsonExample()); this.setChatExample(new ChatJsonExample()); + this.setCosmeticExample(new CosmeticJsonExample()); this.setColoredFireExample(new ColoredFireJsonExample()); this.setCombatExample(new CombatJsonExample()); this.setCooldownExample(new CooldownJsonExample()); diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CooldownJsonExample.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CooldownJsonExample.java index a254be03f..2b853532d 100644 --- a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CooldownJsonExample.java +++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CooldownJsonExample.java @@ -29,6 +29,7 @@ import com.lunarclient.apollo.example.module.impl.CooldownExample; import java.awt.Color; import java.time.Duration; +import java.util.UUID; import org.bukkit.entity.Player; public class CooldownJsonExample extends CooldownExample { @@ -62,6 +63,24 @@ public void displayCooldownWithStyleExample(Player viewer) { JsonPacketUtil.sendPacket(viewer, message); } + @Override + public void displayCooldownWithPlayerTextureExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cooldown.v1.DisplayCooldownMessage"); + message.addProperty("name", "player-head-cooldown"); + message.addProperty("duration", JsonUtil.createDurationObject(Duration.ofSeconds(15))); + message.add("icon", JsonUtil.createItemStackIconObject( + "PLAYER_HEAD", 0, 0, + JsonUtil.createProfileObject( + UUID.fromString("f17627d8-1a97-487b-92ea-c04f413394bd"), + "e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQ4MjUwNWJjZjNiYTU5YzJiZTdlMmQzNmY0ZTJiZGE4MzZmMmZkMTk0YjYyMTJhMmExYzRiNGEyYTQ3MWUifX19", + "" + ) + )); + + JsonPacketUtil.sendPacket(viewer, message); + } + @Override public void displayCooldownResourceExample(Player viewer) { JsonObject message = new JsonObject(); diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CosmeticJsonExample.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CosmeticJsonExample.java new file mode 100644 index 000000000..6fe78acc8 --- /dev/null +++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/CosmeticJsonExample.java @@ -0,0 +1,173 @@ +/* + * 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.JsonPacketUtil; +import com.lunarclient.apollo.example.json.util.JsonUtil; +import com.lunarclient.apollo.example.module.impl.CosmeticExample; +import java.time.Duration; +import java.util.List; +import java.util.UUID; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +public class CosmeticJsonExample extends CosmeticExample { + + @Override + public void equipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + JsonArray cosmeticsArray = new JsonArray(); + + JsonObject first = new JsonObject(); + first.addProperty("id", 434); + cosmeticsArray.add(first); + + JsonObject second = new JsonObject(); + second.addProperty("id", 3654); + cosmeticsArray.add(second); + + JsonObject third = new JsonObject(); + third.addProperty("id", 5095); + JsonObject petOptions = new JsonObject(); + petOptions.addProperty("flip_shoulder", true); + third.add("pet_options", petOptions); + cosmeticsArray.add(third); + + JsonObject fourth = new JsonObject(); + fourth.addProperty("id", 3); + JsonObject cloakOptions = new JsonObject(); + cloakOptions.addProperty("use_cloth_physics", true); + fourth.add("cloak_options", cloakOptions); + cosmeticsArray.add(fourth); + + JsonObject fifth = new JsonObject(); + fifth.addProperty("id", 3977); + cosmeticsArray.add(fifth); + + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.EquipNpcCosmeticsMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + message.add("cosmetics", cosmeticsArray); + + JsonPacketUtil.sendPacket(viewer, message); + } + + @Override + public void equipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List cosmeticIds) { + JsonArray cosmeticsArray = new JsonArray(); + for (int cosmeticId : cosmeticIds) { + JsonObject cosmetic = new JsonObject(); + cosmetic.addProperty("id", cosmeticId); + cosmeticsArray.add(cosmetic); + } + + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.EquipNpcCosmeticsMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + message.add("cosmetics", cosmeticsArray); + + JsonPacketUtil.broadcastPacket(message); + } + + @Override + public void unequipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + List cosmeticIds = Lists.newArrayList(434, 3654, 5095, 3, 3977); + + JsonArray cosmeticIdsArray = new JsonArray(); + cosmeticIds.forEach(cosmeticIdsArray::add); + + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.UnequipNpcCosmeticsMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + message.add("cosmetic_ids", cosmeticIdsArray); + + JsonPacketUtil.broadcastPacket(message); + } + + @Override + public void unequipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List cosmeticIds) { + JsonArray cosmeticIdsArray = new JsonArray(); + cosmeticIds.forEach(cosmeticIdsArray::add); + + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.UnequipNpcCosmeticsMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + message.add("cosmetic_ids", cosmeticIdsArray); + + JsonPacketUtil.broadcastPacket(message); + } + + @Override + public void resetNpcCosmeticsExample(Player viewer, UUID npcUuid) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.ResetNpcCosmeticsMessage"); + message.add("npc_uuid", JsonUtil.createUuidObject(npcUuid)); + + JsonPacketUtil.broadcastPacket(message); + } + + @Override + public void displaySprayExample(Player viewer, int sprayId) { + Block block = viewer.getTargetBlockExact(10); + if (block == null) { + return; + } + + Material material = block.getType(); + if (material.isAir() || !material.isSolid()) { + return; + } + + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.DisplaySprayMessage"); + message.addProperty("spray_id", sprayId); + message.add("location", JsonUtil.createBlockLocationObject(block.getLocation())); + message.addProperty("facing", "UP"); + message.addProperty("rotation", 0f); + message.addProperty("duration", JsonUtil.createDurationObject(Duration.ofSeconds(60))); + + JsonPacketUtil.broadcastPacket(message); + } + + @Override + public void removeSprayExample(int sprayId) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.RemoveSprayMessage"); + message.addProperty("spray_id", sprayId); + + JsonPacketUtil.broadcastPacket(message); + } + + @Override + public void resetSpraysExample() { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.ResetSpraysMessage"); + + JsonPacketUtil.broadcastPacket(message); + } + +} diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/StopwatchJsonExample.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/StopwatchJsonExample.java index 96a972cac..33031f2ea 100644 --- a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/StopwatchJsonExample.java +++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/module/StopwatchJsonExample.java @@ -24,16 +24,52 @@ package com.lunarclient.apollo.example.json.module; 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.StopwatchExample; +import java.awt.Color; +import java.time.Duration; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.entity.Player; public class StopwatchJsonExample extends StopwatchExample { + @Override + public void addStopwatchExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.AddStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); + message.addProperty("name", "Parkour"); + message.addProperty("reset_on_start", true); + message.addProperty("prevent_modification", true); + message.addProperty("hide_when_stopped", false); + message.addProperty("display_format", "mm:ss"); + message.add("text_color", JsonUtil.createColorObject(new Color(0, 170, 170))); + + JsonObject stopwatchPosition = new JsonObject(); + stopwatchPosition.addProperty("x", -30); + stopwatchPosition.addProperty("y", 30); + message.add("hud_position", stopwatchPosition); + + JsonPacketUtil.sendPacket(viewer, message); + } + + @Override + public void removeStopwatchExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.RemoveStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); + + JsonPacketUtil.sendPacket(viewer, message); + } + @Override public void startStopwatchExample(Player viewer) { JsonObject message = new JsonObject(); message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.StartStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); JsonPacketUtil.sendPacket(viewer, message); } @@ -42,6 +78,7 @@ public void startStopwatchExample(Player viewer) { public void stopStopwatchExample(Player viewer) { JsonObject message = new JsonObject(); message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.StopStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); JsonPacketUtil.sendPacket(viewer, message); } @@ -50,6 +87,84 @@ public void stopStopwatchExample(Player viewer) { public void resetStopwatchExample(Player viewer) { JsonObject message = new JsonObject(); message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.ResetStopwatchMessage"); + message.addProperty("id", "parkour-stopwatch"); + + JsonPacketUtil.sendPacket(viewer, message); + } + + @Override + public void resetStopwatchesExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.ResetStopwatchesMessage"); + + JsonPacketUtil.sendPacket(viewer, message); + } + + @Override + public void addTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.AddTimerMessage"); + message.addProperty("id", "game-timer"); + message.addProperty("name", "Countdown"); + message.addProperty("duration", JsonUtil.createDurationObject(Duration.ofSeconds(45))); + message.addProperty("loop", false); + message.addProperty("prevent_modification", true); + message.addProperty("hide_when_stopped", false); + message.addProperty("display_format", "mm:ss"); + message.addProperty("title_text_adventure_json_lines", AdventureUtil.toJson( + Component.text("Time's up!", NamedTextColor.RED) + )); + message.addProperty("in_game_notification", true); + message.add("text_color", JsonUtil.createColorObject(new Color(255, 85, 255))); + + JsonObject timerPosition = new JsonObject(); + timerPosition.addProperty("x", -10); + timerPosition.addProperty("y", 30); + message.add("hud_position", timerPosition); + + JsonPacketUtil.sendPacket(viewer, message); + } + + @Override + public void removeTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.RemoveTimerMessage"); + message.addProperty("id", "game-timer"); + + JsonPacketUtil.sendPacket(viewer, message); + } + + @Override + public void startTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.StartTimerMessage"); + message.addProperty("id", "game-timer"); + + JsonPacketUtil.sendPacket(viewer, message); + } + + @Override + public void stopTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.StopTimerMessage"); + message.addProperty("id", "game-timer"); + + JsonPacketUtil.sendPacket(viewer, message); + } + + @Override + public void resetTimerExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.ResetTimerMessage"); + message.addProperty("id", "game-timer"); + + JsonPacketUtil.sendPacket(viewer, message); + } + + @Override + public void resetTimersExample(Player viewer) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.stopwatch.v1.ResetTimersMessage"); JsonPacketUtil.sendPacket(viewer, message); } diff --git a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/util/JsonPacketUtil.java b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/util/JsonPacketUtil.java index 3e1b97502..d6ff7b8f9 100644 --- a/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/util/JsonPacketUtil.java +++ b/example/bukkit/json/src/main/java/com/lunarclient/apollo/example/json/util/JsonPacketUtil.java @@ -41,7 +41,7 @@ public final class JsonPacketUtil { private static final List APOLLO_MODULES = Arrays.asList("auto_text_hotkey", "beam", "border", "chat", "colored_fire", "combat", "cooldown", - "entity", "glint", "glow", "hologram", "inventory", "limb", "mod_setting", "nametag", "nick_hider", "notification", "pay_now", "packet_enrichment", + "cosmetic", "entity", "glint", "glow", "hologram", "inventory", "limb", "mod_setting", "nametag", "nick_hider", "notification", "pay_now", "packet_enrichment", "rich_presence", "saturation", "server_rule", "staff_mod", "stopwatch", "team", "tebex", "title", "tnt_countdown", "transfer", "vignette", "waypoint" ); 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 841a96943..694ce2f42 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 @@ -95,9 +95,13 @@ public static JsonObject createCuboid2DObject(double minX, double minZ, double m } public static JsonObject createEntityIdObject(@NotNull Entity entity) { + return JsonUtil.createEntityIdObject(entity.getEntityId(), entity.getUniqueId()); + } + + public static JsonObject createEntityIdObject(int entityId, @NotNull UUID uuid) { JsonObject entityIdObject = new JsonObject(); - entityIdObject.addProperty("entity_id", entity.getEntityId()); - entityIdObject.add("entity_uuid", JsonUtil.createUuidObject(entity.getUniqueId())); + entityIdObject.addProperty("entity_id", entityId); + entityIdObject.add("entity_uuid", JsonUtil.createUuidObject(uuid)); return entityIdObject; } @@ -136,6 +140,10 @@ public static Location toBukkitPlayerLocation(JsonObject message) { } public static JsonObject createItemStackIconObject(@Nullable String itemName, int itemId, int customModelData) { + return JsonUtil.createItemStackIconObject(itemName, itemId, customModelData, null); + } + + public static JsonObject createItemStackIconObject(@Nullable String itemName, int itemId, int customModelData, @Nullable JsonObject profile) { JsonObject itemIconObject = new JsonObject(); if (itemName != null) { itemIconObject.addProperty("item_name", itemName); @@ -145,11 +153,25 @@ public static JsonObject createItemStackIconObject(@Nullable String itemName, in itemIconObject.addProperty("custom_model_data", customModelData); + if (profile != null) { + itemIconObject.add("profile", profile); + } + JsonObject iconObject = new JsonObject(); iconObject.add("item_stack", itemIconObject); return iconObject; } + public static JsonObject createProfileObject(@Nullable UUID id, @NotNull String texture, @NotNull String signature) { + JsonObject profileObject = new JsonObject(); + if (id != null) { + profileObject.add("id", JsonUtil.createUuidObject(id)); + } + profileObject.addProperty("texture", texture); + profileObject.addProperty("signature", signature); + return profileObject; + } + public static JsonObject createResourceLocationIconObject(@NotNull String resourceLocation) { JsonObject resourceIconObject = new JsonObject(); resourceIconObject.addProperty("resource_location", resourceLocation); diff --git a/example/bukkit/json/src/main/resources/plugin.yml b/example/bukkit/json/src/main/resources/plugin.yml index f4a02a773..027ac7db1 100644 --- a/example/bukkit/json/src/main/resources/plugin.yml +++ b/example/bukkit/json/src/main/resources/plugin.yml @@ -1,12 +1,14 @@ name: Apollo-Json-Example main: com.lunarclient.apollo.example.json.ApolloJsonExamplePlatform -version: 1.2.5 +version: 1.2.6 author: Moonsworth softdepend: [ Apollo-Bukkit ] api-version: 1.13 folia-supported: true commands: + npc: + description: "NPCs!" autotexthotkey: description: "Auto Text Hotkey!" beam: @@ -21,6 +23,8 @@ commands: description: "Combat!" cooldown: description: "Cooldowns!" + cosmetic: + description: "Cosmetics!" entity: description: "Entity!" glint: diff --git a/example/bukkit/nms/build.gradle.kts b/example/bukkit/nms/build.gradle.kts new file mode 100644 index 000000000..8158fb3c2 --- /dev/null +++ b/example/bukkit/nms/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + id("apollo.base-conventions") + id("io.papermc.paperweight.userdev") version "2.0.0-beta.21" +} + +java { + javaTarget(25) +} + +paperweight.reobfArtifactConfiguration = + io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION + +tasks.named("reobfJar") { + enabled = false +} + +tasks.compileJava { + options.compilerArgs.addAll(listOf("--release", "21")) +} + +configurations.named("compileClasspath") { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 25) + } +} + +configurations.named("runtimeClasspath") { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 25) + } +} + +listOf("apiElements", "runtimeElements").forEach { name -> + configurations.named(name) { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21) + } + } +} + +dependencies { + paperweight.paperDevBundle("26.1.1.build.+") +} diff --git a/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcManager.java b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcManager.java new file mode 100644 index 000000000..22f1d0b29 --- /dev/null +++ b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/NpcManager.java @@ -0,0 +1,186 @@ +/* + * 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.nms; + +import com.mojang.authlib.GameProfile; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ParticleStatus; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.HumanoidArm; +import net.minecraft.world.entity.player.ChatVisiblity; +import net.minecraft.world.level.GameType; +import net.minecraft.world.phys.Vec3; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Nullable; + +public final class NpcManager implements Listener { + + private static final ClientInformation NPC_CLIENT_INFO = new ClientInformation( + "en_us", 2, ChatVisiblity.HIDDEN, false, 0x7F, + HumanoidArm.RIGHT, false, false, ParticleStatus.ALL + ); + + private final Map npcs = new HashMap<>(); + private final JavaPlugin plugin; + + public NpcManager(JavaPlugin plugin) { + this.plugin = plugin; + + Bukkit.getPluginManager().registerEvents(this, plugin); + Bukkit.getScheduler().runTask(plugin, this::spawnDefaultNpcs); + } + + public void removeNpc(UUID uuid) { + PlayerNpc npc = this.npcs.remove(uuid); + if (npc == null) { + return; + } + + this.despawnNpcs(npc); + } + + public void removeAll() { + for (PlayerNpc npc : new ArrayList<>(this.npcs.values())) { + this.despawnNpcs(npc); + } + + this.npcs.clear(); + } + + public Optional findByName(String name) { + return this.npcs.values().stream() + .filter(npc -> npc.getName().equalsIgnoreCase(name)) + .findFirst(); + } + + public Collection getNpcs() { + return new ArrayList<>(this.npcs.values()); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + ServerPlayer viewer = ((CraftPlayer) event.getPlayer()).getHandle(); + for (PlayerNpc npc : this.npcs.values()) { + this.showNpc(viewer, npc); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + ServerPlayer viewer = ((CraftPlayer) event.getPlayer()).getHandle(); + for (PlayerNpc npc : this.npcs.values()) { + this.hideNpc(viewer, npc); + } + } + + private void spawnDefaultNpcs() { + this.spawnNpc("Apollo", new Location(Bukkit.getWorld("world"), 20.5, 65, 5.5, 90f, 0f)); + } + + public @Nullable PlayerNpc spawnNpc(String name, Location location) { + World world = location.getWorld(); + if (world == null) { + return null; + } + + MinecraftServer server = MinecraftServer.getServer(); + ServerLevel level = ((CraftWorld) world).getHandle(); + + UUID npcUuid = new UUID(UUID.randomUUID().getMostSignificantBits(), 0L); + GameProfile profile = new GameProfile(npcUuid, name); + ServerPlayer npc = new ServerPlayer(server, level, profile, NpcManager.NPC_CLIENT_INFO); + + npc.setPos(location.getX(), location.getY(), location.getZ()); + npc.setYRot(location.getYaw()); + npc.setXRot(location.getPitch()); + + PlayerNpc playerNpc = new PlayerNpc(npc.getUUID(), name, location.clone(), npc); + this.npcs.put(playerNpc.getUuid(), playerNpc); + + for (ServerPlayer viewer : server.getPlayerList().getPlayers()) { + this.showNpc(viewer, playerNpc); + } + + return playerNpc; + } + + private void showNpc(ServerPlayer viewer, PlayerNpc npc) { + ServerPlayer entity = npc.getHandle(); + Location location = npc.getLocation(); + GameProfile profile = entity.getGameProfile(); + + ClientboundPlayerInfoUpdatePacket.Entry entry = new ClientboundPlayerInfoUpdatePacket.Entry( + entity.getUUID(), profile, true, 0, GameType.CREATIVE, null, true, 0, null); + + viewer.connection.send(new ClientboundPlayerInfoUpdatePacket( + EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER), entry)); + viewer.connection.send(new ClientboundAddEntityPacket( + entity.getId(), entity.getUUID(), + location.getX(), location.getY(), location.getZ(), + location.getPitch(), location.getYaw(), + entity.getType(), 0, Vec3.ZERO, location.getYaw())); + viewer.connection.send(new ClientboundSetEntityDataPacket( + entity.getId(), entity.getEntityData().getNonDefaultValues())); + + Bukkit.getScheduler().runTaskLater(this.plugin, () -> { + viewer.connection.send(new ClientboundPlayerInfoRemovePacket(List.of(entity.getUUID()))); + }, 5L); + } + + private void hideNpc(ServerPlayer viewer, PlayerNpc npc) { + viewer.connection.send(new ClientboundRemoveEntitiesPacket(npc.getEntityId())); + viewer.connection.send(new ClientboundPlayerInfoRemovePacket(List.of(npc.getUuid()))); + } + + private void despawnNpcs(PlayerNpc npc) { + for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) { + this.hideNpc(player, npc); + } + } + +} diff --git a/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/PlayerNpc.java b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/PlayerNpc.java new file mode 100644 index 000000000..1237a4afa --- /dev/null +++ b/example/bukkit/nms/src/main/java/com/lunarclient/apollo/example/nms/PlayerNpc.java @@ -0,0 +1,45 @@ +/* + * 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.nms; + +import java.util.UUID; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.Location; + +@Getter +@RequiredArgsConstructor +public final class PlayerNpc { + + private final UUID uuid; + private final String name; + private final Location location; + private final ServerPlayer handle; + + public int getEntityId() { + return this.handle.getId(); + } + +} diff --git a/example/bukkit/proto/build.gradle.kts b/example/bukkit/proto/build.gradle.kts index ea3f1a414..37a8c4bcb 100644 --- a/example/bukkit/proto/build.gradle.kts +++ b/example/bukkit/proto/build.gradle.kts @@ -20,3 +20,9 @@ dependencies { compileOnly(libs.folia) implementation(project(":example:bukkit:apollo-example-bukkit-common")) } + +tasks.shadowJar { + manifest { + attributes["paperweight-mappings-namespace"] = "mojang+yarn" + } +} 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 f02c1bfd3..9112f0596 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 @@ -33,6 +33,7 @@ import com.lunarclient.apollo.example.proto.module.ColoredFireProtoExample; import com.lunarclient.apollo.example.proto.module.CombatProtoExample; import com.lunarclient.apollo.example.proto.module.CooldownProtoExample; +import com.lunarclient.apollo.example.proto.module.CosmeticProtoExample; import com.lunarclient.apollo.example.proto.module.EntityProtoExample; import com.lunarclient.apollo.example.proto.module.GlowProtoExample; import com.lunarclient.apollo.example.proto.module.HologramProtoExample; @@ -73,6 +74,7 @@ public void registerModuleExamples() { this.setBeamExample(new BeamProtoExample()); this.setBorderExample(new BorderProtoExample()); this.setChatExample(new ChatProtoExample()); + this.setCosmeticExample(new CosmeticProtoExample()); this.setColoredFireExample(new ColoredFireProtoExample()); this.setCombatExample(new CombatProtoExample()); this.setCooldownExample(new CooldownProtoExample()); 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 1834aab5d..cdcbec812 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 @@ -33,6 +33,7 @@ import com.lunarclient.apollo.example.proto.util.ProtobufUtil; import java.awt.Color; import java.time.Duration; +import java.util.UUID; import org.bukkit.entity.Player; public class CooldownProtoExample extends CooldownExample { @@ -69,6 +70,26 @@ public void displayCooldownWithStyleExample(Player viewer) { ProtobufPacketUtil.sendPacket(viewer, message); } + @Override + public void displayCooldownWithPlayerTextureExample(Player viewer) { + DisplayCooldownMessage message = DisplayCooldownMessage.newBuilder() + .setName("player-head-cooldown") + .setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(15))) + .setIcon(Icon.newBuilder() + .setItemStack(ProtobufUtil.createItemStackIconProto( + "PLAYER_HEAD", 0, 0, + ProtobufUtil.createProfileProto( + UUID.fromString("f17627d8-1a97-487b-92ea-c04f413394bd"), + "e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWQ4MjUwNWJjZjNiYTU5YzJiZTdlMmQzNmY0ZTJiZGE4MzZmMmZkMTk0YjYyMTJhMmExYzRiNGEyYTQ3MWUifX19", + "" + ) + )) + .build()) + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + @Override public void displayCooldownResourceExample(Player viewer) { DisplayCooldownMessage message = DisplayCooldownMessage.newBuilder() diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CosmeticProtoExample.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CosmeticProtoExample.java new file mode 100644 index 000000000..4454da145 --- /dev/null +++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/CosmeticProtoExample.java @@ -0,0 +1,166 @@ +/* + * 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.cosmetic.v1.CloakOptions; +import com.lunarclient.apollo.cosmetic.v1.Cosmetic; +import com.lunarclient.apollo.cosmetic.v1.DisplaySprayMessage; +import com.lunarclient.apollo.cosmetic.v1.EquipNpcCosmeticsMessage; +import com.lunarclient.apollo.cosmetic.v1.PetOptions; +import com.lunarclient.apollo.cosmetic.v1.RemoveSprayMessage; +import com.lunarclient.apollo.cosmetic.v1.ResetNpcCosmeticsMessage; +import com.lunarclient.apollo.cosmetic.v1.ResetSpraysMessage; +import com.lunarclient.apollo.cosmetic.v1.UnequipNpcCosmeticsMessage; +import com.lunarclient.apollo.example.module.impl.CosmeticExample; +import com.lunarclient.apollo.example.proto.util.ProtobufPacketUtil; +import com.lunarclient.apollo.example.proto.util.ProtobufUtil; +import com.lunarclient.apollo.packetenrichment.v1.Direction; +import java.time.Duration; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +public class CosmeticProtoExample extends CosmeticExample { + + @Override + public void equipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + List cosmetics = Lists.newArrayList( + Cosmetic.newBuilder() + .setId(434) + .build(), + Cosmetic.newBuilder() + .setId(3654) + .build(), + Cosmetic.newBuilder() + .setId(5095) + .setPetOptions(PetOptions.newBuilder() + .setFlipShoulder(true) + .build()) + .build(), + Cosmetic.newBuilder() + .setId(3) + .setCloakOptions(CloakOptions.newBuilder() + .setUseClothPhysics(true) + .build()) + .build(), + Cosmetic.newBuilder() + .setId(3977) + .build() + ); + + EquipNpcCosmeticsMessage message = EquipNpcCosmeticsMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .addAllCosmetics(cosmetics) + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + + @Override + public void equipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List cosmeticIds) { + List cosmetics = cosmeticIds.stream() + .map(id -> Cosmetic.newBuilder().setId(id).build()) + .collect(Collectors.toList()); + + EquipNpcCosmeticsMessage message = EquipNpcCosmeticsMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .addAllCosmetics(cosmetics) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); + } + + @Override + public void unequipNpcCosmeticsExample(Player viewer, UUID npcUuid) { + List cosmeticIds = Lists.newArrayList(434, 3654, 5095, 3, 3977); + + UnequipNpcCosmeticsMessage message = UnequipNpcCosmeticsMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .addAllCosmeticIds(cosmeticIds) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); + } + + @Override + public void unequipNpcCosmeticsInternal(Player viewer, UUID npcUuid, List cosmeticIds) { + UnequipNpcCosmeticsMessage message = UnequipNpcCosmeticsMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .addAllCosmeticIds(cosmeticIds) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); + } + + @Override + public void resetNpcCosmeticsExample(Player viewer, UUID npcUuid) { + ResetNpcCosmeticsMessage message = ResetNpcCosmeticsMessage.newBuilder() + .setNpcUuid(ProtobufUtil.createUuidProto(npcUuid)) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); + } + + @Override + public void displaySprayExample(Player viewer, int sprayId) { + Block block = viewer.getTargetBlockExact(10); + if (block == null) { + return; + } + + Material material = block.getType(); + if (material.isAir() || !material.isSolid()) { + return; + } + + DisplaySprayMessage message = DisplaySprayMessage.newBuilder() + .setSprayId(sprayId) + .setLocation(ProtobufUtil.createBlockLocationProto(block.getLocation())) + .setFacing(Direction.DIRECTION_UP) + .setRotation(0f) + .setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(60))) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); + } + + @Override + public void removeSprayExample(int sprayId) { + RemoveSprayMessage message = RemoveSprayMessage.newBuilder() + .setSprayId(sprayId) + .build(); + + ProtobufPacketUtil.broadcastPacket(message); + } + + @Override + public void resetSpraysExample() { + ProtobufPacketUtil.broadcastPacket(ResetSpraysMessage.getDefaultInstance()); + } + +} diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/StopwatchProtoExample.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/StopwatchProtoExample.java index 5f8960b52..9f0015294 100644 --- a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/StopwatchProtoExample.java +++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/module/StopwatchProtoExample.java @@ -24,29 +24,156 @@ package com.lunarclient.apollo.example.proto.module; import com.lunarclient.apollo.example.module.impl.StopwatchExample; +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.hud.v1.HudPosition; +import com.lunarclient.apollo.stopwatch.v1.AddStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.AddTimerMessage; +import com.lunarclient.apollo.stopwatch.v1.RemoveStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.RemoveTimerMessage; import com.lunarclient.apollo.stopwatch.v1.ResetStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.ResetStopwatchesMessage; +import com.lunarclient.apollo.stopwatch.v1.ResetTimerMessage; +import com.lunarclient.apollo.stopwatch.v1.ResetTimersMessage; import com.lunarclient.apollo.stopwatch.v1.StartStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.StartTimerMessage; import com.lunarclient.apollo.stopwatch.v1.StopStopwatchMessage; +import com.lunarclient.apollo.stopwatch.v1.StopTimerMessage; +import java.awt.Color; +import java.time.Duration; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.entity.Player; public class StopwatchProtoExample extends StopwatchExample { + @Override + public void addStopwatchExample(Player viewer) { + AddStopwatchMessage message = AddStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .setName("Parkour") + .setResetOnStart(true) + .setPreventModification(true) + .setHideWhenStopped(false) + .setDisplayFormat("mm:ss") + .setTextColor(ProtobufUtil.createColorProto(new Color(0, 170, 170))) + .setHudPosition(HudPosition.newBuilder() + .setX(-30) + .setY(30) + .build() + ) + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + + @Override + public void removeStopwatchExample(Player viewer) { + RemoveStopwatchMessage message = RemoveStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + @Override public void startStopwatchExample(Player viewer) { - StartStopwatchMessage message = StartStopwatchMessage.getDefaultInstance(); + StartStopwatchMessage message = StartStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .build(); + ProtobufPacketUtil.sendPacket(viewer, message); } @Override public void stopStopwatchExample(Player viewer) { - StopStopwatchMessage message = StopStopwatchMessage.getDefaultInstance(); + StopStopwatchMessage message = StopStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .build(); + ProtobufPacketUtil.sendPacket(viewer, message); } @Override public void resetStopwatchExample(Player viewer) { - ResetStopwatchMessage message = ResetStopwatchMessage.getDefaultInstance(); + ResetStopwatchMessage message = ResetStopwatchMessage.newBuilder() + .setId("parkour-stopwatch") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + + @Override + public void resetStopwatchesExample(Player viewer) { + ResetStopwatchesMessage message = ResetStopwatchesMessage.getDefaultInstance(); + ProtobufPacketUtil.sendPacket(viewer, message); + } + + @Override + public void addTimerExample(Player viewer) { + AddTimerMessage message = AddTimerMessage.newBuilder() + .setId("game-timer") + .setName("Countdown") + .setDuration(ProtobufUtil.createDurationProto(Duration.ofSeconds(45))) + .setLoop(false) + .setPreventModification(true) + .setHideWhenStopped(false) + .setDisplayFormat("mm:ss") + .setTitleTextAdventureJsonLines(AdventureUtil.toJson( + Component.text("Time's up!", NamedTextColor.RED) + )) + .setInGameNotification(true) + .setTextColor(ProtobufUtil.createColorProto(new Color(255, 85, 255))) + .setHudPosition(HudPosition.newBuilder() + .setX(-10) + .setY(30) + .build() + ) + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + + @Override + public void removeTimerExample(Player viewer) { + RemoveTimerMessage message = RemoveTimerMessage.newBuilder() + .setId("game-timer") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + + @Override + public void startTimerExample(Player viewer) { + StartTimerMessage message = StartTimerMessage.newBuilder() + .setId("game-timer") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + + @Override + public void stopTimerExample(Player viewer) { + StopTimerMessage message = StopTimerMessage.newBuilder() + .setId("game-timer") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + + @Override + public void resetTimerExample(Player viewer) { + ResetTimerMessage message = ResetTimerMessage.newBuilder() + .setId("game-timer") + .build(); + + ProtobufPacketUtil.sendPacket(viewer, message); + } + + @Override + public void resetTimersExample(Player viewer) { + ResetTimersMessage message = ResetTimersMessage.getDefaultInstance(); ProtobufPacketUtil.sendPacket(viewer, message); } diff --git a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/util/ProtobufPacketUtil.java b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/util/ProtobufPacketUtil.java index e2130a6b4..f719ecfcd 100644 --- a/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/util/ProtobufPacketUtil.java +++ b/example/bukkit/proto/src/main/java/com/lunarclient/apollo/example/proto/util/ProtobufPacketUtil.java @@ -42,7 +42,7 @@ public final class ProtobufPacketUtil { private static final List APOLLO_MODULES = Arrays.asList("auto_text_hotkey", "beam", "border", "chat", "colored_fire", "combat", "cooldown", - "entity", "glint", "glow", "hologram", "inventory", "limb", "mod_setting", "nametag", "nick_hider", "notification", "pay_now", "packet_enrichment", + "cosmetic", "entity", "glint", "glow", "hologram", "inventory", "limb", "mod_setting", "nametag", "nick_hider", "notification", "pay_now", "packet_enrichment", "rich_presence", "saturation", "server_rule", "staff_mod", "stopwatch", "team", "tebex", "title", "tnt_countdown", "transfer", "vignette", "waypoint" ); 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 2c35b3c34..373fb9fa0 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 @@ -29,6 +29,7 @@ import com.lunarclient.apollo.common.v1.Cuboid2D; import com.lunarclient.apollo.common.v1.EntityId; import com.lunarclient.apollo.common.v1.ItemStackIcon; +import com.lunarclient.apollo.common.v1.Profile; import com.lunarclient.apollo.common.v1.ResourceLocationIcon; import com.lunarclient.apollo.common.v1.SimpleResourceLocationIcon; import com.lunarclient.apollo.common.v1.Uuid; @@ -115,6 +116,10 @@ public static Location toBukkitLocation(com.lunarclient.apollo.common.v1.PlayerL } public static ItemStackIcon createItemStackIconProto(@Nullable String itemName, int itemId, int customModelData) { + return ProtobufUtil.createItemStackIconProto(itemName, itemId, customModelData, null); + } + + public static ItemStackIcon createItemStackIconProto(@Nullable String itemName, int itemId, int customModelData, @Nullable Profile profile) { ItemStackIcon.Builder iconBuilder = ItemStackIcon.newBuilder() .setItemId(itemId) .setCustomModelData(customModelData); @@ -123,9 +128,25 @@ public static ItemStackIcon createItemStackIconProto(@Nullable String itemName, iconBuilder.setItemName(itemName); } + if (profile != null) { + iconBuilder.setProfile(profile); + } + return iconBuilder.build(); } + public static Profile createProfileProto(@Nullable UUID id, String texture, String signature) { + Profile.Builder builder = Profile.newBuilder() + .setTexture(texture) + .setSignature(signature); + + if (id != null) { + builder.setId(ProtobufUtil.createUuidProto(id)); + } + + return builder.build(); + } + public static ResourceLocationIcon createResourceLocationIconProto(String resourceLocation) { return ResourceLocationIcon.newBuilder() .setResourceLocation(resourceLocation) diff --git a/example/bukkit/proto/src/main/resources/plugin.yml b/example/bukkit/proto/src/main/resources/plugin.yml index 6b06ae189..442e5445b 100644 --- a/example/bukkit/proto/src/main/resources/plugin.yml +++ b/example/bukkit/proto/src/main/resources/plugin.yml @@ -1,12 +1,14 @@ name: Apollo-Proto-Example main: com.lunarclient.apollo.example.proto.ApolloProtoExamplePlatform -version: 1.2.5 +version: 1.2.6 author: Moonsworth softdepend: [ Apollo-Bukkit ] api-version: 1.13 folia-supported: true commands: + npc: + description: "NPCs!" autotexthotkey: description: "Auto Text Hotkey!" beam: @@ -21,6 +23,8 @@ commands: description: "Combat!" cooldown: description: "Cooldowns!" + cosmetic: + description: "Cosmetics!" entity: description: "Entity!" glint: diff --git a/example/minestom/api/build.gradle.kts b/example/minestom/api/build.gradle.kts index 47474fed8..74e85d9a2 100644 --- a/example/minestom/api/build.gradle.kts +++ b/example/minestom/api/build.gradle.kts @@ -20,4 +20,8 @@ tasks { attributes["Main-Class"] = "com.lunarclient.apollo.example.ApolloMinestomExample" } } + + shadowJar { + relocators.empty() + } } diff --git a/extra/adventure4/build.gradle.kts b/extra/adventure4/build.gradle.kts index a362a2a0b..ff57f1851 100644 --- a/extra/adventure4/build.gradle.kts +++ b/extra/adventure4/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.github.johnrengelman.shadow") + id("com.gradleup.shadow") id("apollo.publish-conventions") } diff --git a/gradle.properties b/gradle.properties index 21d9025d0..54bb2c7c4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=com.lunarclient -version=1.2.5 +version=1.2.6 description=The API for interacting with Lunar Client players. org.gradle.parallel=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 573ee4a23..cb68d5c45 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,12 +11,13 @@ geantyref = "1.3.11" idea = "1.1.7" jetbrains = "24.0.1" lombok = "1.18.38" -protobuf = "0.1.0" +protobuf = "0.1.6" gson = "2.10.1" -shadow = "8.1.1" -spotless = "6.13.0" +shadow = "9.4.1" +spotless = "8.4.0" velocity = "3.3.0-SNAPSHOT" folia = "1.20.1-R0.1-SNAPSHOT" +paper = "26.1.1.build.+" asm = "9.7.1" minestom = "2025.08.18-1.21.8" @@ -48,6 +49,9 @@ bukkit = { module = "org.spigotmc:spigot", version.ref = "bukkit" } # folia folia = { module = "dev.folia:folia-api", version.ref = "folia" } +# paper +paper-api = { module = "io.papermc.paper:paper-api", version.ref = "paper" } + # bungee bungee = { module = "net.md-5:bungeecord-api", version.ref = "bungee" } @@ -60,7 +64,7 @@ minestom = { module = "net.minestom:minestom", version.ref = "minestom" } # build artifactregistry = { module = "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin", version.ref = "artifactregistry" } spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } -shadow = { module = "com.github.johnrengelman:shadow", version.ref = "shadow" } +shadow = { module = "com.gradleup.shadow:shadow-gradle-plugin", version.ref = "shadow" } idea = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "idea" } asm = { module = "org.ow2.asm:asm", version.ref = "asm" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4c..1b33c55ba 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f853b1c..c61a118f7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index fcb6fca14..23d15a936 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -83,7 +85,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -111,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -144,7 +147,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +155,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -201,16 +204,16 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 6689b85be..5eed7ee84 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,22 +59,22 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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 8965682cf..220be9b17 100644 --- a/platform/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java +++ b/platform/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java @@ -43,6 +43,8 @@ import com.lunarclient.apollo.module.combat.CombatModule; import com.lunarclient.apollo.module.cooldown.CooldownModule; import com.lunarclient.apollo.module.cooldown.CooldownModuleImpl; +import com.lunarclient.apollo.module.cosmetic.CosmeticModule; +import com.lunarclient.apollo.module.cosmetic.CosmeticModuleImpl; import com.lunarclient.apollo.module.entity.EntityModule; import com.lunarclient.apollo.module.entity.EntityModuleImpl; import com.lunarclient.apollo.module.glint.GlintModule; @@ -138,6 +140,7 @@ public void onEnable() { .addModule(BeamModule.class, new BeamModuleImpl()) .addModule(BorderModule.class, new BorderModuleImpl()) .addModule(ChatModule.class, new ChatModuleImpl()) + .addModule(CosmeticModule.class, new CosmeticModuleImpl()) .addModule(ColoredFireModule.class, new ColoredFireModuleImpl()) .addModule(CombatModule.class) .addModule(CooldownModule.class, new CooldownModuleImpl()) diff --git a/platform/bukkit/src/platform-loader/resources/plugin.yml b/platform/bukkit/src/platform-loader/resources/plugin.yml index 590a4f9c1..42078f15f 100644 --- a/platform/bukkit/src/platform-loader/resources/plugin.yml +++ b/platform/bukkit/src/platform-loader/resources/plugin.yml @@ -1,6 +1,6 @@ name: Apollo-Bukkit main: com.lunarclient.apollo.loader.BukkitPlatformLoader -version: 1.2.5 +version: 1.2.6 author: Moonsworth api-version: 1.13 soft-depend: [LunarClient-API] 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 7e958dc05..76cad0837 100644 --- a/platform/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java +++ b/platform/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java @@ -42,6 +42,8 @@ import com.lunarclient.apollo.module.combat.CombatModule; import com.lunarclient.apollo.module.cooldown.CooldownModule; import com.lunarclient.apollo.module.cooldown.CooldownModuleImpl; +import com.lunarclient.apollo.module.cosmetic.CosmeticModule; +import com.lunarclient.apollo.module.cosmetic.CosmeticModuleImpl; import com.lunarclient.apollo.module.entity.EntityModule; import com.lunarclient.apollo.module.entity.EntityModuleImpl; import com.lunarclient.apollo.module.hologram.HologramModule; @@ -122,6 +124,7 @@ public void onEnable() { .addModule(BeamModule.class, new BeamModuleImpl()) .addModule(BorderModule.class, new BorderModuleImpl()) .addModule(ChatModule.class, new ChatModuleImpl()) + .addModule(CosmeticModule.class, new CosmeticModuleImpl()) .addModule(ColoredFireModule.class, new ColoredFireModuleImpl()) .addModule(CombatModule.class) .addModule(CooldownModule.class, new CooldownModuleImpl()) diff --git a/platform/bungee/src/platform-loader/resources/plugin.yml b/platform/bungee/src/platform-loader/resources/plugin.yml index 1fdab7a27..9778623fd 100644 --- a/platform/bungee/src/platform-loader/resources/plugin.yml +++ b/platform/bungee/src/platform-loader/resources/plugin.yml @@ -1,4 +1,4 @@ name: Apollo-Bungee main: com.lunarclient.apollo.loader.BungeePlatformLoader -version: 1.2.5 +version: 1.2.6 author: Moonsworth 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 e28530da7..ecf29b87b 100644 --- a/platform/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java +++ b/platform/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java @@ -42,6 +42,8 @@ import com.lunarclient.apollo.module.combat.CombatModule; import com.lunarclient.apollo.module.cooldown.CooldownModule; import com.lunarclient.apollo.module.cooldown.CooldownModuleImpl; +import com.lunarclient.apollo.module.cosmetic.CosmeticModule; +import com.lunarclient.apollo.module.cosmetic.CosmeticModuleImpl; import com.lunarclient.apollo.module.entity.EntityModule; import com.lunarclient.apollo.module.entity.EntityModuleImpl; import com.lunarclient.apollo.module.glow.GlowModule; @@ -128,6 +130,7 @@ public void onEnable() { .addModule(BeamModule.class, new BeamModuleImpl()) .addModule(BorderModule.class, new BorderModuleImpl()) .addModule(ChatModule.class, new ChatModuleImpl()) + .addModule(CosmeticModule.class, new CosmeticModuleImpl()) .addModule(ColoredFireModule.class, new ColoredFireModuleImpl()) .addModule(CombatModule.class) .addModule(CooldownModule.class, new CooldownModuleImpl()) diff --git a/platform/folia/src/main/resources/plugin.yml b/platform/folia/src/main/resources/plugin.yml index cfe42463c..af84c17c4 100644 --- a/platform/folia/src/main/resources/plugin.yml +++ b/platform/folia/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: Apollo-Folia main: com.lunarclient.apollo.ApolloFoliaPlatform -version: 1.2.5 +version: 1.2.6 author: Moonsworth api-version: 1.13 folia-supported: true 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 a461c6428..fb47f48c3 100644 --- a/platform/minestom/src/main/java/com/lunarclient/apollo/ApolloMinestomPlatform.java +++ b/platform/minestom/src/main/java/com/lunarclient/apollo/ApolloMinestomPlatform.java @@ -42,6 +42,8 @@ import com.lunarclient.apollo.module.combat.CombatModule; import com.lunarclient.apollo.module.cooldown.CooldownModule; import com.lunarclient.apollo.module.cooldown.CooldownModuleImpl; +import com.lunarclient.apollo.module.cosmetic.CosmeticModule; +import com.lunarclient.apollo.module.cosmetic.CosmeticModuleImpl; import com.lunarclient.apollo.module.entity.EntityModule; import com.lunarclient.apollo.module.entity.EntityModuleImpl; import com.lunarclient.apollo.module.glint.GlintModule; @@ -160,6 +162,7 @@ public static void init(ApolloMinestomProperties properties) { .addModule(BeamModule.class, new BeamModuleImpl()) .addModule(BorderModule.class, new BorderModuleImpl()) .addModule(ChatModule.class, new ChatModuleImpl()) + .addModule(CosmeticModule.class, new CosmeticModuleImpl()) .addModule(ColoredFireModule.class, new ColoredFireModuleImpl()) .addModule(CombatModule.class) .addModule(CooldownModule.class, new CooldownModuleImpl()) @@ -240,7 +243,7 @@ public Options getOptions() { @Override public String getApolloVersion() { - return "1.2.5"; + return "1.2.6"; } @Override 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 23a0919fe..2825214a7 100644 --- a/platform/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java +++ b/platform/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java @@ -42,6 +42,8 @@ import com.lunarclient.apollo.module.combat.CombatModule; import com.lunarclient.apollo.module.cooldown.CooldownModule; import com.lunarclient.apollo.module.cooldown.CooldownModuleImpl; +import com.lunarclient.apollo.module.cosmetic.CosmeticModule; +import com.lunarclient.apollo.module.cosmetic.CosmeticModuleImpl; import com.lunarclient.apollo.module.entity.EntityModule; import com.lunarclient.apollo.module.entity.EntityModuleImpl; import com.lunarclient.apollo.module.hologram.HologramModule; @@ -107,7 +109,7 @@ @Plugin( id = "apollo", name = "Apollo-Velocity", - version = "1.2.5", + version = "1.2.6", url = "https://moonsworth.com", description = "Implementation of Apollo for Velocity", authors = {"Moonsworth"} @@ -187,6 +189,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) { .addModule(BeamModule.class, new BeamModuleImpl()) .addModule(BorderModule.class, new BorderModuleImpl()) .addModule(ChatModule.class, new ChatModuleImpl()) + .addModule(CosmeticModule.class, new CosmeticModuleImpl()) .addModule(ColoredFireModule.class, new ColoredFireModuleImpl()) .addModule(CombatModule.class) .addModule(CooldownModule.class, new CooldownModuleImpl()) diff --git a/settings.gradle.kts b/settings.gradle.kts index 8afeca4a6..ff9bce4dc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -35,6 +35,7 @@ listOfNotNull( "extra:adventure4", "api", "common", + "example:bukkit:nms", "example:bukkit:common", "example:bukkit:api", "example:bukkit:json", @@ -42,7 +43,9 @@ listOfNotNull( "example:minestom:api" ).forEach { include(it) - findProject(":$it")?.name = "apollo-${it.replace(':', '-')}" + findProject(":$it")?.let { proj -> + proj.name = "apollo-${it.replace(':', '-')}" + } } @@ -54,5 +57,7 @@ listOfNotNull( if (loadAllVersions) "platform:minestom" else null ).forEach { include(it) - findProject(":$it")?.name = "apollo-${it.split(':').last()}" + findProject(":$it")?.let { proj -> + proj.name = "apollo-${it.split(':').last()}" + } }