mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2026-01-06 15:52:03 +00:00
@@ -114,4 +114,5 @@ warning.config.model.generation.texture.invalid_resource_location: "<yellow>Issu
|
||||
warning.config.model.generation.parent.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a parent argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
|
||||
warning.config.emoji.lack_keywords: "<yellow>Issue found in file <arg:0> - The emoji '<arg:1>' is missing the required 'keywords' argument.</yellow>"
|
||||
warning.config.emoji.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated emoji '<arg:1>'.</yellow>"
|
||||
warning.config.emoji.invalid_image: "<yellow>Issue found in file <arg:0> - The emoji '<arg:1>' has an invalid 'image' argument '<arg:2>'.</yellow>"
|
||||
warning.config.emoji.invalid_image: "<yellow>Issue found in file <arg:0> - The emoji '<arg:1>' has an invalid 'image' argument '<arg:2>'.</yellow>"
|
||||
warning.config.advancement.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated advancement '<arg:1>'.</yellow>"
|
||||
@@ -114,4 +114,5 @@ warning.config.model.generation.texture.invalid_resource_location: "<yellow>在
|
||||
warning.config.model.generation.parent.invalid_resource_location: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 的父模型参数 [<arg:2>] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
|
||||
warning.config.emoji.lack_keywords: "<yellow>在文件 <arg:0> 中发现问题 - 表情 '<arg:1>' 缺少必要的 'keywords' 配置</yellow>"
|
||||
warning.config.emoji.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 表情 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.emoji.invalid_image: "<yellow>在文件 <arg:0> 中发现问题 - 表情 '<arg:1>' 使用了无效的 'image' 图片参数 '<arg:2>'.</yellow>"
|
||||
warning.config.emoji.invalid_image: "<yellow>在文件 <arg:0> 中发现问题 - 表情 '<arg:1>' 使用了无效的 'image' 图片参数 '<arg:2>'.</yellow>"
|
||||
warning.config.advancement.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 进度 '<arg:1>' 重复定义</yellow>"
|
||||
@@ -0,0 +1,62 @@
|
||||
package net.momirealms.craftengine.bukkit.advancement;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.core.advancement.AbstractAdvancementManager;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.util.GsonHelper;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class BukkitAdvancementManager extends AbstractAdvancementManager {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private final AdvancementParser advancementParser;
|
||||
private final Map<Key, JsonElement> advancements = new HashMap<>();
|
||||
|
||||
public BukkitAdvancementManager(BukkitCraftEngine plugin) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
this.advancementParser = new AdvancementParser();
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
advancements.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSectionParser parser() {
|
||||
return this.advancementParser;
|
||||
}
|
||||
|
||||
public class AdvancementParser implements ConfigSectionParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"advancements", "advancement"};
|
||||
|
||||
@Override
|
||||
public String[] sectionId() {
|
||||
return CONFIG_SECTION_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int loadingSequence() {
|
||||
return LoadingSequence.ADVANCEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
if (advancements.containsKey(id)) {
|
||||
TranslationManager.instance().log("warning.config.advancement.duplicated", path.toString(), id.toString());
|
||||
return;
|
||||
}
|
||||
JsonElement jsonTree = GsonHelper.get().toJsonTree(section);
|
||||
FastNMS.INSTANCE.registerAdvancement(id.decompose(), jsonTree);
|
||||
advancements.put(id, jsonTree);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin;
|
||||
|
||||
import net.momirealms.antigrieflib.AntiGriefLib;
|
||||
import net.momirealms.craftengine.bukkit.advancement.BukkitAdvancementManager;
|
||||
import net.momirealms.craftengine.bukkit.api.event.CraftEngineReloadEvent;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors;
|
||||
@@ -156,6 +157,7 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
super.soundManager = new BukkitSoundManager(this);
|
||||
super.vanillaLootManager = new BukkitVanillaLootManager(this);
|
||||
super.fontManager = new BukkitFontManager(this);
|
||||
super.advancementManager = new BukkitAdvancementManager(this);
|
||||
super.onPluginEnable();
|
||||
// compatibility
|
||||
// register expansion
|
||||
@@ -245,6 +247,11 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
return (BukkitBlockManager) blockManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitAdvancementManager advancementManager() {
|
||||
return (BukkitAdvancementManager) advancementManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitFurnitureManager furnitureManager() {
|
||||
return (BukkitFurnitureManager) furnitureManager;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.momirealms.craftengine.core.advancement;
|
||||
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
|
||||
public abstract class AbstractAdvancementManager implements AdvancementManager {
|
||||
private final CraftEngine plugin;
|
||||
|
||||
public AbstractAdvancementManager(CraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.momirealms.craftengine.core.advancement;
|
||||
|
||||
import net.momirealms.craftengine.core.plugin.Manageable;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
||||
|
||||
public interface AdvancementManager extends Manageable {
|
||||
|
||||
ConfigSectionParser parser();
|
||||
}
|
||||
@@ -14,4 +14,5 @@ public class LoadingSequence {
|
||||
public static final int JUKEBOX_SONG = 100;
|
||||
public static final int VANILLA_LOOTS = 110;
|
||||
public static final int EMOJI = 120;
|
||||
public static final int ADVANCEMENT = 130;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
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.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()) {
|
||||
String boundary = UUID.randomUUID().toString();
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(this.gitlabUrl + "/api/v4/projects/" + this.projectId + "/uploads"))
|
||||
.header("PRIVATE-TOKEN", this.accessToken)
|
||||
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
|
||||
.POST(buildMultipartBody(resourcePackPath, boundary))
|
||||
.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 = this.gitlabUrl + 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 (IOException e) {
|
||||
CraftEngine.instance().logger().warn(
|
||||
"[GitLab] Failed to upload resource pack: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private HttpRequest.BodyPublisher buildMultipartBody(Path filePath, String boundary) throws IOException {
|
||||
List<byte[]> parts = new ArrayList<>();
|
||||
String filePartHeader = "--" + boundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"file\"; filename=\"" + filePath.getFileName() + "\"\r\n" +
|
||||
"Content-Type: application/octet-stream\r\n\r\n";
|
||||
parts.add(filePartHeader.getBytes());
|
||||
|
||||
parts.add(Files.readAllBytes(filePath));
|
||||
parts.add("\r\n".getBytes());
|
||||
|
||||
String endBoundary = "--" + boundary + "--\r\n";
|
||||
parts.add(endBoundary.getBytes());
|
||||
|
||||
return HttpRequest.BodyPublishers.ofByteArrays(parts);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.core.plugin;
|
||||
|
||||
import net.momirealms.craftengine.core.advancement.AdvancementManager;
|
||||
import net.momirealms.craftengine.core.block.BlockManager;
|
||||
import net.momirealms.craftengine.core.entity.furniture.FurnitureManager;
|
||||
import net.momirealms.craftengine.core.font.FontManager;
|
||||
@@ -66,6 +67,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
protected GuiManager guiManager;
|
||||
protected SoundManager soundManager;
|
||||
protected VanillaLootManager vanillaLootManager;
|
||||
protected AdvancementManager advancementManager;
|
||||
|
||||
private final Consumer<CraftEngine> reloadEventDispatcher;
|
||||
private boolean isReloading;
|
||||
@@ -142,6 +144,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
this.vanillaLootManager.reload();
|
||||
this.guiManager.reload();
|
||||
this.packManager.reload();
|
||||
this.advancementManager.reload();
|
||||
if (reloadRecipe) {
|
||||
this.recipeManager.reload();
|
||||
}
|
||||
@@ -161,6 +164,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
this.itemBrowserManager.delayedLoad();
|
||||
// collect illegal characters from minecraft:default font
|
||||
this.fontManager.delayedLoad();
|
||||
this.advancementManager.delayedLoad();
|
||||
if (reloadRecipe) {
|
||||
// convert data pack recipes
|
||||
this.recipeManager.delayedLoad();
|
||||
@@ -210,6 +214,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
this.packManager.delayedInit();
|
||||
this.fontManager.delayedInit();
|
||||
this.vanillaLootManager.delayedInit();
|
||||
this.advancementManager.delayedInit();
|
||||
// reload the plugin
|
||||
try {
|
||||
this.reloadPlugin(Runnable::run, Runnable::run, true);
|
||||
@@ -228,6 +233,7 @@ public abstract class CraftEngine implements Plugin {
|
||||
public void onPluginDisable() {
|
||||
if (this.networkManager != null) this.networkManager.disable();
|
||||
if (this.fontManager != null) this.fontManager.disable();
|
||||
if (this.advancementManager != null) this.advancementManager.disable();
|
||||
if (this.packManager != null) this.packManager.disable();
|
||||
if (this.itemManager != null) this.itemManager.disable();
|
||||
if (this.blockManager != null) this.blockManager.disable();
|
||||
@@ -268,6 +274,8 @@ public abstract class CraftEngine implements Plugin {
|
||||
this.packManager.registerConfigSectionParsers(this.soundManager.parsers());
|
||||
// register vanilla loot parser
|
||||
this.packManager.registerConfigSectionParser(this.vanillaLootManager.parser());
|
||||
// register advancement parser
|
||||
this.packManager.registerConfigSectionParser(this.advancementManager.parser());
|
||||
}
|
||||
|
||||
protected abstract void platformDelayedEnable();
|
||||
@@ -391,6 +399,11 @@ public abstract class CraftEngine implements Plugin {
|
||||
return fontManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancementManager advancementManager() {
|
||||
return advancementManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranslationManager translationManager() {
|
||||
return translationManager;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.core.plugin;
|
||||
|
||||
import net.momirealms.craftengine.core.advancement.AdvancementManager;
|
||||
import net.momirealms.craftengine.core.block.BlockManager;
|
||||
import net.momirealms.craftengine.core.entity.furniture.FurnitureManager;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
@@ -61,6 +62,8 @@ public interface Plugin {
|
||||
|
||||
FontManager fontManager();
|
||||
|
||||
AdvancementManager advancementManager();
|
||||
|
||||
Config config();
|
||||
|
||||
TranslationManager translationManager();
|
||||
|
||||
@@ -28,6 +28,10 @@ public record Key(String namespace, String value) {
|
||||
return of(decompose(namespacedId, "minecraft"));
|
||||
}
|
||||
|
||||
public String[] decompose() {
|
||||
return new String[] { namespace, value };
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
|
||||
@@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx1G
|
||||
|
||||
# Project settings
|
||||
# Rule: [major update].[feature update].[bug fix]
|
||||
project_version=0.0.49
|
||||
project_version=0.0.50-beta.1
|
||||
config_version=29
|
||||
lang_version=6
|
||||
lang_version=7
|
||||
project_group=net.momirealms
|
||||
latest_supported_version=1.21.5
|
||||
latest_minecraft_version=1.21.5
|
||||
|
||||
Reference in New Issue
Block a user