1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

Move away from Micronaut HTTP to Avaje Http to reduce jar size

This commit is contained in:
Tim203
2024-09-06 00:43:57 +02:00
parent ba825f76df
commit 330b46aa10
19 changed files with 150 additions and 91 deletions

View File

@@ -27,13 +27,14 @@ dependencies {
api(libs.micronaut.inject) api(libs.micronaut.inject)
annotationProcessor(libs.micronaut.inject.java) annotationProcessor(libs.micronaut.inject.java)
api(libs.micronaut.context) api(libs.micronaut.context)
api(libs.micronaut.http.client)
api(libs.micronaut.validation)
annotationProcessor(libs.micronaut.validation.processor)
api(libs.micronaut.serde.jsonp) //todo re-add validation
compileOnlyApi(libs.jakarta.jsonb) api(libs.avaje.http.client)
annotationProcessor(libs.micronaut.serde.processor) api(libs.avaje.http.api)
annotationProcessor(libs.avaje.http.client.generator)
implementation(libs.avaje.jsonb)
annotationProcessor(libs.avaje.jsonb.generator)
testImplementation(libs.junit.jupiter) testImplementation(libs.junit.jupiter)
testRuntimeOnly(libs.junit.platform.launcher) testRuntimeOnly(libs.junit.platform.launcher)

View File

@@ -27,7 +27,7 @@ package org.geysermc.floodgate.core.command;
import static org.geysermc.floodgate.core.platform.command.Placeholder.literal; import static org.geysermc.floodgate.core.platform.command.Placeholder.literal;
import io.micronaut.http.client.exceptions.HttpClientResponseException; import io.avaje.http.client.HttpException;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Singleton; import jakarta.inject.Singleton;
import java.util.UUID; import java.util.UUID;
@@ -129,14 +129,14 @@ public class WhitelistCommand implements FloodgateCommand {
xboxClient.xuidByGamertag(name) xboxClient.xuidByGamertag(name)
.whenComplete((result, error) -> { .whenComplete((result, error) -> {
if (error != null) { if (error != null) {
if (!(error instanceof HttpClientResponseException exception)) { if (!(error instanceof HttpException exception)) {
sender.sendMessage(Message.API_UNAVAILABLE); sender.sendMessage(Message.API_UNAVAILABLE);
error.printStackTrace(); error.printStackTrace();
return; return;
} }
sender.sendMessage(CommonCommandMessage.UNEXPECTED_ERROR); sender.sendMessage(CommonCommandMessage.UNEXPECTED_ERROR);
//todo proper non-200 status handler //todo proper non-200 status handler, for everything tbh
// var response = exception.getResponse().getBody(UnsuccessfulResponse.class); // var response = exception.getResponse().getBody(UnsuccessfulResponse.class);
// var message = // var message =
// response.isPresent() ? // response.isPresent() ?

View File

@@ -0,0 +1,72 @@
package org.geysermc.floodgate.core.http;
import io.avaje.http.client.HttpClient;
import io.avaje.http.client.JsonbBodyAdapter;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Property;
import io.micronaut.context.annotation.Value;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.util.concurrent.Executor;
import org.geysermc.floodgate.core.http.api.GlobalApiClient;
import org.geysermc.floodgate.core.http.downloads.DownloadClient;
import org.geysermc.floodgate.core.http.link.GlobalLinkClient;
import org.geysermc.floodgate.core.http.minecraft.MinecraftClient;
import org.geysermc.floodgate.core.http.mojang.SessionServerClient;
import org.geysermc.floodgate.core.http.xbox.XboxClient;
import org.geysermc.floodgate.isolation.library.LibraryManager;
@Factory
public class HttpClientFactory {
@Inject LibraryManager manager;
@Named("commonPool") Executor pool;
@Bean
GlobalApiClient globalApiClient(@Property(name = "http.baseUrl.api") String url) {
return create(url, GlobalApiClient.class);
}
@Bean
GlobalLinkClient globalLinkClient(@Value("${http.baseUrl.api}/v2/link") String url) {
return create(url, GlobalLinkClient.class);
}
@Bean
XboxClient xboxClient(@Value("${http.baseUrl.api}/v2/xbox") String url) {
return create(url, XboxClient.class);
}
@Bean
DownloadClient downloadClient(@Property(name = "http.baseUrl.download") String url) {
return create(url, DownloadClient.class);
}
@Bean
MinecraftClient minecraftClient() {
return create("https://api.minecraftservices.com/minecraft", MinecraftClient.class);
}
@Bean
SessionServerClient sessionServerClient() {
return create("https://sessionserver.mojang.com/session/minecraft", SessionServerClient.class);
}
private <T> T create(String baseUrl, Class<T> type) {
var thread = Thread.currentThread();
// for both Avaje's jsonb and http-client to work with it's generated classes.
// The generated classes are in the inner-jar (isolated), and the current context is on the outer-jar
var context = thread.getContextClassLoader();
thread.setContextClassLoader(manager.classLoader());
var result = HttpClient.builder()
.baseUrl(baseUrl)
.bodyAdapter(new JsonbBodyAdapter())
.executor(pool)
.build()
.create(type);
thread.setContextClassLoader(context);
return result;
}
}

View File

@@ -1,13 +1,12 @@
package org.geysermc.floodgate.core.http.api; package org.geysermc.floodgate.core.http.api;
import io.micronaut.http.HttpHeaders; import io.avaje.http.api.Client;
import io.micronaut.http.annotation.Get; import io.avaje.http.api.Get;
import io.micronaut.http.annotation.Header; import io.avaje.http.api.Headers;
import io.micronaut.http.client.annotation.Client;
import org.geysermc.floodgate.core.util.Constants; import org.geysermc.floodgate.core.util.Constants;
@Client("${http.baseUrl.api}") @Client
@Header(name = HttpHeaders.USER_AGENT, value = Constants.USER_AGENT) @Headers({"User-Agent: " + Constants.USER_AGENT})
public interface GlobalApiClient { public interface GlobalApiClient {
/** /**
* Checks if it can connect to the Global Api, any other status code than 204 or 404 will throw * Checks if it can connect to the Global Api, any other status code than 204 or 404 will throw

View File

@@ -1,16 +1,14 @@
package org.geysermc.floodgate.core.http.downloads; package org.geysermc.floodgate.core.http.downloads;
import io.micronaut.http.HttpHeaders; import io.avaje.http.api.Client;
import io.micronaut.http.annotation.Get; import io.avaje.http.api.Get;
import io.micronaut.http.annotation.Header; import io.avaje.http.api.Headers;
import io.micronaut.http.client.annotation.Client;
import jakarta.validation.constraints.NotBlank;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.geysermc.floodgate.core.util.Constants; import org.geysermc.floodgate.core.util.Constants;
@Client("${http.baseUrl.download}") @Client
@Header(name = HttpHeaders.USER_AGENT, value = Constants.USER_AGENT) @Headers({"User-Agent: " + Constants.USER_AGENT})
public interface DownloadClient { public interface DownloadClient {
@Get("/v2/projects/{project}/versions/latest/builds/latest") @Get("/v2/projects/{project}/versions/latest/builds/latest")
CompletableFuture<LatestBuildResult> latestBuildFor(@NotBlank String project); CompletableFuture<LatestBuildResult> latestBuildFor(String project);
} }

View File

@@ -1,7 +1,7 @@
package org.geysermc.floodgate.core.http.downloads; package org.geysermc.floodgate.core.http.downloads;
import io.micronaut.serde.annotation.Serdeable; import io.avaje.jsonb.Json;
@Serdeable @Json
public record LatestBuildResult(int build) { public record LatestBuildResult(int build) {
} }

View File

@@ -25,19 +25,15 @@
package org.geysermc.floodgate.core.http.link; package org.geysermc.floodgate.core.http.link;
import static io.micronaut.http.HttpHeaders.USER_AGENT; import io.avaje.http.api.Client;
import io.avaje.http.api.Get;
import io.micronaut.core.async.annotation.SingleResult; import io.avaje.http.api.Headers;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Header;
import io.micronaut.http.client.annotation.Client;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.geysermc.floodgate.core.util.Constants; import org.geysermc.floodgate.core.util.Constants;
@Client("${http.baseUrl.api}/v2/link") @Client
@Header(name = USER_AGENT, value = Constants.USER_AGENT) @Headers({"User-Agent: " + Constants.USER_AGENT})
public interface GlobalLinkClient { public interface GlobalLinkClient {
@Get("/bedrock/{xuid}") @Get("/bedrock/{xuid}")
@SingleResult
CompletableFuture<LinkedPlayer> bedrockLink(long xuid); CompletableFuture<LinkedPlayer> bedrockLink(long xuid);
} }

View File

@@ -25,20 +25,20 @@
package org.geysermc.floodgate.core.http.link; package org.geysermc.floodgate.core.http.link;
import io.micronaut.serde.annotation.Serdeable; import io.avaje.jsonb.Json;
import io.avaje.jsonb.Json.Property;
import jakarta.annotation.Nullable; import jakarta.annotation.Nullable;
import jakarta.json.bind.annotation.JsonbProperty;
import java.util.UUID; import java.util.UUID;
import org.geysermc.floodgate.core.util.Utils; import org.geysermc.floodgate.core.util.Utils;
@Serdeable @Json
public record LinkedPlayer( public record LinkedPlayer(
@JsonbProperty("bedrock_id") @Property("bedrock_id")
@Nullable Long xuid, @Nullable Long xuid,
@Nullable String gamertag, @Nullable String gamertag,
@JsonbProperty("java_id") @Property("java_id")
@Nullable UUID uuid, @Nullable UUID uuid,
@JsonbProperty("java_name") @Property("java_name")
@Nullable String username @Nullable String username
) { ) {
public boolean isLinked() { public boolean isLinked() {

View File

@@ -1,17 +1,16 @@
package org.geysermc.floodgate.core.http.minecraft; package org.geysermc.floodgate.core.http.minecraft;
import static io.micronaut.http.HttpHeaders.USER_AGENT; import io.avaje.http.api.Client;
import io.avaje.http.api.Get;
import io.micronaut.http.annotation.Get; import io.avaje.http.api.Headers;
import io.micronaut.http.annotation.Header;
import io.micronaut.http.client.annotation.Client;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.floodgate.core.util.Constants;
@Client("https://api.minecraftservices.com/minecraft") @Client
@Header(name = USER_AGENT, value = "${http.userAgent}") @Headers({"User-Agent: " + Constants.USER_AGENT})
public interface MinecraftClient { public interface MinecraftClient {
@Get("/profile/lookup/name/{name}") @Get("/profile/lookup/name/{name}")
CompletableFuture<@Nullable ProfileResult> profileByName(@NonNull String name); CompletableFuture<@Nullable ProfileResult> profileByName(@NonNull String name);

View File

@@ -1,8 +1,8 @@
package org.geysermc.floodgate.core.http.minecraft; package org.geysermc.floodgate.core.http.minecraft;
import io.micronaut.serde.annotation.Serdeable; import io.avaje.jsonb.Json;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@Serdeable @Json
public record ProfileResult(@NonNull String id, @NonNull String name) { public record ProfileResult(@NonNull String id, @NonNull String name) {
} }

View File

@@ -1,9 +1,9 @@
package org.geysermc.floodgate.core.http.mojang; package org.geysermc.floodgate.core.http.mojang;
import io.micronaut.serde.annotation.Serdeable; import io.avaje.jsonb.Json;
import jakarta.validation.constraints.NotNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@Serdeable @Json
public record ProfileProperty(@NotNull String name, @NotNull String value, @Nullable String signature) { public record ProfileProperty(@NonNull String name, @NonNull String value, @Nullable String signature) {
} }

View File

@@ -1,10 +1,10 @@
package org.geysermc.floodgate.core.http.mojang; package org.geysermc.floodgate.core.http.mojang;
import io.micronaut.serde.annotation.Serdeable; import io.avaje.jsonb.Json;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@Serdeable @Json
public record ProfileWithProperties(String id, String name, List<ProfileProperty> properties) { public record ProfileWithProperties(String id, String name, List<ProfileProperty> properties) {
public @Nullable ProfileProperty texture() { public @Nullable ProfileProperty texture() {
for (ProfileProperty property : properties) { for (ProfileProperty property : properties) {

View File

@@ -1,17 +1,16 @@
package org.geysermc.floodgate.core.http.mojang; package org.geysermc.floodgate.core.http.mojang;
import static io.micronaut.http.HttpHeaders.USER_AGENT; import io.avaje.http.api.Client;
import io.avaje.http.api.Get;
import io.micronaut.http.annotation.Get; import io.avaje.http.api.Headers;
import io.micronaut.http.annotation.Header;
import io.micronaut.http.client.annotation.Client;
import jakarta.validation.constraints.NotNull;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.geysermc.floodgate.core.util.Constants;
import org.jetbrains.annotations.NotNull;
@Client("https://sessionserver.mojang.com/session/minecraft") @Client
@Header(name = USER_AGENT, value = "${http.userAgent}") @Headers({"User-Agent: " + Constants.USER_AGENT})
public interface SessionServerClient { public interface SessionServerClient {
@Get("/profile/{uuid}?unsigned=false") @Get("/profile/{uuid}/?unsigned=false")
CompletableFuture<ProfileWithProperties> profileWithProperties(@NotNull UUID uuid); CompletableFuture<ProfileWithProperties> profileWithProperties(@NotNull UUID uuid);
} }

View File

@@ -25,9 +25,9 @@
package org.geysermc.floodgate.core.http.xbox; package org.geysermc.floodgate.core.http.xbox;
import io.micronaut.serde.annotation.Serdeable; import io.avaje.jsonb.Json;
import jakarta.annotation.Nullable; import jakarta.annotation.Nullable;
@Serdeable @Json
public record GetGamertagResult(@Nullable String gamertag) { public record GetGamertagResult(@Nullable String gamertag) {
} }

View File

@@ -25,9 +25,9 @@
package org.geysermc.floodgate.core.http.xbox; package org.geysermc.floodgate.core.http.xbox;
import io.micronaut.serde.annotation.Serdeable; import io.avaje.jsonb.Json;
import jakarta.annotation.Nullable; import jakarta.annotation.Nullable;
@Serdeable @Json
public record GetXuidResult(@Nullable Long xuid) { public record GetXuidResult(@Nullable Long xuid) {
} }

View File

@@ -25,21 +25,21 @@
package org.geysermc.floodgate.core.http.xbox; package org.geysermc.floodgate.core.http.xbox;
import io.micronaut.http.HttpHeaders; import io.avaje.http.api.Client;
import io.micronaut.http.annotation.Get; import io.avaje.http.api.Get;
import io.micronaut.http.annotation.Header; import io.avaje.http.api.Headers;
import io.micronaut.http.client.annotation.Client;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.core.util.Constants; import org.geysermc.floodgate.core.util.Constants;
import org.jetbrains.annotations.Range;
@Client("${http.baseUrl.api}/v2/xbox") @Client
@Header(name = HttpHeaders.USER_AGENT, value = Constants.USER_AGENT) @Headers({"User-Agent: " + Constants.USER_AGENT})
public interface XboxClient { public interface XboxClient {
@Get("/xuid/{gamertag}") @Get("/xuid/{gamertag}")
CompletableFuture<GetXuidResult> xuidByGamertag( CompletableFuture<GetXuidResult> xuidByGamertag(
@NotNull @Size(min = 1, max = 16) String gamertag @Range(from = 1, to = 16)
@NonNull String gamertag
); );
@Get("/gamertag/{xuid}") @Get("/gamertag/{xuid}")

View File

@@ -27,13 +27,11 @@ package org.geysermc.floodgate.core.link;
import io.micronaut.context.annotation.Replaces; import io.micronaut.context.annotation.Replaces;
import io.micronaut.context.annotation.Requires; import io.micronaut.context.annotation.Requires;
import io.micronaut.scheduling.TaskExecutors;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
import jakarta.inject.Singleton; import jakarta.inject.Singleton;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.core.database.PendingLinkRepository; import org.geysermc.floodgate.core.database.PendingLinkRepository;
import org.geysermc.floodgate.core.database.PlayerLinkRepository; import org.geysermc.floodgate.core.database.PlayerLinkRepository;
@@ -50,10 +48,6 @@ public class LocalPlayerLinking extends CommonPlayerLink {
@Inject PlayerLinkRepository linkRepository; @Inject PlayerLinkRepository linkRepository;
@Inject PendingLinkRepository pendingLinkRepository; @Inject PendingLinkRepository pendingLinkRepository;
@Inject
@Named(TaskExecutors.IO)
ExecutorService executor;
@Override @Override
public CompletableFuture<LinkedPlayer> addLink( public CompletableFuture<LinkedPlayer> addLink(
@NonNull UUID javaUniqueId, @NonNull UUID javaUniqueId,

View File

@@ -3,4 +3,4 @@ org.gradle.caching=true
org.gradle.parallel=true org.gradle.parallel=true
version=3.0.0-SNAPSHOT version=3.0.0-SNAPSHOT
micronautVersion=4.3.1 micronautVersion=4.6.0

View File

@@ -1,6 +1,6 @@
[versions] [versions]
# parent # parent
micronaut-gradle = "4.3.2" micronaut-gradle = "4.4.2"
lombok = "8.4" lombok = "8.4"
# api # api
@@ -19,6 +19,8 @@ snakeyaml = "2.0"
bstats = "3.0.3" bstats = "3.0.3"
adventure = "4.16.0" adventure = "4.16.0"
adventure-platform = "4.3.2" adventure-platform = "4.3.2"
avaje-http = "2.7"
avaje-jsonb = "2.1"
# bungee # bungee
bungee = "1.20-R0.3-SNAPSHOT" bungee = "1.20-R0.3-SNAPSHOT"
@@ -70,13 +72,12 @@ adventure-platform-bungee = { module = "net.kyori:adventure-platform-bungeecord"
micronaut-inject = { module = "io.micronaut:micronaut-inject" } micronaut-inject = { module = "io.micronaut:micronaut-inject" }
micronaut-inject-java = { module = "io.micronaut:micronaut-inject-java" } micronaut-inject-java = { module = "io.micronaut:micronaut-inject-java" }
micronaut-context = { module = "io.micronaut:micronaut-context" } micronaut-context = { module = "io.micronaut:micronaut-context" }
micronaut-http-client = { module = "io.micronaut:micronaut-http-client-jdk" }
micronaut-validation = { module = "io.micronaut.validation:micronaut-validation" }
micronaut-validation-processor = { module = "io.micronaut.validation:micronaut-validation-processor" }
micronaut-serde-jsonp = { module = "io.micronaut.serde:micronaut-serde-jsonp" } avaje-http-client = { module = "io.avaje:avaje-http-client", version.ref = "avaje-http" }
micronaut-serde-processor = { module = "io.micronaut.serde:micronaut-serde-processor" } avaje-http-api = { module = "io.avaje:avaje-http-api", version.ref = "avaje-http" }
jakarta-jsonb = { module = "jakarta.json.bind:jakarta.json.bind-api" } avaje-http-client-generator = { module = "io.avaje:avaje-http-client-generator", version.ref = "avaje-http" }
avaje-jsonb = { module = "io.avaje:avaje-jsonb", version.ref = "avaje-jsonb" }
avaje-jsonb-generator = { module = "io.avaje:avaje-jsonb-generator", version.ref = "avaje-jsonb" }
netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" } netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" }