9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-28 11:29:17 +00:00
This commit is contained in:
XiaoMoMi
2025-04-18 20:04:49 +08:00
parent bb3ced5881
commit efcb9e6418
17 changed files with 165 additions and 60 deletions

View File

@@ -16,6 +16,13 @@ reload:
- /craftengine reload
- /ce reload
upload:
enable: true
permission: ce.command.admin.upload
usage:
- /craftengine upload
- /ce upload
get_item:
enable: true
permission: ce.command.admin.get_item

View File

@@ -59,6 +59,8 @@ command.resource.enable.failure.unknown: "<red>Unknown resource <arg:0></red>"
command.resource.disable.success: "<white>Disabled resource <arg:0>. Run <click:run_command:/ce reload all><u>/ce reload all</u></click> to apply changes</white>"
command.resource.disable.failure.unknown: "<red>Unknown resource <arg:0></red>"
command.resource.list: "<white>Enabled resources(<arg:0>): <green><arg:1></green><newline>Disabled resources(<arg:2>): <red><arg:3></red></white>"
command.upload.failure.not_supported: "<red>Current hosting method '<arg:0>' doesn't support uploading resource packs.</red>"
command.upload.on_progress: "<white>Started uploading progress. Check the console for more information.</white>"
warning.config.image.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated image '<arg:1>'.</yellow>"
warning.config.image.lack_height: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'height' argument.</yellow>"
warning.config.image.height_smaller_than_ascent: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' violates the bitmap image rule: 'height' should be no lower than 'ascent'.</yellow>"

View File

@@ -59,6 +59,8 @@ command.resource.enable.failure.unknown: "<red>未知资源 <arg:0></red>"
command.resource.disable.success: "<white>已禁用 <arg:0>. 执行 <click:run_command:/ce reload all><u>/ce reload all</u></click> 以应用更改</white>"
command.resource.disable.failure.unknown: "<red>未知资源 <arg:0></red>"
command.resource.list: "<white>启用的资源(<arg:0>): <green><arg:1></green><newline>禁用的资源(<arg:2>): <red><arg:3></red></white>"
command.upload.failure.not_supported: "<red>当前托管模式 '<arg:0>' 不支持上传资源包.</red>"
command.upload.on_progress: "<white>已开始上传进程. 检查控制台以获取详细信息.</white>"
warning.config.image.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 重复定义</yellow>"
warning.config.image.lack_height: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 缺少必要的 'height' 高度参数</yellow>"
warning.config.image.height_smaller_than_ascent: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 违反位图规则:'height' 高度值不应小于 'ascent' 基准线高度</yellow>"

View File

@@ -49,7 +49,8 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
new TotemAnimationCommand(this, plugin),
new EnableResourceCommand(this, plugin),
new DisableResourceCommand(this, plugin),
new ListResourceCommand(this, plugin)
new ListResourceCommand(this, plugin),
new UploadPackCommand(this, plugin)
));
final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager();
manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true);

View File

@@ -61,7 +61,6 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
plugin().logger().warn("Failed to reload config", e);
}
} else if (argument == ReloadArgument.PACK) {
RELOAD_PACK_FLAG = true;
plugin().scheduler().executeAsync(() -> {
try {
long time1 = System.currentTimeMillis();
@@ -73,8 +72,6 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
} catch (Exception e) {
handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_FAILURE);
plugin().logger().warn("Failed to generate resource pack", e);
} finally {
RELOAD_PACK_FLAG = false;
}
});
} else if (argument == ReloadArgument.ALL) {

View File

@@ -0,0 +1,37 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
import org.bukkit.command.CommandSender;
import org.incendo.cloud.Command;
public class UploadPackCommand extends BukkitCommandFeature<CommandSender> {
public UploadPackCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
super(commandManager, plugin);
}
@Override
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder
.handler(context -> {
ResourcePackHost host = plugin().packManager().resourcePackHost();
if (host.canUpload()) {
handleFeedback(context, MessageConstants.COMMAND_UPLOAD_ON_PROGRESS);
host.upload(Config.fileToUpload());
} else {
handleFeedback(context, MessageConstants.COMMAND_UPLOAD_FAILURE_NOT_SUPPORTED, Component.text(host.type().value()));
}
});
}
@Override
public String getFeatureID() {
return "upload";
}
}

View File

@@ -1,5 +1,7 @@
package net.momirealms.craftengine.core.pack.host;
import net.momirealms.craftengine.core.util.Key;
import java.nio.file.Path;
import java.util.List;
import java.util.UUID;
@@ -10,4 +12,8 @@ public interface ResourcePackHost {
CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player);
CompletableFuture<Void> upload(Path resourcePackPath);
boolean canUpload();
Key type();
}

View File

@@ -6,11 +6,9 @@ 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.MiscUtils;
import net.momirealms.craftengine.core.util.Pair;
import net.momirealms.craftengine.core.util.*;
import javax.annotation.Nullable;
import java.io.IOException;
@@ -26,8 +24,6 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -40,8 +36,7 @@ public class AlistHost implements ResourcePackHost {
private final String filePassword;
private final String otpCode;
private final Duration jwtTokenExpiration;
private final String filePath;
private final boolean disabledUpload;
private final String uploadPath;
private final ProxySelector proxy;
private Pair<String, Date> jwtToken;
private String cachedSha1;
@@ -52,8 +47,7 @@ public class AlistHost implements ResourcePackHost {
String filePassword,
String otpCode,
Duration jwtTokenExpiration,
String filePath,
boolean disabledUpload,
String uploadPath,
ProxySelector proxy) {
this.apiUrl = apiUrl;
this.userName = userName;
@@ -61,12 +55,21 @@ public class AlistHost implements ResourcePackHost {
this.filePassword = filePassword;
this.otpCode = otpCode;
this.jwtTokenExpiration = jwtTokenExpiration;
this.filePath = filePath;
this.disabledUpload = disabledUpload;
this.uploadPath = uploadPath;
this.proxy = proxy;
this.readCacheFromDisk();
}
@Override
public boolean canUpload() {
return true;
}
@Override
public Key type() {
return ResourcePackHosts.ALIST;
}
private void readCacheFromDisk() {
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("alist.cache");
if (!Files.exists(cachePath)) return;
@@ -129,18 +132,13 @@ public class AlistHost implements ResourcePackHost {
@Override
public CompletableFuture<Void> upload(Path resourcePackPath) {
if (this.disabledUpload) {
this.cachedSha1 = "";
saveCacheToDisk();
return CompletableFuture.completedFuture(null);
}
CompletableFuture<Void> future = new CompletableFuture<>();
CraftEngine.instance().scheduler().executeAsync(() -> {
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(this.apiUrl + "/api/fs/put"))
.header("Authorization", getOrRefreshJwtToken())
.header("File-Path", URLEncoder.encode(this.filePath, StandardCharsets.UTF_8)
.header("File-Path", URLEncoder.encode(this.uploadPath, StandardCharsets.UTF_8)
.replace("/", "%2F"))
.header("overwrite", "true")
.header("password", this.filePassword)
@@ -225,7 +223,7 @@ public class AlistHost implements ResourcePackHost {
}
private HttpRequest.BodyPublisher getRequestResourcePackDownloadLinkPost() {
String body = "{\"path\":\"" + this.filePath + "\",\"password\":\"" + this.filePassword + "\"}";
String body = "{\"path\":\"" + this.uploadPath + "\",\"password\":\"" + this.filePassword + "\"}";
return HttpRequest.BodyPublishers.ofString(body);
}
@@ -241,32 +239,6 @@ public class AlistHost implements ResourcePackHost {
boolean isDir = dataObj.getAsJsonPrimitive("is_dir").getAsBoolean();
if (!isDir) {
String url = dataObj.getAsJsonPrimitive("raw_url").getAsString();
if ((this.cachedSha1 == null || this.cachedSha1.isEmpty()) && this.disabledUpload) {
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
HttpResponse<InputStream> responseHash = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
try (InputStream inputStream = responseHash.body()) {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] buffer = new byte[8192];
int len;
while ((len = inputStream.read(buffer)) != -1) {
md.update(buffer, 0, len);
}
byte[] digest = md.digest();
this.cachedSha1 = HexFormat.of().formatHex(digest);
saveCacheToDisk();
} catch (NoSuchAlgorithmException e) {
future.completeExceptionally(new RuntimeException("Failed to calculate SHA-1 hash algorithm", e));
return;
}
} catch (IOException | InterruptedException e) {
future.completeExceptionally(new RuntimeException("Failed to retrieve remote resource pack for hashing", e));
return;
}
}
UUID uuid = UUID.nameUUIDFromBytes(Objects.requireNonNull(this.cachedSha1).getBytes(StandardCharsets.UTF_8));
future.complete(List.of(new ResourcePackDownloadData(url, uuid, this.cachedSha1)));
return;
@@ -297,13 +269,12 @@ public class AlistHost implements ResourcePackHost {
String filePassword = (String) arguments.getOrDefault("file-password", "");
String otpCode = (String) arguments.get("otp-code");
Duration jwtTokenExpiration = Duration.ofHours((int) arguments.getOrDefault("jwt-token-expiration", 48));
String filePath = (String) arguments.get("file-path");
if (filePath == null || filePath.isEmpty()) {
throw new IllegalArgumentException("'file-path' cannot be empty for Alist host");
String uploadPath = (String) arguments.get("upload-path");
if (uploadPath == null || uploadPath.isEmpty()) {
throw new IllegalArgumentException("'upload-path' cannot be empty for Alist host");
}
boolean disabledUpload = (boolean) arguments.getOrDefault("disabled-upload", false);
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
return new AlistHost(apiUrl, userName, password, filePassword, otpCode, jwtTokenExpiration, filePath, disabledUpload, proxy);
return new AlistHost(apiUrl, userName, password, filePassword, otpCode, jwtTokenExpiration, uploadPath, proxy);
}
}
}

View File

@@ -6,9 +6,11 @@ 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;
@@ -89,6 +91,16 @@ public class DropboxHost implements ResourcePackHost {
}
}
@Override
public boolean canUpload() {
return true;
}
@Override
public Key type() {
return ResourcePackHosts.DROPBOX;
}
@Override
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
if (this.url == null) return CompletableFuture.completedFuture(Collections.emptyList());

View File

@@ -3,6 +3,8 @@ 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.util.Key;
import java.nio.file.Path;
import java.util.List;
@@ -28,6 +30,16 @@ public class ExternalHost implements ResourcePackHost {
return CompletableFuture.completedFuture(null);
}
@Override
public boolean canUpload() {
return false;
}
@Override
public Key type() {
return ResourcePackHosts.EXTERNAL;
}
public static class Factory implements ResourcePackHostFactory {
@Override

View File

@@ -4,8 +4,10 @@ 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.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import java.io.IOException;
@@ -41,6 +43,16 @@ public class LobFileHost implements ResourcePackHost {
this.readCacheFromDisk();
}
@Override
public boolean canUpload() {
return true;
}
@Override
public Key type() {
return ResourcePackHosts.LOBFILE;
}
public void readCacheFromDisk() {
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("lobfile.cache");
if (!Files.exists(cachePath)) return;

View File

@@ -3,6 +3,8 @@ 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.util.Key;
import java.nio.file.Path;
import java.util.List;
@@ -24,6 +26,16 @@ public class NoneHost implements ResourcePackHost {
return CompletableFuture.completedFuture(null);
}
@Override
public boolean canUpload() {
return false;
}
@Override
public Key type() {
return ResourcePackHosts.NONE;
}
public static class Factory implements ResourcePackHostFactory {
@Override

View File

@@ -5,11 +5,9 @@ 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.MiscUtils;
import net.momirealms.craftengine.core.util.Tuple;
import net.momirealms.craftengine.core.util.*;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -51,6 +49,16 @@ public class OneDriveHost implements ResourcePackHost {
readCacheFromDisk();
}
@Override
public boolean canUpload() {
return true;
}
@Override
public Key type() {
return ResourcePackHosts.ONEDRIVE;
}
public void readCacheFromDisk() {
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("onedrive.cache");
if (!Files.exists(cachePath)) return;

View File

@@ -3,8 +3,10 @@ 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.util.HashUtils;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
@@ -63,6 +65,16 @@ public class S3Host implements ResourcePackHost {
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()

View File

@@ -3,8 +3,10 @@ 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.config.Config;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import java.nio.file.Path;
@@ -42,6 +44,16 @@ public class SelfHost implements ResourcePackHost {
return future;
}
@Override
public boolean canUpload() {
return true;
}
@Override
public Key type() {
return ResourcePackHosts.SELF;
}
public static class Factory implements ResourcePackHostFactory {
@Override

View File

@@ -26,4 +26,6 @@ public interface MessageConstants {
TranslatableComponent.Builder COMMAND_RESOURCE_DISABLE_SUCCESS = Component.translatable().key("command.resource.disable.success");
TranslatableComponent.Builder COMMAND_RESOURCE_DISABLE_FAILURE = Component.translatable().key("command.resource.disable.failure.unknown");
TranslatableComponent.Builder COMMAND_RESOURCE_LIST = Component.translatable().key("command.resource.list");
TranslatableComponent.Builder COMMAND_UPLOAD_FAILURE_NOT_SUPPORTED = Component.translatable().key("command.upload.failure.not_supported");
TranslatableComponent.Builder COMMAND_UPLOAD_ON_PROGRESS = Component.translatable().key("command.upload.on_progress");
}

View File

@@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx1G
# Project settings
# Rule: [major update].[feature update].[bug fix]
project_version=0.0.49-beta.1
project_version=0.0.49-beta.2
config_version=28
lang_version=4
lang_version=5
project_group=net.momirealms
latest_supported_version=1.21.5
latest_minecraft_version=1.21.5