9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-30 20:39:10 +00:00

feat(core): 添加对 GitLab 的资源包托管支持

This commit is contained in:
jhqwqmc
2025-04-21 19:30:53 +08:00
parent 0c2d8e3b5e
commit cd6e6e1326
3 changed files with 188 additions and 0 deletions

View File

@@ -26,6 +26,10 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager {
this.advancementParser = new AdvancementParser();
}
public void unload() {
advancements.clear();
}
@Override
public ConfigSectionParser parser() {
return this.advancementParser;

View File

@@ -19,6 +19,7 @@ public class ResourcePackHosts {
public static final Key ALIST = Key.of("craftengine:alist");
public static final Key DROPBOX = Key.of("craftengine:dropbox");
public static final Key ONEDRIVE = Key.of("craftengine:onedrive");
public static final Key GITLAB = Key.of("craftengine:gitlab");
static {
register(NONE, NoneHost.FACTORY);
@@ -29,6 +30,7 @@ public class ResourcePackHosts {
register(ALIST, AlistHost.FACTORY);
register(DROPBOX, DropboxHost.FACTORY);
register(ONEDRIVE, OneDriveHost.FACTORY);
register(GITLAB, GitLabHost.FACTORY);
}
public static void register(Key key, ResourcePackHostFactory factory) {

View File

@@ -0,0 +1,182 @@
package net.momirealms.craftengine.core.pack.host.impl;
import com.google.gson.reflect.TypeToken;
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.util.GsonHelper;
import net.momirealms.craftengine.core.util.HashUtils;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.concurrent.CompletableFuture;
public class GitLabHost implements ResourcePackHost {
public static final Factory FACTORY = new Factory();
private final String gitlabUrl;
private final String accessToken;
private final String projectId;
private final ProxySelector proxy;
private String url;
private String sha1;
private UUID uuid;
public GitLabHost(String gitlabUrl, String accessToken, String projectId, ProxySelector proxy) {
this.gitlabUrl = gitlabUrl;
this.accessToken = accessToken;
this.projectId = projectId;
this.proxy = proxy;
this.readCacheFromDisk();
}
public void readCacheFromDisk() {
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("gitlab.cache");
if (!Files.exists(cachePath)) return;
try (InputStream is = Files.newInputStream(cachePath)) {
Map<String, String> cache = GsonHelper.get().fromJson(
new InputStreamReader(is),
new TypeToken<Map<String, String>>(){}.getType()
);
this.url = cache.get("url");
this.sha1 = cache.get("sha1");
String uuidString = cache.get("uuid");
if (uuidString != null && !uuidString.isEmpty()) {
this.uuid = UUID.fromString(uuidString);
}
CraftEngine.instance().logger().info("[GitLab] Loaded cached resource pack info");
} catch (Exception e) {
CraftEngine.instance().logger().warn(
"[GitLab] Failed to read cache file: " + e.getMessage());
}
}
public void saveCacheToDisk() {
Map<String, String> cache = new HashMap<>();
cache.put("url", this.url);
cache.put("sha1", this.sha1);
cache.put("uuid", this.uuid != null ? this.uuid.toString() : "");
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("gitlab.cache");
try {
Files.writeString(
cachePath,
GsonHelper.get().toJson(cache),
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING
);
} catch (IOException e) {
CraftEngine.instance().logger().warn(
"[GitLab] Failed to save cache: " + e.getMessage());
}
}
@Override
public boolean canUpload() {
return true;
}
@Override
public Key type() {
return ResourcePackHosts.GITLAB;
}
@Override
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
if (url == null) return CompletableFuture.completedFuture(Collections.emptyList());
return CompletableFuture.completedFuture(List.of(ResourcePackDownloadData.of(this.url, this.uuid, this.sha1)));
}
@Override
public CompletableFuture<Void> upload(Path resourcePackPath) {
CompletableFuture<Void> future = new CompletableFuture<>();
CraftEngine.instance().scheduler().executeAsync(() -> {
this.sha1 = HashUtils.calculateLocalFileSha1(resourcePackPath);
this.uuid = UUID.nameUUIDFromBytes(this.sha1.getBytes(StandardCharsets.UTF_8));
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(this.gitlabUrl + "/api/v4/projects/" + this.projectId + "/uploads"))
.header("PRIVATE-TOKEN", this.accessToken)
.POST(HttpRequest.BodyPublishers.ofFile(resourcePackPath))
.build();
long uploadStart = System.currentTimeMillis();
CraftEngine.instance().logger().info(
"[GitLab] Initiating resource pack upload...");
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenAccept(response -> {
long uploadTime = System.currentTimeMillis() - uploadStart;
CraftEngine.instance().logger().info(
"[GitLab] Upload request completed in " + uploadTime + "ms");
if (response.statusCode() == 200 || response.statusCode() == 201) {
Map<String, Object> json = GsonHelper.parseJsonToMap(response.body());
if (json.containsKey("full_path")) {
this.url = (String) json.get("full_path");
future.complete(null);
saveCacheToDisk();
return;
}
}
CraftEngine.instance().logger().warn("[GitLab] Upload failed: " + response.body());
future.completeExceptionally(new RuntimeException("Upload failed: " + response.body()));
})
.exceptionally(ex -> {
CraftEngine.instance().logger().warn(
"[GitLab] Upload error: " + ex.getMessage());
future.completeExceptionally(ex);
return null;
});
} catch (FileNotFoundException e) {
CraftEngine.instance().logger().warn(
"[GitLab] Failed to upload resource pack: " + e.getMessage());
}
});
return future;
}
public static class Factory implements ResourcePackHostFactory {
@Override
public ResourcePackHost create(Map<String, Object> arguments) {
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
String gitlabUrl = (String) arguments.get("gitlab-url");
if (gitlabUrl == null || gitlabUrl.isEmpty()) {
throw new IllegalArgumentException("'gitlab-url' cannot be empty for GitLab host");
}
if (gitlabUrl.endsWith("/")) {
gitlabUrl = gitlabUrl.substring(0, gitlabUrl.length() - 1);
}
String accessToken = useEnv ? System.getenv("CE_GITLAB_ACCESS_TOKEN") : (String) arguments.get("access-token");
if (accessToken == null || accessToken.isEmpty()) {
throw new IllegalArgumentException("Missing required 'access-token' configuration");
}
String projectId = (String) arguments.get("project-id");
if (projectId == null || projectId.isEmpty()) {
throw new IllegalArgumentException("Missing required 'project-id' configuration");
}
projectId = URLEncoder.encode(projectId, StandardCharsets.UTF_8).replace("/", "%2F");
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
return new GitLabHost(gitlabUrl, accessToken, projectId, proxy);
}
}
}