diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4ffbd90 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# This is the universal Text Editor Configuration +# for all GTNewHorizons projects +# See: https://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{bat,ini}] +end_of_line = crlf + +[*.{dtd,json,info,mcmeta,md,sh,svg,xml,xsd,xsl,yaml,yml}] +indent_size = 2 + +[*.lang] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes index dfe0770..fd2792b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,44 @@ -# Auto detect text files and perform LF normalization -* text=auto +* text eol=lf + +*.[jJ][aA][rR] binary + +*.[pP][nN][gG] binary +*.[jJ][pP][gG] binary +*.[jJ][pP][eE][gG] binary +*.[gG][iI][fF] binary +*.[tT][iI][fF] binary +*.[tT][iI][fF][fF] binary +*.[iI][cC][oO] binary +*.[sS][vV][gG] text +*.[eE][pP][sS] binary +*.[xX][cC][fF] binary + +*.[kK][aA][rR] binary +*.[mM]4[aA] binary +*.[mM][iI][dD] binary +*.[mM][iI][dD][iI] binary +*.[mM][pP]3 binary +*.[oO][gG][gG] binary +*.[rR][aA] binary + +*.7[zZ] binary +*.[gG][zZ] binary +*.[tT][aA][rR] binary +*.[tT][gG][zZ] binary +*.[zZ][iI][pP] binary + +*.[tT][cC][nN] binary +*.[sS][oO] binary +*.[dD][lL][lL] binary +*.[dD][yY][lL][iI][bB] binary +*.[pP][sS][dD] binary +*.[tT][tT][fF] binary +*.[oO][tT][fF] binary + +*.[pP][aA][tT][cC][hH] -text + +*.[bB][aA][tT] text eol=crlf +*.[cC][mM][dD] text eol=crlf +*.[pP][sS]1 text eol=crlf + +*[aA][uU][tT][oO][gG][eE][nN][eE][rR][aA][tT][eE][dD]* binary diff --git a/.github/scripts/update_version b/.github/scripts/update_version new file mode 100644 index 0000000..3e5f752 --- /dev/null +++ b/.github/scripts/update_version @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +if ! git diff --name-only HEAD HEAD~1 | grep -qF 'build.gradle'; then + new_version="$(date +%s)" + sed --in-place "s!^//version:.*!//version: $new_version!g" build.gradle + git add build.gradle + git commit -m "[ci skip] update build script version to $new_version" + git push + printf 'Updated buildscript version to %s\n' "$new_version" +else + printf 'Ignored buildscript version update: no changes detected\n' +fi diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..3ee2f68 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,13 @@ + +name: Build and test + +on: + pull_request: + branches: [ master, main ] + push: + branches: [ master, main ] + +jobs: + build-and-test: + uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master + secrets: inherit diff --git a/.github/workflows/buildscript-maintenance.yml b/.github/workflows/buildscript-maintenance.yml new file mode 100644 index 0000000..a9d5486 --- /dev/null +++ b/.github/workflows/buildscript-maintenance.yml @@ -0,0 +1,39 @@ +# This workflow only meant for the source mod you shouldn't bring it into your own project + +name: Buildscript maintenance + +on: + push: + branches: [ master, main ] + paths: + - build.gradle + workflow_dispatch: { } + +jobs: + buildscript-maintenance: + runs-on: ubuntu-latest + # Avoid running this workflow on forks + if: github.repository == 'GTNewHorizons/ExampleMod1.7.10' + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Ensure build script version is up to date + id: update-buildscript + run: | + new_version="$(date +%s)" + sed --in-place "1s!^//version:.*!//version: $new_version!g" build.gradle + echo "::set-output name=new_version::$new_version" + + - name: Create Pull Request + id: create-pull-request + uses: peter-evans/create-pull-request@v4 + with: + add-paths: build.gradle + commit-message: '[ci skip] update build script version to ${{ steps.update-buildscript.outputs.new_version }}' + branch: buildscript-maintenance + title: Update build script version to ${{ steps.update-buildscript.outputs.new_version }} + body: This pull request is created by the buildscript-maintenance workflow diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml new file mode 100644 index 0000000..9a1f6bc --- /dev/null +++ b/.github/workflows/release-packages.yml @@ -0,0 +1,86 @@ +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Release latest starter and migration packages + +on: + push: + branches: [ master, main ] + tags: [ '*' ] + +jobs: + zip-and-release-packages: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set release version + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Prepare release folder + run: | + mkdir packages + + - name: Zip starter + uses: montudor/action-zip@v1 + with: + args: > + zip -r packages/starter.zip . \ + \ + -x "*.git/*" \ + \ + -x ".github/scripts/update_version" \ + -x ".github/workflows/buildscript-maintenance.yml" \ + -x ".github/workflows/release-packages.yml" \ + \ + -x "*docs/*" \ + -x "README.md" \ + -x "LICENSE" \ + -x "gtnhShared/*" \ + \ + -x "packages/" \ + -x "packages/starter.zip" \ + -x "packages/migration.zip" + + - name: Zip migration + uses: montudor/action-zip@v1 + with: + args: > + zip -r packages/migration.zip . \ + \ + -x "*.git/*" \ + \ + -x ".github/scripts/update_version" \ + -x ".github/workflows/buildscript-maintenance.yml" \ + -x ".github/workflows/release-packages.yml" \ + \ + -x "*docs/*" \ + -x "*src/*" \ + -x "LICENSE" \ + -x "README.md" \ + -x "gtnhShared/*" \ + \ + -x "packages/" \ + -x "packages/starter.zip" \ + -x "packages/migration.zip" + + - name: Update latest tag + uses: "marvinpinto/action-automatic-releases@latest" + if: ${{ (github.ref == 'master' || github.ref == 'main') }} + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "latest-packages" + prerelease: true + title: "Latest starter and migration packages" + files: packages/* + + - name: Release named version + if: ${{ !(github.ref == 'master' || github.ref == 'main') }} + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "${{ env.RELEASE_VERSION }}-packages" + prerelease: false + title: "${{ env.RELEASE_VERSION }} starter and migration packages" + files: packages/* diff --git a/.github/workflows/release-tags.yml b/.github/workflows/release-tags.yml new file mode 100644 index 0000000..e4c0be6 --- /dev/null +++ b/.github/workflows/release-tags.yml @@ -0,0 +1,14 @@ + +name: Release tagged build + +on: + push: + tags: [ '*' ] + +permissions: + contents: write + +jobs: + release-tags: + uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/release-tags.yml@master + secrets: inherit diff --git a/.gitignore b/.gitignore index f87f9c2..5e80e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,38 @@ -# Personal Directory and Files - .gradle -.idea -build -eclipse +.settings +/.idea/ +/.vscode/ +/run/ +/build/ +/eclipse/ .classpath .project -.settings -bin -.metadata -*.bak -classes +/bin/ +/config/ +/crash-reports/ +/logs/ +options.txt +/saves/ +usernamecache.json +banned-ips.json +banned-players.json +eula.txt +ops.json +server.properties +servers.dat +usercache.json +whitelist.json +/out/ +*.iml +*.ipr +*.iws +src/main/resources/mixins.*([!.]).json +*.bat +*.DS_Store +!gradlew.bat +.factorypath +addon.local.gradle +addon.local.gradle.kts +addon.late.local.gradle +addon.late.local.gradle.kts +layout.json diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..a6b5f68 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +# Any Github changes require admin approval +/.github/** @GTNewHorizons/admin + diff --git a/LICENSE b/LICENSE index ecc1c58..462f920 100644 --- a/LICENSE +++ b/LICENSE @@ -1,54 +1,21 @@ -CustomNPC+ is distributed under a reasonably open license. You probably want to have a read through this if you want to do something with it other than just play. +MIT License -You are completely free and have the right to Use, Share and Adapt the mod. These rights can not be removed from you as long as follow the license terms. -If you simply wish to play with the mod and do nothing else, go for it, this license is of no use to you. +Copyright (c) 2021 Johann Bernhardt -TL;DR -> You CAN --- Fork and modify the code. --- Submit Pull Requests to this repository. --- Copy portions of this code for use in other projects. --- Write your own code that uses this code as a dependency. (addon mods!) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -> You CANNOT --- Redistribute this in its entirety as source or compiled code. --- Create or distribute code which contains 50% or more Functionally Equivalent Statements* from this repository. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -> You MUST --- Maintain a visible repository of your code which is inspired by, derived from, or copied from this code. Basically, if you use it, pay it forward. You keep rights to your OWN code, but you still must make your source visible. - -General Clauses -Clauses that apply in general to any of the uses that follow. -- Personal Permission Clause - I will only give personal permission if I, KAMKEEL, am directly asked. If personal permission is given this does not imply endorsement. -- Waive Clause - All restrictive clauses in this license can be ignored with personal permission. Read the above clause. -- Extensive Clause - This license applies to CustomNPC+, the CustomNPC+ website, the legacy CustomNPC+ website and all other code, assets or binaries found on this website or the github repository unless otherwise indicated. -- Informal Clause - This license will not hold up in court. I have no intention of suing anyone for breaking it, the worst that can happen to you if you do is that I'll get annoyed and ask you to rectify your mistake. If you use a public distribution platform (such as CurseForge) it's also possible your project will get taken down. Breaking the license also makes you a jerk, and you don't want to be a jerk, do you? - -If you want to Distribute CustomNPC+ -"Distribution" refers to making available binaries, assets, or source of the mod available from the original sources as part of your modpack or otherwise. -- Attribution Clause - You must give appropriate credit to KAMKEEL as the creator of CustomNPC+ or the parts of it you're using. Noppes can also be credit as the original Mod Author. If you do any alterations the fact that you do so should also be indicated. A link back is optional but it would be cool if you would do so. -- Redistribution Clause - You may not redistribute the enitirity of the source code provided, unless heavy modifications are made to it as a whole. More than 50% of the code cannot be redistributed unless direct permission is provided from either KAMKEEL or Noppes. -- Non-Monetary Clause - You may not charge for access to the distribution itself or gain money through it, this includes any type of inline advertisement, such as url shorteners (adf.ly or otherwise) or ads in your service slowing the download down. This includes restricting any amount of access behind a paywall. Charging for ingame goods such as mod items or cosmetic features on a server does not count as distribution and falls purely under the Mojang Terms of Service. - -If you want to Feature CustomNPC+ -"Featuring" refers to using CustomNPC+ in an environment where you do not distribute binaries, assets, or source. An example would be a YouTube Let's Play. -- Thief Clause - You must not claim that you made CustomNPC+. Giving appropriate credit to KAMKEEL as the creator of CustomNPC+ makes you cooler, but you don't have to do it if you don't want to. - -If you want to Use CustomNPC+ Code or Assets -Usage of code or assets falls under the Extensive Clause. -- External Clause - Certain parts of the codebase are indicated to be licensed under alternative licenses. You must obey those licenses. -- Attribution Clause - You must give appropriate credit to KAMKEEL as the creator of CustomNPC+ or the parts of it you're using. Noppes can also be credit as the original Mod Author. If you do any alterations the fact that you do so should also be indicated. A link back is optional but it would be cool if you would do so. -- Copyleft Clause - Your project must be open source (have its source visible and allow for redistribution and modification) and include a clause similar to this one in its license. -- API Clause - Any of the other clauses under this section do not apply to any CustomNPC+ API code (the subdirectory src/api or https://github.com/KAMKEEL/CustomNPC-Plus-API). However, if CustomNPC+ API code is used, it must be included verbatim as it was obtained. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE-template b/LICENSE-template new file mode 100644 index 0000000..242da62 --- /dev/null +++ b/LICENSE-template @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index d040bf5..b389db4 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,80 @@ +# Example Forge Mod for Minecraft 1.7.10 -## 👋 Welcome to the CustomNPC+ Gecko Addon Repository. +[![](https://jitpack.io/v/GTNewHorizons/ExampleMod1.7.10.svg)](https://jitpack.io/#GTNewHorizons/ExampleMod1.7.10) +[![](https://github.com/GTNewHorizons/ExampleMod1.7.10/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/GTNewHorizons/ExampleMod1.7.10/actions/workflows/build-and-test.yml) ----------------- +An example mod for Minecraft 1.7.10 with Forge focussed on a stable, updatable setup. - - Support Me +### Motivation -[![Download CustomNPC+](https://img.shields.io/badge/CustomNPC+-0081CB?style=for-the-badge&logo=material-ui&logoColor=white)](https://modrinth.com/mod/customnpc-plus) -[![Download MPM+](https://img.shields.io/badge/MorePlayerModels+-0081CB?style=for-the-badge&logo=material-ui&logoColor=white)](https://www.curseforge.com/minecraft/mc-mods/moreplayermodels-plus) -[![Download PluginMod](https://img.shields.io/badge/Plugin_Mod-0081CB?style=for-the-badge&logo=material-ui&logoColor=white)](https://github.com/KAMKEEL/Plugin-Mod) +We had our fair share in struggles with build scripts for Minecraft Forge. There are quite a few pitfalls from non-obvious error messages. This Example Project provides you a build system you can adapt to over 90% of Minecraft Forge mods and can easily be updated if need be. ----------------- +### Help! I'm stuck! -### ⬇️ Downloads -- **Modrinth**: [NONE]() -- **CurseForge**: [NONE]() +We all have been there! Check out our [FAQ](https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/docs/FAQ.md). If that doesn't help, please open an issue. -### 🔹 Installation -This mod is an addon mod to **CustomNPC+**. It requires both **GeckoLib** and **CustomNPC+** to be installed alongside it. +### Getting started -## Cloning / Compiling / Building +Creating mod from scratch: +1. Unzip [project starter](https://github.com/GTNewHorizons/ExampleMod1.7.10/releases/download/master-packages/starter.zip) into project directory. +2. Replace placeholders in LICENSE-template and rename it to LICENSE, or remove LICENSE-template and put any other license you like on your code. This is an permissive OSS project and we encourage you participate in OSS movement by having permissive license like one in template. You can find out pros and cons of OSS software in [this article](https://www.freecodecamp.org/news/what-is-great-about-developing-open-source-and-what-is-not/) +3. Ensure your project is under VCS. For example initialise git repository by running `git init; git commit --message "initialized repository"`. +4. Replace placeholders (edit values in gradle.properties, change example package and class names, etc.) +5. Run `./gradlew build` +6. Make sure to check out the rest sections of this file. +7. You are good to go! -Using IntelliJ, take the following steps: -1. Run `setupDecompWorkspace` under the `forgegradle` tab. -2. Refresh Gradle. -3. Run `genIntellijRuns` under the `other` tab to set up your Minecraft runs. +We also have described guidelines for existing mod [migration](docs/migration.md) and [porting](docs/porting.md) -or for Eclipse, run `gradlew setupDecompWorkspace eclipse` then import the project. +### Features + + - Updatable: Replace [`build.gradle`](https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/build.gradle) with a newer version + - Optional API artifact (.jar) + - Optional version replacement in Java files + - Optional shadowing of dependencies + - Simplified setup of Mixin and example + - Scala support (add sources under `src/main/scala/` instead of `src/main/java/`) + - Optional named developer account for consistent player progression during testing + - Boilerplate forge mod as starting point + - Improved warnings for pitfalls + - Git Tags integration for versioning + - [Jitpack](https://jitpack.io) CI + - GitHub CI: + - Releasing your artifacts on new tags pushed. Push git tag named after version (e.g. 1.0.0) which will trigger a release of artifacts with according names. + - Running smoke test for server startup. On any server crash occurring workflow will fail and print the crash log. + +### Files + - [`build.gradle`](https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/build.gradle): This is the core script of the build process. You should not need to tamper with it, unless you are trying to accomplish something out of the ordinary. __Do not touch this file! You will make a future update near impossible if you do so!__ + - [`gradle.properties`](https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/gradle.properties): The core configuration file. It includes + - [`dependencies.gradle[.kts]`](https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/dependencies.gradle): Add your mod's dependencies in this file. This is separate from the main build script, so you may replace the [`build.gradle`](https://github.com/SinTh0r4s/ExampleMod1.7.10/blob/main/build.gradle) if an update is available. + - [`repositories.gradle[.kts]`](https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/repositories.gradle): Add your dependencies' repositories. This is separate from the main build script, so you may replace the [`build.gradle`](https://github.com/SinTh0r4s/ExampleMod1.7.10/blob/main/build.gradle) if an update is available. + - [`jitpack.yml`](https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/jitpack.yml): Ensures that your mod is available as import over [Jitpack](https://jitpack.io). + - [`.github/workflows/gradle.yml`](https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/.github/workflows/gradle.yml): A simple CI script that will build your mod any time it is pushed to `master` or `main` and publish the result as release in your repository. This feature is free with GitHub if your repository is public. + +### Forge's Access Transformers + +You may activate Forge's Access Transformers by defining a configuration file in `gradle.properties`. + +Check out the [`example-access-transformers`](https://github.com/GTNewHorizons/ExampleMod1.7.10/tree/example-access-transformers) brach for a working example! + +__Warning:__ Access Transformers are bugged and will deny you any sources for the decompiled minecraft! Your development environment will still work, but you might face some inconveniences. For example, IntelliJ will not permit searches in dependencies without attached sources. + +### Mixins + +Mixins are usually used to modify vanilla or mod/library in runtime without having to change source code. For example, redirect a call, change visibility or make class implement your interface. It's an advanced topic and most mods don't need to do that. + +You can activate Mixin in 'gradle.properties'. In that case a mixin configuration (usually named `mixins.mymodid.json`) will be generated automatically, and you only have to write the mixins itself. Dependencies are handled as well. +Take a look at the examples in [Hodgepodge](https://github.com/GTNewHorizons/Hodgepodge/) and [Angelica](https://github.com/GTNewHorizons/Angelica/pull/8). + +### Advanced + +If your project requires custom gradle commands you may add a `addon.gradle[.kts]` to your project. It will be added automatically to the build script. Although we recommend against it, it is sometimes required. When in doubt, feel free to ask us about it. You may break future updates of this build system! +If you need access to properties modified later in the buildscript, you can also use a `addon.late.gradle[.kts]`. +For local tweaks that you don't want to commit to Git, like adding extra JVM arguments for testing, use `addon[.late].local.gradle[.kts]`. + +### Feedback wanted + +If you tried out this build script we would love to head your opinion! Is there any feature missing for you? Did something not work? Please open an issue and we will try to resolve it asap! + +Happy modding, \ +[SinTh0r4s](https://github.com/SinTh0r4s), [TheElan](https://github.com/TheElan) and [basdxz](https://github.com/basdxz) diff --git a/build.gradle b/build.gradle index f066e35..6274cb2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,81 +1,1624 @@ +//version: 1705357285 +/* + DO NOT CHANGE THIS FILE! + Also, you may replace this file at any time if there is an update available. + Please check https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/master/build.gradle for updates. + */ + + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.gtnewhorizons.retrofuturagradle.ObfuscationAttribute +import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar +import com.gtnewhorizons.retrofuturagradle.minecraft.RunMinecraftTask +import com.gtnewhorizons.retrofuturagradle.util.Distribution +import com.matthewprenger.cursegradle.CurseArtifact +import com.matthewprenger.cursegradle.CurseRelation +import com.modrinth.minotaur.dependencies.ModDependency +import com.modrinth.minotaur.dependencies.VersionDependency +import org.gradle.internal.logging.text.StyledTextOutput.Style +import org.gradle.internal.logging.text.StyledTextOutputFactory +import org.gradle.internal.xml.XmlTransformer +import org.jetbrains.gradle.ext.Application +import org.jetbrains.gradle.ext.Gradle + +import javax.inject.Inject +import java.nio.file.Files +import java.nio.file.Paths +import java.util.concurrent.TimeUnit + buildscript { repositories { - mavenCentral() maven { - name = "forge" - url = "https://maven.minecraftforge.net/" - } - maven { - name = "sonatype" - url = "https://oss.sonatype.org/content/repositories/snapshots/" - } - maven { - name = "jitpack" - url = "https://jitpack.io" - } - maven { - name = "gt" - url = "https://gregtech.mechaenetia.com/" + // GTNH RetroFuturaGradle and ASM Fork + name "GTNH Maven" + url "https://nexus.gtnewhorizons.com/repository/public/" } + mavenLocal() } - dependencies { - classpath 'com.github.GTNewHorizons:ForgeGradle:1.2.11' +} +plugins { + id 'java-library' + id "org.jetbrains.gradle.plugin.idea-ext" version "1.1.7" + id 'eclipse' + id 'scala' + id 'maven-publish' + id 'org.jetbrains.kotlin.jvm' version '1.8.0' apply false + id 'org.jetbrains.kotlin.kapt' version '1.8.0' apply false + id 'com.google.devtools.ksp' version '1.8.0-1.0.9' apply false + id 'org.ajoberstar.grgit' version '4.1.1' // 4.1.1 is the last jvm8 supporting version, unused, available for addon.gradle + id 'com.github.johnrengelman.shadow' version '8.1.1' apply false + id 'com.palantir.git-version' version '3.0.0' apply false + id 'de.undercouch.download' version '5.5.0' + id 'com.github.gmazzo.buildconfig' version '3.1.0' apply false // Unused, available for addon.gradle + id 'com.diffplug.spotless' version '6.13.0' apply false // 6.13.0 is the last jvm8 supporting version + id 'com.modrinth.minotaur' version '2.+' apply false + id 'com.matthewprenger.cursegradle' version '1.4.0' apply false + id 'com.gtnewhorizons.retrofuturagradle' version '1.3.27' +} + +print("You might want to check out './gradlew :faq' if your build fails.\n") + +boolean settingsupdated = verifySettingsGradle() +settingsupdated = verifyGitAttributes() || settingsupdated +if (settingsupdated) + throw new GradleException("Settings has been updated, please re-run task.") + +// In submodules, .git is a file pointing to the real git dir +if (project.file('.git/HEAD').isFile() || project.file('.git').isFile()) { + apply plugin: 'com.palantir.git-version' +} + +def out = services.get(StyledTextOutputFactory).create('an-output') + +def projectJavaVersion = JavaLanguageVersion.of(8) + +boolean disableSpotless = project.hasProperty("disableSpotless") ? project.disableSpotless.toBoolean() : false +boolean disableCheckstyle = project.hasProperty("disableCheckstyle") ? project.disableCheckstyle.toBoolean() : false + +final String CHECKSTYLE_CONFIG = """ + + + + + + + + + + + +""" + +checkPropertyExists("modName") +checkPropertyExists("modId") +checkPropertyExists("modGroup") +checkPropertyExists("autoUpdateBuildScript") +checkPropertyExists("minecraftVersion") +checkPropertyExists("forgeVersion") +checkPropertyExists("replaceGradleTokenInFile") +checkPropertyExists("gradleTokenVersion") +checkPropertyExists("apiPackage") +checkPropertyExists("accessTransformersFile") +checkPropertyExists("usesMixins") +checkPropertyExists("mixinPlugin") +checkPropertyExists("mixinsPackage") +checkPropertyExists("coreModClass") +checkPropertyExists("containsMixinsAndOrCoreModOnly") +checkPropertyExists("usesShadowedDependencies") +checkPropertyExists("developmentEnvironmentUserName") + +propertyDefaultIfUnset("generateGradleTokenClass", "") +propertyDefaultIfUnset("includeWellKnownRepositories", true) +propertyDefaultIfUnset("noPublishedSources", false) +propertyDefaultIfUnset("usesMixinDebug", project.usesMixins) +propertyDefaultIfUnset("forceEnableMixins", false) +propertyDefaultIfUnset("channel", "stable") +propertyDefaultIfUnset("mappingsVersion", "12") +propertyDefaultIfUnset("usesMavenPublishing", true) +propertyDefaultIfUnset("mavenPublishUrl", "https://nexus.gtnewhorizons.com/repository/releases/") +propertyDefaultIfUnset("modrinthProjectId", "") +propertyDefaultIfUnset("modrinthRelations", "") +propertyDefaultIfUnset("curseForgeProjectId", "") +propertyDefaultIfUnset("curseForgeRelations", "") +propertyDefaultIfUnset("versionPattern", "") +propertyDefaultIfUnset("minimizeShadowedDependencies", true) +propertyDefaultIfUnset("relocateShadowedDependencies", true) +// Deprecated properties (kept for backwards compat) +propertyDefaultIfUnset("gradleTokenModId", "") +propertyDefaultIfUnset("gradleTokenModName", "") +propertyDefaultIfUnset("gradleTokenGroupName", "") + +propertyDefaultIfUnset("enableModernJavaSyntax", false) // On by default for new projects only +propertyDefaultIfUnset("enableGenericInjection", false) // On by default for new projects only + +// this is meant to be set using the user wide property file. by default we do nothing. +propertyDefaultIfUnset("ideaOverrideBuildType", "") // Can be nothing, "gradle" or "idea" + +project.extensions.add(com.diffplug.blowdryer.Blowdryer, "Blowdryer", com.diffplug.blowdryer.Blowdryer) // Make blowdryer available in "apply from:" scripts +if (!disableSpotless) { + apply plugin: 'com.diffplug.spotless' + apply from: Blowdryer.file('spotless.gradle') +} + +if (!disableCheckstyle) { + apply plugin: 'checkstyle' + tasks.named("checkstylePatchedMc") { enabled = false } + tasks.named("checkstyleMcLauncher") { enabled = false } + tasks.named("checkstyleIdeVirtualMain") { enabled = false } + tasks.named("checkstyleInjectedTags") { enabled = false } + checkstyle { + config = resources.text.fromString(CHECKSTYLE_CONFIG) } } -task submodulesUpdate(type:Exec) { - description 'Updates (and inits) git submodules' - commandLine 'git', 'submodule', 'update', '--init', '--recursive' - group 'Build Setup' +String javaSourceDir = "src/main/java/" +String scalaSourceDir = "src/main/scala/" +String kotlinSourceDir = "src/main/kotlin/" + +if (usesShadowedDependencies.toBoolean()) { + apply plugin: "com.github.johnrengelman.shadow" +} + +java { + toolchain { + if (enableModernJavaSyntax.toBoolean()) { + languageVersion.set(JavaLanguageVersion.of(17)) + } else { + languageVersion.set(projectJavaVersion) + } + vendor.set(JvmVendorSpec.AZUL) + } + if (!noPublishedSources) { + withSourcesJar() + } +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = "UTF-8" +} + +tasks.withType(ScalaCompile).configureEach { + options.encoding = "UTF-8" +} + +pluginManager.withPlugin('org.jetbrains.kotlin.jvm') { + // If Kotlin is enabled in the project + kotlin { + jvmToolchain(8) + } + // Kotlin hacks our source sets, so we hack Kotlin's tasks + def disabledKotlinTaskList = [ + "kaptGenerateStubsMcLauncherKotlin", + "kaptGenerateStubsPatchedMcKotlin", + "kaptGenerateStubsInjectedTagsKotlin", + "compileMcLauncherKotlin", + "compilePatchedMcKotlin", + "compileInjectedTagsKotlin", + "kaptMcLauncherKotlin", + "kaptPatchedMcKotlin", + "kaptInjectedTagsKotlin", + "kspMcLauncherKotlin", + "kspPatchedMcKotlin", + "kspInjectedTagsKotlin", + ] + tasks.configureEach { task -> + if (task.name in disabledKotlinTaskList) { + task.enabled = false + } + } } configurations { - embed - compile.extendsFrom(embed) - shade - compile.extendsFrom(shade) + create("runtimeOnlyNonPublishable") { + description = "Runtime only dependencies that are not published alongside the jar" + canBeConsumed = false + canBeResolved = false + } + + create("devOnlyNonPublishable") { + description = "Runtime and compiletime dependencies that are not published alongside the jar (compileOnly + runtimeOnlyNonPublishable)" + canBeConsumed = false + canBeResolved = false + } + compileOnly.extendsFrom(devOnlyNonPublishable) + runtimeOnlyNonPublishable.extendsFrom(devOnlyNonPublishable) } -apply plugin: 'forge' +if (enableModernJavaSyntax.toBoolean()) { + repositories { + mavenCentral { + mavenContent { + includeGroup("me.eigenraven.java8unsupported") + } + } + } + + dependencies { + annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:1.0.0' + // workaround for https://github.com/bsideup/jabel/issues/174 + annotationProcessor 'net.java.dev.jna:jna-platform:5.13.0' + compileOnly('com.github.bsideup.jabel:jabel-javac-plugin:1.0.0') { + transitive = false // We only care about the 1 annotation class + } + // Allow using jdk.unsupported classes like sun.misc.Unsafe in the compiled code, working around JDK-8206937. + patchedMinecraft('me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0') + } + + tasks.withType(JavaCompile).configureEach { + if (it.name in ["compileMcLauncherJava", "compilePatchedMcJava"]) { + return + } + sourceCompatibility = 17 // for the IDE support + options.release.set(8) + + javaCompiler.set(javaToolchains.compilerFor { + languageVersion.set(JavaLanguageVersion.of(17)) + vendor.set(JvmVendorSpec.AZUL) + }) + } +} + +eclipse { + classpath { + downloadSources = true + downloadJavadoc = true + } +} + +final String modGroupPath = modGroup.toString().replace('.' as char, '/' as char) +final String apiPackagePath = apiPackage.toString().replace('.' as char, '/' as char) + +String targetPackageJava = javaSourceDir + modGroupPath +String targetPackageScala = scalaSourceDir + modGroupPath +String targetPackageKotlin = kotlinSourceDir + modGroupPath +if (!(getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists() || getFile(targetPackageKotlin).exists())) { + throw new GradleException("Could not resolve \"modGroup\"! Could not find " + targetPackageJava + " or " + targetPackageScala + " or " + targetPackageKotlin) +} + +if (apiPackage) { + targetPackageJava = javaSourceDir + modGroupPath + "/" + apiPackagePath + targetPackageScala = scalaSourceDir + modGroupPath + "/" + apiPackagePath + targetPackageKotlin = kotlinSourceDir + modGroupPath + "/" + apiPackagePath + if (!(getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists() || getFile(targetPackageKotlin).exists())) { + throw new GradleException("Could not resolve \"apiPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala + " or " + targetPackageKotlin) + } +} + +if (accessTransformersFile) { + for (atFile in accessTransformersFile.split(" ")) { + String targetFile = "src/main/resources/META-INF/" + atFile.trim() + if (!getFile(targetFile).exists()) { + throw new GradleException("Could not resolve \"accessTransformersFile\"! Could not find " + targetFile) + } + tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(targetFile) + tasks.srgifyBinpatchedJar.accessTransformerFiles.from(targetFile) + } +} else { + boolean atsFound = false + for (File at : sourceSets.getByName("main").resources.files) { + if (at.name.toLowerCase().endsWith("_at.cfg")) { + atsFound = true + tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(at) + tasks.srgifyBinpatchedJar.accessTransformerFiles.from(at) + } + } + for (File at : sourceSets.getByName("api").resources.files) { + if (at.name.toLowerCase().endsWith("_at.cfg")) { + atsFound = true + tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(at) + tasks.srgifyBinpatchedJar.accessTransformerFiles.from(at) + } + } + if (atsFound) { + logger.warn("Found and added access transformers in the resources folder, please configure gradle.properties to explicitly mention them by name") + } +} + +if (usesMixins.toBoolean()) { + if (mixinsPackage.isEmpty()) { + throw new GradleException("\"usesMixins\" requires \"mixinsPackage\" to be set!") + } + final String mixinPackagePath = mixinsPackage.toString().replaceAll("\\.", "/") + final String mixinPluginPath = mixinPlugin.toString().replaceAll("\\.", "/") + + targetPackageJava = javaSourceDir + modGroupPath + "/" + mixinPackagePath + targetPackageScala = scalaSourceDir + modGroupPath + "/" + mixinPackagePath + targetPackageKotlin = kotlinSourceDir + modGroupPath + "/" + mixinPackagePath + if (!(getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists() || getFile(targetPackageKotlin).exists())) { + throw new GradleException("Could not resolve \"mixinsPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala + " or " + targetPackageKotlin) + } + + if (!mixinPlugin.isEmpty()) { + String targetFileJava = javaSourceDir + modGroupPath + "/" + mixinPluginPath + ".java" + String targetFileScala = scalaSourceDir + modGroupPath + "/" + mixinPluginPath + ".scala" + String targetFileScalaJava = scalaSourceDir + modGroupPath + "/" + mixinPluginPath + ".java" + String targetFileKotlin = kotlinSourceDir + modGroupPath + "/" + mixinPluginPath + ".kt" + if (!(getFile(targetFileJava).exists() || getFile(targetFileScala).exists() || getFile(targetFileScalaJava).exists() || getFile(targetFileKotlin).exists())) { + throw new GradleException("Could not resolve \"mixinPlugin\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava + " or " + targetFileKotlin) + } + } +} + +if (coreModClass) { + final String coreModPath = coreModClass.toString().replaceAll("\\.", "/") + String targetFileJava = javaSourceDir + modGroupPath + "/" + coreModPath + ".java" + String targetFileScala = scalaSourceDir + modGroupPath + "/" + coreModPath + ".scala" + String targetFileScalaJava = scalaSourceDir + modGroupPath + "/" + coreModPath + ".java" + String targetFileKotlin = kotlinSourceDir + modGroupPath + "/" + coreModPath + ".kt" + if (!(getFile(targetFileJava).exists() || getFile(targetFileScala).exists() || getFile(targetFileScalaJava).exists() || getFile(targetFileKotlin).exists())) { + throw new GradleException("Could not resolve \"coreModClass\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava + " or " + targetFileKotlin) + } +} + +configurations.configureEach { + resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS) + + // Make sure GregTech build won't time out + System.setProperty("org.gradle.internal.http.connectionTimeout", 120000 as String) + System.setProperty("org.gradle.internal.http.socketTimeout", 120000 as String) +} + +// Fix Jenkins' Git: chmod a file should not be detected as a change and append a '.dirty' to the version +try { + 'git config core.fileMode false'.execute() +} +catch (Exception ignored) { + out.style(Style.Failure).println("git isn't installed at all") +} + +// Pulls version first from the VERSION env and then git tag +String identifiedVersion +String versionOverride = System.getenv("VERSION") ?: null +boolean checkVersion = false +try { + // Produce a version based on the tag, or for branches something like 0.2.2-configurable-maven-and-extras.38+43090270b6-dirty + if (versionOverride == null) { + def gitDetails = versionDetails() + def isDirty = gitVersion().endsWith(".dirty") // No public API for this, isCleanTag has a different meaning + String branchName = gitDetails.branchName ?: (System.getenv('GIT_BRANCH') ?: 'git') + if (branchName.startsWith('origin/')) { + branchName = branchName.minus('origin/') + } + branchName = branchName.replaceAll("[^a-zA-Z0-9-]+", "-") // sanitize branch names for semver + identifiedVersion = gitDetails.lastTag ?: '${gitDetails.gitHash}' + if (gitDetails.commitDistance > 0) { + identifiedVersion += "-${branchName}.${gitDetails.commitDistance}+${gitDetails.gitHash}" + if (isDirty) { + identifiedVersion += "-dirty" + } + } else if (isDirty) { + identifiedVersion += "-${branchName}+${gitDetails.gitHash}-dirty" + } else { + checkVersion = true + } + } else { + identifiedVersion = versionOverride + } +} +catch (Exception ignored) { + out.style(Style.Failure).text( + 'This mod must be version controlled by Git AND the repository must provide at least one tag,\n' + + 'or the VERSION override must be set! ').style(Style.SuccessHeader).text('(Do NOT download from GitHub using the ZIP option, instead\n' + + 'clone the repository, see ').style(Style.Info).text('https://gtnh.miraheze.org/wiki/Development').style(Style.SuccessHeader).println(' for details.)' + ) + versionOverride = 'NO-GIT-TAG-SET' + identifiedVersion = versionOverride +} +version = identifiedVersion +ext { + modVersion = identifiedVersion +} + +if (identifiedVersion == versionOverride) { + out.style(Style.Failure).text('Override version to ').style(Style.Identifier).text(modVersion).style(Style.Failure).println('!\7') +} else if (checkVersion && versionPattern && !(identifiedVersion ==~ versionPattern)) { + throw new GradleException("Invalid version: '$identifiedVersion' does not match version pattern '$versionPattern'") +} + +group = "com.github.GTNewHorizons" +if (project.hasProperty("customArchiveBaseName") && customArchiveBaseName) { + base { + archivesName = customArchiveBaseName + } +} else { + base { + archivesName = modId + } +} -version = "1.0" -group= "com.kamkeel.customnpc-plus" // http://maven.apache.org/guides/mini/guide-naming-conventions.html -archivesBaseName = "CustomNPC-Plus-Gecko" minecraft { - version = "1.7.10-10.13.4.1614-1.7.10" - runDir = "eclipse" + if (replaceGradleTokenInFile) { + for (f in replaceGradleTokenInFile.split(',')) { + tagReplacementFiles.add f + } + out.style(Style.Info).text('replaceGradleTokenInFile is deprecated! Consider using generateGradleTokenClass.').println() + } + if (gradleTokenModId) { + if (replaceGradleTokenInFile) { + injectedTags.put gradleTokenModId, modId + } else { + out.style(Style.Failure).text('gradleTokenModId is deprecated! The field will no longer be generated.').println() + } + } + if (gradleTokenModName) { + if (replaceGradleTokenInFile) { + injectedTags.put gradleTokenModName, modName + } else { + out.style(Style.Failure).text('gradleTokenModName is deprecated! The field will no longer be generated.').println() + } + } + if (gradleTokenVersion) { + injectedTags.put gradleTokenVersion, modVersion + } + if (gradleTokenGroupName) { + if (replaceGradleTokenInFile) { + injectedTags.put gradleTokenGroupName, modGroup + } else { + out.style(Style.Failure).text('gradleTokenGroupName is deprecated! The field will no longer be generated.').println() + } + } + if (enableGenericInjection.toBoolean()) { + injectMissingGenerics.set(true) + } + + username = developmentEnvironmentUserName.toString() + + lwjgl3Version = "3.3.2" + + // Enable assertions in the current mod + extraRunJvmArguments.add("-ea:${modGroup}") + + if (usesMixins.toBoolean() || forceEnableMixins.toBoolean()) { + if (usesMixinDebug.toBoolean()) { + extraRunJvmArguments.addAll([ + "-Dmixin.debug.countInjections=true", + "-Dmixin.debug.verbose=true", + "-Dmixin.debug.export=true" + ]) + } + } + + // Blowdryer is present in some old mod builds, do not propagate it further as a dependency + // IC2 has no reobf jars in its Maven + groupsToExcludeFromAutoReobfMapping.addAll(["com.diffplug", "com.diffplug.durian", "net.industrial-craft"]) } -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +if (generateGradleTokenClass) { + tasks.injectTags.outputClassName.set(generateGradleTokenClass) +} -processResources - { - // this will ensure that this task is redone when the versions change. - inputs.property "version", project.version - inputs.property "mcversion", project.minecraft.version +// Custom reobf auto-mappings +configurations.configureEach { + dependencies.configureEach { dep -> + if (dep instanceof org.gradle.api.artifacts.ExternalModuleDependency) { + if (dep.group == "net.industrial-craft" && dep.name == "industrialcraft-2") { + // https://www.curseforge.com/minecraft/mc-mods/industrial-craft/files/2353971 + project.dependencies.reobfJarConfiguration("curse.maven:ic2-242638:2353971") + } + } + } + def obfuscationAttr = it.attributes.getAttribute(ObfuscationAttribute.OBFUSCATION_ATTRIBUTE) + if (obfuscationAttr != null && obfuscationAttr.name == ObfuscationAttribute.SRG) { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + // Remap CoFH core cursemaven dev jar to the obfuscated version for runObfClient/Server + if (details.requested.group == 'curse.maven' && details.requested.name.endsWith('-69162') && details.requested.version == '2388751') { + details.useVersion '2388750' + details.because 'Pick obfuscated jar' + } + } + } +} - // replace stuff in mcmod.info, nothing else - from(sourceSets.main.resources.srcDirs) { - include 'mcmod.info' +// Ensure tests have access to minecraft classes +sourceSets { + test { + java { + compileClasspath += sourceSets.patchedMc.output + sourceSets.mcLauncher.output + runtimeClasspath += sourceSets.patchedMc.output + sourceSets.mcLauncher.output + } + } +} - // replace version and mcversion - expand 'version':project.version, 'mcversion':project.minecraft.version +if (file('addon.gradle.kts').exists()) { + apply from: 'addon.gradle.kts' +} else if (file('addon.gradle').exists()) { + apply from: 'addon.gradle' +} + +// File for local tweaks not commited to Git +if (file('addon.local.gradle.kts').exists()) { + apply from: 'addon.local.gradle.kts' +} else if (file('addon.local.gradle').exists()) { + apply from: 'addon.local.gradle' +} + +// Allow unsafe repos but warn +repositories.configureEach { repo -> + if (repo instanceof org.gradle.api.artifacts.repositories.UrlArtifactRepository) { + if (repo.getUrl() != null && repo.getUrl().getScheme() == "http" && !repo.allowInsecureProtocol) { + logger.warn("Deprecated: Allowing insecure connections for repo '${repo.name}' - add 'allowInsecureProtocol = true'") + repo.allowInsecureProtocol = true + } + } +} + +if (file('repositories.gradle.kts').exists()) { + apply from: 'repositories.gradle.kts' +} else if (file('repositories.gradle').exists()) { + apply from: 'repositories.gradle' +} else { + logger.error("Neither repositories.gradle.kts nor repositories.gradle was found, make sure you extracted the full ExampleMod template.") + throw new RuntimeException("Missing repositories.gradle[.kts]") +} + +configurations { + runtimeClasspath.extendsFrom(runtimeOnlyNonPublishable) + testRuntimeClasspath.extendsFrom(runtimeOnlyNonPublishable) + for (config in [compileClasspath, runtimeClasspath, testCompileClasspath, testRuntimeClasspath]) { + if (usesShadowedDependencies.toBoolean()) { + config.extendsFrom(shadowImplementation) + // TODO: remove Compile after all uses are refactored to Implementation + config.extendsFrom(shadeCompile) + config.extendsFrom(shadowCompile) + } + } + // A "bag-of-dependencies"-style configuration for backwards compatibility, gets put in "api" + create("compile") { + description = "Deprecated: use api or implementation instead, gets put in api" + canBeConsumed = false + canBeResolved = false + visible = false + } + create("testCompile") { + description = "Deprecated: use testImplementation instead" + canBeConsumed = false + canBeResolved = false + visible = false + } + api.extendsFrom(compile) + testImplementation.extendsFrom(testCompile) +} + +afterEvaluate { + if (!configurations.compile.allDependencies.empty || !configurations.testCompile.allDependencies.empty) { + logger.warn("This project uses deprecated `compile` dependencies, please migrate to using `api` and `implementation`") + logger.warn("For more details, see https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/master/dependencies.gradle") + } +} + +repositories { + maven { + name = "GTNH Maven" + url = "https://nexus.gtnewhorizons.com/repository/public/" + // Links for convenience: + // Simple HTML browsing: https://nexus.gtnewhorizons.com/service/rest/repository/browse/releases/ + // Rich web UI browsing: https://nexus.gtnewhorizons.com/#browse/browse:releases + } + maven { + name 'Overmind forge repo mirror' + url 'https://gregtech.overminddl1.com/' + } + maven { + name 'sonatype' + url 'https://oss.sonatype.org/content/repositories/snapshots/' + content { + includeGroup "org.lwjgl" + } + } + if (includeWellKnownRepositories.toBoolean()) { + exclusiveContent { + forRepository { + maven { + name "CurseMaven" + url "https://cursemaven.com" + } + } + filter { + includeGroup "curse.maven" + } + } + exclusiveContent { + forRepository { + maven { + name = "Modrinth" + url = "https://api.modrinth.com/maven" + } + } + filter { + includeGroup "maven.modrinth" + } + } + maven { + name = "ic2" + url = getURL("https://maven2.ic2.player.to/", "https://maven.ic2.player.to/") + content { + includeGroup "net.industrial-craft" + } + metadataSources { + mavenPom() + artifact() + } + } + maven { + name "MMD Maven" + url "https://maven.mcmoddev.com/" + } + } +} + +def mixinProviderGroup = "io.github.legacymoddingmc" +def mixinProviderModule = "unimixins" +def mixinProviderVersion = "0.1.13" +def mixinProviderSpecNoClassifer = "${mixinProviderGroup}:${mixinProviderModule}:${mixinProviderVersion}" +def mixinProviderSpec = "${mixinProviderSpecNoClassifer}:dev" +ext.mixinProviderSpec = mixinProviderSpec + +def mixingConfigRefMap = 'mixins.' + modId + '.refmap.json' + +dependencies { + if (usesMixins.toBoolean()) { + annotationProcessor('org.ow2.asm:asm-debug-all:5.0.3') + annotationProcessor('com.google.guava:guava:24.1.1-jre') + annotationProcessor('com.google.code.gson:gson:2.8.6') + annotationProcessor(mixinProviderSpec) + if (usesMixinDebug.toBoolean()) { + runtimeOnlyNonPublishable('org.jetbrains:intellij-fernflower:1.2.1.16') + } + } + if (usesMixins.toBoolean()) { + implementation(modUtils.enableMixins(mixinProviderSpec, mixingConfigRefMap)) + } else if (forceEnableMixins.toBoolean()) { + runtimeOnlyNonPublishable(mixinProviderSpec) + } +} + +pluginManager.withPlugin('org.jetbrains.kotlin.kapt') { + if (usesMixins.toBoolean()) { + dependencies { + kapt(mixinProviderSpec) + } + } +} + +// Replace old mixin mods with unimixins +// https://docs.gradle.org/8.0.2/userguide/resolution_rules.html#sec:substitution_with_classifier +configurations.all { + resolutionStrategy.dependencySubstitution { + substitute module('com.gtnewhorizon:gtnhmixins') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods") + substitute module('com.github.GTNewHorizons:Mixingasm') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods") + substitute module('com.github.GTNewHorizons:SpongePoweredMixin') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods") + substitute module('com.github.GTNewHorizons:SpongeMixins') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods") + substitute module('io.github.legacymoddingmc:unimixins') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Our previous unimixins upload was missing the dev classifier") + + substitute module('org.scala-lang:scala-library:2.11.1') using module('org.scala-lang:scala-library:2.11.5') because('To allow mixing with Java 8 targets') + } +} + +dependencies { + constraints { + def minGtnhLibVersion = "0.0.13" + implementation("com.github.GTNewHorizons:GTNHLib:${minGtnhLibVersion}") { + because("fixes duplicate mod errors in java 17 configurations using old gtnhlib") + } + runtimeOnly("com.github.GTNewHorizons:GTNHLib:${minGtnhLibVersion}") { + because("fixes duplicate mod errors in java 17 configurations using old gtnhlib") + } + devOnlyNonPublishable("com.github.GTNewHorizons:GTNHLib:${minGtnhLibVersion}") { + because("fixes duplicate mod errors in java 17 configurations using old gtnhlib") + } + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GTNHLib:${minGtnhLibVersion}") { + because("fixes duplicate mod errors in java 17 configurations using old gtnhlib") + } + } +} + +if (file('dependencies.gradle.kts').exists()) { + apply from: 'dependencies.gradle.kts' +} else if (file('dependencies.gradle').exists()) { + apply from: 'dependencies.gradle' +} else { + logger.error("Neither dependencies.gradle.kts nor dependencies.gradle was found, make sure you extracted the full ExampleMod template.") + throw new RuntimeException("Missing dependencies.gradle[.kts]") +} + +tasks.register('generateAssets') { + group = "GTNH Buildscript" + description = "Generates a mixin config file at /src/main/resources/mixins.modid.json if needed" + onlyIf { usesMixins.toBoolean() } + doLast { + def mixinConfigFile = getFile("/src/main/resources/mixins." + modId + ".json") + if (!mixinConfigFile.exists()) { + def mixinPluginLine = "" + if (!mixinPlugin.isEmpty()) { + // We might not have a mixin plugin if we're using early/late mixins + mixinPluginLine += """\n "plugin": "${modGroup}.${mixinPlugin}", """ } - // copy everything else, thats not the mcmod.info - from(sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' + mixinConfigFile.text = """{ + "required": true, + "minVersion": "0.8.5-GTNH", + "package": "${modGroup}.${mixinsPackage}",${mixinPluginLine} + "refmap": "${mixingConfigRefMap}", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8", + "mixins": [], + "client": [], + "server": [] +} +""" + } + } +} + +if (usesMixins.toBoolean()) { + tasks.named("processResources").configure { + dependsOn("generateAssets") + } + + tasks.named("compileJava", JavaCompile).configure { + options.compilerArgs += [ + // Elan: from what I understand they are just some linter configs so you get some warning on how to properly code + "-XDenableSunApiLintControl", + "-XDignore.symbol.file" + ] + } +} + +tasks.named("processResources", ProcessResources).configure { + // this will ensure that this task is redone when the versions change. + inputs.property "version", project.version + inputs.property "mcversion", project.minecraft.mcVersion + exclude("spotless.gradle") + + // replace stuff in mcmod.info, nothing else. replaces ${key} with value in text + filesMatching("mcmod.info") { + expand "minecraftVersion": project.minecraft.mcVersion, + "modVersion": modVersion, + "modId": modId, + "modName": modName + } + + if (usesMixins.toBoolean()) { + dependsOn("compileJava", "compileScala") + } +} + +ext.java17Toolchain = (JavaToolchainSpec spec) -> { + spec.languageVersion.set(JavaLanguageVersion.of(17)) + spec.vendor.set(JvmVendorSpec.matching("jetbrains")) +} + +ext.java17DependenciesCfg = configurations.create("java17Dependencies") { + extendsFrom(configurations.getByName("runtimeClasspath")) // Ensure consistent transitive dependency resolution + canBeConsumed = false +} +ext.java17PatchDependenciesCfg = configurations.create("java17PatchDependencies") { + canBeConsumed = false +} + +dependencies { + def lwjgl3ifyVersion = '1.5.7' + if (modId != 'lwjgl3ify') { + java17Dependencies("com.github.GTNewHorizons:lwjgl3ify:${lwjgl3ifyVersion}") + } + if (modId != 'hodgepodge') { + java17Dependencies('com.github.GTNewHorizons:Hodgepodge:2.4.4') + } + + java17PatchDependencies("com.github.GTNewHorizons:lwjgl3ify:${lwjgl3ifyVersion}:forgePatches") {transitive = false} +} + +ext.java17JvmArgs = [ + // Java 9+ support + "--illegal-access=warn", + "-Djava.security.manager=allow", + "-Dfile.encoding=UTF-8", + "--add-opens", "java.base/jdk.internal.loader=ALL-UNNAMED", + "--add-opens", "java.base/java.net=ALL-UNNAMED", + "--add-opens", "java.base/java.nio=ALL-UNNAMED", + "--add-opens", "java.base/java.io=ALL-UNNAMED", + "--add-opens", "java.base/java.lang=ALL-UNNAMED", + "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", + "--add-opens", "java.base/java.text=ALL-UNNAMED", + "--add-opens", "java.base/java.util=ALL-UNNAMED", + "--add-opens", "java.base/jdk.internal.reflect=ALL-UNNAMED", + "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", + "--add-opens", "jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED,java.naming", + "--add-opens", "java.desktop/sun.awt.image=ALL-UNNAMED", + "--add-modules", "jdk.dynalink", + "--add-opens", "jdk.dynalink/jdk.dynalink.beans=ALL-UNNAMED", + "--add-modules", "java.sql.rowset", + "--add-opens", "java.sql.rowset/javax.sql.rowset.serial=ALL-UNNAMED" +] + +ext.hotswapJvmArgs = [ + // DCEVM advanced hot reload + "-XX:+AllowEnhancedClassRedefinition", + "-XX:HotswapAgent=fatjar" +] + +ext.setupHotswapAgentTask = tasks.register("setupHotswapAgent") { + group = "GTNH Buildscript" + description = "Installs a recent version of HotSwapAgent into the Java 17 JetBrains runtime directory" + def hsaUrl = 'https://github.com/HotswapProjects/HotswapAgent/releases/download/1.4.2-SNAPSHOT/hotswap-agent-1.4.2-SNAPSHOT.jar' + def targetFolderProvider = javaToolchains.launcherFor(java17Toolchain).map {it.metadata.installationPath.dir("lib/hotswap")} + def targetFilename = "hotswap-agent.jar" + onlyIf { + !targetFolderProvider.get().file(targetFilename).asFile.exists() + } + doLast { + def targetFolder = targetFolderProvider.get() + targetFolder.asFile.mkdirs() + download.run { + src hsaUrl + dest targetFolder.file(targetFilename).asFile + overwrite false + tempAndMove true + } + } +} + +public abstract class RunHotswappableMinecraftTask extends RunMinecraftTask { + // IntelliJ doesn't seem to allow commandline arguments so we also support an env variable + private boolean enableHotswap = Boolean.valueOf(System.getenv("HOTSWAP")); + + @Input + public boolean getEnableHotswap() { return enableHotswap } + @Option(option = "hotswap", description = "Enables HotSwapAgent for enhanced class reloading under a debugger") + public boolean setEnableHotswap(boolean enable) { enableHotswap = enable } + + @Inject + public RunHotswappableMinecraftTask(Distribution side, String superTask, org.gradle.api.invocation.Gradle gradle) { + super(side, gradle) + + this.lwjglVersion = 3 + this.javaLauncher = project.javaToolchains.launcherFor(project.java17Toolchain) + this.extraJvmArgs.addAll(project.java17JvmArgs) + this.extraJvmArgs.addAll(project.provider(() -> enableHotswap ? project.hotswapJvmArgs : [])) + + this.classpath(project.java17PatchDependenciesCfg) + if (side == Distribution.CLIENT) { + this.classpath(project.minecraftTasks.lwjgl3Configuration) + } + // Use a raw provider instead of map to not create a dependency on the task + this.classpath(project.provider(() -> project.tasks.named(superTask, RunMinecraftTask).get().classpath)) + this.classpath.filter { file -> + !file.path.contains("2.9.4-nightly-20150209") // Remove lwjgl2 + } + this.classpath(project.java17DependenciesCfg) + } + + public void setup(Project project) { + super.setup(project) + if (project.usesMixins.toBoolean()) { + this.extraJvmArgs.addAll(project.provider(() -> { + def mixinCfg = project.configurations.detachedConfiguration(project.dependencies.create(project.mixinProviderSpec)) + mixinCfg.canBeConsumed = false + mixinCfg.transitive = false + enableHotswap ? ["-javaagent:" + mixinCfg.singleFile.absolutePath] : [] + })) + } + } +} + +def runClient17Task = tasks.register("runClient17", RunHotswappableMinecraftTask, Distribution.CLIENT, "runClient") +runClient17Task.configure { + setup(project) + group = "Modded Minecraft" + description = "Runs the modded client using Java 17, lwjgl3ify and Hodgepodge" + dependsOn(setupHotswapAgentTask, mcpTasks.launcherSources.classesTaskName, minecraftTasks.taskDownloadVanillaAssets, mcpTasks.taskPackagePatchedMc, 'jar') + mainClass = "GradleStart" + username = minecraft.username + userUUID = minecraft.userUUID +} + +def runServer17Task = tasks.register("runServer17", RunHotswappableMinecraftTask, Distribution.DEDICATED_SERVER, "runServer") +runServer17Task.configure { + setup(project) + group = "Modded Minecraft" + description = "Runs the modded server using Java 17, lwjgl3ify and Hodgepodge" + dependsOn(setupHotswapAgentTask, mcpTasks.launcherSources.classesTaskName, minecraftTasks.taskDownloadVanillaAssets, mcpTasks.taskPackagePatchedMc, 'jar') + mainClass = "GradleStartServer" + extraArgs.add("nogui") +} + +def getManifestAttributes() { + def manifestAttributes = [:] + if (!containsMixinsAndOrCoreModOnly.toBoolean() && (usesMixins.toBoolean() || coreModClass)) { + manifestAttributes += ["FMLCorePluginContainsFMLMod": true] + } + + if (accessTransformersFile) { + manifestAttributes += ["FMLAT": accessTransformersFile.toString()] + } + + if (coreModClass) { + manifestAttributes += ["FMLCorePlugin": modGroup + "." + coreModClass] + } + + if (usesMixins.toBoolean()) { + manifestAttributes += [ + "TweakClass" : "org.spongepowered.asm.launch.MixinTweaker", + "MixinConfigs" : "mixins." + modId + ".json", + "ForceLoadAsMod": !containsMixinsAndOrCoreModOnly.toBoolean() + ] + } + return manifestAttributes +} + +tasks.named("jar", Jar).configure { + manifest { + attributes(getManifestAttributes()) + } +} + +if (usesShadowedDependencies.toBoolean()) { + tasks.named("shadowJar", ShadowJar).configure { + manifest { + attributes(getManifestAttributes()) + } + + if (minimizeShadowedDependencies.toBoolean()) { + minimize() // This will only allow shading for actually used classes + } + configurations = [ + project.configurations.shadowImplementation, + project.configurations.shadowCompile, + project.configurations.shadeCompile + ] + archiveClassifier.set('dev') + if (relocateShadowedDependencies.toBoolean()) { + relocationPrefix = modGroup + ".shadow" + enableRelocation = true + } + } + configurations.runtimeElements.outgoing.artifacts.clear() + configurations.apiElements.outgoing.artifacts.clear() + configurations.runtimeElements.outgoing.artifact(tasks.named("shadowJar", ShadowJar)) + configurations.apiElements.outgoing.artifact(tasks.named("shadowJar", ShadowJar)) + tasks.named("jar", Jar) { + archiveClassifier.set('dev-preshadow') + } + tasks.named("reobfJar", ReobfuscatedJar) { + inputJar.set(tasks.named("shadowJar", ShadowJar).flatMap({it.archiveFile})) + } + AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.components.findByName("java") + javaComponent.withVariantsFromConfiguration(configurations.shadowRuntimeElements) { + skip() + } +} +ext.publishableDevJar = usesShadowedDependencies.toBoolean() ? tasks.shadowJar : tasks.jar +ext.publishableObfJar = tasks.reobfJar + +tasks.register('apiJar', Jar) { + from(sourceSets.main.allSource) { + include modGroupPath + "/" + apiPackagePath + '/**' + } + + from(sourceSets.main.output) { + include modGroupPath + "/" + apiPackagePath + '/**' + } + + from(sourceSets.main.resources.srcDirs) { + include("LICENSE") + } + + getArchiveClassifier().set('api') +} + +artifacts { + if (!noPublishedSources) { + archives tasks.named("sourcesJar") + } + if (apiPackage) { + archives tasks.named("apiJar") + } +} + +idea { + module { + downloadJavadoc = true + downloadSources = true + inheritOutputDirs = true + } + project { + settings { + if (ideaOverrideBuildType != "") { + delegateActions { + if ("gradle".equalsIgnoreCase(ideaOverrideBuildType)) { + delegateBuildRunToGradle = true + testRunner = org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.GRADLE + } else if ("idea".equalsIgnoreCase(ideaOverrideBuildType)) { + delegateBuildRunToGradle = false + testRunner = org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.PLATFORM + } else { + throw GradleScriptException('Accepted value for ideaOverrideBuildType is one of gradle or idea.') + } + } + } + runConfigurations { + "0. Build and Test"(Gradle) { + taskNames = ["build"] + } + "1. Run Client"(Gradle) { + taskNames = ["runClient"] + } + "2. Run Server"(Gradle) { + taskNames = ["runServer"] + } + "1a. Run Client (Java 17)"(Gradle) { + taskNames = ["runClient17"] + } + "2a. Run Server (Java 17)"(Gradle) { + taskNames = ["runServer17"] + } + "1b. Run Client (Java 17, Hotswap)"(Gradle) { + taskNames = ["runClient17"] + envs = ["HOTSWAP": "true"] + } + "2b. Run Server (Java 17, Hotswap)"(Gradle) { + taskNames = ["runServer17"] + envs = ["HOTSWAP": "true"] + } + "3. Run Obfuscated Client"(Gradle) { + taskNames = ["runObfClient"] + } + "4. Run Obfuscated Server"(Gradle) { + taskNames = ["runObfServer"] + } + if (!disableSpotless) { + "5. Apply spotless"(Gradle) { + taskNames = ["spotlessApply"] + } + } + def coreModArgs = "" + if (coreModClass) { + coreModArgs = ' "-Dfml.coreMods.load=' + modGroup + '.' + coreModClass + '"' + } + "Run Client (IJ Native)"(Application) { + mainClass = "GradleStart" + moduleName = project.name + ".ideVirtualMain" + afterEvaluate { + workingDirectory = tasks.runClient.workingDir.absolutePath + programParameters = tasks.runClient.calculateArgs(project).collect { '"' + it + '"' }.join(' ') + jvmArgs = tasks.runClient.calculateJvmArgs(project).collect { '"' + it + '"' }.join(' ') + + ' ' + tasks.runClient.systemProperties.collect { '"-D' + it.key + '=' + it.value.toString() + '"' }.join(' ') + + coreModArgs + } + } + "Run Server (IJ Native)"(Application) { + mainClass = "GradleStartServer" + moduleName = project.name + ".ideVirtualMain" + afterEvaluate { + workingDirectory = tasks.runServer.workingDir.absolutePath + programParameters = tasks.runServer.calculateArgs(project).collect { '"' + it + '"' }.join(' ') + jvmArgs = tasks.runServer.calculateJvmArgs(project).collect { '"' + it + '"' }.join(' ') + + ' ' + tasks.runServer.systemProperties.collect { '"-D' + it.key + '=' + it.value.toString() + '"' }.join(' ') + + coreModArgs + } + } + } + compiler.javac { + afterEvaluate { + javacAdditionalOptions = "-encoding utf8" + moduleJavacAdditionalOptions = [ + (project.name + ".main"): tasks.compileJava.options.compilerArgs.collect { '"' + it + '"' }.join(' ') + ] + } + } + withIDEADir { File ideaDir -> + if (!ideaDir.path.contains(".idea")) { + // If an .ipr file exists, the project root directory is passed here instead of the .idea subdirectory + ideaDir = new File(ideaDir, ".idea") + } + if (ideaDir.isDirectory()) { + def miscFile = new File(ideaDir, "misc.xml") + if (miscFile.isFile()) { + boolean dirty = false + def miscTransformer = new XmlTransformer() + miscTransformer.addAction { root -> + Node rootNode = root.asNode() + def rootManager = rootNode + .component.find { it.@name == 'ProjectRootManager' } + if (!rootManager) { + rootManager = rootNode.appendNode('component', ['name': 'ProjectRootManager', 'version': '2']) + dirty = true + } + def output = rootManager.output + if (!output) { + output = rootManager.appendNode('output') + dirty = true + } + if (!output.@url) { + // Only modify the output url if it doesn't yet have one, or if the existing one is blank somehow. + // This is a sensible default for most setups + output.@url = 'file://$PROJECT_DIR$/build/ideaBuild' + dirty = true + } + } + def result = miscTransformer.transform(miscFile.text) + if (dirty) { + miscFile.write(result) + } + } else { + miscFile.text = """ + + + + + +""" + } + } + } + } + } +} + +tasks.named("processIdeaSettings").configure { + dependsOn("injectTags") +} + +tasks.named("ideVirtualMainClasses").configure { + // Make IntelliJ "Build project" build the mod jars + dependsOn("jar", "reobfJar") + if (!disableSpotless) { + dependsOn("spotlessCheck") + } +} + +// workaround variable hiding in pom processing +def projectConfigs = project.configurations + +publishing { + publications { + create("maven", MavenPublication) { + from components.java + + if (apiPackage) { + artifact apiJar + } + + groupId = System.getenv("ARTIFACT_GROUP_ID") ?: project.group + artifactId = System.getenv("ARTIFACT_ID") ?: project.name + // Using the identified version, not project.version as it has the prepended 1.7.10 + version = System.getenv("RELEASE_VERSION") ?: identifiedVersion + } + } + repositories { + if (usesMavenPublishing.toBoolean() && System.getenv("MAVEN_USER") != null) { + maven { + url = mavenPublishUrl + allowInsecureProtocol = mavenPublishUrl.startsWith("http://") + credentials { + username = System.getenv("MAVEN_USER") ?: "NONE" + password = System.getenv("MAVEN_PASSWORD") ?: "NONE" + } + } + } + } +} + +if (modrinthProjectId.size() != 0 && System.getenv("MODRINTH_TOKEN") != null) { + apply plugin: 'com.modrinth.minotaur' + + File changelogFile = new File(System.getenv("CHANGELOG_FILE") ?: "CHANGELOG.md") + + modrinth { + token = System.getenv("MODRINTH_TOKEN") + projectId = modrinthProjectId + versionNumber = identifiedVersion + versionType = identifiedVersion.endsWith("-pre") ? "beta" : "release" + changelog = changelogFile.exists() ? changelogFile.getText("UTF-8") : "" + uploadFile = publishableObfJar + additionalFiles = getSecondaryArtifacts() + gameVersions = [minecraftVersion] + loaders = ["forge"] + debugMode = false + } + + if (modrinthRelations.size() != 0) { + String[] deps = modrinthRelations.split(";") + deps.each { dep -> + if (dep.size() == 0) { + return + } + String[] parts = dep.split(":") + String[] qual = parts[0].split("-") + addModrinthDep(qual[0], qual[1], parts[1]) + } + } + if (usesMixins.toBoolean()) { + addModrinthDep("required", "project", "unimixins") + } + tasks.modrinth.dependsOn(build) + tasks.publish.dependsOn(tasks.modrinth) +} + +if (curseForgeProjectId.size() != 0 && System.getenv("CURSEFORGE_TOKEN") != null) { + apply plugin: 'com.matthewprenger.cursegradle' + + File changelogFile = new File(System.getenv("CHANGELOG_FILE") ?: "CHANGELOG.md") + + curseforge { + apiKey = System.getenv("CURSEFORGE_TOKEN") + project { + id = curseForgeProjectId + if (changelogFile.exists()) { + changelogType = "markdown" + changelog = changelogFile + } + releaseType = identifiedVersion.endsWith("-pre") ? "beta" : "release" + addGameVersion minecraftVersion + addGameVersion "Forge" + mainArtifact publishableObfJar + for (artifact in getSecondaryArtifacts()) addArtifact artifact + } + + options { + javaIntegration = false + forgeGradleIntegration = false + debug = false + } + } + + if (curseForgeRelations.size() != 0) { + String[] deps = curseForgeRelations.split(";") + deps.each { dep -> + if (dep.size() == 0) { + return + } + String[] parts = dep.split(":") + addCurseForgeRelation(parts[0], parts[1]) + } + } + if (usesMixins.toBoolean()) { + addCurseForgeRelation("requiredDependency", "unimixins") + } + tasks.curseforge.dependsOn(build) + tasks.publish.dependsOn(tasks.curseforge) +} + +def addModrinthDep(String scope, String type, String name) { + com.modrinth.minotaur.dependencies.Dependency dep; + if (!(scope in ["required", "optional", "incompatible", "embedded"])) { + throw new Exception("Invalid modrinth dependency scope: " + scope) + } + switch (type) { + case "project": + dep = new ModDependency(name, scope) + break + case "version": + dep = new VersionDependency(name, scope) + break + default: + throw new Exception("Invalid modrinth dependency type: " + type) + } + project.modrinth.dependencies.add(dep) +} + +def addCurseForgeRelation(String type, String name) { + if (!(type in ["requiredDependency", "embeddedLibrary", "optionalDependency", "tool", "incompatible"])) { + throw new Exception("Invalid CurseForge relation type: " + type) + } + CurseArtifact artifact = project.curseforge.curseProjects[0].mainArtifact + CurseRelation rel = (artifact.curseRelations ?: (artifact.curseRelations = new CurseRelation())) + rel."$type"(name) +} + +// Updating + +def buildscriptGradleVersion = "8.5" + +tasks.named('wrapper', Wrapper).configure { + gradleVersion = buildscriptGradleVersion +} + +tasks.register('updateBuildScript') { + group = 'GTNH Buildscript' + description = 'Updates the build script to the latest version' + + if (gradle.gradleVersion != buildscriptGradleVersion && !Boolean.getBoolean('DISABLE_BUILDSCRIPT_GRADLE_UPDATE')) { + dependsOn('wrapper') + } + + doLast { + if (performBuildScriptUpdate()) return + + print("Build script already up-to-date!") + } +} + +if (!project.getGradle().startParameter.isOffline() && !Boolean.getBoolean('DISABLE_BUILDSCRIPT_UPDATE_CHECK') && isNewBuildScriptVersionAvailable()) { + if (autoUpdateBuildScript.toBoolean()) { + performBuildScriptUpdate() + } else { + out.style(Style.SuccessHeader).println("Build script update available! Run 'gradle updateBuildScript'") + if (gradle.gradleVersion != buildscriptGradleVersion) { + out.style(Style.SuccessHeader).println("updateBuildScript can update gradle from ${gradle.gradleVersion} to ${buildscriptGradleVersion}\n") + } + } +} + +// If you want to add more cases to this task, implement them as arguments if total amount to print gets too large +tasks.register('faq') { + group = 'GTNH Buildscript' + description = 'Prints frequently asked questions about building a project' + + doLast { + print("If your build fails to fetch dependencies, run './gradlew updateDependencies'. " + + "Or you can manually check if the versions are still on the distributing sites - " + + "the links can be found in repositories.gradle and build.gradle:repositories, " + + "but not build.gradle:buildscript.repositories - those ones are for gradle plugin metadata.\n\n" + + "If your build fails to recognize the syntax of new Java versions, enable Jabel in your " + + "gradle.properties. See how it's done in GTNH ExampleMod/gradle.properties. " + + "However, keep in mind that Jabel enables only syntax features, but not APIs that were introduced in " + + "Java 9 or later.") + } +} + +static URL availableBuildScriptUrl() { + new URL("https://raw.githubusercontent.com/GTNewHorizons/ExampleMod1.7.10/master/build.gradle") +} + +static URL exampleSettingsGradleUrl() { + new URL("https://raw.githubusercontent.com/GTNewHorizons/ExampleMod1.7.10/master/settings.gradle.example") +} + +static URL exampleGitAttributesUrl() { + new URL("https://raw.githubusercontent.com/GTNewHorizons/ExampleMod1.7.10/master/.gitattributes") +} + + +boolean verifyGitAttributes() { + def gitattributesFile = getFile(".gitattributes") + if (!gitattributesFile.exists()) { + println("Downloading default .gitattributes") + exampleGitAttributesUrl().withInputStream { i -> gitattributesFile.withOutputStream { it << i } } + exec { + workingDir '.' + commandLine 'git', 'add', '--renormalize', '.' + } + return true + } + return false +} + +boolean verifySettingsGradle() { + def settingsFile = getFile("settings.gradle") + if (!settingsFile.exists()) { + println("Downloading default settings.gradle") + exampleSettingsGradleUrl().withInputStream { i -> settingsFile.withOutputStream { it << i } } + return true + } + return false +} + +boolean performBuildScriptUpdate() { + if (isNewBuildScriptVersionAvailable()) { + def buildscriptFile = getFile("build.gradle") + availableBuildScriptUrl().withInputStream { i -> buildscriptFile.withOutputStream { it << i } } + def out = services.get(StyledTextOutputFactory).create('buildscript-update-output') + out.style(Style.Success).print("Build script updated. Please REIMPORT the project or RESTART your IDE!") + boolean settingsupdated = verifySettingsGradle() + settingsupdated = verifyGitAttributes() || settingsupdated + if (settingsupdated) + throw new GradleException("Settings has been updated, please re-run task.") + return true + } + return false +} + +boolean isNewBuildScriptVersionAvailable() { + Map parameters = ["connectTimeout": 2000, "readTimeout": 2000] + + String currentBuildScript = getFile("build.gradle").getText() + String currentBuildScriptHash = getVersionHash(currentBuildScript) + String availableBuildScriptHash + try { + String availableBuildScript = availableBuildScriptUrl().newInputStream(parameters).getText() + availableBuildScriptHash = getVersionHash(availableBuildScript) + } catch (IOException e) { + logger.warn("Could not check for buildscript update availability: {}", e.message) + return false + } + + boolean isUpToDate = currentBuildScriptHash.empty || availableBuildScriptHash.empty || currentBuildScriptHash == availableBuildScriptHash + return !isUpToDate +} + +static String getVersionHash(String buildScriptContent) { + String versionLine = buildScriptContent.find("^//version: [a-z0-9]*") + if (versionLine != null) { + return versionLine.split(": ").last() + } + return "" +} + +// Parameter Deobfuscation + +tasks.register('deobfParams') { + group = 'GTNH Buildscript' + description = 'Rename all obfuscated parameter names inherited from Minecraft classes' + doLast { // TODO + + String mcpDir = "$project.gradle.gradleUserHomeDir/caches/minecraft/de/oceanlabs/mcp/mcp_$channel/$mappingsVersion" + String mcpZIP = "$mcpDir/mcp_$channel-$mappingsVersion-${minecraftVersion}.zip" + String paramsCSV = "$mcpDir/params.csv" + + download.run { + src "https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp_$channel/$mappingsVersion-$minecraftVersion/mcp_$channel-$mappingsVersion-${minecraftVersion}.zip" + dest mcpZIP + overwrite false + } + + if (!file(paramsCSV).exists()) { + println("Extracting MCP archive ...") + copy { + from(zipTree(mcpZIP)) + into(mcpDir) } } -jar { - from sourceSets.api.output + println("Parsing params.csv ...") + Map params = new HashMap<>() + Files.lines(Paths.get(paramsCSV)).forEach { line -> + String[] cells = line.split(",") + if (cells.length > 2 && cells[0].matches("p_i?\\d+_\\d+_")) { + params.put(cells[0], cells[1]) + } + } - manifest { - attributes( - 'FMLAT': "customnpcs_at.cfg", - ) + out.style(Style.Success).println("Modified ${replaceParams(file("$projectDir/src/main/java"), params)} files!") + out.style(Style.Failure).println("Don't forget to verify that the code still works as before!\n It could be broken due to duplicate variables existing now\n or parameters taking priority over other variables.") } } + +static int replaceParams(File file, Map params) { + int fileCount = 0 + + if (file.isDirectory()) { + for (File f : file.listFiles()) { + fileCount += replaceParams(f, params) + } + return fileCount + } + println("Visiting ${file.getName()} ...") + try { + String content = new String(Files.readAllBytes(file.toPath())) + int hash = content.hashCode() + params.forEach { key, value -> + content = content.replaceAll(key, value) + } + if (hash != content.hashCode()) { + Files.write(file.toPath(), content.getBytes("UTF-8")) + return 1 + } + } catch (Exception e) { + e.printStackTrace() + } + return 0 +} + +// Dependency Deobfuscation (Deprecated, use the new RFG API documented in dependencies.gradle) + +def deobf(String sourceURL) { + try { + URL url = new URL(sourceURL) + String fileName = url.getFile() + + //get rid of directories: + int lastSlash = fileName.lastIndexOf("/") + if (lastSlash > 0) { + fileName = fileName.substring(lastSlash + 1) + } + //get rid of extension: + if (fileName.endsWith(".jar") || fileName.endsWith(".litemod")) { + fileName = fileName.substring(0, fileName.lastIndexOf(".")) + } + + String hostName = url.getHost() + if (hostName.startsWith("www.")) { + hostName = hostName.substring(4) + } + List parts = Arrays.asList(hostName.split("\\.")) + Collections.reverse(parts) + hostName = String.join(".", parts) + + return deobf(sourceURL, "$hostName/$fileName") + } catch (Exception ignored) { + return deobf(sourceURL, "deobf/${sourceURL.hashCode()}") + } +} + +def deobfMaven(String repoURL, String mavenDep) { + if (!repoURL.endsWith("/")) { + repoURL += "/" + } + String[] parts = mavenDep.split(":") + parts[0] = parts[0].replace('.', '/') + def jarURL = repoURL + parts[0] + "/" + parts[1] + "/" + parts[2] + "/" + parts[1] + "-" + parts[2] + ".jar" + return deobf(jarURL) +} + +def deobfCurse(String curseDep) { + return dependencies.rfg.deobf("curse.maven:$curseDep") +} + +// The method above is to be preferred. Use this method if the filename is not at the end of the URL. +def deobf(String sourceURL, String rawFileName) { + String bon2Version = "2.5.1" + String fileName = URLDecoder.decode(rawFileName, "UTF-8") + String cacheDir = "$project.gradle.gradleUserHomeDir/caches" + String obfFile = "$cacheDir/modules-2/files-2.1/${fileName}.jar" + + download.run { + src sourceURL + dest obfFile + quiet true + overwrite false + } + return dependencies.rfg.deobf(files(obfFile)) +} +// Helper methods + +def checkPropertyExists(String propertyName) { + if (!project.hasProperty(propertyName)) { + throw new GradleException("This project requires a property \"" + propertyName + "\"! Please add it your \"gradle.properties\". You can find all properties and their description here: https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/gradle.properties") + } +} + +def propertyDefaultIfUnset(String propertyName, defaultValue) { + if (!project.hasProperty(propertyName) || project.property(propertyName) == "") { + project.ext.setProperty(propertyName, defaultValue) + } +} + +def getFile(String relativePath) { + return new File(projectDir, relativePath) +} + +def getSecondaryArtifacts() { + // Because noPublishedSources from the beginning of the script is somehow not visible here... + boolean noPublishedSources = project.hasProperty("noPublishedSources") ? project.noPublishedSources.toBoolean() : false + def secondaryArtifacts = [publishableDevJar] + if (!noPublishedSources) secondaryArtifacts += [sourcesJar] + if (apiPackage) secondaryArtifacts += [apiJar] + return secondaryArtifacts +} + +def getURL(String main, String fallback) { + return pingURL(main, 10000) ? main : fallback +} + +// credit: https://stackoverflow.com/a/3584332 +def pingURL(String url, int timeout) { + url = url.replaceFirst("^https", "http") // Otherwise an exception may be thrown on invalid SSL certificates. + try { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection() + connection.setConnectTimeout(timeout) + connection.setReadTimeout(timeout) + connection.setRequestMethod("HEAD") + int responseCode = connection.getResponseCode() + return 200 <= responseCode && responseCode <= 399 + } catch (IOException ignored) { + return false + } +} + +// For easier scripting of things that require variables defined earlier in the buildscript +if (file('addon.late.gradle.kts').exists()) { + apply from: 'addon.late.gradle.kts' +} else if (file('addon.late.gradle').exists()) { + apply from: 'addon.late.gradle' +} + +// File for local tweaks not commited to Git +if (file('addon.late.local.gradle.kts').exists()) { + apply from: 'addon.late.local.gradle.kts' +} else if (file('addon.late.local.gradle').exists()) { + apply from: 'addon.late.local.gradle' +} diff --git a/dependencies.gradle b/dependencies.gradle new file mode 100644 index 0000000..624c487 --- /dev/null +++ b/dependencies.gradle @@ -0,0 +1,37 @@ +/* + * Add your dependencies here. Supported configurations: + * - api("group:name:version:classifier"): if you use the types from this dependency in the public API of this mod + * Available at runtime and compiletime for mods depending on this mod + * - implementation("g:n:v:c"): if you need this for internal implementation details of the mod, but none of it is visible via the public API + * Available at runtime but not compiletime for mods depending on this mod + * - compileOnly("g:n:v:c"): if the mod you're building doesn't need this dependency during runtime at all, e.g. for optional mods + * Not available at all for mods depending on this mod, only visible at compiletime for this mod + * - compileOnlyApi("g:n:v:c"): like compileOnly, but also visible at compiletime for mods depending on this mod + * Available at compiletime but not runtime for mods depending on this mod + * - runtimeOnlyNonPublishable("g:n:v:c"): if you want to include a mod in this mod's runClient/runServer runs, but not publish it as a dependency + * Not available at all for mods depending on this mod, only visible at runtime for this mod + * - devOnlyNonPublishable("g:n:v:c"): a combination of runtimeOnlyNonPublishable and compileOnly for dependencies present at both compiletime and runtime, + * but not published as Maven dependencies - useful for RFG-deobfuscated dependencies or local testing + * - runtimeOnly("g:n:v:c"): if you don't need this at compile time, but want it to be present at runtime + * Available at runtime for mods depending on this mod + * - annotationProcessor("g:n:v:c"): mostly for java compiler plugins, if you know you need this, use it, otherwise don't worry + * - testCONFIG("g:n:v:c") - replace CONFIG by one of the above (except api), same as above but for the test sources instead of main + * + * - shadowImplementation("g:n:v:c"): effectively the same as API, but the dependency is included in your jar under a renamed package name + * Requires you to enable usesShadowedDependencies in gradle.properties + * + * - compile("g:n:v:c"): deprecated, replace with "api" (works like the old "compile") or "implementation" (can be more efficient) + * + * You can exclude transitive dependencies (dependencies of the chosen dependency) by appending { transitive = false } if needed, + * but use this sparingly as it can break using your mod as another mod's dependency if you're not careful. + * + * To depend on obfuscated jars you can use `devOnlyNonPublishable(rfg.deobf("dep:spec:1.2.3"))` to fetch an obfuscated jar from maven, + * or `devOnlyNonPublishable(rfg.deobf(project.files("libs/my-mod-jar.jar")))` to use a file. + * + * Gradle names for some of the configuration can be misleading, compileOnlyApi and runtimeOnly both get published as dependencies in Maven, but compileOnly does not. + * The buildscript adds runtimeOnlyNonPublishable to also have a runtime dependency that's not published. + * + * For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph + */ +dependencies { +} diff --git a/docs/FAQ.md b/docs/FAQ.md new file mode 100644 index 0000000..ea8362d --- /dev/null +++ b/docs/FAQ.md @@ -0,0 +1,9 @@ +# Things we cannot protect you from (yet) + +### Select an mcp conf dir for the deobfuscator + +You may or may not run into this popup. For now, the only solution is to point the deopfuscator into the right direction. + +![](http://i.imgur.com/gzBMLrr.png) + +Solution: Point it to `~/.gradle/caches/minecraft/net/minecraftforge/forge/1.7.10-10.13.4.1614-1.7.10/unpacked/conf`. On Windows, please use `%USERPROFILE%/.gradle/caches/minecraft/net/minecraftforge/forge/1.7.10-10.13.4.1614-1.7.10/unpacked/conf`. diff --git a/docs/migration.md b/docs/migration.md new file mode 100644 index 0000000..66ea324 --- /dev/null +++ b/docs/migration.md @@ -0,0 +1,22 @@ +# Migration guides + +## Generic migration +Migration for the typical mod which doesn't use anything special but Minecraft forge and some library dependencies. +For core plugin, Mixins, shadowing, access transformers, ASM or etc. you'll need to do some extra steps. +If they are missing in this document - we will gladly receive your suggestions/contribution. + +1. Copy and replace all files from [template](https://github.com/GTNewHorizons/ExampleMod1.7.10/releases/download/master-packages/migration.zip) to your repository, but `build.gradle` +2. Copy all repositories from your `build.gradle(.kts)` to `repositories.gradle` +3. Copy all dependencies from your `build.gradle(.kts)` to `dependecies.gradle` +4. replace your `build.gradle(.kts)` with `build.gradle` from template. In case you have written some custom tasks/configurations not present in the template - move them into `addon.gradle`. It will automatically be integrated if present. +5. Adapt `gradle.properties` to your mod +6. Ensure `src/main/resources/mcmod.info` contains `${modId}`, `${modName}`. `${modVersion}` and `${minecraftVersion}` +7. Re-import the project to your IDE (e.g. restart with clean caches in IntelliJ IDEA) +8. Run `./gradlew clean setupDecompWorkspace` + +## Mixin configuration +For the reference checkout the [example mixin configuration branch](https://github.com/GTNewHorizons/ExampleMod1.7.10/tree/example-mixins) of the template. + +1. Extract mixins package and plugin configuration from `mixins.yourModId.json` to `gradle.properties` +2. Implement MixinPlugin according to example from the reference +3. Remove mixins.mymodid.json diff --git a/docs/porting.md b/docs/porting.md new file mode 100644 index 0000000..6a97a9b --- /dev/null +++ b/docs/porting.md @@ -0,0 +1,56 @@ +# Porting guidelines + +This is a list of steps which should help you on your probably not so easy journey of porting some mod: + +### 1. Setting up repository and build system +1. Checkout any information in mod REAMDE/Wiki/Docs to find out if there are any special tasks/configs that need to be applied to the build +2. Fork original repository to preserve commit history +3. Apply build migration as explained in [migration guidelines](migration.md) on your fork + +### 2. Refining the fork +Try to get rid of dependencies on concrete jars (usually in the `lib` folder) if any present. This way it will be much easier to change (upgrade/downgrade) your project dependencies, when needed. + +Check if they are in maven repository (usually authors put such information in the project readme), if it isn't accessible but project is open source with permissive enough license (e.g., MIT) - you still can publish them yourselves: + 1. Fork the repository + 2. Drop `jitpack.yml` and `.github/workflows/gradle.yml` in project root. You can find this file in this repository root. + 3. Make sure everything builds from console by running `./gradlew clean setupCIWorkspace` + 4. If all is fine/after fixing the errors - make a tag on Github or using console, this should trigger Github build hook and generate a release + 4. Lookup forked repository on `https://jitpack.io/` + 5. Find your release and click "Get it", which should scroll you down to the example of how to add the dependency (make sure you have jitpack repository in mod you are porting) + 6. Checkout build log beside button you clicked to make sure it succeeds + +Now when you are sure dependency is available in maven repository - just add it as a normal gradle dependency in `dependencies.gradle`. + +If there is not online dependency available, you may upload it as a jar to jitpack, see [jitpack single file publishing thread](https://gist.github.com/jitpack-io/f928a858aa5da08ad9d9662f982da983). Please ensure, that you have the rights to do so! + +There may also be a case where mods depend on another mods - then you'll need to port any dependencies first. (Yay, dependency hell! :D) + +### 3. Preparing for porting +Try to build the project and see check what types of errors are you getting. Generally, there should be 2 types of errors you encounter: + - Missing references to packages/classes/methods/fields/parameters. Things get renamed, moved, restructured, removed or even not yet exist. That's the straightforward part - you'll need to adjust references and way things are invoked. + In case of missing things, you'll either need to implement something that's imitates missing parts or resign from some functionality + - Build related errors (e.g., something that is a part of the mod in never versions previously was an external library - you'll need to add it as a dependencies) + +Fix all build related errors (so build system won't get in your way) + +### 4. Porting the mod +After all these preparations nothing should be in the way of porting the mod, the only thing left is the actual code to change, which probably is a most tedious part of this process. + +Good approach is to start working with smaller things first, building up your confidence in how the mod works and gradually approaching more complex stuff, here is a general algorithm: + 1. Begin with fixing moved/renamed things by deleting all bad imports and with help of the IDE re-import equivalents if present. + IntelliJ IDEA has settings for unambiguous auto-import and import optimization on the fly, which can greatly speedup the process. Just pay attention to what is actually imported. + 2. Remove all nonworking code which is not easily fixable (e.g., class only introduced in newer forge) and provide stubs in its place. + For example, replace reference to method of non existing class with your method in your class, it can have an empty body and mocked return so the code can compile and run without issues. + Do not forget to track all things you've stubbed, if you are working on port alone - TODOs should be sufficient (most IDEs have a built in TODO browser). + 3. Build the project and attempt to run it + 4. If there were any critical errors which cause Minecraft to crash or mod to not work - try fixing them first, so you can test your changes + 5. Start fixing small things, ones that you think you have most chances to fix and work your way up + 6. If any there is any feature that is not worth it's time or you simply don't know how to do it - consider dropping it entirely and open an issue in your repository where you'll explain your findings and blockers. + Maybe somebody with greater knowledge/more time/motivation will try to take bite at it. +9. Fix bugs you've introduced when porting. + It is uncommon for mods to have lots of workarounds and hidden connections. + You'll need to test things and check if they work as intended (gl;hf ;p) + +### 5. Final words + +If after reading this, you are not discouraged and still want to port it - good luck porting it! You'll definitively need it. diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..f19d03e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,157 @@ +modName = CustomNPC-Gecko-Addon + +# This is a case-sensitive string to identify your mod. Convention is to use lower case. +modId = cnpcgeckoaddon + +modGroup = com.goodbird.cnpcgeckoaddon + +# WHY is there no version field? +# The build script relies on git to provide a version via tags. It is super easy and will enable you to always know the +# code base or your binary. Check out this tutorial: https://blog.mattclemente.com/2017/10/13/versioning-with-git-tags/ + +# Will update your build.gradle automatically whenever an update is available +autoUpdateBuildScript = false + +minecraftVersion = 1.7.10 +forgeVersion = 10.13.4.1614 + +# Specify a MCP channel and mappings version for dependency deobfuscation and the deobfParams task. +channel = stable +mappingsVersion = 12 + +# Define other MCP mappings for dependency deobfuscation +remoteMappings = https://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/ + +# Select a username for testing your mod with breakpoints. You may leave this empty for a random username each time you +# restart Minecraft in development. Choose this dependent on your mod: +# Do you need consistent player progressing (for example Thaumcraft)? -> Select a name +# Do you need to test how your custom blocks interacts with a player that is not the owner? -> leave name empty +developmentEnvironmentUserName = Developer + +# Enables using modern java syntax (up to version 17) via Jabel, while still targeting JVM 8. +# See https://github.com/bsideup/jabel for details on how this works. +enableModernJavaSyntax = true + +# Enables injecting missing generics into the decompiled source code for a better coding experience +# Turns most publicly visible List, Map, etc. into proper List, Map types +enableGenericInjection = false + +# Generate a class with a String field for the mod version named as defined below. +# If generateGradleTokenClass is empty or not missing, no such class will be generated. +# If gradleTokenVersion is empty or missing, the field will not be present in the class. +generateGradleTokenClass = +gradleTokenVersion = VERSION +# [DEPRECATED] +# Multiple source files can be defined here by providing a comma-seperated list: Class1.java,Class2.java,Class3.java +# public static final String VERSION = "GRADLETOKEN_VERSION"; +# The string's content will be replaced with your mod's version when compiled. You should use this to specify your mod's +# version in @Mod([...], version = VERSION, [...]) +# Leave these properties empty to skip individual token replacements +replaceGradleTokenInFile = + +# In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can +# leave this property empty. +# Example value: apiPackage = api + modGroup = com.myname.mymodid -> com.myname.mymodid.api +apiPackage = + +# Specify the configuration file for Forge's access transformers here. It must be placed into /src/main/resources/META-INF/ +# There can be multiple files in a space-separated list. +# Example value: mymodid_at.cfg nei_at.cfg +accessTransformersFile = + +# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled! +usesMixins = true +# Adds some debug arguments like verbose output and export +usesMixinDebug = true +# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise. +mixinPlugin = +# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail! +mixinsPackage = mixin +# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! +# This parameter is for legacy compatibility only +# Example value: coreModClass = asm.FMLPlugin + modGroup = com.myname.mymodid -> com.myname.mymodid.asm.FMLPlugin +coreModClass = +# If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class +# that is annotated with @Mod) you want this to be true. When in doubt: leave it on false! +containsMixinsAndOrCoreModOnly = false + +# Enables Mixins even if this mod doesn't use them, useful if one of the dependencies uses mixins. +forceEnableMixins = false + +# If enabled, you may use 'shadowCompile' for dependencies. They will be integrated in your jar. It is your +# responsibility check the licence and request permission for distribution, if required. +usesShadowedDependencies = false +# If disabled, won't remove unused classes from shaded dependencies. Some libraries use reflection to access +# their own classes, making the minimization unreliable. +minimizeShadowedDependencies = true +# If disabled, won't rename the shadowed classes. +relocateShadowedDependencies = true + +# Adds the GTNH maven, CurseMaven, IC2/Player maven, and some more well-known 1.7.10 repositories +includeWellKnownRepositories = true + +# Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven. +# Authenticate with the MAVEN_USERNAME and MAVEN_PASSWORD environment variables. +# If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle. +usesMavenPublishing = true +# mavenPublishUrl = https://nexus.gtnewhorizons.com/repository/releases/ + +# Publishing to modrinth requires you to set the MODRINTH_TOKEN environment variable to your current modrinth API token. + +# The project's ID on Modrinth. Can be either the slug or the ID. +# Leave this empty if you don't want to publish on Modrinth. +modrinthProjectId = + +# The project's relations on Modrinth. You can use this to refer to other projects on Modrinth. +# Syntax: scope1-type1:name1;scope2-type2:name2;... +# Where scope can be one of [required, optional, incompatible, embedded], +# type can be one of [project, version], +# and the name is the Modrinth project or version slug/id of the other mod. +# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech +# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true +modrinthRelations = + + +# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens. + +# The project's numeric ID on CurseForge. You can find this in the About Project box. +# Leave this empty if you don't want to publish on CurseForge. +curseForgeProjectId = + +# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge. +# Syntax: type1:name1;type2:name2;... +# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible], +# and the name is the CurseForge project slug of the other mod. +# Example: requiredDependency:railcraft;embeddedLibrary:cofhlib;incompatible:buildcraft +# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true +curseForgeRelations = + + +# Optional parameter to customize the produced artifacts. Use this to preserver artifact naming when migrating older +# projects. New projects should not use this parameter. +# customArchiveBaseName = + +# Optional parameter to have the build automatically fail if an illegal version is used. +# This can be usefull, if you e.g. only want to allow versions in the form of '1.1.xxx'. +# The check is ONLY performed if the version is a git tag. +# Note: the specified string must be escaped, so e.g. 1\\.1\\.\\d+ instead of 1\.1\.\d+ +# versionPattern = + +# Optional parameter to prevent the source code from being published +# noPublishedSources = + +# Uncomment this to disable spotless checks +# This should only be uncommented to keep it easier to sync with upstream/other forks. +# That is, if there is no other active fork/upstream, NEVER change this. +disableSpotless = true + +# Uncomment this to disable checkstyle checks (currently wildcard import check). +disableCheckstyle = true + +# Override the IDEA build type. Valid value is "" (leave blank, do not override), "idea" (force use native IDEA build), "gradle" +# (force use delegated build). +# This is meant to be set in $HOME/.gradle/gradle.properties. +# e.g. add "systemProp.org.gradle.project.ideaOverrideBuildType=idea" will override the build type to be always native build. +# WARNING: If you do use this option, it will overwrite whatever you have in your existing projects. This might not be what you want! +# Usually there is no need to uncomment this here as other developers do not necessarily use the same build type as you. +# ideaOverrideBuildType = idea diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index b761216..d64cd49 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 e6d7cad..1af9e09 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip \ No newline at end of file diff --git a/gradlew b/gradlew index 91a7e26..1aa94a4 100644 --- a/gradlew +++ b/gradlew @@ -1,79 +1,127 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 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. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -82,83 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# 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" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + 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. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..93e3f59 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,4 +1,20 @@ -@if "%DEBUG%" == "" @echo off +@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 @@ -8,20 +24,24 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@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= - set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused 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 init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +55,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,44 +65,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :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 %CMD_LINE_ARGS% +"%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 +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! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +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 diff --git a/gtnhShared/spotless.eclipseformat.xml b/gtnhShared/spotless.eclipseformat.xml new file mode 100644 index 0000000..183ef10 --- /dev/null +++ b/gtnhShared/spotless.eclipseformat.xml @@ -0,0 +1,400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gtnhShared/spotless.gradle b/gtnhShared/spotless.gradle new file mode 100644 index 0000000..ed56cb7 --- /dev/null +++ b/gtnhShared/spotless.gradle @@ -0,0 +1,40 @@ + +apply plugin: 'com.diffplug.spotless' + +// Spotless autoformatter +// See https://github.com/diffplug/spotless/tree/main/plugin-gradle +// Can be locally toggled via spotless:off/spotless:on comments +spotless { + encoding 'UTF-8' + + format 'misc', { + target '.gitignore' + + trimTrailingWhitespace() + indentWithSpaces(4) + endWithNewline() + } + java { + target 'src/*/java/**/*.java', 'src/*/scala/**/*.java' + + toggleOffOn() + importOrderFile(Blowdryer.file('spotless.importorder')) + removeUnusedImports() + eclipse('4.19.0').configFile(Blowdryer.file('spotless.eclipseformat.xml')) + } + kotlin { + target 'src/*/kotlin/**/*.kt' + + toggleOffOn() + ktfmt('0.39') + + trimTrailingWhitespace() + indentWithSpaces(4) + endWithNewline() + } + scala { + target 'src/*/scala/**/*.scala' + + scalafmt('3.7.1') + } +} diff --git a/gtnhShared/spotless.importorder b/gtnhShared/spotless.importorder new file mode 100644 index 0000000..ce4fe27 --- /dev/null +++ b/gtnhShared/spotless.importorder @@ -0,0 +1,7 @@ +#Organize Import Order +#Sat Jan 28 17:57:48 GMT 2023 +0=java +1=javax +2=net +3=org +4=com diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000..09bbb51 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +before_install: + - ./gradlew setupCIWorkspace \ No newline at end of file diff --git a/libs/CustomNPC-Plus-1.9-beta5.jar b/libs/CustomNPC-Plus-1.8.6-dev.jar similarity index 56% rename from libs/CustomNPC-Plus-1.9-beta5.jar rename to libs/CustomNPC-Plus-1.8.6-dev.jar index 2fd3901..d440279 100644 Binary files a/libs/CustomNPC-Plus-1.9-beta5.jar and b/libs/CustomNPC-Plus-1.8.6-dev.jar differ diff --git a/repositories.gradle b/repositories.gradle new file mode 100644 index 0000000..c884390 --- /dev/null +++ b/repositories.gradle @@ -0,0 +1,5 @@ +// Add any additional repositories for your dependencies here + +repositories { + +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..d5cde6d --- /dev/null +++ b/settings.gradle @@ -0,0 +1,27 @@ + +pluginManagement { + repositories { + maven { + // RetroFuturaGradle + name "GTNH Maven" + url "https://nexus.gtnewhorizons.com/repository/public/" + mavenContent { + includeGroupByRegex("com\\.gtnewhorizons\\..+") + } + } + gradlePluginPortal() + mavenCentral() + mavenLocal() + } +} + +plugins { + id 'com.diffplug.blowdryerSetup' version '1.7.1' + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' // Provides java toolchains +} + +blowdryerSetup { + repoSubfolder 'gtnhShared' + github('GTNewHorizons/ExampleMod1.7.10', 'tag', '0.2.2') + //devLocal '.' // Use this when testing config updates locally +} diff --git a/src/main/java/noppes/npcs/CommonGeckoProxy.java b/src/main/java/noppes/npcs/CommonGeckoProxy.java deleted file mode 100644 index ec199cc..0000000 --- a/src/main/java/noppes/npcs/CommonGeckoProxy.java +++ /dev/null @@ -1,7 +0,0 @@ -package noppes.npcs; - -public class CommonGeckoProxy { - - public void load(){} - -} diff --git a/src/main/java/noppes/npcs/CustomNpcsGecko.java b/src/main/java/noppes/npcs/CustomNpcsGecko.java deleted file mode 100644 index 2885982..0000000 --- a/src/main/java/noppes/npcs/CustomNpcsGecko.java +++ /dev/null @@ -1,36 +0,0 @@ -package noppes.npcs; - -import cpw.mods.fml.common.Mod; -import cpw.mods.fml.common.Mod.EventHandler; -import cpw.mods.fml.common.SidedProxy; -import cpw.mods.fml.common.event.*; -import cpw.mods.fml.common.registry.EntityRegistry; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityList; -import noppes.npcs.entity.*; - -@Mod(modid = "customnpcsgecko", - name = "CustomNPC+ Gecko", - version = "1.0", - dependencies = "required-after:customnpcs;") -public class CustomNpcsGecko { - - @SidedProxy(clientSide = "noppes.npcs.client.ClientGeckoProxy", serverSide = "noppes.npcs.CommonGeckoProxy") - public static CommonGeckoProxy proxy; - - private static int NewEntityStartId = 0; - - public CustomNpcsGecko() {} - - @EventHandler - public void load(FMLPreInitializationEvent ev) { - proxy.load(); - - registerCustomModel(EntityCustomModel.class, "CustomModel"); - } - - public void registerCustomModel(Class cl, String name) { - EntityRegistry.registerModEntity(cl, name, NewEntityStartId++, this, 64, 10, false); - EntityList.stringToClassMapping.put(name, cl); - } -} \ No newline at end of file diff --git a/src/main/java/noppes/npcs/DataGecko.java b/src/main/java/noppes/npcs/DataGecko.java deleted file mode 100644 index c2d139b..0000000 --- a/src/main/java/noppes/npcs/DataGecko.java +++ /dev/null @@ -1,82 +0,0 @@ -package noppes.npcs; - -import kamkeel.addon.DataDisplaySupport; -import net.minecraft.nbt.NBTTagCompound; - -public class DataGecko { - private String model = "geckolib3:geo/npc.geo.json"; - private String animFile = "custom:geo_npc.animation.json"; - private String idleAnim = ""; - private String walkAnim = ""; - private String attackAnim = ""; - private String hurtAnim = ""; - - public NBTTagCompound writeToNBT(NBTTagCompound nbttagcompound) { - nbttagcompound.setString("Model", model); - nbttagcompound.setString("AnimFile", animFile); - nbttagcompound.setString("IdleAnim", idleAnim); - nbttagcompound.setString("WalkAnim", walkAnim); - nbttagcompound.setString("AttackAnim", attackAnim); - nbttagcompound.setString("HurtAnim", hurtAnim); - return nbttagcompound; - } - - public void readFromNBT(NBTTagCompound nbttagcompound) { - if(nbttagcompound.hasKey("Model")) { - model = nbttagcompound.getString("Model"); - animFile = nbttagcompound.getString("AnimFile"); - idleAnim = nbttagcompound.getString("IdleAnim"); - walkAnim = nbttagcompound.getString("WalkAnim"); - hurtAnim = nbttagcompound.getString("HurtAnim"); - attackAnim = nbttagcompound.getString("AttackAnim"); - } - } - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public String getAnimFile() { - return animFile; - } - - public void setAnimFile(String animFile) { - this.animFile = animFile; - } - - public String getIdleAnim() { - return idleAnim; - } - - public void setIdleAnim(String idleAnim) { - this.idleAnim = idleAnim; - } - - public String getWalkAnim() { - return walkAnim; - } - - public void setWalkAnim(String walkAnim) { - this.walkAnim = walkAnim; - } - - public String getAttackAnim() { - return attackAnim; - } - - public void setAttackAnim(String attackAnim) { - this.attackAnim = attackAnim; - } - - public String getHurtAnim() { - return hurtAnim; - } - - public void setHurtAnim(String hurtAnim) { - this.hurtAnim = hurtAnim; - } -} \ No newline at end of file diff --git a/src/main/java/noppes/npcs/client/ClientGeckoProxy.java b/src/main/java/noppes/npcs/client/ClientGeckoProxy.java deleted file mode 100644 index e4194c6..0000000 --- a/src/main/java/noppes/npcs/client/ClientGeckoProxy.java +++ /dev/null @@ -1,15 +0,0 @@ -package noppes.npcs.client; - -import cpw.mods.fml.client.registry.RenderingRegistry; -import noppes.npcs.CommonGeckoProxy; -import noppes.npcs.client.renderer.RenderCustomModel; -import noppes.npcs.entity.EntityCustomModel; - -public class ClientGeckoProxy extends CommonGeckoProxy { - - @Override - public void load() { - RenderingRegistry.registerEntityRenderingHandler(EntityCustomModel.class, new RenderCustomModel()); - } - -} diff --git a/src/main/java/noppes/npcs/client/gui/model/custom/GuiCustomAnimFileSelection.java b/src/main/java/noppes/npcs/client/gui/model/custom/GuiCustomAnimFileSelection.java deleted file mode 100644 index 9a1e183..0000000 --- a/src/main/java/noppes/npcs/client/gui/model/custom/GuiCustomAnimFileSelection.java +++ /dev/null @@ -1,80 +0,0 @@ -package noppes.npcs.client.gui.model.custom; - -import kamkeel.addon.DataDisplaySupport; -import kamkeel.addon.GeckoAddonSupport; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.util.ResourceLocation; -import noppes.npcs.DataGecko; -import noppes.npcs.client.NoppesUtil; -import noppes.npcs.client.gui.util.GuiNPCInterface; -import noppes.npcs.client.gui.util.GuiNPCStringSlot; -import noppes.npcs.client.gui.util.GuiNpcButton; -import noppes.npcs.client.gui.util.GuiNpcLabel; -import noppes.npcs.entity.EntityNPCInterface; -import software.bernie.geckolib3.resource.GeckoLibCache; - -import java.util.Collections; -import java.util.Vector; - -import static kamkeel.addon.GeckoAddonSupport.getGeckoData; - -public class GuiCustomAnimFileSelection extends GuiNPCInterface { - public GuiNPCStringSlot slot; - public GuiScreen parent; - - public GuiCustomAnimFileSelection(EntityNPCInterface npc, GuiScreen parent){ - super(npc); - drawDefaultBackground = false; - title = ""; - this.parent = parent; - } - - @Override - public void initGui(){ - super.initGui(); - String ss = "Selecting geckolib animation file:"; - addLabel(new GuiNpcLabel(0,ss, width / 2 - (this.fontRendererObj.getStringWidth(ss)/2), 20, 0xffffff)); - Vector list = new Vector(); - for(ResourceLocation resLoc : GeckoLibCache.getInstance().getAnimations().keySet()){ - list.add(resLoc.toString()); - } - Collections.sort(list,String.CASE_INSENSITIVE_ORDER); - slot = new GuiNPCStringSlot(list,this,false,18); - slot.registerScrollButtons(4, 5); - - this.addButton(new GuiNpcButton(2, width / 2 - 100, height - 44,98, 20, "gui.back")); - } - - @Override - public void drawScreen(int i, int j, float f) - { - slot.drawScreen(i, j, f); - super.drawScreen(i, j, f); - } - - @Override - public void elementClicked(){ - } - - @Override - public void doubleClicked(){ - DataGecko geckoData = (DataGecko) getGeckoData(npc); - GeckoAddonSupport.putGeckoData(npc, geckoData); - close(); - NoppesUtil.openGUI(player, parent); - } - - @Override - protected void actionPerformed(GuiButton guibutton){ - int id = guibutton.id; - if(id == 2){ - close(); - NoppesUtil.openGUI(player, parent); - } - } - - @Override - public void save() { - } -} \ No newline at end of file diff --git a/src/main/java/noppes/npcs/client/gui/model/custom/GuiCustomAnimationSelection.java b/src/main/java/noppes/npcs/client/gui/model/custom/GuiCustomAnimationSelection.java deleted file mode 100644 index 8eca95d..0000000 --- a/src/main/java/noppes/npcs/client/gui/model/custom/GuiCustomAnimationSelection.java +++ /dev/null @@ -1,89 +0,0 @@ -package noppes.npcs.client.gui.model.custom; - -import kamkeel.addon.DataDisplaySupport; -import kamkeel.addon.GeckoAddonSupport; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.util.ResourceLocation; -import noppes.npcs.DataGecko; -import noppes.npcs.client.NoppesUtil; -import noppes.npcs.client.gui.util.GuiNPCInterface; -import noppes.npcs.client.gui.util.GuiNPCStringSlot; -import noppes.npcs.client.gui.util.GuiNpcButton; -import noppes.npcs.client.gui.util.GuiNpcLabel; -import noppes.npcs.entity.EntityNPCInterface; -import software.bernie.geckolib3.core.builder.Animation; -import software.bernie.geckolib3.file.AnimationFile; -import software.bernie.geckolib3.resource.GeckoLibCache; - -import java.util.Collections; -import java.util.Vector; -import java.util.function.Consumer; - -import static kamkeel.addon.GeckoAddonSupport.getGeckoData; - -public class GuiCustomAnimationSelection extends GuiNPCInterface { - public GuiNPCStringSlot slot; - public GuiScreen parent; - public Consumer action; - - public GuiCustomAnimationSelection(EntityNPCInterface npc, GuiScreen parent, Consumer action){ - super(npc); - drawDefaultBackground = false; - title = ""; - this.parent = parent; - this.action=action; - } - - @Override - public void initGui(){ - super.initGui(); - String ss = "Selecting geckolib animation:"; - addLabel(new GuiNpcLabel(0,ss, width / 2 - (this.fontRendererObj.getStringWidth(ss)/2), 20, 0xffffff)); - Vector list = new Vector(); - - DataGecko geckoData = (DataGecko) getGeckoData(npc); - AnimationFile file = GeckoLibCache.getInstance().getAnimations().get(new ResourceLocation(geckoData.animFile)); - if(file!=null) { - for (Animation anim : file.getAllAnimations()) { - list.add(anim.animationName); - } - } - Collections.sort(list,String.CASE_INSENSITIVE_ORDER); - slot = new GuiNPCStringSlot(list,this,false,18); - slot.registerScrollButtons(4, 5); - - this.addButton(new GuiNpcButton(2, width / 2 - 100, height - 44,98, 20, "gui.back")); - } - - @Override - public void drawScreen(int i, int j, float f) - { - slot.drawScreen(i, j, f); - super.drawScreen(i, j, f); - } - - @Override - public void elementClicked(){ - } - - @Override - public void doubleClicked(){ - action.accept(slot.selected); - close(); - NoppesUtil.openGUI(player, parent); - } - - @Override - protected void actionPerformed(GuiButton guibutton){ - int id = guibutton.id; - if(id == 2){ - close(); - NoppesUtil.openGUI(player, parent); - } - } - - @Override - public void save() { - } -} \ No newline at end of file diff --git a/src/main/java/noppes/npcs/client/gui/model/custom/GuiCustomModelSelection.java b/src/main/java/noppes/npcs/client/gui/model/custom/GuiCustomModelSelection.java deleted file mode 100644 index c0b0882..0000000 --- a/src/main/java/noppes/npcs/client/gui/model/custom/GuiCustomModelSelection.java +++ /dev/null @@ -1,82 +0,0 @@ -package noppes.npcs.client.gui.model.custom; - -import kamkeel.addon.DataDisplaySupport; -import kamkeel.addon.GeckoAddonSupport; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.util.ResourceLocation; -import noppes.npcs.DataGecko; -import noppes.npcs.client.NoppesUtil; -import noppes.npcs.client.gui.util.GuiNPCInterface; -import noppes.npcs.client.gui.util.GuiNPCStringSlot; -import noppes.npcs.client.gui.util.GuiNpcButton; -import noppes.npcs.client.gui.util.GuiNpcLabel; -import noppes.npcs.entity.EntityNPCInterface; -import software.bernie.geckolib3.resource.GeckoLibCache; - -import java.util.Collections; -import java.util.Vector; - -import static kamkeel.addon.GeckoAddonSupport.getGeckoData; - -public class GuiCustomModelSelection extends GuiNPCInterface { - public GuiNPCStringSlot slot; - public GuiScreen parent; - - public GuiCustomModelSelection(EntityNPCInterface npc, GuiScreen parent){ - super(npc); - drawDefaultBackground = false; - title = ""; - this.parent = parent; - } - - @Override - public void initGui(){ - super.initGui(); - String ss = "Selecting geckolib model:"; - addLabel(new GuiNpcLabel(0,ss, width / 2 - (this.fontRendererObj.getStringWidth(ss)/2), 20, 0xffffff)); - Vector list = new Vector(); - for(ResourceLocation resLoc : GeckoLibCache.getInstance().getGeoModels().keySet()){ - list.add(resLoc.toString()); - } - Collections.sort(list,String.CASE_INSENSITIVE_ORDER); - slot = new GuiNPCStringSlot(list,this,false,18); - slot.registerScrollButtons(4, 5); - - this.addButton(new GuiNpcButton(2, width / 2 - 100, height - 44,98, 20, "gui.back")); - } - - @Override - public void drawScreen(int i, int j, float f) - { - slot.drawScreen(i, j, f); - super.drawScreen(i, j, f); - } - - @Override - public void elementClicked(){ - } - - @Override - public void doubleClicked(){ - DataGecko geckoData = (DataGecko) getGeckoData(npc); - geckoData.model=slot.selected; - - GeckoAddonSupport.putGeckoData(npc, geckoData); - close(); - NoppesUtil.openGUI(player, parent); - } - - @Override - protected void actionPerformed(GuiButton guibutton){ - int id = guibutton.id; - if(id == 2){ - close(); - NoppesUtil.openGUI(player, parent); - } - } - - @Override - public void save() { - } -} \ No newline at end of file diff --git a/src/main/java/noppes/npcs/client/model/ModelCustom.java b/src/main/java/noppes/npcs/client/model/ModelCustom.java deleted file mode 100644 index 7ab1a8f..0000000 --- a/src/main/java/noppes/npcs/client/model/ModelCustom.java +++ /dev/null @@ -1,22 +0,0 @@ -package noppes.npcs.client.model; - -import net.minecraft.util.ResourceLocation; -import noppes.npcs.entity.EntityCustomModel; -import software.bernie.geckolib3.model.AnimatedGeoModel; - -public class ModelCustom extends AnimatedGeoModel { - @Override - public ResourceLocation getAnimationFileLocation(EntityCustomModel entity) { - return entity.animResLoc;//new ResourceLocation(GeckoLib.ModID, "animations/bike.animation.json"); - } - - @Override - public ResourceLocation getModelLocation(EntityCustomModel entity) { - return entity.modelResLoc;//new ResourceLocation(GeckoLib.ModID, "geo/bike.geo.json"); - } - - @Override - public ResourceLocation getTextureLocation(EntityCustomModel entity) { - return entity.textureResLoc;//new ResourceLocation(GeckoLib.ModID, "textures/model/entity/bike.png"); - } -} \ No newline at end of file diff --git a/src/main/java/noppes/npcs/client/renderer/RenderCustomModel.java b/src/main/java/noppes/npcs/client/renderer/RenderCustomModel.java deleted file mode 100644 index 10ba064..0000000 --- a/src/main/java/noppes/npcs/client/renderer/RenderCustomModel.java +++ /dev/null @@ -1,65 +0,0 @@ -package noppes.npcs.client.renderer; - -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.item.ItemStack; -import net.minecraftforge.client.IItemRenderer; -import noppes.npcs.client.model.ModelCustom; -import noppes.npcs.entity.EntityCustomModel; -import org.lwjgl.opengl.GL11; -import software.bernie.geckolib3.geo.render.built.GeoBone; -import software.bernie.geckolib3.geo.render.built.GeoModel; -import software.bernie.geckolib3.renderers.geo.GeoEntityRenderer; - -import java.util.ArrayList; - -public class RenderCustomModel extends GeoEntityRenderer { - - public RenderCustomModel() { - super(new ModelCustom()); - } - public void renderAfter(GeoModel model, EntityCustomModel animatable, float ticks, float red, float green, float blue, float alpha) { - super.renderAfter(model, animatable, ticks, red, green, blue, alpha); - if (model.getBone("held_item").isPresent() && animatable.getHeldItem() != null) { - GeoBone bone = model.getBone("held_item").get(); - this.renderItem(animatable, bone, ticks); - } - } - - public GeoBone[] getPathFromRoot(GeoBone bone) { - ArrayList bones; - for(bones = new ArrayList(); bone != null; bone = bone.parent) { - bones.add(0, bone); - } - - return (GeoBone[])bones.toArray(new GeoBone[0]); - } - - public void renderItem(EntityCustomModel animatable, GeoBone locator, float ticks) { - GL11.glPushMatrix(); - float scale = 0.5F; - GL11.glScaled(scale, scale, scale); - GeoBone[] bonePath = this.getPathFromRoot(locator); - - for(int i = 0; i < bonePath.length; ++i) { - GeoBone b = bonePath[i]; - GL11.glTranslatef(b.getPositionX() / (16.0F * scale), b.getPositionY() / (16.0F * scale), b.getPositionZ() / (16.0F * scale)); - GL11.glTranslatef(b.getPivotX() / (16.0F * scale), b.getPivotY() / (16.0F * scale), b.getPivotZ() / (16.0F * scale)); - GL11.glRotated((double)b.getRotationZ() / Math.PI * 180.0, 0.0, 0.0, 1.0); - GL11.glRotated((double)b.getRotationY() / Math.PI * 180.0, 0.0, 1.0, 0.0); - GL11.glRotated((double)b.getRotationX() / Math.PI * 180.0, 1.0, 0.0, 0.0); - GL11.glScalef(b.getScaleX(), b.getScaleY(), b.getScaleZ()); - GL11.glTranslatef(-b.getPivotX() / (16.0F * scale), -b.getPivotY() / (16.0F * scale), -b.getPivotZ() / (16.0F * scale)); - } - - GL11.glRotatef(250.0F, 1.0F, 0.0F, 0.0F); - GL11.glRotatef(40.0F, 0.0F, 1.0F, 0.0F); - GL11.glTranslatef(-0.4F, -0.55F, 1.7F); - ItemStack stack = animatable.getHeldItem(); - RenderManager.instance.itemRenderer.renderItem(animatable, stack, 0, IItemRenderer.ItemRenderType.INVENTORY); - GL11.glPopMatrix(); - } - - public boolean isBoneRenderOverriden(EntityCustomModel entity, GeoBone bone) { - return bone.name.equals("held_item"); - } -} \ No newline at end of file diff --git a/src/main/java/noppes/npcs/entity/EntityCustomModel.java b/src/main/java/noppes/npcs/entity/EntityCustomModel.java deleted file mode 100644 index 426dcac..0000000 --- a/src/main/java/noppes/npcs/entity/EntityCustomModel.java +++ /dev/null @@ -1,104 +0,0 @@ -package noppes.npcs.entity; - -import net.minecraft.entity.EntityCreature; -import net.minecraft.entity.ai.EntityAIWatchClosest; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import software.bernie.geckolib3.core.AnimationState; -import software.bernie.geckolib3.core.IAnimatable; -import software.bernie.geckolib3.core.IAnimationTickable; -import software.bernie.geckolib3.core.PlayState; -import software.bernie.geckolib3.core.builder.AnimationBuilder; -import software.bernie.geckolib3.core.controller.AnimationController; -import software.bernie.geckolib3.core.event.predicate.AnimationEvent; -import software.bernie.geckolib3.core.manager.AnimationData; -import software.bernie.geckolib3.core.manager.AnimationFactory; - -import java.util.Objects; - -public class EntityCustomModel extends EntityCreature implements IAnimatable, IAnimationTickable { - private AnimationFactory factory = new AnimationFactory(this); - public ResourceLocation modelResLoc = new ResourceLocation("geckolib3", "geo/npc.geo.json"); - public ResourceLocation animResLoc = new ResourceLocation("custom", "geo_npc.animation.json"); - public ResourceLocation textureResLoc = new ResourceLocation("geckolib3", "textures/model/entity/geo_npc.png"); - public String idleAnim = ""; - public String walkAnim = ""; - public String hurtAnim = ""; - public String attackAnim = ""; - public AnimationBuilder dialogAnim = null; - public AnimationBuilder manualAnim = null; - public ItemStack leftHeldItem; - - private PlayState predicateMovement(AnimationEvent event) { - if (manualAnim != null) { - if (event.getController().getAnimationState() == AnimationState.Stopped) { - manualAnim = null; - } else { - if (event.getController().currentAnimationBuilder != manualAnim) { - event.getController().markNeedsReload(); - } - event.getController().setAnimation(manualAnim); - return PlayState.CONTINUE; - } - } - if (dialogAnim != null) { - if (event.getController().getAnimationState() == AnimationState.Stopped) { - dialogAnim = null; - } else { - if (event.getController().currentAnimationBuilder != dialogAnim) { - event.getController().markNeedsReload(); - } - event.getController().setAnimation(dialogAnim); - return PlayState.CONTINUE; - } - } - if (!event.isMoving() || walkAnim.isEmpty()) { - if (!idleAnim.isEmpty()) { - event.getController().setAnimation(new AnimationBuilder().loop(idleAnim)); - } else { - return PlayState.STOP; - } - } else { - event.getController().setAnimation(new AnimationBuilder().loop(walkAnim)); - } - return PlayState.CONTINUE; - } - - private PlayState predicateAttack(AnimationEvent event) { - return PlayState.CONTINUE; - } - - public void setDialogAnim(String name) { - dialogAnim = new AnimationBuilder().playOnce(name); - } - - public EntityCustomModel(World worldIn) { - super(worldIn); - this.ignoreFrustumCheck = true; - this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); - this.setSize(0.7F, 2F); - } - - @Override - public void registerControllers(AnimationData data) { - data.addAnimationController(new AnimationController<>(this, "movement", 10, this::predicateMovement)); - data.addAnimationController(new AnimationController<>(this, "attack", 10, this::predicateAttack)); - } - - @Override - public AnimationFactory getFactory() { - return this.factory; - } - - @Override - public int tickTimer() { - return ticksExisted; - } - - @Override - public void tick() { - super.onUpdate(); - } -} \ No newline at end of file diff --git a/src/main/java/noppes/npcs/util/AnimationFileUtil.java b/src/main/java/noppes/npcs/util/AnimationFileUtil.java deleted file mode 100644 index 6c76a06..0000000 --- a/src/main/java/noppes/npcs/util/AnimationFileUtil.java +++ /dev/null @@ -1,30 +0,0 @@ -package noppes.npcs.util; - -import net.minecraft.util.ResourceLocation; -import software.bernie.geckolib3.core.builder.Animation; -import software.bernie.geckolib3.file.AnimationFile; -import software.bernie.geckolib3.resource.GeckoLibCache; - -import java.util.List; -import java.util.Vector; - -public class AnimationFileUtil { - public static List getAnimationList(String animFileName) { - Vector list = new Vector<>(); - AnimationFile file = GeckoLibCache.getInstance().getAnimations().get(new ResourceLocation(animFileName)); - if (file != null) { - for (Animation anim : file.getAllAnimations()) { - list.add(anim.animationName); - } - } - return list; - } - - public static List getAnimationFileList() { - Vector list = new Vector<>(); - for (ResourceLocation resLoc : GeckoLibCache.getInstance().getAnimations().keySet()) { - list.add(resLoc.toString()); - } - return list; - } -} diff --git a/src/main/resources/LICENSE b/src/main/resources/LICENSE new file mode 100644 index 0000000..63b4b68 --- /dev/null +++ b/src/main/resources/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index da2fc5e..0e12184 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -1,18 +1,21 @@ -[ { - "modid": "customnpcsgecko", - "name": "CustomNPC+ Gecko", - "version": "1.7.10", - "mcversion": "1.7.10", - "description": "CustomNPC+ Gecko is an addon mod for CustomNPC+ to add Custom Model support", - "credits": "KAMKEEL (CNPC+)", - "logoFile": "assets/customnpcs/textures/gui/logo.png", - "url": "https://tinyurl.com/customnpc-plus", - "updateUrl": "", - "authorList": [ - "Goodbird"], - "parent":"", - "screenshots": [], - "dependencies": [] + "modListVersion": 2, + "modList": [{ + "modid": "${modId}", + "name": "${modName}", + "description": "A mod that is made for integrating GeckoLib3 and CustomNPC", + "version": "${modVersion}", + "mcversion": "${minecraftVersion}", + "url": "https://github.com/KAMKEEL/CustomNPC-Gecko-Addon", + "updateUrl": "", + "authorList": ["Goodbird","KAMKEEL"], + "credits": "", + "logoFile": "", + "screenshots": [], + "parent": "", + "requiredMods": [], + "dependencies": [], + "dependants": [], + "useDependencyInformation": false + }] } -] \ No newline at end of file diff --git a/src/main/resources/mixins.cnpcgeckoaddon.json b/src/main/resources/mixins.cnpcgeckoaddon.json new file mode 100644 index 0000000..f3baff0 --- /dev/null +++ b/src/main/resources/mixins.cnpcgeckoaddon.json @@ -0,0 +1,11 @@ +{ + "required": true, + "minVersion": "0.8.5-GTNH", + "package": "com.goodbird.cnpcgeckoaddon.mixin", + "refmap": "mixins.cnpcgeckoaddon.refmap.json", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8", + "mixins": [], + "client": [], + "server": [] +}