Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
fi

- name: Gradle Publish
if: "${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/version/') || github.ref == 'refs/heads/feature/totem-counter-module' }}"
if: "${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/version/') }}"
run: ./gradlew publish

- name: Gradle Release
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,27 @@
package com.lunarclient.apollo.event.modsetting;

import com.lunarclient.apollo.event.Event;
import com.lunarclient.apollo.module.RestrictedAccess;
import com.lunarclient.apollo.module.modsetting.ModSettingModule;
import com.lunarclient.apollo.option.Option;
import com.lunarclient.apollo.player.ApolloPlayer;
import lombok.Value;
import org.jetbrains.annotations.Nullable;

/**
* Represents an event that is fired when an option is updated.
* Represents an event that is fired when a player's Lunar Client mod option is updated.
*
* <p>This event is fired for live updates only, not for the options listed in a
* {@link ModSettingModule#requestModStatus(ApolloPlayer)} response.</p>
*
* <p>This event is part of the private Mod Status API of {@link ModSettingModule}. Lunar Client
* only reports mod status to servers that have been granted access, so this event is never
* fired on other servers.</p>
*
* @since 1.2.1
*/
@Value
@RestrictedAccess
public class ApolloUpdateModOptionEvent implements Event {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Marks access restricted Apollo API, either a whole module or part of a public module.
*
* <p>Restricted API only works on servers approved by the Lunar Client team. Lunar Client
* enforces this, so without approval the API does not throw, it just has no effect.</p>
*
* <p>See <a href="https://lunarclient.dev/apollo/developers/private-modules">Private Modules</a>
* to request access.</p>
*
* @since 1.3.0
*/
@Documented
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface RestrictedAccess {
// Marker annotation.
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.lunarclient.apollo.common.location.ApolloBlockLocation;
import com.lunarclient.apollo.module.ApolloModule;
import com.lunarclient.apollo.module.ModuleDefinition;
import com.lunarclient.apollo.module.RestrictedAccess;
import com.lunarclient.apollo.module.cosmetic.options.BodyOptions;
import com.lunarclient.apollo.module.cosmetic.options.CloakOptions;
import com.lunarclient.apollo.module.cosmetic.options.CosmeticOptions;
Expand All @@ -41,8 +42,11 @@
/**
* Represents the cosmetic module.
*
* <p>Access to this module is {@link RestrictedAccess restricted}.</p>
*
* @since 1.2.6
*/
@RestrictedAccess
@ApiStatus.NonExtendable
@ModuleDefinition(id = "cosmetic", name = "Cosmetic")
public abstract class CosmeticModule extends ApolloModule {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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.modsetting;

import com.lunarclient.apollo.module.RestrictedAccess;
import com.lunarclient.apollo.option.Option;
import lombok.Builder;
import lombok.Getter;

/**
* Represents the current value of a Lunar Client mod option.
*
* @since 1.3.0
*/
@Getter
@Builder
@RestrictedAccess
public final class ModOptionStatus {

/**
* Returns the mod {@link Option}.
*
* @return the option
* @since 1.3.0
*/
Option<?, ?, ?> option;

/**
* Returns the current {@link Object} value of the option.
*
* @return the value
* @since 1.3.0
*/
Object value;

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.lunarclient.apollo.async.Future;
import com.lunarclient.apollo.module.ApolloModule;
import com.lunarclient.apollo.module.ModuleDefinition;
import com.lunarclient.apollo.module.RestrictedAccess;
import com.lunarclient.apollo.option.Option;
import com.lunarclient.apollo.player.ApolloPlayer;
import com.lunarclient.apollo.util.ConfigTarget;
Expand Down Expand Up @@ -65,6 +66,7 @@ public boolean isClientNotify() {
* @return the value of the option
* @since 1.2.1
*/
@RestrictedAccess
public abstract <T, C extends Option<T, ?, ?>> T getStatus(@NotNull ApolloPlayer player, @NonNull C option);

/**
Expand All @@ -76,4 +78,16 @@ public boolean isClientNotify() {
*/
public abstract Future<InstalledModsResponse> requestInstalledMods(ApolloPlayer player);

/**
* Sends the {@link ModStatusRequest} to the {@link ApolloPlayer}.
*
* <p>The response also updates {@link #getStatus(ApolloPlayer, Option)}.</p>
*
* @param player the player
* @return future to be listened to for errors/success
* @since 1.3.0
*/
@RestrictedAccess
public abstract Future<ModStatusResponse> requestModStatus(ApolloPlayer player);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.modsetting;

import com.lunarclient.apollo.module.RestrictedAccess;
import com.lunarclient.apollo.roundtrip.ApolloRequest;
import lombok.Builder;

/**
* Represents the mod status request.
*
* @since 1.3.0
*/
@Builder
@RestrictedAccess
public final class ModStatusRequest extends ApolloRequest<ModStatusResponse> {

}
Original file line number Diff line number Diff line change
@@ -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.module.modsetting;

import com.lunarclient.apollo.module.RestrictedAccess;
import com.lunarclient.apollo.roundtrip.pagination.ApolloPaginatedResponse;
import java.util.List;
import java.util.UUID;
import lombok.Getter;
import lombok.experimental.SuperBuilder;

/**
* Represents the mod status response.
*
* <p>The elements are the mod options the player has changed from their default
* value. Any option that is not listed is at its default value.</p>
*
* @since 1.3.0
*/
@Getter
@SuperBuilder
@RestrictedAccess
public final class ModStatusResponse extends ApolloPaginatedResponse<ModOptionStatus> {

@Override
public ApolloPaginatedResponse<ModOptionStatus> combine(UUID packetId, List<ModOptionStatus> elements) {
return ModStatusResponse.builder()
.packetId(packetId)
.elements(elements)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@
import com.lunarclient.apollo.modsetting.v1.Mod;
import com.lunarclient.apollo.module.modsetting.InstalledModsRequest;
import com.lunarclient.apollo.module.modsetting.InstalledModsResponse;
import com.lunarclient.apollo.module.modsetting.ModOptionStatus;
import com.lunarclient.apollo.module.modsetting.ModSettingModule;
import com.lunarclient.apollo.module.modsetting.ModStatusRequest;
import com.lunarclient.apollo.module.modsetting.ModStatusResponse;
import com.lunarclient.apollo.network.NetworkOptions;
import com.lunarclient.apollo.option.Option;
import com.lunarclient.apollo.option.StatusOptionsImpl;
import com.lunarclient.apollo.player.AbstractApolloPlayer;
import com.lunarclient.apollo.player.ApolloPlayer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -89,6 +93,17 @@ public Future<InstalledModsResponse> requestInstalledMods(@NotNull ApolloPlayer
return ((AbstractApolloPlayer) player).sendRoundTripPacket(request, requestProto);
}

@Override
public Future<ModStatusResponse> requestModStatus(@NotNull ApolloPlayer player) {
ModStatusRequest request = ModStatusRequest.builder().build();

com.lunarclient.apollo.modsetting.v1.ModStatusRequest requestProto = com.lunarclient.apollo.modsetting.v1.ModStatusRequest.newBuilder()
.setRequestId(ByteString.copyFromUtf8(request.getRequestId().toString()))
.build();

return ((AbstractApolloPlayer) player).sendRoundTripPacket(request, requestProto);
}

private void onReceivePacket(ApolloReceivePacketEvent event) {
ApolloPlayer player = event.getPlayer();
Any any = event.getPacket();
Expand All @@ -112,6 +127,17 @@ private void onReceivePacket(ApolloReceivePacketEvent event) {

ApolloManager.getRoundtripManager().handleResponse(response);
});

event.unpack(com.lunarclient.apollo.modsetting.v1.ModStatusResponse.class).ifPresent(packet -> {
ModStatusResponse response = ModStatusResponse.builder()
.packetId(UUID.fromString(packet.getRequestId().toStringUtf8()))
.page(packet.getPage())
.totalPages(packet.getTotalPages())
.elements(this.updateModStatus(player, packet))
.build();

ApolloManager.getRoundtripManager().handleResponse(response);
});
}

private LunarClientMod fromProtobuf(LunarClientModType type, Mod mod) {
Expand Down Expand Up @@ -188,4 +214,32 @@ public void updateOptions(ApolloPlayer player, Map<String, Value> properties, bo
}
}

private List<ModOptionStatus> updateModStatus(ApolloPlayer player, com.lunarclient.apollo.modsetting.v1.ModStatusResponse packet) {
StatusOptionsImpl statusOptions = ApolloManager.getModsManager().getPlayerOptions();

if (packet.getPage() == 0) {
statusOptions.getPlayerOptions().remove(player.getUniqueId());
}

List<ModOptionStatus> elements = new ArrayList<>();
for (com.lunarclient.apollo.modsetting.v1.ModOptionStatus status : packet.getModOptionsList()) {
Option<?, ?, ?> option = statusOptions.getRegistry().get(status.getKey());

// Option exists on the client but doesn't exist in the API yet.
if (option == null) {
continue;
}

Object value = NetworkOptions.unwrapValue(status.getValue(), option.getTypeToken().getType());
statusOptions.set(player, option, value);

elements.add(ModOptionStatus.builder()
.option(option)
.value(value)
.build());
}

return elements;
}

}
Loading
Loading