9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-28 11:29:17 +00:00

远离awssdk

This commit is contained in:
XiaoMoMi
2025-05-23 05:36:04 +08:00
parent 5ce44d6cda
commit ce55fceb16
2 changed files with 2 additions and 238 deletions

View File

@@ -52,13 +52,12 @@ dependencies {
compileOnly("com.mojang:datafixerupper:${rootProject.properties["datafixerupper_version"]}")
// Aho-Corasick java implementation
compileOnly("org.ahocorasick:ahocorasick:${rootProject.properties["ahocorasick_version"]}")
// Amazon S3
compileOnly("software.amazon.awssdk:s3:${rootProject.properties["amazon_awssdk_version"]}")
compileOnly("software.amazon.awssdk:netty-nio-client:${rootProject.properties["amazon_awssdk_version"]}")
// EvalEx
compileOnly("com.ezylang:EvalEx:${rootProject.properties["evalex_version"]}")
// Jimfs
compileOnly("com.google.jimfs:jimfs:${rootProject.properties["jimfs_version"]}")
// S3
implementation("net.momirealms:craft-engine-s3:0.1")
}
java {

View File

@@ -1,235 +0,0 @@
package net.momirealms.craftengine.core.pack.host.impl;
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.util.HashUtils;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.auth.signer.AwsS3V4Signer;
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
import software.amazon.awssdk.http.nio.netty.ProxyConfiguration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3AsyncClientBuilder;
import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class S3Host implements ResourcePackHost {
public static final Factory FACTORY = new Factory();
private final S3AsyncClient s3AsyncClient;
private final S3Presigner preSigner;
private final String bucket;
private final String uploadPath;
private final String cdnDomain;
private final String cdnProtocol;
private final Duration validity;
public S3Host(
S3AsyncClient s3AsyncClient,
S3Presigner preSigner,
String bucket,
String uploadPath,
String cdnDomain,
String cdnProtocol,
Duration validity
) {
this.s3AsyncClient = s3AsyncClient;
this.preSigner = preSigner;
this.bucket = bucket;
this.uploadPath = uploadPath;
this.cdnDomain = cdnDomain;
this.cdnProtocol = cdnProtocol;
this.validity = validity;
}
@Override
public boolean canUpload() {
return true;
}
@Override
public Key type() {
return ResourcePackHosts.S3;
}
@Override
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
return this.s3AsyncClient.headObject(HeadObjectRequest.builder()
.bucket(this.bucket)
.key(this.uploadPath)
.build())
.handle((headResponse, exception) -> {
if (exception != null) {
Throwable cause = exception.getCause();
if (cause instanceof NoSuchKeyException) {
CraftEngine.instance().logger().warn("[S3] Resource pack not found in bucket '" + this.bucket + "'. Path: " + this.uploadPath);
return Collections.emptyList();
} else {
CraftEngine.instance().logger().warn(
"[S3] Failed to retrieve resource pack metadata. Reason: " +
cause.getClass().getSimpleName() + " - " + cause.getMessage()
);
throw new CompletionException("Metadata request failed for path: " + this.uploadPath, cause);
}
}
String sha1 = headResponse.metadata().get("sha1");
if (sha1 == null) {
CraftEngine.instance().logger().warn("[S3] Missing SHA-1 checksum in object metadata. Path: " + this.uploadPath);
throw new CompletionException(new IllegalStateException("Missing SHA-1 metadata for S3 object: " + this.uploadPath));
}
GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
.signatureDuration(this.validity)
.getObjectRequest(b -> b.bucket(this.bucket).key(this.uploadPath))
.build();
return Collections.singletonList(
ResourcePackDownloadData.of(
replaceWithCdnUrl(this.preSigner.presignGetObject(presignRequest).url()),
UUID.nameUUIDFromBytes(sha1.getBytes(StandardCharsets.UTF_8)),
sha1
)
);
});
}
@Override
public CompletableFuture<Void> upload(Path resourcePackPath) {
String sha1 = HashUtils.calculateLocalFileSha1(resourcePackPath);
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(this.bucket)
.key(this.uploadPath)
.metadata(Map.of("sha1", sha1))
.build();
long uploadStart = System.currentTimeMillis();
CraftEngine.instance().logger().info("[S3] Initiating resource pack upload to '" + this.uploadPath + "'");
return this.s3AsyncClient.putObject(putObjectRequest, AsyncRequestBody.fromFile(resourcePackPath))
.handle((response, exception) -> {
if (exception != null) {
Throwable cause = exception instanceof CompletionException ?
exception.getCause() :
exception;
CraftEngine.instance().logger().warn(
"[S3] Upload failed for path '" + this.uploadPath + "'. Error: " +
cause.getClass().getSimpleName() + " - " + cause.getMessage()
);
throw new CompletionException("Failed to upload to S3 path: " + this.uploadPath, cause);
}
CraftEngine.instance().logger().info(
"[S3] Successfully uploaded resource pack to '" + this.uploadPath + "' in " +
(System.currentTimeMillis() - uploadStart) + " ms"
);
return null;
});
}
private String replaceWithCdnUrl(URL originalUrl) {
if (this.cdnDomain == null) return originalUrl.toString();
return this.cdnProtocol + "://" + this.cdnDomain
+ originalUrl.getPath()
+ (originalUrl.getQuery() != null ? "?" + originalUrl.getQuery() : "");
}
public static class Factory implements ResourcePackHostFactory {
@Override
@SuppressWarnings("deprecation")
public ResourcePackHost create(Map<String, Object> arguments) {
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
String endpoint = Optional.ofNullable(arguments.get("endpoint")).map(String::valueOf).orElse(null);
if (endpoint == null || endpoint.isEmpty()) {
throw new LocalizedException("warning.config.host.s3.missing_endpoint");
}
String protocol = arguments.getOrDefault("protocol", "https").toString();
boolean usePathStyle = (boolean) arguments.getOrDefault("path-style", false);
String bucket = Optional.ofNullable(arguments.get("bucket")).map(String::valueOf).orElse(null);
if (bucket == null || bucket.isEmpty()) {
throw new LocalizedException("warning.config.host.s3.missing_bucket");
}
String region = arguments.getOrDefault("region", "auto").toString();
String accessKeyId = useEnv ? System.getenv("CE_S3_ACCESS_KEY_ID") : Optional.ofNullable(arguments.get("access-key-id")).map(String::valueOf).orElse(null);
if (accessKeyId == null || accessKeyId.isEmpty()) {
throw new LocalizedException("warning.config.host.s3.missing_access_key");
}
String accessKeySecret = useEnv ? System.getenv("CE_S3_ACCESS_KEY_SECRET") : Optional.ofNullable(arguments.get("access-key-secret")).map(String::valueOf).orElse(null);
if (accessKeySecret == null || accessKeySecret.isEmpty()) {
throw new LocalizedException("warning.config.host.s3.missing_secret");
}
String uploadPath = arguments.getOrDefault("upload-path", "craftengine/resource_pack.zip").toString();
if (uploadPath == null || uploadPath.isEmpty()) {
throw new LocalizedException("warning.config.host.s3.missing_upload_path");
}
boolean useLegacySignature = (boolean) arguments.getOrDefault("use-legacy-signature", true);
Duration validity = Duration.ofSeconds((int) arguments.getOrDefault("validity", 10));
Map<String, Object> cdn = MiscUtils.castToMap(arguments.get("cdn"), true);
String cdnDomain = null;
String cdnProtocol = "https";
if (cdn != null) {
cdnDomain = Optional.ofNullable(arguments.get("domain")).map(String::valueOf).orElse(null);
cdnProtocol = arguments.getOrDefault("protocol", "https").toString();
}
AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKeyId, accessKeySecret);
S3AsyncClientBuilder s3AsyncClientBuilder = S3AsyncClient.builder()
.endpointOverride(URI.create(protocol + "://" + endpoint))
.region(Region.of(region))
.credentialsProvider(StaticCredentialsProvider.create(credentials))
.serviceConfiguration(b -> b.pathStyleAccessEnabled(usePathStyle));
if (useLegacySignature) {
s3AsyncClientBuilder.overrideConfiguration(b -> b
.putAdvancedOption(SdkAdvancedClientOption.SIGNER, AwsS3V4Signer.create())
);
}
Map<String, Object> proxySetting = MiscUtils.castToMap(arguments.get("proxy"), true);
if (proxySetting != null) {
String host = ResourceConfigUtils.requireNonEmptyStringOrThrow(proxySetting.get("host"), () -> new LocalizedException("warning.config.host.proxy.missing_host"));
int port = ResourceConfigUtils.getAsInt(proxySetting.get("port"), "port");
if (port <= 0 || port > 65535) {
throw new LocalizedException("warning.config.host.proxy.missing_port");
}
String scheme = ResourceConfigUtils.requireNonEmptyStringOrThrow(proxySetting.get("scheme"), () -> new LocalizedException("warning.config.host.proxy.missing_scheme"));
String username = Optional.ofNullable(proxySetting.get("username")).map(String::valueOf).orElse(null);
String password = Optional.ofNullable(proxySetting.get("password")).map(String::valueOf).orElse(null);
ProxyConfiguration.Builder builder = ProxyConfiguration.builder().host(host).port(port).scheme(scheme);
if (username != null) builder.username(username);
if (password != null) builder.password(password);
SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder().proxyConfiguration(builder.build()).build();
s3AsyncClientBuilder.httpClient(httpClient);
}
S3AsyncClient s3AsyncClient = s3AsyncClientBuilder.build();
S3Presigner preSigner = S3Presigner.builder()
.endpointOverride(URI.create(protocol + "://" + endpoint))
.region(Region.of(region))
.credentialsProvider(StaticCredentialsProvider.create(credentials))
.build();
return new S3Host(s3AsyncClient, preSigner, bucket, uploadPath, cdnDomain, cdnProtocol, validity);
}
}
}