Skip to content

Commit d13f8c2

Browse files
committed
NPC Cosmetic module!
1 parent 9c75d93 commit d13f8c2

44 files changed

Lines changed: 1107 additions & 49 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.checkstyle/suppressions.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<!DOCTYPE suppressions PUBLIC "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "http://checkstyle.org/dtds/suppressions_1_2.dtd">
33
<suppressions>
44
<!-- don't require javadocs on platform modules -->
5-
<suppress files="example[\\/](bukkit|minestom)[\\/](api|json|proto|common)[\\/]src[\\/]main[\\/]java[\\/].*" checks="(FilteringWriteTag|MissingJavadoc.*)"/>
5+
<suppress files="example[\\/](bukkit|minestom)[\\/](api|json|proto|common|nms)[\\/]src[\\/]main[\\/]java[\\/].*" checks="(FilteringWriteTag|MissingJavadoc.*)"/>
66

77
<!-- ignore illegal import in loader -->
88
<suppress files="extra[\\/]loader[\\/]src[\\/]main[\\/]java[\\/].*" checks="(IllegalImport)"/>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* This file is part of Apollo, licensed under the MIT License.
3+
*
4+
* Copyright (c) 2026 Moonsworth
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.lunarclient.apollo.module.cosmetic;
25+
26+
import com.lunarclient.apollo.common.ApolloEntity;
27+
import com.lunarclient.apollo.module.ApolloModule;
28+
import com.lunarclient.apollo.module.ModuleDefinition;
29+
import com.lunarclient.apollo.recipients.Recipients;
30+
import java.util.List;
31+
import org.jetbrains.annotations.ApiStatus;
32+
33+
/**
34+
* Represents the cosmetic module, responsible for applying cosmetics onto player NPCs.
35+
*
36+
* @since 1.2.6
37+
*/
38+
@ApiStatus.NonExtendable
39+
@ModuleDefinition(id = "cosmetic", name = "Cosmetic")
40+
public abstract class CosmeticModule extends ApolloModule {
41+
42+
/**
43+
* Equips the provided cosmetics on an NPC for the given {@link Recipients}.
44+
*
45+
* @param recipients the recipients that are receiving the packet
46+
* @param entity the {@link ApolloEntity} of the NPC to equip the cosmetics on
47+
* @param cosmeticIds the list of cosmetic ids to equip
48+
* @since 1.2.6
49+
*/
50+
public abstract void equipNpcCosmetics(Recipients recipients, ApolloEntity entity, List<Integer> cosmeticIds);
51+
52+
/**
53+
* Unequips the provided cosmetics from an NPC for the given {@link Recipients}.
54+
*
55+
* @param recipients the recipients that are receiving the packet
56+
* @param entity the {@link ApolloEntity} of the NPC to unequip the cosmetics from
57+
* @param cosmeticIds the list of cosmetic ids to unequip
58+
* @since 1.2.6
59+
*/
60+
public abstract void unequipNpcCosmetics(Recipients recipients, ApolloEntity entity, List<Integer> cosmeticIds);
61+
62+
/**
63+
* Resets all cosmetics on an NPC for the given {@link Recipients}.
64+
*
65+
* @param recipients the recipients that are receiving the packet
66+
* @param entity the {@link ApolloEntity} of the NPC to reset the cosmetics on
67+
* @since 1.2.6
68+
*/
69+
public abstract void resetNpcCosmetics(Recipients recipients, ApolloEntity entity);
70+
71+
}

build-logic/build.gradle.kts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,12 @@ dependencies {
1515
}
1616

1717
java {
18-
sourceCompatibility = JavaVersion.VERSION_1_8
19-
targetCompatibility = JavaVersion.VERSION_1_8
18+
sourceCompatibility = JavaVersion.VERSION_17
19+
targetCompatibility = JavaVersion.VERSION_17
2020
}
2121

2222
kotlin {
23-
target {
24-
compilations.configureEach {
25-
kotlinOptions {
26-
jvmTarget = "1.8"
27-
}
28-
}
23+
compilerOptions {
24+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
2925
}
3026
}

build-logic/src/main/kotlin/apollo.shadow-conventions.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
22

33
plugins {
44
id("apollo.base-conventions")
5-
id("com.github.johnrengelman.shadow")
5+
id("com.gradleup.shadow")
66
}
77

88
tasks {

build-logic/src/main/kotlin/extensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ fun Project.setupDynamicLoader() {
147147
archiveClassifier.set("all")
148148
}
149149

150-
val shadowJarLoader by tasks.creating(ShadowJar::class) {
150+
val shadowJarLoader by tasks.registering(ShadowJar::class) {
151151
archiveClassifier.set("")
152152
configurations = listOf(loaderImplementationConfig.get())
153153

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* This file is part of Apollo, licensed under the MIT License.
3+
*
4+
* Copyright (c) 2026 Moonsworth
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.lunarclient.apollo.module.cosmetic;
25+
26+
import com.lunarclient.apollo.common.ApolloEntity;
27+
import com.lunarclient.apollo.cosmetic.v1.EquipNpcCosmeticsMessage;
28+
import com.lunarclient.apollo.cosmetic.v1.ResetNpcCosmeticsMessage;
29+
import com.lunarclient.apollo.cosmetic.v1.UnequipNpcCosmeticsMessage;
30+
import com.lunarclient.apollo.network.NetworkTypes;
31+
import com.lunarclient.apollo.player.AbstractApolloPlayer;
32+
import com.lunarclient.apollo.recipients.Recipients;
33+
import java.util.List;
34+
import lombok.NonNull;
35+
36+
/**
37+
* Provides the cosmetic module.
38+
*
39+
* @since 1.2.6
40+
*/
41+
public final class CosmeticModuleImpl extends CosmeticModule {
42+
43+
@Override
44+
public void equipNpcCosmetics(@NonNull Recipients recipients, @NonNull ApolloEntity entity, @NonNull List<Integer> cosmeticIds) {
45+
EquipNpcCosmeticsMessage message = EquipNpcCosmeticsMessage.newBuilder()
46+
.setEntityId(NetworkTypes.toProtobuf(entity))
47+
.addAllCosmeticIds(cosmeticIds)
48+
.build();
49+
50+
recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message));
51+
}
52+
53+
@Override
54+
public void unequipNpcCosmetics(@NonNull Recipients recipients, @NonNull ApolloEntity entity, @NonNull List<Integer> cosmeticIds) {
55+
UnequipNpcCosmeticsMessage message = UnequipNpcCosmeticsMessage.newBuilder()
56+
.setEntityId(NetworkTypes.toProtobuf(entity))
57+
.addAllCosmeticIds(cosmeticIds)
58+
.build();
59+
60+
recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message));
61+
}
62+
63+
@Override
64+
public void resetNpcCosmetics(@NonNull Recipients recipients, @NonNull ApolloEntity entity) {
65+
ResetNpcCosmeticsMessage message = ResetNpcCosmeticsMessage.newBuilder()
66+
.setEntityId(NetworkTypes.toProtobuf(entity))
67+
.build();
68+
69+
recipients.forEach(player -> ((AbstractApolloPlayer) player).sendPacket(message));
70+
}
71+
72+
}

docs/developers/lightweight/json/object-util.mdx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,13 @@ public static JsonObject createCuboid2DObject(double minX, double minZ, double m
5252
}
5353

5454
public static JsonObject createEntityIdObject(@NotNull Entity entity) {
55+
return JsonUtil.createEntityIdObject(entity.getEntityId(), entity.getUniqueId());
56+
}
57+
58+
public static JsonObject createEntityIdObject(int entityId, @NotNull UUID uuid) {
5559
JsonObject entityIdObject = new JsonObject();
56-
entityIdObject.addProperty("entity_id", entity.getEntityId());
57-
entityIdObject.add("entity_uuid", JsonUtil.createUuidObject(entity.getUniqueId()));
60+
entityIdObject.addProperty("entity_id", entityId);
61+
entityIdObject.add("entity_uuid", JsonUtil.createUuidObject(uuid));
5862
return entityIdObject;
5963
}
6064
```

docs/developers/lightweight/protobuf/getting-started.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Available fields for each message, including their types, are available on the B
2626
<dependency>
2727
<groupId>com.lunarclient</groupId>
2828
<artifactId>apollo-protos</artifactId>
29-
<version>0.1.0</version>
29+
<version>0.1.4</version>
3030
</dependency>
3131
</dependencies>
3232
```
@@ -41,7 +41,7 @@ Available fields for each message, including their types, are available on the B
4141
}
4242
4343
dependencies {
44-
api 'com.lunarclient:apollo-protos:0.1.0'
44+
api 'com.lunarclient:apollo-protos:0.1.4'
4545
}
4646
```
4747
</Tab>
@@ -55,7 +55,7 @@ Available fields for each message, including their types, are available on the B
5555
}
5656

5757
dependencies {
58-
api("com.lunarclient:apollo-protos:0.1.0")
58+
api("com.lunarclient:apollo-protos:0.1.4")
5959
}
6060
```
6161
</Tab>
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { Tab, Tabs } from 'nextra-theme-docs'
2+
3+
# Cosmetic Module
4+
5+
## Overview
6+
7+
## Integration
8+
9+
### Sample Code
10+
Explore each integration by cycling through each tab to find the best fit for your requirements and needs.
11+
12+
<Tabs items={['Apollo API', 'apollo-protos library', 'Manual JSON Object Construction']}>
13+
14+
<Tab>
15+
16+
**Equip cosmetics on an NPC**
17+
18+
```java
19+
public void equipNpcCosmeticsExample(int entityId, UUID npcUuid, List<Integer> cosmeticIds) {
20+
this.cosmeticModule.equipNpcCosmetics(Recipients.ofEveryone(), new ApolloEntity(entityId, npcUuid), cosmeticIds);
21+
}
22+
```
23+
24+
**Unequip cosmetics from an NPC**
25+
26+
```java
27+
public void unequipNpcCosmeticsExample(int entityId, UUID npcUuid, List<Integer> cosmeticIds) {
28+
this.cosmeticModule.unequipNpcCosmetics(Recipients.ofEveryone(), new ApolloEntity(entityId, npcUuid), cosmeticIds);
29+
}
30+
```
31+
32+
**Reset all cosmetics on an NPC**
33+
34+
```java
35+
public void resetNpcCosmeticsExample(int entityId, UUID npcUuid) {
36+
this.cosmeticModule.resetNpcCosmetics(Recipients.ofEveryone(), new ApolloEntity(entityId, npcUuid));
37+
}
38+
```
39+
40+
</Tab>
41+
42+
<Tab>
43+
44+
**Equip cosmetics on an NPC**
45+
46+
```java
47+
public void equipNpcCosmeticsExample(int entityId, UUID npcUuid, List<Integer> cosmeticIds) {
48+
EquipNpcCosmeticsMessage message = EquipNpcCosmeticsMessage.newBuilder()
49+
.setEntityId(ProtobufUtil.createEntityIdProto(entityId, npcUuid))
50+
.addAllCosmeticIds(cosmeticIds)
51+
.build();
52+
53+
ProtobufPacketUtil.broadcastPacket(message);
54+
}
55+
```
56+
57+
**Unequip cosmetics from an NPC**
58+
59+
```java
60+
public void unequipNpcCosmeticsExample(int entityId, UUID npcUuid, List<Integer> cosmeticIds) {
61+
UnequipNpcCosmeticsMessage message = UnequipNpcCosmeticsMessage.newBuilder()
62+
.setEntityId(ProtobufUtil.createEntityIdProto(entityId, npcUuid))
63+
.addAllCosmeticIds(cosmeticIds)
64+
.build();
65+
66+
ProtobufPacketUtil.broadcastPacket(message);
67+
}
68+
```
69+
70+
**Reset all cosmetics on an NPC**
71+
72+
```java
73+
public void resetNpcCosmeticsExample(int entityId, UUID npcUuid) {
74+
ResetNpcCosmeticsMessage message = ResetNpcCosmeticsMessage.newBuilder()
75+
.setEntityId(ProtobufUtil.createEntityIdProto(entityId, npcUuid))
76+
.build();
77+
78+
ProtobufPacketUtil.broadcastPacket(message);
79+
}
80+
```
81+
82+
</Tab>
83+
84+
<Tab>
85+
86+
**Equip cosmetics on an NPC**
87+
88+
```java
89+
public void equipNpcCosmeticsExample(int entityId, UUID npcUuid, List<Integer> cosmeticIds) {
90+
JsonArray cosmeticIdsArray = new JsonArray();
91+
cosmeticIds.forEach(cosmeticIdsArray::add);
92+
93+
JsonObject message = new JsonObject();
94+
message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.EquipNpcCosmeticsMessage");
95+
message.add("entity_id", JsonUtil.createEntityIdObject(entityId, npcUuid));
96+
message.add("cosmetic_ids", cosmeticIdsArray);
97+
98+
JsonPacketUtil.broadcastPacket(message);
99+
}
100+
```
101+
102+
**Unequip cosmetics from an NPC**
103+
104+
```java
105+
public void unequipNpcCosmeticsExample(int entityId, UUID npcUuid, List<Integer> cosmeticIds) {
106+
JsonArray cosmeticIdsArray = new JsonArray();
107+
cosmeticIds.forEach(cosmeticIdsArray::add);
108+
109+
JsonObject message = new JsonObject();
110+
message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.UnequipNpcCosmeticsMessage");
111+
message.add("entity_id", JsonUtil.createEntityIdObject(entityId, npcUuid));
112+
message.add("cosmetic_ids", cosmeticIdsArray);
113+
114+
JsonPacketUtil.broadcastPacket(message);
115+
}
116+
```
117+
118+
**Reset all cosmetics on an NPC**
119+
120+
```java
121+
public void resetNpcCosmeticsExample(int entityId, UUID npcUuid) {
122+
JsonObject message = new JsonObject();
123+
message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.cosmetic.v1.ResetNpcCosmeticsMessage");
124+
message.add("entity_id", JsonUtil.createEntityIdObject(entityId, npcUuid));
125+
126+
JsonPacketUtil.broadcastPacket(message);
127+
}
128+
```
129+
130+
</Tab>
131+
132+
</Tabs>

example/bukkit/api/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ dependencies {
1515
compileOnly(libs.folia)
1616
implementation(project(":example:bukkit:apollo-example-bukkit-common"))
1717
}
18+
19+
tasks.shadowJar {
20+
manifest {
21+
attributes["paperweight-mappings-namespace"] = "mojang+yarn"
22+
}
23+
}

0 commit comments

Comments
 (0)