diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..85b8c92 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +# Set update schedule for GitHub Actions + +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "daily" diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index 22007db..b175ccc 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -12,26 +12,32 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '21' distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v5 + with: + cache-read-only: false + + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" - name: Build with Gradle run: ./gradlew build env: + PRESERVE_PRERELEASE_VERSION: true DISABLE_PROPERTIES_UPDATE: true - - - name: Delete common libs - run: rm -r ./common/build/libs + VERSION_SUFFIX: ${{ env.short_commit_hash }} - name: Upload build artifacts uses: actions/upload-artifact@v4 with: - name: Artifacts - path: ./*/build/libs/ + name: Build Artifacts + path: ./loader/*/*/build/libs/ diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d7898d3..90f1b85 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,35 +14,39 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '21' distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: false - name: Build with Gradle run: ./gradlew build env: + IS_RELEASE: true DISABLE_PROPERTIES_UPDATE: true + VERSION_SUFFIX: "" - name: Upload to Modrinth run: ./gradlew modrinth env: + IS_RELEASE: true + DISABLE_PROPERTIES_UPDATE: true + VERSION_SUFFIX: "" MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} VERSION_NAME: ${{ github.event.release.name }} VERSION_IS_PRERELEASE: ${{ github.event.release.prerelease }} VERSION_CHANGELOG: ${{ github.event.release.body }} - - name: Delete common libs - run: rm -r ./common/build/libs - - name: Upload to GitHub uses: softprops/action-gh-release@v2 with: - files: ./*/build/libs/*.jar + files: ./loader/*/*/build/libs/*.jar diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..cc7fe46 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "buildSrc"] + path = buildSrc + url = https://github.com/OffsetMods538/multiversion-buildscripts diff --git a/LICENSE b/LICENSE index ba28297..801d9ff 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023,2024,2025 OffsetMonkey538 +Copyright (c) 2023-2026 OffsetMonkey538 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 375bd41..2fcdb9c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,19 @@ -# GitHub Resourcepack Manager -[![discord-singular](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/social/discord-singular_vector.svg)](https://discord.offsetmonkey538.top/) -[![modrinth](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/modrinth_vector.svg)](https://modrinth.com/mod/github-resourcepack-manager) -[![requires-monkeylib538](https://raw.githubusercontent.com/OffsetMods538/MonkeyLib538/master/images/requires_monkeylib538.png)](https://modrinth.com/mod/monkeylib538) -[![Requires Fabric API](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/requires/fabric-api_vector.svg)](https://modrinth.com/mod/fabric-api) +# Git Pack Manager +[![Chat with me on Discord](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/social/discord-singular_vector.svg)](https://discord.offsetmonkey538.top/) +[![Available on Modrinth](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/modrinth_vector.svg)](https://modrinth.com/mod/git-pack-manager) +[![Available on GitHub](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/github_vector.svg)](https://github.com/OffsetMods538/Git-Pack-Manager) +[![Requires MonkeyLib538](https://raw.githubusercontent.com/OffsetMods538/MonkeyLib538/master/images/requires_badge.svg)](https://modrinth.com/mod/monkeylib538) +[![Requires MESH Lib](https://raw.githubusercontent.com/OffsetMods538/MESH-Lib/refs/heads/master/images/requires_badge.svg)](https://modrinth.com/mod/mesh-lib) +[![Read the Documentation](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/documentation/ghpages_vector.svg)](https://git-pack-manager.docs.offsetmonkey538.top/) -Please read the documentation located [here](https://github-resourcepack-manager.docs.offsetmonkey538.top) and if you have any questions, feel free to contact me on [discord](https://discord.offsetmonkey538.top/) +## What's this? +Git Pack Manager is a mod/plugin for dedicated servers that allows for handling resource and data packs with git. +Using git allows for easier management and collaboration. +The mod also supports automatically updating the packs as soon as modifications are made with git. +Resource packs are automatically combined based on their priorities. + +See the [boring showcase for Git Pack Manager](https://www.youtube.com/watch?v=S-ei-7u6yCI) video for an example of what it can do. +This allows for easier management of the packs and makes collaboration easier. + +## Using +Please read the documentation located [here](https://git-pack-manager.docs.offsetmonkey538.top) and if you have any questions, feel free to contact me on [Discord](https://discord.offsetmonkey538.top/) diff --git a/build.gradle b/build.gradle index d0be512..cb30f92 100644 --- a/build.gradle +++ b/build.gradle @@ -1,64 +1,43 @@ -import dex.plugins.outlet.v2.util.ReleaseType - plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' apply false - id 'io.github.dexman545.outlet' version '1.6.1' apply false - id 'com.modrinth.minotaur' version '2.+' apply false + id 'multiloader-root' + id 'java' + id 'net.neoforged.moddev' version "${neoforged_moddev}" apply false + id 'fabric-loom' version "${fabric_loom}" apply false + id 'io.papermc.paperweight.userdev' version "${papermc_paperweight_userdev}" apply false + id 'xyz.jpenilla.run-paper' version "${jpenilla_run_task}" apply false + id 'com.gradleup.shadow' version "${gradleup_shadow}" apply false + id 'xyz.jpenilla.resource-factory' version "${jpenilla_resource_factory}" apply false + id 'io.github.dexman545.outlet' version "${dexman_outlet}" apply false + id 'com.modrinth.minotaur' version "${modrinth_minotaur}" apply false + id 'hamburg.janove.elevator-music' version "0.1" } -allprojects { - group = "top.offsetmonkey538.githubresourcepackmanager" - - repositories { - mavenLocal() - } -} +//elevatorMusic { +// if (Boolean.parseBoolean(System.getenv("DISABLE_MUSIC"))) return +// waitMusic = file("${rootProject.projectDir}/veryImportant/music.wav") +// successSound = file("${rootProject.projectDir}/veryImportant/done.wav") +//} subprojects { - apply plugin: "java" - - archivesBaseName = "github-resourcepack-manager-${project.nameSuffix}" - version = "${project.mod_version}+${project.minecraft_version}" - - java { - withSourcesJar() - } + apply plugin: "java-library" - jar { - from("${rootProject.projectDir}/LICENSE") { - rename { "${it}" } + repositories { + mavenCentral() + mavenLocal() + exclusiveContent { + forRepository { + maven { + name = "OffsetMods538" + url = "https://maven.offsetmonkey538.top/releases" + } + } + filter { + includeGroupAndSubgroups "top.offsetmonkey538" + } } } -} -configure(subprojects.findAll { it.name != "common" }) { - apply plugin: 'com.modrinth.minotaur' - apply plugin: 'io.github.dexman545.outlet' - - outlet { - mcVersionRange = rootProject.supported_minecraft_versions - allowedReleaseTypes = Set.of(ReleaseType.RELEASE) + dependencies { + compileOnlyApi "org.jspecify:jspecify:${rootProject.jspecify_version}" } - - modrinth { - token = System.getenv("MODRINTH_TOKEN") - projectId = "github-resourcepack-manager" - def customVersionName = System.getenv("VERSION_NAME") - if (customVersionName != null) versionName = customVersionName - versionNumber = "${project.version}" - versionType = "alpha" - def isPreRelease = System.getenv("VERSION_IS_PRERELEASE") - versionType = !"false".equals(isPreRelease) ? "beta" : "release" - additionalFiles = [sourcesJar.archiveFile] - gameVersions = outlet.mcVersions() - syncBodyFrom = rootProject.file("README.md").text - def changelogEnv = System.getenv("VERSION_CHANGELOG") - if (changelogEnv != null) changelog = changelogEnv - - dependencies { - embedded.version "mesh-lib", "${project.meshlib_version}" - } - } - tasks.modrinth.dependsOn(tasks.modrinthSyncBody) - } diff --git a/buildSrc b/buildSrc new file mode 160000 index 0000000..c0d7dd2 --- /dev/null +++ b/buildSrc @@ -0,0 +1 @@ +Subproject commit c0d7dd2fa6ad3280692c8921eaed4ce4c5b48f17 diff --git a/common/build.gradle b/common/build.gradle index 91a5e77..919b20b 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,30 +1,36 @@ plugins { - id 'com.gradleup.shadow' version '9.0.0-beta4' + id 'multiloader-base' } repositories { - mavenCentral() maven { - name = "OffsetMods538" - url = "https://maven.offsetmonkey538.top/releases" + name = "Mojang Libraries" + url = "https://libraries.minecraft.net" content { - includeGroup "top.offsetmonkey538.meshlib" + includeGroup "com.mojang" } } } dependencies { - implementation "org.eclipse.jgit:org.eclipse.jgit:${project.jgit_version}" - implementation "blue.endless:jankson:${project.jankson_version}" + compileOnlyApi("top.offsetmonkey538.monkeylib538:monkeylib538-common:${rootProject.monkeylib538_version}+common") { + exclude(group: "net.kyori") + } + compileOnlyApi("top.offsetmonkey538.meshlib:mesh-lib-common:${rootProject.meshlib_version}+common") { + exclude(group: "net.kyori") + } - shadow compileOnly("top.offsetmonkey538.meshlib:mesh-lib-api:${project.meshlib_version}") + compileOnlyApi "net.kyori:adventure-api:${project.adventure_api_version}" + compileOnlyApi "net.kyori:adventure-text-minimessage:${project.adventure_api_version}" - // should be bundeled with minecraft - shadow compileOnly("com.google.guava:guava:${project.guava_version}") - shadow compileOnly("commons-io:commons-io:${project.commons_version}") - shadow compileOnly("com.google.code.gson:gson:${project.gson_version}") + api("org.eclipse.jgit:org.eclipse.jgit:${rootProject.jgit_version}") { + transitive = false + } - // Jetbrains annotations for gud code - shadow compileOnly("org.jetbrains:annotations:24.0.0") + // should be bundled with minecraft + compileOnly "com.mojang:brigadier:${rootProject.brigadier_version}" + compileOnly "com.google.guava:guava:${rootProject.guava_version}" + compileOnly "commons-io:commons-io:${rootProject.commonsio_version}" + compileOnly "org.apache.commons:commons-lang3:${rootProject.commonslang_version}" + compileOnly "com.google.code.gson:gson:${rootProject.gson_version}" } -tasks.build.dependsOn(shadowJar) diff --git a/common/gradle.properties b/common/gradle.properties index 8f66902..6d3b82c 100644 --- a/common/gradle.properties +++ b/common/gradle.properties @@ -1,8 +1,5 @@ -jgit_version = 6.9.0.202403050737-r -guava_version = 33.4.0-jre -commons_version = 2.18.0 -gson_version = 2.11.0 -# Jankson, check at https://github.com/falkreon/Jankson -jankson_version = 1.2.3 +project_name = common -nameSuffix = api \ No newline at end of file +# Dependencies +## Adventure +adventure_api_version = 4.26.1 \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java deleted file mode 100644 index 42f4c98..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ /dev/null @@ -1,204 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager; - -import org.apache.commons.io.FileUtils; -import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.handler.GitHandler; -import top.offsetmonkey538.githubresourcepackmanager.handler.PackHandler; -import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; -import top.offsetmonkey538.githubresourcepackmanager.config.ConfigManager; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; -import top.offsetmonkey538.githubresourcepackmanager.utils.*; -import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.regex.Pattern; - -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public final class GithubResourcepackManager { - private GithubResourcepackManager() { - - } - - public static final String MOD_ID = "github-resourcepack-manager"; - public static final String MOD_URI = "gh-rp-manager"; - - public static final Path RESOURCEPACK_FOLDER = PlatformMain.INSTANCE.getConfigDir().resolve(".resource-pack"); - public static final Path REPO_ROOT_FOLDER = RESOURCEPACK_FOLDER.resolve("git"); - public static final Path OUTPUT_FOLDER = RESOURCEPACK_FOLDER.resolve("output"); - public static final Pattern PACK_NAME_PATTERN = Pattern.compile("\\d+-"); - public static final UUID PACK_UUID = UUID.fromString("60ab8dc7-08d1-4f5f-a9a8-9a01d048b7b9"); - - - public static ModConfig config; - - public static GitHandler gitHandler; - public static PackHandler packHandler; - - - public static void initialize() { - PlatformCommand.INSTANCE.registerGithubRpManagerCommand(); - - ConfigManager.loadConfig(); - - try { - createFolderStructure(); - } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to create folder structure!", e); - } - - HttpHandlerRegistry.INSTANCE.register(MOD_URI, new MainHttpHandler()); - - PlatformMain.INSTANCE.runOnServerStart(() -> updatePack(UpdateType.RESTART)); - } - - private static void createFolderStructure() throws GithubResourcepackManagerException { - try { - Files.createDirectories(OUTPUT_FOLDER); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to create directory '%s'!", OUTPUT_FOLDER); - } - } - - public static void updatePack(final UpdateType updateType) { - LOGGER.info("Updating resourcepack..."); - - if (updateType == UpdateType.COMMAND_FORCE) { - LOGGER.warn("Forced pack update! Deleting data directory and continuing..."); - try { - FileUtils.deleteDirectory(RESOURCEPACK_FOLDER.toFile()); - } catch (IOException e) { - LOGGER.error("Failed to delete directory!", e); - return; - } - } - - try { - createFolderStructure(); - } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to create folder structure!", e); - } - - // Git stuff - gitHandler = new GitHandler(); - - LOGGER.info("Updating git repository..."); - boolean failed = false; - try { - gitHandler.updateRepositoryAndGenerateCommitProperties(); - } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to update git repository!", e); - failed = true; - } - if (!failed) LOGGER.info("Successfully updated git repository!"); - - - // Get the location of the old pack, if it exists. - final String oldPackName = getOldPackName(); - final Path oldPackPath = oldPackName == null ? null : OUTPUT_FOLDER.resolve(oldPackName); - - - // Check if pack was updated - final boolean wasUpdated = gitHandler.getWasUpdated() || oldPackPath == null || !oldPackPath.toFile().exists(); - if (!wasUpdated) { - LOGGER.info("Pack hasn't changed since last update. Skipping new pack generation."); - } - - // Generate pack - packHandler = new PackHandler(); - - LOGGER.info("Getting pack location..."); - failed = false; - try { - packHandler.generatePack(wasUpdated, oldPackPath, oldPackName); - } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to generate pack!", e); - failed = true; - } - if (!failed) LOGGER.info("Pack location is '%s'!", packHandler.getOutputPackPath()); - - - // Update server.properties file. - try { - PlatformServerProperties.INSTANCE.updatePackProperties(packHandler); - } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to update server.properties file!", e); - } - - // Generate placeholder map - final Map placeholders = new HashMap<>(); - if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); - placeholders.put("{downloadUrl}", config.getPackUrl(packHandler.getOutputPackName())); - placeholders.put("{updateType}", updateType.name()); - placeholders.put("{wasUpdated}", String.valueOf(wasUpdated)); - LOGGER.info("Placeholders: %s", placeholders); - - // Send chat message - try { - sendUpdateMessage(wasUpdated, placeholders); - } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to send update message in chat!", e); - } - - // Trigger webhook - try { - triggerWebhook(wasUpdated, placeholders, updateType); - } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to trigger webhook!", e); - } - - - LOGGER.info("Resourcepack updated!"); - } - - private static void triggerWebhook(boolean wasUpdated, Map placeholders, UpdateType updateType) throws GithubResourcepackManagerException { - if (config.webhookUrl == null || config.webhookBody == null) return; - if (config.webhookBody.contains("discord") && !wasUpdated) { - LOGGER.info("Not sending discord webhook because pack was not updated."); - return; - } - - try { - //noinspection DataFlowIssue: Only returns null when `config.webhookBody` is null, which we have already checked - String webhookBody = Files.readString(config.getWebhookBody()); - webhookBody = StringUtils.replacePlaceholders(webhookBody, placeholders, true); - - WebhookSender.send(webhookBody, config.getWebhookUrl(), updateType, gitHandler.getWasUpdated()); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to read content of webhook body file '%s'!", e, config.webhookBody); - } - } - - private static void sendUpdateMessage(boolean wasUpdated, final Map placeholders) throws GithubResourcepackManagerException { - if (!wasUpdated) { - LOGGER.info("Not sending chat message because pack was not updated."); - return; - } - - PlatformText.INSTANCE.sendUpdateMessage(placeholders); - } - - private static String getOldPackName() { - final String oldPackUrl = PlatformServerProperties.INSTANCE.getResourcePackUrl(); - if (oldPackUrl == null) return null; - - int nameStartIndex = oldPackUrl.lastIndexOf('/'); - if (nameStartIndex == -1) return null; - - return oldPackUrl.substring(nameStartIndex + 1); - } - - public enum UpdateType { - RESTART, - WEBHOOK, - COMMAND, - COMMAND_FORCE - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java deleted file mode 100644 index 87dc2d9..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java +++ /dev/null @@ -1,160 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.config; - -import blue.endless.jankson.Jankson; -import blue.endless.jankson.JsonElement; -import blue.endless.jankson.JsonObject; -import blue.endless.jankson.JsonPrimitive; -import blue.endless.jankson.api.SyntaxError; -import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; - -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public final class ConfigManager { - private ConfigManager() { - - } - - public static final String VERSION_KEY = "!!!version"; - - public static final Path OLD_CONFIG_FILE_PATH = PlatformMain.INSTANCE.getConfigDir().getParent().resolve(MOD_ID + ".json"); - public static final Path NEW_CONFIG_FILE_PATH = PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json"); - public static final Path CURRENT_CONFIG_FILE_PATH = PlatformMain.INSTANCE.getConfigDir().resolve("config").resolve("main.json"); - - - public static void loadConfig() { - if (Files.exists(OLD_CONFIG_FILE_PATH)) { - try { - Files.createDirectories(NEW_CONFIG_FILE_PATH.getParent()); - Files.move(OLD_CONFIG_FILE_PATH, NEW_CONFIG_FILE_PATH); - } catch (IOException e) { - throw new RuntimeException("Failed to move config file to new location!", e); - } - } - if (Files.exists(NEW_CONFIG_FILE_PATH)) { - try { - Files.createDirectories(CURRENT_CONFIG_FILE_PATH.getParent()); - Files.move(NEW_CONFIG_FILE_PATH, CURRENT_CONFIG_FILE_PATH); - } catch (IOException e) { - throw new RuntimeException("Failed to move config file to new location!", e); - } - } - - config = new ModConfig(); - if (CURRENT_CONFIG_FILE_PATH.toFile().exists()) config = load(); - save(config); - - LOGGER.info("Writing default webhook bodies"); - config.createDefaultWebhooks(); - - // Checking if config is valid - final List errors = checkConfigErrors(); - - // Return when nothing's wrong - if (errors.isEmpty()) return; - - // There were errors, time to log em. - LOGGER.error("There were problems with the config for GitHub Resourcepack Manager, see below for more details!"); - errors.stream().map(string -> "\t" + string).forEach(LOGGER::error); - LOGGER.error("There were problems with the config for Github Resourcepack Manager, see above for more details!"); - - throw new RuntimeException("There were problems with the config for Github Resourcepack Manager, see above for more details!"); - } - - private static @NotNull List checkConfigErrors() { - final List errors = new ArrayList<>(); - - if (config.serverPublicIp == null) errors.add("Field 'serverPublicIp' not set!"); - if (config.repoUrl == null) errors.add("Field 'repoUrl' not set!"); - if (config.isRepoPrivate) { - if (config.githubUsername == null) errors.add("Field 'githubUsername' not set, but 'isRepoPrivate' is true!"); - if (config.githubToken == null) errors.add("Field 'githubToken' not set, but 'isRepoPrivate' is true!"); - } else { - if (config.githubUsername != null) errors.add("Field 'githubUsername' set, but 'isRepoPrivate' is false! Can be unset"); - if (config.githubToken != null) errors.add("Field 'githubToken' set, but 'isRepoPrivate' is false! Can be unset"); - } - return errors; - } - - private static ModConfig load() { - final Jankson jankson = Jankson.builder().build(); - final File configFile = CURRENT_CONFIG_FILE_PATH.toFile(); - - // Load the config from disk - final JsonObject json; - try { - json = jankson.load(configFile); - } catch (IOException e) { - LOGGER.error("Config file '%s' could not be read!", CURRENT_CONFIG_FILE_PATH, e); - return config; - } catch (SyntaxError e) { - LOGGER.error("Config file '%s' is formatted incorrectly!", CURRENT_CONFIG_FILE_PATH, e); - return config; - } - - // Check if version matches the latest version - applyDatafixers(config, configFile.toPath(), json, jankson); - - // Load config class from the final json - return jankson.fromJson(json, config.getClass()); - } - - private static void applyDatafixers(ModConfig config, Path configFile, JsonObject json, Jankson jankson) { - int last_version = json.getInt(VERSION_KEY, 0); - int current_version = config.getConfigVersion(); - if (last_version < current_version) try { - final Path backupPath = configFile.resolveSibling(String.format("%s-%s-backup.json", configFile.getFileName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy_MM_dd-HH_mm_ss")))); - Files.copy(configFile, backupPath); - - for (Datafixer datafixer : config.getDatafixers().subList(last_version, current_version)) { - datafixer.apply(json, jankson); - } - save(config); - } catch (IOException e) { - LOGGER.error("Unable to create backup of config file '%s'! Continuing anyway cause I don't care if your config gets messed up and I can't think of a reason for this even happening cause like the initial config file has to be there so I'd imagine that the directory is writeable so like why wouldn't it be possible to write the backup of the file?", configFile, e); - } - - if (last_version > current_version) { - throw new IllegalStateException(String.format("Config file '%s' is for a newer version of the mod, please update! Expected config version '%s', found '%s'!", CURRENT_CONFIG_FILE_PATH, current_version, last_version)); - } - } - - private static void save(ModConfig config) { - final Jankson jankson = Jankson.builder().build(); - - // Convert config to json - final JsonElement jsonAsElement = jankson.toJson(config); - if (!(jsonAsElement instanceof final JsonObject json)) { - LOGGER.error("Could not cast '%s' to 'JsonObject'! Config will not be saved!", jsonAsElement.getClass().getName()); - return; - } - - // Write config version - json.put(VERSION_KEY, new JsonPrimitive(config.getConfigVersion()), "!!!!! DO NOT MODIFY THIS VALUE !!!!"); - - // Convert final json to string - final String result = json.toJson(true, true); - - try { - Files.createDirectories(CURRENT_CONFIG_FILE_PATH.getParent()); - Files.writeString(CURRENT_CONFIG_FILE_PATH, result); - } catch (IOException e) { - LOGGER.error("Config file '%s' could not be written to!", CURRENT_CONFIG_FILE_PATH, e); - } - } - - @FunctionalInterface - public interface Datafixer { - void apply(JsonObject original, Jankson jankson); - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java deleted file mode 100644 index 2cbb7fa..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ /dev/null @@ -1,115 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.config; - -import blue.endless.jankson.Comment; -import blue.endless.jankson.Jankson; -import blue.endless.jankson.JsonGrammar; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.BasicWebhook; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.BasicMessage; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.EmbedMessage; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; - -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; - -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; -import static top.offsetmonkey538.githubresourcepackmanager.config.ConfigManager.CURRENT_CONFIG_FILE_PATH; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public class ModConfig { - @Comment("!!!!Please check the wiki for how to set up the mod. It is linked on both the Modrinth and GitHub pages!!!!") - public String packUpdateMessage = "Server resourcepack has been updated!\nPlease click {packUpdateCommand} to get the most up to date pack."; - public String packUpdateMessageHoverMessage = "{longDescription}"; - @Comment("The public ip of your server (\"123.45.67.89\" or \"play.coolserver.net\")") - public String serverPublicIp = null; - @Comment("If set, this port will be used in the server.properties file instead of the Minecraft server port. HTTP server will still be hosted on the Minecraft port. Only useful when running the server behind a proxy like nginx, traefik, cloudflare tunnel, etc.") - public String proxyPort = null; - @Comment("Should be \"[YOUR BRANCH NAME HERE]\". Common names include \"master\" and \"main\"") - public String branch = "master"; - public String repoUrl = null; - @Comment("Where the mod will search for resource packs in the cloned repository") - public String resourcePackRoot = ""; - public boolean isRepoPrivate = false; - public String githubUsername = null; - @Comment("PLEASE DON'T SHARE THIS WITH ANYONE EVER") - public String githubToken = null; - public String webhookUrl = null; - public String webhookBody = null; - - - protected String getName() { - return MOD_ID + "/" + MOD_ID; - } - - protected int getConfigVersion() { - return 2; - } - - protected List getDatafixers() { - return List.of( - (original, jankson) -> { - // 0 -> 1 - original.put("branch", jankson.toJson(jankson.getMarshaller().marshall(String.class, original.get("githubRef")).replace("refs/heads/", ""))); - original.put("repoUrl", original.get("githubUrl")); - original.put("isRepoPrivate", original.get("isPrivate")); - }, - (original, jankson) -> { - // 1 -> 2 - // noop - } - ); - } - - public void createDefaultWebhooks() { - final Jankson jankson = new Jankson.Builder().build(); - final List webhookBodies = List.of(new BasicWebhook(), new BasicMessage(), new EmbedMessage()); - - for (DefaultWebhookBody webhook : webhookBodies) { - final Path location = CURRENT_CONFIG_FILE_PATH.getParent().resolve(webhook.getName()); - - if (Files.exists(location)) continue; - - try { - Files.createDirectories(location.getParent()); - Files.writeString(location, jankson.toJson(webhook).toJson(JsonGrammar.STRICT)); - } catch (IOException e) { - LOGGER.error("Failed to write default webhook body '%s'!", webhook.getName(), e); - } - } - } - - public String getPackUrl(String outputFileName) { - return String.format( - "http://%s:%s/%s/%s", - serverPublicIp, - proxyPort == null ? PlatformServerProperties.INSTANCE.getServerPort() : proxyPort, - MOD_URI, - outputFileName - ); - } - - public URI getWebhookUrl() { - if (webhookUrl == null) return null; - return URI.create(webhookUrl); - } - - public Path getWebhookBody() { - if (webhookBody == null) return null; - return CURRENT_CONFIG_FILE_PATH.getParent().resolve(webhookBody); - } - - public Path getResourcePackRoot() { - return REPO_ROOT_FOLDER.resolve(config.resourcePackRoot.startsWith("/") ? config.resourcePackRoot.substring(1) : config.resourcePackRoot); - } - public Path getPacksDir() { - return getResourcePackRoot().resolve("packs"); - } - - public String getGithubRef() { - return "refs/heads/" + branch; - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/DefaultWebhookBody.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/DefaultWebhookBody.java deleted file mode 100644 index 5e0be6b..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/DefaultWebhookBody.java +++ /dev/null @@ -1,5 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.config.webhook; - -public interface DefaultWebhookBody { - String getName(); -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/BasicMessage.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/BasicMessage.java deleted file mode 100644 index 640a4d4..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/BasicMessage.java +++ /dev/null @@ -1,15 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord; - -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; - -public final class BasicMessage implements DefaultWebhookBody { - - public final String username = "GitHub Resource Pack Manager"; - public final String avatar_url = "https://github.com/OffsetMods538/Github-Resourcepack-Manager/blob/master/src/main/resources/assets/github-resourcepack-manager/icon.png?raw=true"; - public final String content = "New update for pack released!\nDescription: {shortDescription}\nDownload [here]({downloadUrl})"; - - @Override - public String getName() { - return "discord/basic_message.json"; - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/exception/GithubResourcepackManagerException.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/exception/GithubResourcepackManagerException.java deleted file mode 100644 index bf397a0..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/exception/GithubResourcepackManagerException.java +++ /dev/null @@ -1,19 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.exception; - -public class GithubResourcepackManagerException extends Exception { - public GithubResourcepackManagerException(String message) { - super(message); - } - - public GithubResourcepackManagerException(String message, Object... args) { - this(String.format(message, args)); - } - - public GithubResourcepackManagerException(String message, Throwable cause) { - super(message, cause); - } - - public GithubResourcepackManagerException(String message, Throwable cause, Object... args) { - this(String.format(message, args), cause); - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java deleted file mode 100644 index 94ed0c5..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java +++ /dev/null @@ -1,160 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.handler; - -import org.apache.commons.io.FileUtils; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.PullResult; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.errors.RepositoryNotFoundException; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.merge.ContentMergeStrategy; -import org.eclipse.jgit.merge.MergeStrategy; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.transport.CredentialsProvider; -import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.git.CommitProperties; - -import java.io.IOException; - -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public class GitHandler { - - private CommitProperties commitProperties; - private boolean wasUpdated; - - public void updateRepositoryAndGenerateCommitProperties() throws GithubResourcepackManagerException { - String originalCommitHash; - try { - originalCommitHash = getLatestCommitHash(); - } catch (GithubResourcepackManagerException e) { - if (!(e.getCause() instanceof RepositoryNotFoundException)) throw e; - - originalCommitHash = ""; - } - - updateRepository(true); - - final String newCommitHash = getLatestCommitHash(); - - commitProperties = getLatestCommitProperties(originalCommitHash, newCommitHash); - wasUpdated = !newCommitHash.equals(originalCommitHash); - } - - - private static CommitProperties getLatestCommitProperties(String lastCommitHash, String newCommitHash) throws GithubResourcepackManagerException { - try { - final Repository repository = getRepository(); - final RevCommit commit = new RevWalk(getRepository()).parseCommit(getLatestCommit().getObjectId()); - - - return new CommitProperties( - repository.getFullBranch(), - lastCommitHash, - newCommitHash, - commit.getAuthorIdent().getName(), - commit.getFullMessage().replace("\r\n", "\\n").replace("\n", "\\n"), - commit.getShortMessage(), - String.valueOf(commit.getCommitTime()) - ); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to parse latest commit!", e); - } - } - - private static void updateRepository(boolean retry) throws GithubResourcepackManagerException { - // Create credentials provider if repository is private - CredentialsProvider credentialsProvider = null; - if (config.isRepoPrivate) - credentialsProvider = new UsernamePasswordCredentialsProvider(config.githubUsername, config.githubToken); - - // If the repo folder doesn't exist, clone the repository. - if (!REPO_ROOT_FOLDER.toFile().exists()) cloneRepository(credentialsProvider); - - // Pull from the remote - boolean updateFailed = false; - try (Git git = Git.open(REPO_ROOT_FOLDER.toFile())) { - final PullResult result = git.pull() - .setCredentialsProvider(credentialsProvider) - .setContentMergeStrategy(ContentMergeStrategy.THEIRS) - .setStrategy(MergeStrategy.THEIRS) - .setRemoteBranchName(config.getGithubRef()) - .call(); - - // Handle errors - if (result.isSuccessful()) { - LOGGER.debug("Successfully updated repository!"); - return; - } - - LOGGER.error("Failed to update repository!"); - updateFailed = true; - } catch (GitAPIException e) { - LOGGER.error("Failed to update repository!", e); - updateFailed = true; - } catch (IOException e) { - LOGGER.error("Failed to open repository!", e); - updateFailed = true; - } finally { - if (updateFailed && retry) { - // Oh god this is so stupid - // Repository updating *should* only fail when remote repository is changed or - // some files are changed locally, so it should be fine to just delete and re-clone it. - LOGGER.info("Deleting git folder and trying again..."); - - try { - FileUtils.deleteDirectory(REPO_ROOT_FOLDER.toFile()); - } catch (IOException e) { - LOGGER.error("Failed to delete directory!", e); - } - - updateRepository(false); - } - } - } - - private static void cloneRepository(CredentialsProvider credentialsProvider) throws GithubResourcepackManagerException { - try { - Git git = Git.cloneRepository() - .setURI(config.repoUrl) - .setDirectory(REPO_ROOT_FOLDER.toFile()) - .setBranch(config.getGithubRef()) - .setCredentialsProvider(credentialsProvider) - .call(); - git.close(); - } catch (GitAPIException e) { - throw new GithubResourcepackManagerException("Failed to clone repository!", e); - } - } - - private static String getLatestCommitHash() throws GithubResourcepackManagerException { - return getLatestCommit().getObjectId().getName(); - } - - private static Ref getLatestCommit() throws GithubResourcepackManagerException { - try { - return getRepository().findRef("HEAD"); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to get latest commit in repository!", e); - } - } - - private static Repository getRepository() throws GithubResourcepackManagerException { - try (Git git = Git.open(REPO_ROOT_FOLDER.toFile())) { - return git.getRepository(); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to open repository!", e); - } - } - - public CommitProperties getCommitProperties() { - return commitProperties; - } - - public boolean getWasUpdated() { - return wasUpdated; - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java deleted file mode 100644 index c20635f..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java +++ /dev/null @@ -1,214 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.handler; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.filefilter.HiddenFileFilter; -import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.utils.MyFileUtils; -import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; -import top.offsetmonkey538.githubresourcepackmanager.utils.ZipUtils; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Comparator; -import java.util.List; -import java.util.Random; -import java.util.stream.Stream; - -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public class PackHandler { - private Path outputPackPath; - - public void generatePack(boolean wasUpdated, Path oldPackPath, String oldPackName) throws GithubResourcepackManagerException { - outputPackPath = handleOldPackAndGetOutputPackPath(wasUpdated, oldPackPath, oldPackName); - - // If using the old pack, don't generate a new one. - if (!wasUpdated) return; - - // Generate new one - generateNewPack(); - } - - private void generateNewPack() throws GithubResourcepackManagerException { - final boolean isMultiPack; - - try { - isMultiPack = getPackType(); - } catch (GithubResourcepackManagerException e) { - throw new GithubResourcepackManagerException("Failed to determine pack type!", e); - } - - - // Create temp directories to extract pack(s) into. - final Path tmpDir; - try { - tmpDir = Files.createDirectories(RESOURCEPACK_FOLDER.resolve("temp")); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to create temporary directory!", e); - } - final File tempOutputDir = MyFileUtils.createDir(tmpDir.resolve("output").toFile()); - - // Extract packs into the temporary packs directory - final List sourcePacks; - try { - sourcePacks = extractSourcePacks(isMultiPack, tempOutputDir); - } catch (GithubResourcepackManagerException e) { - throw new GithubResourcepackManagerException("Failed to extract source packs!", e); - } - - // Write file with source pack names - try { - writeSourcePacksFile(isMultiPack, sourcePacks, tempOutputDir); - } catch (GithubResourcepackManagerException e) { - throw new GithubResourcepackManagerException("Failed to write pack content file!", e); - } - - // Zip the pack content and put the output file in the output directory. - try { - ZipUtils.zipDirectory(tempOutputDir, outputPackPath.toFile()); - } catch (GithubResourcepackManagerException e) { - throw new GithubResourcepackManagerException("Failed to zip pack content!", e); - } - - // Delete temp directories. - try { - FileUtils.deleteDirectory(tmpDir.toFile()); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to delete temporary directory!", e); - } - } - - private void writeSourcePacksFile(boolean isMultiPack, List sourcePacks, File outputDir) throws GithubResourcepackManagerException { - if (!isMultiPack) return; - - final Path sourcePacksFile = outputDir.toPath().resolve("content.txt"); - MyFileUtils.createNewFile(sourcePacksFile.toFile()); - - StringBuilder fileContent = new StringBuilder("This pack was put together using GitHub Resourcepack Manager from the following packs:\n\n"); - - for (File pack : sourcePacks) { - fileContent.append("- ").append(StringUtils.nameWithoutPriorityString(pack)).append("\n"); - } - - try { - Files.writeString(sourcePacksFile, fileContent); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to write to file '%s'!", e, sourcePacksFile); - } - } - - private List extractSourcePacks(boolean isMultiPack, File tempOutputDir) throws GithubResourcepackManagerException { - // Gather source packs - final List sourcePacks; - - if (!isMultiPack) sourcePacks = List.of(config.getResourcePackRoot().toFile()); - else sourcePacks = gatherSourcePacks(); - - // Extract source packs - for (File sourcePack : sourcePacks) { - if (sourcePack.isDirectory()) { - try { - FileUtils.copyDirectory(sourcePack, tempOutputDir, HiddenFileFilter.VISIBLE); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to copy pack '%s' into output directory '%s'!", e, sourcePack, tempOutputDir); - } - continue; - } - if (sourcePack.getName().endsWith(".zip")) { - ZipUtils.unzipFile(sourcePack, tempOutputDir); - continue; - } - - LOGGER.error("'%s' is not a valid pack! Ignoring...", sourcePack); - sourcePacks.remove(sourcePack); - } - - return sourcePacks; - } - - private List gatherSourcePacks() throws GithubResourcepackManagerException { - // Gather resource packs - final File[] sourcePacksArray = config.getPacksDir().toFile().listFiles(); - if (sourcePacksArray == null) throw new GithubResourcepackManagerException("Repository contains empty 'packs' folder!"); - - // Return source packs sorted in correct order. - return Stream.of(sourcePacksArray) - .sorted(Comparator.comparingInt(StringUtils::extractPriorityFromFile).reversed()) - .toList(); - } - - /** - * Get the type of the pack. Either multi or single pack. - * - * @return true if type is multi pack, false if it's single pack. - * @throws GithubResourcepackManagerException when the type of the pack couldn't be determined. - */ - private boolean getPackType() throws GithubResourcepackManagerException { - LOGGER.info("Checking for 'pack.mcmeta' in repository root..."); - final boolean hasPackMcmeta = config.getResourcePackRoot().resolve("pack.mcmeta").toFile().exists(); - LOGGER.info("%sFound!", hasPackMcmeta ? "" : "Not "); - - LOGGER.info("Checking for 'packs' directory in repository root..."); - final boolean hasPacksFolder = config.getPacksDir().toFile().exists() && config.getPacksDir().toFile().isDirectory(); - LOGGER.info("%sFound!", hasPacksFolder ? "" : "Not "); - - if (hasPackMcmeta && hasPacksFolder) { - throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in repository root '%s'!", config.getPacksDir().toAbsolutePath()); - } - if (!hasPackMcmeta && !hasPacksFolder) { - throw new GithubResourcepackManagerException("Found neither 'pack.mcmeta' nor the 'packs' directory in repository root '%s'!", config.getPacksDir().toAbsolutePath()); - } - - - if (hasPackMcmeta) { - LOGGER.info("Using repository root as resource pack."); - return false; - } - - LOGGER.info("Using 'packs' directory for resource packs."); - return true; - } - - - private Path handleOldPackAndGetOutputPackPath(boolean wasUpdated, @Nullable Path oldPackPath, String oldPackName) { - if (!wasUpdated) return oldPackPath; - - final String newPackName = generateRandomPackName(oldPackName); - - try { - if (oldPackPath != null && Files.exists(oldPackPath)) Files.delete(oldPackPath); - } catch (IOException e) { - LOGGER.error("Failed to delete old pack!", new GithubResourcepackManagerException("Failed to delete old pack '%s'!", e, oldPackPath)); - } - return OUTPUT_FOLDER.resolve(newPackName); - } - - private String generateRandomPackName(@Nullable String oldPackNameString) { - long oldPackName = -1; - - if (oldPackNameString != null) try { - oldPackName = Long.parseLong(oldPackNameString.replace(".zip", "")); - } catch (NumberFormatException ignored) {} - - - long newPackName = Math.abs(new Random().nextLong()); - if (newPackName == oldPackName) newPackName++; - return newPackName + ".zip"; - } - - public Path getOutputPackPath() { - return outputPackPath; - } - - public File getOutputPackFile() { - return getOutputPackPath().toFile(); - } - - public String getOutputPackName() { - return getOutputPackFile().getName(); - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java deleted file mode 100644 index 217b1a0..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.networking; - -import io.netty.channel.*; -import io.netty.handler.codec.http.*; - -import java.io.File; -import java.nio.file.Files; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.packHandler; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public final class FileHttpHandler { - private FileHttpHandler() {} - - public static void handleRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { - final File fileToServe = packHandler.getOutputPackFile(); - - final long fileLength = fileToServe.length(); - - final HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); - response.headers().set(CONTENT_LENGTH, fileLength); - response.headers().set(CONTENT_TYPE, Files.probeContentType(fileToServe.toPath())); - - ctx.write(response); - - final ChannelFuture sendFileFuture = ctx.write(new DefaultFileRegion(fileToServe, 0, fileLength), ctx.newProgressivePromise()); - - // progress - sendFileFuture.addListener(new ChannelProgressiveFutureListener() { - @Override - public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception { - LOGGER.debug("%s Transfer Progress: %s/%s\n", future.channel(), progress, total); - } - - @Override - public void operationComplete(ChannelProgressiveFuture future) throws Exception { - LOGGER.debug("%s Transfer complete!\n", future.channel()); - } - }); - - - ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE); - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java deleted file mode 100644 index 4771df9..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.networking; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpMethod; -import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.meshlib.api.HttpHandler; - -import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public class MainHttpHandler implements HttpHandler { - - @Override - public void handleRequest(@NotNull ChannelHandlerContext ctx, @NotNull FullHttpRequest request) throws Exception { - if (!request.decoderResult().isSuccess()) { - HttpHandler.sendError(ctx, BAD_REQUEST); - return; - } - - LOGGER.debug("Received Request: " + request); - - - final HttpMethod method = request.method(); - - // GET request should go to fileserver - if (method == HttpMethod.GET) { - FileHttpHandler.handleRequest(ctx, request); - return; - } - - // POST request should go to the webhook handler - if (method == HttpMethod.POST) { - WebhookHttpHandler.handleRequest(ctx, request); - return; - } - - // If we reach this point, then the request method isn't supported - HttpHandler.sendError(ctx, METHOD_NOT_ALLOWED); - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java deleted file mode 100644 index 52bfd94..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.networking; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import io.netty.channel.*; -import io.netty.handler.codec.http.*; -import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.meshlib.api.HttpHandler; - -import java.nio.charset.StandardCharsets; - -import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public final class WebhookHttpHandler { - private WebhookHttpHandler() {} - - private static final Gson GSON = new GsonBuilder().create(); - - public static void handleRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { - if (!"application/json".contentEquals(HttpUtil.getMimeType(request))) { - LOGGER.warn(String.format("Bad request: POST request made with incorrect mime type '%s', expected 'application/json'", HttpUtil.getMimeType(request))); - HttpHandler.sendError(ctx, BAD_REQUEST); - return; - } - - - // Get the event header - final String githubEvent = request.headers().get("x-github-event"); - - if (githubEvent == null || githubEvent.isBlank()) { - HttpHandler.sendError(ctx, BAD_REQUEST, "Request headers don't contain 'x-github-event'"); - return; - } - - if (!githubEvent.contains("push")) { - HttpHandler.sendError(ctx, BAD_REQUEST, "Request isn't for push event"); - return; - } - LOGGER.debug("Received github push event"); - - // Get payload - final JsonObject payload = GSON.fromJson(request.content().toString(StandardCharsets.UTF_8), JsonObject.class); - if (payload == null) { - HttpHandler.sendError(ctx, BAD_REQUEST, "Request doesn't contain content"); - return; - } - - // Respond with "yeh bro everythins alright" - ctx.writeAndFlush(new DefaultFullHttpResponse(HTTP_1_1, OK)).addListener(ChannelFutureListener.CLOSE); - - // Check which branch was pushed to - final String ref = payload.get("ref").getAsString(); - LOGGER.debug("Ref: %s", ref); - - if (!config.getGithubRef().equals(ref)) return; - - LOGGER.debug("Tracked branch has been updated, updating local pack..."); - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.WEBHOOK); - LOGGER.debug("Local pack has been updated."); - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformCommand.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformCommand.java deleted file mode 100644 index 425d1ff..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformCommand.java +++ /dev/null @@ -1,9 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform; - -import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; - -public interface PlatformCommand { - PlatformCommand INSTANCE = load(PlatformCommand.class); - - void registerGithubRpManagerCommand(); -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java deleted file mode 100644 index 9cf032d..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java +++ /dev/null @@ -1,33 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform; - -import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; - -public interface PlatformLogging { - PlatformLogging LOGGER = load(PlatformLogging.class); - - default void debug(String message, Object... args) { - debug(String.format(message, args)); - } - default void info(String message, Object... args) { - info(String.format(message, args)); - } - default void warn(String message, Object... args) { - warn(String.format(message, args)); - } - default void warn(String message, Throwable error, Object... args) { - warn(String.format(message, args), error); - } - default void error(String message, Object... args) { - error(String.format(message, args)); - } - default void error(String message, Throwable error, Object... args) { - error(String.format(message, args), error); - } - - void debug(String message); - void info(String message); - void warn(String message); - void warn(String message, Throwable error); - void error(String message); - void error(String message, Throwable error); -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java deleted file mode 100644 index a3f80d6..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java +++ /dev/null @@ -1,26 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform; - -import java.nio.file.Path; - -import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; - -public interface PlatformMain { - PlatformMain INSTANCE = load(PlatformMain.class); - - /** - * Must already contain the mod id. - *

- * Example: .minecraft/config/github-resourcepack-manager/ - * Example: .minecraft/plugins/Github-Resourcepack-Manager/ - * - * @return - */ - Path getConfigDir(); - - /** - * Must be called when initializing - * - * @param work the stuff to run - */ - void runOnServerStart(Runnable work); -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java deleted file mode 100644 index 3a7341c..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java +++ /dev/null @@ -1,13 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform; - -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; - -import java.util.Map; - -import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; - -public interface PlatformText { - PlatformText INSTANCE = load(PlatformText.class); - - void sendUpdateMessage(Map placeholders) throws GithubResourcepackManagerException; -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/MyFileUtils.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/MyFileUtils.java deleted file mode 100644 index 73fcaca..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/MyFileUtils.java +++ /dev/null @@ -1,28 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.utils; - -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; - -import java.io.File; -import java.io.IOException; - -public final class MyFileUtils { - private MyFileUtils() { - - } - - public static File createDir(File file) throws GithubResourcepackManagerException { - if (!file.exists() && !file.mkdirs()) throw new GithubResourcepackManagerException("Failed to create directory '%s'!", file); - return file; - } - - public static void createNewFile(File file) throws GithubResourcepackManagerException { - if (file.exists() && !file.delete()) throw new GithubResourcepackManagerException("Failed to delete file '%s'!", file); - - try { - //noinspection ResultOfMethodCallIgnored - file.createNewFile(); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to create file '%s'!", e, file); - } - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java deleted file mode 100644 index 67a731b..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java +++ /dev/null @@ -1,70 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.utils; - -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; - -import java.io.File; -import java.util.Map; -import java.util.regex.Matcher; - -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.PACK_NAME_PATTERN; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public final class StringUtils { - private StringUtils() { - - } - - /** - * Replaces all instances of the keys in the placeholders map with their values. - *

- * Same as calling {@link StringUtils#replacePlaceholders(String, Map, boolean)} with {@code escapeQuotes} false. - * - * @param string The string to replace placeholders in. - * @param placeholders The placeholders to replace. - * @return The original string with all instances of the keys in the placeholders map replaced with their values. - */ - public static String replacePlaceholders(String string, Map placeholders) { - return replacePlaceholders(string, placeholders, false); - } - - /** - * Replaces all instances of the keys in the placeholders map with their values. - *

- * Replaces {@code "} with {@code \"} in the placeholders if {@code escapeQuotes} is true. - * - * @param string The string to replace placeholders in. - * @param placeholders The placeholders to replace. - * @param escapeQuotes Whether quotes should be escaped. - * @return The original string with all instances of the keys in the placeholders map replaced with their values. - */ - public static String replacePlaceholders(String string, Map placeholders, boolean escapeQuotes) { - for (Map.Entry entry : placeholders.entrySet()) { - // I love strings. "\"" matches " and "\\\"" matches \" - string = string.replace(entry.getKey(), escapeQuotes ? entry.getValue().replace("\"", "\\\"") : entry.getValue()); - } - return string; - } - - public static int extractPriorityFromFile(File file) { - final String filename = file.getName(); - - final Matcher matcher = PACK_NAME_PATTERN.matcher(filename); - - if (!matcher.find()) { - LOGGER.error("File '%s' doesn't start with priority!", file); - return -1; - } - - return Integer.parseInt(matcher.group().replace('-', ' ').strip()); - } - - public static String nameWithoutPriorityString(File file) throws GithubResourcepackManagerException { - final String filename = file.getName(); - - final Matcher matcher = PACK_NAME_PATTERN.matcher(filename); - - if (!matcher.find()) throw new GithubResourcepackManagerException("File '%s' doesn't start with priority!", file); - - return filename.replace(matcher.group(), "").strip(); - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/WebhookSender.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/WebhookSender.java deleted file mode 100644 index 9f88c8c..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/WebhookSender.java +++ /dev/null @@ -1,51 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.utils; - -import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; - -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; - -public final class WebhookSender { - private WebhookSender() { - - } - - public static void send(String body, URI url, GithubResourcepackManager.UpdateType updateType, boolean isUpdated) throws GithubResourcepackManagerException { - final HttpRequest request = HttpRequest.newBuilder(url) - .header("Content-Type", "application/json") - .header("X-Resource-Pack-Update-Type", updateType.name()) - .header("X-Resource-Pack-Is-Updated", String.valueOf(isUpdated)) - .POST(HttpRequest.BodyPublishers.ofString(body)) - .build(); - - final HttpClient client = HttpClient.newHttpClient(); - - final HttpResponse response; - try { - response = client.send(request, HttpResponse.BodyHandlers.ofString()); - } catch (IOException | InterruptedException e) { - throw new GithubResourcepackManagerException("Failed to send http request!", e); - } - - final int statusCode = response.statusCode(); - if (!(statusCode >= 200 && statusCode < 300)) { - throw new GithubResourcepackManagerException("Http status code '%s'! Response was: '%s'.", statusCode, response.body()); - } - - // From JDK 21 the HttpClient class extends AutoCloseable, but as we want to support Minecraft versions - // that use JDK 17, where HttpClient doesn't extend AutoCloseable, we need to check if it's - // an instance of AutoCloseable before trying to close it. - //noinspection ConstantValue - if (client instanceof AutoCloseable) { - try { - ((AutoCloseable) client).close(); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - } -} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/GitPackManager.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/GitPackManager.java new file mode 100644 index 0000000..9442377 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/GitPackManager.java @@ -0,0 +1,381 @@ +package top.offsetmonkey538.gitpackmanager.common; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.jspecify.annotations.Nullable; +import top.offsetmonkey538.gitpackmanager.common.command.GitPackManagerCommand; +import top.offsetmonkey538.gitpackmanager.common.config.ConfigHandler; +import top.offsetmonkey538.gitpackmanager.common.config.ModConfig; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; +import top.offsetmonkey538.gitpackmanager.common.handler.DataPackHandler; +import top.offsetmonkey538.gitpackmanager.common.handler.GitHandler; +import top.offsetmonkey538.gitpackmanager.common.handler.ResourcePackHandler; +import top.offsetmonkey538.gitpackmanager.common.networking.MainHttpHandler; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformMain; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformText; +import top.offsetmonkey538.gitpackmanager.common.utils.StringUtils; +import top.offsetmonkey538.meshlib.common.api.router.HttpRouter; +import top.offsetmonkey538.meshlib.common.api.router.HttpRouterRegistry; +import top.offsetmonkey538.monkeylib538.common.api.command.ConfigCommandApi; +import top.offsetmonkey538.monkeylib538.common.api.lifecycle.ServerLifecycleApi; +import top.offsetmonkey538.monkeylib538.common.api.platform.LoaderUtil; +import top.offsetmonkey538.monkeylib538.common.api.telemetry.TelemetryRegistry; +import top.offsetmonkey538.offsetutils538.api.config.ConfigHolder; +import top.offsetmonkey538.offsetutils538.api.config.ConfigManager; +import top.offsetmonkey538.offsetutils538.api.log.OffsetLogger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.regex.Pattern; + +import static top.offsetmonkey538.offsetutils538.api.text.ArgReplacer.replaceArgs; + +public final class GitPackManager { + private GitPackManager() { + + } + + public static final String MOD_ID = "git-pack-manager"; + public static final OffsetLogger LOGGER = OffsetLogger.create(MOD_ID); + + public static final Path DATA_FOLDER = LoaderUtil.getConfigDir().resolve(MOD_ID).resolve(".packs"); + public static final Path GIT_FOLDER = DATA_FOLDER.resolve("git"); + + public static final Path RESOURCEPACK_FOLDER = DATA_FOLDER.resolve("resource-pack"); + public static final Path DATAPACK_FOLDER = DATA_FOLDER.resolve("data-pack"); + + public static final Path RESOURCEPACK_OUTPUT_FOLDER = RESOURCEPACK_FOLDER.resolve("output"); + + public static final Pattern RESOURCEPACK_NAME_PATTERN = Pattern.compile("\\d+-"); + public static final UUID RESOURCEPACK_UUID = UUID.fromString("60ab8dc7-08d1-4f5f-a9a8-9a01d048b7b9"); + + private static final Executor EXECUTOR = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(MOD_ID + "-%d").build()); + + private static final List MESSAGE_QUEUE = new ArrayList<>(); + + private static final Component MESSAGE_QUEUE_EMPTY_MESSAGE = MiniMessage.miniMessage().deserialize(replaceArgs("Admin message queue can be emptied using the [/%s reset-admin-message-queue] command.", MOD_ID, MOD_ID)); + + public static ConfigHolder config = ConfigHolder.create(ModConfig::new, LOGGER); + + public static @Nullable ResourcePackHandler resourcePackHandler = null; + + private static boolean disabled; + + public static void initialize() { + TelemetryRegistry.register(MOD_ID); + + GitPackManagerCommand.register(); + ConfigCommandApi.registerConfigCommand( + config, + () -> disabled = ConfigHandler.handleConfig(), + MOD_ID, "config" + ); + + // Have to run all this stuff (definitely initializing config at least) after other mods have initialized. Namely, after MESH-Lib has its rule serialization stuff to the jankson event + ServerLifecycleApi.STARTED.listen(() -> { + addLogToAdminListeners(); + LoaderUtil.sendMessagesToAdminsOnJoin(() -> { + if (MESSAGE_QUEUE.isEmpty()) return new Component[]{}; + + final Component[] result = new Component[MESSAGE_QUEUE.size() + 1]; + MESSAGE_QUEUE.toArray(result); + result[result.length - 1] = MESSAGE_QUEUE_EMPTY_MESSAGE; + return result; + }); + + // config should be initialized after the error listeners + ConfigManager.init(config); + + disabled = ConfigHandler.handleConfig(); + + try { + createFolderStructure(); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to create folder structure!", e); + } + + updatePack(UpdateType.RESTART, true); + }); + + HttpRouterRegistry.HTTP_ROUTER_REGISTRATION_EVENT.listen(registry -> registry.register( + MOD_ID, new HttpRouter( + config.get().serverInfo.routingRule, + new MainHttpHandler() + ) + )); + } + + private static void addLogToAdminListeners() { + LOGGER.addListener(OffsetLogger.LogLevel.ERROR, createLogToAdminListener(NamedTextColor.RED)); + LOGGER.addListener(OffsetLogger.LogLevel.WARN, createLogToAdminListener(NamedTextColor.YELLOW)); + } + + private static OffsetLogger.LogListener createLogToAdminListener(final TextColor textColor) { + return (message, error) -> { + final Component text = Component + .text(replaceArgs("[%s] %s", MOD_ID, message)) + .style(style -> style.color(textColor)) + .style(error == null ? style -> {} : style -> style.hoverEvent(HoverEvent.showText(Component.text(ExceptionUtils.getRootCauseMessage(error))))); + + PlatformMain.INSTANCE.sendMessageToAdmins(text); + MESSAGE_QUEUE.addLast(text); + }; + } + + private static void createFolderStructure() throws GitPackManagerException { + try { + Files.createDirectories(RESOURCEPACK_OUTPUT_FOLDER); + Files.createDirectories(DATAPACK_FOLDER); + Files.createDirectories(GIT_FOLDER); + } catch (IOException e) { + throw new GitPackManagerException("Failed to create directory!", e); + } + } + + public static CompletableFuture updatePack(final UpdateType updateType, final boolean onSameThread) { + if (!onSameThread) return CompletableFuture.runAsync(() -> updatePack(updateType), EXECUTOR); + + updatePack(updateType); + return CompletableFuture.completedFuture(null); + } + + private static void updatePack(final UpdateType updateType) { + if (updateType != UpdateType.RESTART) { + LOGGER.debug("Clearing admin message queue before updating after a restart..."); + MESSAGE_QUEUE.clear(); + } + + if (disabled) { + LOGGER.warn("Skipping pack updating because mod is disabled!"); + return; + } + + LOGGER.info("Updating packs..."); + + if (updateType == UpdateType.COMMAND_FORCE) { + LOGGER.warn("Forced pack update! Deleting data directory and continuing..."); + try { + FileUtils.deleteDirectory(DATA_FOLDER.toFile()); + } catch (IOException e) { + LOGGER.error("Failed to delete directory!", e); + return; + } + } + + try { + createFolderStructure(); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to create folder structure!", e); + } + + // Git stuff + final GitHandler gitHandler = new GitHandler(); + + LOGGER.info("Updating git repository..."); + boolean failed = false; + try { + gitHandler.updateRepositoryAndGenerateCommitProperties(); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to update git repository!", e); + failed = true; + } + if (!failed) LOGGER.info("Successfully updated git repository!"); + + if (config.get().resourcePackProvider.enabled) { + LOGGER.info(""); + LOGGER.info("Updating resource pack..."); + updateResourcePack(gitHandler, updateType, failed); + LOGGER.info("Resource pack updated!"); + } + if (config.get().dataPackProvider.enabled) { + LOGGER.info(""); + LOGGER.info("Updating data pack..."); + updateDataPack(gitHandler, updateType, failed); + LOGGER.info("Data pack updated!"); + } + } + + private static void updateResourcePack(final GitHandler gitHandler, final UpdateType updateType, boolean updateFailed) { + // Get the location of the old pack, if it exists. + final String oldResourcePackName = getOldResourcePackName(); + final Path oldResourcePackPath = oldResourcePackName == null ? null : RESOURCEPACK_OUTPUT_FOLDER.resolve(oldResourcePackName); + + // Check if pack was updated + final boolean wasUpdated = + gitHandler.getChangedFiles().map(changes -> changes.stream().anyMatch(it -> it.startsWith(config.get().resourcePackProvider.getRootLocation()))).orElse(true) + || oldResourcePackPath == null + || !oldResourcePackPath.toFile().exists(); + if (!wasUpdated) LOGGER.info("Pack hasn't changed since last update. Skipping new pack generation."); + + // Generate pack + resourcePackHandler = new ResourcePackHandler(); + + LOGGER.info("Getting pack location..."); + boolean failed = false; + try { + resourcePackHandler.generatePack(wasUpdated, oldResourcePackPath, oldResourcePackName); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to generate pack!", e); + failed = updateFailed = true; + } + if (!failed) LOGGER.info("Pack location is '%s'!", resourcePackHandler.getOutputPackPath().toAbsolutePath()); + + + // Update server.properties file. + try { + PlatformServerProperties.INSTANCE.updatePackProperties(resourcePackHandler); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to update server.properties file!", e); + } + + // Generate placeholder map + final Map placeholders = generatePlaceholders(gitHandler, resourcePackHandler, updateType, "resource", wasUpdated); + + // Send chat message + try { + if (!failed) sendUpdateMessage(config.get().resourcePackProvider.updateMessage, wasUpdated, placeholders); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to send update message for resource pack in chat!", e); + } + + // Trigger webhooks + if (!wasUpdated) { + LOGGER.info("Not sending webhook because pack was not updated."); + return; + } + if (!updateFailed) try { + config.get().resourcePackProvider.successWebhook.trigger(true, placeholders, updateType); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to trigger success webhook!", e); + } + else try { + config.get().resourcePackProvider.failWebhook.trigger(false, placeholders, updateType); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to trigger fail webhook!", e); + } + } + + private static void updateDataPack(final GitHandler gitHandler, final UpdateType updateType, boolean updateFailed) { + // Check if pack was updated + final boolean wasUpdated = gitHandler.getChangedFiles().map(changes -> changes.stream().anyMatch(it -> it.startsWith(config.get().dataPackProvider.getRootLocation()))).orElse(true); + if (!wasUpdated) { + LOGGER.info("Pack hasn't changed since last update. Datapack processing will be skipped."); + return; + } + + // Generate pack + final DataPackHandler dataPackHandler = new DataPackHandler(); + + LOGGER.info("Getting pack location..."); + try { + dataPackHandler.generatePack(); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to generate pack!", e); + updateFailed = true; + } + + // Refresh datapack list + PlatformMain.INSTANCE.refreshDatapacks(); + if (config.get().dataPackProvider.autoReload) PlatformMain.INSTANCE.reloadEnabledDatapacks(); + + + // Generate placeholder map + final Map placeholders = generatePlaceholders(gitHandler, null, updateType, "data", true); + + // Send chat message + try { + sendUpdateMessage(config.get().dataPackProvider.updateMessage, true, placeholders, true); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to send update message for datapack in chat!", e); + } + + // Trigger webhooks + if (!updateFailed) try { + config.get().dataPackProvider.successWebhook.trigger(true, placeholders, updateType); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to trigger success webhook!", e); + } + else try { + config.get().dataPackProvider.failWebhook.trigger(false, placeholders, updateType); + } catch (GitPackManagerException e) { + LOGGER.error("Failed to trigger fail webhook!", e); + } + } + + private static void sendUpdateMessage(final String[] updateMessage, boolean wasUpdated, final Map placeholders) throws GitPackManagerException { + sendUpdateMessage(updateMessage, wasUpdated, placeholders, false); + } + + private static void sendUpdateMessage(final String[] updateMessage, boolean wasUpdated, final Map placeholders, boolean adminsOnly) throws GitPackManagerException { + if (!wasUpdated) { + LOGGER.info("Not sending chat message because pack was not updated."); + return; + } + + for (final Component line : createUpdateMessage(updateMessage, placeholders)) + PlatformText.INSTANCE.sendUpdateMessage(line, adminsOnly); + } + + public static Component[] createUpdateMessage(final String[] updateMessage, final Map placeholders) throws GitPackManagerException { + final Component[] result = new Component[updateMessage.length]; + + for (int lineIndex = 0; lineIndex < updateMessage.length; lineIndex++) { + final String line = StringUtils.replacePlaceholders(updateMessage[lineIndex], placeholders, true, false).replace("\\n", "\n"); + + result[lineIndex] = MiniMessage.miniMessage().deserialize(line); + } + + return result; + } + + public static Map generatePlaceholders(final GitHandler gitHandler, final @Nullable ResourcePackHandler resourcePackHandler, final UpdateType updateType, final String packType, final boolean wasUpdated) { + final Map placeholders = new HashMap<>(); + + if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); + if (resourcePackHandler != null) placeholders.put("{downloadUrl}", config.get().getPackUrl(Objects.requireNonNull(resourcePackHandler.getOutputPackName()))); + placeholders.put("{packType}", packType); + placeholders.put("{updateType}", updateType.name()); + placeholders.put("{wasUpdated}", String.valueOf(wasUpdated)); + LOGGER.info("Placeholders: %s", placeholders); + + return placeholders; + } + + public static void clearAdminMessageQueue() { + MESSAGE_QUEUE.clear(); + } + + private static @Nullable String getOldResourcePackName() { + final String oldPackUrl = PlatformServerProperties.INSTANCE.getResourcePackUrl(); + if (oldPackUrl == null) return null; + + int nameStartIndex = oldPackUrl.lastIndexOf('/'); + if (nameStartIndex == -1) return null; + + return oldPackUrl.substring(nameStartIndex + 1); + } + + public enum UpdateType { + RESTART, + WEBHOOK, + COMMAND, + COMMAND_FORCE + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/command/GitPackManagerCommand.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/command/GitPackManagerCommand.java new file mode 100644 index 0000000..1608e4d --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/command/GitPackManagerCommand.java @@ -0,0 +1,126 @@ +package top.offsetmonkey538.gitpackmanager.common.command; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import org.apache.commons.lang3.exception.ExceptionUtils; +import top.offsetmonkey538.gitpackmanager.common.GitPackManager; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; +import top.offsetmonkey538.gitpackmanager.common.handler.GitHandler; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformCommand; +import top.offsetmonkey538.monkeylib538.common.api.command.CommandAbstractionApi; +import top.offsetmonkey538.monkeylib538.common.api.command.CommandRegistrationApi; +import top.offsetmonkey538.offsetutils538.api.log.OffsetLogger; + +import java.util.Map; + +import static com.mojang.brigadier.arguments.BoolArgumentType.bool; +import static com.mojang.brigadier.arguments.BoolArgumentType.getBool; +import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.MOD_ID; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.UpdateType; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.clearAdminMessageQueue; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.config; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.createUpdateMessage; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.generatePlaceholders; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.resourcePackHandler; +import static top.offsetmonkey538.monkeylib538.common.api.command.CommandAbstractionApi.literal; +import static top.offsetmonkey538.monkeylib538.common.api.command.CommandAbstractionApi.sendText; +import static top.offsetmonkey538.offsetutils538.api.text.ArgReplacer.replaceArgs; + +public final class GitPackManagerCommand { + + public static void register() { + CommandRegistrationApi.registerCommand(createCommand()); + } + + private static LiteralArgumentBuilder createCommand() { + return literal(MOD_ID) + .then(literal("request-pack") + .requires(CommandAbstractionApi::executedByPlayer) + .executes(PlatformCommand.INSTANCE::executeRequestPackCommand) + ) + + .then(literal("trigger-update") + .requires(CommandAbstractionApi::isOp) + .executes( + context -> { + runTriggerUpdate(context, false); + return 1; + } + ) + .then(argument("force", bool()) + .executes( + context -> { + runTriggerUpdate(context, getBool(context, "force")); + return 1; + } + ) + ) + ) + + .then(literal("test-update-message") + .requires(CommandAbstractionApi::isOp) + .then(literal("resource").executes(context -> runTestUpdateMessage(context, true))) + .then(literal("data").executes(context -> runTestUpdateMessage(context, false))) + ) + + .then(literal("reset-admin-message-queue") + .requires(CommandAbstractionApi::isOp) + .executes(context -> { + clearAdminMessageQueue(); + CommandAbstractionApi.sendMessage(context, "Admin message queue cleared!"); + return 1; + }) + ); + } + + private static void runTriggerUpdate(CommandContext context, boolean force) { + final OffsetLogger.LogListener infoListener = (message, error) -> { + sendText(context, Component.text(replaceArgs("[%s] %s", MOD_ID, message)).style(style -> style.color(NamedTextColor.GRAY))); + }; + final OffsetLogger.LogListener warnListener = (message, error) -> { + final Component text = Component + .text(replaceArgs("[%s] %s", MOD_ID, message)) + .style(style -> style.color(NamedTextColor.YELLOW)) + .style(error == null ? style -> {} : style -> style.hoverEvent(HoverEvent.showText(Component.text(ExceptionUtils.getRootCauseMessage(error))))); + + sendText(context, text); + }; + GitPackManager.LOGGER.addListener(OffsetLogger.LogLevel.INFO, infoListener); + GitPackManager.LOGGER.addListener(OffsetLogger.LogLevel.WARN, warnListener); + + GitPackManager.updatePack(force ? GitPackManager.UpdateType.COMMAND_FORCE : GitPackManager.UpdateType.COMMAND, false).thenRun(() -> { + GitPackManager.LOGGER.removeListener(OffsetLogger.LogLevel.INFO, infoListener); + GitPackManager.LOGGER.removeListener(OffsetLogger.LogLevel.WARN, warnListener); + }); + } + + private static int runTestUpdateMessage(CommandContext context, boolean isResource) { + final GitHandler gitHandler = new GitHandler(); + try { + gitHandler.updateRepositoryAndGenerateCommitProperties(); + } catch (GitPackManagerException e) { + CommandAbstractionApi.sendError(context, "Failed to update repository, git related placeholders will not be replaced!"); + CommandAbstractionApi.sendError(context, "Cause:\n%s\n", e); + LOGGER.error("Failed to update repository, git related placeholders will not be replaced!", e); + } + final Map placeholders = generatePlaceholders(gitHandler, isResource ? resourcePackHandler : null, UpdateType.COMMAND, isResource ? "resource" : "data", true); + + final Component[] text; + try { + text = createUpdateMessage(isResource ? config.get().resourcePackProvider.updateMessage : config.get().dataPackProvider.updateMessage, placeholders); + } catch (GitPackManagerException e) { + CommandAbstractionApi.sendError(context, "Failed to create update message!"); + CommandAbstractionApi.sendError(context, "Cause:\n%s\n", e); + LOGGER.error("Failed to create update message for %spack!", e, isResource ? "resource" : "data"); + return 0; + } + + for (final Component line : text) CommandAbstractionApi.sendText(context, line); + return 1; + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/command/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/command/package-info.java new file mode 100644 index 0000000..8f9d289 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/command/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.command; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/ConfigHandler.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/ConfigHandler.java new file mode 100644 index 0000000..acc40c5 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/ConfigHandler.java @@ -0,0 +1,107 @@ +package top.offsetmonkey538.gitpackmanager.common.config; + +import blue.endless.jankson.Jankson; +import blue.endless.jankson.JsonGrammar; +import top.offsetmonkey538.gitpackmanager.common.config.webhook.BasicWebhook; +import top.offsetmonkey538.gitpackmanager.common.config.webhook.DefaultWebhookBody; +import top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.basic.BasicFailMessage; +import top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.basic.BasicSuccessMessage; +import top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.embed.EmbedFailMessage; +import top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.embed.EmbedSuccessMessage; +import top.offsetmonkey538.meshlib.common.api.MESHLibApi; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.config; +import static top.offsetmonkey538.offsetutils538.api.text.ArgReplacer.replaceArgs; + +public final class ConfigHandler { + private ConfigHandler() { + + } + + public static boolean handleConfig() { + MESHLibApi.reload(); + + LOGGER.info("Writing default webhook bodies"); + createDefaultWebhooks(); + + // Checking if config is valid + final List errors = checkConfigErrors(); + + boolean success = true; + if (!errors.isEmpty()) { + // There were errors, time to log em. + LOGGER.error("There were problems with the config for Git Pack Manager, see below for more details!"); + errors.stream().map(string -> " " + string).forEach(LOGGER::error); + success = false; + } + if (MESHLibApi.getExternalPort() == null) { + LOGGER.error("MESH Lib hasn't been configured correctly! Disabling Git Pack Manager!"); + success = false; + } + + return !success; + } + + private static void createDefaultWebhooks() { + final Jankson jankson = new Jankson.Builder().build(); + final List webhookBodies = List.of( + new BasicWebhook(), + + new BasicSuccessMessage(), + new BasicFailMessage(), + new EmbedSuccessMessage(), + new EmbedFailMessage() + ); + + for (DefaultWebhookBody webhook : webhookBodies) { + final Path location = config.get().getFilePath().getParent().resolve(webhook.getName()); + + if (Files.exists(location)) continue; + + try { + Files.createDirectories(location.getParent()); + Files.writeString(location, jankson.toJson(webhook).toJson(JsonGrammar.STRICT)); + } catch (IOException e) { + LOGGER.error("Failed to write default webhook body '%s'!", webhook.getName(), e); + } + } + } + + private static List checkConfigErrors() { + final List errors = new ArrayList<>(); + + if (config.get().serverInfo.publicIp == null) errors.add("Field 'serverInfo.publicIp' not set!"); + if (config.get().repositoryInfo.url == null) errors.add("Field 'repositoryInfo.url' not set!"); + if (config.get().repositoryInfo.isPrivate) { + if (config.get().repositoryInfo.username == null) errors.add("Field 'repositoryInfo.username' not set, but 'repositoryInfo.isPrivate' is true!"); + if (config.get().repositoryInfo.token == null) errors.add("Field 'repositoryInfo.token' not set, but 'repositoryInfo.isPrivate' is true!"); + } else { + if (config.get().repositoryInfo.username != null) errors.add("Field 'repositoryInfo.username' set, but 'repositoryInfo.isPrivate' is false! Can be unset"); + if (config.get().repositoryInfo.token != null) errors.add("Field 'repositoryInfo.token' set, but 'repositoryInfo.isPrivate' is false! Can be unset"); + } + + checkWebhookErrors(errors, config.get().resourcePackProvider.successWebhook, "resourcePackProvider.successWebhook"); + checkWebhookErrors(errors, config.get().resourcePackProvider.failWebhook, "resourcePackProvider.failWebhook"); + checkWebhookErrors(errors, config.get().dataPackProvider.successWebhook, "dataPackProvider.successWebhook"); + checkWebhookErrors(errors, config.get().dataPackProvider.failWebhook, "dataPackProvider.failWebhook"); + + return errors; + } + + private static void checkWebhookErrors(final List errors, final ModConfig.WebhookInfo webhook, final String webhookPath) { + if (!webhook.enabled) return; + + if (webhook.url == null) errors.add(replaceArgs("Field '%s.url' not set, but '%s.enabled' is true!", webhookPath, webhookPath)); + if (webhook.body == null) errors.add(replaceArgs("Field '%s.body' not set, but '%s.enabled' is true!", webhookPath, webhookPath)); + + final Path bodyPath = webhook.getBodyPath(); + if (bodyPath != null && !Files.exists(bodyPath)) errors.add(replaceArgs("Field '%s.body' points to file '%s', which doesn't exist!", webhookPath, bodyPath.toAbsolutePath())); + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/ModConfig.java new file mode 100644 index 0000000..1b98d71 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/ModConfig.java @@ -0,0 +1,366 @@ +package top.offsetmonkey538.gitpackmanager.common.config; + +import blue.endless.jankson.Comment; +import blue.endless.jankson.JsonArray; +import blue.endless.jankson.JsonElement; +import blue.endless.jankson.JsonNull; +import blue.endless.jankson.JsonObject; +import blue.endless.jankson.api.Marshaller; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.jspecify.annotations.Nullable; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; +import top.offsetmonkey538.gitpackmanager.common.utils.StringUtils; +import top.offsetmonkey538.gitpackmanager.common.utils.WebhookSender; +import top.offsetmonkey538.meshlib.common.api.MESHLibApi; +import top.offsetmonkey538.meshlib.common.api.rule.HttpRule; +import top.offsetmonkey538.meshlib.common.api.rule.rules.PathHttpRule; +import top.offsetmonkey538.monkeylib538.common.api.platform.LoaderUtil; +import top.offsetmonkey538.monkeylib538.common.api.text.TextFormattingApi; +import top.offsetmonkey538.offsetutils538.api.config.Config; +import top.offsetmonkey538.offsetutils538.api.config.Datafixer; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.GIT_FOLDER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.MOD_ID; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.UpdateType; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.config; +import static top.offsetmonkey538.offsetutils538.api.text.ArgReplacer.replaceArgs; + +public final class ModConfig implements Config { + + @Comment("!!!! Please check the wiki for how to set up the mod. It should be available here: https://git-pack-manager.docs.offsetmonkey538.top and is also linked on the Modrinth page. !!!!") + public ServerInfo serverInfo = new ServerInfo(); + public RepositoryInfo repositoryInfo = new RepositoryInfo(); + public ResourcePackProvider resourcePackProvider = new ResourcePackProvider(); + public DataPackProvider dataPackProvider = new DataPackProvider(); + + + public static class ServerInfo { + @Comment("The MESH Lib rule to use for routing to the Git Pack Manager http handler. See MESH Lib docs: https://mesh-lib.docs.offsetmonkey538.top/reference/rules/") + public HttpRule routingRule = new PathHttpRule(MOD_ID); + @Comment("The public ip of your server. (\"123.45.67.89\" or \"play.coolserver.net\")") + public @Nullable String publicIp = null; + @SuppressWarnings("HttpUrlsUsage") + @Comment("The url matched by the routing rule defined above. May include placeholders for \"{exposedPort}\" (defined in MESH Lib config), \"{publicIp}\" (defined above) and \"{filename}\" (the file to download). Used for generating the download url for clients. Default value: \"http://{publicIp}:{exposedPort}/" + MOD_ID + "/{filename}\"") + public String downloadUrlPattern = "http://{publicIp}:{exposedPort}/" + MOD_ID + "/{filename}"; + } + + public static class RepositoryInfo { + @Comment("Should be \"[YOUR BRANCH NAME HERE]\". Common names include \"master\" and \"main\"") + public String branch = "master"; + @Comment("The URL of your repository. For example \"https://github.com/MyName/MyRepository\"") + public @Nullable String url = null; + + @Comment("Whether or not the repository is private. Username and token will need to be populated when this is set to 'true'!") + public boolean isPrivate = false; + + @Comment("The two values below only need to be set when 'isPrivate' is true!") + public @Nullable String username = null; + @Comment("PLEASE DO NOT SHARE THIS WITH ANYONE") + public @Nullable String token = null; + } + + public static class ResourcePackProvider { + @Comment("Whether or not the resource pack provider is enabled. Default: true") + public boolean enabled = true; + @Comment("Where the mod will search for resource packs in the cloned repository. MUST NOT be same as or child of the 'rootLocation' of the datapack provider") + public String rootLocation = "/resourcepacks"; + + @Comment("Messages sent in chat when pack has been updated. Each entry will be on a new line. May be 'null' or empty to disable.") + public String[] updateMessage = new String[] { + "Server resource pack has been updated!", + "Please click [HERE] to get the most up to date pack." + }; + + @Comment("Webhook to be sent when pack updating succeeded") + public WebhookInfo successWebhook = new WebhookInfo(); + @Comment("Webhook to be sent when pack updating failed") + public WebhookInfo failWebhook = new WebhookInfo(); + + public String getRootLocation() { + return rootLocation.startsWith("/") ? rootLocation.substring(1) : rootLocation; + } + public Path getPackRoot() { + return GIT_FOLDER.resolve(getRootLocation()); + } + public Path getPackPacksDir() { + return getPackRoot().resolve("packs"); + } + } + + public static class DataPackProvider { + @Comment("Whether or not the data pack provider is enabled. Default: false") + public boolean enabled = false; + @Comment("Automatically reloads enabled datapacks after an update. May be desirable to disable with mods that break reload functionality. Default: true") + public boolean autoReload = true; + @Comment("Where the mod will search for data packs in the cloned repository. MUST NOT be same as or child of the 'rootLocation' of the resourcepack provider") + public String rootLocation = "/datapacks"; + @Comment("Messages sent TO ADMINS in chat when pack has been updated. Each entry will be on a new line. May be 'null' or empty to disable.") + public String[] updateMessage = new String[] { + "Server datapacks have been updated!", + "New packs (if any) will need to be enabled with the /datapack enable command.", + "If 'autoReload' isn't enabled in the config and datapacks weren't automatically reloaded, please run /reload or restart the server to reload datapacks." + }; + + @Comment("Webhook to be sent when pack updating succeeded") + public WebhookInfo successWebhook = new WebhookInfo(); + @Comment("Webhook to be sent when pack updating failed") + public WebhookInfo failWebhook = new WebhookInfo(); + + public String getRootLocation() { + return rootLocation.startsWith("/") ? rootLocation.substring(1) : rootLocation; + } + public Path getPackRoot() { + return GIT_FOLDER.resolve(getRootLocation()); + } + public Path getPackPacksDir() { + return getPackRoot().resolve("packs"); + } + } + + public static class WebhookInfo { + public WebhookInfo() { + + } + + @Comment("Whether or not this webhook is enabled") + public boolean enabled = false; + + @Comment("The URL to send the webhook to. For example \"https://discord.com/api/webhooks/1234567890123456789/eW91J3JlIG5vdCBzdGVhbGluZyBhIHRva2Vu_bm9wZQ==_eWVyJyBub3Q=\" or something custom like \"https://api.example.com/NDI6IHRoZSBtZWFuaW5nIG9mIGxpZmUsIHRoZSB1bml2ZXJzZSwgYW5kIGV2ZXJ5dGhpbmc=\"") + public @Nullable String url = null; + @Comment("The relative path from the config directory to a webhook body file. For example \"discord/basic/success.json\" or \"discord/embed/success.json\"") + public @Nullable String body = null; + + public void trigger(final boolean updateSucceeded, final Map placeholders, final UpdateType updateType) throws GitPackManagerException { + if (!enabled) return; + + try { + //noinspection DataFlowIssue: Only returns null when `body` is null, which we have already checked + String webhookBody = Files.readString(getBodyPath()); + webhookBody = StringUtils.replacePlaceholders(webhookBody, placeholders, false, true); + + WebhookSender.send(webhookBody, getWebhookUrl(), updateType, updateSucceeded); + } catch (IOException e) { + throw new GitPackManagerException("Failed to read content of webhook body file '%s'!", e, config.get().resourcePackProvider.successWebhook.body); + } + } + + public @Nullable URI getWebhookUrl() { + if (url == null) return null; + return URI.create(url); + } + + public @Nullable Path getBodyPath() { + return body == null ? null : config.get().getFilePath().getParent().resolve(body); + } + } + + @Override + public Datafixer[] getDatafixers() { + return new Datafixer[]{ + (original, jankson) -> { + // 0 -> 1 + original.put("branch", jankson.toJson(jankson.getMarshaller().marshall(String.class, original.get("githubRef")).replace("refs/heads/", ""))); + datafixField(original, "githubUrl", "repoUrl"); + datafixField(original, "isPrivate", "isRepoPrivate"); + }, + (original, jankson) -> { + // 1 -> 2 + // noop + }, + (original, jankson) -> { + // 2 -> 3 + + final Marshaller marsh = jankson.getMarshaller(); + + // Server Info + final JsonObject serverInfo = new JsonObject(); + + datafixField(original, "serverPublicIp", serverInfo, "publicIp"); + datafixField(original, "proxyPort", serverInfo, "proxyPort"); + + original.put("serverInfo", serverInfo); + + // Repository Info + final JsonObject repositoryInfo = new JsonObject(); + + datafixField(original, "branch", repositoryInfo, "branch"); + datafixField(original, "repoUrl", repositoryInfo, "url"); + + datafixField(original, "isRepoPrivate", repositoryInfo, "isPrivate"); + + datafixField(original, "githubUsername", repositoryInfo, "username"); + datafixField(original, "githubToken", repositoryInfo, "token"); + + original.put("repositoryInfo", repositoryInfo); + + // Resource Pack Provider + final JsonObject resourcePackProvider = new JsonObject(); + + datafixField(original, "resourcePackRoot", serverInfo, "rootLocation"); + + datafixField(original, "packUpdateMessage", serverInfo, "updateMessage"); + datafixField(original, "packUpdateMessageHoverMessage", serverInfo, "updateMessageHoverMessage"); + + final String webhookUrl = datafixGetAndRemove(marsh, String.class, original, "webhookUrl"); + final String webhookBody = datafixGetAndRemove(marsh, String.class, original, "webhookBody"); + if (webhookUrl != null && webhookBody != null) { + final JsonObject webhookInfo = new JsonObject(); + webhookInfo.put("enabled", jankson.toJson(true)); + webhookInfo.put("url", jankson.toJson(webhookUrl)); + webhookInfo.put("body", jankson.toJson(webhookBody)); + + resourcePackProvider.put("successWebhook", webhookInfo); + if (!webhookBody.contains("discord")) resourcePackProvider.put("failWebhook", webhookInfo); + } + + original.put("resourcePackProvider", resourcePackProvider); + }, + (original, jankson) -> { + // 3 -> 4 + final Marshaller marsh = jankson.getMarshaller(); + + original.put("resourcePackProvider", datafix3to4UpdateMessage((JsonObject) original.get("resourcePackProvider"), marsh)); + original.put("dataPackProvider", datafix3to4UpdateMessage((JsonObject) original.get("dataPackProvider"), marsh)); + }, + (original, jankson) -> { + // 4 -> 5 + final JsonObject serverInfo = original.getObject("serverInfo"); + serverInfo.remove("proxyPort"); + original.put("serverInfo", serverInfo); + + final Marshaller marsh = jankson.getMarshaller(); + + original.put("resourcePackProvider", datafix4to5UpdateMessage((JsonObject) original.get("resourcePackProvider"), marsh)); + original.put("dataPackProvider", datafix4to5UpdateMessage((JsonObject) original.get("dataPackProvider"), marsh)); + } + }; + } + + private static void datafixField(final JsonObject originalObject, final String originalKey, final String newKey) { + datafixField(originalObject, originalKey, originalObject, newKey); + } + private static void datafixField(final JsonObject originalObject, final String originalKey, final JsonObject newObject, final String newKey) { + final JsonElement originalValue = originalObject.get(originalKey); + if (originalValue == null) { + LOGGER.warn("JSON of current config doesn't contain value with key '%s', new value with key '%s' will be reset to default!", originalKey, newKey); + return; + } + + newObject.put(newKey, originalValue); + originalObject.remove(originalKey); + } + + @Nullable + private static T datafixGetAndRemove(final Marshaller marsh, final Class type, final JsonObject object, final String key) { + final JsonElement jsonValue = object.get(key); + if (jsonValue == null) { + LOGGER.warn("JSON of current config doesn't contain value with key '%s'!", key); + return null; + } + object.remove(key); + return marsh.marshall(type, jsonValue); + } + + private static JsonObject datafix3to4UpdateMessage(final JsonObject originalJson, final Marshaller marsh) { + final String updateMessage = datafixGetAndRemove(marsh, String.class, originalJson, "updateMessage"); + if (updateMessage == null) { + originalJson.put("updateMessage", JsonNull.INSTANCE); + return originalJson; + } + + final String updateMessageHoverMessage = datafixGetAndRemove(marsh, String.class, originalJson, "updateMessageHoverMessage"); + + final String[] newUpdateMessage = updateMessage.split("\\n"); + for (int i = 0; i < newUpdateMessage.length; i++) { + newUpdateMessage[i] = newUpdateMessage[i].replaceAll("\\{packUpdateCommand}", "&{hoverText,'Click to update pack','&{runCommand,'{packUpdateCommand}','[HERE]'}'}"); + if (updateMessageHoverMessage != null) + newUpdateMessage[i] = replaceArgs("&{hoverText,'%s','%s'}", updateMessageHoverMessage, newUpdateMessage[i].replace("'", "\\'")); + } + originalJson.put("updateMessage", new JsonArray(newUpdateMessage, marsh)); + + return originalJson; + } + + private static JsonObject datafix4to5UpdateMessage(final JsonObject originalJson, final Marshaller marsh) { + final JsonArray updateMessageJson = ((JsonArray) originalJson.get("updateMessage")); + if (updateMessageJson == null) { + originalJson.put("updateMessage", JsonNull.INSTANCE); + return originalJson; + } + + final String[] updateMessage = new String[updateMessageJson.size()]; + + for (int i = 0; i < updateMessage.length; i++) { + try { + //noinspection deprecation + updateMessage[i] = MiniMessage.miniMessage().serialize(TextFormattingApi.styleText(updateMessageJson.getString(i, "").replace("{packUpdateCommand}", "/git-pack-manager request-pack"))); + } catch (Exception e) { + LOGGER.error("Failed to migrate update message at line %s!", e, i); + } + } + + originalJson.put("updateMessage", new JsonArray(updateMessage, marsh)); + + return originalJson; + } + + @Override + public int getConfigVersion() { + return 5; + } + + @Override + public Path getConfigDirPath() { + return LoaderUtil.getConfigDir(); + } + + @Override + public String getId() { + return MOD_ID + "/main"; + } + + @Override + public void beforeLoadStart() { + tryMoveConfig( + LoaderUtil.getConfigDir().resolve("github-resourcepack-manager.json"), + LoaderUtil.getConfigDir().resolve("github-resourcepack-manager").resolve("github-resourcepack-manager.json") + ); + tryMoveConfig( + LoaderUtil.getConfigDir().resolve("github-resourcepack-manager").resolve("github-resourcepack-manager.json"), + LoaderUtil.getConfigDir().resolve("github-resourcepack-manager/main.json") + ); + tryMoveConfig( + LoaderUtil.getConfigDir().resolve("github-resourcepack-manager/main.json"), + getFilePath() + ); + } + + private static void tryMoveConfig(final Path oldPath, final Path newPath) { + if (Files.exists(oldPath)) { + try { + Files.createDirectories(newPath.getParent()); + Files.move(oldPath, newPath); + } catch (IOException e) { + throw new RuntimeException("Failed to move config file to new location!", e); + } + } + } + + public String getPackUrl(String outputFileName) { + return serverInfo.downloadUrlPattern + .replace("{publicIp}", serverInfo.publicIp) + .replace("{exposedPort}", String.valueOf(MESHLibApi.getExternalPort())) + .replace("{filename}", outputFileName); + } + + public String getGithubRef() { + return "refs/heads/" + repositoryInfo.branch; + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/package-info.java new file mode 100644 index 0000000..5a5adef --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.config; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/BasicWebhook.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/BasicWebhook.java similarity index 90% rename from common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/BasicWebhook.java rename to common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/BasicWebhook.java index 224ffa2..7e7ec94 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/BasicWebhook.java +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/BasicWebhook.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.githubresourcepackmanager.config.webhook; +package top.offsetmonkey538.gitpackmanager.common.config.webhook; public final class BasicWebhook implements DefaultWebhookBody { diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/DefaultWebhookBody.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/DefaultWebhookBody.java new file mode 100644 index 0000000..6c7d270 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/DefaultWebhookBody.java @@ -0,0 +1,5 @@ +package top.offsetmonkey538.gitpackmanager.common.config.webhook; + +public interface DefaultWebhookBody { + String getName(); +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/basic/BasicFailMessage.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/basic/BasicFailMessage.java new file mode 100644 index 0000000..4888d0d --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/basic/BasicFailMessage.java @@ -0,0 +1,15 @@ +package top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.basic; + +import top.offsetmonkey538.gitpackmanager.common.config.webhook.DefaultWebhookBody; + +public final class BasicFailMessage implements DefaultWebhookBody { + + public final String username = "Git Pack Manager"; + public final String avatar_url = "https://github.com/OffsetMods538/Git-Pack-Manager/blob/master/common/src/main/resources/assets/git-pack-manager/icon.png?raw=true"; + public final String content = "Pack update failed!"; + + @Override + public String getName() { + return "discord/basic/fail.json"; + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/basic/BasicSuccessMessage.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/basic/BasicSuccessMessage.java new file mode 100644 index 0000000..3874e95 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/basic/BasicSuccessMessage.java @@ -0,0 +1,15 @@ +package top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.basic; + +import top.offsetmonkey538.gitpackmanager.common.config.webhook.DefaultWebhookBody; + +public final class BasicSuccessMessage implements DefaultWebhookBody { + + public final String username = "Git Pack Manager"; + public final String avatar_url = "https://github.com/OffsetMods538/Git-Pack-Manager/blob/master/common/src/main/resources/assets/git-pack-manager/icon.png?raw=true"; + public final String content = "New update for pack released!\nDescription: {shortDescription}\nDownload [here]({downloadUrl})"; + + @Override + public String getName() { + return "discord/basic/success.json"; + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/basic/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/basic/package-info.java new file mode 100644 index 0000000..22548e1 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/basic/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.basic; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/embed/EmbedFailMessage.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/embed/EmbedFailMessage.java new file mode 100644 index 0000000..01d76b8 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/embed/EmbedFailMessage.java @@ -0,0 +1,22 @@ +package top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.embed; + +import top.offsetmonkey538.gitpackmanager.common.config.webhook.DefaultWebhookBody; + +public final class EmbedFailMessage implements DefaultWebhookBody { + + public final String username = "Git Pack Manager"; + public final String avatar_url = "https://github.com/OffsetMods538/Git-Pack-Manager/blob/master/common/src/main/resources/assets/git-pack-manager/icon.png?raw=true"; + public final Embed[] embeds = new Embed[] { + new Embed( + "Pack update failed!", + 0xFF0000 + ) + }; + + public record Embed(String title, int color) {} + + @Override + public String getName() { + return "discord/embed/fail.json"; + } +} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/EmbedMessage.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/embed/EmbedSuccessMessage.java similarity index 58% rename from common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/EmbedMessage.java rename to common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/embed/EmbedSuccessMessage.java index b5057d2..513150e 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/EmbedMessage.java +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/embed/EmbedSuccessMessage.java @@ -1,16 +1,16 @@ -package top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord; +package top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.embed; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; +import top.offsetmonkey538.gitpackmanager.common.config.webhook.DefaultWebhookBody; -public final class EmbedMessage implements DefaultWebhookBody { +public final class EmbedSuccessMessage implements DefaultWebhookBody { - public final String username = "GitHub Resource Pack Manager"; - public final String avatar_url = "https://github.com/OffsetMods538/Github-Resourcepack-Manager/blob/master/src/main/resources/assets/github-resourcepack-manager/icon.png?raw=true"; + public final String username = "Git Pack Manager"; + public final String avatar_url = "https://github.com/OffsetMods538/Git-Pack-Manager/blob/master/common/src/main/resources/assets/git-pack-manager/icon.png?raw=true"; public final Embed[] embeds = new Embed[] { new Embed( "New update for pack released!", "Download [here]({downloadUrl})!", - 16722304, + 0xFF2980, new Field[] { new Field( "Description", @@ -25,6 +25,6 @@ public record Field(String name, String value) {} @Override public String getName() { - return "discord/embed_message.json"; + return "discord/embed/success.json"; } } diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/embed/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/embed/package-info.java new file mode 100644 index 0000000..31e32f8 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/discord/embed/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.config.webhook.discord.embed; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/package-info.java new file mode 100644 index 0000000..a10350d --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/config/webhook/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.config.webhook; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/exception/GitPackManagerException.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/exception/GitPackManagerException.java new file mode 100644 index 0000000..9739a47 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/exception/GitPackManagerException.java @@ -0,0 +1,37 @@ +package top.offsetmonkey538.gitpackmanager.common.exception; + +import static top.offsetmonkey538.offsetutils538.api.text.ArgReplacer.replaceArgs; + +public class GitPackManagerException extends Exception { + public GitPackManagerException(String message) { + super(message); + } + + public GitPackManagerException(String message, Object arg) { + this(replaceArgs(message, arg)); + } + + public GitPackManagerException(String message, Object arg1, Object arg2) { + this(replaceArgs(message, arg1, arg2)); + } + + public GitPackManagerException(String message, Object... args) { + this(replaceArgs(message, args)); + } + + public GitPackManagerException(String message, Throwable cause) { + super(message, cause); + } + + public GitPackManagerException(String message, Throwable cause, Object arg) { + this(replaceArgs(message, arg), cause); + } + + public GitPackManagerException(String message, Throwable cause, Object arg1, Object arg2) { + this(replaceArgs(message, arg1, arg2), cause); + } + + public GitPackManagerException(String message, Throwable cause, Object... args) { + this(replaceArgs(message, args), cause); + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/exception/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/exception/package-info.java new file mode 100644 index 0000000..77ccfac --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/exception/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.exception; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/git/CommitProperties.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/git/CommitProperties.java similarity index 93% rename from common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/git/CommitProperties.java rename to common/src/main/java/top/offsetmonkey538/gitpackmanager/common/git/CommitProperties.java index 9d3de19..a5ba93b 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/git/CommitProperties.java +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/git/CommitProperties.java @@ -1,4 +1,4 @@ -package top.offsetmonkey538.githubresourcepackmanager.git; +package top.offsetmonkey538.gitpackmanager.common.git; import java.util.Map; diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/git/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/git/package-info.java new file mode 100644 index 0000000..c432f81 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/git/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.git; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/DataPackHandler.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/DataPackHandler.java new file mode 100644 index 0000000..0673803 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/DataPackHandler.java @@ -0,0 +1,169 @@ +package top.offsetmonkey538.gitpackmanager.common.handler; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.apache.commons.io.file.PathUtils; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.DATAPACK_FOLDER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.config; +import static top.offsetmonkey538.offsetutils538.api.text.ArgReplacer.replaceArgs; + +public class DataPackHandler { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + private static final Path STATE_FILE = DATAPACK_FOLDER.resolve("state.json"); + + public void generatePack() throws GitPackManagerException { + final Path datapacks = PlatformServerProperties.INSTANCE.getDatapacksDir(); + + + // Delete existing stuff + final State existingPacks; + try { + existingPacks = readStateFile(); + } catch (IOException e) { + throw new GitPackManagerException("Failed to read state file!", e); + } + + Arrays.stream(existingPacks.packs) + .map(datapacks::resolve) + .forEach(path -> { + if (Files.notExists(path)) { + LOGGER.warn("Not deleting pack at '%s' as it doesn't exist!", path.toAbsolutePath()); + return; + } + + try { + PathUtils.delete(path); + LOGGER.info("Deleted pack at '%s'!", path.toAbsolutePath()); + } catch (IOException e) { + LOGGER.error("Failed to delete pack at '%s'!", e, path.toAbsolutePath()); + } + }); + + + // Get new packs + final List sourcePacks; + + try { + sourcePacks = gatherSourcePacks(); + } catch (GitPackManagerException e) { + throw new GitPackManagerException("Failed to gather source packs!", e); + } + + // Delete ones with same name from datapacks + for (final Path sourcePack : sourcePacks) { + final Path path = datapacks.resolve(sourcePack.getFileName()); + if (Files.notExists(path)) continue; + + LOGGER.info("Deleting pack at '%s' to replace it...", path.toAbsolutePath()); + try { + PathUtils.delete(path); + LOGGER.info("Deleted pack at '%s'!", path.toAbsolutePath()); + } catch (IOException e) { + throw new GitPackManagerException("Failed to delete pack at '%s'!", e, path.toAbsolutePath()); + } + } + + // Finally copy over the new ones + for (final Path path : sourcePacks) { + try { + final Path destination = datapacks.resolve(path.getFileName()); + + if (Files.isDirectory(path)) PathUtils.copyDirectory(path, destination); + else Files.copy(path, destination); + + LOGGER.info("Copied pack from '%s' to '%s'.", path.toAbsolutePath(), destination.toAbsolutePath()); + } catch (IOException e) { + throw new GitPackManagerException("Failed to copy pack at '%s' to datapacks directory at '%s'!", e, path.toAbsolutePath(), datapacks.toAbsolutePath()); + } + } + + try { + writeStateFile(sourcePacks); + } catch (IOException e) { + throw new GitPackManagerException("Failed to write state file!", e); + } + } + + private void writeStateFile(List files) throws IOException { + Files.createDirectories(STATE_FILE.getParent()); + + final String json = GSON.toJson(new State(files.stream().map(Path::getFileName).map(Path::toString).toArray(String[]::new))); + + Files.writeString(STATE_FILE, json); + } + + private State readStateFile() throws IOException { + if (Files.notExists(STATE_FILE)) { + LOGGER.warn(replaceArgs("State file '%s' not found! No datapacks will be deleted!", STATE_FILE)); + return new State(new String[]{}); + } + + return GSON.fromJson(Files.readString(STATE_FILE), State.class); + } + + private List gatherSourcePacks() throws GitPackManagerException { + LOGGER.info("Checking for 'pack.mcmeta' in data pack root..."); + final boolean hasPackMcmeta = Files.exists(config.get().dataPackProvider.getPackRoot().resolve("pack.mcmeta")); + LOGGER.info("%sFound!", hasPackMcmeta ? "" : "Not "); + + LOGGER.info("Checking for 'packs' directory in data pack root..."); + Path packsDir = config.get().dataPackProvider.getPackPacksDir(); + final boolean hasPacksFolder = Files.exists(packsDir) && Files.isDirectory(packsDir); + LOGGER.info("%sFound!", hasPacksFolder ? "" : "Not "); + + if (hasPackMcmeta && hasPacksFolder) { + throw new GitPackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in data pack root '%s'!", config.get().dataPackProvider.getPackRoot().toAbsolutePath()); + } + if (!hasPackMcmeta && !hasPacksFolder) { + LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in data pack root '%s'!", config.get().dataPackProvider.getPackPacksDir().toAbsolutePath()); + LOGGER.info("Assuming data pack root '%s' as 'packs' directory.", config.get().dataPackProvider.getPackPacksDir().toAbsolutePath()); + packsDir = config.get().dataPackProvider.getPackRoot(); + } + + + if (hasPackMcmeta) { + LOGGER.info("Using data pack root as data pack."); + return List.of(config.get().dataPackProvider.getPackRoot()); + } + + LOGGER.info("Using 'packs' directory for data packs."); + return gatherSourcePacksFrom(packsDir); + } + + private List gatherSourcePacksFrom(final Path packsDir) throws GitPackManagerException { + try (final Stream sourcePacks = Files.list(packsDir)) { + final List result = sourcePacks + .filter(path -> { + final boolean hidden = path.getFileName().startsWith("."); + if (!hidden) return true; + LOGGER.warn("Excluding hidden file '%s'", path.toAbsolutePath()); + return false; + }) + .toList(); + + if (result.isEmpty()) + throw new GitPackManagerException("Repository contains empty 'packs' folder!"); + + return result; + } catch (IOException e) { + throw new GitPackManagerException("Failed to list files in 'packs' folder'!", e); + } + } + + + private record State(String[] packs) { + + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/GitHandler.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/GitHandler.java new file mode 100644 index 0000000..476d2a6 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/GitHandler.java @@ -0,0 +1,205 @@ +package top.offsetmonkey538.gitpackmanager.common.handler; + +import org.apache.commons.io.file.PathUtils; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.PullResult; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.merge.ContentMergeStrategy; +import org.eclipse.jgit.merge.MergeStrategy; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.jspecify.annotations.Nullable; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; +import top.offsetmonkey538.gitpackmanager.common.git.CommitProperties; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.GIT_FOLDER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.config; +import static top.offsetmonkey538.offsetutils538.api.text.ArgReplacer.replaceArgs; + +public class GitHandler { + + private @Nullable CommitProperties commitProperties = null; + private @Nullable List changedFiles; + + public void updateRepositoryAndGenerateCommitProperties() throws GitPackManagerException { + String originalCommitHash; + try { + originalCommitHash = getLatestCommitHash(); + } catch (GitPackManagerException e) { + if (!(e.getCause() instanceof RepositoryNotFoundException)) throw e; + + originalCommitHash = ""; + } + + updateRepository(true); + + final String newCommitHash = getLatestCommitHash(); + + commitProperties = getLatestCommitProperties(originalCommitHash, newCommitHash); + + try { + changedFiles = getDiff(originalCommitHash); + } catch (Exception e) { + // Most likely to happen with a fresh install or a forced update so should be fine to just assume this + LOGGER.error("Failed to get diff between commit '%s' and '%s'! Assuming pack directories were updated anyway...", e, originalCommitHash, newCommitHash); + changedFiles = null; + } + } + + + private static CommitProperties getLatestCommitProperties(String lastCommitHash, String newCommitHash) throws GitPackManagerException { + try { + final Repository repository = getRepository(); + final RevCommit commit = new RevWalk(getRepository()).parseCommit(getLatestCommit().getObjectId()); + + + return new CommitProperties( + repository.getFullBranch(), + lastCommitHash, + newCommitHash, + commit.getAuthorIdent().getName(), + commit.getFullMessage().replace("\r\n", "\\n").replace("\n", "\\n"), + commit.getShortMessage(), + String.valueOf(commit.getCommitTime()) + ); + } catch (IOException e) { + throw new GitPackManagerException("Failed to parse latest commit!", e); + } + } + + private static void updateRepository(boolean retry) throws GitPackManagerException { + // Create credentials provider if repository is private + CredentialsProvider credentialsProvider = null; + if (config.get().repositoryInfo.isPrivate) + credentialsProvider = new UsernamePasswordCredentialsProvider(config.get().repositoryInfo.username, config.get().repositoryInfo.token); + + // If the repo folder doesn't exist, clone the repository. + if (!GIT_FOLDER.toFile().exists()) cloneRepository(credentialsProvider); + + // Pull from the remote + boolean updateFailed = false; + try (Git git = Git.open(GIT_FOLDER.toFile())) { + final PullResult result = git.pull() + .setCredentialsProvider(credentialsProvider) + .setContentMergeStrategy(ContentMergeStrategy.THEIRS) + .setStrategy(MergeStrategy.THEIRS) + .setRemoteBranchName(config.get().getGithubRef()) + .call(); + + // Handle errors + if (result.isSuccessful()) { + LOGGER.debug("Successfully updated repository!"); + return; + } + + LOGGER.error("Failed to update repository!"); + updateFailed = true; + } catch (GitAPIException e) { + LOGGER.error("Failed to update repository!", e); + updateFailed = true; + } catch (IOException e) { + LOGGER.error("Failed to open repository!", e); + updateFailed = true; + } finally { + if (updateFailed && retry) { + // Oh god this is so stupid + // Repository updating *should* only fail when remote repository is changed or + // some files are changed locally, so it should be fine to just delete and re-clone it. + LOGGER.info("Deleting git folder and trying again..."); + + try { + PathUtils.deleteDirectory(GIT_FOLDER); + } catch (IOException e) { + LOGGER.error("Failed to delete directory!", e); + } + + updateRepository(false); + } + } + } + + private static void cloneRepository(@Nullable CredentialsProvider credentialsProvider) throws GitPackManagerException { + try { + Git git = Git.cloneRepository() + .setURI(config.get().repositoryInfo.url) + .setDirectory(GIT_FOLDER.toFile()) + .setBranch(config.get().getGithubRef()) + .setCredentialsProvider(credentialsProvider) + .call(); + git.close(); + } catch (GitAPIException e) { + throw new GitPackManagerException("Failed to clone repository!", e); + } + } + + private static String getLatestCommitHash() throws GitPackManagerException { + return getLatestCommit().getObjectId().getName(); + } + + private static Ref getLatestCommit() throws GitPackManagerException { + try { + return getRepository().findRef("HEAD"); + } catch (IOException e) { + throw new GitPackManagerException("Failed to get latest commit in repository!", e); + } + } + + private static List getDiff(String startingHash) throws IOException, GitAPIException { + try (Git git = Git.open(GIT_FOLDER.toFile())) { + final Repository repository = git.getRepository(); + + final ObjectId headCommit = repository.resolve("HEAD^{tree}"); + final ObjectId startingCommit = repository.resolve(startingHash + "^{tree}"); + if (startingCommit == null) throw new IllegalArgumentException(replaceArgs("Previous commit (hash '%s') doesn't exist!", startingHash)); + + try (final ObjectReader repoReader = repository.newObjectReader()) { + final CanonicalTreeParser headTreeParser = new CanonicalTreeParser(); + headTreeParser.reset(repoReader, headCommit); + + final CanonicalTreeParser startingTreeParser = new CanonicalTreeParser(); + startingTreeParser.reset(repoReader, startingCommit); + + return git + .diff() + .setNewTree(headTreeParser) + .setOldTree(startingTreeParser) + .call() + + .stream() + .map(entry -> "/dev/null".equals(entry.getNewPath()) ? entry.getOldPath() : entry.getNewPath()) + .toList(); + } + } catch (IOException e) { + throw new IOException("Failed to open repository!", e); + } + } + + private static Repository getRepository() throws GitPackManagerException { + try (Git git = Git.open(GIT_FOLDER.toFile())) { + return git.getRepository(); + } catch (IOException e) { + throw new GitPackManagerException("Failed to open repository!", e); + } + } + + public @Nullable CommitProperties getCommitProperties() { + return commitProperties; + } + + public Optional> getChangedFiles() { + return Optional.ofNullable(changedFiles); + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/ResourcePackHandler.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/ResourcePackHandler.java new file mode 100644 index 0000000..f55ca27 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/ResourcePackHandler.java @@ -0,0 +1,211 @@ +package top.offsetmonkey538.gitpackmanager.common.handler; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.HiddenFileFilter; +import org.jspecify.annotations.Nullable; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; +import top.offsetmonkey538.gitpackmanager.common.utils.MyFileUtils; +import top.offsetmonkey538.gitpackmanager.common.utils.StringUtils; +import top.offsetmonkey538.gitpackmanager.common.utils.ZipUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.stream.Stream; + +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.RESOURCEPACK_FOLDER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.RESOURCEPACK_OUTPUT_FOLDER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.config; + +public class ResourcePackHandler { + private @Nullable Path outputPackPath = null; + + public void generatePack(boolean wasUpdated, @Nullable Path oldPackPath, @Nullable String oldPackName) throws GitPackManagerException { + outputPackPath = handleOldPackAndGetOutputPackPath(wasUpdated, oldPackPath, oldPackName); + + // If using the old pack, don't generate a new one. + if (!wasUpdated) return; + + // Generate new one + generateNewPack(); + } + + private void generateNewPack() throws GitPackManagerException { + final List sourcePacks; + + try { + sourcePacks = gatherSourcePacks(); + } catch (GitPackManagerException e) { + throw new GitPackManagerException("Failed to gather source packs!", e); + } + + + // Create temp directories to extract pack(s) into. + final Path tmpDir; + try { + tmpDir = Files.createDirectories(RESOURCEPACK_FOLDER.resolve(".temp")); + } catch (IOException e) { + throw new GitPackManagerException("Failed to create temporary directory!", e); + } + final File tempOutputDir = MyFileUtils.createDir(tmpDir.resolve("output").toFile()); + + // Extract packs into the temporary packs directory + try { + extractSourcePacks(sourcePacks, tempOutputDir); + } catch (GitPackManagerException e) { + throw new GitPackManagerException("Failed to extract source packs!", e); + } + + // Write file with source pack names + try { + writeSourcePacksFile(sourcePacks, tempOutputDir); + } catch (GitPackManagerException e) { + throw new GitPackManagerException("Failed to write pack content file!", e); + } + + // Zip the pack content and put the output file in the output directory. + try { + ZipUtils.zipDirectory(tempOutputDir, Objects.requireNonNull(getOutputPackFile())); + } catch (GitPackManagerException e) { + throw new GitPackManagerException("Failed to zip pack content!", e); + } + + // Delete temp directories. + try { + FileUtils.deleteDirectory(tmpDir.toFile()); + } catch (IOException e) { + throw new GitPackManagerException("Failed to delete temporary directory!", e); + } + } + + private void writeSourcePacksFile(List sourcePacks, File outputDir) throws GitPackManagerException { + if (sourcePacks.size() == 1) return; + + final Path sourcePacksFile = outputDir.toPath().resolve("content.txt"); + MyFileUtils.createNewFile(sourcePacksFile.toFile()); + + StringBuilder fileContent = new StringBuilder("This pack was put together using Git Pack Manager from the following packs:\n\n"); + + for (File pack : sourcePacks) { + fileContent.append("- ").append(StringUtils.nameWithoutPriorityString(pack)).append("\n"); + } + + try { + Files.writeString(sourcePacksFile, fileContent); + } catch (IOException e) { + throw new GitPackManagerException("Failed to write to file '%s'!", e, sourcePacksFile); + } + } + + private void extractSourcePacks(List sourcePacks, File tempOutputDir) throws GitPackManagerException { + // Extract source packs + for (File sourcePack : sourcePacks) { + if (sourcePack.isDirectory()) { + try { + FileUtils.copyDirectory(sourcePack, tempOutputDir, HiddenFileFilter.VISIBLE); + } catch (IOException e) { + throw new GitPackManagerException("Failed to copy pack '%s' into output directory '%s'!", e, sourcePack, tempOutputDir); + } + continue; + } + if (sourcePack.getName().endsWith(".zip")) { + ZipUtils.unzipFile(sourcePack, tempOutputDir); + continue; + } + + LOGGER.error("'%s' is not a valid pack! Ignoring...", sourcePack); + sourcePacks.remove(sourcePack); + } + } + + private List gatherSourcePacksFrom(final Path packsDir) throws GitPackManagerException { + // Gather resource packs + final File[] sourcePacksArray = packsDir.toFile().listFiles(); + if (sourcePacksArray == null) throw new GitPackManagerException("Repository contains empty 'packs' folder!"); + + // Return source packs sorted in correct order. + return Stream.of(sourcePacksArray) + .filter(file -> { + final boolean hidden = file.getName().startsWith("."); + if (!hidden) return true; + LOGGER.warn("Excluding hidden file '%s'", file.getAbsolutePath()); + return false; + }) + .sorted(Comparator.comparingInt(StringUtils::extractPriorityFromFile).reversed()) + .toList(); + } + + private List gatherSourcePacks() throws GitPackManagerException { + LOGGER.info("Checking for 'pack.mcmeta' in resource pack root..."); + final boolean hasPackMcmeta = config.get().resourcePackProvider.getPackRoot().resolve("pack.mcmeta").toFile().exists(); + LOGGER.info("%sFound!", hasPackMcmeta ? "" : "Not "); + + LOGGER.info("Checking for 'packs' directory in resource pack root..."); + Path packsDir = config.get().resourcePackProvider.getPackPacksDir(); + final boolean hasPacksFolder = Files.exists(packsDir) && Files.isDirectory(packsDir); + LOGGER.info("%sFound!", hasPacksFolder ? "" : "Not "); + + if (hasPackMcmeta && hasPacksFolder) { + throw new GitPackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in resource pack root '%s'!", config.get().resourcePackProvider.getPackPacksDir().toAbsolutePath()); + } + if (!hasPackMcmeta && !hasPacksFolder) { + LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in resource pack root '%s'!", config.get().resourcePackProvider.getPackPacksDir().toAbsolutePath()); + LOGGER.info("Assuming resource pack root '%s' as 'packs' directory.", config.get().resourcePackProvider.getPackPacksDir().toAbsolutePath()); + packsDir = config.get().resourcePackProvider.getPackRoot(); + } + + + if (hasPackMcmeta) { + LOGGER.info("Using resource pack root as resource pack."); + return List.of(config.get().resourcePackProvider.getPackRoot().toFile()); + } + + LOGGER.info("Using 'packs' directory for resource packs."); + return gatherSourcePacksFrom(packsDir); + } + + + private @Nullable Path handleOldPackAndGetOutputPackPath(boolean wasUpdated, @Nullable Path oldPackPath, @Nullable String oldPackName) { + if (!wasUpdated) return oldPackPath; + + final String newPackName = generateRandomPackName(oldPackName); + + try { + if (oldPackPath != null && Files.exists(oldPackPath)) Files.delete(oldPackPath); + } catch (IOException e) { + LOGGER.error("Failed to delete old pack!", new GitPackManagerException("Failed to delete old pack '%s'!", e, oldPackPath)); + } + return RESOURCEPACK_OUTPUT_FOLDER.resolve(newPackName); + } + + private String generateRandomPackName(@Nullable String oldPackNameString) { + long oldPackName = -1; + + if (oldPackNameString != null) try { + oldPackName = Long.parseLong(oldPackNameString.replace(".zip", "")); + } catch (NumberFormatException ignored) {} + + + long newPackName = Math.abs(new Random().nextLong()); + if (newPackName == oldPackName) newPackName++; + return newPackName + ".zip"; + } + + public @Nullable Path getOutputPackPath() { + return outputPackPath; + } + + public @Nullable File getOutputPackFile() { + return getOutputPackPath() == null ? null : getOutputPackPath().toFile(); + } + + public @Nullable String getOutputPackName() { + return getOutputPackFile() == null ? null : getOutputPackFile().getName(); + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/package-info.java new file mode 100644 index 0000000..e72aaaf --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/handler/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.handler; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/networking/MainHttpHandler.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/networking/MainHttpHandler.java new file mode 100644 index 0000000..48507d1 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/networking/MainHttpHandler.java @@ -0,0 +1,42 @@ +package top.offsetmonkey538.gitpackmanager.common.networking; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpMethod; +import top.offsetmonkey538.gitpackmanager.common.GitPackManager; +import top.offsetmonkey538.meshlib.common.api.handler.HttpHandler; +import top.offsetmonkey538.meshlib.common.api.rule.HttpRule; +import top.offsetmonkey538.meshlib.common.api.util.HttpResponseUtil; + +import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED; +import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.resourcePackHandler; +import static top.offsetmonkey538.meshlib.common.api.util.HttpResponseUtil.sendError; +import static top.offsetmonkey538.meshlib.common.api.util.HttpResponseUtil.sendFile; + +public class MainHttpHandler implements HttpHandler { + @Override + public void handleRequest(ChannelHandlerContext ctx, FullHttpRequest request, HttpRule rule) throws Exception { + final HttpMethod method = request.method(); + + // GET request should be sent the resource pack + if (method == HttpMethod.GET) { + if (resourcePackHandler.getOutputPackPath() == null) sendError(ctx, request, NOT_FOUND); + else sendFile(ctx, request, resourcePackHandler.getOutputPackPath()); + return; + } + + // POST request should trigger a pack update + if (method == HttpMethod.POST) { + HttpResponseUtil.sendResponse(ctx, request, new DefaultFullHttpResponse(HTTP_1_1, OK)); + GitPackManager.updatePack(GitPackManager.UpdateType.WEBHOOK, false); + return; + } + + // If we reach this point, then the request method isn't supported + sendError(ctx, request, METHOD_NOT_ALLOWED); + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/networking/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/networking/package-info.java new file mode 100644 index 0000000..4ee40e5 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/networking/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.networking; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/package-info.java new file mode 100644 index 0000000..e27cbc4 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformCommand.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformCommand.java new file mode 100644 index 0000000..95288ae --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformCommand.java @@ -0,0 +1,12 @@ +package top.offsetmonkey538.gitpackmanager.common.platform; + +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import static top.offsetmonkey538.gitpackmanager.common.platform.ServiceLoader.load; + +public interface PlatformCommand { + PlatformCommand INSTANCE = load(PlatformCommand.class); + + int executeRequestPackCommand(final CommandContext ctx) throws CommandSyntaxException; +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformMain.java new file mode 100644 index 0000000..ac35c93 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformMain.java @@ -0,0 +1,28 @@ +package top.offsetmonkey538.gitpackmanager.common.platform; + +import net.kyori.adventure.text.Component; + +import static top.offsetmonkey538.gitpackmanager.common.platform.ServiceLoader.load; + +public interface PlatformMain { + PlatformMain INSTANCE = load(PlatformMain.class); + + /** + * Tell the server to refresh the list of available datapacks. + *

+ * This way the user won't have to run {@code /datapack list} before being able to enable them + */ + void refreshDatapacks(); + + /** + * Reload all enabled datapacks + */ + void reloadEnabledDatapacks(); + + /** + * Sends provided message to currently online admins + * + * @param message the message to send + */ + void sendMessageToAdmins(final Component message); +} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformServerProperties.java similarity index 53% rename from common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java rename to common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformServerProperties.java index bb728b0..beb0e15 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformServerProperties.java @@ -1,40 +1,43 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform; +package top.offsetmonkey538.gitpackmanager.common.platform; import com.google.common.hash.Hashing; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.handler.PackHandler; +import org.jspecify.annotations.Nullable; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; +import top.offsetmonkey538.gitpackmanager.common.handler.ResourcePackHandler; import java.io.IOException; +import java.nio.file.Path; import java.util.Map; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; -import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.RESOURCEPACK_UUID; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.config; +import static top.offsetmonkey538.gitpackmanager.common.platform.ServiceLoader.load; public interface PlatformServerProperties { PlatformServerProperties INSTANCE = load(PlatformServerProperties.class); - String getResourcePackUrl(); - String getServerPort(); + @Nullable String getResourcePackUrl(); + Path getDatapacksDir(); void setProperties(Map properties); - void reload() throws GithubResourcepackManagerException; + void reload() throws GitPackManagerException; - default void updatePackProperties(PackHandler packHandler) throws GithubResourcepackManagerException { - final String resourcePackUrl = config.getPackUrl(packHandler.getOutputPackName()); + default void updatePackProperties(ResourcePackHandler packHandler) throws GitPackManagerException { + final String resourcePackUrl = config.get().getPackUrl(packHandler.getOutputPackName()); final String resourcePackSha1; try { // Ignore the fact that sha1 hashing is deprecated as Minecraft uses it for validating server resource packs. //noinspection deprecation resourcePackSha1 = Hashing.sha1().hashBytes(com.google.common.io.Files.toByteArray(packHandler.getOutputPackFile())).toString(); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to get sha1 hash from pack file '%s'!", e, packHandler.getOutputPackFile()); + throw new GitPackManagerException("Failed to get sha1 hash from pack file '%s'!", e, packHandler.getOutputPackFile()); } LOGGER.info("Saving new resource pack properties to 'server.properties' file..."); LOGGER.info("New resource pack url: '%s'", resourcePackUrl); LOGGER.info("New resource pack sha1: '%s'", resourcePackSha1); setProperties(Map.of( - "resource-pack-id", PACK_UUID.toString(), + "resource-pack-id", RESOURCEPACK_UUID.toString(), "resource-pack", resourcePackUrl, "resource-pack-sha1", resourcePackSha1 )); diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformText.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformText.java new file mode 100644 index 0000000..f737359 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/PlatformText.java @@ -0,0 +1,11 @@ +package top.offsetmonkey538.gitpackmanager.common.platform; + +import net.kyori.adventure.text.Component; + +import static top.offsetmonkey538.gitpackmanager.common.platform.ServiceLoader.load; + +public interface PlatformText { + PlatformText INSTANCE = load(PlatformText.class); + + void sendUpdateMessage(final Component updateMessage, boolean adminsOnly); +} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/ServiceLoader.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/ServiceLoader.java similarity index 58% rename from common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/ServiceLoader.java rename to common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/ServiceLoader.java index 47c081c..3e74404 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/ServiceLoader.java +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/ServiceLoader.java @@ -1,11 +1,12 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform; +package top.offsetmonkey538.gitpackmanager.common.platform; -public final class ServiceLoader { - private ServiceLoader() { +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; - } +public final class ServiceLoader { + private ServiceLoader() {} public static T load(Class clazz) { + LOGGER.info("Loading service for: %s", clazz); return java.util.ServiceLoader.load(clazz, ServiceLoader.class.getClassLoader()) .findFirst() .orElseThrow(() -> new RuntimeException("Failed to load service for " + clazz.getName())); diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/package-info.java new file mode 100644 index 0000000..4a65497 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/platform/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.platform; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/MyFileUtils.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/MyFileUtils.java new file mode 100644 index 0000000..0dfa809 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/MyFileUtils.java @@ -0,0 +1,26 @@ +package top.offsetmonkey538.gitpackmanager.common.utils; + +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; + +import java.io.File; +import java.io.IOException; + +public final class MyFileUtils { + private MyFileUtils() {} + + public static File createDir(File file) throws GitPackManagerException { + if (!file.exists() && !file.mkdirs()) throw new GitPackManagerException("Failed to create directory '%s'!", file); + return file; + } + + public static void createNewFile(File file) throws GitPackManagerException { + if (file.exists() && !file.delete()) throw new GitPackManagerException("Failed to delete file '%s'!", file); + + try { + //noinspection ResultOfMethodCallIgnored + file.createNewFile(); + } catch (IOException e) { + throw new GitPackManagerException("Failed to create file '%s'!", e, file); + } + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/StringUtils.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/StringUtils.java new file mode 100644 index 0000000..fbf4ba4 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/StringUtils.java @@ -0,0 +1,67 @@ +package top.offsetmonkey538.gitpackmanager.common.utils; + +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; + +import java.io.File; +import java.util.Map; +import java.util.regex.Matcher; + +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.RESOURCEPACK_NAME_PATTERN; + +public final class StringUtils { + private StringUtils() {} + + /** + * Replaces all instances of the keys in the placeholders map with their values. + *

+ * Replaces {@code '} with {@code \'} in the placeholders if {@code escapeSingleQuotes} is true. + *
+ * Replaces {@code "} with {@code \"} in the placeholders if {@code escapeDoubleQuotes} is true. + * + * @param string The string to replace placeholders in. + * @param placeholders The placeholders to replace. + * @param escapeSingleQuotes Whether single quotes (') should be escaped. + * @param escapeDoubleQuotes Whether double quotes (") should be escaped. + * @return The original string with all instances of the keys in the placeholders map replaced with their values. + */ + public static String replacePlaceholders(String string, Map placeholders, boolean escapeSingleQuotes, boolean escapeDoubleQuotes) { + for (Map.Entry entry : placeholders.entrySet()) { + String placeholderValue = entry.getValue(); + if (escapeSingleQuotes) placeholderValue = placeholderValue.replace("'", "\\'"); + if (escapeDoubleQuotes) placeholderValue = placeholderValue.replace("\"", "\\\""); + + string = string.replace(entry.getKey(), placeholderValue); + } + return string; + } + + public static int extractPriorityFromFile(File file) { + final int result = extractPriorityFromFileInternal(file); + if (result != -1) return result; + LOGGER.error("File '%s' doesn't start with priority!", file); + return -1; + } + + private static int extractPriorityFromFileInternal(File file) { + final String filename = file.getName(); + + final Matcher matcher = RESOURCEPACK_NAME_PATTERN.matcher(filename); + + if (!matcher.find()) { + return -1; + } + + return Integer.parseInt(matcher.group().replace('-', ' ').strip()); + } + + public static String nameWithoutPriorityString(File file) throws GitPackManagerException { + final String filename = file.getName(); + + final Matcher matcher = RESOURCEPACK_NAME_PATTERN.matcher(filename); + + if (!matcher.find()) throw new GitPackManagerException("File '%s' doesn't start with priority!", file); + + return filename.replace(matcher.group(), "").strip(); + } +} diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/WebhookSender.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/WebhookSender.java new file mode 100644 index 0000000..39f9916 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/WebhookSender.java @@ -0,0 +1,36 @@ +package top.offsetmonkey538.gitpackmanager.common.utils; + +import top.offsetmonkey538.gitpackmanager.common.GitPackManager; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +public final class WebhookSender { + private WebhookSender() {} + + public static void send(String body, URI url, GitPackManager.UpdateType updateType, boolean updateSucceeded) throws GitPackManagerException { + final HttpRequest request = HttpRequest.newBuilder(url) + .header("Content-Type", "application/json") + .header("X-Resource-Pack-Update-Type", updateType.name()) + .header("X-Resource-Pack-Update-Succeeded", String.valueOf(updateSucceeded)) + .POST(HttpRequest.BodyPublishers.ofString(body)) + .build(); + + final HttpResponse response; + // I think we now depend on JDK 21 so HttpClient will always be AutoCloseable + try (final HttpClient client = HttpClient.newHttpClient()) { + response = client.send(request, HttpResponse.BodyHandlers.ofString()); + } catch (IOException | InterruptedException e) { + throw new GitPackManagerException("Failed to send http request!", e); + } + + final int statusCode = response.statusCode(); + if (statusCode < 200 || statusCode >= 300) { + throw new GitPackManagerException("Http status code '%s'! Response was: '%s'.", statusCode, response.body()); + } + } +} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/ZipUtils.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/ZipUtils.java similarity index 65% rename from common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/ZipUtils.java rename to common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/ZipUtils.java index 81eaba3..775c622 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/ZipUtils.java +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/ZipUtils.java @@ -1,20 +1,24 @@ -package top.offsetmonkey538.githubresourcepackmanager.utils; +package top.offsetmonkey538.gitpackmanager.common.utils; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; -public final class ZipUtils { - private ZipUtils() { +import static top.offsetmonkey538.offsetutils538.api.text.ArgReplacer.replaceArgs; - } +public final class ZipUtils { + private ZipUtils() {} - public static void zipDirectory(File directoryToZip, File destinationFile) throws GithubResourcepackManagerException { + public static void zipDirectory(File directoryToZip, File destinationFile) throws GitPackManagerException { if (!directoryToZip.exists()) - throw new GithubResourcepackManagerException("Directory '%s' does not exist!", directoryToZip); + throw new GitPackManagerException("Directory '%s' does not exist!", directoryToZip); try { final FileOutputStream fos = new FileOutputStream(destinationFile); @@ -25,13 +29,13 @@ public static void zipDirectory(File directoryToZip, File destinationFile) throw zos.close(); fos.close(); } catch (FileNotFoundException e) { - throw new GithubResourcepackManagerException("Failed to find file '%s'!", e, destinationFile); + throw new GitPackManagerException("Failed to find file '%s'!", e, destinationFile); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to zip directory '%s' to file '%s'!", e, directoryToZip, destinationFile); + throw new GitPackManagerException("Failed to zip directory '%s' to file '%s'!", e, directoryToZip, destinationFile); } } - public static void zipDirectory(File directoryToZip, ZipOutputStream zipOutputStream) throws GithubResourcepackManagerException { + public static void zipDirectory(File directoryToZip, ZipOutputStream zipOutputStream) throws GitPackManagerException { if (!directoryToZip.isDirectory()) return; final File[] children = directoryToZip.listFiles(); @@ -41,7 +45,7 @@ public static void zipDirectory(File directoryToZip, ZipOutputStream zipOutputSt } } - private static void zipFile(File fileToZip, String filename, ZipOutputStream zipOutputStream) throws GithubResourcepackManagerException { + private static void zipFile(File fileToZip, String filename, ZipOutputStream zipOutputStream) throws GitPackManagerException { if (fileToZip.isHidden()) return; if (fileToZip.isDirectory()) { @@ -53,7 +57,7 @@ private static void zipFile(File fileToZip, String filename, ZipOutputStream zip zipOutputStream.putNextEntry(zipEntry); zipOutputStream.closeEntry(); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to add directory '%s' to zip file!", e, filename); + throw new GitPackManagerException("Failed to add directory '%s' to zip file!", e, filename); } final File[] children = fileToZip.listFiles(); @@ -75,16 +79,16 @@ private static void zipFile(File fileToZip, String filename, ZipOutputStream zip zipOutputStream.write(bytes, 0, length); } } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to add file '%s' to zip file!", e, filename); + throw new GitPackManagerException("Failed to add file '%s' to zip file!", e, filename); } } - public static void unzipFile(File fileToUnzip, File destinationDir) throws GithubResourcepackManagerException { + public static void unzipFile(File fileToUnzip, File destinationDir) throws GitPackManagerException { final ZipInputStream zipInputStream; try { zipInputStream = new ZipInputStream(new FileInputStream(fileToUnzip)); } catch (FileNotFoundException e) { - throw new GithubResourcepackManagerException("Failed to find zip file '%s'!", e, fileToUnzip); + throw new GitPackManagerException("Failed to find zip file '%s'!", e, fileToUnzip); } final byte[] buffer = new byte[1024]; @@ -92,7 +96,7 @@ public static void unzipFile(File fileToUnzip, File destinationDir) throws Githu try { zipEntry = zipInputStream.getNextEntry(); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to get next entry in zip file '%s'!", e, fileToUnzip); + throw new GitPackManagerException("Failed to get next entry in zip file '%s'!", e, fileToUnzip); } while (zipEntry != null) { @@ -100,19 +104,19 @@ public static void unzipFile(File fileToUnzip, File destinationDir) throws Githu try { newFile = newFileFromZipEntry(destinationDir, zipEntry); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to create file from zip entry '%s'!", e, zipEntry); + throw new GitPackManagerException("Failed to create file from zip entry '%s'!", e, zipEntry); } if (zipEntry.isDirectory()) { if (!newFile.isDirectory() && !newFile.mkdirs()) { - throw new GithubResourcepackManagerException("Failed to create directory '%s'!", newFile); + throw new GitPackManagerException("Failed to create directory '%s'!", newFile); } try { zipEntry = zipInputStream.getNextEntry(); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to get next entry in zip file '%'!", e, fileToUnzip); + throw new GitPackManagerException("Failed to get next entry in zip file '%'!", e, fileToUnzip); } continue; @@ -120,7 +124,7 @@ public static void unzipFile(File fileToUnzip, File destinationDir) throws Githu final File parent = newFile.getParentFile(); if (!parent.isDirectory() && !parent.mkdirs()) { - throw new GithubResourcepackManagerException("Failed to create directory '%s'", newFile); + throw new GitPackManagerException("Failed to create directory '%s'", newFile); } // Write file content @@ -130,15 +134,15 @@ public static void unzipFile(File fileToUnzip, File destinationDir) throws Githu fileOutputStream.write(buffer, 0, length); } } catch (FileNotFoundException e) { - throw new GithubResourcepackManagerException("Failed to find file!", e); + throw new GitPackManagerException("Failed to find file!", e); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to write file content!", e); + throw new GitPackManagerException("Failed to write file content!", e); } try { zipEntry = zipInputStream.getNextEntry(); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to get next entry in zip file '%'!", e, fileToUnzip); + throw new GitPackManagerException("Failed to get next entry in zip file '%'!", e, fileToUnzip); } } @@ -146,7 +150,7 @@ public static void unzipFile(File fileToUnzip, File destinationDir) throws Githu zipInputStream.closeEntry(); zipInputStream.close(); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to close zip file '%s'!", e, fileToUnzip); + throw new GitPackManagerException("Failed to close zip file '%s'!", e, fileToUnzip); } } @@ -157,7 +161,7 @@ private static File newFileFromZipEntry(File destinationDir, ZipEntry zipEntry) String destinationFilePath = destinationFile.getCanonicalPath(); if (!destinationFilePath.startsWith(destinationDirPath + File.separator)) { - throw new IOException(String.format("Entry is outside of the target dir '%s'!", zipEntry.getName())); + throw new IOException(replaceArgs("Entry is outside of the target dir '%s'!", zipEntry.getName())); } return destinationFile; diff --git a/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/package-info.java b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/package-info.java new file mode 100644 index 0000000..29a1246 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/gitpackmanager/common/utils/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.common.utils; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/common/src/main/resources/assets/git-pack-manager/icon.png b/common/src/main/resources/assets/git-pack-manager/icon.png new file mode 100644 index 0000000..ad3c458 Binary files /dev/null and b/common/src/main/resources/assets/git-pack-manager/icon.png differ diff --git a/fabric/build.gradle b/fabric/build.gradle deleted file mode 100644 index f671054..0000000 --- a/fabric/build.gradle +++ /dev/null @@ -1,128 +0,0 @@ -import dex.plugins.outlet.v2.util.ReleaseType - -plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' - id 'io.github.dexman545.outlet' version '1.6.1' -} - -outlet { - maintainPropertiesFile = System.getenv("DISABLE_PROPERTIES_UPDATE") == null - mcVersionRange = rootProject.supported_minecraft_versions - allowedReleaseTypes = Set.of(ReleaseType.RELEASE) - propertiesData = [ - 'yarn_version': outlet.yarnVersion(rootProject.minecraft_version), - 'loader_version': outlet.loaderVersion(), - 'fapi_version': outlet.fapiVersion(rootProject.minecraft_version) - ] -} - - -loom { - mods { - modid { - sourceSet sourceSets.main - } - } - - accessWidenerPath = file("src/main/resources/github-resourcepack-manager.accesswidener") - - runs { - server { - runDir "run/server" - } - - client { - runDir "run/client" - } - } -} - -// https://gist.github.com/maityyy/3dbcd558d58a6412c3a2a38c72706e8e -afterEvaluate { - loom.runs.configureEach { - vmArg "-javaagent:${project.configurations.compileClasspath.find{ it.name.contains("sponge-mixin") }}" - if (System.getenv("DISABLE_PROPERTIES_UPDATE") == null) vmArg "-Ddevauth.enabled=true" - } -} - -repositories { - mavenCentral() - mavenLocal() - maven { - name = "DevAuth" - url = "https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1" - content { - includeGroup "me.djtheredstoner" - } - } - maven { - name = "OffsetMods538" - url = "https://maven.offsetmonkey538.top/releases" - content { - includeGroup "top.offsetmonkey538.monkeylib538" - includeGroup "top.offsetmonkey538.meshlib" - } - } -} - -configurations { - includeModImplementation - - include.extendsFrom includeModImplementation - modImplementation.extendsFrom includeModImplementation - - - includeImplementation - - include.extendsFrom includeImplementation - implementation.extendsFrom includeImplementation - - common { - canBeResolved = true - canBeConsumed = false - } - api.extendsFrom common -} - -dependencies { - minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings "net.fabricmc:yarn:${project.yarn_version}:v2" - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - - modRuntimeOnly "me.djtheredstoner:DevAuth-fabric:${devauth_version}" - - modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fapi_version}" - - modImplementation "top.offsetmonkey538.monkeylib538:monkeylib538:${project.monkeylib538_version}" - - include runtimeOnly("top.offsetmonkey538.meshlib:mesh-lib-fabric:${rootProject.meshlib_version}") - - include project(path: ":common", configuration: "shadow") - common project(":common") -} - -processResources { - final Map properties = Map.of( - "modVersion", project.mod_version, - "supportedMinecraftVersions", project.supported_minecraft_versions, - "monkeylib538Version", project.monkeylib538_version - ) - - inputs.properties(properties) - - filesMatching("fabric.mod.json") { - expand(properties) - } - - exclude ".cache/**" -} - -modrinth { - loaders = ["fabric"] - uploadFile = remapJar.archiveFile - - dependencies { - required.project "monkeylib538" - required.project "fabric-api" - } -} \ No newline at end of file diff --git a/fabric/gradle.properties b/fabric/gradle.properties deleted file mode 100644 index fccff0f..0000000 --- a/fabric/gradle.properties +++ /dev/null @@ -1,15 +0,0 @@ -# Fabric -# Check at https://fabricmc.net/develop -# These should be automatically updated, unless the environment -# variable "DISABLE_PROPERTIES_UPDATE" is set. -yarn_version = 1.21.4+build.8 -loader_version = 0.16.14 -fapi_version = 0.119.3+1.21.4 - -# Dependencies -## DevAuth, check at https://github.com/DJtheRedstoner/DevAuth -devauth_version = 1.2.1 - -monkeylib538_version = 2.0.2+1.21 - -nameSuffix = fabric diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/AbstractPropertiesHandlerAccessor.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/AbstractPropertiesHandlerAccessor.java deleted file mode 100644 index 95da1e6..0000000 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/AbstractPropertiesHandlerAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.mixin; - -import net.minecraft.server.dedicated.AbstractPropertiesHandler; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.Properties; - -@Mixin(AbstractPropertiesHandler.class) -public interface AbstractPropertiesHandlerAccessor { - - @Accessor - Properties getProperties(); -} diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/MinecraftDedicatedServerAccessor.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/MinecraftDedicatedServerAccessor.java deleted file mode 100644 index abfa7de..0000000 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/MinecraftDedicatedServerAccessor.java +++ /dev/null @@ -1,13 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.mixin; - -import net.minecraft.server.dedicated.MinecraftDedicatedServer; -import net.minecraft.server.dedicated.ServerPropertiesLoader; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(MinecraftDedicatedServer.class) -public interface MinecraftDedicatedServerAccessor { - - @Accessor - ServerPropertiesLoader getPropertiesLoader(); -} diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/ServerPropertiesLoaderAccessor.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/ServerPropertiesLoaderAccessor.java deleted file mode 100644 index d4db21c..0000000 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/ServerPropertiesLoaderAccessor.java +++ /dev/null @@ -1,20 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.mixin; - -import net.minecraft.server.dedicated.ServerPropertiesHandler; -import net.minecraft.server.dedicated.ServerPropertiesLoader; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.nio.file.Path; - -@Mixin(ServerPropertiesLoader.class) -public interface ServerPropertiesLoaderAccessor { - - @Accessor - Path getPath(); - - @Mutable - @Accessor - void setPropertiesHandler(ServerPropertiesHandler handler); -} diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java deleted file mode 100644 index 3e58206..0000000 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java +++ /dev/null @@ -1,75 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; - -import com.mojang.brigadier.CommandDispatcher; -import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; -import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.command.ControlFlowAware; -import net.minecraft.network.packet.s2c.common.ResourcePackSendS2CPacket; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.command.CommandManager; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.Text; -import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; - -import java.util.Optional; - -import static com.mojang.brigadier.arguments.BoolArgumentType.bool; -import static net.minecraft.server.command.CommandManager.argument; -import static net.minecraft.server.command.CommandManager.literal; - -public class FabricPlatformCommand implements PlatformCommand { - @Override - public void registerGithubRpManagerCommand() { - CommandRegistrationCallback.EVENT.register(FabricPlatformCommand::register); - } - - public static void register(CommandDispatcher dispatcher, CommandRegistryAccess commandRegistryAccess, CommandManager.RegistrationEnvironment registrationEnvironment) { - dispatcher.register(literal("gh-rp-manager") - .requires(ServerCommandSource::isExecutedByPlayer) - .then(literal("request-pack") - .executes( - context -> { - final ServerPlayerEntity player = context.getSource().getPlayerOrThrow(); - final MinecraftServer.ServerResourcePackProperties resourcePackProperties = context.getSource().getServer().getResourcePackProperties().orElse(null); - if (resourcePackProperties == null) { - context.getSource().sendFeedback(() -> Text.literal("Failed to send pack update packet to client!"), true); - return 0; - } - - player.networkHandler.send( - new ResourcePackSendS2CPacket( - resourcePackProperties.id(), - resourcePackProperties.url(), - resourcePackProperties.hash(), - resourcePackProperties.isRequired(), - Optional.ofNullable(resourcePackProperties.prompt()) - ), - null - ); - - return ControlFlowAware.Command.SINGLE_SUCCESS; - }) - ) - - .then(literal("trigger-update") - .requires(source -> source.hasPermissionLevel(2)) - .executes( - context -> { - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.COMMAND); - return 1; - } - ) - .then(argument("force", bool()) - .executes( - context -> { - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.COMMAND_FORCE); - return 1; - } - ) - ) - ) - ); - } -} diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java deleted file mode 100644 index 0184c28..0000000 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java +++ /dev/null @@ -1,43 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; - -import org.slf4j.Logger; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; - -public class FabricPlatformLogging implements PlatformLogging { - - private static Logger logger; - - @Override - public void debug(String message) { - logger.debug(message); - } - - @Override - public void info(String message) { - logger.info(message); - } - - @Override - public void warn(String message) { - logger.warn(message); - } - - @Override - public void warn(String message, Throwable error) { - logger.warn(message, error); - } - - @Override - public void error(String message) { - logger.error(message); - } - - @Override - public void error(String message, Throwable error) { - logger.error(message, error); - } - - public static void setLogger(Logger logger) { - FabricPlatformLogging.logger = logger; - } -} diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java deleted file mode 100644 index 132aac0..0000000 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ /dev/null @@ -1,44 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; - -import net.fabricmc.api.DedicatedServerModInitializer; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.server.MinecraftServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; - -import java.nio.file.Path; - -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; - -public class FabricPlatformMain implements PlatformMain, DedicatedServerModInitializer { - public static final FabricPlatformMain INSTANCE = (FabricPlatformMain) PlatformMain.INSTANCE; - - private static MinecraftServer minecraftServer; - - @Override - public void onInitializeServer() { - FabricPlatformLogging.setLogger(LoggerFactory.getLogger(MOD_ID)); - - GithubResourcepackManager.initialize(); - - ServerLifecycleEvents.SERVER_STARTING.register(minecraftServer1 -> minecraftServer = minecraftServer1); - } - - public static MinecraftServer getServer() { - return minecraftServer; - } - - - @Override - public Path getConfigDir() { - return FabricLoader.getInstance().getConfigDir().resolve(MOD_ID); - } - - @Override - public void runOnServerStart(Runnable work) { - ServerLifecycleEvents.SERVER_STARTED.register(minecraftServer1 -> work.run()); - } -} diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformServerProperties.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformServerProperties.java deleted file mode 100644 index b0885bf..0000000 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformServerProperties.java +++ /dev/null @@ -1,50 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; - -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.dedicated.ServerPropertiesHandler; -import net.minecraft.server.dedicated.ServerPropertiesLoader; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.mixin.AbstractPropertiesHandlerAccessor; -import top.offsetmonkey538.githubresourcepackmanager.mixin.MinecraftDedicatedServerAccessor; -import top.offsetmonkey538.githubresourcepackmanager.mixin.ServerPropertiesLoaderAccessor; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; - -import java.util.Map; -import java.util.Optional; -import java.util.Properties; - -public class FabricPlatformServerProperties implements PlatformServerProperties { - - @Override - public String getResourcePackUrl() { - final Optional resourcePackProperties = FabricPlatformMain.getServer().getResourcePackProperties(); - - return resourcePackProperties.map(MinecraftServer.ServerResourcePackProperties::url).orElse(null); - } - - @Override - public String getServerPort() { - return String.valueOf(FabricPlatformMain.getServer().getServerPort()); - } - - @Override - public void setProperties(Map properties) { - final ServerPropertiesLoader propertiesLoader = ((MinecraftDedicatedServerAccessor) FabricPlatformMain.getServer()).getPropertiesLoader(); - - propertiesLoader.apply(propertiesHandler -> { - final Properties serverProperties = ((AbstractPropertiesHandlerAccessor) propertiesHandler).getProperties(); - - properties.forEach(serverProperties::setProperty); - - return propertiesHandler; - }); - } - - @Override - public void reload() throws GithubResourcepackManagerException { - final ServerPropertiesLoader propertiesLoader = ((MinecraftDedicatedServerAccessor) FabricPlatformMain.getServer()).getPropertiesLoader(); - final ServerPropertiesLoaderAccessor propertiesLoaderAccess = (ServerPropertiesLoaderAccessor) propertiesLoader; - - propertiesLoaderAccess.setPropertiesHandler(ServerPropertiesHandler.load(propertiesLoaderAccess.getPath())); - } -} diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java deleted file mode 100644 index ab38519..0000000 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ /dev/null @@ -1,71 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; - -import net.minecraft.server.PlayerManager; -import net.minecraft.text.*; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; -import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; -import top.offsetmonkey538.monkeylib538.utils.TextUtils; - -import java.util.Map; - -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; - -public class FabricPlatformText implements PlatformText { - @Override - public void sendUpdateMessage(Map placeholders) throws GithubResourcepackManagerException { - final PlayerManager playerManager = FabricPlatformMain.getServer().getPlayerManager(); - if (playerManager == null) return; - - String message = config.packUpdateMessage; - final String[] splitMessage = message.split("\n"); - - final HoverEvent hoverEvent; - try { - hoverEvent = config.packUpdateMessageHoverMessage == null ? null : new HoverEvent( - HoverEvent.Action.SHOW_TEXT, - TextUtils.INSTANCE.getStyledText( - StringUtils.replacePlaceholders(config.packUpdateMessageHoverMessage, placeholders).replace("\\n", "\n") - ) - ); - } catch (Exception e) { - throw new GithubResourcepackManagerException("Failed to style update hover message!", e); - } - - for (int lineNumber = 0; lineNumber < splitMessage.length; lineNumber++) { - final String currentLineString = StringUtils.replacePlaceholders(splitMessage[lineNumber], placeholders).replace("\\n", "\n"); - final MutableText currentLine = Text.empty(); - try { - for (Text currentLineSibling : TextUtils.INSTANCE.getStyledText(currentLineString).getSiblings()) { - final MutableText sibling = currentLineSibling.copy(); - - if (hoverEvent != null) sibling.setStyle(sibling.getStyle().withHoverEvent(hoverEvent)); - - final String siblingString = sibling.getString(); - if (!siblingString.contains("{packUpdateCommand}")) { - currentLine.append(sibling); - continue; - } - - final Style siblingStyle = sibling.getStyle(); - final String[] splitSibling = siblingString.split("\\{packUpdateCommand}"); - - if (splitSibling.length > 0) - currentLine.append(Text.literal(splitSibling[0]).setStyle(siblingStyle)); - - currentLine.append(Text.literal("[HERE]").setStyle(siblingStyle - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.of("Click to update pack"))) - .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/gh-rp-manager request-pack")) - )); - - if (splitSibling.length > 1) - currentLine.append(Text.literal(splitSibling[1]).setStyle(siblingStyle)); - } - } catch (Exception e) { - throw new GithubResourcepackManagerException("Failed to style update message at line number '%s'!", e, lineNumber); - } - - playerManager.broadcast(currentLine, false); - } - } -} diff --git a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand b/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand deleted file mode 100644 index 8bb05be..0000000 --- a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.fabric.FabricPlatformCommand \ No newline at end of file diff --git a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging b/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging deleted file mode 100644 index 7695764..0000000 --- a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.fabric.FabricPlatformLogging \ No newline at end of file diff --git a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain b/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain deleted file mode 100644 index 9dced14..0000000 --- a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.fabric.FabricPlatformMain \ No newline at end of file diff --git a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties b/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties deleted file mode 100644 index 5fc63aa..0000000 --- a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.fabric.FabricPlatformServerProperties \ No newline at end of file diff --git a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText b/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText deleted file mode 100644 index 2614502..0000000 --- a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.fabric.FabricPlatformText \ No newline at end of file diff --git a/fabric/src/main/resources/assets/github-resourcepack-manager/icon.png b/fabric/src/main/resources/assets/github-resourcepack-manager/icon.png deleted file mode 100644 index a6544fa..0000000 Binary files a/fabric/src/main/resources/assets/github-resourcepack-manager/icon.png and /dev/null differ diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json deleted file mode 100644 index f8f1dce..0000000 --- a/fabric/src/main/resources/fabric.mod.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "schemaVersion": 1, - "id": "github-resourcepack-manager", - "version": "${modVersion}", - "name": "Github Resourcepack Manager", - "description": "Modify server resourcepack from GitHub.", - "authors": [ - "OffsetMonkey538" - ], - "contact": { - "sources": "https://github.com/OffsetMods538/Github-Resourcepack-manager", - "issues": "https://github.com/OffsetMods538/Github-Resourcepack-manager/issues", - "homepage": "https://modrinth.com/mod/github-resourcepack-manager" - }, - "license": "MIT", - "icon": "assets/github-resourcepack-manager/icon.png", - "environment": "server", - "entrypoints": { - "server": [ - "top.offsetmonkey538.githubresourcepackmanager.platform.fabric.FabricPlatformMain" - ] - }, - "mixins": [ - "github-resourcepack-manager.mixins.json" - ], - "accessWidener": "github-resourcepack-manager.accesswidener", - "depends": { - "minecraft": "${supportedMinecraftVersions}", - "monkeylib538": ">=${monkeylib538Version}", - "fabric-api": "*" - } -} diff --git a/fabric/src/main/resources/github-resourcepack-manager.accesswidener b/fabric/src/main/resources/github-resourcepack-manager.accesswidener deleted file mode 100644 index b9bc9cb..0000000 --- a/fabric/src/main/resources/github-resourcepack-manager.accesswidener +++ /dev/null @@ -1 +0,0 @@ -accessWidener v1 named diff --git a/fabric/src/main/resources/github-resourcepack-manager.mixins.json b/fabric/src/main/resources/github-resourcepack-manager.mixins.json deleted file mode 100644 index d6a8c7a..0000000 --- a/fabric/src/main/resources/github-resourcepack-manager.mixins.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "required": true, - "package": "top.offsetmonkey538.githubresourcepackmanager.mixin", - "compatibilityLevel": "JAVA_17", - "mixins": [ - "AbstractPropertiesHandlerAccessor", - "MinecraftDedicatedServerAccessor", - "ServerPropertiesLoaderAccessor" - ], - "injectors": { - "defaultRequire": 1 - } -} diff --git a/gradle.properties b/gradle.properties index 512ef06..cf39a72 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,12 +1,43 @@ # Increase memory and *parallel* -org.gradle.jvmargs = -Xmx6G -org.gradle.parallel = true - -minecraft_version = 1.21.4 +org.gradle.jvmargs = -Xmx6G +org.gradle.parallel = true +# MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 +monkeylib538_version = 3.0.2 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib -meshlib_version = 1.0.5+1.21.4 +meshlib_version = 2.0.1 +# JGit +jgit_version = 7.5.0.202512021534-r +## JSpecify, check at https://mvnrepository.com/artifact/org.jspecify/jspecify +jspecify_version = 1.0.0 +## Bundeled with Minecraft, common needs to use +guava_version = 33.4.0-jre +## Bundeled with Minecraft, common needs to use +brigadier_version = 1.3.10 +## Bundeled with Minecraft, common needs to use +commonsio_version = 2.18.0 +## Bundeled with Minecraft, common needs to use +commonslang_version = 3.18.0 +## Bundeled with Minecraft, common needs to use +gson_version = 2.11.0 +## DevAuth, check at https://github.com/DJtheRedstoner/DevAuth +devauth_version = 1.2.2 + +## Gradle plugins +neoforged_moddev = 2.0.+ +fabric_loom = 1.15-SNAPSHOT +papermc_paperweight_userdev = 2.0.0-beta.19 +jpenilla_run_task = 3.0.2 +gradleup_shadow = 9.3.1 +jpenilla_resource_factory = 1.3.1 +dexman_outlet = 1.6.1 +modrinth_minotaur = 2.+ # Mod Properties -mod_version = 4.0.1 -supported_minecraft_versions = >=1.19.2 +mod_version = 5.0.0-alpha.0 +mod_name = Git Pack Manager +mod_id = git-pack-manager +mod_description = Allows for auto-updating resource and data packs at runtime from a git repository. +mod_website = https://modrinth.com/mod/git-pack-manager +mod_issues = https://github.com/OffsetMods538/Git-Pack-Manager/issues +mod_sources = https://github.com/OffsetMods538/Git-Pack-Manager diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55..61285a6 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3735f26..44ae953 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionSha256Sum=0d585f69da091fc5b2beced877feab55a3064d43b8a1d46aeb07996b0915e0e0 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a9..adff685 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac..c4bdd3a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/jitpack.yml b/jitpack.yml deleted file mode 100644 index 11927c4..0000000 --- a/jitpack.yml +++ /dev/null @@ -1,4 +0,0 @@ -jdk: - - openjdk17 -env: - DISABLE_PROPERTIES_UPDATE: true \ No newline at end of file diff --git a/loader/fabric/1.21.1/gradle.properties b/loader/fabric/1.21.1/gradle.properties new file mode 100644 index 0000000..63c4806 --- /dev/null +++ b/loader/fabric/1.21.1/gradle.properties @@ -0,0 +1,19 @@ +project_name = fabric-1.21.1 +commonModdedVersion = 1.21.1 + +# Dependencies +## MonkeyLib538 +monkeylib538_suffix = 1.21.1 +## MESH-Lib +meshlib_suffix = 1.21.1 +## Adventure +adventure_version = 5.14.2 + +# Minecraft version +minecraft_version = 1.21.1 +supported_minecraft_versions = >=1.21.1 <=1.21.11 + +# These should be automatically updated, unless the environment +# variable "DISABLE_PROPERTIES_UPDATE" is set. +loader_version = 0.18.4 +fapi_version = 0.116.8+1.21.1 diff --git a/loader/fabric/build.gradle b/loader/fabric/build.gradle new file mode 100644 index 0000000..dacc6e1 --- /dev/null +++ b/loader/fabric/build.gradle @@ -0,0 +1,75 @@ +import dex.plugins.outlet.v2.util.ReleaseType +import xyz.jpenilla.resourcefactory.fabric.Environment + +plugins { + id 'multiloader-fabric' + id 'multiloader-modrinth-publish' apply false +} + +allprojects { + fabricModJson { + mitLicense() + author("OffsetMonkey538") + contact { + extra = ["discord": "https://discord.offsetmonkey538.top"] + } + environment = Environment.SERVER + serverEntrypoint("top.offsetmonkey538.gitpackmanager.fabric.GitPackManagerInitializer") + mixin("git-pack-manager.modded.mixins.json") + + depends("fabric-api", "*") + depends("adventure-platform-fabric", "*") + depends("monkeylib538", ">=${rootProject.monkeylib538_version}") + depends("mesh-lib", ">=${rootProject.meshlib_version}") + } + + // Gotta have this here cause I can't import it in buildSrc + outlet.allowedReleaseTypes = Set.of(ReleaseType.RELEASE) + + dependencies { + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fapi_version}" + + modCompileOnly("top.offsetmonkey538.monkeylib538:monkeylib538-fabric:${rootProject.monkeylib538_version}+fabric") { + exclude(group: "net.fabricmc.fabric-api") + exclude(group: "net.kyori") + } + modCompileOnly("top.offsetmonkey538.meshlib:mesh-lib-fabric:${rootProject.meshlib_version}+fabric") { + exclude(group: "net.fabricmc.fabric-api") + exclude(group: "net.kyori") + exclude(group: "top.offsetmonkey538.monkeylib538") + } + modImplementation("net.kyori:adventure-platform-fabric:${project.adventure_version}") { + exclude(group: "net.fabricmc.fabric-api") + } + } +} + +subprojects { + apply plugin: "multiloader-modrinth-publish" + + dependencies { + modImplementation("top.offsetmonkey538.monkeylib538:monkeylib538-fabric-${project.monkeylib538_suffix}:${rootProject.monkeylib538_version}+fabric+${project.monkeylib538_suffix}") { + exclude(group: "net.fabricmc.fabric-api") + exclude(group: "net.kyori") + } + modImplementation("top.offsetmonkey538.meshlib:mesh-lib-fabric-${project.meshlib_suffix}:${rootProject.meshlib_version}+fabric+${project.meshlib_suffix}") { + exclude(group: "net.fabricmc.fabric-api") + exclude(group: "net.kyori") + exclude(group: "top.offsetmonkey538.monkeylib538") + } + + includeRuntime "org.eclipse.jgit:org.eclipse.jgit:${rootProject.jgit_version}" + } + + modrinth { + loaders = ["fabric"] + uploadFile = remapJar + + dependencies { + required.project "fabric-api" + required.project "adventure-platform-mod" + required.project "monkeylib538" + required.project "mesh-lib" + } + } +} diff --git a/loader/fabric/gradle.properties b/loader/fabric/gradle.properties new file mode 100644 index 0000000..0c88634 --- /dev/null +++ b/loader/fabric/gradle.properties @@ -0,0 +1,14 @@ +project_name = fabric + +# Dependencies +## Adventure +adventure_version = 6.8.0 + +# Minecraft version +minecraft_version = 1.21.11 +supported_minecraft_versions = * + +# These should be automatically updated, unless the environment +# variable "DISABLE_PROPERTIES_UPDATE" is set. +loader_version = 0.18.4 +fapi_version = 0.141.3+1.21.11 diff --git a/loader/fabric/src/main/java/top/offsetmonkey538/gitpackmanager/fabric/GitPackManagerInitializer.java b/loader/fabric/src/main/java/top/offsetmonkey538/gitpackmanager/fabric/GitPackManagerInitializer.java new file mode 100644 index 0000000..b059d46 --- /dev/null +++ b/loader/fabric/src/main/java/top/offsetmonkey538/gitpackmanager/fabric/GitPackManagerInitializer.java @@ -0,0 +1,15 @@ +package top.offsetmonkey538.gitpackmanager.fabric; + +import net.fabricmc.api.DedicatedServerModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import top.offsetmonkey538.gitpackmanager.common.GitPackManager; +import top.offsetmonkey538.gitpackmanager.modded.platform.ModdedPlatformMain; + +public class GitPackManagerInitializer implements DedicatedServerModInitializer { + @Override + public void onInitializeServer() { + GitPackManager.initialize(); + + ServerLifecycleEvents.SERVER_STARTING.register(ModdedPlatformMain::setServer); + } +} diff --git a/loader/fabric/src/main/java/top/offsetmonkey538/gitpackmanager/fabric/package-info.java b/loader/fabric/src/main/java/top/offsetmonkey538/gitpackmanager/fabric/package-info.java new file mode 100644 index 0000000..f22e6c5 --- /dev/null +++ b/loader/fabric/src/main/java/top/offsetmonkey538/gitpackmanager/fabric/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.fabric; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/loader/neoforge/1.21.1/gradle.properties b/loader/neoforge/1.21.1/gradle.properties new file mode 100644 index 0000000..3b41175 --- /dev/null +++ b/loader/neoforge/1.21.1/gradle.properties @@ -0,0 +1,19 @@ +project_name = neoforge-1.21.1 +commonModdedVersion = 1.21.1 + +# Dependencies +## MonkeyLib538 +monkeylib538_suffix = 1.21.1 +## MESH-Lib +meshlib_suffix = 1.21.1 +## Adventure +adventure_version = 6.0.1 + +# Minecraft version +minecraft_version = 1.21.1 +supported_minecraft_versions = >=1.21.1 <=1.21.11 +minecraft_version_range = [1.21.1,1.21.11] + +neoforge_version = 21.1.218 +parchment_minecraft_version = 1.21.1 +parchment_mappings_version = 2024.11.17 diff --git a/loader/neoforge/build.gradle b/loader/neoforge/build.gradle new file mode 100644 index 0000000..f714a92 --- /dev/null +++ b/loader/neoforge/build.gradle @@ -0,0 +1,64 @@ +import dex.plugins.outlet.v2.util.ReleaseType + +plugins { + id 'multiloader-neoforge' + id 'multiloader-modrinth-publish' apply false +} + +allprojects { + neoForgeModsToml { + mitLicense() + mod(rootProject.neo_mod_id) { + authors = "OffsetMonkey538" + dependencies { + required("adventure_platform_neoforge") + required("monkeylib538", "[${rootProject.monkeylib538_version},)") + required("mesh_lib", "[${rootProject.meshlib_version},)") + } + } + mixin("git-pack-manager.modded.mixins.json") + } + + // Gotta have this here cause I can't import it in buildSrc + outlet.allowedReleaseTypes = Set.of(ReleaseType.RELEASE) + + dependencies { + compileOnly("top.offsetmonkey538.monkeylib538:monkeylib538-neoforge:${rootProject.monkeylib538_version}+neoforge") { + exclude(group: "net.kyori") + } + compileOnly("top.offsetmonkey538.meshlib:mesh-lib-neoforge:${rootProject.meshlib_version}+neoforge") { + exclude(group: "net.kyori") + exclude(group: "top.offsetmonkey538.monkeylib538") + } + implementation "net.kyori:adventure-platform-neoforge:${project.adventure_version}" + } +} + +subprojects { + apply plugin: "multiloader-modrinth-publish" + + dependencies { + implementation("top.offsetmonkey538.monkeylib538:monkeylib538-neoforge-${project.monkeylib538_suffix}:${rootProject.monkeylib538_version}+neoforge+${project.monkeylib538_suffix}") { + exclude(group: "net.kyori") + } + implementation ("top.offsetmonkey538.meshlib:mesh-lib-neoforge-${project.meshlib_suffix}:${rootProject.meshlib_version}+neoforge+${project.meshlib_suffix}") { + exclude(group: "net.kyori") + exclude(group: "top.offsetmonkey538.monkeylib538") + } + + includeRuntime("org.eclipse.jgit:org.eclipse.jgit:${rootProject.jgit_version}") { + transitive = false + } + } + + modrinth { + loaders = ["neoforge"] + uploadFile = jar + + dependencies { + required.project "adventure-platform-mod" + required.project "monkeylib538" + required.project "mesh-lib" + } + } +} diff --git a/loader/neoforge/gradle.properties b/loader/neoforge/gradle.properties new file mode 100644 index 0000000..6a8b468 --- /dev/null +++ b/loader/neoforge/gradle.properties @@ -0,0 +1,12 @@ +project_name = neoforge + +# Dependencies +## Adventure +adventure_version = 6.8.0 + +# Minecraft version +minecraft_version = 1.21.11 + +neoforge_version = 21.11.37-beta +parchment_minecraft_version = 1.21.11 +parchment_mappings_version = 2025.12.20 diff --git a/loader/neoforge/src/main/java/top/offsetmonkey538/gitpackmanager/neoforge/GitPackManagerInitializer.java b/loader/neoforge/src/main/java/top/offsetmonkey538/gitpackmanager/neoforge/GitPackManagerInitializer.java new file mode 100644 index 0000000..6160655 --- /dev/null +++ b/loader/neoforge/src/main/java/top/offsetmonkey538/gitpackmanager/neoforge/GitPackManagerInitializer.java @@ -0,0 +1,22 @@ +package top.offsetmonkey538.gitpackmanager.neoforge; + +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.server.ServerStartingEvent; +import top.offsetmonkey538.gitpackmanager.common.GitPackManager; +import top.offsetmonkey538.gitpackmanager.modded.platform.ModdedPlatformMain; + +@Mod( + value = "git_pack_manager", + dist = Dist.DEDICATED_SERVER +) +public class GitPackManagerInitializer { + public GitPackManagerInitializer(IEventBus modEventBus, ModContainer modContainer) { + GitPackManager.initialize(); + + NeoForge.EVENT_BUS.addListener(ServerStartingEvent.class, serverStartingEvent -> ModdedPlatformMain.setServer(serverStartingEvent.getServer())); + } +} diff --git a/loader/neoforge/src/main/java/top/offsetmonkey538/gitpackmanager/neoforge/package-info.java b/loader/neoforge/src/main/java/top/offsetmonkey538/gitpackmanager/neoforge/package-info.java new file mode 100644 index 0000000..07d89ac --- /dev/null +++ b/loader/neoforge/src/main/java/top/offsetmonkey538/gitpackmanager/neoforge/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.neoforge; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/loader/paper/1.21.1/gradle.properties b/loader/paper/1.21.1/gradle.properties new file mode 100644 index 0000000..dc50f6a --- /dev/null +++ b/loader/paper/1.21.1/gradle.properties @@ -0,0 +1,10 @@ +project_name = paper-1.21.1 + +monkeylib538_suffix = 1.21.1 +meshlib_suffix = 1.21.1 + +# Minecraft version +minecraft_version = 1.21.1 +supported_minecraft_versions = >=1.21.1 <=1.21.11 + +paper_version = 1.21.1-R0.1-SNAPSHOT diff --git a/loader/paper/build.gradle b/loader/paper/build.gradle new file mode 100644 index 0000000..e91208a --- /dev/null +++ b/loader/paper/build.gradle @@ -0,0 +1,56 @@ +import dex.plugins.outlet.v2.util.ReleaseType +import xyz.jpenilla.resourcefactory.paper.PaperPluginYaml + +plugins { + id 'multiloader-paper' + id 'multiloader-modrinth-publish' apply false +} + +allprojects { + // Gotta have this here cause I can't import it in buildSrc + outlet.allowedReleaseTypes = Set.of(ReleaseType.RELEASE) + + paperPluginYaml { + main = "top.offsetmonkey538.gitpackmanager.paper.platform.PaperPlatformMain\$GitPackManagerInitializer" + authors.add("OffsetMonkey538") + load = "STARTUP" + dependencies { + server("MonkeyLib538", PaperPluginYaml.Load.BEFORE, true, true) + server("MESH_Lib", PaperPluginYaml.Load.BEFORE, true, true) + } + } + + dependencies { + compileOnly("top.offsetmonkey538.monkeylib538:monkeylib538-paper:${rootProject.monkeylib538_version}+paper") + compileOnly("top.offsetmonkey538.meshlib:mesh-lib-paper:${rootProject.meshlib_version}+paper") { + exclude(group: "top.offsetmonkey538.monkeylib538") + } + } +} + +subprojects { + apply plugin: "multiloader-modrinth-publish" + + dependencies { + include implementation("org.eclipse.jgit:org.eclipse.jgit:${rootProject.jgit_version}") { + transitive = false + } + } + + tasks.runServer { + downloadPlugins { + url("https://maven.offsetmonkey538.top/releases/top/offsetmonkey538/monkeylib538/monkeylib538-paper-${project.monkeylib538_suffix}/${rootProject.monkeylib538_version}+paper+${project.monkeylib538_suffix}/monkeylib538-paper-${project.monkeylib538_suffix}-${rootProject.monkeylib538_version}+paper+${project.monkeylib538_suffix}-all.jar") + url("https://maven.offsetmonkey538.top/releases/top/offsetmonkey538/meshlib/mesh-lib-paper-${project.meshlib_suffix}/${rootProject.meshlib_version}+paper+${project.meshlib_suffix}/mesh-lib-paper-${project.meshlib_suffix}-${rootProject.meshlib_version}+paper+${project.meshlib_suffix}-all.jar") + } + } + + modrinth { + loaders = ["paper"] + uploadFile = shadowJar + + dependencies { + required.project "monkeylib538" + required.project "mesh-lib" + } + } +} diff --git a/loader/paper/gradle.properties b/loader/paper/gradle.properties new file mode 100644 index 0000000..03c2982 --- /dev/null +++ b/loader/paper/gradle.properties @@ -0,0 +1,5 @@ +project_name = paper + +# Paper +minecraft_version = 1.21.11 +paper_version = 1.21.11-R0.1-SNAPSHOT diff --git a/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformCommand.java b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformCommand.java new file mode 100644 index 0000000..18fec2c --- /dev/null +++ b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformCommand.java @@ -0,0 +1,44 @@ +package top.offsetmonkey538.gitpackmanager.paper.platform; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import net.kyori.adventure.resource.ResourcePackInfo; +import net.kyori.adventure.resource.ResourcePackRequest; +import net.minecraft.server.MinecraftServer; +import org.bukkit.entity.Player; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformCommand; +import top.offsetmonkey538.monkeylib538.paper.api.command.PaperCommandAbstractionApi; + +import java.net.URI; + +public class PaperPlatformCommand implements PlatformCommand { + @SuppressWarnings("UnstableApiUsage") + @Override + public int executeRequestPackCommand(CommandContext ctx) throws CommandSyntaxException { + final CommandSourceStack source = PaperCommandAbstractionApi.get(ctx); + final Player player = (Player) source.getExecutor(); + if (player == null) return 0; + + final MinecraftServer.ServerResourcePackInfo resourcePackProperties = MinecraftServer.getServer().getServerResourcePack().orElse(null); + if (resourcePackProperties == null) { + source.getSender().sendMessage("Failed to send pack update packet to client!"); + return 0; + } + player.sendResourcePacks( + ResourcePackRequest.resourcePackRequest() + .packs( + ResourcePackInfo.resourcePackInfo( + resourcePackProperties.id(), + URI.create(resourcePackProperties.url()), + resourcePackProperties.hash() + ) + ) + .replace(true) + .required(resourcePackProperties.isRequired()) + .asResourcePackRequest() + ); + return Command.SINGLE_SUCCESS; + } +} diff --git a/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformMain.java b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformMain.java new file mode 100644 index 0000000..62d7f82 --- /dev/null +++ b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformMain.java @@ -0,0 +1,42 @@ +package top.offsetmonkey538.gitpackmanager.paper.platform; + +import net.kyori.adventure.text.Component; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.plugin.java.JavaPlugin; +import top.offsetmonkey538.gitpackmanager.common.GitPackManager; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformMain; + +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; + +public class PaperPlatformMain implements PlatformMain { + @Override + public void refreshDatapacks() { + MinecraftServer.getServer().getPackRepository().reload(); + } + + @Override + public void reloadEnabledDatapacks() { + MinecraftServer.getServer().reloadResources(MinecraftServer.getServer().getPackRepository().getSelectedIds()).exceptionally(throwable -> { + LOGGER.warn("Failed to execute reload! Keeping old data.", throwable); + return null; + }); + } + + @Override + public void sendMessageToAdmins(Component message) { + for (final OfflinePlayer operator : Bukkit.getServer().getOperators()) { + if (operator.getPlayer() == null) continue; + operator.getPlayer().sendMessage(message); + } + } + + + public static final class GitPackManagerInitializer extends JavaPlugin { + @Override + public void onEnable() { + GitPackManager.initialize(); + } + } +} diff --git a/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformServerProperties.java b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformServerProperties.java new file mode 100644 index 0000000..a50fe7a --- /dev/null +++ b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformServerProperties.java @@ -0,0 +1,60 @@ +package top.offsetmonkey538.gitpackmanager.paper.platform; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.dedicated.DedicatedServerProperties; +import net.minecraft.server.dedicated.DedicatedServerSettings; +import net.minecraft.world.level.storage.LevelResource; +import org.jspecify.annotations.Nullable; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties; + +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; + +public class PaperPlatformServerProperties implements PlatformServerProperties { + @Override + public @Nullable String getResourcePackUrl() { + final Optional resourcePackProperties = MinecraftServer.getServer().getServerResourcePack(); + + return resourcePackProperties.map(MinecraftServer.ServerResourcePackInfo::url).orElse(null); + } + + @Override + public Path getDatapacksDir() { + return MinecraftServer.getServer().getWorldPath(LevelResource.DATAPACK_DIR); + } + + @Override + public void setProperties(Map properties) { + final DedicatedServerSettings settings = ((DedicatedServer) MinecraftServer.getServer()).settings; + + settings.update(dedicatedServerProperties -> { + properties.forEach(dedicatedServerProperties.properties::setProperty); + + return dedicatedServerProperties; + }); + } + + @Override + public void reload() throws GitPackManagerException { + final DedicatedServerSettings settings = ((DedicatedServer) MinecraftServer.getServer()).settings; + + try { + final Field sourceField = DedicatedServerSettings.class.getDeclaredField("source"); + final Field propertiesField = DedicatedServerSettings.class.getDeclaredField("properties"); + + sourceField.setAccessible(true); + propertiesField.setAccessible(true); + + propertiesField.set( + settings, + DedicatedServerProperties.fromFile((Path) sourceField.get(settings), MinecraftServer.getServer().options) + ); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new GitPackManagerException("Failed to reload 'server.properties' file!", e); + } + } +} diff --git a/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformText.java b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformText.java new file mode 100644 index 0000000..96aaa42 --- /dev/null +++ b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/PaperPlatformText.java @@ -0,0 +1,19 @@ +package top.offsetmonkey538.gitpackmanager.paper.platform; + +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.permissions.ServerOperator; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformText; + +import java.util.stream.Stream; + +public class PaperPlatformText implements PlatformText { + @Override + public void sendUpdateMessage(Component updateMessage, boolean adminsOnly) { + Stream playerStream = Bukkit.getOnlinePlayers().stream(); + if (adminsOnly) playerStream = playerStream.filter(ServerOperator::isOp); + + playerStream.forEach(player -> player.sendMessage(updateMessage)); + } +} diff --git a/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/package-info.java b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/package-info.java new file mode 100644 index 0000000..05208fc --- /dev/null +++ b/loader/paper/src/main/java/top/offsetmonkey538/gitpackmanager/paper/platform/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.paper.platform; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformCommand b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformCommand new file mode 100644 index 0000000..32adb30 --- /dev/null +++ b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformCommand @@ -0,0 +1 @@ +top.offsetmonkey538.gitpackmanager.paper.platform.PaperPlatformCommand \ No newline at end of file diff --git a/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformLogging b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformLogging new file mode 100644 index 0000000..70a1690 --- /dev/null +++ b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformLogging @@ -0,0 +1 @@ +top.offsetmonkey538.gitpackmanager.common.platform.paper.PaperPlatformLogging \ No newline at end of file diff --git a/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformMain b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformMain new file mode 100644 index 0000000..0f34156 --- /dev/null +++ b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformMain @@ -0,0 +1 @@ +top.offsetmonkey538.gitpackmanager.paper.platform.PaperPlatformMain \ No newline at end of file diff --git a/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties new file mode 100644 index 0000000..3e6d56f --- /dev/null +++ b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties @@ -0,0 +1 @@ +top.offsetmonkey538.gitpackmanager.paper.platform.PaperPlatformServerProperties \ No newline at end of file diff --git a/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformText b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformText new file mode 100644 index 0000000..c80f956 --- /dev/null +++ b/loader/paper/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformText @@ -0,0 +1 @@ +top.offsetmonkey538.gitpackmanager.paper.platform.PaperPlatformText \ No newline at end of file diff --git a/modded/1.21.1/gradle.properties b/modded/1.21.1/gradle.properties new file mode 100644 index 0000000..e778756 --- /dev/null +++ b/modded/1.21.1/gradle.properties @@ -0,0 +1,12 @@ +project_name = modded-1.21.1 + +# Dependencies +## MonkeyLib538 +monkeylib538_suffix = 1.21.1 +## MESH-Lib +meshlib_suffix = 1.21.1 +## Adventure +adventure_version = 6.0.1 + +# Minecraft version +minecraft_version = 1.21.1 diff --git a/modded/build.gradle b/modded/build.gradle new file mode 100644 index 0000000..cea2d63 --- /dev/null +++ b/modded/build.gradle @@ -0,0 +1,27 @@ +plugins { + id 'multiloader-modded' +} + +allprojects { + dependencies { + modCompileOnly("top.offsetmonkey538.monkeylib538:monkeylib538-modded:${rootProject.monkeylib538_version}+modded") { + exclude(group: "net.kyori") + } + modCompileOnly("top.offsetmonkey538.meshlib:mesh-lib-modded:${rootProject.meshlib_version}+modded") { + exclude(group: "net.kyori") + exclude(group: "top.offsetmonkey538.monkeylib538") + } + } +} + +subprojects { + dependencies { + modCompileOnly("top.offsetmonkey538.monkeylib538:monkeylib538-modded-${project.monkeylib538_suffix}:${rootProject.monkeylib538_version}+modded+${project.monkeylib538_suffix}") { + exclude(group: "net.kyori") + } + modCompileOnly("top.offsetmonkey538.meshlib:mesh-lib-modded-${project.meshlib_suffix}:${rootProject.meshlib_version}+modded+${project.meshlib_suffix}") { + exclude(group: "net.kyori") + exclude(group: "top.offsetmonkey538.monkeylib538") + } + } +} diff --git a/modded/gradle.properties b/modded/gradle.properties new file mode 100644 index 0000000..e6ea150 --- /dev/null +++ b/modded/gradle.properties @@ -0,0 +1,8 @@ +project_name = modded + +# Dependencies +## Adventure +adventure_version = 6.8.0 + +# Minecraft version +minecraft_version = 1.21.11 diff --git a/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/DedicatedServerAccessor.java b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/DedicatedServerAccessor.java new file mode 100644 index 0000000..e9ed807 --- /dev/null +++ b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/DedicatedServerAccessor.java @@ -0,0 +1,13 @@ +package top.offsetmonkey538.gitpackmanager.modded.mixin; + +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.dedicated.DedicatedServerSettings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(DedicatedServer.class) +public interface DedicatedServerAccessor { + + @Accessor + DedicatedServerSettings getSettings(); +} diff --git a/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/DedicatedServerSettingsAccessor.java b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/DedicatedServerSettingsAccessor.java new file mode 100644 index 0000000..1484e9b --- /dev/null +++ b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/DedicatedServerSettingsAccessor.java @@ -0,0 +1,20 @@ +package top.offsetmonkey538.gitpackmanager.modded.mixin; + +import net.minecraft.server.dedicated.DedicatedServerProperties; +import net.minecraft.server.dedicated.DedicatedServerSettings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.nio.file.Path; + +@Mixin(DedicatedServerSettings.class) +public interface DedicatedServerSettingsAccessor { + + @Accessor + Path getSource(); + + @Mutable + @Accessor + void setProperties(DedicatedServerProperties properties); +} diff --git a/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/SettingsAccessor.java b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/SettingsAccessor.java new file mode 100644 index 0000000..1072918 --- /dev/null +++ b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/SettingsAccessor.java @@ -0,0 +1,14 @@ +package top.offsetmonkey538.gitpackmanager.modded.mixin; + +import net.minecraft.server.dedicated.Settings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Properties; + +@Mixin(Settings.class) +public interface SettingsAccessor { + + @Accessor + Properties getProperties(); +} diff --git a/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/package-info.java b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/package-info.java new file mode 100644 index 0000000..740e44e --- /dev/null +++ b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/mixin/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.modded.mixin; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformCommand.java b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformCommand.java new file mode 100644 index 0000000..5a9d2bb --- /dev/null +++ b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformCommand.java @@ -0,0 +1,40 @@ +package top.offsetmonkey538.gitpackmanager.modded.platform; + +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformCommand; +import top.offsetmonkey538.monkeylib538.modded.api.command.ModdedCommandAbstractionApi; + +import java.util.Optional; + +import static com.mojang.brigadier.Command.SINGLE_SUCCESS; + +public class ModdedPlatformCommand implements PlatformCommand { + @Override + public int executeRequestPackCommand(CommandContext ctx) throws CommandSyntaxException { + final CommandSourceStack source = ModdedCommandAbstractionApi.get(ctx); + final ServerPlayer player = source.getPlayerOrException(); + final MinecraftServer.ServerResourcePackInfo resourcePackProperties = source.getServer().getServerResourcePack().orElse(null); + if (resourcePackProperties == null) { + source.sendSuccess(() -> Component.literal("Failed to send pack update packet to client!"), true); + return 0; + } + + player.connection.send( + new ClientboundResourcePackPushPacket( + resourcePackProperties.id(), + resourcePackProperties.url(), + resourcePackProperties.hash(), + resourcePackProperties.isRequired(), + Optional.ofNullable(resourcePackProperties.prompt()) + ) + ); + + return SINGLE_SUCCESS; + } +} diff --git a/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformMain.java b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformMain.java new file mode 100644 index 0000000..cc9ba73 --- /dev/null +++ b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformMain.java @@ -0,0 +1,45 @@ +package top.offsetmonkey538.gitpackmanager.modded.platform; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.entity.player.Player; +import org.jspecify.annotations.Nullable; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformMain; +import top.offsetmonkey538.monkeylib538.modded.api.player.ModdedPlayerApi; + +import static top.offsetmonkey538.gitpackmanager.common.GitPackManager.LOGGER; + +public class ModdedPlatformMain implements PlatformMain { + private static @Nullable MinecraftServer minecraftServer = null; + + public static @Nullable MinecraftServer getServer() { + return minecraftServer; + } + + public static void setServer(MinecraftServer server) { + minecraftServer = server; + } + + @Override + public void sendMessageToAdmins(Component message) { + if (getServer() == null) return; + for (final Player player : getServer().getPlayerList().getPlayers()) { + if (!ModdedPlayerApi.isPlayerOp(getServer().getPlayerList(), player)) continue; + ((Audience) player).sendMessage(message); + } + } + + @Override + public void refreshDatapacks() { + getServer().getPackRepository().reload(); + } + + @Override + public void reloadEnabledDatapacks() { + getServer().reloadResources(getServer().getPackRepository().getSelectedIds()).exceptionally(throwable -> { + LOGGER.warn("Failed to execute reload! Keeping old data.", throwable); + return null; + }); + } +} diff --git a/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformServerProperties.java b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformServerProperties.java new file mode 100644 index 0000000..7d3f62d --- /dev/null +++ b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformServerProperties.java @@ -0,0 +1,53 @@ +package top.offsetmonkey538.gitpackmanager.modded.platform; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServerProperties; +import net.minecraft.server.dedicated.DedicatedServerSettings; +import net.minecraft.world.level.storage.LevelResource; +import org.jspecify.annotations.Nullable; +import top.offsetmonkey538.gitpackmanager.common.exception.GitPackManagerException; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties; +import top.offsetmonkey538.gitpackmanager.modded.mixin.DedicatedServerAccessor; +import top.offsetmonkey538.gitpackmanager.modded.mixin.DedicatedServerSettingsAccessor; +import top.offsetmonkey538.gitpackmanager.modded.mixin.SettingsAccessor; + +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +public class ModdedPlatformServerProperties implements PlatformServerProperties { + + @Override + public @Nullable String getResourcePackUrl() { + final Optional resourcePackProperties = ModdedPlatformMain.getServer().getServerResourcePack(); + + return resourcePackProperties.map(MinecraftServer.ServerResourcePackInfo::url).orElse(null); + } + + @Override + public Path getDatapacksDir() { + return ModdedPlatformMain.getServer().getWorldPath(LevelResource.DATAPACK_DIR); + } + + @Override + public void setProperties(Map properties) { + final DedicatedServerSettings propertiesLoader = ((DedicatedServerAccessor) ModdedPlatformMain.getServer()).getSettings(); + + propertiesLoader.update(propertiesHandler -> { + final Properties serverProperties = ((SettingsAccessor) propertiesHandler).getProperties(); + + properties.forEach(serverProperties::setProperty); + + return propertiesHandler; + }); + } + + @Override + public void reload() throws GitPackManagerException { + final DedicatedServerSettings propertiesLoader = ((DedicatedServerAccessor) ModdedPlatformMain.getServer()).getSettings(); + final DedicatedServerSettingsAccessor propertiesLoaderAccess = (DedicatedServerSettingsAccessor) propertiesLoader; + + propertiesLoaderAccess.setProperties(DedicatedServerProperties.fromFile(propertiesLoaderAccess.getSource())); + } +} diff --git a/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformText.java b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformText.java new file mode 100644 index 0000000..254ddb9 --- /dev/null +++ b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/ModdedPlatformText.java @@ -0,0 +1,21 @@ +package top.offsetmonkey538.gitpackmanager.modded.platform; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; +import top.offsetmonkey538.gitpackmanager.common.platform.PlatformText; +import top.offsetmonkey538.monkeylib538.modded.api.player.ModdedPlayerApi; + +public class ModdedPlatformText implements PlatformText { + @Override + public void sendUpdateMessage(Component updateMessage, boolean adminsOnly) { + assert ModdedPlatformMain.getServer() != null : "Literally HOW?? Why are we trying to send the update message when the server hasn't started yet!??!?!?"; + final PlayerList playerList = ModdedPlatformMain.getServer().getPlayerList(); + + for (final ServerPlayer player : playerList.getPlayers()) { + if (adminsOnly && !ModdedPlayerApi.isPlayerOp(playerList, player)) continue; + ((Audience) player).sendMessage(updateMessage); + } + } +} diff --git a/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/package-info.java b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/package-info.java new file mode 100644 index 0000000..b3fc67c --- /dev/null +++ b/modded/src/main/java/top/offsetmonkey538/gitpackmanager/modded/platform/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package top.offsetmonkey538.gitpackmanager.modded.platform; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformCommand b/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformCommand new file mode 100644 index 0000000..5c1ea6d --- /dev/null +++ b/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformCommand @@ -0,0 +1 @@ +top.offsetmonkey538.gitpackmanager.modded.platform.ModdedPlatformCommand \ No newline at end of file diff --git a/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformMain b/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformMain new file mode 100644 index 0000000..5572c8e --- /dev/null +++ b/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformMain @@ -0,0 +1 @@ +top.offsetmonkey538.gitpackmanager.modded.platform.ModdedPlatformMain \ No newline at end of file diff --git a/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties b/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties new file mode 100644 index 0000000..a9a3020 --- /dev/null +++ b/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformServerProperties @@ -0,0 +1 @@ +top.offsetmonkey538.gitpackmanager.modded.platform.ModdedPlatformServerProperties \ No newline at end of file diff --git a/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformText b/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformText new file mode 100644 index 0000000..a32fccb --- /dev/null +++ b/modded/src/main/resources/META-INF/services/top.offsetmonkey538.gitpackmanager.common.platform.PlatformText @@ -0,0 +1 @@ +top.offsetmonkey538.gitpackmanager.modded.platform.ModdedPlatformText \ No newline at end of file diff --git a/modded/src/main/resources/git-pack-manager.modded.mixins.json b/modded/src/main/resources/git-pack-manager.modded.mixins.json new file mode 100644 index 0000000..293639c --- /dev/null +++ b/modded/src/main/resources/git-pack-manager.modded.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "package": "top.offsetmonkey538.gitpackmanager.modded.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "DedicatedServerAccessor", + "DedicatedServerSettingsAccessor", + "SettingsAccessor" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/paper/build.gradle b/paper/build.gradle deleted file mode 100644 index 540f806..0000000 --- a/paper/build.gradle +++ /dev/null @@ -1,70 +0,0 @@ -import io.papermc.paperweight.userdev.ReobfArtifactConfiguration - -plugins { - id 'com.gradleup.shadow' version '9.0.0-beta4' - id 'io.papermc.paperweight.userdev' version '2.0.0-beta.8' - id 'xyz.jpenilla.run-paper' version '2.3.1' -} - -paperweight.reobfArtifactConfiguration = ReobfArtifactConfiguration.getMOJANG_PRODUCTION() - -repositories { - mavenCentral() - mavenLocal() - maven { - name = "PaperMC" - url = "https://repo.papermc.io/repository/maven-public/" - content { - includeGroup "io.papermc.paper" - } - } - maven { - name = "OffsetMods538" - url = "https://maven.offsetmonkey538.top/releases" - content { - includeGroup "top.offsetmonkey538.meshlib" - } - } -} - -configurations { - common { - canBeResolved = true - canBeConsumed = false - } - implementation.extendsFrom common -} - -dependencies { - compileOnly "io.papermc.paper:paper-api:${project.paper_version}" - - paperweight.paperDevBundle(project.paper_version) - - implementation "xyz.jpenilla:reflection-remapper:${project.reflection_remapper_version}" - implementation "top.offsetmonkey538.meshlib:mesh-lib-paper:${rootProject.meshlib_version}" - - common project(path: ":common", configuration: "shadow") -} -tasks.build.dependsOn(shadowJar) - -processResources { - final Map properties = Map.of( - "modVersion", project.mod_version, - "lowestMinecraftVersion", outlet.mcVersions().first() // Hopefully outlet always does stuff in this order - ) - - inputs.properties(properties) - - filesMatching("plugin.yml") { - expand(properties) - } -} - -tasks.runServer { - minecraftVersion(project.minecraft_version) -} - -modrinth { - loaders = ["fabric"] - uploadFile = shadowJar -} \ No newline at end of file diff --git a/paper/gradle.properties b/paper/gradle.properties deleted file mode 100644 index eebf3fe..0000000 --- a/paper/gradle.properties +++ /dev/null @@ -1,5 +0,0 @@ -paper_version = 1.21.4-R0.1-SNAPSHOT - -reflection_remapper_version = 0.1.1 - -nameSuffix = paper diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java deleted file mode 100644 index 36b7082..0000000 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java +++ /dev/null @@ -1,109 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.paper; - -import com.mojang.brigadier.Command; -import io.papermc.paper.command.brigadier.Commands; -import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; -import net.kyori.adventure.resource.ResourcePackInfo; -import net.kyori.adventure.resource.ResourcePackRequest; -import net.minecraft.server.MinecraftServer; -import org.bukkit.entity.Player; -import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; - -import java.net.URI; - -import static com.mojang.brigadier.arguments.BoolArgumentType.bool; -import static io.papermc.paper.command.brigadier.Commands.argument; -import static io.papermc.paper.command.brigadier.Commands.literal; - -public class PaperPlatformCommand implements PlatformCommand { - @SuppressWarnings("UnstableApiUsage") - @Override - public void registerGithubRpManagerCommand() { - PaperPlatformMain.getPlugin().getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> { - final Commands commands = event. registrar(); - - commands.register(literal("gh-rp-manager") - .requires(commandSourceStack -> commandSourceStack.getExecutor() instanceof Player) - .then(literal("request-pack").executes( - context -> { - final Player player = (Player) context.getSource().getExecutor(); - if (player == null) return 0; - - final MinecraftServer.ServerResourcePackInfo resourcePackProperties = MinecraftServer.getServer().getServerResourcePack().orElse(null); - if (resourcePackProperties == null) { - context.getSource().getSender().sendMessage("Failed to send pack update packet to client!"); - return 0; - } - player.sendResourcePacks( - ResourcePackRequest.resourcePackRequest() - .packs( - ResourcePackInfo.resourcePackInfo( - resourcePackProperties.id(), - URI.create(resourcePackProperties.url()), - resourcePackProperties.hash() - ) - ) - .replace(true) - .required(resourcePackProperties.isRequired()) - .asResourcePackRequest() - ); - return Command.SINGLE_SUCCESS; - }) - ) - .then(literal("trigger-update") - .requires(source -> source.getSender().isOp()) - .executes( - context -> { - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.COMMAND); - return 1; - } - ) - .then(argument("force", bool()) - .executes( - context -> { - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.COMMAND_FORCE); - return 1; - } - ) - ) - ) - .build() - ); - }); - } - - //@Override - //public void registerGithubRpManagerCommand() { - // CommandRegistrationCallback.EVENT.register(FabricPlatformCommand::register); - //} - - //public static void register(CommandDispatcher dispatcher, CommandRegistryAccess commandRegistryAccess, CommandManager.RegistrationEnvironment registrationEnvironment) { - // dispatcher.register(literal("gh-rp-manager") - // .requires(ServerCommandSource::isExecutedByPlayer) - // .then(literal("request-pack").executes( - // context -> { - // final ServerPlayerEntity player = context.getSource().getPlayerOrThrow(); - // final MinecraftServer.ServerResourcePackProperties resourcePackProperties = context.getSource().getServer().getResourcePackProperties().orElse(null); - // if (resourcePackProperties == null) { - // context.getSource().sendFeedback(() -> Text.literal("Failed to send pack update packet to client!"), true); - // return 0; - // } - - // player.networkHandler.send( - // new ResourcePackSendS2CPacket( - // resourcePackProperties.id(), - // resourcePackProperties.url(), - // resourcePackProperties.hash(), - // resourcePackProperties.isRequired(), - // Optional.ofNullable(resourcePackProperties.prompt()) - // ), - // null - // ); - - // return ControlFlowAware.Command.SINGLE_SUCCESS; - // }) - // ) - // ); - //} -} diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java deleted file mode 100644 index e89673a..0000000 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java +++ /dev/null @@ -1,45 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.paper; - -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class PaperPlatformLogging implements PlatformLogging { - - private static Logger logger; - - @Override - public void debug(String message) { - logger.log(Level.FINE, message); - } - - @Override - public void info(String message) { - logger.log(Level.INFO, message); - } - - @Override - public void warn(String message) { - logger.log(Level.WARNING, message); - } - - @Override - public void warn(String message, Throwable error) { - logger.log(Level.WARNING, message, error); - } - - @Override - public void error(String message) { - logger.log(Level.SEVERE, message); - } - - @Override - public void error(String message, Throwable error) { - logger.log(Level.SEVERE, message, error); - } - - public static void setLogger(Logger logger) { - PaperPlatformLogging.logger = logger; - } -} diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java deleted file mode 100644 index c20d396..0000000 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ /dev/null @@ -1,61 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.paper; - -import org.bukkit.plugin.java.JavaPlugin; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; - -import java.nio.file.Path; - -public class PaperPlatformMain implements PlatformMain { - private static JavaPlugin plugin; - - @Override - public Path getConfigDir() { - return getPlugin().getDataPath(); - } - - @Override - public void runOnServerStart(Runnable work) { - work.run(); - } - - public static void setPlugin(JavaPlugin plugin) { - PaperPlatformMain.plugin = plugin; - } - - public static JavaPlugin getPlugin() { - return plugin; - } - - - //@Override - //public void onInitializeServer() { - // GithubResourcepackManager.initialize(); - - // ServerLifecycleEvents.SERVER_STARTING.register(minecraftServer1 -> minecraftServer = minecraftServer1); - //} - - //public MinecraftServer getServer() { - // return minecraftServer; - //} - - - //@Override - //public Logger getLogger() { - // return LOGGER; - //} - - //@Override - //public Path getConfigDir() { - // return FabricLoader.getInstance().getConfigDir(); - //} - - //@Override - //public Path getGameDir() { - // return FabricLoader.getInstance().getGameDir(); - //} - - //@Override - //public void runOnServerStart(Runnable work) { - // ServerLifecycleEvents.SERVER_STARTED.register(minecraftServer1 -> work.run()); - //} -} diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java deleted file mode 100644 index 45ef937..0000000 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java +++ /dev/null @@ -1,79 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.paper; - -import joptsimple.OptionSet; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraft.server.dedicated.DedicatedServerProperties; -import net.minecraft.server.dedicated.DedicatedServerSettings; -import org.bukkit.packs.ResourcePack; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; -import xyz.jpenilla.reflectionremapper.ReflectionRemapper; -import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory; -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter; -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldSetter; -import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies; - -import java.lang.reflect.Field; -import java.nio.file.Path; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; - -public class PaperPlatformServerProperties implements PlatformServerProperties { - @Override - public String getResourcePackUrl() { - final Optional resourcePackProperties = MinecraftServer.getServer().getServerResourcePack(); - - return resourcePackProperties.map(MinecraftServer.ServerResourcePackInfo::url).orElse(null); - } - - @Override - public String getServerPort() { - return String.valueOf(MinecraftServer.getServer().getPort()); - } - - @Override - public void setProperties(Map properties) { - final DedicatedServerSettings settings = ((DedicatedServer) MinecraftServer.getServer()).settings; - - settings.update(dedicatedServerProperties -> { - properties.forEach(dedicatedServerProperties.properties::setProperty); - - return dedicatedServerProperties; - }); - } - - @Override - public void reload() throws GithubResourcepackManagerException { - final DedicatedServerSettings settings = ((DedicatedServer) MinecraftServer.getServer()).settings; - - final ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar(); - - - try { - final Field sourceField = DedicatedServerSettings.class.getDeclaredField(reflectionRemapper.remapFieldName(DedicatedServerSettings.class, "source")); - final Field propertiesField = DedicatedServerSettings.class.getDeclaredField(reflectionRemapper.remapFieldName(DedicatedServerSettings.class, "properties")); - - sourceField.setAccessible(true); - propertiesField.setAccessible(true); - - propertiesField.set( - settings, - DedicatedServerProperties.fromFile((Path) sourceField.get(settings), MinecraftServer.getServer().options) - ); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new GithubResourcepackManagerException("Failed to reload 'server.properties' file!", e); - } - } - - - //@Override - //public void reload() { - // final ServerPropertiesLoader propertiesLoader = ((MinecraftDedicatedServerAccessor) FabricPlatformMain.INSTANCE.getServer()).getPropertiesLoader(); - // final ServerPropertiesLoaderAccessor propertiesLoaderAccess = (ServerPropertiesLoaderAccessor) propertiesLoader; - - // propertiesLoaderAccess.setPropertiesHandler(ServerPropertiesHandler.load(propertiesLoaderAccess.getPath())); - //} -} diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java deleted file mode 100644 index e3e44a1..0000000 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java +++ /dev/null @@ -1,148 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.paper; - -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.*; -import net.minecraft.server.MinecraftServer; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; -import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; - -import java.util.List; -import java.util.Map; - -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; - -public class PaperPlatformText implements PlatformText { - @Override - public void sendUpdateMessage(Map placeholders) throws GithubResourcepackManagerException { - String message = config.packUpdateMessage; - final String[] splitMessage = message.split("\n"); - - final HoverEvent hoverEvent; - try { - hoverEvent = config.packUpdateMessageHoverMessage == null ? null : new HoverEvent( - HoverEvent.Action.SHOW_TEXT, - getStyledText( - StringUtils.replacePlaceholders(config.packUpdateMessageHoverMessage, placeholders).replace("\\n", "\n") - ) - ); - } catch (Exception e) { - throw new GithubResourcepackManagerException("Failed to style update hover message!", e); - } - - for (int lineNumber = 0; lineNumber < splitMessage.length; lineNumber++) { - final String currentLineString = StringUtils.replacePlaceholders(splitMessage[lineNumber], placeholders).replace("\\n", "\n"); - final MutableComponent currentLine = Component.empty(); - try { - for (Component currentLineSibling : getStyledText(currentLineString).getSiblings()) { - final MutableComponent sibling = currentLineSibling.copy(); - - if (hoverEvent != null) sibling.setStyle(sibling.getStyle().withHoverEvent(hoverEvent)); - - final String siblingString = sibling.getString(); - if (!siblingString.contains("{packUpdateCommand}")) { - currentLine.append(sibling); - continue; - } - - final Style siblingStyle = sibling.getStyle(); - final String[] splitSibling = siblingString.split("\\{packUpdateCommand}"); - - if (splitSibling.length > 0) - currentLine.append(Component.literal(splitSibling[0]).setStyle(siblingStyle)); - - currentLine.append(Component.literal("[HERE]").setStyle(siblingStyle - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal("Click to update pack"))) - .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/gh-rp-manager request-pack")) - )); - - if (splitSibling.length > 1) - currentLine.append(Component.literal(splitSibling[1]).setStyle(siblingStyle)); - } - } catch (Exception e) { - throw new GithubResourcepackManagerException("Failed to style update message at line number '%s'!", e, lineNumber); - } - - MinecraftServer.getServer().getPlayerList().broadcastSystemMessage(currentLine, false); - } - } - - - private static final Style DEFAULT_STYLE = Style.EMPTY.withItalic(false).withColor(ChatFormatting.WHITE); - - private static MutableComponent getStyledText(String text) throws Exception { - final MutableComponent result = Component.empty(); - Style style = DEFAULT_STYLE; - - boolean isFormattingCode = false; - boolean isEscaped = false; - char[] characters = text.toCharArray(); - for (int characterIndex = 0; characterIndex < characters.length; characterIndex++) { - char currentChar = characters[characterIndex]; - - if (isFormattingCode) { - // Hex color - if (currentChar == '#') { - if (characterIndex + 7 >= characters.length) - throw new Exception("Unfinished hex code starting at character number '" + characterIndex + "'!"); - - try { - style = style.withColor(TextColor.parseColor(text.substring(characterIndex, characterIndex + 7)).getOrThrow(Exception::new)); - } catch (Exception e) { - throw new Exception("Failed to parse hex color starting at character number '" + characterIndex + "'!", e); - } - - // Move pointer 6 characters ahead as we already read the whole hex code - characterIndex += 6; - isFormattingCode = false; - continue; - } - - style = getStyleForFormattingCode(currentChar, style); - - if (style == null) - throw new Exception("Invalid formatting code at character number '" + characterIndex + "'!"); - - isFormattingCode = false; - continue; - } - - if (!isEscaped) { - switch (currentChar) { - case '&': - isFormattingCode = true; - continue; - case '\\': - isEscaped = true; - continue; - } - } - isEscaped = false; - - - final List siblings = result.getSiblings(); - final int lastSiblingIndex = siblings.size() - 1; - final Component lastSibling = siblings.isEmpty() ? Component.empty() : siblings.get(lastSiblingIndex); - - // Check if the style of the last sibling is the same as the current one - if (!siblings.isEmpty() && lastSibling.getStyle().equals(style)) { - // If so, set the last sibling to itself plus the new character - siblings.set(lastSiblingIndex, Component.literal(lastSibling.getString() + currentChar).setStyle(style)); - } else { - // Otherwise, just append a new sibling to the result - result.append(Component.literal(String.valueOf(currentChar)).setStyle(style)); - } - } - - return result; - } - - private static Style getStyleForFormattingCode(char formattingCode, Style currentStyle) { - if (formattingCode == 'r') return DEFAULT_STYLE; - - final ChatFormatting formatting = ChatFormatting.getByCode(formattingCode); - if (formatting == null) return null; - - return currentStyle.applyFormat(formatting); - } -} diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java deleted file mode 100644 index caf4270..0000000 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java +++ /dev/null @@ -1,18 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.paper; - -import org.bukkit.plugin.java.JavaPlugin; -import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.meshlib.MeshLib; - -public class PaperPlugin extends JavaPlugin { - - @Override - public void onEnable() { - MeshLib.initialize(); - - PaperPlatformMain.setPlugin(this); - PaperPlatformLogging.setLogger(getLogger()); - - GithubResourcepackManager.initialize(); - } -} diff --git a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand b/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand deleted file mode 100644 index d8331b3..0000000 --- a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.paper.PaperPlatformCommand \ No newline at end of file diff --git a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging b/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging deleted file mode 100644 index f3f2b72..0000000 --- a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.paper.PaperPlatformLogging \ No newline at end of file diff --git a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain b/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain deleted file mode 100644 index 4f830cb..0000000 --- a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.paper.PaperPlatformMain \ No newline at end of file diff --git a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties b/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties deleted file mode 100644 index 744beda..0000000 --- a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.paper.PaperPlatformServerProperties \ No newline at end of file diff --git a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText b/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText deleted file mode 100644 index f27ec27..0000000 --- a/paper/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.paper.PaperPlatformText \ No newline at end of file diff --git a/paper/src/main/resources/plugin.yml b/paper/src/main/resources/plugin.yml deleted file mode 100644 index 2010759..0000000 --- a/paper/src/main/resources/plugin.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: Github-Resourcepack-Manager -version: '${modVersion}' -main: top.offsetmonkey538.githubresourcepackmanager.platform.paper.PaperPlugin -description: Modify server resourcepack from GitHub." -author: OffsetMonkey538 -website: https://github.com/OffsetMods538/Github-Resourcepack-Manager -api-version: '${lowestMinecraftVersion}' -load: STARTUP -prefix: GitHub Resourcepack Manager diff --git a/settings.gradle b/settings.gradle index e866fb5..51c75c7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,10 +9,19 @@ pluginManagement { } } -rootProject.name = "github-resourcepack-manager" +rootProject.name = "git-pack-manager" include "common" -include "fabric" -include "paper" +include "modded" +include "modded:1.21.1" + +include "loader:fabric" +include "loader:fabric:1.21.1" + +include "loader:neoforge" +include "loader:neoforge:1.21.1" + +include "loader:paper" +include "loader:paper:1.21.1"