Compare commits

..
107 Commits
Author SHA1 Message Date
Matt c974df523e Update MDK to use FG7 (#10923) 2026-11-08 09:39:33 -08:00
LexManos d13dd327bd Fix legacy object holders not being fired in the registry specific codepath
Better optimize the scanning for object holders by unrolling streams.
2026-10-13 09:21:04 -07:00
BonsUnleashedandLexManos 216cc50162 Store object holder handlers per registry name (#10899)
Co-authored-by: LexManos <[email protected]>
2026-10-12 18:46:47 -07:00
BonsUnleashed 0ec923d730 Group the status ping channels by mod once instead of filtering per mod (#10893) 2026-10-02 13:19:02 -07:00
BonsUnleashed 8418bad66d Skip the MessageFormat parse in ForgeI18n for strings with no format syntax (#10894) 2026-10-02 13:18:40 -07:00
LexManos ae3c937073 Explicitly make heap copy of ClientboundCustomPayloadPacket's Data. Closes #10861 2026-10-02 13:07:53 -07:00
Owen 3c3f496015 Fix BlockElements not properly saving forge_data when writing to json (#10877) 2026-08-19 10:05:11 -07:00
Meme Man 22d0c94c82 Update equality check for RegistryObject to include RegistryObject#key. (#10854) 2026-07-21 11:25:36 -07:00
Matt 77785cb181 Toml parsing errors are now visible in the latest log (#10846) 2026-07-09 13:44:19 -07:00
Matt 9130c27d05 Add support for multiple access transformer configs. Backport (#10760) (#10786) 2026-04-10 08:05:36 -07:00
LexManos 1b26f0c967 [no ci] Bump ForgeGradle, disable clean project. Closes #10802 2026-04-01 09:49:22 -07:00
embeddedt 723799cdb8 Fix HandshakeHandler potentially skipping last packet (#10787) 2026-03-19 22:38:11 -07:00
Paint_Ninja 64caebe63c Skip processing vanilla classes in RuntimeDistCleaner when running on a client
Backport of #10682 to 1.20.1. Also bumped ASM to 9.9.1 for Java 26 support
2026-03-17 21:21:26 +00:00
embeddedt 8dffec4dbc Bump EventBus to 6.2.32 (#10744)
Brings all the improvements made to EventBus 6 over the past 2+ years to Forge 1.20.1. Highlights:
- Lower memory usage, faster event posting and listener registration (EventBus#98, #85, #84, #70, #65, #64, #62, #60)
- EventBus#fire(), Event#cancel() and Event.Result convenience methods (EventBus#57, #94, e2a19cb)
- New monitor phase for read-only event listeners that want to run last (EventBus c510e2c)
- Fixed memory leak after unregistering listeners in some cases (EventBus#65, #39)
- Factors in bus phase tracking and event class modifiers (final/sealed) to enable further optimisations (EventBus#98, #85, #70)
2026-01-20 21:29:49 +00:00
LexManos d6b2bf7ca3 Re-trigger build due to Github Actions Issue 2026-01-16 18:28:04 -08:00
MattandLexManos e7629d5ae4 Optimize memory allocation for checking entity fluid collisions. Backport of #10745
Co-authored-by: LexManos <[email protected]>
2026-01-16 13:13:06 -08:00
LexManos 7187d8d97f Migrate to Github Actions 2025-12-02 15:17:12 -08:00
LexManos f20ab8e6ce Fix loading ImmediateWindowHandler services from the wrong Module Layer. Closes #10719
Fix errors not logging to log files when early loading screen errors.
Fix generics compile issue in ConctendatedListView.
2025-11-23 20:37:18 -08:00
Yuri 1837d8c36c Allow loading ImmediateWindowProvider from mods dir (#10718) 2025-11-21 09:57:56 -08:00
Jonathing 132704e5f2 Work around mixins targeting IForgePlayer#canReach (#10680) 2025-10-09 15:50:41 -07:00
embeddedt e9ce8a6cc6 Bump ASM to 9.8 for Java 25 runtime support (#10664) 2025-09-19 18:57:32 -07:00
Jonathing 350bfa51e1 Optimize fix for MC-176559 (#10649) 2025-09-01 20:22:28 -07:00
Jonathing ddb5f6c749 Ignore item equality when checking shouldCauseBlockBreakReset, fixes #10645 (#10646) 2025-08-26 12:29:48 -07:00
Joseph T. McQuigg d00f5c4013 Allow command suggestions/autocomplete to search all modded paths at once without namespace provided (#10595) 2025-08-03 09:55:59 -07:00
Jenny Mei f4e8c6b67e Fix ServerChatEvent thread not using correct class loader. (#10591) 2025-08-01 13:25:26 -07:00
Jonathing 6c4f5e2865 Fix ignitedByLava making blocks permanently flammable (#10570) 2025-07-16 09:18:45 -07:00
LexManos d1071fd0f8 Add back magic number to make mixin happy. 2025-06-28 22:30:12 -07:00
LexManos 064e2fed48 Fix container screens closing when opened with an extended REACH attribute. Fixes #10560
Also fixes cases where creative mode effected reach incorrectly.
2025-06-11 14:23:16 -07:00
JonathingandLexManos f36781652c Deprecate EntityRenderersEvent.AddLayers functions that hard case to LivingEntityRenderers as the backing maps do not guarantee that type. (#10513)
Co-authored-by: LexManos <[email protected]>
2025-05-22 00:47:40 -07:00
Jonathing 753d68d20b 1.20.1 RB 4
https://forums.minecraftforge.net/topic/154387-forge-474-minecraft-1201/
2025-03-04 17:36:12 -05:00
Jonathing 4cd832fc4d Fix ForgeDev's test runs not working due to dead test mod (#10483) 2025-03-04 13:53:12 -08:00
Jonathing 8f074e1da1 Cache this.useItem before running item break logic, Fixes #10344 (#10376) 2025-03-04 13:30:10 -08:00
JonathingandLexManos 1596190810 Speed up mod annotation scanning by ~30% (#10470)
Co-authored-by: LexManos <[email protected]>
2025-03-04 13:26:07 -08:00
Jonathing 1fa795a103 Add missed license headers (#10479) 2025-03-04 13:25:58 -08:00
Jonathing fcbd3593f6 Add '#forge:chorus_additionally_grows_on' tag for similar mechanics to '#minecraft:azalea_grows_on' but for chorus (#10456) 2025-03-04 13:25:47 -08:00
Jonathing 68a8d3b830 Fix cancelling ProjectileImpactEvent still firing onBlockHit (#10481) 2025-03-04 12:11:11 -08:00
Jonathing 783c8b7d2b Honor attacker shield disabling status (#10321) 2025-02-18 14:58:43 -08:00
Jonathing dd2591b5f3 Add fast graphics render type to block model jsons (#10393)
Make modded leaves behave like vanilla leaves by default (Fixes #10389)
2025-02-18 13:40:12 -08:00
Jonathing 4cf787d7dd Fix invalidly symlinked worlds crashing on level select (#10439) 2025-02-18 13:14:12 -08:00
Jonathing b805fa74f8 Backport even more future ResourceLocation methods (#10428) 2025-02-18 13:13:58 -08:00
Jonathing fa109380f7 Ensure NetworkConstants is loaded before mod construction (#10407) 2025-02-06 10:49:59 -08:00
Jonathing 59979642e9 Account for problematic mixins in VillagerTrades.EmeraldsForVillagerTypeItem (#10402) 2025-02-05 09:04:56 -08:00
Jonathing 90e8fb1c33 Fix incorrect method reference in TntBlock.explode() 2025-01-27 20:20:30 -08:00
Joseph T. McQuigg f09c6d0a8e Fix issues in VillagerTrades.EmeraldsForVillagerTypeItem related to custom Villager Types (#10315)
Add VillagerType#registerBiomeType
Signed-off-by: Joseph T. McQuigg <[email protected]>
2025-01-27 20:20:08 -08:00
Paint_NinjaandJonathing 56eb44f1cb Add clientSideOnly feature to mods.toml (#10085) (backport of #9804 to 1.20.1)
Co-authored-by: Jonathing <[email protected]>
2025-01-23 20:30:03 +00:00
Jonathing 2e7e537029 Fix non-passengers being tickable without checking canUpdate() (#10304) 2025-01-22 13:36:57 -08:00
Jonathing b9600a27dc Fix finalizeSpawn's return value not being used correctly (#10301) 2025-01-22 13:36:49 -08:00
Jonathing d736300566 Bump CoreMods to 5.2.4 (#10263) 2024-12-23 19:34:27 -08:00
Jonathing fea0d014b8 Allow mipmap lowering to be disabled (#10252) 2024-12-23 19:34:19 -08:00
Jonathing ea355d9bb6 Add optional fix of use item duration, disabled by default (#10246) 2024-12-23 19:15:03 -08:00
JonathingandPaint_Ninja a5293855d6 Backport some Vanilla 1.21 ResourceLocation methods (#10241)
Co-authored-by: Paint_Ninja <[email protected]>
2024-12-23 18:57:33 -08:00
JonathingandPaint_Ninja 4ab4dacf6c Simplify memory usage display on loading screen (#10233)
Co-authored-by: Paint_Ninja <[email protected]>
2024-12-23 18:57:04 -08:00
JonathingandPaint_Ninja 442f4723c8 Deprecate @ObjectHolder, add a couple of fast-paths (#10228)
Co-authored-by: Paint_Ninja <[email protected]>
2024-12-23 18:56:46 -08:00
JonathingandPaint_Ninja 12682da09a Skip Vanilla classes for the CapabilityTokenSubclass transformer (#10221)
Co-authored-by: Paint_Ninja <[email protected]>
2024-12-23 18:39:58 -08:00
JonathingandPaint_Ninja da4112efa9 Skip Forge classes in the RuntimeEnumExtender transformer (#10216)
Mod classes are still transformed as usual

Co-authored-by: Paint_Ninja <[email protected]>
2024-12-23 18:06:02 -08:00
JonathingandPaint_Ninja ca6094c73d Skip processing Forge classes in RuntimeDistCleaner (#10208)
Co-authored-by: Paint_Ninja <[email protected]>
2024-12-23 17:49:22 -08:00
Jonathing 9f4d3ef1d7 Disable clean on TeamCity (#10258) 2024-12-23 17:30:33 -08:00
Jonathing 622e560b21 Bump CoreMods to 5.2 (#10130)
Full Changelog:
https://gist.github.com/Jonathing/c3ad28b2a048ac839a7baba5417ee870

The key features are:
- ES6 language support
- Thoroughly updated ASMAPI, with full documentation
- Bug fixes (some optional for backwards-compatibility)
- Partial internal code cleanup
- Request CoreMods to not apply fix for ASMAPI.findFirstInstructionBefore by default
- Updated ASM to 9.7.1
- Updated Nashorn to 15.4
2024-11-08 17:58:50 -08:00
Paint_Ninjaandandan42 f7e5db7149 Remove unneeded boat patch (backport of #10061 to 1.20.1) (#10096)
Co-authored-by: andan42 <[email protected]>
2024-10-06 18:52:29 -07:00
Paint_NinjaandRealMangoRage 29986615fe Optionally supply FMLJavaModLoadingContext as a param to mod constructors (backport of #10074 to 1.20.1) (#10100)
Co-authored-by: RealMangoRage <[email protected]>
2024-09-12 14:06:42 +01:00
Paint_Ninja e615774a0e Minor cleanup to ModListScreen and VersionChecker (backport of #9988 to 1.20.1) (#10095) 2024-09-12 14:06:20 +01:00
Paint_Ninja 9779f8eaad Cleanup FML Bindings (backport of #10004 to 1.20.1) (#10094) 2024-09-12 14:06:08 +01:00
Paint_Ninja e643bbb764 Early display fixes/workarounds for buggy drivers. Backport of #9921 to 1.20.1 (#10073) 2024-08-20 13:07:49 -07:00
James Mitchell 25bc8679b5 Add a way to render tooltips from Formatted text and TooltipComponents elements (#10055)
Backport of #10056 for 1.20.1
2024-08-16 22:51:00 +01:00
Thomas Kern 361e99805e Make HangingSignBlockEntity useable with custom BlockEntityTypes. #10038 2024-07-05 14:22:36 -07:00
Edouard127andLexManos 0d3921efb5 Unlock wrapped registries when firing register events. (#10035)
Co-authored-by: LexManos <[email protected]>
2024-07-02 11:09:00 -07:00
Jonathingandthedarkcolour 7eee9dcbcd Choose default JarJar mod file type based on parent JAR (#10023)
Co-authored-by: thedarkcolour <[email protected]>
2024-06-28 22:55:31 -07:00
JonathingandVen 3098f463a0 Fixed falling block entities not rendering as moving blocks (#10006) (#10018)
Co-authored-by: Ven <[email protected]>
2024-06-28 22:36:07 -07:00
LexManos 862d557279 Fix boat travel distance being incorrect. Closes #9997 #9999 2024-06-04 10:35:59 -07:00
LexManos e1f057278d 1.20.1 RB 3
https://forums.minecraftforge.net/topic/139825-forge-473-minecraft-1201/
2024-06-03 13:36:29 -07:00
LexManos 6bbe2c64a9 Bump gradle to 8.8 2024-06-03 13:17:20 -07:00
LexManos 684cd96e32 Bump gradle and java runtime version on team city 2024-06-03 13:00:13 -07:00
LexManos 1f4e13fce0 Fix LevelSettings ignoring data configuration. Close #9938 2024-06-03 12:50:43 -07:00
LexManos 55ad9e6d7d Fix erroneous patch in FireBlock. Closes #9996 2024-06-03 12:50:30 -07:00
Paint_Ninja 5c8d1618bc Fix early window crash when parsing options.txt (#9934)
Backport of #9933
2024-05-09 23:16:13 +01:00
Paint_Ninja a6f5057840 Prevent mixins from crashing the game when there are missing mods (#9916)
1.20.1 backport of 49.0.14
2024-05-08 22:06:39 +01:00
Paint_NinjaandJ-RAP 1b0360928f Fix NPE when acceptableValues in defineInList() does not allow nulls, backport of #9903 (#9907)
Co-authored-by: J-RAP <[email protected]>
2024-04-18 21:22:11 +01:00
Paint_Ninja 921771bd11 Fix Crowdin (#9929) 2024-04-18 21:05:13 +01:00
Paint_Ninja 089bb1fc84 Optimise capabilities a tad, backport of #9886 (#9911) 2024-04-18 20:33:15 +01:00
Paint_Ninja 602f1ef947 Add helper method to OnDatapackSyncEvent, backport of #9901 (#9919) 2024-04-18 20:32:29 +01:00
Paint_Ninja 3b18e003f6 Add CPU usage config option to early window, hide by default, backport of #9866 (#9915) 2024-04-18 20:32:05 +01:00
Paint_NinjaandDennis C c33f2d1be6 Fix slightly offset mods screen link positioning, backport of #9860 (#9861)
Co-authored-by: Dennis C <[email protected]>
2024-04-18 20:31:40 +01:00
Paint_Ninja 022116141c Make common config screen registration tasks easier, backport of #9884 (#9912) 2024-04-18 20:31:01 +01:00
Joseph T. McQuigg a8b243c0cd Add Leaves method to ModelProvider.java (#9889) 2024-04-04 13:54:13 +01:00
Paint_Ninja c22355cc45 [1.20.1] Bump CoreMods and ASM (#9897)
- CoreMods 5.1.2 -> 5.1.6
- ASM 9.6 -> 9.7
2024-04-04 13:52:49 +01:00
J-RAP 74bc36307e Bump some deps (#9880)
- CoreMods 5.0.1 -> 5.1.2
- ASM 9.5 -> 9.6
- Installer 2.1 -> 2.2
- Installer tools 1.3.0 -> 1.4.1
2024-03-10 21:22:53 +00:00
LexManos 88d2dc6c8f Fix missing patch for Item.onInventoryTick. Closes #9812 2024-01-13 12:38:56 -08:00
Paint_Ninja de13ba8ab8 Make common DisplayTest registration tasks easier (#9823) 2023-12-27 09:13:37 -08:00
Paint_Ninja 4f838a87d4 Optimise ForgeConfigSpec and make Range public (#9824)
Backport of #9810 to 1.20.1
2023-12-19 13:24:07 -08:00
J.T. McQuigg 70d700e630 Datagen addOptionalTag/s methods that allow passing the TagKey itself instead of passing the location (#9807) (#9808) 2023-12-04 22:25:14 -08:00
Paint_Ninja 78fffcf45a Update VersionSupportMatrix.java (#9805) 2023-11-29 11:12:12 -08:00
J-RAP cdba403ab6 Backport of Registries optimization, now uses fastutils collections to minimize boxing 2023-11-29 11:11:55 -08:00
LexManos 6821d7b1cc Fix patch offset 2023-11-28 21:19:54 -08:00
LexManos 9524ce4580 Fix fire related mobs not taking enough freezing damage. Closes #9686 2023-11-28 20:10:28 -08:00
LexManos f2bbeab3b9 Fix TagLoader error not printing tag name correctly. Closes #9693 2023-11-28 20:09:47 -08:00
LexManos 4a8adf5465 Fix LoadingErrorScreen inner headers are not centered. Closes #9687 2023-11-28 20:08:59 -08:00
LexManos f5fa2f2a57 Rework KeyModifiers system to properly allow keybinds to be triggered when multiple modifiers are pressed.
Fix setting keybinds whel using keyboard inputs to select the menu. Closes #9793
2023-11-28 20:07:44 -08:00
LexManos b33e168969 Fix KeyModifiers not being properly taken into account. Closes #9806 2023-11-28 19:56:02 -08:00
Paint_Ninjaandembeddedt 1f6e1ff257 Don't turn off VSync when rendering from Minecraft context (#9801)
Co-authored-by: embeddedt <[email protected]>
2023-11-26 16:57:11 -08:00
Paint_Ninja 7f8897141e Fix rare crash with early display window, fixes MinecraftForge#9673 (#9799) 2023-11-26 16:24:45 -08:00
LexManos b9a097c9e1 Fix tag loading being unordered. Closes #9774 2023-11-13 08:50:07 -08:00
LexManos 62f00266c5 Fix misaligned patch in RegistryDataLoader 2023-11-10 11:26:16 -08:00
Paint_Ninja 6986cd7ab3 Backport CrashReportAnalyser to 1.20.1 (#9757) 2023-11-02 12:45:49 -07:00
Paint_Ninja e7051b3484 Minor MDK changes (#9752) 2023-11-02 12:45:39 -07:00
Paint_NinjaandSu5eD f667d25d0b Improve mod description formatting in mods screen (#9769)
Co-authored-by: Su5eD <[email protected]>
2023-11-02 12:45:29 -07:00
Paint_Ninja 48d72fb7c6 [1.20.1] Improve mod loading error message for errors inside mod constructors (#9707) 2023-09-28 00:20:40 -07:00
LexManos 2fb2bbeed0 1.20.1 RB 2023-09-21 10:17:41 -07:00
1619 changed files with 30470 additions and 19703 deletions

No files matched your search

-3
View File
@@ -21,6 +21,3 @@ src/generated/**/*.json text eol=lf
src/generated_test/**/.cache/cache text eol=lf
src/generated_test/**/*.json text eol=lf
# We force eclipse to use unix line endings
ide/eclipse/template/.settings/*.prefs text eol=lf
+28
View File
@@ -0,0 +1,28 @@
name: Publish
on:
push:
branches: [ '1.20.1' ]
permissions:
contents: read
jobs:
build:
uses: MinecraftForge/SharedActions/.github/workflows/gradle.yml@main
with:
java: 17
gradle_setup: './gradlew setup'
gradle_tasks: 'build publish'
artifact_name: 'forge'
secrets:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
PROMOTE_ARTIFACT_WEBHOOK: ${{ secrets.PROMOTE_ARTIFACT_WEBHOOK }}
PROMOTE_ARTIFACT_USERNAME: ${{ secrets.PROMOTE_ARTIFACT_USERNAME }}
PROMOTE_ARTIFACT_PASSWORD: ${{ secrets.PROMOTE_ARTIFACT_PASSWORD }}
MAVEN_USER: ${{ secrets.MAVEN_USER }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
SIGN_KEY_ALIAS: ${{ secrets.SIGN_KEY_ALIAS }}
SIGN_KEY_PASSWORD: ${{ secrets.SIGN_KEY_PASSWORD }}
SIGN_KEYSTORE_PASSWORD: ${{ secrets.SIGN_KEYSTORE_PASSWORD }}
SIGN_KEYSTORE_DATA: ${{ secrets.SIGN_KEYSTORE_DATA }}
+1 -3
View File
@@ -13,9 +13,6 @@
/classes
*.iml
# We however WANT the ide folder as it has tempaltes/configs for developing Forge.
!ide/**
#gradle
/build
/.gradle
@@ -26,6 +23,7 @@
/projects/mcp/
/projects/clean/
/projects/forge/
/projects/fmlonly/
#/patches/*
#occupational hazards
-104
View File
@@ -1,104 +0,0 @@
<?xml version="1.0"?>
<project>
<modelVersion>4.0.0</modelVersion>
<name>MinecraftForge_Forge Config DSL Script</name>
<groupId>MinecraftForge_Forge</groupId>
<artifactId>MinecraftForge_Forge_dsl</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>configs-dsl-kotlin-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<repositories>
<repository>
<id>jetbrains-all</id>
<url>https://download.jetbrains.com/teamcity-repository</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>teamcity-server</id>
<url>https://teamcity.minecraftforge.net/app/dsl-plugins-repository</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>JetBrains</id>
<url>https://download.jetbrains.com/teamcity-repository</url>
</pluginRepository>
</pluginRepositories>
<build>
<sourceDirectory>${basedir}</sourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<configuration/>
<executions>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>process-test-sources</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>teamcity-configs-maven-plugin</artifactId>
<version>${teamcity.dsl.version}</version>
<configuration>
<format>kotlin</format>
<dstDir>target/generated-configs</dstDir>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>configs-dsl-kotlin</artifactId>
<version>${teamcity.dsl.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>configs-dsl-kotlin-plugins</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-script-runtime</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
-193
View File
@@ -1,193 +0,0 @@
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.projectFeatures.githubIssues
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
/*
The settings script is an entry point for defining a TeamCity
project hierarchy. The script should contain a single call to the
project() function with a Project instance or an init function as
an argument.
VcsRoots, BuildTypes, Templates, and subprojects can be
registered inside the project using the vcsRoot(), buildType(),
template(), and subProject() methods respectively.
To debug settings scripts in command-line, run the
mvnDebug org.jetbrains.teamcity:teamcity-configs-maven-plugin:generate
command and attach your debugger to the port 8000.
To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
-> Tool Windows -> Maven Projects), find the generate task node
(Plugins -> teamcity-configs -> teamcity-configs:generate), the
'Debug' option is available in the context menu for the task.
*/
version = "2021.2"
project {
buildType(Build)
buildType(BuildSecondaryBranches)
buildType(PullRequests)
buildType(PullRequestChecks)
buildType(PullRequestCompatibility)
params {
text("docker_jdk_version", "17", label = "Gradle version", description = "The version of the JDK to use during execution of tasks in a JDK.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("docker_gradle_version", "7.5.1", label = "Gradle version", description = "The version of Gradle to use during execution of Gradle tasks.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("git_main_branch", "1.20.x", label = "Git Main Branch", description = "The git main or default branch to use in VCS operations.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("git_branch_spec", """
+:refs/heads/(main*)
+:refs/heads/(master*)
+:refs/heads/(develop|release|staging|main|master)
+:refs/heads/(1.*)
""".trimIndent(), label = "The branch specification of the repository", description = "By default all main branches are build by the configuration. Modify this value to adapt the branches build.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
text("github_repository_name", "MinecraftForge", label = "The github repository name. Used to connect to it in VCS Roots.", description = "This is the repository slug on github. So for example `MinecraftForge` or `MinecraftForge`. It is interpolated into the global VCS Roots.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("env.PUBLISHED_JAVA_ARTIFACT_ID", "forge", label = "Published artifact id", description = "The maven coordinate artifact id that has been published by this build. Can not be empty.", allowEmpty = false)
text("env.PUBLISHED_JAVA_GROUP", "net.minecraftforge", label = "Published group", description = "The maven coordinate group that has been published by this build. Can not be empty.", allowEmpty = false)
//These are references and not actually keys
password("env.CROWDIN_KEY", "credentialsJSON:a3102dbe-805d-4177-9f54-3d2c2eb08fd5", display = ParameterDisplay.HIDDEN)
password("env.KEYSTORE_URL", "credentialsJSON:a7ae1c82-8058-4061-8d12-7f6bc2618d2e", display = ParameterDisplay.HIDDEN)
password("env.KEYSTORE_PASSWORD", "credentialsJSON:d7b964e3-a1fd-47a8-b892-6f601fe47479", display = ParameterDisplay.HIDDEN)
text("additional_publishing_gradle_parameters", "-PcrowdinKey=%env.CROWDIN_KEY% -PkeystoreKeyPass=%env.KEYSTORE_PASSWORD% -PkeystoreStorePass=%env.KEYSTORE_PASSWORD% -Pkeystore=%env.KEYSTORE_URL%", label = "Additional gradle parameters for publish", description = "Contains the additional gradle parameters used during publishing.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
checkbox("should_execute_build", "false", label = "Should build", description = "Indicates if the build task should be executed.", display = ParameterDisplay.HIDDEN,
checked = "true", unchecked = "false")
checkbox("should_execute_test", "false", label = "Should build", description = "Indicates if the build task should be executed.", display = ParameterDisplay.HIDDEN,
checked = "true", unchecked = "false")
}
features {
githubIssues {
id = "MinecraftForge_MinecraftForge__IssueTracker"
displayName = "MinecraftForge/MinecraftForge"
repositoryURL = "https://github.com/MinecraftForge/MinecraftForge"
}
}
}
object Build : BuildType({
templates(AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_BuildMainBranches"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"), AbsoluteId("MinecraftForge_BuildUsingGradle"), AbsoluteId("MinecraftForge_PublishProjectUsingGradle"), AbsoluteId("MinecraftForge_TriggersStaticFilesWebpageGenerator"))
id("MinecraftForge_MinecraftForge__Build")
name = "Build"
description = "Builds and Publishes the main branches of the project."
})
object BuildSecondaryBranches : BuildType({
templates(AbsoluteId("MinecraftForge_ExcludesBuildingDefaultBranch"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_BuildMainBranches"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"), AbsoluteId("MinecraftForge_BuildUsingGradle"))
id("MinecraftForge_MinecraftForge__BuildSecondaryBranches")
name = "Build - Secondary Branches"
description = "Builds and Publishes the secondary branches of the project."
params {
text("git_branch_spec", """
+:refs/heads/(*)
-:refs/heads/noci*
""".trimIndent(), label = "The branch specification of the repository", description = "By default all main branches are build by the configuration. Modify this value to adapt the branches build.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
}
vcs {
branchFilter = """
+:*
-:1.*
-:<default>
""".trimIndent()
}
})
object PullRequests : BuildType({
templates(AbsoluteId("MinecraftForge_BuildPullRequests"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"), AbsoluteId("MinecraftForge_BuildUsingGradle"))
id("MinecraftForge_MinecraftForge__PullRequests")
name = "Pull Requests"
description = "Builds pull requests for the project"
params {
checkbox("should_execute_build", "true", label = "Should build", description = "Indicates if the build task should be executed.", display = ParameterDisplay.HIDDEN,
checked = "true", unchecked = "false")
text(
"gradle_build_task",
"assemble",
label = "Gradle build task to execute during build",
description = "Determines the build task that is executed to build the project.",
display = ParameterDisplay.HIDDEN,
allowEmpty = false
)
}
vcs {
branchFilter = """
+:*
-:1.*
-:<default>
""".trimIndent()
}
})
object PullRequestChecks : BuildType({
templates(AbsoluteId("MinecraftForge_BuildPullRequests"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"))
id("MinecraftForge_MinecraftForge__PullRequestChecks")
name = "Pull Requests (Checks)"
description = "Checks pull requests for the project"
steps {
gradle {
name = "Check"
id = "RUNNER_10_Check"
tasks = "checkAll"
gradleParams = "--continue %gradle_custom_args%"
enableStacktrace = true
dockerImage = "%docker_gradle_image%"
dockerRunParameters = """
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
-v "/opt/cache/shared/gradle:/home/gradle/rocache:ro"
--network=host
-u 1000:1000
%docker_additional_args%
""".trimIndent()
}
}
vcs {
branchFilter = """
+:*
-:1.*
-:<default>
""".trimIndent()
}
})
object PullRequestCompatibility : BuildType({
templates(AbsoluteId("MinecraftForge_BuildPullRequests"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"))
id("MinecraftForge_MinecraftForge__PullRequestCompatibility")
name = "Pull Requests (Compatibility)"
description = "Validates binary compatibility for pull requests made to the project"
steps {
gradle {
name = "Validate"
id = "RUNNER_10_Compatibility"
tasks = "checkJarCompatibility"
gradleParams = "--continue %gradle_custom_args%"
enableStacktrace = true
dockerImage = "%docker_gradle_image%"
dockerRunParameters = """
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
-v "/opt/cache/shared/gradle:/home/gradle/rocache:ro"
--network=host
-u 1000:1000
%docker_additional_args%
""".trimIndent()
}
}
vcs {
branchFilter = """
+:*
-:1.*
-:<default>
""".trimIndent()
}
})
+444 -152
View File
@@ -1,3 +1,4 @@
import de.undercouch.gradle.tasks.download.Download
import groovy.json.JsonBuilder
import net.minecraftforge.forge.tasks.checks.CheckATs
import net.minecraftforge.forge.tasks.checks.CheckExcs
@@ -16,7 +17,6 @@ import net.minecraftforge.gradle.common.tasks.CheckJarCompatibility
import net.minecraftforge.gradle.common.tasks.DownloadMavenArtifact
import net.minecraftforge.gradle.common.tasks.ExtractInheritance
import net.minecraftforge.gradle.common.tasks.SignJar
import net.minecraftforge.gradle.patcher.tasks.GeneratePatches
import net.minecraftforge.gradle.userdev.tasks.RenameJar
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.plugins.ide.eclipse.model.SourceFolder
@@ -25,16 +25,18 @@ import org.objectweb.asm.Opcodes
plugins {
id 'org.cadixdev.licenser' version '0.6.1'
id 'com.github.ben-manes.versions' version '0.46.0'
id 'net.minecraftforge.gradleutils' version '2.+'
id 'net.minecraftforge.gradleutils' version '[2.2,2.3)'
id 'eclipse'
id 'idea'
id 'de.undercouch.download' version '5.4.0'
id 'net.minecraftforge.gradle.patcher' version '[6.0.13,6.2)' apply false
id 'net.minecraftforge.gradle.mcp' version '[6.0.13,6.2)' apply false
id 'net.minecraftforge.gradle.patcher' version '[6.0.52,6.2)' apply false
id 'net.minecraftforge.gradle.mcp' version '[6.0.52,6.2)' apply false
}
Util.init() //Init all our extension methods!
ext {
HAS_CLEAN = findProject(':clean') != null
JAR_SIGNER = null
if (project.hasProperty('keystore')) {
JAR_SIGNER = [
@@ -44,22 +46,22 @@ ext {
]
}
MAPPING_CHANNEL = 'official'
MC_VERSION = '1.20.2'
MAPPING_VERSION = MC_VERSION
MAPPING_VERSION = '1.20.1'
MC_VERSION = '1.20.1'
MC_NEXT_VERSION = '1.21'
MCP_VERSION = '20230921.100330'
MCP_VERSION = '20230612.114412'
SNAPSHOT = false
SPI_VERSION = '7.0.1'
MERGETOOL_VERSION = '1.1.5'
ACCESSTRANSFORMERS_VERSION = '8.0.4'
COREMODS_VERSION = '5.0.1'
EVENTBUS_VERSION = '6.0.5'
COREMODS_VERSION = '5.2.4'
EVENTBUS_VERSION = '6.2.33'
MODLAUNCHER_VERSION = '10.0.9'
SECUREJARHANDLER_VERSION = '2.1.10'
BOOTSTRAPLAUNCHER_VERSION = '1.1.2'
ASM_VERSION = '9.5'
INSTALLER_VERSION = '2.1.+'
ASM_VERSION = '9.9.1'
INSTALLER_VERSION = '2.2.+'
MIXIN_VERSION = '0.8.5'
TERMINALCONSOLEAPPENDER_VERSION = '1.2.0'
JLINE_VERSION = '3.12.+'
@@ -73,11 +75,11 @@ ext {
// To use a version newer than the version manifest JSON, the dependency must be added to the installer configuration
MOJANG_LOGGING_VERSION = '1.1.1'
LOG4J_VERSION = '2.19.0'
GUAVA_VERSION = '32.1.2-jre'
GSON_VERSION = '2.10.1'
GUAVA_VERSION = '31.1-jre'
GSON_VERSION = '2.10'
APACHE_COMMONS_LANG3_VERSION = '3.12.0'
JOPT_SIMPLE_VERSION = '5.0.4'
COMMONS_IO_VERSION = '2.13.0'
COMMONS_IO_VERSION = '2.11.0'
GIT_INFO = gradleutils.gitInfo
VERSION = gradleutils.getFilteredMCTagOffsetBranchVersion(true, '[0-9]', MC_VERSION)
@@ -85,10 +87,10 @@ ext {
FORGE_VERSION = VERSION.substring(MC_VERSION.length() + 1)
BINPATCH_TOOL = 'net.minecraftforge:binarypatcher:1.1.1:fatjar'
INSTALLER_TOOLS = 'net.minecraftforge:installertools:1.3.0'
INSTALLER_TOOLS = 'net.minecraftforge:installertools:1.4.1'
JAR_SPLITTER = 'net.minecraftforge:jarsplitter:1.1.4'
FART = 'net.minecraftforge:ForgeAutoRenamingTool:1.0.5:all'
MIN_TAG_FOR_CHANGELOG = '47.999'
FART = 'net.minecraftforge:ForgeAutoRenamingTool:0.1.22:all'
MIN_TAG_FOR_CHANGELOG = '41.0'
}
println('Version: ' + VERSION +
@@ -166,9 +168,9 @@ subprojects {
tasks.withType(Javadoc).configureEach {
options.tags = [
'apiNote:a:<em>API Note:</em>',
'implSpec:a:<em>Implementation Requirements:</em>',
'implNote:a:<em>Implementation Note:</em>'
'apiNote:a:<em>API Note:</em>',
'implSpec:a:<em>Implementation Requirements:</em>',
'implNote:a:<em>Implementation Note:</em>'
]
options.addStringOption('Xdoclint:all,-missing', '-public')
}
@@ -226,53 +228,50 @@ project(':mcp') {
}
}
project(':clean') {
evaluationDependsOn(':mcp')
apply plugin: 'net.minecraftforge.gradle.patcher'
tasks.withType(AbstractPublishToMaven.class).forEach{tsk -> tsk.enabled = false } // We don't want to publish anything
if (HAS_CLEAN) {
project(':clean') {
evaluationDependsOn(':mcp')
apply plugin: 'net.minecraftforge.gradle.patcher'
tasks.withType(AbstractPublishToMaven.class).forEach{tsk -> tsk.enabled = false } // We don't want to publish anything
dependencies {
implementation 'net.minecraftforge:forgespi:' + SPI_VERSION
}
dependencies {
implementation 'net.minecraftforge:forgespi:' + SPI_VERSION
}
patcher {
parent = project(':mcp')
mcVersion = MC_VERSION
patchedSrc = file('src/main/java')
patcher {
parent = project(':mcp')
mcVersion = MC_VERSION
patchedSrc = file('src/main/java')
mappings channel: MAPPING_CHANNEL, version: MAPPING_VERSION
mappings channel: MAPPING_CHANNEL, version: MAPPING_VERSION
runs {
clean_client {
client true
taskName 'clean_client'
ideaModule "${rootProject.name}.${project.name}.main"
runs {
clean_client {
client true
taskName 'clean_client'
ideaModule "${rootProject.name}.${project.name}.main"
main 'net.minecraft.client.main.Main'
workingDirectory project.file('run/client')
main 'net.minecraft.client.main.Main'
workingDirectory project.file('run')
args '--gameDir', '.'
args '--version', MC_VERSION
args '--assetsDir', downloadAssets.output
args '--assetIndex', '{asset_index}'
args '--accessToken', '0'
}
args '--gameDir', '.'
args '--version', MC_VERSION
args '--assetsDir', downloadAssets.output
args '--assetIndex', '{asset_index}'
args '--accessToken', '0'
}
clean_server {
client false
taskName 'clean_server'
ideaModule "${rootProject.name}.${project.name}.main"
clean_server {
client false
taskName 'clean_server'
ideaModule "${rootProject.name}.${project.name}.main"
main 'net.minecraft.server.Main'
workingDirectory project.file('run/server')
main 'net.minecraft.server.Main'
workingDirectory project.file('run')
}
}
}
}
eclipse.classpath.file.whenMerged {
// Disable optional warnings on the minecraft decompiled source code. It just muddies the warning window and hides warnings in our own codebase
def src = entries.find { it.path == 'src/main/java' }
src.entryAttributes['ignore_optional_problems'] = 'true'
}
}
def sharedDeps = {
@@ -307,7 +306,7 @@ def sharedDeps = {
installer "org.jline:jline-reader:${JLINE_VERSION}" // Dep of TerminalConsoleAppender
installer "org.jline:jline-terminal:${JLINE_VERSION}" // Dep of TerminalConsoleAppender
installer "org.spongepowered:mixin:${MIXIN_VERSION}"
installer 'org.openjdk.nashorn:nashorn-core:15.3'
installer 'org.openjdk.nashorn:nashorn-core:15.4'
installer "net.minecraftforge:JarJarSelector:${JARJAR_VERSION}"
installer "net.minecraftforge:JarJarMetadata:${JARJAR_VERSION}"
@@ -360,7 +359,7 @@ def sharedFmlonlyForge = { Project prj ->
}
prj.task([type: DownloadMavenArtifact], 'downloadInstaller') {
artifact = "net.minecraftforge:installer:${INSTALLER_VERSION}:shrunk"
artifact = "net.minecraftforge:installer:${INSTALLER_VERSION}:fatjar"
changing = true
}
@@ -482,6 +481,7 @@ def sharedFmlonlyForge = { Project prj ->
}
for (def run : prj.patcher.runs) {
if (run.parents) continue // We already added this to the parent run config
run.workingDirectory prj.file('run')
if (run.name.contains('client') || run.name.contains('data')) {
// Natives are loaded from the classpath now in all cases
@@ -494,7 +494,7 @@ def sharedFmlonlyForge = { Project prj ->
run.property 'org.lwjgl.system.SharedLibraryExtractDirectory', 'lwjgl_dll'
run.args '--username', 'Dev'
run.args '--version', prj.name
//run.args '--uuid', '0'
run.args '--uuid', '0'
run.args '--accessToken', '0'
run.args '--userType', 'mojang'
run.args '--versionType', 'release'
@@ -567,8 +567,331 @@ def sharedFmlonlyForge = { Project prj ->
}
}
def symlink = file("$rootDir/fmlonly/src/fmlcommon")
def symlinkValid = symlink.exists() && symlink.directory
if (symlinkValid)
project(':fmlonly') {
evaluationDependsOn(':mcp')
apply plugin: 'java-library'
apply plugin: 'net.minecraftforge.gradle.patcher'
ext {
MCP_ARTIFACT = project(':mcp').mcp.config.get()
PACKED_DEPS = [':fmlcore:jar', ':fmlloader:jar', ':fmlearlydisplay:jar', ':javafmllanguage:jar', ':lowcodelanguage:jar', ':mclanguage:jar']
MAVEN_PATH = "${group.replace('.', '/')}/${project.name}/${VERSION}"
}
sourceSets {
main {
java {
srcDirs = ["$rootDir/fmlonly/src/fmlcommon/java", "$rootDir/fmlonly/src/main/java"]
}
resources {
srcDirs = ["$rootDir/fmlonly/src/fmlcommon/resources", "$rootDir/fmlonly/src/main/resources"]
}
}
}
configurations {
moduleonly
installer {
// Don't pull all libraries, if we're missing something, add it to the installer list so the installer knows to download it.
transitive = false
}
api.extendsFrom(installer)
}
// TODO: Better coordination between project dependencies and the installer.
dependencies {
implementation project(':fmlcore')
implementation project(':fmlloader')
implementation project(':fmlearlydisplay')
implementation project(':javafmllanguage')
implementation project(':lowcodelanguage')
implementation project(':mclanguage')
runtimeOnly "cpw.mods:bootstraplauncher:${BOOTSTRAPLAUNCHER_VERSION}"
}
dependencies sharedDeps
patcher {
parent = project(':mcp')
mcVersion = MC_VERSION
patchedSrc = file('src/main/java')
mappings channel: MAPPING_CHANNEL, version: MAPPING_VERSION
runs {
fmlonly_client {
args '--launchTarget', 'fmlclientdev'
ideaModule "${rootProject.name}.${project.name}.main"
mods {
minecraft { source sourceSets.main }
}
}
fmlonly_server {
args '--launchTarget', 'fmlserverdev'
ideaModule "${rootProject.name}.${project.name}.main"
mods {
minecraft { source sourceSets.main }
}
}
}
}
sharedFmlonlyForge.call(project)
task launcherJson(type: LauncherJson) {
packedDependencies = [':fmlloader:jar',':fmlearlydisplay:jar']
doFirst {
def fmlonly_client = project(':fmlonly').patcher.runs.fmlonly_client
json = [
_comment_: comment,
id: id,
time: timestamp,
releaseTime: timestamp,
type: 'release',
mainClass: 'cpw.mods.bootstraplauncher.BootstrapLauncher',
inheritsFrom: MC_VERSION,
logging: {},
arguments: [
game: ['--launchTarget', 'fmlclient',
'--fml.forgeVersion', FORGE_VERSION,
'--fml.mcVersion', MC_VERSION,
'--fml.forgeGroup', project.group,
'--fml.mcpVersion', MCP_VERSION],
jvm: ["-DignoreList=${fmlonly_client.properties.ignoreList},\${version_name}.jar",
"-DmergeModules=${fmlonly_client.properties.mergeModules}",
'-DlibraryDirectory=${library_directory}',
'-p', Util.getArtifacts(project, configurations.moduleonly, false).values().collect{"\${library_directory}/${it.downloads.artifact.path}"}.join('${classpath_separator}'),
'--add-modules', 'ALL-MODULE-PATH',
// Additions to these JVM module args should be mirrored to server_files/args.txt and other similar blocks in the buildscript
'--add-opens', 'java.base/java.util.jar=cpw.mods.securejarhandler',
'--add-opens', 'java.base/java.lang.invoke=cpw.mods.securejarhandler',
'--add-exports', 'java.base/sun.security.util=cpw.mods.securejarhandler',
'--add-exports', 'jdk.naming.dns/com.sun.jndi.dns=java.naming'
]
],
libraries: []
]
}
}
task installerJson(type: InstallerJson, dependsOn: 'signUniversalJar') {
icon = rootProject.file('icon.ico')
packedDependencies = PACKED_DEPS
doFirst {
def libs = libraries
json = [
_comment_: launcherJson.comment,
spec: 1,
profile: project.name,
version: launcherJson.id,
path: null,
minecraft: MC_VERSION,
serverJarPath: '{LIBRARY_DIR}/net/minecraft/server/{MINECRAFT_VERSION}/server-{MINECRAFT_VERSION}.jar',
welcome: "Welcome to the simple FML installer.",
data: [
MAPPINGS: [
client: "[${MCP_ARTIFACT.group}:${MCP_ARTIFACT.name}:${MCP_ARTIFACT.version}:mappings@txt]",
server: "[${MCP_ARTIFACT.group}:${MCP_ARTIFACT.name}:${MCP_ARTIFACT.version}:mappings@txt]"
],
MOJMAPS: [
client: "[net.minecraft:client:${MC_VERSION}-${MCP_VERSION}:mappings@txt]",
server: "[net.minecraft:server:${MC_VERSION}-${MCP_VERSION}:mappings@txt]"
],
MERGED_MAPPINGS: [
client: "[${MCP_ARTIFACT.group}:${MCP_ARTIFACT.name}:${MCP_ARTIFACT.version}:mappings-merged@txt]",
server: "[${MCP_ARTIFACT.group}:${MCP_ARTIFACT.name}:${MCP_ARTIFACT.version}:mappings-merged@txt]"
],
MC_UNPACKED: [
client: "[net.minecraft:client:${MC_VERSION}-${MCP_VERSION}:unpacked]",
server: "[net.minecraft:server:${MC_VERSION}-${MCP_VERSION}:unpacked]"
],
MC_SLIM: [
client: "[net.minecraft:client:${MC_VERSION}-${MCP_VERSION}:slim]",
server: "[net.minecraft:server:${MC_VERSION}-${MCP_VERSION}:slim]"
],
MC_SLIM_SHA: [
client: "'${downloadClientSlim.output.get().asFile.sha1()}'",
server: "'${downloadServerSlim.output.get().asFile.sha1()}'"
],
MC_EXTRA: [
client: "[net.minecraft:client:${MC_VERSION}-${MCP_VERSION}:extra]",
server: "[net.minecraft:server:${MC_VERSION}-${MCP_VERSION}:extra]"
],
MC_EXTRA_SHA: [
client: "'${downloadClientExtra.output.get().asFile.sha1()}'",
server: "'${downloadServerExtra.output.get().asFile.sha1()}'"
],
MC_SRG: [
client: "[net.minecraft:client:${MC_VERSION}-${MCP_VERSION}:srg]",
server: "[net.minecraft:server:${MC_VERSION}-${MCP_VERSION}:srg]"
],
/*MC_SRG_SHA: [
client: "'${createClientSRG.output.sha1()}'",
server: "'${createServerSRG.output.sha1()}'"
],*/
MCP_VERSION: [
client: "'${MCP_VERSION}'",
server: "'${MCP_VERSION}'"
]
],
processors: [
[
sides: ['server'],
jar: INSTALLER_TOOLS,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'EXTRACT_FILES',
'--archive', '{INSTALLER}',
'--from', 'data/run.sh',
'--to', '{ROOT}/run.sh',
'--exec', '{ROOT}/run.sh',
//'--from', "data/run_fml_${VERSION.replace('-', '_').sh",
//'--to', '{ROOT}/run_fml_${VERSION.replace('-', '_').sh',
//'--exec', '{ROOT}/run_fml_${VERSION.replace('-', '_').sh',
'--from', 'data/run.bat',
'--to', '{ROOT}/run.bat',
//'--from', 'data/run_fml_${VERSION.replace('-', '_').bat',
//'--to', '{ROOT}/run_fml_${VERSION.replace('-', '_').bat',
'--from', 'data/user_jvm_args.txt',
'--to', '{ROOT}/user_jvm_args.txt',
'--optional', '{ROOT}/user_jvm_args.txt',
'--from', 'data/win_args.txt',
'--to', "{ROOT}/libraries/${MAVEN_PATH}/win_args.txt",
'--from', 'data/unix_args.txt',
'--to', "{ROOT}/libraries/${MAVEN_PATH}/unix_args.txt"
]
], [
sides: ['server'],
jar: INSTALLER_TOOLS,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'BUNDLER_EXTRACT',
'--input', '{MINECRAFT_JAR}',
'--output', '{ROOT}/libraries/',
'--libraries'
]
], [
sides: ['server'],
jar: INSTALLER_TOOLS,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'BUNDLER_EXTRACT',
'--input', '{MINECRAFT_JAR}',
'--output', '{MC_UNPACKED}',
'--jar-only'
]
], [
jar: INSTALLER_TOOLS,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'MCP_DATA',
'--input', "[${MCP_ARTIFACT.descriptor}]",
'--output', '{MAPPINGS}',
'--key', 'mappings'
]
], [
jar: INSTALLER_TOOLS,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'DOWNLOAD_MOJMAPS',
'--version', MC_VERSION,
'--side', '{SIDE}',
'--output', '{MOJMAPS}'
]
], [
jar: INSTALLER_TOOLS,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'MERGE_MAPPING',
'--left', '{MAPPINGS}',
'--right', '{MOJMAPS}',
'--output', '{MERGED_MAPPINGS}',
'--classes', '--reverse-right'
]
], [
sides: ['client'],
jar: JAR_SPLITTER,
classpath: getClasspath(project, libs, JAR_SPLITTER),
args: [
'--input', '{MINECRAFT_JAR}',
'--slim', '{MC_SLIM}',
'--extra', '{MC_EXTRA}',
'--srg', '{MERGED_MAPPINGS}'
],
outputs: [
'{MC_SLIM}': '{MC_SLIM_SHA}',
'{MC_EXTRA}': '{MC_EXTRA_SHA}'
]
], [
sides: ['server'],
jar: JAR_SPLITTER,
classpath: getClasspath(project, libs, JAR_SPLITTER),
args: [
'--input', '{MC_UNPACKED}',
'--slim', '{MC_SLIM}',
'--extra', '{MC_EXTRA}',
'--srg', '{MERGED_MAPPINGS}'
],
outputs: [
'{MC_SLIM}': '{MC_SLIM_SHA}',
'{MC_EXTRA}': '{MC_EXTRA_SHA}'
]
], [
jar: FART,
classpath: getClasspath(project, libs, FART),
args: [
'--input', '{MC_SLIM}',
'--output', '{MC_SRG}',
'--names', '{MERGED_MAPPINGS}',
'--ann-fix', '--ids-fix', '--src-fix', '--record-fix'
]/*,
outputs: [
'{MC_SRG}': '{MC_SRG_SHA}'
]
*/
]
]
]
getClasspath(project, libs, MCP_ARTIFACT.descriptor) //Tell it to download mcp_config
}
}
task installerJar(type: InstallerJar) {
packedDependencies = PACKED_DEPS
from(extraTxts)
//TODO: make FML installer logo?
from(rootProject.file('/forge_installer_logo.png')) {
rename { 'big_logo.png' }
}
}
publishing {
publications {
mavenJava(MavenPublication) {
artifact universalJar
artifact installerJar
artifact userdevJar
artifact sourcesJar
}
}
}
// Since we need the modules in the bootstrap, we need to make sure they are compiled before we do each run
afterEvaluate { prepareRuns.dependsOn(PACKED_DEPS) }
}
project(':forge') {
evaluationDependsOn(':clean')
evaluationDependsOn(HAS_CLEAN ? ':clean' : ':mcp')
apply plugin: 'java-library'
apply plugin: 'net.minecraftforge.gradle.patcher'
apply plugin: 'org.cadixdev.licenser'
@@ -608,12 +931,8 @@ project(':forge') {
}
}
}
eclipse.classpath.file.whenMerged {
// Disable optional warnings on the minecraft decompiled source code. It just muddies the warning window and hides warnings in our own codebase
def src = entries.find { it.path == 'src/main/java' }
src.entryAttributes['ignore_optional_problems'] = 'true'
}
//Eclipse adds the sourcesets twice, once where we tell it to, once in the projects folder. No idea why. So delete them
eclipse.classpath.file.whenMerged { cls -> cls.entries.removeIf { e -> e instanceof SourceFolder && e.path.startsWith('src/') && !e.path.startsWith('src/main/') } }
ext {
SPEC_VERSION = gradleutils.gitInfo.tag
@@ -652,16 +971,17 @@ project(':forge') {
patcher {
excs.from file("$rootDir/src/main/resources/forge.exc")
parent = project(':clean')
parent = project(HAS_CLEAN ? ':clean' : ':mcp')
patches = file("$rootDir/patches/minecraft")
patchedSrc = file('src/main/java')
srgPatches = false
srgPatches = true
accessTransformers.from file("$rootDir/src/main/resources/META-INF/accesstransformer.cfg")
sideAnnotationStrippers.from file("$rootDir/src/main/resources/forge.sas")
mappings channel: MAPPING_CHANNEL, version: MAPPING_VERSION
mcVersion = MC_VERSION
runs {
forge_client {
workingDirectory file('run/client')
property 'eventbus.checkTypesOnDispatch', 'true'
args '--launchTarget', 'forgeclientdev'
ideaModule "${rootProject.name}.${project.name}.main"
@@ -671,7 +991,6 @@ project(':forge') {
}
forge_test_client {
workingDirectory file('run/client_test')
parent runs.forge_client
taskName 'forge_test_client'
@@ -686,7 +1005,6 @@ project(':forge') {
}
forge_server {
workingDirectory file('run/server')
args '--launchTarget', 'forgeserverdev'
ideaModule "${rootProject.name}.${project.name}.main"
@@ -696,7 +1014,6 @@ project(':forge') {
}
forge_test_server {
workingDirectory file('run/server_test')
parent runs.forge_server
taskName 'forge_test_server'
@@ -769,20 +1086,19 @@ project(':forge') {
}
args '--flat', '--all', '--validate',
'--mod', 'empty',
//'--mod', 'data_gen_test',
//'--mod', 'piston_event_test',
'--mod', 'data_gen_test',
'--mod', 'piston_event_test',
'--mod', 'global_loot_test',
//'--mod', 'scaffolding_test',
//'--mod', 'custom_tag_types_test',
//'--mod', 'new_model_loader_test',
//'--mod', 'remove_tag_datagen_test',
//'--mod', 'tag_based_tool_types',
//'--mod', 'custom_transformtype_test',
//'--mod', 'data_pack_registries_test',
//'--mod', 'biome_modifiers_test',
//'--mod', 'structure_modifiers_test',
//'--mod', 'custom_preset_editor_test',
'--mod', 'scaffolding_test',
'--mod', 'custom_tag_types_test',
'--mod', 'new_model_loader_test',
'--mod', 'remove_tag_datagen_test',
'--mod', 'tag_based_tool_types',
'--mod', 'custom_transformtype_test',
'--mod', 'data_pack_registries_test',
'--mod', 'biome_modifiers_test',
'--mod', 'structure_modifiers_test',
'--mod', 'custom_preset_editor_test',
'--output', rootProject.file('src/generated_test/resources/'),
'--existing', sourceSets.main.resources.srcDirs[0],
'--existing', sourceSets.test.resources.srcDirs[0]
@@ -816,33 +1132,6 @@ project(':forge') {
}
}
}
afterEvaluate {
if (!patcher.srgPatches) {
srg2mcpClean {
dependsOn = []
input = project(':mcp').setupMCP.output
}
userdevJar {
onlyIf = { t -> true }
}
def patches = project.file('build/genPatchesForUserdev/output/')
patches.mkdirs()
def genPatchesForUserdev = tasks.register('genPatchesForUserdev', GeneratePatches){
base = project(':mcp').setupMCP.output
modified = applyRangeMapBase.output
originalPrefix = genPatches.originalPrefix
modifiedPrefix = genPatches.modifiedPrefix
output = patches
autoHeader true
lineEnding = '\n'
}
bakePatches {
dependsOn = []
input = genPatchesForUserdev.get().output
}
}
}
ext {
MANIFESTS = [
@@ -895,9 +1184,12 @@ project(':forge') {
]
}
task crowdin(type: Crowdin) {
key = project.hasProperty('crowdinKey') ? project.crowdinKey : null
//TODO: Merge our english.json?
tasks.register('downloadCrowdin', Download) {
src 'https://files.minecraftforge.net/crowdin.zip'
dest file('build/crowdin.zip')
useETag 'all'
onlyIfModified true
quiet true
}
sharedFmlonlyForge.call(project)
@@ -1099,8 +1391,6 @@ project(':forge') {
doFirst {
def libs = libraries
def INSTALLER_TOOLS_CLASSPATH = getClasspath(project, libs, INSTALLER_TOOLS)
def JAR_SPLITTER_CLASSPATH = getClasspath(project, libs, JAR_SPLITTER)
json = [
_comment_: launcherJson.comment,
spec: 1,
@@ -1171,7 +1461,7 @@ project(':forge') {
[
sides: ['server'],
jar: INSTALLER_TOOLS,
classpath: INSTALLER_TOOLS_CLASSPATH,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'EXTRACT_FILES',
'--archive', '{INSTALLER}',
@@ -1201,7 +1491,7 @@ project(':forge') {
], [
sides: ['server'],
jar: INSTALLER_TOOLS,
classpath: INSTALLER_TOOLS_CLASSPATH,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'BUNDLER_EXTRACT',
'--input', '{MINECRAFT_JAR}',
@@ -1211,7 +1501,7 @@ project(':forge') {
], [
sides: ['server'],
jar: INSTALLER_TOOLS,
classpath: INSTALLER_TOOLS_CLASSPATH,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'BUNDLER_EXTRACT',
'--input', '{MINECRAFT_JAR}',
@@ -1220,7 +1510,7 @@ project(':forge') {
]
], [
jar: INSTALLER_TOOLS,
classpath: INSTALLER_TOOLS_CLASSPATH,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'MCP_DATA',
'--input', "[${MCP_ARTIFACT.descriptor}]",
@@ -1229,7 +1519,7 @@ project(':forge') {
]
], [
jar: INSTALLER_TOOLS,
classpath: INSTALLER_TOOLS_CLASSPATH,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'DOWNLOAD_MOJMAPS',
'--version', MC_VERSION,
@@ -1238,7 +1528,7 @@ project(':forge') {
]
], [
jar: INSTALLER_TOOLS,
classpath: INSTALLER_TOOLS_CLASSPATH,
classpath: getClasspath(project, libs, INSTALLER_TOOLS),
args: [
'--task', 'MERGE_MAPPING',
'--left', '{MAPPINGS}',
@@ -1249,7 +1539,7 @@ project(':forge') {
], [
sides: ['client'],
jar: JAR_SPLITTER,
classpath: JAR_SPLITTER_CLASSPATH,
classpath: getClasspath(project, libs, JAR_SPLITTER),
args: [
'--input', '{MINECRAFT_JAR}',
'--slim', '{MC_SLIM}',
@@ -1263,7 +1553,7 @@ project(':forge') {
], [
sides: ['server'],
jar: JAR_SPLITTER,
classpath: JAR_SPLITTER_CLASSPATH,
classpath: getClasspath(project, libs, JAR_SPLITTER),
args: [
'--input', '{MC_UNPACKED}',
'--slim', '{MC_SLIM}',
@@ -1310,9 +1600,12 @@ project(':forge') {
from extraTxts
// add crowdin locales
from { crowdin.output.present && crowdin.output.get().asFile.exists() ? zipTree(crowdin.output) : null}
dependsOn crowdin
duplicatesStrategy = 'exclude'
from zipTree(downloadCrowdin.dest).matching {
include 'assets/forge/lang/*.json'
}
dependsOn downloadCrowdin
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
doFirst {
MANIFESTS.each{ pkg, values ->
@@ -1339,6 +1632,12 @@ project(':forge') {
}
}
final mdkGradleWrapper = tasks.register('mdkGradleWrapper', Wrapper) {
gradleVersion = "9.8.2"
scriptFile = layout.buildDirectory.dir("wrapper/" + gradleVersion + "/gradlew")
jarFile = layout.buildDirectory.dir("wrapper/" + gradleVersion + "/gradle/wrapper/gradle-wrapper.jar")
}
task makeMdk(type: Zip) {
archiveBaseName = project.name
archiveClassifier = 'mdk'
@@ -1348,8 +1647,9 @@ project(':forge') {
from rootProject.file('gradlew')
from rootProject.file('gradlew.bat')
from extraTxts
from(rootProject.file('gradle/')){
into('gradle/')
into('gradle/wrapper/') {
from mdkGradleWrapper.map(Wrapper.&getJarFile)
from mdkGradleWrapper.map(Wrapper.&getPropertiesFile)
}
from(rootProject.file('mdk/')){
rootProject.file('mdk/gitignore.txt').eachLine{
@@ -1507,7 +1807,10 @@ project(':forge') {
//evaluationDependsOnChildren()
task setup() { //These must be strings so that we can do lazy resolution. Else we need evaluationDependsOnChildren above
dependsOn ':clean:extractMapped'
if (HAS_CLEAN)
dependsOn ':clean:extractMapped'
if (symlinkValid)
dependsOn ':fmlonly:extractMapped'
dependsOn ':forge:extractMapped'
}
@@ -1516,27 +1819,16 @@ task doChecks {
dependsOn ':forge:publish'
}
project.gradle.startParameter.excludedTaskNames.add(':clean:genPatches')
if (symlinkValid)
project.gradle.startParameter.excludedTaskNames.add(':fmlonly:genPatches')
if (System.env.TEAMCITY_VERSION) {
//Only setup the CI environment if and only if the environment variables are set.
tasks.configureTeamCity {
doLast {
println "##teamcity[buildNumber '${project(':forge').version}']"
println "##teamcity[setParameter name='env.PUBLISHED_JAVA_ARTIFACT_VERSION' value='${project(':forge').version}']"
}
}
}
allprojects { prj ->
prj.tasks.eclipse.doFirst {
rootProject.fileTree('ide/eclipse/template/.settings/').matching { include '**/*.prefs' }.each { file ->
def target = project.file('.settings/' + file.name)
def temp = new CleanProperties().load(file)
def exst = new CleanProperties().load(target)
exst.put('eclipse.preferences.version', '1')
temp.forEach(exst::put)
exst.store(target)
}
if (HAS_CLEAN)
project.gradle.startParameter.excludedTaskNames.add(':clean:genPatches')
idea {
module {
// IntelliJ IDEA does not do this by itself anymore...
downloadJavadoc = true
downloadSources = true
}
}
+3 -3
View File
@@ -4,9 +4,9 @@ repositories {
}
dependencies {
implementation 'org.ow2.asm:asm:9.5'
implementation 'org.ow2.asm:asm-tree:9.5'
implementation 'net.minecraftforge:srgutils:0.5.+'
implementation 'org.ow2.asm:asm:9.9.1'
implementation 'org.ow2.asm:asm-tree:9.9.1'
implementation 'net.minecraftforge:srgutils:0.6.5'
implementation 'commons-io:commons-io:2.12.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r'
@@ -27,6 +27,6 @@ abstract class BytecodePredicateFinder extends BytecodeFinder {
@Internal
@Override
protected Object getData() {
return matches.toSorted(Comparator.naturalOrder()) ?: {throw new RuntimeException('Failed to find any targets, please ensure that method names and descriptors are correct.')}()
return matches.stream().sorted().toList() ?: {throw new RuntimeException('Failed to find any targets, please ensure that method names and descriptors are correct.')}()
}
}
@@ -1,92 +0,0 @@
package net.minecraftforge.forge.tasks;
import java.util.Properties;
import java.util.Enumeration;
import java.util.Map;
import java.util.TreeSet;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.Reader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Set;
/**
* Eclipse config files are literally just java properties, with the header cleaned up.
* https://github.com/eclipse/buildship/blob/5b2c7fca7fa86cd74d71b3c099c7b1559eba038e/org.eclipse.buildship.core/src/main/java/org/eclipse/buildship/core/internal/configuration/PreferenceStore.java#L238
*
* This does the same thing, as well as sorting alphabetically.
* It also ignores all comments. We can add them latter if someone cares.
*/
public class CleanProperties extends Properties {
private static final long serialVersionUID = 1L;
private static final String LINE_SEP = System.getProperty("line.separator");
private static final String UNIX_LINE_SEP = "\n";
private static final Charset ENCODING = StandardCharsets.UTF_8;
public CleanProperties load(File input) throws IOException {
if (input.exists()) {
try (Reader is = new InputStreamReader(new FileInputStream(input), ENCODING)) {
super.load(is);
}
}
return this;
}
public void store(File out) throws IOException {
if (!out.getParentFile().exists())
out.getParentFile().mkdirs();
try (OutputStream os = new FileOutputStream(out)) {
store(os, null);
}
}
@Override
public synchronized Enumeration<Object> keys() {
Set<Object> ret = new TreeSet<>();
for (Enumeration<?> e = super.keys(); e.hasMoreElements();)
ret.add(e.nextElement());
return Collections.enumeration(ret);
}
@Override
public Set<Map.Entry<Object, Object>> entrySet() {
Set<Map.Entry<Object, Object>> ret = new TreeSet<>((l, r) -> ((String)l.getKey()).compareTo((String)r.getKey()));
ret.addAll(super.entrySet());
return ret;
}
@Override
public void store(OutputStream out, String comments) throws IOException {
out.write(clean().getBytes(ENCODING));
out.flush();
}
@Override
public void store(Writer out, String comments) throws IOException {
out.write(clean());
out.flush();
}
private String clean() throws IOException {
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
try {
super.store(tmp, null);
} finally {
tmp.close();
}
String ret = tmp.toString(ENCODING).replace(LINE_SEP, UNIX_LINE_SEP);
ret = ret.substring(ret.indexOf(UNIX_LINE_SEP) + 1);
return ret;
}
}
+30 -11
View File
@@ -2,16 +2,15 @@
MinecraftForge
=============
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.20.1&query=$.promos["1.20.1-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)][Download]
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.20.1-&query=$.promos["1.20.1-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)][Download]
[![Latest Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Latest&prefix=1.20.1-&query=$.promos["1.20.1-latest"]&color=blue&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYnVnIGZhLXctMTYiIGFyaWEtaGlkZGVuPSJ0cnVlIiBkYXRhLWljb249ImJ1ZyIgZGF0YS1wcmVmaXg9ImZhcyIgZm9jdXNhYmxlPSJmYWxzZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUxMS45ODggMjg4LjljLS40NzggMTcuNDMtMTUuMjE3IDMxLjEtMzIuNjUzIDMxLjFINDI0djE2YzAgMjEuODY0LTQuODgyIDQyLjU4NC0xMy42IDYxLjE0NWw2MC4yMjggNjAuMjI4YzEyLjQ5NiAxMi40OTcgMTIuNDk2IDMyLjc1OCAwIDQ1LjI1NS0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDBsLTU0LjczNi01NC43MzZDMzQ1Ljg4NiA0NjcuOTY1IDMxNC4zNTEgNDgwIDI4MCA0ODBWMjM2YzAtNi42MjctNS4zNzMtMTItMTItMTJoLTI0Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0NGMtMzQuMzUxIDAtNjUuODg2LTEyLjAzNS05MC42MzYtMzIuMTA4bC01NC43MzYgNTQuNzM2Yy0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDAtMTIuNDk2LTEyLjQ5Ny0xMi40OTYtMzIuNzU4IDAtNDUuMjU1bDYwLjIyOC02MC4yMjhDOTIuODgyIDM3OC41ODQgODggMzU3Ljg2NCA4OCAzMzZ2LTE2SDMyLjY2NkMxNS4yMyAzMjAgLjQ5MSAzMDYuMzMuMDEzIDI4OC45LS40ODQgMjcwLjgxNiAxNC4wMjggMjU2IDMyIDI1Nmg1NnYtNTguNzQ1bC00Ni42MjgtNDYuNjI4Yy0xMi40OTYtMTIuNDk3LTEyLjQ5Ni0zMi43NTggMC00NS4yNTUgMTIuNDk4LTEyLjQ5NyAzMi43NTgtMTIuNDk3IDQ1LjI1NiAwTDE0MS4yNTUgMTYwaDIyOS40ODlsNTQuNjI3LTU0LjYyN2MxMi40OTgtMTIuNDk3IDMyLjc1OC0xMi40OTcgNDUuMjU2IDAgMTIuNDk2IDEyLjQ5NyAxMi40OTYgMzIuNzU4IDAgNDUuMjU1TDQyNCAxOTcuMjU1VjI1Nmg1NmMxNy45NzIgMCAzMi40ODQgMTQuODE2IDMxLjk4OCAzMi45ek0yNTcgMGMtNjEuODU2IDAtMTEyIDUwLjE0NC0xMTIgMTEyaDIyNEMzNjkgNTAuMTQ0IDMxOC44NTYgMCAyNTcgMHoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=
)][Download] [![Discord](https://img.shields.io/discord/313125603924639766.svg?color=%237289da&label=Discord&logo=discord&logoColor=%237289da)][Discord] [![Support](https://img.shields.io/badge/Patreon-Support-orange.svg?logo=Patreon)][Patreon]
Forge is a free, open-source modding API all of your favourite mods use!
| Version | Support |
|---------| ------------- |
| 1.20.x | Active |
| 1.19.x | Legacy |
| Version | Support |
|--------------|-----------------------------|
| All versions | [Supported][Support-Policy] |
* [Download]
* [Forum]
@@ -27,10 +26,23 @@ You can download the installer for the *Recommended Build* or the
*Latest build* there. Latest builds may have newer features but may be
more unstable as a result. The installer will attempt to install Forge
into your vanilla launcher environment, where you can then create a new
profile using that version and play the game!
profile using that version and play the game!
For support and questions, visit [the Support Forum][Support-Forum] or [the Forge Discord server][Discord].
## Required Java Versions
We require the version of Java that Minecraft requires, which is currently:
| Minecraft | Java |
|---------------|-------------------|
| 1.20.6+ | [21][Adoptium-21] |
| 1.18 - 1.20.4 | [17][Adoptium-17] |
| 1.17.1 | [16][Adoptium-16] |
| 1.16.5- | [8][Adoptium-8] |
**If you are on Windows and have difficulty using Java to launch JAR files, consider using [Jarfix by Johann Löfflmann][Jarfix].**
# Creating Mods
[See the "Getting Started" section in the Forge Documentation][Getting-Started].
@@ -63,14 +75,21 @@ Please ensure you have a valid email address associated with your GitHub account
[Download]: https://files.minecraftforge.net/
[Adoptium-21]: https://adoptium.net/temurin/releases/?version=21
[Adoptium-17]: https://adoptium.net/temurin/releases/?version=17
[Adoptium-16]: https://adoptium.net/temurin/releases/?version=16
[Adoptium-8]: https://adoptium.net/temurin/releases/?version=8
[Jarfix]: https://johann.loefflmann.net/en/software/jarfix/index.html
[Forum]: https://forums.minecraftforge.net/
[Support-Forum]: https://forums.minecraftforge.net/forum/18-support-bug-reports/
[Support-Policy]: https://forums.minecraftforge.net/topic/144690-new-tiered-support-policy
[Discord]: https://discord.minecraftforge.net/
[Documentation]: https://docs.minecraftforge.net/
[Getting-Started]: https://docs.minecraftforge.net/en/latest/gettingstarted/
[ForgeDev]: https://docs.minecraftforge.net/en/latest/forgedev/
[Pull-Requests]: https://docs.minecraftforge.net/en/latest/forgedev/#making-changes-and-pull-requests
[Documentation]: https://docs.minecraftforge.net/en/1.20.1/
[Getting-Started]: https://docs.minecraftforge.net/en/1.20.1/gettingstarted/
[ForgeDev]: https://docs.minecraftforge.net/en/1.20.1/forgedev/
[Pull-Requests]: https://docs.minecraftforge.net/en/1.20.1/forgedev/#making-changes-and-pull-requests
[Patreon]: https://www.patreon.com/MinecraftForge
[Patreon]: https://www.patreon.com/LexManos
+3 -2
View File
@@ -2,9 +2,10 @@ plugins {
id 'com.github.ben-manes.versions'
id 'org.javamodularity.moduleplugin' version '1.8.3' apply false
id 'org.cadixdev.licenser'
id 'java-library'
id 'jacoco'
}
apply plugin: 'java-library'
apply plugin: 'jacoco'
apply plugin: 'org.javamodularity.moduleplugin'
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
@@ -5,36 +5,39 @@
package net.minecraftforge.fml;
import cpw.mods.modlauncher.util.ServiceLoaderUtils;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.config.IConfigEvent;
import net.minecraftforge.fml.loading.FMLLoader;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.function.Supplier;
/**
* Used to allow access to certain things from the game layer
*/
public class Bindings {
private static final Bindings INSTANCE = new Bindings();
private Bindings() {}
private final IBindingsProvider provider;
private Bindings() {
final var providers = ServiceLoaderUtils.streamServiceLoader(()->ServiceLoader.load(FMLLoader.getGameLayer(), IBindingsProvider.class), sce->{}).toList();
if (providers.size() != 1) {
throw new IllegalStateException("Could not find bindings provider");
}
this.provider = providers.get(0);
}
/**
* The provider of the bindings
* @implNote May also throw a {@link ServiceConfigurationError}
*/
private static final IBindingsProvider PROVIDER = ServiceLoader.load(FMLLoader.getGameLayer(), IBindingsProvider.class)
.findFirst().orElseThrow(() -> new IllegalStateException("Could not find bindings provider"));
/**
* @return A supplier of net.minecraftforge.common.MinecraftForge#EVENT_BUS
*/
public static Supplier<IEventBus> getForgeBus() {
return INSTANCE.provider.getForgeBusSupplier();
return PROVIDER.getForgeBusSupplier();
}
public static Supplier<I18NParser> getMessageParser() {
return INSTANCE.provider.getMessageParser();
return PROVIDER.getMessageParser();
}
public static Supplier<IConfigEvent.ConfigConfig> getConfigConfiguration() {
return INSTANCE.provider.getConfigConfiguration();
return PROVIDER.getConfigConfiguration();
}
}
@@ -25,7 +25,6 @@ import java.util.function.Supplier;
* @param <T> the type of the record which is held by the extension point
*/
@SuppressWarnings("unused") // Type parameter T
// TODO: Complete re-write, this hasnt actually be used in a generic way by mods and we could make it a lot simpler.
public interface IExtensionPoint<T extends Record>
{
/**
@@ -41,7 +40,7 @@ public interface IExtensionPoint<T extends Record>
* where {@code true} means it is from the server and {@code false} means it is from the world save. The return
* value of the predicate determines whether the remote version is "compatible" for the purposes of the display test.</p>
*
* <p>The local compatibility version may be of the value {@link DisplayTest#IGNORESERVERONLY},
* <p>The local compatibility version may be of the value {@link net.minecraftforge.network.NetworkConstants#IGNORESERVERONLY},
* in which case clients will ignore the mod's presence if it is present on the server but not on the client.
* However, the remote version test predicate must still accept this value as a remote version in order to display
* as compatible if the mod is present on the client.</p>
@@ -95,6 +94,25 @@ public interface IExtensionPoint<T extends Record>
*/
@SuppressWarnings("JavadocReference") // reference to NetworkConstants, ForgeHooksClient
record DisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) implements IExtensionPoint<DisplayTest> {
public static final String IGNORESERVERONLY = "SERVER_ONLY";
public static final String IGNORESERVERONLY = "OHNOES\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31";
/**
* Ignores any version information coming from the server - use for server only mods
*/
public static final Supplier<DisplayTest> IGNORE_SERVER_VERSION = () -> new DisplayTest(IGNORESERVERONLY, (remoteVersion, isFromServer) -> true);
/**
* Ignores all information and provides no information
*/
public static final Supplier<DisplayTest> IGNORE_ALL_VERSION = () -> new DisplayTest("", (remoteVersion, isFromServer) -> true);
/**
* An optional alternative to {@link #DisplayTest(Supplier, BiPredicate)} which accepts a constant version string
* instead of a {@link Supplier}.
* <p>Internally, the provided version string is wrapped in a Supplier for you.</p>
*/
public DisplayTest(String version, BiPredicate<String, Boolean> remoteVersionTest) {
this(() -> version, remoteVersionTest);
}
}
}
@@ -9,6 +9,7 @@ import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.config.IConfigEvent;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.IModBusEvent;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.loading.progress.ProgressMeter;
import net.minecraftforge.forgespi.language.IModInfo;
@@ -21,6 +22,7 @@ import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -58,15 +60,16 @@ public abstract class ModContainer
this.modInfo = info;
this.modLoadingStage = ModLoadingStage.CONSTRUCT;
final String displayTestString = info.getConfig().<String>getConfigElement("displayTest").orElse("MATCH_VERSION"); // missing defaults to DEFAULT type
final String displayTestString = info.getConfig().<String>getConfigElement("displayTest")
.orElseGet(() -> (Boolean) info.getOwningFile().getFileProperties().getOrDefault(ModFileInfo.CLIENT_SIDE_ONLY_PROP, Boolean.FALSE) ? "IGNORE_ALL_VERSION" : "MATCH_VERSION");
Supplier<IExtensionPoint.DisplayTest> displayTestSupplier = switch (displayTestString) {
case "MATCH_VERSION" -> // default displaytest checks for version string match
() -> new IExtensionPoint.DisplayTest(() -> this.modInfo.getVersion().toString(),
(incoming, isNetwork) -> Objects.equals(incoming, this.modInfo.getVersion().toString()));
case "IGNORE_SERVER_VERSION" -> // Ignores any version information coming from the server - use for server only mods
() -> new IExtensionPoint.DisplayTest(() -> IExtensionPoint.DisplayTest.IGNORESERVERONLY, (incoming, isNetwork) -> true);
IExtensionPoint.DisplayTest.IGNORE_SERVER_VERSION;
case "IGNORE_ALL_VERSION" -> // Ignores all information and provides no information
() -> new IExtensionPoint.DisplayTest(() -> "", (incoming, isNetwork) -> true);
IExtensionPoint.DisplayTest.IGNORE_ALL_VERSION;
case "NONE" -> null; // NO display test at all - use this if you're going to do your own display test
default -> // any other value throws an exception
throw new IllegalArgumentException("Invalid displayTest value supplied in mods.toml");
@@ -145,6 +148,22 @@ public abstract class ModContainer
extensionPoints.put(point, extension);
}
public void registerDisplayTest(IExtensionPoint.DisplayTest displayTest) {
registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> displayTest);
}
public void registerDisplayTest(Supplier<IExtensionPoint.DisplayTest> displayTest) {
registerExtensionPoint(IExtensionPoint.DisplayTest.class, displayTest);
}
public void registerDisplayTest(String version, BiPredicate<String, Boolean> remoteVersionTest) {
registerDisplayTest(new IExtensionPoint.DisplayTest(version, remoteVersionTest));
}
public void registerDisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) {
registerDisplayTest(new IExtensionPoint.DisplayTest(suppliedVersion, remoteVersionTest));
}
public void addConfig(final ModConfig modConfig) {
configs.put(modConfig.getType(), modConfig);
}
@@ -8,38 +8,54 @@ package net.minecraftforge.fml;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.config.IConfigSpec;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.loading.FMLLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.BiPredicate;
import java.util.function.Supplier;
public class ModLoadingContext
{
private static final Logger LOGGER = LogUtils.getLogger();
private static final ThreadLocal<ModLoadingContext> context = ThreadLocal.withInitial(ModLoadingContext::new);
private ModContainer activeContainer;
private Object languageExtension;
private ModLoadingStage stage;
/**
* @deprecated Use the context provided by your language loader in your mod's constructor.
*/
@Deprecated(forRemoval = true, since = "1.21.1")
public static ModLoadingContext get() {
return context.get();
}
private ModContainer activeContainer;
/**
* @deprecated Going to be moved to ForgeHooks for internal use.
*/
@Deprecated(forRemoval = true, since = "1.21.1")
public void setActiveContainer(final ModContainer container) {
this.activeContainer = container;
this.languageExtension = container == null ? null : container.contextExtension.get();
}
/**
* Going to be moved to ForgeHooks for internal use.
* @deprecated Override/Use {@link ModLoadingContext#getContainer()}
*/
@Deprecated(forRemoval = true, since = "1.21.1")
public ModContainer getActiveContainer() {
return activeContainer == null ? ModList.get().getModContainerById("minecraft").orElseThrow(()->new RuntimeException("Where is minecraft???!")) : activeContainer;
}
@Deprecated(forRemoval = true, since = "1.21.1")
public String getActiveNamespace() {
return activeContainer == null ? "minecraft" : activeContainer.getNamespace();
}
public ModContainer getContainer() {
return getActiveContainer();
}
/**
* Register an {@link IExtensionPoint} with the mod container.
* @param point The extension point to register
@@ -47,29 +63,67 @@ public class ModLoadingContext
* @param <T> The type signature of the extension operator
*/
public <T extends Record & IExtensionPoint<T>> void registerExtensionPoint(Class<? extends IExtensionPoint<T>> point, Supplier<T> extension) {
getActiveContainer().registerExtensionPoint(point, extension);
getContainer().registerExtensionPoint(point, extension);
}
/**
* Register a {@link IExtensionPoint.DisplayTest} with the mod container.
* <p>A shorthand for registering a DisplayTest with {@link #registerExtensionPoint(Class, Supplier)}.</p>
* @param displayTest The {@link IExtensionPoint.DisplayTest} to register
*/
public void registerDisplayTest(IExtensionPoint.DisplayTest displayTest) {
getContainer().registerDisplayTest(() -> displayTest);
}
/**
* Register a {@link IExtensionPoint.DisplayTest} with the mod container.
* <p>A shorthand for registering a DisplayTest supplier with {@link #registerExtensionPoint(Class, Supplier)}.</p>
* @param displayTest The {@link Supplier<IExtensionPoint.DisplayTest>} to register
*/
public void registerDisplayTest(Supplier<IExtensionPoint.DisplayTest> displayTest) {
getContainer().registerDisplayTest(displayTest);
}
/**
* Register a {@link IExtensionPoint.DisplayTest} with the mod container.
* <p>A shorthand for registering a DisplayTest with {@link #registerExtensionPoint(Class, Supplier)} that also
* creates the DisplayTest instance for you using the provided parameters.</p>
* @see IExtensionPoint.DisplayTest#DisplayTest(String, BiPredicate)
*/
public void registerDisplayTest(String version, BiPredicate<String, Boolean> remoteVersionTest) {
getContainer().registerDisplayTest(new IExtensionPoint.DisplayTest(version, remoteVersionTest));
}
/**
* Register a {@link IExtensionPoint.DisplayTest} with the mod container.
* <p>A shorthand for registering a DisplayTest with {@link #registerExtensionPoint(Class, Supplier)} that also
* creates the DisplayTest instance for you using the provided parameters.</p>
* @see IExtensionPoint.DisplayTest#DisplayTest(Supplier, BiPredicate)
*/
public void registerDisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) {
getContainer().registerDisplayTest(new IExtensionPoint.DisplayTest(suppliedVersion, remoteVersionTest));
}
public void registerConfig(ModConfig.Type type, IConfigSpec<?> spec) {
if (spec.isEmpty())
{
// This handles the case where a mod tries to register a config, without any options configured inside it.
LOGGER.debug("Attempted to register an empty config for type {} on mod {}", type, getActiveContainer().getModId());
LOGGER.debug("Attempted to register an empty config for type {} on mod {}", type, getContainer().getModId());
return;
}
getActiveContainer().addConfig(new ModConfig(type, spec, getActiveContainer()));
getContainer().addConfig(new ModConfig(type, spec, getContainer()));
}
public void registerConfig(ModConfig.Type type, IConfigSpec<?> spec, String fileName) {
if (spec.isEmpty())
{
// This handles the case where a mod tries to register a config, without any options configured inside it.
LOGGER.debug("Attempted to register an empty config for type {} on mod {} using file name {}", type, getActiveContainer().getModId(), fileName);
LOGGER.debug("Attempted to register an empty config for type {} on mod {} using file name {}", type, getContainer().getModId(), fileName);
return;
}
getActiveContainer().addConfig(new ModConfig(type, spec, getActiveContainer(), fileName));
getContainer().addConfig(new ModConfig(type, spec, getContainer(), fileName));
}
@@ -51,8 +51,8 @@ public class VersionChecker
BETA(),
BETA_OUTDATED(6, true);
final int sheetOffset;
final boolean draw, animated;
private final int sheetOffset;
private final boolean draw, animated;
Status()
{
@@ -91,6 +91,9 @@ public class VersionChecker
return animated;
}
public boolean isOutdated() {
return this == OUTDATED || this == BETA_OUTDATED;
}
}
public record CheckResult(VersionChecker.Status status, ComparableVersion target, Map<ComparableVersion, String> changes, String url) {}
@@ -257,7 +260,7 @@ public class VersionChecker
LOGGER.warn("Failed to process update information", e);
status = FAILED;
}
results.put(mod, new CheckResult(status, target, changes, display_url));
RESULTS.put(mod, new CheckResult(status, target, changes, display_url));
}
}.start();
}
@@ -273,12 +276,12 @@ public class VersionChecker
return ret;
}
private static Map<IModInfo, CheckResult> results = new ConcurrentHashMap<>();
private static final Map<IModInfo, CheckResult> RESULTS = new ConcurrentHashMap<>();
private static final CheckResult PENDING_CHECK = new CheckResult(PENDING, null, null, null);
public static CheckResult getResult(IModInfo mod)
{
return results.getOrDefault(mod, PENDING_CHECK);
return RESULTS.getOrDefault(mod, PENDING_CHECK);
}
}
+3 -2
View File
@@ -2,9 +2,10 @@ plugins {
id 'com.github.ben-manes.versions'
id 'org.javamodularity.moduleplugin' version '1.8.7' apply false
id 'org.cadixdev.licenser'
id 'java-library'
id 'jacoco'
}
apply plugin: 'java-library'
apply plugin: 'jacoco'
apply plugin: 'org.javamodularity.moduleplugin'
import org.gradle.internal.os.OperatingSystem
@@ -14,9 +14,7 @@ import net.minecraftforge.fml.loading.ImmediateWindowProvider;
import net.minecraftforge.fml.loading.progress.StartupNotificationManager;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFWImage;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.stb.STBImage;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
@@ -30,9 +28,8 @@ import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -40,15 +37,16 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
@@ -78,7 +76,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
private static final Logger LOGGER = LoggerFactory.getLogger("EARLYDISPLAY");
private final AtomicBoolean animationTimerTrigger = new AtomicBoolean(true);
private ColourScheme colourScheme;
private ColourScheme colourScheme = ColourScheme.RED;
private ElementShader elementShader;
private RenderElement.DisplayContext context;
@@ -86,6 +84,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
private int framecount;
private EarlyFramebuffer framebuffer;
private ScheduledFuture<?> windowTick;
private ScheduledFuture<?> initializationFuture;
private PerformanceInfo performanceInfo;
private ScheduledFuture<?> performanceTick;
@@ -134,13 +133,17 @@ public class DisplayWindow implements ImmediateWindowProvider {
this.colourScheme = ColourScheme.BLACK;
} else {
try {
var optionLines = Files.readAllLines(FMLPaths.GAMEDIR.get().resolve(Paths.get("options.txt")));
var options = optionLines.stream().map(l -> l.split(":")).filter(a -> a.length == 2).collect(Collectors.toMap(a -> a[0], a -> a[1]));
var colourScheme = Boolean.parseBoolean(options.getOrDefault("darkMojangStudiosBackground", "false"));
this.colourScheme = colourScheme ? ColourScheme.BLACK : ColourScheme.RED;
} catch (IOException ioe) {
// No options
this.colourScheme = ColourScheme.RED; // default to red colourscheme
// check the options file for the colour scheme
var optionLines = Files.readAllLines(FMLPaths.GAMEDIR.get().resolve(Path.of("options.txt")));
var keyName = "darkMojangStudiosBackground:";
for (String line : optionLines) {
if (line.startsWith(keyName)) {
this.colourScheme = line.startsWith("true", keyName.length()) ? ColourScheme.BLACK : ColourScheme.RED;
break;
}
}
} catch (IOException e) {
this.colourScheme = ColourScheme.RED; // fallback to red colourScheme
}
}
this.maximized = parsed.has(maximizedopt) || FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_MAXIMIZED);
@@ -225,7 +228,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
LOGGER.error("Crash during font initialization", t);
crashElegantly("An error occurred initializing a font for rendering. "+t.getMessage());
}
this.elements = new ArrayList<>(Arrays.asList(
this.elements = new ArrayList<>(List.of(
RenderElement.anvil(font),
RenderElement.logMessageOverlay(font),
RenderElement.forgeVersionOverlay(font, mcVersion+"-"+forgeVersion.split("-")[0]),
@@ -264,7 +267,6 @@ public class DisplayWindow implements ImmediateWindowProvider {
public void render(int alpha) {
var currentVAO = glGetInteger(GL_VERTEX_ARRAY_BINDING);
var currentFB = glGetInteger(GL_READ_FRAMEBUFFER_BINDING);
glfwSwapInterval(0);
glViewport(0, 0, this.context.scaledWidth(), this.context.scaledHeight());
RenderElement.globalAlpha = alpha;
framebuffer.activate();
@@ -287,7 +289,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
return thread;
});
initWindow(mcVersion);
renderScheduler.schedule(() -> initRender(mcVersion, forgeVersion), 1, TimeUnit.MILLISECONDS);
this.initializationFuture = renderScheduler.schedule(() -> initRender(mcVersion, forgeVersion), 1, TimeUnit.MILLISECONDS);
return this::periodicTick;
}
@@ -360,14 +362,15 @@ public class DisplayWindow implements ImmediateWindowProvider {
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
if (mcVersion != null) {
// this emulates what we would get without early progress window
// as vanilla never sets these, so GLFW uses the first window title
// set them explicitly to avoid it using "FML early loading progress" as the class
String vanillaWindowTitle = "Minecraft* " + mcVersion;
glfwWindowHintString(GLFW_X11_CLASS_NAME, vanillaWindowTitle);
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, vanillaWindowTitle);
}
String vanillaWindowTitle = "Minecraft* ";
if (mcVersion != null) vanillaWindowTitle += mcVersion;
// this emulates what we would get without early progress window
// as vanilla never sets these, so GLFW uses the first window title
// set them explicitly to avoid it using "FML early loading progress" as the class
glfwWindowHintString(GLFW_X11_CLASS_NAME, vanillaWindowTitle);
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, vanillaWindowTitle);
long primaryMonitor = glfwGetPrimaryMonitor();
if (primaryMonitor == 0) {
@@ -389,7 +392,8 @@ public class DisplayWindow implements ImmediateWindowProvider {
}, 10, TimeUnit.SECONDS);
int versidx = 0;
var skipVersions = FMLConfig.<String>getListConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_SKIP_GL_VERSIONS);
final String[] lastGLError=new String[GL_VERSIONS.length];
boolean showHelpLog = FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_LOG_HELP_MSG);
final String[] lastGLError = new String[GL_VERSIONS.length];
do {
final var glVersionToTry = GL_VERSIONS[versidx][0] + "." + GL_VERSIONS[versidx][1];
if (skipVersions.contains(glVersionToTry)) {
@@ -398,11 +402,23 @@ public class DisplayWindow implements ImmediateWindowProvider {
continue;
}
LOGGER.info("Trying GL version " + glVersionToTry);
if (showHelpLog && versidx == 0) {
LOGGER.info("""
If this message is the only thing at the bottom of your log before a crash, you probably have a driver issue.
Possible solutions:
A) Make sure Minecraft is set to prefer high performance graphics in the OS and/or driver control panel
B) Check for driver updates on the graphics brand's website
C) Try reinstalling your graphics drivers
D) If still not working after trying all of the above, ask for further help on the Forge forums or Discord
You can safely ignore this message if the game starts up successfully.""");
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSIONS[versidx][0]); // we try our versions one at a time
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSIONS[versidx][1]);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
window = glfwCreateWindow(winWidth, winHeight, "Minecraft: Forge Loading...", 0L, 0L);
window = glfwCreateWindow(winWidth, winHeight, vanillaWindowTitle, 0L, 0L);
var erridx = versidx;
handleLastGLFWError((error, description) -> lastGLError[erridx] = String.format("Trying %d.%d: GLFW error: [0x%X]%s", GL_VERSIONS[erridx][0], GL_VERSIONS[erridx][1], error, description));
if (lastGLError[versidx] != null) {
@@ -430,6 +446,9 @@ public class DisplayWindow implements ImmediateWindowProvider {
this.glVersion = gotVersion;
this.window = window;
if (showHelpLog)
FMLConfig.updateConfig(FMLConfig.ConfigValue.EARLY_WINDOW_LOG_HELP_MSG, false);
int[] x = new int[1];
int[] y = new int[1];
glfwGetMonitorPos(primaryMonitor, x, y);
@@ -447,19 +466,19 @@ public class DisplayWindow implements ImmediateWindowProvider {
glfwSetWindowPos(window, (vidmode.width() - this.winWidth) / 2 + monitorX, (vidmode.height() - this.winHeight) / 2 + monitorY);
// Attempt setting the icon
int[] channels = new int[1];
try (var glfwImgBuffer = GLFWImage.create(MemoryUtil.getAllocator().malloc(GLFWImage.SIZEOF), 1)) {
final ByteBuffer imgBuffer;
try (GLFWImage glfwImages = GLFWImage.malloc()) {
imgBuffer = STBHelper.loadImageFromClasspath("forge_logo.png", 20000, x, y, channels);
glfwImgBuffer.put(glfwImages.set(x[0], y[0], imgBuffer));
glfwSetWindowIcon(window, glfwImgBuffer);
STBImage.stbi_image_free(imgBuffer);
}
} catch (NullPointerException e) {
System.err.println("Failed to load forge logo");
}
handleLastGLFWError((error, description) -> LOGGER.debug(String.format("Suppressing GLFW icon error: [0x%X]%s", error, description)));
// int[] channels = new int[1];
// try (var glfwImgBuffer = GLFWImage.create(MemoryUtil.getAllocator().malloc(GLFWImage.SIZEOF), 1)) {
// final ByteBuffer imgBuffer;
// try (GLFWImage glfwImages = GLFWImage.malloc()) {
// imgBuffer = STBHelper.loadImageFromClasspath("forge_logo.png", 20000, x, y, channels);
// glfwImgBuffer.put(glfwImages.set(x[0], y[0], imgBuffer));
// glfwSetWindowIcon(window, glfwImgBuffer);
// STBImage.stbi_image_free(imgBuffer);
// }
// } catch (NullPointerException e) {
// System.err.println("Failed to load forge logo");
// }
// handleLastGLFWError((error, description) -> LOGGER.debug(String.format("Suppressing GLFW icon error: [0x%X]%s", error, description)));
glfwSetFramebufferSizeCallback(window, this::fbResize);
glfwSetWindowPosCallback(window, this::winMove);
@@ -518,6 +537,16 @@ public class DisplayWindow implements ImmediateWindowProvider {
* @return the Window we own.
*/
public long setupMinecraftWindow(final IntSupplier width, final IntSupplier height, final Supplier<String> title, final LongSupplier monitorSupplier) {
// wait for the window to actually be initialized
try {
this.initializationFuture.get(30, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
Thread.dumpStack();
crashElegantly("We seem to be having trouble initializing the window, waited for 30 seconds");
}
// we have to spin wait for the window ticker
ImmediateWindowHandler.updateProgress("Initializing Game Graphics");
while (!this.windowTick.isDone()) {
@@ -584,8 +613,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
var fm = layer.findModule("forge").orElseThrow();
getClass().getModule().addReads(fm);
var clz = FMLLoader.getGameLayer().findModule("forge").map(l->Class.forName(l, "net.minecraftforge.client.loading.ForgeLoadingOverlay")).orElseThrow();
var methods = Arrays.stream(clz.getMethods()).filter(m-> Modifier.isStatic(m.getModifiers())).collect(Collectors.toMap(Method::getName, Function.identity()));
loadingOverlay = methods.get("newInstance");
loadingOverlay = Arrays.stream(clz.getMethods()).filter(m-> Modifier.isStatic(m.getModifiers()) && m.getName().equals("newInstance")).findFirst().orElseThrow();
}
public int getFramebufferTextureId() {
@@ -6,35 +6,44 @@
package net.minecraftforge.fml.earlydisplay;
import com.sun.management.OperatingSystemMXBean;
import net.minecraftforge.fml.loading.FMLConfig;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
public class PerformanceInfo {
private static final MemoryMXBean MEMORY_BEAN = ManagementFactory.getMemoryMXBean();
private final boolean showCPUUsage;
private final OperatingSystemMXBean osBean;
private final MemoryMXBean memoryBean;
float memory;
private String text;
PerformanceInfo() {
osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
memoryBean = ManagementFactory.getMemoryMXBean();
showCPUUsage = FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_SHOW_CPU);
osBean = showCPUUsage ? ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class) : null;
}
void update() {
final MemoryUsage heapusage = memoryBean.getHeapMemoryUsage();
final MemoryUsage heapusage = MEMORY_BEAN.getHeapMemoryUsage();
memory = (float) heapusage.getUsed() / heapusage.getMax();
if (!showCPUUsage) {
text = "Memory: %d/%dMB (%.1f%%)".formatted(heapusage.getUsed() >>> 20, heapusage.getMax() >>> 20, memory * 100f);
return;
}
var cpuLoad = osBean.getProcessCpuLoad();
String cpuText;
if (cpuLoad == -1) {
cpuText = String.format("*CPU: %.1f%%", osBean.getCpuLoad() * 100f);
cpuText = "*CPU: %.1f%%".formatted(osBean.getCpuLoad() * 100f);
} else {
cpuText = String.format("CPU: %.1f%%", cpuLoad * 100f);
cpuText = "CPU: %.1f%%".formatted(cpuLoad * 100f);
}
text = String.format("Heap: %d/%d MB (%.1f%%) OffHeap: %d MB %s", heapusage.getUsed() >> 20, heapusage.getMax() >> 20, memory * 100.0, memoryBean.getNonHeapMemoryUsage().getUsed() >> 20, cpuText);
text = "Memory: %d/%dMB (%.1f%%) %s".formatted(heapusage.getUsed() >>> 20, heapusage.getMax() >>> 20, memory * 100f, cpuText);
}
String text() {
@@ -34,11 +34,14 @@ public class RenderElement {
};
}
}
@FunctionalInterface
interface TextureRenderer {
void accept(SimpleBufferBuilder bb, DisplayContext context, int[] size, int frame);
}
@FunctionalInterface
interface Initializer extends Supplier<Renderer> {}
@FunctionalInterface
interface TextGenerator {
void accept(SimpleBufferBuilder bb, SimpleFont fh, DisplayContext ctx);
}
@@ -201,14 +204,17 @@ public class RenderElement {
bar.then(label).accept(buffer, context, frameNumber);
}
@FunctionalInterface
interface ColourFunction {
int colour(int frame);
}
@FunctionalInterface
interface ProgressDisplay {
float[] progress(int frame);
}
@FunctionalInterface
interface BarPosition {
int[] location(DisplayContext context);
}
@@ -280,7 +286,7 @@ public class RenderElement {
if (num < min) {
return min;
} else {
return num > max ? max : num;
return Math.min(num, max);
}
}
@@ -288,7 +294,7 @@ public class RenderElement {
if (num < min) {
return min;
} else {
return num > max ? max : num;
return Math.min(num, max);
}
}
+4 -3
View File
@@ -2,9 +2,10 @@ plugins {
id 'com.github.ben-manes.versions'
id 'org.javamodularity.moduleplugin' version '1.8.3' apply false
id 'org.cadixdev.licenser'
id 'java-library'
id 'jacoco'
}
apply plugin: 'java-library'
apply plugin: 'jacoco'
apply plugin: 'org.javamodularity.moduleplugin'
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
configurations.forEach{ it.transitive = false }
@@ -76,7 +77,7 @@ jar.doFirst {
}
tasks.withType(JavaCompile) {
options.compilerArgs << '-Xlint:unchecked'
options.compilerArgs << '-Xlint:-unchecked'
}
license {
@@ -36,9 +36,9 @@ import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
*/
public class CapabilityTokenSubclass implements ILaunchPluginService {
private final String FUNC_NAME = "getType";
private final String FUNC_DESC = "()Ljava/lang/String;";
private final String CAP_INJECT = "net/minecraftforge/common/capabilities/CapabilityToken"; //Don't directly reference this to prevent class loading.
private static final String FUNC_NAME = "getType";
private static final String FUNC_DESC = "()Ljava/lang/String;";
private static final String CAP_INJECT = "net/minecraftforge/common/capabilities/CapabilityToken"; //Don't directly reference this to prevent class loading.
@Override
public String name() {
@@ -51,7 +51,14 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
@Override
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
{
return isEmpty ? NAY : YAY;
if (isEmpty)
return NAY;
String internalName = classType.getInternalName();
if (internalName.startsWith("net/minecraft/") || internalName.startsWith("com/mojang/"))
return NAY;
return YAY;
}
@Override
@@ -75,7 +82,7 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
SignatureReader reader = new SignatureReader(classNode.signature); // Having a node version of this would probably be useful.
reader.accept(new SignatureVisitor(Opcodes.ASM9)
{
Deque<String> stack = new ArrayDeque<>();
final Deque<String> stack = new ArrayDeque<>();
@Override
public void visitClassType(final String name)
@@ -113,7 +120,7 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
}
}
private static class Holder {
private static final class Holder {
String value;
}
@@ -8,6 +8,7 @@ package net.minecraftforge.fml.common.asm;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -35,7 +36,11 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
new VanillaObjectHolderData("net.minecraft.core.particles.ParticleTypes", "particle_type", "net.minecraft.core.particles.ParticleType"),
new VanillaObjectHolderData("net.minecraft.sounds.SoundEvents", "sound_event", "net.minecraft.sounds.SoundEvent")
).collect(Collectors.toMap(VanillaObjectHolderData::holderClass, Function.identity()));
private final String OBJECT_HOLDER = "Lnet/minecraftforge/registries/ObjectHolder;"; //Don't directly reference this to prevent class loading.
private static final Set<String> VANILLA_OBJECT_HOLDER_CLASSES = VANILLA_OBJECT_HOLDERS.keySet().stream()
.map(it -> it.replace('.', '/'))
.collect(Collectors.toUnmodifiableSet());
private static final String OBJECT_HOLDER = "Lnet/minecraftforge/registries/ObjectHolder;"; //Don't directly reference this to prevent class loading.
private static final int PUBLIC_STATIC_FINAL_FLAGS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
@Override
public String name() {
@@ -46,13 +51,30 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
@Override
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
{
return isEmpty ? NAY : YAY;
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
if (isEmpty)
return NAY;
// Let's skip processing classes that definitely don't have object holders
String internalName = classType.getInternalName();
// Forge classes that aren't in the debug package (that package is used for tests)
if (internalName.startsWith("net/minecraftforge/") && !internalName.substring(18).contains("debug")
&& !internalName.equals("net/minecraftforge/common/crafting/ConditionalRecipe"))
return NAY;
// Vanilla classes that don't have object holders
if (internalName.startsWith("com/mojang/"))
return NAY;
// ...except specific ones we added to the VANILLA_OBJECT_HOLDERS map
if (internalName.startsWith("net/minecraft/") && !VANILLA_OBJECT_HOLDER_CLASSES.contains(internalName))
return NAY;
return YAY;
}
private boolean hasHolder(List<AnnotationNode> lst)
{
private boolean hasHolder(List<AnnotationNode> lst) {
return lst != null && lst.stream().anyMatch(n -> n.desc.equals(OBJECT_HOLDER));
}
@@ -75,10 +97,9 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
{
final AtomicBoolean changes = new AtomicBoolean();
//Must be public static finals, and non-array objects
final int flags = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
//Fix Annotated Fields before injecting from class level
classNode.fields.stream().filter(f -> ((f.access & flags) == flags) && f.desc.startsWith("L") && hasHolder(f.visibleAnnotations)).forEach(f ->
classNode.fields.stream().filter(f -> ((f.access & PUBLIC_STATIC_FINAL_FLAGS) == PUBLIC_STATIC_FINAL_FLAGS) && f.desc.startsWith("L") && hasHolder(f.visibleAnnotations)).forEach(f ->
{
int prev = f.access;
f.access &= ~Opcodes.ACC_FINAL; //Strip final
@@ -88,7 +109,7 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
if (VANILLA_OBJECT_HOLDERS.containsKey(classType.getClassName())) //Class level, de-finalize all fields and add @ObjectHolder to them!
{
classNode.fields.stream().filter(f -> ((f.access & flags) == flags) && f.desc.startsWith("L")).forEach(f ->
classNode.fields.stream().filter(f -> ((f.access & PUBLIC_STATIC_FINAL_FLAGS) == PUBLIC_STATIC_FINAL_FLAGS) && f.desc.startsWith("L")).forEach(f ->
{
int prev = f.access;
f.access &= ~Opcodes.ACC_FINAL;
@@ -30,15 +30,16 @@ import org.slf4j.MarkerFactory;
public class RuntimeEnumExtender implements ILaunchPluginService {
private static final Logger LOGGER = LogUtils.getLogger();
private final Type STRING = Type.getType(String.class);
private final Type ENUM = Type.getType(Enum.class);
private final Type MARKER_IFACE = Type.getType("Lnet/minecraftforge/common/IExtensibleEnum;");
private final Type ARRAY_UTILS = Type.getType("Lorg/apache/commons/lang3/ArrayUtils;"); //Don't directly reference this to prevent class loading.
private final String ADD_DESC = Type.getMethodDescriptor(Type.getType(Object[].class), Type.getType(Object[].class), Type.getType(Object.class));
private final Type UNSAFE_HACKS = Type.getType("Lnet/minecraftforge/fml/unsafe/UnsafeHacks;"); //Again, not direct reference to prevent class loading.
private final String CLEAN_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class));
private final String NAME_DESC = Type.getMethodDescriptor(STRING);
private final String EQUALS_DESC = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, STRING);
private static final Type STRING = Type.getType(String.class);
private static final Type ENUM = Type.getType(Enum.class);
private static final Type MARKER_IFACE = Type.getType("Lnet/minecraftforge/common/IExtensibleEnum;");
private static final Type ARRAY_UTILS = Type.getType("Lorg/apache/commons/lang3/ArrayUtils;"); //Don't directly reference this to prevent class loading.
private static final String ADD_DESC = Type.getMethodDescriptor(Type.getType(Object[].class), Type.getType(Object[].class), Type.getType(Object.class));
private static final Type UNSAFE_HACKS = Type.getType("Lnet/minecraftforge/fml/unsafe/UnsafeHacks;"); //Again, not direct reference to prevent class loading.
private static final String CLEAN_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class));
private static final String NAME_DESC = Type.getMethodDescriptor(STRING);
private static final String EQUALS_DESC = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, STRING);
private static final int FLAGS = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
@Override
public String name() {
@@ -49,9 +50,15 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
@Override
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
{
return isEmpty ? NAY : YAY;
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
if (isEmpty)
return NAY;
String internalName = classType.getInternalName();
if (internalName.startsWith("net/minecraftforge/") || internalName.startsWith("com/mojang/"))
return NAY;
return YAY;
}
@Override
@@ -60,25 +67,24 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
if ((classNode.access & Opcodes.ACC_ENUM) == 0)
return ComputeFlags.NO_REWRITE;
Type array = Type.getType("[" + classType.getDescriptor());
final int flags = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
FieldNode values = classNode.fields.stream().filter(f -> f.desc.contentEquals(array.getDescriptor()) && ((f.access & flags) == flags)).findFirst().orElse(null);
if (!classNode.interfaces.contains(MARKER_IFACE.getInternalName())) {
if (!classNode.interfaces.contains(MARKER_IFACE.getInternalName()))
return ComputeFlags.NO_REWRITE;
}
Type array = Type.getType("[" + classType.getDescriptor());
String arrayDesc = array.getDescriptor();
FieldNode values = classNode.fields.stream().filter(f -> f.desc.equals(arrayDesc) && ((f.access & FLAGS) == FLAGS)).findFirst().orElse(null);
//Static methods named "create" with first argument as a string
List<MethodNode> candidates = classNode.methods.stream()
.filter(m -> ((m.access & Opcodes.ACC_STATIC) != 0) && m.name.equals("create"))
.collect(Collectors.toList());
.toList();
if (candidates.isEmpty()) {
throw new IllegalStateException("IExtensibleEnum has no candidate factory methods: " + classType.getClassName());
}
candidates.forEach(mtd ->
for (var mtd : candidates)
{
Type[] args = Type.getArgumentTypes(mtd.desc);
if (args.length == 0 || !args[0].equals(STRING)) {
@@ -110,8 +116,7 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
Type[] ctrArgs = new Type[args.length + 1];
ctrArgs[0] = STRING;
ctrArgs[1] = Type.INT_TYPE;
for (int x = 1; x < args.length; x++)
ctrArgs[1 + x] = args[x];
System.arraycopy(args, 1, ctrArgs, 2, args.length - 1);
String desc = Type.getMethodDescriptor(Type.VOID_TYPE, ctrArgs);
@@ -233,7 +238,7 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
ins.load(vars, classType);
ins.areturn(classType);
}
});
}
return ComputeFlags.COMPUTE_FRAMES;
}
@@ -36,7 +36,9 @@ public class FMLConfig
EARLY_WINDOW_FBSCALE("earlyWindowFBScale", 1, "Early window framebuffer scale"),
EARLY_WINDOW_MAXIMIZED("earlyWindowMaximized", Boolean.FALSE, "Early window starts maximized"),
EARLY_WINDOW_SKIP_GL_VERSIONS("earlyWindowSkipGLVersions", List.of(), "Skip specific GL versions, may help with buggy graphics card drivers"),
EARLY_WINDOW_SQUIR("earlyWindowSquir", Boolean.FALSE, "Squir?")
EARLY_WINDOW_SQUIR("earlyWindowSquir", Boolean.FALSE, "Squir?"),
EARLY_WINDOW_SHOW_CPU("earlyWindowShowCPU", Boolean.FALSE, "Whether to show CPU usage stats in early window"),
EARLY_WINDOW_LOG_HELP_MSG("earlyWindowLogHelpMessage", Boolean.TRUE, "Whether to log a help message on first attempt, to aid troubleshooting. This setting should automatically disable itself after a successful launch"),
;
private final String entry;
@@ -65,6 +65,9 @@ public class FMLLoader
throw new IncompatibleEnvironmentException("Incompatible modlauncher found "+modLauncherPackage.getSpecificationVersion());
}
// Allows us to communicate properties with other services through ModLauncher
setupBlackboardKeys();
accessTransformer = (AccessTransformerService) environment.findLaunchPlugin("accesstransformer").orElseThrow(()-> {
LOGGER.error(CORE, "Access Transformer library is missing, we need this to run");
return new IncompatibleEnvironmentException("Missing AccessTransformer, cannot run");
@@ -126,6 +129,12 @@ public class FMLLoader
}
}
private static void setupBlackboardKeys() {
LOGGER.debug(CORE, "Requesting CoreMods to not apply the fix for ASMAPI.findFirstInstructionBefore by default");
var blackboardKey = TypesafeMap.Key.getOrCreate(Launcher.INSTANCE.blackboard(), "coremods.use_old_findFirstInstructionBefore", Boolean.class);
Launcher.INSTANCE.blackboard().<Boolean>computeIfAbsent(blackboardKey, k -> true);
}
static void setupLaunchHandler(final IEnvironment environment, final Map<String, Object> arguments)
{
final String launchTarget = environment.getProperty(IEnvironment.Keys.LAUNCHTARGET.get()).orElse("MISSING");
@@ -193,7 +202,7 @@ public class FMLLoader
public static void addAccessTransformer(Path atPath, ModFile modName)
{
LOGGER.debug(SCAN, "Adding Access Transformer in {}", modName.getFilePath());
LOGGER.debug(SCAN, "Adding Access Transformer in {} in {}", atPath, modName.getFilePath());
accessTransformer.offerResource(atPath, modName.getFileName());
}
@@ -5,6 +5,8 @@
package net.minecraftforge.fml.loading;
import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.IModuleLayerManager;
import net.minecraftforge.fml.loading.progress.ProgressMeter;
import net.minecraftforge.fml.loading.progress.StartupNotificationManager;
import org.apache.logging.log4j.LogManager;
@@ -23,28 +25,44 @@ public class ImmediateWindowHandler {
private static ProgressMeter earlyProgress;
public static void load(final String launchTarget, final String[] arguments) {
final var serviceLayer = Launcher.INSTANCE.findLayerManager()
.flatMap(manager -> manager.getLayer(IModuleLayerManager.Layer.SERVICE))
.orElse(null);
final var providerName = FMLConfig.getConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_PROVIDER);
if (!List.of("forgeclient", "forgeclientuserdev", "forgeclientdev").contains(launchTarget)) {
provider = new DummyProvider();
LOGGER.info("ImmediateWindowProvider not loading because launch target is {}", launchTarget);
} else if (!FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_CONTROL)) {
provider = new DummyProvider();
LOGGER.info("ImmediateWindowProvider not loading because splash screen is disabled");
} else if (providerName == null) {
LOGGER.info("ImmediateWindowProvider not loading because splash screen provider is null");
} else if (serviceLayer == null) {
LOGGER.error("Failed to find service layer for ImmediateWindowProvider");
} else {
final var providername = FMLConfig.getConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_PROVIDER);
LOGGER.info("Loading ImmediateWindowProvider {}", providername);
final var maybeProvider = ServiceLoader.load(ImmediateWindowProvider.class)
.stream()
.map(ServiceLoader.Provider::get)
.filter(p -> Objects.equals(p.name(), providername))
.findFirst();
provider = maybeProvider.or(() -> {
LOGGER.info("Failed to find ImmediateWindowProvider {}, disabling", providername);
return Optional.of(new DummyProvider());
}).orElseThrow();
LOGGER.info("Loading ImmediateWindowProvider {}", providerName);
for (var itr = ServiceLoader.load(serviceLayer, ImmediateWindowProvider.class).iterator(); itr.hasNext(); ) {
try {
var srvc = itr.next();
if (providerName.equals(srvc.name())) {
provider = new Wrapper(srvc);
break;
}
} catch (ServiceConfigurationError e) {
LOGGER.error("Failed to initalize ImmediateWindowProvider Service", e);
}
}
if (provider == null)
LOGGER.info("Failed to find ImmediateWindowProvider {}, disabling", providerName);
}
// Only update config if the provider isn't the dummy provider
if (!Objects.equals(provider.name(), "dummyprovider"))
if (provider != null)
FMLConfig.updateConfig(FMLConfig.ConfigValue.EARLY_WINDOW_PROVIDER, provider.name());
else
provider = new DummyProvider();
FMLLoader.progressWindowTick = provider.initialize(arguments);
earlyProgress = StartupNotificationManager.addProgressBar("EARLY", 0);
earlyProgress.label("Bootstrapping Minecraft");
@@ -78,6 +96,7 @@ public class ImmediateWindowHandler {
public static String getGLVersion() {
return provider.getGLVersion();
}
public static void updateProgress(final String message) {
earlyProgress.label(message);
}
@@ -156,4 +175,94 @@ public class ImmediateWindowHandler {
// NOOP
}
}
// We create a wrapper because we want to log errors to the log files, which not all paths of ModLauncher do. Little dirty, but works for now.
private static class Wrapper implements ImmediateWindowProvider {
private final ImmediateWindowProvider delegate;
private Wrapper(ImmediateWindowProvider delegate) {
this.delegate = delegate;
}
public String name() {
try {
return delegate.name();
} catch (Throwable t) {
LOGGER.error("Failed to call provider.name", t);
throw t;
}
}
public Runnable initialize(String[] arguments) {
try {
return delegate.initialize(arguments);
} catch (Throwable t) {
LOGGER.error("Failed to call provider.initialize", t);
throw t;
}
}
public void updateFramebufferSize(IntConsumer width, IntConsumer height) {
try {
delegate.updateFramebufferSize(width, height);
} catch (Throwable t) {
LOGGER.error("Failed to call provider.updateFramebufferSize", t);
throw t;
}
}
public long setupMinecraftWindow(IntSupplier width, IntSupplier height, Supplier<String> title, LongSupplier monitor) {
try {
return delegate.setupMinecraftWindow(width, height, title, monitor);
} catch (Throwable t) {
LOGGER.error("Failed to call provider.setupMinecraftWindow", t);
throw t;
}
}
public boolean positionWindow(Optional<Object> monitor, IntConsumer widthSetter, IntConsumer heightSetter, IntConsumer xSetter, IntConsumer ySetter) {
try {
return delegate.positionWindow(monitor, widthSetter, heightSetter, xSetter, ySetter);
} catch (Throwable t) {
LOGGER.error("Failed to call provider.positionWindow", t);
throw t;
}
}
public <T> Supplier<T> loadingOverlay(Supplier<?> mc, Supplier<?> ri, Consumer<Optional<Throwable>> ex, boolean fade) {
try {
return delegate.loadingOverlay(mc, ri, ex, fade);
} catch (Throwable t) {
LOGGER.error("Failed to call provider.loadingOverlay", t);
throw t;
}
}
public void updateModuleReads(ModuleLayer layer) {
try {
delegate.updateModuleReads(layer);
} catch (Throwable t) {
LOGGER.error("Failed to call provider.updateModuleReads", t);
throw t;
}
}
public void periodicTick() {
try {
delegate.periodicTick();
} catch (Throwable t) {
LOGGER.error("Failed to call provider.periodicTick", t);
throw t;
}
}
public String getGLVersion() {
try {
return delegate.getGLVersion();
} catch (Throwable t) {
LOGGER.error("Failed to call provider.getGLVersion", t);
throw t;
}
}
}
}
@@ -5,12 +5,14 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import net.minecraftforge.forgespi.locating.IModFile;
import org.slf4j.Logger;
import java.net.URL;
import java.nio.file.Files;
@@ -30,6 +32,7 @@ import java.util.stream.Collectors;
*/
public class LoadingModList
{
private static final Logger LOGGER = LogUtils.getLogger();
private static LoadingModList INSTANCE;
private final List<ModFileInfo> modFiles;
private final List<ModInfo> sortedList;
@@ -78,9 +81,22 @@ public class LoadingModList
public void addAccessTransformers()
{
modFiles.stream()
.map(ModFileInfo::getFile)
.forEach(mod -> mod.getAccessTransformer().ifPresent(path -> FMLLoader.addAccessTransformer(path, mod)));
var errors = new ArrayList<EarlyLoadingException.ExceptionData>();
for (ModFileInfo modFile : modFiles) {
ModFile mod = modFile.getFile();
for (Path at : mod.getAccessTransformers()) {
if (!Files.exists(at)) {
var message = "Invalid mod file: " + modFile.getFile().getFileName() + ". Missing Access Transformer: " + at;
errors.add(new EarlyLoadingException.ExceptionData(message));
LOGGER.error(message);
} else {
FMLLoader.addAccessTransformer(at, mod);
}
}
}
if (!errors.isEmpty()) {
preLoadErrors.add(new EarlyLoadingException("Invalid Access Transformers", null, errors));
}
}
public void addForScanning(BackgroundScanHandler backgroundScanHandler)
@@ -28,7 +28,8 @@ public class ModDirTransformerDiscoverer implements ITransformerDiscoveryService
private static final Set<String> SERVICES = Set.of(
"cpw.mods.modlauncher.api.ITransformationService",
"net.minecraftforge.forgespi.locating.IModLocator",
"net.minecraftforge.forgespi.locating.IDependencyLocator"
"net.minecraftforge.forgespi.locating.IDependencyLocator",
"net.minecraftforge.fml.loading.ImmediateWindowProvider"
);
@Override
@@ -34,8 +34,7 @@ import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
public class RuntimeDistCleaner implements ILaunchPluginService
{
public final class RuntimeDistCleaner implements ILaunchPluginService {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Marker DISTXFORM = MarkerFactory.getMarker("DISTXFORM");
private static String DIST;
@@ -61,7 +60,7 @@ public class RuntimeDistCleaner implements ILaunchPluginService
{
unpack(classNode.visibleAnnotations).stream()
.filter(ann->Objects.equals(ann.desc, ONLYIN))
.filter(ann->ann.values.indexOf("_interface") != -1)
.filter(ann-> ann.values.contains("_interface"))
.filter(ann->!Objects.equals(((String[])ann.values.get(ann.values.indexOf("value") + 1))[1], DIST))
.map(ann -> ((Type)ann.values.get(ann.values.indexOf("_interface") + 1)).getInternalName())
.forEach(intf -> {
@@ -147,11 +146,11 @@ public class RuntimeDistCleaner implements ILaunchPluginService
return ret;
}
private boolean remove(final List<AnnotationNode> anns, final String side)
private static boolean remove(final List<AnnotationNode> anns, final String side)
{
return unpack(anns).stream().
filter(ann->Objects.equals(ann.desc, ONLYIN)).
filter(ann->ann.values.indexOf("_interface") == -1).
filter(ann-> !ann.values.contains("_interface")).
anyMatch(ann -> !Objects.equals(((String[])ann.values.get(ann.values.indexOf("value")+1))[1], side));
}
@@ -169,9 +168,19 @@ public class RuntimeDistCleaner implements ILaunchPluginService
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
@Override
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
{
return isEmpty ? NAY : YAY;
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
if (isEmpty)
return NAY;
String internalName = classType.getInternalName();
if (internalName.startsWith("net/minecraftforge/"))
return NAY;
// No Vanilla classes use @OnlyIn(Dist.SERVER), so we can skip them entirely when we're running on a client
if (FMLEnvironment.dist.isClient() && (internalName.startsWith("net/minecraft/") || internalName.startsWith("com/mojang/")))
return NAY;
return YAY;
}
private static class LambdaGatherer extends MethodVisitor {
@@ -18,12 +18,15 @@ import java.util.function.BiPredicate;
public class VersionSupportMatrix {
private static final HashMap<String, List<ArtifactVersion>> overrideVersions = new HashMap<>();
static {
final ArtifactVersion version = new DefaultArtifactVersion(FMLLoader.versionInfo().mcVersion());
if (MavenVersionAdapter.createFromVersionSpec("[1.19.2]").containsVersion(version)) {
// 1.19.2 is Compatible with 1.19.1
add("languageloader.javafml", "42");
add("mod.minecraft", "1.19.1");
add("mod.forge", "42.0.9");
// final ArtifactVersion version = new DefaultArtifactVersion(FMLLoader.versionInfo().mcVersion());
// if (MavenVersionAdapter.createFromVersionSpec("[1.19.2]").containsVersion(version)) {
// // 1.19.2 is Compatible with 1.19.1
// add("languageloader.javafml", "42");
// add("mod.minecraft", "1.19.1");
// add("mod.forge", "42.0.9");
// }
if (FMLLoader.versionInfo().mcVersion().equals("1.20.1")) {
add("mod.forge", "47.1.79");
}
}
private static void add(String key, String value) {
@@ -34,6 +34,11 @@ public abstract class AbstractModProvider implements IModProvider
protected static final String MANIFEST = "META-INF/MANIFEST.MF";
protected IModLocator.ModFileOrException createMod(Path... path) {
return this.createMod(getDefaultJarModType(), path);
}
// FORGE: Backporting artifact, the defaultType must be the first parameter since path is varargs
protected IModLocator.ModFileOrException createMod(String defaultType, Path... path) {
var mjm = new ModJarMetadata();
var sj = SecureJar.from(
Manifest::new,
@@ -45,7 +50,7 @@ public abstract class AbstractModProvider implements IModProvider
IModFile mod;
var type = sj.moduleDataProvider().getManifest().getMainAttributes().getValue(ModFile.TYPE);
if (type == null) {
type = getDefaultJarModType();
type = defaultType;
}
if (sj.moduleDataProvider().findFile(MODS_TOML).isPresent()) {
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MODS_TOML, type, path);
@@ -81,7 +81,14 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
final Map<String, ?> outerFsArgs = ImmutableMap.of("packagePath", pathInModFile);
final FileSystem zipFS = FileSystems.newFileSystem(filePathUri, outerFsArgs);
final Path pathInFS = zipFS.getPath("/");
return Optional.of(createMod(pathInFS).file());
final IModFile.Type parentType = file.getType();
final String modType;
if (parentType == IModFile.Type.LIBRARY || parentType == IModFile.Type.LANGPROVIDER) {
modType = IModFile.Type.LIBRARY.name();
} else {
modType = IModFile.Type.GAMELIBRARY.name();
}
return Optional.of(createMod(modType, pathInFS).file());
}
catch (Exception e)
{
@@ -12,6 +12,7 @@ import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.IModuleLayerManager;
import cpw.mods.modlauncher.util.ServiceLoaderUtils;
import net.minecraftforge.fml.loading.EarlyLoadingException;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.ImmediateWindowHandler;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.fml.loading.UniqueModListBuilder;
@@ -21,6 +22,7 @@ import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.locating.IDependencyLocator;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModLocator;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.ArrayList;
@@ -67,6 +69,7 @@ public class ModDiscoverer {
List<EarlyLoadingException.ExceptionData> discoveryErrorData = new ArrayList<>();
boolean successfullyLoadedMods = true;
List<IModFileInfo> brokenFiles = new ArrayList<>();
boolean distIsDedicatedServer = FMLLoader.getDist().isDedicatedServer();
ImmediateWindowHandler.updateProgress("Discovering mod files");
//Loop all mod locators to get the prime mods to load from.
for (IModLocator locator : modLocatorList) {
@@ -85,8 +88,24 @@ public class ModDiscoverer {
if (!badModFiles.isEmpty()) {
LOGGER.error(LogMarkers.SCAN, "Locator {} returned {} files which is are not ModFile instances! They will be skipped!", locator, badModFiles.size());
brokenFiles.addAll(badModFiles.stream().map(IModFile::getModFileInfo).toList());
locatedFiles.removeAll(badModFiles);
}
locatedFiles.removeAll(badModFiles);
if (distIsDedicatedServer) {
var clientOnlyModFiles = locatedFiles.stream()
.filter(file -> {
// some mod files can have null infos, like javafml, mclanguage, lowcode, and fmlcore
@Nullable var info = file.getModFileInfo();
return info != null && (Boolean) info.getFileProperties().getOrDefault(ModFileInfo.CLIENT_SIDE_ONLY_PROP, Boolean.FALSE);
})
.toList();
if (!clientOnlyModFiles.isEmpty()) {
LOGGER.warn(LogMarkers.SCAN, "Locator {} returned {} files which are client-side-only mods, but we're on a dedicated server. They will be skipped!", locator, clientOnlyModFiles.size());
locatedFiles.removeAll(clientOnlyModFiles);
}
}
LOGGER.debug(LogMarkers.SCAN, "Locator {} found {} valid mod files", locator, locatedFiles.size());
handleLocatedFiles(loadedFiles, locatedFiles);
} catch (InvalidModFileException imfe) {
@@ -28,6 +28,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Collections;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@@ -60,6 +62,7 @@ public class ModFile implements IModFile {
private CompletableFuture<ModFileScanData> futureScanResult;
private List<CoreModFile> coreMods;
private Path accessTransformer;
private List<Path> accessTransformers = Collections.emptyList();
static final Attributes.Name TYPE = new Attributes.Name("FMLModType");
private SecureJar.Status securityStatus;
@@ -104,7 +107,11 @@ public class ModFile implements IModFile {
}
public Optional<Path> getAccessTransformer() {
return Optional.ofNullable(Files.exists(accessTransformer) ? accessTransformer : null);
return Optional.ofNullable(accessTransformer);
}
public List<Path> getAccessTransformers() {
return accessTransformers;
}
public boolean identifyMods() {
@@ -113,7 +120,28 @@ public class ModFile implements IModFile {
LOGGER.debug(LogMarkers.LOADING,"Loading mod file {} with languages {}", this.getFilePath(), this.modFileInfo.requiredLanguageLoaders());
this.coreMods = ModFileParser.getCoreMods(this);
this.coreMods.forEach(mi-> LOGGER.debug(LogMarkers.LOADING,"Found coremod {}", mi.getPath()));
this.accessTransformer = findResource("META-INF", "accesstransformer.cfg");
List<String> cfg;
// Note: Some mods may have invalid tomls copied from other projects.
// Unfortunately we must protect against those landmines.
try {
cfg = this.modFileInfo.getConfig().<List<String>>getConfigElement("accessTransformers").orElse(null);
} catch (Exception e) {
cfg = null;
LOGGER.warn("{} contains an invalid 'accessTransformers' TOML entry. Should be e.g. accessTransformers = [\"META-INF/accesstransformer.cfg\", \"META-INF/extra_at.cfg\"] or accessTransformers = [] for no ATs. Falling back to default.", this.getFileName());
}
if (cfg == null) {
var path = findResource("META-INF", "accesstransformer.cfg");
if (Files.exists(path)) {
this.accessTransformer = path;
this.accessTransformers = List.of(path);
}
} else if (!cfg.isEmpty()) {
var paths = new ArrayList<Path>(cfg.size());
for (var path : cfg) {
paths.add(getSecureJar().getPath(path.replace('\\', '/')));
}
this.accessTransformers = List.copyOf(paths);
}
return true;
}
@@ -29,6 +29,7 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -50,6 +51,10 @@ public class ModFileInfo implements IModFileInfo, IConfigurable
private final String license;
private final List<String> usesServices;
public static final String CLIENT_SIDE_ONLY_PROP = "__FORGE_clientSideOnly";
private static final Map<String, Object> CLIENT_SIDE_ONLY_MAP = Map.of(CLIENT_SIDE_ONLY_PROP, Boolean.TRUE);
ModFileInfo(final ModFile modFile, final IConfigurable config, Consumer<IModFileInfo> configFileConsumer)
{
this.modFile = modFile;
@@ -68,10 +73,23 @@ public class ModFileInfo implements IModFileInfo, IConfigurable
.orElse("");
this.showAsResourcePack = config.<Boolean>getConfigElement("showAsResourcePack")
.orElse(false);
boolean clientSideOnly = config.<Boolean>getConfigElement("clientSideOnly")
.orElse(false);
this.usesServices = config.<List<String>>getConfigElement("services")
.orElse(List.of());
this.properties = config.<Map<String, Object>>getConfigElement("properties")
.orElse(Collections.emptyMap());
var maybeProps = config.<Map<String, Object>>getConfigElement("properties");
if (clientSideOnly) {
if (maybeProps.isPresent()) {
this.properties = new HashMap<>(maybeProps.get());
this.properties.put(CLIENT_SIDE_ONLY_PROP, Boolean.TRUE);
} else {
this.properties = CLIENT_SIDE_ONLY_MAP;
}
} else {
this.properties = maybeProps.orElse(Collections.emptyMap());
}
this.modFile.setFileProperties(this.properties);
this.issueURL = config.<String>getConfigElement("issueTrackerURL")
.map(StringUtils::toURL)
@@ -6,6 +6,7 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.electronwill.nightconfig.core.file.FileConfig;
import com.electronwill.nightconfig.core.io.ParsingException;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.mojang.logging.LogUtils;
@@ -13,6 +14,7 @@ import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.ModFileFactory;
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
import org.slf4j.Logger;
import java.io.IOException;
@@ -22,7 +24,6 @@ import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ModFileParser {
private static final Logger LOGGER = LogUtils.getLogger();
@@ -41,10 +42,15 @@ public class ModFileParser {
}
final FileConfig fileConfig = FileConfig.builder(modsjson).build();
fileConfig.load();
fileConfig.close();
final NightConfigWrapper configWrapper = new NightConfigWrapper(fileConfig);
return new ModFileInfo(modFile, configWrapper, configWrapper::setFile);
try {
fileConfig.load();
fileConfig.close();
final NightConfigWrapper configWrapper = new NightConfigWrapper(fileConfig);
return new ModFileInfo(modFile, configWrapper, configWrapper::setFile);
} catch (ParsingException e) {
LOGGER.error("Mod candidate {} contains a corrupt or misconfigured toml.", modFile.getFileName());
throw new ModFileLoadingException("Mod candidate " + modFile.getFileName() + " contains a corrupt or misconfigured toml.");
}
}
protected static List<CoreModFile> getCoreMods(final ModFile modFile) {
@@ -80,7 +80,8 @@ public class ModInfo implements IModInfo, IConfigurable
this.displayName = config.<String>getConfigElement("displayName")
.orElse(this.modId);
this.description = config.<String>getConfigElement("description")
.orElse("MISSING DESCRIPTION");
.orElse("MISSING DESCRIPTION")
.replace("\r\n", "\n").stripIndent();
this.logoFile = Optional.ofNullable(config.<String>getConfigElement("logoFile")
.orElseGet(() -> ownFile.flatMap(mf -> mf.<String>getConfigElement("logoFile"))
.orElse(null)));
@@ -205,7 +206,7 @@ public class ModInfo implements IModInfo, IConfigurable
private final boolean mandatory;
private final Ordering ordering;
private final DependencySide side;
private Optional<URL> referralUrl;
private final Optional<URL> referralUrl;
public ModVersion(final IModInfo owner, final IConfigurable config) {
this.owner = owner;
@@ -6,6 +6,7 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import cpw.mods.jarhandling.SecureJar;
import cpw.mods.modlauncher.api.IModuleLayerManager;
import cpw.mods.modlauncher.api.ITransformationService;
import net.minecraftforge.fml.loading.*;
@@ -26,14 +27,17 @@ public class ModValidator {
private final Map<IModFile.Type, List<ModFile>> modFiles;
private final List<ModFile> candidatePlugins;
private final List<ModFile> candidateMods;
private final List<ModFile> gameLibraries;
private LoadingModList loadingModList;
private List<IModFile> brokenFiles;
private final List<IModFile> brokenFiles;
private final List<EarlyLoadingException.ExceptionData> discoveryErrorData;
public ModValidator(final Map<IModFile.Type, List<ModFile>> modFiles, final List<IModFileInfo> brokenFiles, final List<EarlyLoadingException.ExceptionData> discoveryErrorData) {
this.modFiles = modFiles;
this.candidateMods = lst(modFiles.get(IModFile.Type.MOD));
this.candidateMods.addAll(lst(modFiles.get(IModFile.Type.GAMELIBRARY)));
this.gameLibraries = lst(modFiles.get(IModFile.Type.GAMELIBRARY));
this.candidateMods.addAll(this.gameLibraries);
this.candidatePlugins = lst(modFiles.get(IModFile.Type.LANGPROVIDER));
this.candidatePlugins.addAll(lst(modFiles.get(IModFile.Type.LIBRARY)));
this.discoveryErrorData = discoveryErrorData;
@@ -55,8 +59,7 @@ public class ModValidator {
@NotNull
private List<ModFile> validateFiles(final List<ModFile> mods) {
final List<ModFile> brokenFiles = new ArrayList<>();
for (Iterator<ModFile> iterator = mods.iterator(); iterator.hasNext(); )
{
for (Iterator<ModFile> iterator = mods.iterator(); iterator.hasNext();) {
ModFile modFile = iterator.next();
if (!modFile.getProvider().isValid(modFile) || !modFile.identifyMods()) {
LOGGER.warn(LogMarkers.SCAN, "File {} has been ignored - it is invalid", modFile.getFilePath());
@@ -72,7 +75,26 @@ public class ModValidator {
}
public ITransformationService.Resource getModResources() {
return new ITransformationService.Resource(IModuleLayerManager.Layer.GAME, this.candidateMods.stream().map(IModFile::getSecureJar).toList());
var mods = new ArrayList<SecureJar>();
// Add only the valid mods that we will be attempting to load.
// If any detectable error happens during the sorting process {missing deps, duplicates,
// This helps prevent coremods/mixins from screwing up us displaying the error screens that the sorting/validation is trying to display.
// This won't fix them all as they are still loaded and will still apply but it might help
for (var info : this.loadingModList.getModFiles())
mods.add(info.getFile().getSecureJar());
// Add any game libraries, this *may* be duplicates, depending on the state of the sorting. But until I get around to re-writing
// that clusterfuck of a system, have this simple de-duplication. Order of resource is important, but i'm unsure if duplication is
// so might as well waste a few cycles on checking. Still faster then converting from a set to a list.
// Ideally we would explicitly list out which mods to load {forge and MC being the only ones} but some coremods could require extra things.
// So try game libraries as well
for (var lib : this.gameLibraries) {
var jar = lib.getSecureJar();
if (!mods.contains(jar))
mods.add(jar);
}
return new ITransformationService.Resource(IModuleLayerManager.Layer.GAME, mods);
}
private List<EarlyLoadingException.ExceptionData> validateLanguages() {
@@ -45,7 +45,7 @@ public class Scanner {
try (InputStream in = Files.newInputStream(path)){
ModClassVisitor mcv = new ModClassVisitor();
ClassReader cr = new ClassReader(in);
cr.accept(mcv, 0);
cr.accept(mcv, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
mcv.buildData(result.getClasses(), result.getAnnotations());
} catch (IOException | IllegalArgumentException e) {
// mark path bad
@@ -10,7 +10,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public final class ProgressMeter {
private final String name;
private final int steps;
private AtomicInteger current;
private final AtomicInteger current;
private Message label;
public ProgressMeter(String name, int steps, int current, Message label) {
@@ -0,0 +1,18 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.targets;
import net.minecraftforge.api.distmarker.Dist;
public class FMLClientDevLaunchHandler extends CommonDevLaunchHandler {
@Override public String name() { return "fmlclientdev"; }
@Override public Dist getDist() { return Dist.CLIENT; }
@Override
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
clientService(arguments, layer);
}
}
@@ -0,0 +1,23 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.targets;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;
import net.minecraftforge.fml.loading.LibraryFinder;
import net.minecraftforge.fml.loading.VersionInfo;
public class FMLClientLaunchHandler extends CommonClientLaunchHandler {
@Override public String name() { return "fmlclient"; }
@Override
protected void processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods) {
var fmlonly = LibraryFinder.findPathForMaven(versionInfo.forgeGroup(), "fmlonly", "", "universal", versionInfo.mcAndForgeVersion());
mods.add(List.of(fmlonly));
}
}
@@ -0,0 +1,21 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.targets;
import net.minecraftforge.api.distmarker.Dist;
public class FMLClientUserdevLaunchHandler extends FMLUserdevLaunchHandler {
@Override
public String name() { return "fmlclientuserdev"; }
@Override
public Dist getDist() { return Dist.CLIENT; }
@Override
protected void devService(String[] arguments, ModuleLayer layer) throws Throwable {
clientService(arguments, layer);
}
}
@@ -0,0 +1,24 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.targets;
import net.minecraftforge.api.distmarker.Dist;
public class FMLDataUserdevLaunchHandler extends FMLUserdevLaunchHandler {
@Override
public String name() { return "fmldatauserdev"; }
@Override
public Dist getDist() { return Dist.CLIENT; }
@Override
public boolean isData() { return true; }
@Override
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
dataService(arguments, layer);
}
}
@@ -0,0 +1,18 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.targets;
import net.minecraftforge.api.distmarker.Dist;
public class FMLServerDevLaunchHandler extends CommonDevLaunchHandler {
@Override public String name() { return "fmlserverdev"; }
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
@Override
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
serverService(arguments, layer);
}
}
@@ -0,0 +1,26 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.targets;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import net.minecraftforge.fml.loading.LibraryFinder;
import net.minecraftforge.fml.loading.VersionInfo;
import java.nio.file.Path;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.stream.Stream;
public class FMLServerLaunchHandler extends CommonServerLaunchHandler implements ILaunchHandlerService {
@Override public String name() { return "fmlserver"; }
@Override
protected BiPredicate<String, String> processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, BiPredicate<String, String> filter, Stream.Builder<List<Path>> mods) {
var fmlonly = LibraryFinder.findPathForMaven(versionInfo.forgeGroup(), "fmlonly", "", "universal", versionInfo.mcAndForgeVersion());
mods.add(List.of(fmlonly));
return filter;
}
}
@@ -0,0 +1,18 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.targets;
import net.minecraftforge.api.distmarker.Dist;
public class FMLServerUserdevLaunchHandler extends FMLUserdevLaunchHandler {
@Override public String name() { return "fmlserveruserdev"; }
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
@Override
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
serverService(arguments, layer);
}
}
@@ -0,0 +1,20 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.targets;
import net.minecraftforge.fml.loading.VersionInfo;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;
public abstract class FMLUserdevLaunchHandler extends CommonUserdevLaunchHandler {
@Override
protected void processStreams(String[] classpath, VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods) {
var fmlonly = findJarOnClasspath(classpath, "fmlonly-" + versionInfo.mcAndForgeVersion());
mc.add(fmlonly);
}
}
@@ -1,3 +1,11 @@
net.minecraftforge.fml.loading.targets.FMLClientLaunchHandler
net.minecraftforge.fml.loading.targets.FMLClientDevLaunchHandler
net.minecraftforge.fml.loading.targets.FMLClientUserdevLaunchHandler
net.minecraftforge.fml.loading.targets.FMLServerLaunchHandler
net.minecraftforge.fml.loading.targets.FMLServerDevLaunchHandler
net.minecraftforge.fml.loading.targets.FMLServerUserdevLaunchHandler
net.minecraftforge.fml.loading.targets.FMLDataUserdevLaunchHandler
net.minecraftforge.fml.loading.targets.ForgeClientLaunchHandler
net.minecraftforge.fml.loading.targets.ForgeClientDevLaunchHandler
net.minecraftforge.fml.loading.targets.ForgeClientUserdevLaunchHandler
+1
View File
@@ -0,0 +1 @@
../../src/fmlcommon/
@@ -0,0 +1,47 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fmlonly;
import net.minecraftforge.eventbus.api.BusBuilder;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.I18NParser;
import net.minecraftforge.fml.IBindingsProvider;
import net.minecraftforge.fml.config.IConfigEvent;
import net.minecraftforge.fml.event.config.ModConfigEvent;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class FMLOnlyBindings implements IBindingsProvider {
private static final IEventBus DUMMYFORGEBUS = BusBuilder.builder().build();
@Override
public Supplier<IEventBus> getForgeBusSupplier() {
return ()->DUMMYFORGEBUS;
}
@Override
public Supplier<I18NParser> getMessageParser() {
return ()->new I18NParser() {
@Override
public String parseMessage(final String i18nMessage, final Object... args) {
return i18nMessage + Arrays.stream(args).map(Objects::toString).collect(Collectors.joining(",", "[", "]"));
}
@Override
public String stripControlCodes(final String toStrip) {
return toStrip;
}
};
}
@Override
public Supplier<IConfigEvent.ConfigConfig> getConfigConfiguration() {
return ()->new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new, ModConfigEvent.Unloading::new);
}
}
@@ -0,0 +1,11 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fmlonly;
import net.minecraftforge.fml.common.Mod;
@Mod("fmlonly")
public class FMLOnlyMod {}
@@ -0,0 +1,81 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fmlonlyclient;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.ClientPackSource;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.server.packs.resources.PreparableReloadListener;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.LoadingFailedException;
import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.fml.ModWorkManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@OnlyIn(Dist.CLIENT)
public class ClientModLoader
{
private static boolean loading;
private static Minecraft mc;
private static boolean loadingComplete;
private static LoadingFailedException error;
public static void begin(final Minecraft minecraft, final PackRepository defaultResourcePacks, final ReloadableResourceManager mcResourceManager)
{
// force log4j to shutdown logging in a shutdown hook. This is because we disable default shutdown hook so the server properly logs it's shutdown
Runtime.getRuntime().addShutdownHook(new Thread(LogManager::shutdown));
loading = true;
ClientModLoader.mc = minecraft;
createRunnableWithCatch(()->ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{})).run();
mcResourceManager.registerReloadListener(ClientModLoader::onResourceReload);
}
private static CompletableFuture<Void> onResourceReload(final PreparableReloadListener.PreparationBarrier stage, final ResourceManager resourceManager, final ProfilerFiller prepareProfiler, final ProfilerFiller executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) {
return CompletableFuture.runAsync(createRunnableWithCatch(() -> startModLoading(ModWorkManager.wrappedExecutor(syncExecutor), asyncExecutor)), ModWorkManager.parallelExecutor())
.thenCompose(stage::wait)
.thenRunAsync(() -> finishModLoading(ModWorkManager.wrappedExecutor(syncExecutor), asyncExecutor), ModWorkManager.parallelExecutor());
}
private static Runnable createRunnableWithCatch(Runnable r) {
return ()-> {
if (loadingComplete) return;
try {
r.run();
} catch (LoadingFailedException e) {
if (error == null) error = e;
}
};
}
private static void startModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor) {
if (error!=null) throw error;
createRunnableWithCatch(() -> ModLoader.get().loadMods(syncExecutor, parallelExecutor, ()->{})).run();
}
private static void finishModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor)
{
if (error!=null) throw error;
createRunnableWithCatch(() -> ModLoader.get().finishMods(syncExecutor, parallelExecutor, ()->{})).run();
loading = false;
loadingComplete = true;
// reload game settings on main thread
syncExecutor.execute(()->mc.options.load());
}
public static boolean isLoading()
{
return loading;
}
}
@@ -0,0 +1,56 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fmlonlyserver;
import joptsimple.OptionParser;
import net.minecraftforge.fml.LoadingFailedException;
import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.fml.ModLoadingWarning;
import net.minecraftforge.fml.ModWorkManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.util.List;
import static net.minecraftforge.fml.Logging.LOADING;
public class ServerModLoader
{
private static final Logger LOGGER = LogManager.getLogger();
private static boolean hasErrors = false;
public static void addOptions(OptionParser parser)
{
// We consume this argument as we use it in the launcher and the client side.
parser.accepts("gameDir").withRequiredArg().ofType(File.class).defaultsTo(new File("."));
}
public static void load()
{
try
{
ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), () -> {});
ModLoader.get().loadMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), () -> {});
ModLoader.get().finishMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), () -> {});
} catch (LoadingFailedException error)
{
ServerModLoader.hasErrors = true;
throw error;
}
List<ModLoadingWarning> warnings = ModLoader.get().getWarnings();
if (!warnings.isEmpty())
{
LOGGER.warn(LOADING, "Mods loaded with {} warnings", warnings.size());
warnings.forEach(warning -> LOGGER.warn(LOADING, warning.formatToString()));
}
}
public static boolean hasErrors()
{
return ServerModLoader.hasErrors;
}
}
@@ -0,0 +1,4 @@
{
"load_fml_client": "coremods/load_fml_client.js",
"load_fml_server": "coremods/load_fml_server.js"
}
@@ -0,0 +1,16 @@
modLoader="javafml"
loaderVersion="[24,]"
issueTrackerURL="https://www.minecraftforge.net/"
license="LGPL v2.1"
[[mods]]
modId="fmlonly"
# We use the global forge version
version="${global.forgeVersion}"
updateJSONURL="https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json"
displayName="Forge"
credits="Anyone who has contributed on Github and supports our development"
authors="LexManos,cpw"
description='''
Forge, a broad compatibility API.
'''
@@ -0,0 +1 @@
net.minecraftforge.fmlonly.FMLOnlyBindings
@@ -0,0 +1 @@
net.minecraftforge.fml.core.ModStateProvider
@@ -0,0 +1,36 @@
var ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI')
var Opcodes = Java.type('org.objectweb.asm.Opcodes')
function initializeCoreMod() {
return {
'minecraft': {
'target': {
'type': 'METHOD',
'class': 'net.minecraft.client.Minecraft',
'methodName': '<init>',
'methodDesc': '(Lnet/minecraft/client/main/GameConfig;)V'
},
'transformer': function(methodNode) {
//print(ASMAPI.methodNodeToString(methodNode))
var meth = ASMAPI.getMethodNode();
var resourcePackRepository = ASMAPI.mapField("f_91038_")
var resourceManager = ASMAPI.mapField("f_91036_")
meth.visitVarInsn(Opcodes.ALOAD, 0);
meth.visitVarInsn(Opcodes.ALOAD, 0);
meth.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/client/Minecraft", resourcePackRepository, "Lnet/minecraft/server/packs/repository/PackRepository;");
meth.visitVarInsn(Opcodes.ALOAD, 0);
meth.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/client/Minecraft", resourceManager, "Lnet/minecraft/server/packs/resources/ReloadableResourceManager;");
meth.visitMethodInsn(Opcodes.INVOKESTATIC, "net/minecraftforge/fmlonlyclient/ClientModLoader", "begin", "(Lnet/minecraft/client/Minecraft;Lnet/minecraft/server/packs/repository/PackRepository;Lnet/minecraft/server/packs/resources/ReloadableResourceManager;)V", false)
for (var i=0; i<methodNode.instructions.size(); i++) {
var ain = methodNode.instructions.get(i);
if (ain.getOpcode() == Opcodes.PUTFIELD && ain.name.equals(resourceManager)) {
methodNode.instructions.insert(ain, meth.instructions);
break;
}
}
return methodNode;
}
},
}
}
@@ -0,0 +1,35 @@
var ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI')
var Opcodes = Java.type('org.objectweb.asm.Opcodes')
function initializeCoreMod() {
return {
'minecraft': {
'target': {
'type': 'METHOD',
'class': 'net.minecraft.server.Main',
'methodName': 'main',
'methodDesc': '([Ljava/lang/String;)V'
},
'transformer': function(methodNode) {
var meth = ASMAPI.getMethodNode();
meth.visitMethodInsn(Opcodes.INVOKESTATIC, 'net/minecraftforge/fmlonlyserver/ServerModLoader', 'load', '()V', false)
var methOptions = ASMAPI.getMethodNode();
methOptions.visitIntInsn(Opcodes.ALOAD, 1);
methOptions.visitMethodInsn(Opcodes.INVOKESTATIC, 'net/minecraftforge/fmlonlyserver/ServerModLoader', 'addOptions', '(Ljoptsimple/OptionParser;)V', false)
for (var i=0; i<methodNode.instructions.size(); i++) {
var ain = methodNode.instructions.get(i);
if (ain.getOpcode() == Opcodes.INVOKESPECIAL && ain.owner == 'joptsimple/OptionParser' && ain.name == '<init>' && ain.desc == '()V') {
// Inject after the STORE
methodNode.instructions.insert(methodNode.instructions.get(i + 1), methOptions.instructions);
} else if (ain.getOpcode() == Opcodes.INVOKESTATIC && ain.owner == 'net/minecraft/Util' && ain.name == ASMAPI.mapMethod('m_137584_') /* startTimerHackThread */ && ain.desc == '()V') {
methodNode.instructions.insert(ain, meth.instructions);
break; // By this point we've already reached the first injection point so break
}
}
return methodNode;
}
},
}
}
+1
View File
@@ -1,4 +1,5 @@
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
# This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+6 -5
View File
@@ -85,9 +85,6 @@ done
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# 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"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -144,7 +141,7 @@ 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=SC3045
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +149,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then
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, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
@@ -1,19 +0,0 @@
activeContentFilterList=*.makefile,makefile,*.Makefile,Makefile,Makefile.*,*.mk,MANIFEST.MF,.project,*.yml
addNewLine=true
convertActionOnSaave=AnyEdit.CnvrtTabToSpaces
eclipse.preferences.version=1
encoding/<project>=UTF-8
fixLineDelimiters=false
ignoreBlankLinesWhenTrimming=false
inActiveContentFilterList=
javaTabWidthForJava=true
org.eclipse.jdt.ui.editor.tab.width=2
projectPropsEnabled=true
removeTrailingSpaces=true
replaceAllSpaces=false
replaceAllTabs=false
saveAndAddLine=false
saveAndConvert=true
saveAndFixLineDelimiters=false
saveAndTrim=true
useModulo4Tabs=false
@@ -1,2 +0,0 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8
@@ -1,2 +0,0 @@
eclipse.preferences.version=1
line.separator=\n
+3 -2
View File
@@ -2,9 +2,10 @@ plugins {
id 'com.github.ben-manes.versions'
id 'org.javamodularity.moduleplugin' version '1.8.3' apply false
id 'org.cadixdev.licenser'
id 'java-library'
id 'jacoco'
}
apply plugin: 'java-library'
apply plugin: 'jacoco'
apply plugin: 'org.javamodularity.moduleplugin'
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
@@ -8,7 +8,7 @@ package net.minecraftforge.fml.javafmlmod;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.ModLoadingContext;
public class FMLJavaModLoadingContext
public class FMLJavaModLoadingContext extends ModLoadingContext
{
private final FMLModContainer container;
@@ -24,11 +24,18 @@ public class FMLJavaModLoadingContext
return container.getEventBus();
}
@Override
public FMLModContainer getContainer() {
return container;
}
/**
* Helper to get the right instance from the {@link ModLoadingContext} correctly.
* @return The FMLJavaMod language specific extension from the ModLoadingContext
*
* @deprecated use {@link FMLJavaModLoadingContext} in your mod constructor
*/
@Deprecated(forRemoval = true, since = "1.21.1")
public static FMLJavaModLoadingContext get() {
return ModLoadingContext.get().extension();
}
@@ -21,6 +21,9 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
import java.util.Optional;
public class FMLModContainer extends ModContainer
@@ -31,6 +34,7 @@ public class FMLModContainer extends ModContainer
private final IEventBus eventBus;
private Object modInstance;
private final Class<?> modClass;
private final FMLJavaModLoadingContext context = new FMLJavaModLoadingContext(this);
public FMLModContainer(IModInfo info, String className, ModFileScanData modFileScanResults, ModuleLayer gameLayer)
{
@@ -40,8 +44,7 @@ public class FMLModContainer extends ModContainer
activityMap.put(ModLoadingStage.CONSTRUCT, this::constructMod);
this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).useModLauncher().build();
this.configHandler = Optional.of(ce->this.eventBus.post(ce.self()));
final FMLJavaModLoadingContext contextExtension = new FMLJavaModLoadingContext(this);
this.contextExtension = () -> contextExtension;
this.contextExtension = () -> context;
try
{
var layer = gameLayer.findModule(info.getOwningFile().moduleName()).orElseThrow();
@@ -65,11 +68,22 @@ public class FMLModContainer extends ModContainer
try
{
LOGGER.trace(LOADING, "Loading mod instance {} of type {}", getModId(), modClass.getName());
this.modInstance = modClass.getDeclaredConstructor().newInstance();
Constructor<?> constructor;
try {
constructor = modClass.getDeclaredConstructor(context.getClass());
} catch (NoSuchMethodException | SecurityException exception) {
constructor = modClass.getDeclaredConstructor();
}
this.modInstance = constructor.getParameterCount() == 0 ? constructor.newInstance() : constructor.newInstance(context);
LOGGER.trace(LOADING, "Loaded mod instance {} of type {}", getModId(), modClass.getName());
}
catch (Throwable e)
{
// When a mod constructor throws an exception, it's wrapped in an InvocationTargetException which hides the
// actual exception from the mod loading error screen.
if (e instanceof InvocationTargetException wrapped)
e = Objects.requireNonNullElse(wrapped.getCause(), e); // unwrap the exception
LOGGER.error(LOADING,"Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e);
throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass);
}
+3 -2
View File
@@ -2,9 +2,10 @@ plugins {
id 'com.github.ben-manes.versions'
id 'org.javamodularity.moduleplugin' version '1.8.3' apply false
id 'org.cadixdev.licenser'
id 'java-library'
id 'jacoco'
}
apply plugin: 'java-library'
apply plugin: 'jacoco'
apply plugin: 'org.javamodularity.moduleplugin'
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
+3 -2
View File
@@ -2,9 +2,10 @@ plugins {
id 'com.github.ben-manes.versions'
id 'org.javamodularity.moduleplugin' version '1.8.3' apply false
id 'org.cadixdev.licenser'
id 'java-library'
id 'jacoco'
}
apply plugin: 'java-library'
apply plugin: 'jacoco'
apply plugin: 'org.javamodularity.moduleplugin'
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
+7 -7
View File
@@ -16,19 +16,19 @@ Step 1: Open your command-line and browse to the folder where you extracted the
Step 2: You're left with a choice.
If you prefer to use Eclipse:
1. Run the following command: `gradlew genEclipseRuns` (`./gradlew genEclipseRuns` if you are on Mac/Linux)
1. Run the following command: `./gradlew genEclipseRuns`
2. Open Eclipse, Import > Existing Gradle Project > Select Folder
or run `gradlew eclipse` to generate the project.
If you prefer to use IntelliJ:
1. Open IDEA, and import project.
2. Select your build.gradle file and have it import.
3. Run the following command: `gradlew genIntellijRuns` (`./gradlew genIntellijRuns` if you are on Mac/Linux)
3. Run the following command: `./gradlew genIntellijRuns`
4. Refresh the Gradle Project in IDEA if required.
If at any point you are missing libraries in your IDE, or you've run into problems you can
run `gradlew --refresh-dependencies` to refresh the local cache. `gradlew clean` to reset everything
{this does not affect your code} and then start the process again.
(this does not affect your code) and then start the process again.
Mapping Names:
=============================
@@ -40,7 +40,7 @@ https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md
Additional Resources:
=========================
Community Documentation: https://mcforge.readthedocs.io/en/latest/gettingstarted/
LexManos' Install Video: https://www.youtube.com/watch?v=8VEdtQLuLO0
Forge Forum: https://forums.minecraftforge.net/
Forge Discord: https://discord.gg/UvedJ9m
Community Documentation: https://docs.minecraftforge.net/en/1.20.1/gettingstarted/
LexManos' Install Video: https://youtu.be/8VEdtQLuLO0
Forge Forums: https://forums.minecraftforge.net/
Forge Discord: https://discord.minecraftforge.net/
+93 -132
View File
@@ -1,156 +1,87 @@
// Don't see a feature you're wanting to use?
// See our MDKExamples repository for minimal examples for features like JarInJar
// https://github.com/MinecraftForge/MDKExamples
plugins {
id 'eclipse'
id 'idea'
id 'java'
id 'maven-publish'
id 'net.minecraftforge.gradle' version '[6.0,6.2)'
id 'net.minecraftforge.gradle' version '[7.0,8)'
id 'net.minecraftforge.renamer' version '1.1.8+' // Required on all versions which use SRG maps at runtime.
}
version = mod_version
group = mod_group_id
base {
archivesName = mod_id
}
version = '1.0'
group = 'com.example'
base.archivesName = 'examplemod'
// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17.
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}"
println "Java: ${providers.systemProperty('java.version').get()}, " +
"JVM: ${providers.systemProperty('java.vm.version').get()} (${providers.systemProperty('java.vendor').get()}), " +
"Arch: ${providers.systemProperty('os.arch').get()}"
minecraft {
// The mappings can be changed at any time and must be in the following format.
// Channel: Version:
// official MCVersion Official field/method names from Mojang mapping files
// parchment YYYY.MM.DD-MCVersion Open community-sourced parameter names and javadocs layered on top of official
//
// You must be aware of the Mojang license when using the 'official' or 'parchment' mappings.
// See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md
//
// Parchment is an unofficial project maintained by ParchmentMC, separate from MinecraftForge
// Additional setup is needed to use their mappings: https://parchmentmc.org/docs/getting-started
//
// Use non-default mappings at your own risk. They may not always work.
// Simply re-run your setup task after changing the mappings to update your workspace.
mappings channel: mapping_channel, version: mapping_version
// Declare which mappings to use
mappings channel: mapping_channel, version: minecraft_version
// When true, this property will have all Eclipse/IntelliJ IDEA run configurations run the "prepareX" task for the given run configuration before launching the game.
// In most cases, it is not necessary to enable.
// enableEclipsePrepareRuns = true
// enableIdeaPrepareRuns = true
// Access Transformer files may now be automatically detected, or explicitly declared.
// Setting the below flag will cause ForgeGradle to look for META-INF/accesstransformer.cfg
// Alternatively, provide it a file(<path>) or files(<paths>) for explicitly declared files. If you use any
// explicitly declared access transformers, all transformers must be also declared in the mods.toml
// accessTransformer = true
// This property allows configuring Gradle's ProcessResources task(s) to run on IDE output locations before launching the game.
// It is REQUIRED to be set to true for this template to function.
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
copyIdeResources = true
// When true, this property will add the folder name of all declared run configurations to generated IDE run configurations.
// The folder name can be set on a run configuration using the "folderName" property.
// By default, the folder name of a run configuration is the name of the Gradle project containing it.
// generateRunFolders = true
// This property enables access transformers for use in development.
// They will be applied to the Minecraft artifact.
// The access transformer file can be anywhere in the project.
// However, it must be at "META-INF/accesstransformer.cfg" in the final mod jar to be loaded by Forge.
// This default location is a best practice to automatically put the file in the right place in the final jar.
// See https://docs.minecraftforge.net/en/latest/advanced/accesstransformers/ for more information.
// accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
runs {
// applies to all the run configs below
configureEach {
workingDirectory project.file('run')
workingDir.convention layout.projectDirectory.dir('run')
// Recommended logging data for a userdev environment
// The markers can be added/remove as needed separated by commas.
// "SCAN": For mods scan.
// "REGISTRIES": For firing of registry events.
// "REGISTRYDUMP": For getting the contents of all registries.
property 'forge.logging.markers', 'REGISTRIES'
//systemProperty 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
// You can set various levels here.
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
property 'forge.logging.console.level', 'debug'
mods {
"${mod_id}" {
source sourceSets.main
}
}
//args "-mixin.config=${mod_id}.mixins.json"
}
client {
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
property 'forge.enabledGameTestNamespaces', mod_id
register('client') {
systemProperty 'forge.enabledGameTestNamespaces', mod_id
}
server {
property 'forge.enabledGameTestNamespaces', mod_id
register('server') {
systemProperty 'forge.enabledGameTestNamespaces', mod_id
args '--nogui'
}
// This run config launches GameTestServer and runs all registered gametests, then exits.
// By default, the server will crash when no gametests are provided.
// The gametest system is also enabled by default for other run configs under the /test command.
gameTestServer {
property 'forge.enabledGameTestNamespaces', mod_id
register('gameTestServer') {
systemProperty 'forge.enabledGameTestNamespaces', mod_id
}
data {
// example of overriding the workingDirectory set in configureEach above
workingDirectory project.file('run-data')
register('data') {
workingDir = layout.projectDirectory.dir('run-data')
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
args '--mod', mod_id, '--all', '--output', layout.projectDirectory.dir('src/generated/resources'), '--existing', layout.projectDirectory.dir('src/main/resources')
}
}
}
// Include resources generated by data generators.
sourceSets.main.resources { srcDir 'src/generated/resources' }
repositories {
// Put repositories for dependencies here
// ForgeGradle automatically adds the Forge maven and Maven Central for you
// If you have mod jar dependencies in ./libs, you can declare them as a repository like so.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver
// flatDir {
// dir 'libs'
// }
minecraft.mavenizer(it)
maven fg.forgeMaven
maven fg.minecraftLibsMaven
// Needed for JEI
// maven { url = 'https://maven.blamejared.com/' }
}
dependencies {
// Specify the version of Minecraft to use.
// Any artifact can be supplied so long as it has a "userdev" classifier artifact and is a compatible patcher artifact.
// The "userdev" classifier will be requested and setup by ForgeGradle.
// If the group id is "net.minecraft" and the artifact id is one of ["client", "server", "joined"],
// then special handling is done to allow a setup of a vanilla dependency without the use of an external repository.
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
// Example mod dependency with JEI - using fg.deobf() ensures the dependency is remapped to your development mappings
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}-common-api:${jei_version}")
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}")
// runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}")
// Example mod dependency using a mod jar from ./libs with a flat dir repository
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
// The group id is ignored when searching -- in this case, it is "blank"
// implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}")
// For more info:
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
// Minecraft/Forge itself
implementation minecraft.dependency("net.minecraftforge:forge:$minecraft_version-$forge_version")
// A 3rd party mod, which needs to be deobfuscated from SRG names to the mappings we're using
// implementation renamer.dependency('mezz.jei:jei-1.20.1-forge:15.20.0.129')
// annotationProcessor 'org.spongepowered:mixin:0.8.7:processor'
}
// This block of code expands all declared replace properties in the specified resource targets.
// A missing property will result in an error. Properties are expanded using ${} Groovy notation.
// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments.
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
tasks.named('processResources', ProcessResources).configure {
tasks.named('processResources', ProcessResources) {
var replaceProperties = [
minecraft_version: minecraft_version, minecraft_version_range: minecraft_version_range,
forge_version: forge_version, forge_version_range: forge_version_range,
@@ -158,15 +89,15 @@ tasks.named('processResources', ProcessResources).configure {
mod_id: mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version,
mod_authors: mod_authors, mod_description: mod_description,
]
inputs.properties replaceProperties
inputs.property('replaceProperties', replaceProperties)
filesMatching(['META-INF/mods.toml', 'pack.mcmeta']) {
expand replaceProperties + [project: project]
expand replaceProperties
}
}
// Example for how to get properties into the manifest for reading at runtime.
tasks.named('jar', Jar).configure {
tasks.named('jar', Jar) {
manifest {
attributes([
'Specification-Title' : mod_id,
@@ -174,34 +105,64 @@ tasks.named('jar', Jar).configure {
'Specification-Version' : '1', // We are version 1 of ourselves
'Implementation-Title' : project.name,
'Implementation-Version' : project.jar.archiveVersion,
'Implementation-Vendor' : mod_authors,
'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
'Implementation-Vendor' : mod_authors
])
//attributes['MixinConfigs'] = "${mod_id}.mixins.json"
}
// This is the preferred method to reobfuscate your jar file
finalizedBy 'reobfJar'
// This artifact will not be renamed. While this will not launch in production
// the artifact may be helpful for other developers if your mod is used as a library.
// By assigning this archive classifier, jar file name will end in '-unobfuscated'
// e.g. 'examplemod-1.0-unobfuscated.jar'
archiveClassifier = 'unobfuscated'
}
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing:
// tasks.named('publish').configure {
// dependsOn 'reobfJar'
// }
// Set the default mappings file to our default minecraft dependency
// This must be done after `minecraft.dependency('coords')` has been done so that gradle knows what dependency we want
// This mapping will be used by default for all renamer tasks, including deobfuscating dependencies
// You can still override each specific task, this is just to save repeating yourself in your buildscript.
renamer.mappings.from minecraft.dependency.toSrgFile
// If you are choosing to use mixins, you must give Renamer the additional information for it to handle the refmaps.
//renamer.enableMixinRefmaps {
// config "${mod_id}.mixins.json"
// refMap "${mod_id}.refmap.json"
//}
// Creates a task named 'renameJar'
renamer.classes(tasks.named('jar', Jar)) {
// This artifact will be remapped according to the provided mappings file
// Setting the classifier to '' will attach no extra details to the file name.
// e.g. 'examplemod-1.0.jar'
archiveClassifier = ''
// Unless set globally like above, You need to point to the mappings you wish to apply, typically this is
// the Mapped names to SRG for older versions.
// ForgeGradle/Mavenizer generate these files for the dependencies you declare. So you can use the helper.
// Or you can specify the file or dependency if you host them yourself.
// map.from minecraft.dependency.toSrgFile
// The mixin AP does not include all annotated entries, instead containing some in the augmented mappings.
// So, if you are using mixins or have dependencies which use them, uncomment the below line to tell renamer
// that the generated mappings are available. See the MDKExample for more details.
// mappings renamer.mixin.generatedMappings
}
// Example configuration to allow publishing using the maven-publish plugin
// We recommend publishing all of your artifacts to publicly accessible maven so that others can easily consume them as dependencies
publishing {
publications {
register('mavenJava', MavenPublication) {
artifact jar
}
}
repositories {
maven {
url "file://${project.projectDir}/mcmodsrepo"
}
// Publishing to a local folder makes it easy to see exactly what you would publish to a real server
maven { url = layout.projectDirectory.dir('repo').asFile.toURI() }
}
publications.register('mavenJava', MavenPublication) {
from components.java // Publish the normal jar
artifact renameJar // Publish the renamed jar in addition
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
}
// IntelliJ no longer downloads javadocs and sources by default, this tells Gradle to force IntelliJ to do it.
idea.module { downloadJavadoc = downloadSources = true }
+16 -13
View File
@@ -1,25 +1,29 @@
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
# This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.configuration-cache=true
org.gradle.configuration-cache.parallel=true
org.gradle.configuration-cache.problems=warn
net.minecraftforge.gradle.merge-source-sets=true
## Environment Properties
# The Minecraft version must agree with the Forge version to get a valid artifact
minecraft_version=@MC_VERSION@
minecraft_version=1.20.1
# The Minecraft version range can use any release version of Minecraft as bounds.
# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly
# as they do not follow standard versioning conventions.
minecraft_version_range=[@MC_VERSION@,@MC_NEXT_VERSION@)
minecraft_version_range=[1.20.1, 1.20.2)
# The Forge version must agree with the Minecraft version to get a valid artifact
forge_version=@FORGE_VERSION@
# The Forge version range can use any version of Forge as bounds or match the loader version range
forge_version_range=[@FORGE_SPEC_VERSION@,)
forge_version_range=[47,)
# The loader version range can only use the major version of Forge/FML as bounds
loader_version_range=[@FORGE_SPEC_VERSION@,)
loader_version_range=[47,)
# The mapping channel to use for mappings.
# The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"].
# The default set of supported mapping channels are ["official", "parchment"].
# Additional mapping channels can be registered through the "channelProviders" extension in a Gradle plugin.
#
# | Channel | Version | |
@@ -31,12 +35,11 @@ loader_version_range=[@FORGE_SPEC_VERSION@,)
# See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md
#
# Parchment is an unofficial project maintained by ParchmentMC, separate from Minecraft Forge.
# Additional setup is needed to use their mappings, see https://parchmentmc.org/docs/getting-started
mapping_channel=@MAPPING_CHANNEL@
# Unlike in FG6, Parchment no longer requires the Librarian plugin to be set up.
mapping_channel=official
# The mapping version to query from the mapping channel.
# This must match the format required by the mapping channel.
mapping_version=@MAPPING_VERSION@
mapping_version=1.20.1
## Mod Properties
+3 -11
View File
@@ -1,13 +1,5 @@
pluginManagement {
repositories {
gradlePluginPortal()
maven {
name = 'MinecraftForge'
url = 'https://maven.minecraftforge.net/'
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0'
}
rootProject.name = 'examplemod'
@@ -18,7 +18,6 @@ import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
@@ -61,9 +60,9 @@ public class ExampleMod
output.accept(EXAMPLE_ITEM.get()); // Add the example item to the tab. For your own tabs, this method is preferred over the event
}).build());
public ExampleMod()
public ExampleMod(FMLJavaModLoadingContext context)
{
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
IEventBus modEventBus = context.getModEventBus();
// Register the commonSetup method for modloading
modEventBus.addListener(this::commonSetup);
@@ -82,7 +81,7 @@ public class ExampleMod
modEventBus.addListener(this::addCreative);
// Register our mod's ForgeConfigSpec so that Forge can create and load the config file for us
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, Config.SPEC);
context.registerConfig(ModConfig.Type.COMMON, Config.SPEC);
}
private void commonSetup(final FMLCommonSetupEvent event)
+11 -2
View File
@@ -12,6 +12,9 @@ loaderVersion="${loader_version_range}" #mandatory This is typically bumped ever
license="${mod_license}"
# A URL to refer people to when problems occur with this mod
#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional
# If your mod is purely client-side and has no multiplayer functionality (be it dedicated servers or Open to LAN),
# set this to true, and Forge will set the correct displayTest for you and skip loading your mod on dedicated servers.
#clientSideOnly=true #optional - defaults to false if absent
# A list of mods - how many allowed here is determined by the individual mod loader
[[mods]] #mandatory
# The modid of the mod
@@ -36,7 +39,13 @@ authors="${mod_authors}" #optional
# IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component.
# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value.
# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself.
#displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional)
#displayTest="MATCH_VERSION" # if nothing is specified, MATCH_VERSION is the default when clientSideOnly=false, otherwise IGNORE_ALL_VERSION when clientSideOnly=true (#optional)
# Access Transformers
# Optional, if not specified Forge will attempt to find the default META-INF/accesstransformer.cfg and silently continue if not found. May default to an empty list in a future MC.
# If you specify AT path strings in this list, Forge will attempt to load each of them and throw errors for any that aren't found.
# Specifying an empty list when your mod has no ATs is recommended to opt-out of the default for slightly better loading performance.
#accessTransformers = []
# The description text for the mod (multi line!) (#mandatory)
description='''${mod_description}'''
@@ -67,4 +76,4 @@ description='''${mod_description}'''
# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't
# stop your mod loading on the server for example.
#[features.${mod_id}]
#openGLVersion="[3.2,)"
#openGLVersion="[3.2,)"
@@ -5,30 +5,30 @@
GlStateManager._texParameter(3553, 10242, 33071);
GlStateManager._texParameter(3553, 10243, 33071);
+ if (!stencilEnabled)
GlStateManager._texImage2D(3553, 0, 6402, this.width, this.height, 0, 6402, 5126, (IntBuffer)null);
GlStateManager._texImage2D(3553, 0, 6402, this.f_83915_, this.f_83916_, 0, 6402, 5126, (IntBuffer)null);
+ else
+ GlStateManager._texImage2D(3553, 0, org.lwjgl.opengl.GL30.GL_DEPTH32F_STENCIL8, this.width, this.height, 0, org.lwjgl.opengl.GL30.GL_DEPTH_STENCIL, org.lwjgl.opengl.GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV, null);
+ GlStateManager._texImage2D(3553, 0, org.lwjgl.opengl.GL30.GL_DEPTH32F_STENCIL8, this.f_83915_, this.f_83916_, 0, org.lwjgl.opengl.GL30.GL_DEPTH_STENCIL, org.lwjgl.opengl.GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV, null);
}
this.setFilterMode(9728);
this.m_83936_(9728);
@@ -124,7 +_,14 @@
GlStateManager._glBindFramebuffer(36160, this.frameBufferId);
GlStateManager._glFramebufferTexture2D(36160, 36064, 3553, this.colorTextureId, 0);
if (this.useDepth) {
GlStateManager._glBindFramebuffer(36160, this.f_83920_);
GlStateManager._glFramebufferTexture2D(36160, 36064, 3553, this.f_83923_, 0);
if (this.f_83919_) {
+ if(!stencilEnabled)
GlStateManager._glFramebufferTexture2D(36160, 36096, 3553, this.depthBufferId, 0);
GlStateManager._glFramebufferTexture2D(36160, 36096, 3553, this.f_83924_, 0);
+ else if(net.minecraftforge.common.ForgeConfig.CLIENT.useCombinedDepthStencilAttachment.get()) {
+ GlStateManager._glFramebufferTexture2D(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER, org.lwjgl.opengl.GL30.GL_DEPTH_STENCIL_ATTACHMENT, 3553, this.depthBufferId, 0);
+ GlStateManager._glFramebufferTexture2D(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER, org.lwjgl.opengl.GL30.GL_DEPTH_STENCIL_ATTACHMENT, 3553, this.f_83924_, 0);
+ } else {
+ GlStateManager._glFramebufferTexture2D(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER, org.lwjgl.opengl.GL30.GL_DEPTH_ATTACHMENT, 3553, this.depthBufferId, 0);
+ GlStateManager._glFramebufferTexture2D(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER, org.lwjgl.opengl.GL30.GL_STENCIL_ATTACHMENT, 3553, this.depthBufferId, 0);
+ GlStateManager._glFramebufferTexture2D(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER, org.lwjgl.opengl.GL30.GL_DEPTH_ATTACHMENT, 3553, this.f_83924_, 0);
+ GlStateManager._glFramebufferTexture2D(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER, org.lwjgl.opengl.GL30.GL_STENCIL_ATTACHMENT, 3553, this.f_83924_, 0);
+ }
}
this.checkStatus();
this.m_83949_();
@@ -292,4 +_,27 @@
public int getDepthTextureId() {
return this.depthBufferId;
public int m_83980_() {
return this.f_83924_;
}
+
+ /*================================ FORGE START ================================================*/
@@ -42,7 +42,7 @@
+ public void enableStencil() {
+ if(stencilEnabled) return;
+ stencilEnabled = true;
+ this.resize(viewWidth, viewHeight, net.minecraft.client.Minecraft.ON_OSX);
+ this.m_83941_(f_83917_, f_83918_, net.minecraft.client.Minecraft.f_91002_);
+ }
+
+ /**
@@ -1,28 +1,28 @@
--- a/com/mojang/blaze3d/platform/Window.java
+++ b/com/mojang/blaze3d/platform/Window.java
@@ -81,7 +_,8 @@
@@ -83,7 +_,8 @@
GLFW.glfwWindowHint(139267, 2);
GLFW.glfwWindowHint(139272, 204801);
GLFW.glfwWindowHint(139270, 1);
- this.window = GLFW.glfwCreateWindow(this.width, this.height, p_85376_, this.fullscreen && monitor != null ? monitor.getMonitor() : 0L, 0L);
+ this.window = net.minecraftforge.fml.loading.ImmediateWindowHandler.setupMinecraftWindow(()->this.width, ()->this.height, ()->p_85376_, ()->this.fullscreen && monitor != null ? monitor.getMonitor() : 0L);
+ if (!net.minecraftforge.fml.loading.ImmediateWindowHandler.positionWindow(Optional.ofNullable(monitor), w->this.width = this.windowedWidth = w, h->this.height = this.windowedHeight = h, x->this.x = this.windowedX = x, y->this.y = this.windowedY = y)) {
- this.f_85349_ = GLFW.glfwCreateWindow(this.f_85359_, this.f_85360_, p_85376_, this.f_85355_ && monitor != null ? monitor.m_84954_() : 0L, 0L);
+ this.f_85349_ = net.minecraftforge.fml.loading.ImmediateWindowHandler.setupMinecraftWindow(()->this.f_85359_, ()->this.f_85360_, ()->p_85376_, ()->this.f_85355_ && monitor != null ? monitor.m_84954_() : 0L);
+ if (!net.minecraftforge.fml.loading.ImmediateWindowHandler.positionWindow(Optional.ofNullable(monitor), w->this.f_85359_ = this.f_85352_ = w, h->this.f_85360_ = this.f_85353_ = h, x->this.f_85357_ = this.f_85350_ = x, y->this.f_85358_ = this.f_85351_ = y)) {
if (monitor != null) {
VideoMode videomode = monitor.getPreferredVidMode(this.fullscreen ? this.preferredFullscreenVideoMode : Optional.empty());
this.windowedX = this.x = monitor.getX() + videomode.getWidth() / 2 - this.width / 2;
@@ -93,6 +_,7 @@
this.windowedX = this.x = aint1[0];
this.windowedY = this.y = aint[0];
VideoMode videomode = monitor.m_84948_(this.f_85355_ ? this.f_85354_ : Optional.empty());
this.f_85350_ = this.f_85357_ = monitor.m_84951_() + videomode.m_85332_() / 2 - this.f_85359_ / 2;
@@ -95,6 +_,7 @@
this.f_85350_ = this.f_85357_ = aint1[0];
this.f_85351_ = this.f_85358_ = aint[0];
}
+ }
GLFW.glfwMakeContextCurrent(this.window);
GL.createCapabilities();
@@ -242,6 +_,7 @@
GLFW.glfwGetFramebufferSize(this.window, aint, aint1);
this.framebufferWidth = aint[0] > 0 ? aint[0] : 1;
this.framebufferHeight = aint1[0] > 0 ? aint1[0] : 1;
+ if (this.framebufferHeight == 0 || this.framebufferWidth==0) net.minecraftforge.fml.loading.ImmediateWindowHandler.updateFBSize(w->this.framebufferWidth=w, h->this.framebufferHeight=h);
GLFW.glfwMakeContextCurrent(this.f_85349_);
Locale locale = Locale.getDefault(Category.FORMAT);
@@ -238,6 +_,7 @@
GLFW.glfwGetFramebufferSize(this.f_85349_, aint, aint1);
this.f_85361_ = aint[0] > 0 ? aint[0] : 1;
this.f_85362_ = aint1[0] > 0 ? aint1[0] : 1;
+ if (this.f_85362_ == 0 || this.f_85361_==0) net.minecraftforge.fml.loading.ImmediateWindowHandler.updateFBSize(w->this.f_85361_=w, h->this.f_85362_=h);
}
private void onResize(long p_85428_, int p_85429_, int p_85430_) {
private void m_85427_(long p_85428_, int p_85429_, int p_85430_) {
@@ -1,17 +1,17 @@
--- a/com/mojang/blaze3d/vertex/BufferBuilder.java
+++ b/com/mojang/blaze3d/vertex/BufferBuilder.java
@@ -459,4 +_,14 @@
this.sorting = p_277855_;
this.f_276566_ = p_277855_;
}
}
+
+ // Forge start
+ public void putBulkData(ByteBuffer buffer) {
+ ensureCapacity(buffer.limit() + this.format.getVertexSize());
+ this.buffer.position(this.nextElementByte);
+ this.buffer.put(buffer);
+ this.buffer.position(0);
+ this.vertices += buffer.limit() / this.format.getVertexSize();
+ this.nextElementByte += buffer.limit();
+ m_85722_(buffer.limit() + this.f_85658_.m_86020_());
+ this.f_85648_.position(this.f_85652_);
+ this.f_85648_.put(buffer);
+ this.f_85648_.position(0);
+ this.f_85654_ += buffer.limit() / this.f_85658_.m_86020_();
+ this.f_85652_ += buffer.limit();
+ }
}
@@ -6,6 +6,6 @@
@OnlyIn(Dist.CLIENT)
-public class PoseStack {
+public class PoseStack implements net.minecraftforge.client.extensions.IForgePoseStack {
private final Deque<PoseStack.Pose> poseStack = Util.make(Queues.newArrayDeque(), (p_85848_) -> {
private final Deque<PoseStack.Pose> f_85834_ = Util.m_137469_(Queues.newArrayDeque(), (p_85848_) -> {
Matrix4f matrix4f = new Matrix4f();
Matrix3f matrix3f = new Matrix3f();
@@ -2,10 +2,10 @@
+++ b/com/mojang/blaze3d/vertex/SheetedDecalTextureGenerator.java
@@ -46,7 +_,7 @@
public void endVertex() {
Vector3f vector3f = this.normalInversePose.transform(new Vector3f(this.nx, this.ny, this.nz));
- Direction direction = Direction.getNearest(vector3f.x(), vector3f.y(), vector3f.z());
public void m_5752_() {
Vector3f vector3f = this.f_85869_.transform(new Vector3f(this.f_85876_, this.f_85877_, this.f_85878_));
- Direction direction = Direction.m_122372_(vector3f.x(), vector3f.y(), vector3f.z());
+ Direction direction = net.minecraftforge.client.ForgeHooksClient.getNearestStable(vector3f.x(), vector3f.y(), vector3f.z());
Vector4f vector4f = this.cameraInversePose.transform(new Vector4f(this.x, this.y, this.z, 1.0F));
Vector4f vector4f = this.f_85868_.transform(new Vector4f(this.f_85870_, this.f_85871_, this.f_85872_, 1.0F));
vector4f.rotateY((float)Math.PI);
vector4f.rotateX((-(float)Math.PI / 2F));
@@ -6,20 +6,20 @@
@OnlyIn(Dist.CLIENT)
-public interface VertexConsumer {
+public interface VertexConsumer extends net.minecraftforge.client.extensions.IForgeVertexConsumer {
VertexConsumer vertex(double p_85945_, double p_85946_, double p_85947_);
VertexConsumer m_5483_(double p_85945_, double p_85946_, double p_85947_);
VertexConsumer color(int p_85973_, int p_85974_, int p_85975_, int p_85976_);
VertexConsumer m_6122_(int p_85973_, int p_85974_, int p_85975_, int p_85976_);
@@ -64,6 +_,10 @@
}
default void putBulkData(PoseStack.Pose p_85996_, BakedQuad p_85997_, float[] p_85998_, float p_85999_, float p_86000_, float p_86001_, int[] p_86002_, int p_86003_, boolean p_86004_) {
default void m_85995_(PoseStack.Pose p_85996_, BakedQuad p_85997_, float[] p_85998_, float p_85999_, float p_86000_, float p_86001_, int[] p_86002_, int p_86003_, boolean p_86004_) {
+ putBulkData(p_85996_, p_85997_, p_85998_, p_85999_, p_86000_, p_86001_, 1, p_86002_, p_86003_, p_86004_);
+ }
+
+ default void putBulkData(PoseStack.Pose p_85996_, BakedQuad p_85997_, float[] p_85998_, float p_85999_, float p_86000_, float p_86001_, float alpha, int[] p_86002_, int p_86003_, boolean p_86004_) {
float[] afloat = new float[]{p_85998_[0], p_85998_[1], p_85998_[2], p_85998_[3]};
int[] aint = new int[]{p_86002_[0], p_86002_[1], p_86002_[2], p_86002_[3]};
int[] aint1 = p_85997_.getVertices();
int[] aint1 = p_85997_.m_111303_();
@@ -99,11 +_,13 @@
f5 = afloat[k] * p_86001_;
}
@@ -29,10 +29,10 @@
float f9 = bytebuffer.getFloat(16);
float f10 = bytebuffer.getFloat(20);
Vector4f vector4f = matrix4f.transform(new Vector4f(f, f1, f2, 1.0F));
- this.vertex(vector4f.x(), vector4f.y(), vector4f.z(), f3, f4, f5, 1.0F, f9, f10, p_86003_, l, vector3f.x(), vector3f.y(), vector3f.z());
+ applyBakedNormals(vector3f, bytebuffer, p_85996_.normal());
- this.m_5954_(vector4f.x(), vector4f.y(), vector4f.z(), f3, f4, f5, 1.0F, f9, f10, p_86003_, l, vector3f.x(), vector3f.y(), vector3f.z());
+ applyBakedNormals(vector3f, bytebuffer, p_85996_.m_252943_());
+ float vertexAlpha = p_86004_ ? alpha * (float) (bytebuffer.get(15) & 255) / 255.0F : alpha;
+ this.vertex(vector4f.x(), vector4f.y(), vector4f.z(), f3, f4, f5, vertexAlpha, f9, f10, p_86003_, l, vector3f.x(), vector3f.y(), vector3f.z());
+ this.m_5954_(vector4f.x(), vector4f.y(), vector4f.z(), f3, f4, f5, vertexAlpha, f9, f10, p_86003_, l, vector3f.x(), vector3f.y(), vector3f.z());
}
}
@@ -5,10 +5,10 @@
}
}
+
+ public ImmutableMap<String, VertexFormatElement> getElementMapping() { return elementMapping; }
+ public int getOffset(int index) { return offsets.getInt(index); }
+ public boolean hasPosition() { return elements.stream().anyMatch(e -> e.getUsage() == VertexFormatElement.Usage.POSITION); }
+ public boolean hasNormal() { return elements.stream().anyMatch(e -> e.getUsage() == VertexFormatElement.Usage.NORMAL); }
+ public boolean hasColor() { return elements.stream().anyMatch(e -> e.getUsage() == VertexFormatElement.Usage.COLOR); }
+ public boolean hasUV(int which) { return elements.stream().anyMatch(e -> e.getUsage() == VertexFormatElement.Usage.UV && e.getIndex() == which); }
+ public ImmutableMap<String, VertexFormatElement> getElementMapping() { return f_166905_; }
+ public int getOffset(int index) { return f_86013_.getInt(index); }
+ public boolean hasPosition() { return f_86012_.stream().anyMatch(e -> e.m_86048_() == VertexFormatElement.Usage.POSITION); }
+ public boolean hasNormal() { return f_86012_.stream().anyMatch(e -> e.m_86048_() == VertexFormatElement.Usage.NORMAL); }
+ public boolean hasColor() { return f_86012_.stream().anyMatch(e -> e.m_86048_() == VertexFormatElement.Usage.COLOR); }
+ public boolean hasUV(int which) { return f_86012_.stream().anyMatch(e -> e.m_86048_() == VertexFormatElement.Usage.UV && e.m_86049_() == which); }
}
@@ -1,11 +1,11 @@
--- a/com/mojang/blaze3d/vertex/VertexFormatElement.java
+++ b/com/mojang/blaze3d/vertex/VertexFormatElement.java
@@ -90,6 +_,10 @@
this.usage.clearBufferState(this.index, p_166964_);
this.f_86031_.m_166978_(this.f_86032_, p_166964_);
}
+ public int getElementCount() {
+ return count;
+ return f_86033_;
+ }
+
@OnlyIn(Dist.CLIENT)
@@ -1,17 +1,17 @@
--- a/com/mojang/math/Transformation.java
+++ b/com/mojang/math/Transformation.java
@@ -13,7 +_,7 @@
@@ -14,7 +_,7 @@
import org.joml.Vector3f;
import org.joml.Vector3fc;
-public final class Transformation {
+public final class Transformation implements net.minecraftforge.common.extensions.IForgeTransformation {
private final Matrix4f matrix;
public static final Codec<Transformation> CODEC = RecordCodecBuilder.create((p_269604_) -> {
return p_269604_.group(ExtraCodecs.VECTOR3F.fieldOf("translation").forGetter((p_269599_) -> {
@@ -156,6 +_,19 @@
private final Matrix4f f_121078_;
public static final Codec<Transformation> f_268453_ = RecordCodecBuilder.create((p_269604_) -> {
return p_269604_.group(ExtraCodecs.f_252432_.fieldOf("translation").forGetter((p_269599_) -> {
@@ -163,6 +_,19 @@
public int hashCode() {
return Objects.hash(this.matrix);
return Objects.hash(this.f_121078_);
}
+
+ private Matrix3f normalTransform = null;
@@ -21,11 +21,11 @@
+ }
+ private void checkNormalTransform() {
+ if (normalTransform == null) {
+ normalTransform = new Matrix3f(matrix);
+ normalTransform = new Matrix3f(f_121078_);
+ normalTransform.invert();
+ normalTransform.transpose();
+ }
+ }
public Transformation slerp(Transformation p_175938_, float p_175939_) {
Vector3f vector3f = this.getTranslation();
public Transformation m_175937_(Transformation p_175938_, float p_175939_) {
Vector3f vector3f = this.m_252829_();
@@ -1,18 +1,18 @@
--- a/com/mojang/realmsclient/gui/screens/RealmsGenericErrorScreen.java
+++ b/com/mojang/realmsclient/gui/screens/RealmsGenericErrorScreen.java
@@ -61,6 +_,15 @@
return Component.empty().append(this.lines.title).append(": ").append(this.lines.detail);
@@ -68,6 +_,15 @@
return Component.m_237119_().m_7220_(this.f_200947_.f_287789_).m_130946_(": ").m_7220_(this.f_200947_.f_287787_);
}
+ @Override
+ public boolean keyPressed(int key, int scanCode, int modifiers) {
+ public boolean m_7933_(int key, int scanCode, int modifiers) {
+ if (key == org.lwjgl.glfw.GLFW.GLFW_KEY_ESCAPE) {
+ minecraft.setScreen(this.nextScreen);
+ f_96541_.m_91152_(this.f_88665_);
+ return true;
+ }
+ return super.keyPressed(key, scanCode, modifiers);
+ return super.m_7933_(key, scanCode, modifiers);
+ }
+
public void render(GuiGraphics p_283497_, int p_88680_, int p_88681_, float p_88682_) {
super.render(p_283497_, p_88680_, p_88681_, p_88682_);
p_283497_.drawCenteredString(this.font, this.lines.title, this.width / 2, 80, -1);
public void m_88315_(GuiGraphics p_283497_, int p_88680_, int p_88681_, float p_88682_) {
this.m_280273_(p_283497_);
p_283497_.m_280653_(this.f_96547_, this.f_200947_.f_287789_, this.f_96543_ / 2, 80, 16777215);
@@ -0,0 +1,15 @@
--- a/com/mojang/realmsclient/gui/screens/RealmsNotificationsScreen.java
+++ b/com/mojang/realmsclient/gui/screens/RealmsNotificationsScreen.java
@@ -45,9 +_,9 @@
}
};
private final RealmsNotificationsScreen.DataFetcherConfiguration f_273849_ = new RealmsNotificationsScreen.DataFetcherConfiguration() {
- public DataFetcher.Subscription m_274316_(RealmsDataFetcher p_275731_) {
- DataFetcher.Subscription datafetcher$subscription = p_275731_.f_238549_.m_239139_();
- RealmsNotificationsScreen.this.m_274585_(p_275731_, datafetcher$subscription);
+ public DataFetcher.Subscription m_274316_(RealmsDataFetcher p_275318_) {
+ DataFetcher.Subscription datafetcher$subscription = p_275318_.f_238549_.m_239139_();
+ RealmsNotificationsScreen.this.m_274585_(p_275318_, datafetcher$subscription);
return datafetcher$subscription;
}
@@ -1,32 +1,33 @@
--- a/net/minecraft/CrashReport.java
+++ b/net/minecraft/CrashReport.java
@@ -57,14 +_,8 @@
if (this.uncategorizedStackTrace != null && this.uncategorizedStackTrace.length > 0) {
@@ -57,14 +_,9 @@
if (this.f_127506_ != null && this.f_127506_.length > 0) {
p_127520_.append("-- Head --\n");
p_127520_.append("Thread: ").append(Thread.currentThread().getName()).append("\n");
- p_127520_.append("Stacktrace:\n");
-
- for(StackTraceElement stacktraceelement : this.uncategorizedStackTrace) {
- for(StackTraceElement stacktraceelement : this.f_127506_) {
- p_127520_.append("\t").append("at ").append((Object)stacktraceelement);
- p_127520_.append("\n");
- }
-
- p_127520_.append("\n");
+ p_127520_.append(net.minecraftforge.logging.CrashReportAnalyser.appendSuspectedMods(this.f_127501_, this.f_127506_));
+ p_127520_.append("Stacktrace:");
+ p_127520_.append(net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(this.uncategorizedStackTrace));
+ p_127520_.append(net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(this.f_127506_));
}
for(CrashReportCategory crashreportcategory : this.details) {
for(CrashReportCategory crashreportcategory : this.f_127503_) {
@@ -72,6 +_,7 @@
p_127520_.append("\n\n");
}
+ net.minecraftforge.logging.CrashReportExtender.extendSystemReport(systemReport);
this.systemReport.appendToCrashReportString(p_127520_);
+ net.minecraftforge.logging.CrashReportExtender.extendSystemReport(f_178624_);
this.f_178624_.m_143525_(p_127520_);
}
@@ -91,23 +_,13 @@
throwable.setStackTrace(this.exception.getStackTrace());
throwable.setStackTrace(this.f_127501_.getStackTrace());
}
- String s;
@@ -44,10 +45,10 @@
+ return net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(throwable);
}
public String getFriendlyReport() {
public String m_127526_() {
StringBuilder stringbuilder = new StringBuilder();
stringbuilder.append("---- Minecraft Crash Report ----\n");
+ net.minecraftforge.logging.CrashReportExtender.addCrashReportHeader(stringbuilder, this);
stringbuilder.append("// ");
stringbuilder.append(getErrorComment());
stringbuilder.append(m_127531_());
stringbuilder.append("\n\n");
@@ -4,35 +4,35 @@
if (astacktraceelement.length <= 0) {
return 0;
} else {
- this.stackTrace = new StackTraceElement[astacktraceelement.length - 3 - p_128149_];
- System.arraycopy(astacktraceelement, 3 + p_128149_, this.stackTrace, 0, this.stackTrace.length);
- this.f_128139_ = new StackTraceElement[astacktraceelement.length - 3 - p_128149_];
- System.arraycopy(astacktraceelement, 3 + p_128149_, this.f_128139_, 0, this.f_128139_.length);
+ int len = astacktraceelement.length - 3 - p_128149_;
+ if (len <= 0) len = astacktraceelement.length;
+ this.stackTrace = new StackTraceElement[len];
+ System.arraycopy(astacktraceelement, astacktraceelement.length - len, this.stackTrace, 0, this.stackTrace.length);
return this.stackTrace.length;
+ this.f_128139_ = new StackTraceElement[len];
+ System.arraycopy(astacktraceelement, astacktraceelement.length - len, this.f_128139_, 0, this.f_128139_.length);
return this.f_128139_.length;
}
}
@@ -147,17 +_,17 @@
if (this.stackTrace != null && this.stackTrace.length > 0) {
if (this.f_128139_ != null && this.f_128139_.length > 0) {
p_128169_.append("\nStacktrace:");
-
- for(StackTraceElement stacktraceelement : this.stackTrace) {
- for(StackTraceElement stacktraceelement : this.f_128139_) {
- p_128169_.append("\n\tat ");
- p_128169_.append((Object)stacktraceelement);
- }
+ p_128169_.append(net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(this.stackTrace));
+ p_128169_.append(net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(this.f_128139_));
}
}
public StackTraceElement[] getStacktrace() {
return this.stackTrace;
public StackTraceElement[] m_128143_() {
return this.f_128139_;
+ }
+
+ public void applyStackTrace(Throwable t) {
+ this.stackTrace = t.getStackTrace();
+ this.f_128139_ = t.getStackTrace();
}
public static void populateBlockDetails(CrashReportCategory p_178951_, LevelHeightAccessor p_178952_, BlockPos p_178953_, @Nullable BlockState p_178954_) {
public static void m_178950_(CrashReportCategory p_178951_, LevelHeightAccessor p_178952_, BlockPos p_178953_, @Nullable BlockState p_178954_) {
@@ -1,10 +1,10 @@
--- a/net/minecraft/SharedConstants.java
+++ b/net/minecraft/SharedConstants.java
@@ -212,6 +_,7 @@
@@ -210,6 +_,7 @@
}
static {
+ if (System.getProperty("io.netty.leakDetection.level") == null) // Forge: allow level to be manually specified
ResourceLeakDetector.setLevel(NETTY_LEAK_DETECTION);
ResourceLeakDetector.setLevel(f_136180_);
CommandSyntaxException.ENABLE_COMMAND_STACK_TRACES = false;
CommandSyntaxException.BUILT_IN_EXCEPTIONS = new BrigadierExceptions();
@@ -1,6 +1,6 @@
--- a/net/minecraft/Util.java
+++ b/net/minecraft/Util.java
@@ -172,6 +_,11 @@
@@ -171,6 +_,11 @@
return 255;
}
@@ -9,15 +9,15 @@
+ @SuppressWarnings("unused") private static java.util.function.LongSupplier INNER_CLASS_SHIFT1 = new java.util.function.LongSupplier() { public long getAsLong() { return 0; } };
+ @SuppressWarnings("unused") private static java.util.function.LongSupplier INNER_CLASS_SHIFT2 = new java.util.function.LongSupplier() { public long getAsLong() { return 0; } };
+
public static ExecutorService backgroundExecutor() {
return BACKGROUND_EXECUTOR;
public static ExecutorService m_183991_() {
return f_137444_;
}
@@ -240,7 +_,7 @@
@@ -239,7 +_,7 @@
try {
type = DataFixers.getDataFixer().getSchema(DataFixUtils.makeKey(SharedConstants.getCurrentVersion().getDataVersion().getVersion())).getChoiceType(p_137552_, p_137553_);
type = DataFixers.m_14512_().getSchema(DataFixUtils.makeKey(SharedConstants.m_183709_().m_183476_().m_193006_())).getChoiceType(p_137552_, p_137553_);
} catch (IllegalArgumentException illegalargumentexception) {
- LOGGER.error("No data fixer registered for {}", (Object)p_137553_);
+ LOGGER.debug("No data fixer registered for {}", (Object)p_137553_);
if (SharedConstants.IS_RUNNING_IN_IDE) {
- f_137446_.error("No data fixer registered for {}", (Object)p_137553_);
+ f_137446_.debug("No data fixer registered for {}", (Object)p_137553_);
if (SharedConstants.f_136183_) {
throw illegalargumentexception;
}
@@ -1,30 +1,26 @@
--- a/net/minecraft/advancements/Advancement.java
+++ b/net/minecraft/advancements/Advancement.java
@@ -62,7 +_,14 @@
return jsonobject;
@@ -150,7 +_,7 @@
return this.f_138305_;
}
+ /** @deprecated Forge: use {@linkplain #fromJson(JsonObject, DeserializationContext, net.minecraftforge.common.crafting.conditions.ICondition.IContext) overload with context}. */
+ @Deprecated
public static Advancement fromJson(JsonObject p_300691_, DeserializationContext p_300053_) {
+ return fromJson(p_300691_, p_300053_, net.minecraftforge.common.crafting.conditions.ICondition.IContext.EMPTY);
+ }
+
+ public static Advancement fromJson(JsonObject p_300691_, DeserializationContext p_300053_, net.minecraftforge.common.crafting.conditions.ICondition.IContext context) {
+ //if ((p_300691_ = net.minecraftforge.common.crafting.ConditionalAdvancement.processConditional(p_300691_, context)) == null) return null;
Optional<ResourceLocation> optional = p_300691_.has("parent") ? Optional.of(new ResourceLocation(GsonHelper.getAsString(p_300691_, "parent"))) : Optional.empty();
Optional<DisplayInfo> optional1 = p_300691_.has("display") ? Optional.of(DisplayInfo.fromJson(GsonHelper.getAsJsonObject(p_300691_, "display"))) : Optional.empty();
AdvancementRewards advancementrewards = p_300691_.has("rewards") ? AdvancementRewards.deserialize(GsonHelper.getAsJsonObject(p_300691_, "rewards")) : AdvancementRewards.EMPTY;
@@ -182,7 +_,11 @@
- public static class Builder {
+ public static class Builder implements net.minecraftforge.common.extensions.IForgeAdvancementBuilder {
@Nullable
private ResourceLocation f_138332_;
@Nullable
@@ -340,7 +_,14 @@
return "Task Advancement{parentId=" + this.f_138332_ + ", display=" + this.f_138334_ + ", rewards=" + this.f_138335_ + ", criteria=" + this.f_138336_ + ", requirements=" + Arrays.deepToString(this.f_138337_) + ", sends_telemetry_event=" + this.f_285655_ + "}";
}
public AdvancementHolder save(Consumer<AdvancementHolder> p_138390_, String p_138391_) {
- AdvancementHolder advancementholder = this.build(new ResourceLocation(p_138391_));
+ return save(p_138390_, new ResourceLocation(p_138391_));
+ /** @deprecated Forge: use {@linkplain #fromJson(JsonObject, DeserializationContext, net.minecraftforge.common.crafting.conditions.ICondition.IContext) overload with context}. */
+ @Deprecated
public static Advancement.Builder m_138380_(JsonObject p_138381_, DeserializationContext p_138382_) {
+ return fromJson(p_138381_, p_138382_, net.minecraftforge.common.crafting.conditions.ICondition.IContext.EMPTY);
+ }
+
+ public AdvancementHolder save(Consumer<AdvancementHolder> p_138390_, ResourceLocation id) {
+ AdvancementHolder advancementholder = this.build(id);
p_138390_.accept(advancementholder);
return advancementholder;
}
+ public static Advancement.Builder fromJson(JsonObject p_138381_, DeserializationContext p_138382_, net.minecraftforge.common.crafting.conditions.ICondition.IContext context) {
+ if ((p_138381_ = net.minecraftforge.common.crafting.ConditionalAdvancement.processConditional(p_138381_, context)) == null) return null;
ResourceLocation resourcelocation = p_138381_.has("parent") ? new ResourceLocation(GsonHelper.m_13906_(p_138381_, "parent")) : null;
DisplayInfo displayinfo = p_138381_.has("display") ? DisplayInfo.m_14981_(GsonHelper.m_13930_(p_138381_, "display")) : null;
AdvancementRewards advancementrewards = p_138381_.has("rewards") ? AdvancementRewards.m_9991_(GsonHelper.m_13930_(p_138381_, "rewards")) : AdvancementRewards.f_9978_;
@@ -2,10 +2,10 @@
+++ b/net/minecraft/advancements/AdvancementRewards.java
@@ -43,7 +_,7 @@
public void grant(ServerPlayer p_9990_) {
p_9990_.giveExperiencePoints(this.experience);
- LootParams lootparams = (new LootParams.Builder(p_9990_.serverLevel())).withParameter(LootContextParams.THIS_ENTITY, p_9990_).withParameter(LootContextParams.ORIGIN, p_9990_.position()).create(LootContextParamSets.ADVANCEMENT_REWARD);
+ LootParams lootparams = (new LootParams.Builder(p_9990_.serverLevel())).withParameter(LootContextParams.THIS_ENTITY, p_9990_).withParameter(LootContextParams.ORIGIN, p_9990_.position()).withLuck(p_9990_.getLuck()).create(LootContextParamSets.ADVANCEMENT_REWARD); // Forge: Add luck to LootContext
public void m_9989_(ServerPlayer p_9990_) {
p_9990_.m_6756_(this.f_9979_);
- LootParams lootparams = (new LootParams.Builder(p_9990_.m_284548_())).m_287286_(LootContextParams.f_81455_, p_9990_).m_287286_(LootContextParams.f_81460_, p_9990_.m_20182_()).m_287235_(LootContextParamSets.f_81418_);
+ LootParams lootparams = (new LootParams.Builder(p_9990_.m_284548_())).m_287286_(LootContextParams.f_81455_, p_9990_).m_287286_(LootContextParams.f_81460_, p_9990_.m_20182_()).m_287239_(p_9990_.m_36336_()).m_287235_(LootContextParamSets.f_81418_); // Forge: Add luck to LootContext
boolean flag = false;
for(ResourceLocation resourcelocation : this.loot) {
for(ResourceLocation resourcelocation : this.f_9980_) {
@@ -1,11 +1,47 @@
--- a/net/minecraft/advancements/critereon/ItemPredicate.java
+++ b/net/minecraft/advancements/critereon/ItemPredicate.java
@@ -50,7 +_,7 @@
@@ -29,6 +_,8 @@
import net.minecraft.world.level.ItemLike;
public class ItemPredicate {
+ private static final Map<ResourceLocation, java.util.function.Function<JsonObject, ItemPredicate>> custom_predicates = new java.util.HashMap<>();
+ private static final Map<ResourceLocation, java.util.function.Function<JsonObject, ItemPredicate>> unmod_predicates = java.util.Collections.unmodifiableMap(custom_predicates);
public static final ItemPredicate f_45028_ = new ItemPredicate();
@Nullable
private final TagKey<Item> f_45029_;
@@ -81,7 +_,7 @@
return false;
} else {
if (!this.enchantments.isEmpty()) {
- Map<Enchantment, Integer> map = EnchantmentHelper.deserializeEnchantments(p_45050_.getEnchantmentTags());
if (this.f_45033_.length > 0) {
- Map<Enchantment, Integer> map = EnchantmentHelper.m_44882_(p_45050_.m_41785_());
+ Map<Enchantment, Integer> map = p_45050_.getAllEnchantments();
for(EnchantmentPredicate enchantmentpredicate : this.enchantments) {
if (!enchantmentpredicate.containedIn(map)) {
for(EnchantmentPredicate enchantmentpredicate : this.f_45033_) {
if (!enchantmentpredicate.m_30476_(map)) {
@@ -108,6 +_,11 @@
public static ItemPredicate m_45051_(@Nullable JsonElement p_45052_) {
if (p_45052_ != null && !p_45052_.isJsonNull()) {
JsonObject jsonobject = GsonHelper.m_13918_(p_45052_, "item");
+ if (jsonobject.has("type")) {
+ final ResourceLocation rl = new ResourceLocation(GsonHelper.m_13906_(jsonobject, "type"));
+ if (custom_predicates.containsKey(rl)) return custom_predicates.get(rl).apply(jsonobject);
+ else throw new JsonSyntaxException("There is no ItemPredicate of type "+rl);
+ }
MinMaxBounds.Ints minmaxbounds$ints = MinMaxBounds.Ints.m_55373_(jsonobject.get("count"));
MinMaxBounds.Ints minmaxbounds$ints1 = MinMaxBounds.Ints.m_55373_(jsonobject.get("durability"));
if (jsonobject.has("data")) {
@@ -215,6 +_,14 @@
} else {
return new ItemPredicate[0];
}
+ }
+
+ public static void register(ResourceLocation name, java.util.function.Function<JsonObject, ItemPredicate> deserializer) {
+ custom_predicates.put(name, deserializer);
+ }
+
+ public static Map<ResourceLocation, java.util.function.Function<JsonObject, ItemPredicate>> getPredicates() {
+ return unmod_predicates;
}
public static class Builder {
@@ -1,21 +1,21 @@
--- a/net/minecraft/client/Camera.java
+++ b/net/minecraft/client/Camera.java
@@ -206,6 +_,18 @@
this.initialized = false;
this.f_90549_ = false;
}
+ public void setAnglesInternal(float yaw, float pitch) {
+ this.yRot = yaw;
+ this.xRot = pitch;
+ this.f_90558_ = yaw;
+ this.f_90557_ = pitch;
+ }
+
+ public net.minecraft.world.level.block.state.BlockState getBlockAtCamera() {
+ if (!this.initialized)
+ return net.minecraft.world.level.block.Blocks.AIR.defaultBlockState();
+ if (!this.f_90549_)
+ return net.minecraft.world.level.block.Blocks.f_50016_.m_49966_();
+ else
+ return this.level.getBlockState(this.blockPosition).getStateAtViewpoint(this.level, this.blockPosition, this.position);
+ return this.f_90550_.m_8055_(this.f_90553_).getStateAtViewpoint(this.f_90550_, this.f_90553_, this.f_90552_);
+ }
+
@OnlyIn(Dist.CLIENT)
public static class NearPlane {
final Vec3 forward;
final Vec3 f_167687_;
@@ -1,20 +1,20 @@
--- a/net/minecraft/client/ClientRecipeBook.java
+++ b/net/minecraft/client/ClientRecipeBook.java
@@ -56,7 +_,7 @@
Recipe<?> recipe = recipeholder.value();
if (!recipe.isSpecial() && !recipe.isIncomplete()) {
RecipeBookCategories recipebookcategories = getCategory(recipeholder);
- String s = recipe.getGroup();
+ String s = recipe.getGroup().isEmpty() ? recipeholder.id().toString() : recipe.getGroup(); // FORGE: Group value defaults to the recipe's ID if the recipe's explicit group is empty.
@@ -54,7 +_,7 @@
for(Recipe<?> recipe : p_90643_) {
if (!recipe.m_5598_() && !recipe.m_142505_()) {
RecipeBookCategories recipebookcategories = m_90646_(recipe);
- String s = recipe.m_6076_();
+ String s = recipe.m_6076_().isEmpty() ? recipe.m_6423_().toString() : recipe.m_6076_(); // FORGE: Group value defaults to the recipe's ID if the recipe's explicit group is empty.
if (s.isEmpty()) {
map.computeIfAbsent(recipebookcategories, (p_90645_) -> {
return Lists.newArrayList();
@@ -144,6 +_,8 @@
} else if (recipetype == RecipeType.SMITHING) {
@@ -141,6 +_,8 @@
} else if (recipetype == RecipeType.f_44113_) {
return RecipeBookCategories.SMITHING;
} else {
+ RecipeBookCategories categories = net.minecraftforge.client.RecipeBookManager.findCategories((RecipeType) recipetype, p_299041_);
+ RecipeBookCategories categories = net.minecraftforge.client.RecipeBookManager.findCategories((RecipeType) recipetype, p_90647_);
+ if (categories != null) return categories;
LOGGER.warn("Unknown recipe category: {}/{}", LogUtils.defer(() -> {
return BuiltInRegistries.RECIPE_TYPE.getKey(recipe.getType());
}), LogUtils.defer(p_299041_::id));
f_90618_.warn("Unknown recipe category: {}/{}", LogUtils.defer(() -> {
return BuiltInRegistries.f_256990_.m_7981_(p_90647_.m_6671_());
}), LogUtils.defer(p_90647_::m_6423_));
@@ -6,58 +6,58 @@
@OnlyIn(Dist.CLIENT)
-public class KeyMapping implements Comparable<KeyMapping> {
+public class KeyMapping implements Comparable<KeyMapping>, net.minecraftforge.client.extensions.IForgeKeyMapping {
private static final Map<String, KeyMapping> ALL = Maps.newHashMap();
- private static final Map<InputConstants.Key, KeyMapping> MAP = Maps.newHashMap();
+ private static final net.minecraftforge.client.settings.KeyMappingLookup MAP = new net.minecraftforge.client.settings.KeyMappingLookup();
private static final Set<String> CATEGORIES = Sets.newHashSet();
public static final String CATEGORY_MOVEMENT = "key.categories.movement";
public static final String CATEGORY_MISC = "key.categories.misc";
private static final Map<String, KeyMapping> f_90809_ = Maps.newHashMap();
- private static final Map<InputConstants.Key, KeyMapping> f_90810_ = Maps.newHashMap();
+ private static final net.minecraftforge.client.settings.KeyMappingLookup f_90810_ = new net.minecraftforge.client.settings.KeyMappingLookup();
private static final Set<String> f_90811_ = Sets.newHashSet();
public static final String f_167805_ = "key.categories.movement";
public static final String f_167806_ = "key.categories.misc";
@@ -41,7 +_,7 @@
private int clickCount;
private int f_90818_;
public static void click(InputConstants.Key p_90836_) {
- KeyMapping keymapping = MAP.get(p_90836_);
+ for (KeyMapping keymapping : MAP.getAll(p_90836_))
public static void m_90835_(InputConstants.Key p_90836_) {
- KeyMapping keymapping = f_90810_.get(p_90836_);
+ for (KeyMapping keymapping : f_90810_.getAll(p_90836_))
if (keymapping != null) {
++keymapping.clickCount;
++keymapping.f_90818_;
}
@@ -49,7 +_,7 @@
}
public static void set(InputConstants.Key p_90838_, boolean p_90839_) {
- KeyMapping keymapping = MAP.get(p_90838_);
+ for (KeyMapping keymapping : MAP.getAll(p_90838_))
public static void m_90837_(InputConstants.Key p_90838_, boolean p_90839_) {
- KeyMapping keymapping = f_90810_.get(p_90838_);
+ for (KeyMapping keymapping : f_90810_.getAll(p_90838_))
if (keymapping != null) {
keymapping.setDown(p_90839_);
keymapping.m_7249_(p_90839_);
}
@@ -105,7 +_,7 @@
}
public boolean isDown() {
- return this.isDown;
+ return this.isDown && isConflictContextAndModifierActive();
public boolean m_90857_() {
- return this.f_90817_;
+ return this.f_90817_ && isConflictContextAndModifierActive();
}
public String getCategory() {
public String m_90858_() {
@@ -139,7 +_,13 @@
}
public int compareTo(KeyMapping p_90841_) {
- return this.category.equals(p_90841_.category) ? I18n.get(this.name).compareTo(I18n.get(p_90841_.name)) : CATEGORY_SORT_ORDER.get(this.category).compareTo(CATEGORY_SORT_ORDER.get(p_90841_.category));
+ if (this.category.equals(p_90841_.category)) return I18n.get(this.name).compareTo(I18n.get(p_90841_.name));
+ Integer tCat = CATEGORY_SORT_ORDER.get(this.category);
+ Integer oCat = CATEGORY_SORT_ORDER.get(p_90841_.category);
- return this.f_90815_.equals(p_90841_.f_90815_) ? I18n.m_118938_(this.f_90813_).compareTo(I18n.m_118938_(p_90841_.f_90813_)) : f_90812_.get(this.f_90815_).compareTo(f_90812_.get(p_90841_.f_90815_));
+ if (this.f_90815_.equals(p_90841_.f_90815_)) return I18n.m_118938_(this.f_90813_).compareTo(I18n.m_118938_(p_90841_.f_90813_));
+ Integer tCat = f_90812_.get(this.f_90815_);
+ Integer oCat = f_90812_.get(p_90841_.f_90815_);
+ if (tCat == null && oCat != null) return 1;
+ if (tCat != null && oCat == null) return -1;
+ if (tCat == null && oCat == null) return I18n.get(this.category).compareTo(I18n.get(p_90841_.category));
+ if (tCat == null && oCat == null) return I18n.m_118938_(this.f_90815_).compareTo(I18n.m_118938_(p_90841_.f_90815_));
+ return tCat.compareTo(oCat);
}
public static Supplier<Component> createNameSupplier(String p_90843_) {
public static Supplier<Component> m_90842_(String p_90843_) {
@@ -150,6 +_,20 @@
}
public boolean same(KeyMapping p_90851_) {
public boolean m_90850_(KeyMapping p_90851_) {
+ if (getKeyConflictContext().conflicts(p_90851_.getKeyConflictContext()) || p_90851_.getKeyConflictContext().conflicts(getKeyConflictContext())) {
+ net.minecraftforge.client.settings.KeyModifier keyModifier = getKeyModifier();
+ net.minecraftforge.client.settings.KeyModifier otherKeyModifier = p_90851_.getKeyModifier();
@@ -72,30 +72,30 @@
+ (keyModifier == net.minecraftforge.client.settings.KeyModifier.NONE || otherKeyModifier == net.minecraftforge.client.settings.KeyModifier.NONE));
+ }
+ }
return this.key.equals(p_90851_.key);
return this.f_90816_.equals(p_90851_.f_90816_);
}
@@ -170,11 +_,13 @@
}
public Component getTranslatedKeyMessage() {
+ return getKeyModifier().getCombinedName(key, () -> {
return this.key.getDisplayName();
public Component m_90863_() {
+ return getKeyModifier().getCombinedName(f_90816_, () -> {
return this.f_90816_.m_84875_();
+ });
}
public boolean isDefault() {
- return this.key.equals(this.defaultKey);
+ return this.key.equals(this.defaultKey) && getKeyModifier() == getDefaultKeyModifier();
public boolean m_90864_() {
- return this.f_90816_.equals(this.f_90814_);
+ return this.f_90816_.equals(this.f_90814_) && getKeyModifier() == getDefaultKeyModifier();
}
public String saveString() {
@@ -184,4 +_,84 @@
public void setDown(boolean p_90846_) {
this.isDown = p_90846_;
}
public String m_90865_() {
@@ -183,5 +_,88 @@
public void m_7249_(boolean p_90846_) {
this.f_90817_ = p_90846_;
+ }
+
+ /****************** Forge Start *****************************/
+ private net.minecraftforge.client.settings.KeyModifier keyModifierDefault = net.minecraftforge.client.settings.KeyModifier.NONE;
+ private net.minecraftforge.client.settings.KeyModifier keyModifier = net.minecraftforge.client.settings.KeyModifier.NONE;
+ private net.minecraftforge.client.settings.IKeyConflictContext keyConflictContext = net.minecraftforge.client.settings.KeyConflictContext.UNIVERSAL;
@@ -104,7 +104,7 @@
+ * Convenience constructor for creating KeyBindings with keyConflictContext set.
+ */
+ public KeyMapping(String description, net.minecraftforge.client.settings.IKeyConflictContext keyConflictContext, final InputConstants.Type inputType, final int keyCode, String category) {
+ this(description, keyConflictContext, inputType.getOrCreate(keyCode), category);
+ this(description, keyConflictContext, inputType.m_84895_(keyCode), category);
+ }
+
+ /**
@@ -118,30 +118,30 @@
+ * Convenience constructor for creating KeyBindings with keyConflictContext and keyModifier set.
+ */
+ public KeyMapping(String description, net.minecraftforge.client.settings.IKeyConflictContext keyConflictContext, net.minecraftforge.client.settings.KeyModifier keyModifier, final InputConstants.Type inputType, final int keyCode, String category) {
+ this(description, keyConflictContext, keyModifier, inputType.getOrCreate(keyCode), category);
+ this(description, keyConflictContext, keyModifier, inputType.m_84895_(keyCode), category);
+ }
+
+ /**
+ * Convenience constructor for creating KeyBindings with keyConflictContext and keyModifier set.
+ */
+ public KeyMapping(String description, net.minecraftforge.client.settings.IKeyConflictContext keyConflictContext, net.minecraftforge.client.settings.KeyModifier keyModifier, InputConstants.Key keyCode, String category) {
+ this.name = description;
+ this.key = keyCode;
+ this.defaultKey = keyCode;
+ this.category = category;
+ this.f_90813_ = description;
+ this.f_90816_ = keyCode;
+ this.f_90814_ = keyCode;
+ this.f_90815_ = category;
+ this.keyConflictContext = keyConflictContext;
+ this.keyModifier = keyModifier;
+ this.keyModifierDefault = keyModifier;
+ if (this.keyModifier.matches(keyCode))
+ this.keyModifier = net.minecraftforge.client.settings.KeyModifier.NONE;
+ ALL.put(description, this);
+ MAP.put(keyCode, this);
+ CATEGORIES.add(category);
+ f_90809_.put(description, this);
+ f_90810_.put(keyCode, this);
+ f_90811_.add(category);
+ }
+
+ @Override
+ public InputConstants.Key getKey() {
+ return this.key;
+ return this.f_90816_;
+ }
+
+ @Override
@@ -165,13 +165,17 @@
+ }
+
+ @Override
+ public void setKeyModifierAndCode(net.minecraftforge.client.settings.KeyModifier keyModifier, InputConstants.Key keyCode) {
+ this.key = keyCode;
+ if (keyModifier.matches(keyCode))
+ public void setKeyModifierAndCode(@org.jetbrains.annotations.Nullable net.minecraftforge.client.settings.KeyModifier keyModifier, InputConstants.Key keyCode) {
+ f_90810_.remove(this);
+
+ if (keyModifier == null)
+ keyModifier = net.minecraftforge.client.settings.KeyModifier.getModifier(this.f_90816_);
+ if (keyModifier == null || keyCode == InputConstants.f_84822_ || net.minecraftforge.client.settings.KeyModifier.isKeyCodeModifier(keyCode))
+ keyModifier = net.minecraftforge.client.settings.KeyModifier.NONE;
+ MAP.remove(this);
+
+ this.f_90816_ = keyCode;
+ this.keyModifier = keyModifier;
+ MAP.put(keyCode, this);
+ }
+ /****************** Forge End *****************************/
+
+ f_90810_.put(keyCode, this);
}
}
Loaded 100 of 1619 files, more files were not shown because too many files have changed in this diff. Show more