From c524e320face936680877bbe0fdefb05aee91c25 Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Tue, 3 Nov 2026 11:04:38 -0500 Subject: [PATCH 1/2] update to the new build+release system --- .gitattributes | 77 ++++++++ .github/workflows/build_status.yml | 52 +++++ .github/workflows/publish.yml | 92 +++++++++ .gitignore | 11 +- build.gradle | 155 ++++++++++++--- gradle.properties | 42 +++- gradle/gradle-daemon-jvm.properties | 13 ++ gradle/verification.gradle | 33 +++- gradle/wrapper/gradle-wrapper.properties | 6 +- gradlew | 6 - gradlew.bat | 180 +++++++++--------- scripts/gui-smoke.init.gradle | 8 +- settings.gradle | 5 + .../resources/META-INF/neoforge.mods.toml | 37 ++-- update.json | 4 + 15 files changed, 555 insertions(+), 166 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/build_status.yml create mode 100644 .github/workflows/publish.yml create mode 100644 gradle/gradle-daemon-jvm.properties create mode 100644 update.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7086f8c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,77 @@ +# Store and check out repository text as LF on every platform. Windows command +# files are the sole exception below. +* text=auto eol=lf + +*.bat text eol=crlf +#*.bat text eol=lf +*.gradle text diff=java +mcmod.info text +*.java text +*.json text +*.lang text +*.mcmeta text +*.md text +*.properties text +gradlew text eol=lf +*.sh text eol=lf +*.txt text +*.xml text +*.yml text +*.yaml text +*.toml text + +# Documents +*.pdf binary + +# Graphics +*.eps binary +*.gif binary +*.ico binary +*.jpg binary +*.jpeg binary +*.png binary +*.psd binary +# SVG treated as an asset (binary) by default. If you want to treat it as text, +# comment-out the following line and uncomment the line after. +*.svg binary +#*.svg text +*.tif binary +*.tiff binary +*.webp binary +*.xcf binary + +# Other +*.exe binary +*.jar binary + +############################### +# Git Large File System (LFS) # +############################### + +# Archives +#*.7z filter=lfs diff=lfs merge=lfs -text +#*.br filter=lfs diff=lfs merge=lfs -text +#*.bz2 filter=lfs diff=lfs merge=lfs -text +#*.gz filter=lfs diff=lfs merge=lfs -text +#*.tar filter=lfs diff=lfs merge=lfs -text +#*.zip filter=lfs diff=lfs merge=lfs -text + +# Documents +#*.pdf filter=lfs diff=lfs merge=lfs -text + +# Graphics +#*.eps filter=lfs diff=lfs merge=lfs -text +#*.gif filter=lfs diff=lfs merge=lfs -text +#*.ico filter=lfs diff=lfs merge=lfs -text +#*.jpg filter=lfs diff=lfs merge=lfs -text +#*.jpeg filter=lfs diff=lfs merge=lfs -text +#*.png filter=lfs diff=lfs merge=lfs -text +#*.psd filter=lfs diff=lfs merge=lfs -text +#*.tif filter=lfs diff=lfs merge=lfs -text +#*.tiff filter=lfs diff=lfs merge=lfs -text +#*.webp filter=lfs diff=lfs merge=lfs -text +#*.xcf filter=lfs diff=lfs merge=lfs -text + +# Other +#*.exe filter=lfs diff=lfs merge=lfs -text +#*.jar filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.github/workflows/build_status.yml b/.github/workflows/build_status.yml new file mode 100644 index 0000000..0803185 --- /dev/null +++ b/.github/workflows/build_status.yml @@ -0,0 +1,52 @@ +name: Build and Test + +on: + push: + branches: [ '**' ] + pull_request: + +permissions: + contents: read + +concurrency: + group: build-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + JAVA_VERSION: 21 + +jobs: + build: + name: Build and test + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Check out source + uses: actions/checkout@v7 + + - name: Validate Gradle Wrapper + uses: gradle/actions/wrapper-validation@v6 + + - name: Set up JDK ${{ env.JAVA_VERSION }} + uses: actions/setup-java@v6 + with: + java-version: ${{ env.JAVA_VERSION }} + distribution: 'microsoft' + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v6 + + - name: Build and test + run: | + chmod +x ./gradlew + ./gradlew clean check build + + - name: Upload build artifacts + continue-on-error: true + uses: actions/upload-artifact@v7 + with: + name: ${{ github.event.repository.name }}-${{ github.sha }} + if-no-files-found: error + retention-days: 14 + path: | + build/libs/*.jar diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..913e426 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,92 @@ +name: Upload Release Artifacts + +on: + push: + tags: [ '*' ] + +permissions: + contents: read + +concurrency: + group: publish-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false # Never cancel a release halfway through uploading. + +env: + JAVA_VERSION: 21 + # Only booleans here, the secrets themselves are passed to the steps that need them. This is a preventative measure. + HAS_MAVEN: ${{ secrets.MAVEN_UPLOAD_URL != '' }} + HAS_CURSEFORGE: ${{ secrets.CURSEFORGE_TOKEN != '' }} + # The release tag ("modMajor.modMinor.modBug.minecraftVersionNoDots.loader" example: 1.2.1.121012) is used as-is for the mod version, build.gradle reads this. + TAG: ${{ github.ref_name }} + +jobs: + publish: + name: Build and Publish + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write # Required for mc-publish to create a GitHub release. + + steps: + - name: Check out source + uses: actions/checkout@v7 + + - name: Validate Gradle Wrapper + uses: gradle/actions/wrapper-validation@v6 + + - name: Set up JDK ${{ env.JAVA_VERSION }} + uses: actions/setup-java@v6 + with: + java-version: ${{ env.JAVA_VERSION }} + distribution: 'microsoft' + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v6 + + - name: Read gradle.properties + run: | + # Keeps the CurseForge ID defined in one place. + prop() { grep -E "^$1\s*=" gradle.properties | cut -d= -f2- | tr -d '[:space:]'; } + echo "CURSEFORGE_ID=$(prop curseforge_id)" >> "$GITHUB_ENV" + + - name: Build and test + run: | + chmod +x ./gradlew + ./gradlew clean check build + + - name: Publish to Maven + if: env.HAS_MAVEN == 'true' + run: ./gradlew publish + env: + MAVEN_UPLOAD_URL: ${{ secrets.MAVEN_UPLOAD_URL }} + MAVEN_UPLOAD_USERNAME: ${{ secrets.MAVEN_UPLOAD_USERNAME }} + MAVEN_UPLOAD_PASSWORD: ${{ secrets.MAVEN_UPLOAD_PASSWORD }} + + # For additional properties and values that are not detected/set up here, you can read up on the docs here: + # https://github.com/Kira-NT/mc-publish + # I HEAVILY advise you read about this action, as this action can do a lot but also has a few quirks like changelog publishing not working right. + - name: Publish to GitHub + uses: Kira-NT/mc-publish@v3.3.2 + with: + github-token: ${{ github.token }} + version: ${{ env.TAG }} + # Main jar first so it becomes the primary file, sources/javadoc attached as extras. + files: | + build/libs/!(*-@(sources|javadoc)).jar + build/libs/*-@(sources|javadoc).jar + + - name: Publish to CurseForge + if: env.HAS_CURSEFORGE == 'true' && env.CURSEFORGE_ID != '' + uses: Kira-NT/mc-publish@v3.3.2 + with: + curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} + # Read from curseforge_id in gradle.properties. + curseforge-id: ${{ env.CURSEFORGE_ID }} + version: ${{ env.TAG }} + files: | + build/libs/!(*-@(sources|javadoc)).jar + build/libs/*-@(sources|javadoc).jar + loaders: | + neoforge + environment: client | server + java: ${{ env.JAVA_VERSION }} diff --git a/.gitignore b/.gitignore index 43e9f6c..d0544dc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,19 @@ /.gradle/ /debugmods/ /.idea/ -/gradle/ /bin/ /.vscode/ /logs/ # Local tools (google-java-format for ./gradlew format) /tools/*.jar + +# Eclipse (generated by ./gradlew eclipse) +/.classpath +/.project +/.settings/ +/*.launch + +# Local Maven repository (publishAllPublicationsToLocalRepository) +/mcmodsrepo/ +/.eclipse/ diff --git a/build.gradle b/build.gradle index 9cb79d5..9c2641c 100644 --- a/build.gradle +++ b/build.gradle @@ -3,58 +3,142 @@ plugins { id 'eclipse' id 'idea' id 'maven-publish' - id 'net.neoforged.gradle.userdev' version '7.1.38' + id 'net.neoforged.moddev' version '2.0.148' } -version = '2.0.0-alpha' -group = 'zone.moddev.mc.lib' -base { archivesName = 'mmdlib' } -java.toolchain.languageVersion = JavaLanguageVersion.of(21) + +def env = System.getenv() +def tag = providers.environmentVariable('TAG') +version = tag.orElse("${mod_version}-development").get() +group = mod_group + +base { + archivesName = mod_archive_name +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(21) + withSourcesJar() + withJavadocJar() +} + repositories { mavenCentral() // JEI (optional integration; compile-time API only) maven { url = 'https://maven.blamejared.com' } } + +// Dev-only runtime dependencies: on the runtime classpath, but kept out of the published POM. +configurations { + localRuntime + runtimeClasspath.extendsFrom localRuntime +} + dependencies { - implementation 'net.neoforged:neoforge:21.1.247' // Optional JEI integration: compiled against the API only, never bundled or required. compileOnly "mezz.jei:jei-1.21.1-common-api:${jei_version}" compileOnly "mezz.jei:jei-1.21.1-neoforge-api:${jei_version}" // Dev runs include JEI; pass -PwithoutJei to launch without it. if (!providers.gradleProperty('withoutJei').isPresent()) { - runtimeOnly "mezz.jei:jei-1.21.1-neoforge:${jei_version}" + localRuntime "mezz.jei:jei-1.21.1-neoforge:${jei_version}" } } -runs { - configureEach { - workingDirectory project.layout.projectDirectory.dir('run') - modSource project.sourceSets.main + +neoForge { + version = neoforge_version + + parchment { + minecraftVersion = parchment_minecraft_version + mappingsVersion = parchment_mappings_version + } + + runs { + configureEach { + gameDirectory = file('run') + } + client { + client() + } + server { + server() + programArgument '--nogui' + } + data { + data() + systemProperty 'mmdlib.projectDir', project.layout.projectDirectory.asFile.absolutePath + systemProperty 'mmdlib.python', providers.gradleProperty('mmdlibPython').getOrElse('python3') + if (providers.gradleProperty('mmdlibMinecraftAssets').isPresent()) { + systemProperty 'mmdlib.minecraftAssets', providers.gradleProperty('mmdlibMinecraftAssets').get() + } + programArguments.addAll '--mod', mod_id, '--all', '--output', + file('src/generated/resources').absolutePath, '--existing', file('src/main/resources').absolutePath + } } - client {} - server { argument '--nogui' } - data { - systemProperty 'mmdlib.projectDir', project.layout.projectDirectory.asFile.absolutePath - systemProperty 'mmdlib.python', providers.gradleProperty('mmdlibPython').getOrElse('python3') - if (providers.gradleProperty('mmdlibMinecraftAssets').isPresent()) { - systemProperty 'mmdlib.minecraftAssets', providers.gradleProperty('mmdlibMinecraftAssets').get() + + mods { + "${mod_id}" { + sourceSet sourceSets.main } - arguments.addAll '--mod', 'mmdlib', '--all', '--output', - file('src/generated/resources').absolutePath, '--existing', file('src/main/resources').absolutePath } } + sourceSets { main.resources.srcDir 'src/generated/resources' main.resources.exclude '.cache/**' } -tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } -publishing { - publications { mavenJava(MavenPublication) { from components.java } } - repositories { maven { url = layout.projectDirectory.dir('mcmodsrepo') } } + +tasks.named('processResources', ProcessResources) { + def properties = [ + minecraft_version_range: minecraft_version_range, + neoforge_version_range: neoforge_version_range, + loader_version_range: loader_version_range, + jei_version_range: jei_version_range, + mod_id: mod_id, + mod_name: mod_name, + mod_license: mod_license, + mod_version: project.version, + mod_authors: mod_authors, + mod_description: mod_description, + display_url: display_url, + issue_url: issue_url, + update_url: update_url + ] + inputs.properties properties + filesMatching('META-INF/neoforge.mods.toml') { + expand properties + } } +tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } + tasks.withType(Jar).configureEach { manifest.attributes('Implementation-Version': project.version.toString()) } +publishing { + publications { + mavenJava(MavenPublication) { + artifactId = mod_archive_name + from components.java + } + } + repositories { + if (env.MAVEN_UPLOAD_URL) { + maven { + url = uri(env.MAVEN_UPLOAD_URL) + credentials { + username = env.MAVEN_UPLOAD_USERNAME + password = env.MAVEN_UPLOAD_PASSWORD + } + } + } + // Local inspection: `./gradlew publishAllPublicationsToLocalRepository` writes to mcmodsrepo/. + maven { + name = 'local' + url = layout.projectDirectory.dir('mcmodsrepo') + } + } +} + // runData launches from main output, so processResources necessarily precedes it. // Refresh that output after generation for combined invocations such as runData build. def refreshDataResources = tasks.register('refreshDataResources', Sync) { @@ -62,13 +146,11 @@ def refreshDataResources = tasks.register('refreshDataResources', Sync) { into tasks.named('processResources').map { it.destinationDir } duplicatesStrategy = DuplicatesStrategy.FAIL } +tasks.named('runData') { finalizedBy refreshDataResources } tasks.configureEach { task -> - if (task.name == 'runData') task.finalizedBy(refreshDataResources) - if (task.name == 'jar' || task.name == 'jarJar' || task.name == 'javadoc' || task.name.startsWith('verify') - || task.name.startsWith('test') - || task.name in ['compileTestJava', 'compileRuntimeTestJava', - 'writeMinecraftClasspathJunit', 'writeMinecraftClasspathClient', 'writeMinecraftClasspathServer', - 'runClient', 'runServer', 'runGameTestServer']) { + if (task.name in ['jar', 'sourcesJar', 'javadoc', 'compileTestJava', 'compileRuntimeTestJava', + 'runClient', 'runServer', 'runGameTestServer'] + || task.name.startsWith('verify') || task.name.startsWith('test')) { task.mustRunAfter(refreshDataResources) } } @@ -114,3 +196,16 @@ tasks.register('format') { logger.lifecycle("Formatted ${sources.files.size()} files. " + grouping.standardOutput.asText.get().trim()) } } + +idea { + module { + downloadSources = true + } +} + +eclipse { + classpath { + downloadJavadoc = true + downloadSources = true + } +} diff --git a/gradle.properties b/gradle.properties index 3300053..f9e5568 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,39 @@ -org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=false -org.gradle.configuration-cache=false +# Project +# Version string only used in dev, however when making a tag on github it should be like major.minor.bug.mc+loader +# Example: 2.0.0.121012 -> 2.0.0, then 1 21 01 for Minecraft 1.21.1, then 2 for NeoForge (1 is Forge). +mod_version = 2.0.0.121012 +mod_name = MMDLib +mod_id = mmdlib +mod_archive_name = mmdlib +mod_description = This mod provides a complete JSON-based configuration system and a means for mods to provide JSON-based descriptions of blocks and items to be loaded and registered. Hopefully, at some point, it will also include the ability to handle Tile/Block Entities and a lot of other special bits as well. +mod_group = zone.moddev.mc.lib +mod_authors = Cyanobacterium (Dr. Cyano), jriwanek, Mistress Curle, DShadowWolf, Gigaherz +mod_license = LGPL-2.1-only +# CurseForge project ID for release uploads; leave blank to skip CurseForge publishing. +curseforge_id = +display_url = https://github.com/MinecraftModDevelopmentMods/MMDLib +update_url = https://raw.githubusercontent.com/MinecraftModDevelopmentMods/MMDLib/refs/heads/master-2.0-1.21.1-neo/update.json +issue_url = https://github.com/MinecraftModDevelopmentMods/MMDLib/issues + +# Dependencies +minecraft_version = 1.21.1 +minecraft_version_range = [1.21.1,1.21.2) +neoforge_version = 21.1.247 +neoforge_version_range = [21.1.247,21.2) +loader_version_range = [3,) + +# Parchment (parameter names + javadocs on top of Mojang mappings) +# Versions: https://parchmentmc.org/docs/getting-started +parchment_minecraft_version = 1.21.1 +parchment_mappings_version = 2024.11.17 + # Optional JEI integration (compile-time API; dev runtime only) -jei_version=19.57.0.451 +jei_version = 19.57.0.451 +jei_version_range = [19,) + +# Gradle +org.gradle.jvmargs = -Xmx3G +org.gradle.daemon = false +org.gradle.configuration-cache = false +org.gradle.caching = true +org.gradle.parallel = false diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 0000000..1f3ebb8 --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/94dcf80ef6d7f8ebba6aa801e7f908e7/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/7dd80dacefbd7589c9e69d11aaf5a3f6/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/94dcf80ef6d7f8ebba6aa801e7f908e7/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/7dd80dacefbd7589c9e69d11aaf5a3f6/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c3c29437af65f2043571384cb538fa79/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/277b468367efd3c295b1db6bb6841e6e/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/94dcf80ef6d7f8ebba6aa801e7f908e7/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/7dd80dacefbd7589c9e69d11aaf5a3f6/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1bc4262b416455add8bcc03444549614/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/0a8069b872b7b97d7cbf8627b652bdc3/redirect +toolchainVendor=MICROSOFT +toolchainVersion=21 diff --git a/gradle/verification.gradle b/gradle/verification.gradle index 05c018e..1c9a19a 100644 --- a/gradle/verification.gradle +++ b/gradle/verification.gradle @@ -1,6 +1,9 @@ // Verification: the JUnit tests, the runtimeTest source set and every verify* task. // All of it runs on `./gradlew check`; `build` only compiles and packages (see the end of this file). +// MDG only puts Minecraft on main by default; the verification source sets build on test's classpath. +neoForge.addModdingDependenciesTo(sourceSets.test) + sourceSets { runtimeTest { compileClasspath += sourceSets.main.output + sourceSets.test.compileClasspath @@ -13,16 +16,30 @@ tasks.named('check') { dependsOn tasks.named('compileRuntimeTestJava') } // `check`. The run loads main and runtimeTest as one mod, in a fresh world each time, and exits // with the number of failed tests. Release jars never contain runtimeTest (verifyReleaseClasses). def gameTestDir = layout.buildDirectory.dir('gametest') -runs { - gameTestServer { - workingDirectory gameTestDir - modSource project.sourceSets.runtimeTest - systemProperty 'neoforge.enabledGameTestNamespaces', 'mmdlib' +neoForge { + // Game tests are found by scanning the mod's own files, so this run loads mmdlib from main and + // runtimeTest together; the other runs load only the release mod (main). + mods { + mmdlib_gametest { + sourceSet sourceSets.main + sourceSet sourceSets.runtimeTest + } } + runs { + gameTestServer { + type = 'gameTestServer' + gameDirectory = gameTestDir + sourceSet = sourceSets.runtimeTest + loadedMods = [neoForge.mods.mmdlib_gametest] + systemProperty 'neoforge.enabledGameTestNamespaces', 'mmdlib' + } + } +} +neoForge.runs.matching { it.name != 'gameTestServer' }.configureEach { + loadedMods = [neoForge.mods.named(mod_id).get()] } -// NeoGradle creates the run task late, so it is found by name. -def gameTestTask = tasks.matching { it.name == 'runGameTestServer' } -gameTestTask.configureEach { it.doFirst { project.delete(gameTestDir); gameTestDir.get().asFile.mkdirs() } } +def gameTestTask = tasks.named('runGameTestServer') +gameTestTask.configure { it.doFirst { project.delete(gameTestDir); gameTestDir.get().asFile.mkdirs() } } tasks.named('check') { dependsOn gameTestTask } // Unit tests in src/test: JUnit 5, no running game. `./gradlew test` (part of `check`). diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 407030f..98f6dc4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ -#Sat Oct 24 08:13:16 EDT 2026 +#Wed Aug 05 02:24:10 BST 2026 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.2-bin.zip +distributionSha256Sum=21bd5da2de49b642904077f38c0efdc7778e92b3ed0889392dca61b97e389607 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=9c0f7faeeb306cb14e4279a3e084ca6b596894089a0638e68a07c945a32c9e14 diff --git a/gradlew b/gradlew index a69d9cb..1b6c787 100755 --- a/gradlew +++ b/gradlew @@ -205,12 +205,6 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 53a6b23..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,91 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/scripts/gui-smoke.init.gradle b/scripts/gui-smoke.init.gradle index b4fc49d..2447662 100644 --- a/scripts/gui-smoke.init.gradle +++ b/scripts/gui-smoke.init.gradle @@ -3,15 +3,15 @@ // Run a normal build afterward to restore main output without the injected smoke classes. allprojects { afterEvaluate { - if (extensions.findByName('runs') != null) { + if (extensions.findByName('neoForge') != null) { sourceSets.main.java.srcDir(file('src/runtimeTest/java')) sourceSets.main.resources.srcDir(file('src/runtimeTest/resources')) def smokeDir = file(providers.gradleProperty('withoutJei').isPresent() ? 'build/gui-smoke-without-jei' : 'build/gui-smoke') smokeDir.mkdirs() new File(smokeDir, 'options.txt').text = 'guiScale:2\nmaxFps:30\nonboardAccessibility:false\n' - runs.client.arguments.addAll '--width', '1280', '--height', '960' - runs.client.workingDirectory = smokeDir - runs.client.systemProperty 'mmdlib.guiSmoke', 'true' + neoForge.runs.client.programArguments.addAll '--width', '1280', '--height', '960' + neoForge.runs.client.gameDirectory = smokeDir + neoForge.runs.client.systemProperty 'mmdlib.guiSmoke', 'true' tasks.configureEach { task -> if (task.name == 'runClient') task.doLast { def report = new File(smokeDir, 'logs/latest.log') diff --git a/settings.gradle b/settings.gradle index cd3fed7..aee25d0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,4 +4,9 @@ pluginManagement { maven { url = 'https://maven.neoforged.net/releases' } } } + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + rootProject.name = 'MMDLib' diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index d7bfd26..7281966 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -6,32 +6,31 @@ # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml modLoader="javafml" #mandatory # A version range to match for said mod loader - for regular FML @Mod it will be the forge version -loaderVersion="[3,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. +loaderVersion="${loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. # The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. # Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. -license="LGPL-2.1-only" +license="${mod_license}" # A URL to refer people to when problems occur with this mod -#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +issueTrackerURL="${issue_url}" #optional # A list of mods - how many allowed here is determined by the individual mod loader [[mods]] #mandatory # The modid of the mod -modId="mmdlib" #mandatory -# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it -# ${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata -# see the associated build.gradle script for how to populate this completely automatically during a build -version="${file.jarVersion}" #mandatory +modId="${mod_id}" #mandatory +# The version number of the mod +# Filled in from the build version (the release tag on CI) by processResources in build.gradle +version="${mod_version}" #mandatory # A display name for the mod -displayName="MMDLib" #mandatory +displayName="${mod_name}" #mandatory # A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/ -#updateJSONURL="https://change.me.example.invalid/updates.json" #optional +updateJSONURL="${update_url}" #optional # A URL for the "homepage" for this mod, displayed in the mod UI -#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional +displayURL="${display_url}" #optional # A file name (in the root of the mod JAR) containing a logo for display logoFile="examplemod.png" #optional # A text field displayed in the mod UI credits="" #optional # A text field displayed in the mod UI -authors="Cyanobacterium (Dr. Cyano), jriwanek, Mistress Curle, DShadowWolf, Gigaherz" #optional +authors="${mod_authors}" #optional # Display Test controls the display for your mod in the server connection screen # MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. # IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. @@ -42,32 +41,32 @@ authors="Cyanobacterium (Dr. Cyano), jriwanek, Mistress Curle, DShadowWolf, Giga # The description text for the mod (multi line!) (#mandatory) description=''' -This mod provides a complete JSON-based configuration system and a means for mods to provide JSON-based descriptions of blocks and items to be loaded and registered. Hopefully, at some point, it will also include the ability to handle Tile/Block Entities and a lot of other special bits as well. +${mod_description} ''' # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. -[[dependencies.mmdlib]] #optional +[[dependencies.${mod_id}]] #optional # the modid of the dependency modId="neoforge" #mandatory # Does this dependency have to exist - if not, ordering below must be specified type="required" #mandatory # The version range of the dependency - versionRange="[21.1.247,21.2)" #mandatory + versionRange="${neoforge_version_range}" #mandatory # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory ordering="NONE" # Side this dependency is applied on - BOTH, CLIENT or SERVER side="BOTH" # Here's another dependency -[[dependencies.mmdlib]] +[[dependencies.${mod_id}]] modId="minecraft" type="required" # This version range declares a minimum of the current minecraft version up to but not including the next major version - versionRange="[1.21.1,1.21.2)" + versionRange="${minecraft_version_range}" ordering="NONE" side="BOTH" # Optional: JEI shows crackhammer crushing recipes when installed; never required. -[[dependencies.mmdlib]] +[[dependencies.${mod_id}]] modId="jei" type="optional" - versionRange="[19,)" + versionRange="${jei_version_range}" ordering="NONE" side="CLIENT" diff --git a/update.json b/update.json new file mode 100644 index 0000000..542fe28 --- /dev/null +++ b/update.json @@ -0,0 +1,4 @@ +{ + "homepage": "https://github.com/MinecraftModDevelopmentMods/MMDLib", + "promos": {} +} From ef646594d5fe19817e908c5413cb8252454cf9ba Mon Sep 17 00:00:00 2001 From: Daniel Hazelton Date: Tue, 3 Nov 2026 11:33:36 -0500 Subject: [PATCH 2/2] fix some issues that were breaking CI/CD --- gradle/verification.gradle | 8 +++++++- scripts/validate_static_assets.py | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/gradle/verification.gradle b/gradle/verification.gradle index 1c9a19a..b225283 100644 --- a/gradle/verification.gradle +++ b/gradle/verification.gradle @@ -82,10 +82,16 @@ def verifyMachineImports = tasks.register('verifyMachineImports') { } tasks.named('check') { dependsOn verifyMachineImports } +// Model parents and textures may point into vanilla or NeoForge, so the script also reads their jars. +def minecraftArtifacts = tasks.named('createMinecraftArtifacts') def verifyStaticAssets = tasks.register('verifyStaticAssets', Exec) { group = 'verification' - dependsOn tasks.named('classes') + dependsOn tasks.named('classes'), minecraftArtifacts + def minecraftJar = minecraftArtifacts.flatMap { it.resourcesArtifact } + def neoforgeJar = minecraftArtifacts.flatMap { it.compiledArtifact } + inputs.files(minecraftJar, neoforgeJar) commandLine providers.gradleProperty('mmdlibPython').getOrElse('python3'), file('scripts/validate_static_assets.py').absolutePath + argumentProviders.add({ [minecraftJar.get().asFile.absolutePath, neoforgeJar.get().asFile.absolutePath] } as CommandLineArgumentProvider) } tasks.named('check') { dependsOn verifyStaticAssets } diff --git a/scripts/validate_static_assets.py b/scripts/validate_static_assets.py index bbc142b..2e10596 100644 --- a/scripts/validate_static_assets.py +++ b/scripts/validate_static_assets.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 """Validate static model dependencies, overrides, textures, and animation metadata without launching a GUI.""" -import json,struct,zipfile +import json,struct,sys,zipfile from pathlib import Path +# Usage: validate_static_assets.py (passed by ./gradlew verifyStaticAssets) def main(): repo=Path(__file__).resolve().parents[1];root=repo/'src/main/resources';assets=root/'assets/mmdlib' - jars={name:zipfile.ZipFile(repo/path) for name,path in {'minecraft':'.gradle/repositories/ng_dummy_ng/net/minecraft/client/1.21.1/client-1.21.1-client-extra.jar','neoforge':'.gradle/repositories/ng_dummy_ng/net/neoforged/neoforge/21.1.247/neoforge-21.1.247.jar'}.items()} + if len(sys.argv)!=3:sys.exit('usage: validate_static_assets.py ') + jars={name:zipfile.ZipFile(path) for name,path in zip(('minecraft','neoforge'),sys.argv[1:])} def parts(ref):return ref.split(':',1) if ':' in ref else ('minecraft',ref) def read_model(ref): ns,path=parts(ref)