Compare commits

..
142 Commits
Author SHA1 Message Date
LexManos 658e614dc1 1.19.4 Recommended Build 2023-06-07 12:08:15 -07:00
Apex 9abfcbc90b Add method to GatherDataEvent to obtain collection of all input paths. (#9499) 2023-05-29 13:47:21 -07:00
Dennis C 6ee20899bf Log error when Sheets is class-loaded before registration is completed (#9475) 2023-05-29 13:47:04 -07:00
Dennis C 42115d37d6 [1.19.x] Re-implement RenderTooltipEvent.Color (#9497)
* Reimplement RenderTooltipEvent.Color

* Formatting, comments, EXC

* Deprecate instead of replacing
2023-05-16 12:24:27 +02:00
Joseph B 8abe9b6b0c Add API for registering custom world preset editors (#9436) 2023-05-14 15:53:34 -05:00
embeddedt fc91dfd1d1 Remove unneeded extra reload of datapacks on world creation screen (#9454) 2023-05-14 15:52:57 -05:00
SizableShrimp 8cd6f2d237 Bump ASM to 9.5 2023-05-14 15:50:07 -05:00
SizableShrimp f1702495ef Fix crash when running server from root directory
Fixes #9498
2023-05-14 15:26:24 -05:00
Dennis C 2ab2ced589 Fix root transform matrix format, allow using all four root transform formats (#9496) 2023-05-12 11:20:01 -07:00
Apex c8f9e59fef Add missing AT lines to allow registering custom game rule boolean/integer types (#9489) 2023-05-10 09:27:10 -07:00
Brennan WardandBrennan Ward 9a25527a54 [1.19.x] Fix SaplingGrowTreeEvent#setFeature being ignored in FungusBlock (#9485)
Co-authored-by: Brennan Ward <[email protected]>
2023-05-05 23:09:13 -05:00
SizableShrimp ad6d11671e Restore AccessibilityOnboardingScreen
Fixes #9488
2023-05-05 23:07:49 -05:00
Brennan Ward 3c93cd414d Update documentation on FinalizeSpawn (#9467) 2023-05-05 10:26:41 -07:00
DBotThePony 2c243626f6 Fix fluids without sound event causing exception in tryFillContainer and tryEmptyContainer (#9445) 2023-05-05 10:26:28 -07:00
Technici4n ae05036bb3 Make FakePlayerFactory respect the given ServerLevel (#9479) 2023-05-05 10:03:55 -07:00
Dennis C 3de22ffba3 Collect and log exceptions occurring in DeferredWorkQueue tasks (#9449) 2023-05-04 16:43:34 -07:00
matyrobbrt e1bf07dece Fix NamespacedWrapper#wrapAsHolder (#9450) 2023-05-04 16:42:02 -07:00
RealMangoRage 0ae63b0b07 Fixes ChatScreen calling .setScreen (#9443)
Fix test compile failures also.
2023-04-30 22:57:52 +01:00
matyrobbrt d4f211b815 Determine the Forge version the PR was built against when running PR compat checks (#9374) 2023-04-24 10:59:56 -07:00
SizableShrimp 393b28d7bb Add buildscript test to error on deprecated members that should of been removed. (#9460) 2023-04-24 10:59:37 -07:00
sciwhiz12 47c807c0e3 Remove erroneous brace patch in Inventory (#9462)
Fixes #9459
2023-04-23 13:24:20 +01:00
Dennis C b398f52a7f [1.19.4] Move root transform builder to ModelBuilder to allow use in ItemModelBuilder (#9456) 2023-04-20 09:27:30 +02:00
SizableShrimp f2aeeeb1fe Fix forge grindstone hooks allowing stacks of non-stackable items (#9457) 2023-04-20 00:19:35 -07:00
matyrobbrt dcef406df8 [1.19.4] Fix FMLOnly (#9415) 2023-04-18 16:46:04 +01:00
Dennis C 01846c729a Fix ItemLayerModel erroneously adding particle texture to layer texture list (#9441) 2023-04-07 08:16:17 +02:00
LexManos e6aaaff3f3 Temporary fix for Canceling ProjectileImpactEvents of Piercing ammo.
Event needs to be re-worked to have finer control. #9370
2023-04-05 11:16:39 -07:00
Brennan Ward d4836bc769 Fix dummy air blocks not being marked as air (#9440) 2023-04-03 15:04:34 -07:00
matyrobbrt b5655b0ddc Add support for splitting the login packet (#9367)
It contains full copies of data registries and can easily surpass vanilla's limits
2023-03-30 15:59:01 -07:00
Brennan Ward d75d333e13 Remove Attack Range and Reach Distance and add Block Reach and Entity Reach (#9361) 2023-03-29 18:53:41 -07:00
ChampionAsh5357 f5e16aa353 Add default bucket sounds for milk (#9432) 2023-03-29 18:48:29 -07:00
LexManos 4e5eecac12 Deprecate Item.onUsingTick, as vanilla provides the same function in Item.onUseTick now. Closes #9342 2023-03-29 18:46:00 -07:00
Brennan Ward a111073181 Fix ScreenEvent.Init.[Pre/Post] not working correctly (#9431) 2023-03-29 18:18:38 -07:00
LexManos a05f555ab8 Allow FenceGateBlock to be used without a WoodType. Closes #9392 2023-03-29 17:59:51 -07:00
LexManos df3db236e5 Deprecate duplicate tool tags that vanilla added in 1.19.4
We will maintain a seperate 'tools' tag until Mojang adds all relevent tool tags.
Closes #9393
2023-03-29 17:59:50 -07:00
LexManos 71b115c8fe Fix BlockEvent.Break not using ItemStack enchantment hooks. 2023-03-29 17:59:49 -07:00
LexManos 3e1b01af2c Move Block.onCatchFire to above block removal to allow usage of BlockEntity data. Closes #9400 2023-03-29 17:59:48 -07:00
Brennan Ward 1ab0685d04 Fix FinalizeSpawn not blocking spawns during worldgen (#9420) 2023-03-29 16:10:59 -07:00
LexManos 5c44992d1c Fixed issue with MutableHashedLinkedMap when removing multiple sequential entries in the middle of the map.
Added Unit tests for MutableHashLinkedMap
Added support for removing using the iterator
Added concurrent modification detection to the iterator
Added default constructor with basic hashing strategy.

Closes #9426
2023-03-29 15:49:49 -07:00
TelepathicGrunt d448ffd539 Loosen access for BucketItem's canBlockContainFluid (#9421) 2023-03-28 13:34:10 -07:00
ChampionAsh5357 9f895a79f2 Update and Regenerate Datapacks (#9419)
Add generation for pack.mcmeta
2023-03-28 13:33:59 -07:00
Paint_Ninja 6e1de81ff5 Restore ability to change message in ClientChatEvent (#9377) 2023-03-27 14:35:05 -07:00
sciwhiz12 b493a63fb6 Remove duplicate line in FoodData patch (#9424)
The line was accidentally duplicated in the 1.19.4 update and patching
process.

Fixes #9422
2023-03-27 12:51:53 -07:00
Joseph B ae65f5a255 Rename RegisterParticleProviderEvent's register methods to describe what kind of particle providers they register (deprecating old methods to avoid breaking) and minor docs tweaks (#9388) 2023-03-25 19:57:39 -07:00
noeppi_noeppi bb49b9e6dc Update pack versions (#9414) 2023-03-24 13:17:22 -07:00
Brennan Ward 2815e69407 [1.19.4] Revamp and fix spawn events (#9133) 2023-03-23 18:29:06 +00:00
Exopandora b33efc0727 [1.19.4] Replace blitOffset parameter with PoseStack in IItemDecorator (#9409)
* Replace blitOffset with PoseStack in IItemDecorator

* Circumvent breaking changes

* Fix blitOffset type
2023-03-22 11:11:01 +01:00
Dennis C 434c1ec4f8 Fix JSON model root transforms (#9410) 2023-03-22 11:09:41 +01:00
Dennis C c58d922324 Fix tossed items not being able to be picked up by other players. Closes #9412 (#9404) 2023-03-21 19:15:04 -07:00
Dennis C 331374dc7a Fix infinite BE render bounds failing frustum intersection test. Closes #9321 (#9407) 2023-03-21 12:16:49 -07:00
matyrobbrt c18eb23259 Make ForgeSlider use the new vanilla texture (#9406) 2023-03-20 14:16:35 -07:00
J.T. McQuigg d0e2242043 Add BlockSetType#register to accesstransformer.cfg (#9386) 2023-03-20 14:16:18 -07:00
Dennis C 684d36201f Add option to completely hide a crash-callable depending on a runtime value (#9372) 2023-03-20 14:15:59 -07:00
Jason 7b8ca60d32 Add isNewChunk to ChunkEvent.Load (#9369) 2023-03-20 14:15:33 -07:00
sciwhiz12 576ff81856 Remove DistExecutor calls in PistonEventTest (#9348) 2023-03-20 14:14:21 -07:00
sciwhiz12 73e68c9322 Fix hardcoded precipitation in ClimateSettingsBuilder (#9402)
This effectively caused all biomes to have precipitation, such as
minecraft:desert.

Fixes #9397
2023-03-20 08:56:10 +01:00
sciwhiz12 3cd758647d Fix incorrect variable used for swimming check (#9403)
Because of the incorrect variable, the check to stop sprinting (and stop
swimming) never fired correctly.

1.19.3's `flag5` variable was renamed to `flag7` in 1.19.4; however,
this was not caught during patching because of the fuzzy patcher.

Fixes #9399
2023-03-20 08:44:34 +01:00
sciwhiz12 14bc8d6611 Fix incorrect boolean used for glint effect (#9401)
The `flag1` variable is ultimately controlled by whether the armor slot
being rendered is for the leggings, which explains this bug where the
leggings always had the enchantment glint but not any other armor piece.

Fixes #9394
2023-03-20 08:02:18 +01:00
Redstone_Dubstep d552ce5eac Fixed ModMismatchDisconnectedScreen displaying missing mods wrongly (#9398) 2023-03-19 15:51:03 -07:00
Dennis C f03adc840e Fix misaligned text render type patch (#9391) 2023-03-16 14:44:20 -07:00
LexManos 31e73355c3 Remove thread filter from processing clientside custom payloads. Closes @9390 2023-03-16 11:51:46 -07:00
LexManos 88368dfa1c Fix LivingEntity patch which caused crash while entities got hurt. Closes #9389 2023-03-16 11:50:11 -07:00
matyrobbrt 7e6c8351c3 Fix wrong parameters in Screen#renderTooltipInternal patch (#9387) 2023-03-15 13:05:46 -07:00
LexManos fb20e4fa90 Fix misaligned patch in LevelRenderer. Closes #9385 2023-03-15 13:04:16 -07:00
Jonathan Coates 94c5699a03 Remove our fix for MC-121048 as it has been fixed by Vanilla (#9381) 2023-03-15 11:42:02 -07:00
LexManos 58626d1a22 Fix advancements not loading, bug seems to be fixed by vanilla now. Closes #9384 2023-03-15 11:41:12 -07:00
LexManos 631e72f99d Fixed patch verifier for cases where patches lowered access levels. Closes #9383 2023-03-15 11:39:42 -07:00
LexManos 3fffc18729 Fix crouching while sprinting stopping the player when step height is modified. Closes #9376 2023-03-14 23:56:29 -07:00
44c689f171 Forge 1.19.4
Properly move `ServerStatusPing` to codec
Reimplement custom display contexts

Co-authored-by: Matyrobbrt <[email protected]>
Co-authored-by: coehlrich <[email protected]>
Signed-off-by: SizableShrimp <[email protected]>
2023-03-14 17:22:27 -05:00
coehlrich 1f44339d6e Fix experimental world warning screen appearing everytime (#9375) 2023-03-09 08:26:32 -08:00
KnightMiner 001372bef2 Fix continuing to use items after dropping or when a shield breaks (MC-231097, MC-168573) (#9344) 2023-03-08 12:21:04 -08:00
KnightMiner 8f72c143a1 Add onStopUsing hook to IForgeItem (#9343) 2023-03-03 11:14:51 -08:00
Joseph B 3c75e1269d Document RegisterParticleProvidersEvent's APIs (#9346) 2023-02-24 13:18:07 -08:00
CreativeMD 121120aee1 Fix incorrect ListTag.getLongArray result (MC-260378) (#9351) 2023-02-24 13:16:32 -08:00
Su5eD 62ce55d8d7 Fix missing patch that left TagBuilder#replace unused (#9354) 2023-02-24 10:49:37 -08:00
Adneths 183d43f5f8 Add 2 new RenderLevelStageEvent.Stage for After Entities and After Block Entities (#9259) 2023-02-18 15:43:10 +01:00
ferriarnus 79dfdb0ace Cleanup StemBlock Patch (#9337) 2023-02-06 14:05:21 -08:00
Dennis C bb4a0b7dd6 Cleanup ItemProperties patch (#9332) 2023-02-06 14:04:04 -08:00
Dennis C 4cddc62be7 Make IForgeIntrinsicHolderTagAppender methods properly chainable (#9331) 2023-02-06 14:03:51 -08:00
ChampionAsh5357 914b2fe984 Fix in custom fluids not respecting max height correctly. (#9319) 2023-02-06 14:03:38 -08:00
cech12 308d4fa68e Fix inconsistent vaporization in BucketItem & FluidType (#9269) 2023-02-06 13:30:49 -08:00
cpw 50f325c8fe Fix reloading event firing during server shutdown and add explicit unloading event instead (#9016) 2023-02-06 13:30:16 -08:00
Joseph B 57fd0ecb2e Homogenize and/or holdersets when serializing to prevent serializing to NBT from crashing (#9048) Fixes #9043 2023-02-06 11:48:44 -08:00
matyrobbrt 94af5cc43a [1.19.x] Fix ForgeSlider not respecting custom height (#9237) 2023-02-05 18:58:08 +01:00
Mysterious_Dev 3c19a0309e Fix stepsound for blocks in the inside_step_sound_blocks tag. (#9318) 2023-01-25 13:19:54 -08:00
Minecraftschurli 0eb9d94bf3 Fix missing hanging sign material for modded wood type (#9303) 2023-01-23 13:34:22 -08:00
Jonathan Colmenares 4d57a1bec6 Fire TickEvent.LevelTickEvent on ClientLevel tick (#9299) 2023-01-23 11:47:00 -08:00
coehlrich 59ab01a559 Add ClientChatReceivedEvent for system messages (#9284) 2023-01-10 13:45:04 -08:00
matyrobbrt 5946356f8e PR Action update (#9274) 2023-01-08 21:24:00 +00:00
Minecraftschurli f0f7b18528 fix HangingSignEditScreen crash when using custom wood types using modid (#9294) 2023-01-08 21:21:23 +00:00
LexManos 5311b0bc90 Bump SecureJarHandler version, to help identify invalid mods. 2023-01-06 13:01:59 -08:00
Brennan Ward 43580f5804 [1.19.3] Hotfix missing null check in createUnbakedItemElements (#9285) 2023-01-06 19:42:56 +01:00
Curle b0a68b8c79 Mark 1.19.3 Recommended Build 2023-01-02 03:37:31 +00:00
Brennan Ward f225d1f3fb [1.19.3] Allow Item and Elements models to specify static color, sky light, and block light values. (#9106) 2022-12-31 10:36:56 +01:00
bconlon 15214a6e9a Fix StemBlock not checking canSustainPlant for the correct block, it now checks for Melons/Pumpkins instead of the stem itself. (#9270) 2022-12-30 11:13:42 -08:00
matyrobbrt dfce6b62d4 Add github shared actions for automation purposes. (#9251) 2022-12-30 11:12:49 -08:00
Mysterious_Dev 0232ad7424 Add translate key for Forge pack.mcmeta description (#9260) 2022-12-30 11:12:11 -08:00
Mysterious_Dev e6ff2e0c24 Fix broken link for update checker docs in mdk (#9271) 2022-12-30 11:11:43 -08:00
sciwhiz12 72980d2b03 Remove duplicate updateNeighbourForOutputSignal call Fixes #9169 (#9234) 2022-12-30 11:11:23 -08:00
Minecraftschurli 6e3b51d81f Add helper methods to access the set of loaded sprite locations (#9223) 2022-12-30 11:11:02 -08:00
Dennis C 6b1c628326 Disable guiLight3d for generated item models (#9230) 2022-12-30 17:59:36 +01:00
matyrobbrt 85f9b1012c Remove resource caching (#9254) 2022-12-27 11:40:06 -08:00
failall 7569de7004 Add TradeWithVillagerEvent (#9244) 2022-12-26 13:20:01 -08:00
Mysterious_Dev 5ccde7422c Update link for Parchment "Getting Started" (#9243) 2022-12-26 13:19:44 -08:00
bconlon ad6bf9428e Allows DatapackBuiltinEntriesProvider to datagen LevelStems (#9247) 2022-12-26 13:19:36 -08:00
noeppi_noeppi d176bab28e Add a method to LootContext.Builder that allows changing the queried loot table id (#9084) 2022-12-19 15:18:52 -08:00
ChampionAsh5357 58d6f072e4 [1.19.3] Fix Datagen Tests and Providers (#9212) 2022-12-19 16:28:19 +00:00
matyrobbrt cc393818e4 Fix concrete powder not being hydrated by singular water sources (#9236) 2022-12-19 16:05:22 +00:00
Girafi e84098a011 [1.19.3] Fix LootTableLoadEvent not getting fired (#9239) 2022-12-19 16:05:08 +00:00
matyrobbrt f2db3941ea Allow using custom factories in button builders (#9238) 2022-12-19 16:04:41 +00:00
Dennis C c2bfe12068 Fix logspam when a root resource is requested from DelegatingPackResources, fixes #9197 (#9227) 2022-12-19 16:04:27 +00:00
matyrobbrt d9e3a0081e [1.19.3] Fix retrieveRegistryLookup attempting to get the registry lookup from a HolderGetter (#9225) 2022-12-19 16:03:56 +00:00
Minecraftschurliandsciwhiz12 84d7a0facb [1.19.3] Add ability to datagen forge specific values in pack.mcmeta (#9221)
Co-authored-by: sciwhiz12 <[email protected]>
2022-12-16 16:52:54 +00:00
Dennis C 9c66928854 Add block atlas config to register forge:white texture (#9187) 2022-12-16 16:48:20 +00:00
bl4ckscor3 4b494451db Fix ExtendedButton not being highlighted when focused (#9144) 2022-12-16 16:46:58 +00:00
matyrobbrt e9ea4ca169 Separate checkAndFix from the check* tasks. (#9213) 2022-12-16 16:44:56 +00:00
Dennis C c5ca054936 Fix forge resources overriding vanilla ones (#9222) 2022-12-16 16:43:46 +00:00
Exopandora 7d2c7c0f91 Fix tooltip customization not working for creative inventory (#9218) 2022-12-16 16:42:51 +00:00
sciwhiz12 54b872af69 Fix glowing item frame entity's texture (#9126)
Fixes #9123
2022-12-16 16:41:45 +00:00
matyrobbrt ae954bea17 Fix datapack registries not being synced to clients (#9219) 2022-12-15 11:53:53 -08:00
Dennis C ad59985a9b Fix creatives tabs rendering overlapping tabs if the selected tab isn't on the current page. (#9214) 2022-12-15 11:41:29 -08:00
Technici4n f8fdd43552 Fix SidedInvWrapper not accounting for vanilla stacking special cases in brewing stands and furnaces (#9189) 2022-12-15 11:40:25 -08:00
Marc Hermans b269f7dbfd Update to the latest JarJar. (#9217) 2022-12-15 11:39:37 -08:00
ChampionAsh5357 e688f136dd Specify NetworkHooks#getEntitySpawningPacket Generic Return Type (#9220) 2022-12-15 11:39:29 -08:00
LexManos 81276b341a Fix using a DeferredRegister on a non-forge wrapped registry. Closes #9199 2022-12-12 12:00:23 -08:00
Dennis C f050617b47 Add support for custom CreativeModeTab implementations (#9210) 2022-12-12 10:23:48 -08:00
eerussianguy e6c7958456 Simplify tree grower patches (#9209) 2022-12-12 10:23:32 -08:00
ChampionAsh5357 4802ceeccc Replace AdvancementProvider patch with Forge helper (#9188) 2022-12-12 10:23:10 -08:00
matyrobbrt 2ea16d89ca Allow using PackOutputs in Forge-added datagen classes (#9182) 2022-12-12 10:22:48 -08:00
J.T. McQuigg e541201129 Add simpleBlockWithItem for data gens (#9170) 2022-12-12 10:22:32 -08:00
Technici4n 09aac3a4d3 Fix running test mods (#9211) 2022-12-12 10:22:09 -08:00
Dennis C dccb1ab3a4 [1.19.3] Fix models nested in custom geometries not resolving parents (#9200) 2022-12-11 21:25:14 +01:00
gigaherz 8bf96d8738 Fix OBJ Loader caches not being thread-safe. (#9204) 2022-12-11 20:36:17 +01:00
Dennis C 069a94f205 [1.19.3] Add event before baked models are cached by the BlockModelShaper (#9190) 2022-12-11 20:35:45 +01:00
sciwhiz12 332ed4ab76 Fix compatibility checker task configuration (#9202) 2022-12-11 20:24:47 +01:00
matyrobbrt 030f2dbd38 Fix chat offset (#9184) 2022-12-11 16:46:17 +01:00
LexManos ec3abdea14 Redesign CreativeTab collection event to be a lot more straight forward. (#9198) 2022-12-10 15:03:23 -08:00
LexManos 4cf5508e04 Move ICondition patch placement to before MC throws an error.
Disable the explicitly erroring test biome modifier.
2022-12-09 19:52:30 -08:00
shartte d861f647d7 Fix BlockStateProvider not waiting for models before finishing. (#9196) Fixes #9195: 2022-12-09 12:50:55 -08:00
LexManos 992f793695 Fix tooltips not rendering on screens. Closes #9191 2022-12-09 12:48:59 -08:00
LexManos 69eb940764 Fix merged mod resource pack not returning all resources with the same name when asked. Closes #9194 2022-12-09 12:44:40 -08:00
Marc Hermans c1f26d544a Fix searching using the wrong prefix for items or tags. Fixes #9176 Fixes #9179 (#9177) 2022-12-08 09:11:18 -08:00
8255d7aed3 Forge 1.19.3
Created a CreativeModeTabEvent to register creative mode tabs and populate entries per tab
Moved datapack registries to DataPackRegistryEvent.NewRegistry event instead of tying them to ForgeRegistry
Made it easier for mods to datagen datapack builtin entries with DatapackBuiltinEntriesProvider
Provided access to lookupProvider for datagen
Updated dependencies to match versions used by vanilla and update JarJar to 0.3.18
Added a test mod for the new CreativeModeTabEvent
Throws better error message for Forge registries in tag datagen
Deleted ForgeRegistryTagsProvider
Updated ClientChatReceivedEvent and ServerChatEvent for Mojang changes
Added patches for both sign related methods in ModelLayers
Changed RegisterShadersEvent to use ResourceProvider
Migrated old Mojang math types to JOML

Co-authored-by: Marc Hermans <[email protected]>
Co-authored-by: LexManos <[email protected]>
Co-authored-by: sciwhiz12 <[email protected]>
Co-authored-by: coehlrich <[email protected]>
2022-12-08 01:45:22 -06:00
1032 changed files with 18739 additions and 11472 deletions

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
name: 'Comment actions'
on: issue_comment
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/comment_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
+16
View File
@@ -0,0 +1,16 @@
name: 'Issue actions'
on: issues
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/issue_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
+13
View File
@@ -0,0 +1,13 @@
name: 'PR Review'
on: pull_request_review
jobs:
upload_payload:
name: Upload payload
runs-on: ubuntu-latest
steps:
- uses: actions/upload-artifact@v3 # Payload artifact is consumed in Actionable
with:
name: payload
path: ${{ github.event_path }}
@@ -0,0 +1,13 @@
name: 'PR Review Comment'
on: pull_request_review_comment
jobs:
upload_payload:
name: Upload payload
runs-on: ubuntu-latest
steps:
- uses: actions/upload-artifact@v3 # Payload artifact is consumed in Actionable
with:
name: payload
path: ${{ github.event_path }}
@@ -0,0 +1,20 @@
name: 'Pull Request Review Comment actions'
on:
workflow_run:
workflows: [PR Review Comment]
types:
- completed
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/pr_review_comment_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
@@ -0,0 +1,20 @@
name: 'Pull Request Review actions'
on:
workflow_run:
workflows: [PR Review]
types:
- completed
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/pr_review_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
+18
View File
@@ -0,0 +1,18 @@
name: 'Pull Request actions'
on:
pull_request_target:
types: [assigned, unassigned, labeled, unlabeled, opened, edited, closed, reopened, synchronize, converted_to_draft, ready_for_review, locked, unlocked, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled]
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/pr_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
+16
View File
@@ -0,0 +1,16 @@
name: 'Push actions'
on: push
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/push_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
-42
View File
@@ -1,42 +0,0 @@
name: 'Spam label'
on:
issues:
types: [labeled]
pull_request:
types: [labeled]
discussion:
types: [labeled]
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
lock:
if: ${{ (github.event.label.name == 'spam') && (github.event.action == 'labeled') }}
runs-on: ubuntu-latest
steps:
- uses: peter-evans/close-issue@v2
name: Close Issue
with:
close-reason: 'not_planned'
if: ${{ github.event_name != 'pull_request' }}
- run: gh pr close ${{ github.event.pull_request.html_url }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: Close PR
if: ${{ github.event_name == 'pull_request' }}
- uses: maxkomarychev/[email protected]
name: Lock PR
if: ${{ github.event_name == 'pull_request' }}
with:
token: "${{ secrets.GITHUB_TOKEN }}"
issue_number: ${{ github.event.number }}
- uses: OSDKDev/[email protected]
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
name: Lock issue
if: ${{ github.event_name != 'pull_request' }}
-27
View File
@@ -1,27 +0,0 @@
name: Support Requests
on:
issues:
types:
- labeled
- unlabeled
- reopened
permissions:
issues: write
jobs:
support:
runs-on: ubuntu-latest
steps:
- uses: dessant/support-requests@v2
with:
support-label: Forum
issue-comment: >
:wave: We use the issue tracker exclusively for final bug reports and feature requests.
However, this issue appears to be better suited for the
[Forge Support Forums](https://forums.minecraftforge.net/) or
[Forge Discord](https://discord.gg/UvedJ9m). Please create a new topic on the support forum
with this issue or ask in the `#tech-support` channel in the Discord server, and the
conversation can continue there.
lock-issue: true
+1
View File
@@ -34,6 +34,7 @@
/repo/
src/generated/resources/.cache/
src/generated_test/resources/.cache/
src/generated_mdk/resources/.cache/
#Patch rejects
/patches-/
+3 -3
View File
@@ -37,7 +37,7 @@ project {
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.4", label = "Gradle version", description = "The version of Gradle to use during execution of Gradle tasks.", 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.19.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*)
@@ -155,7 +155,7 @@ object PullRequestChecks : BuildType({
enableStacktrace = true
dockerImage = "%docker_gradle_image%"
dockerRunParameters = """
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
-v "/opt/cache/shared/gradle:/home/gradle/rocache:ro"
--network=host
-u 1000:1000
@@ -189,7 +189,7 @@ object PullRequestCompatibility : BuildType({
enableStacktrace = true
dockerImage = "%docker_gradle_image%"
dockerRunParameters = """
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
-v "/opt/cache/shared/gradle:/home/gradle/rocache:ro"
--network=host
-u 1000:1000
+92 -26
View File
@@ -1,4 +1,10 @@
import groovy.json.JsonBuilder
import net.minecraftforge.forge.tasks.checks.CheckATs
import net.minecraftforge.forge.tasks.checks.CheckExcs
import net.minecraftforge.forge.tasks.checks.CheckPatches
import net.minecraftforge.forge.tasks.checks.CheckSAS
import net.minecraftforge.forge.tasks.checks.CheckTask
import net.minecraftforge.forge.tasks.checks.CheckMode
import java.nio.file.Files
import java.util.Date
@@ -13,6 +19,7 @@ import net.minecraftforge.gradle.common.tasks.SignJar
import net.minecraftforge.gradle.userdev.tasks.RenameJar
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.plugins.ide.eclipse.model.SourceFolder
import org.objectweb.asm.Opcodes
plugins {
id 'org.cadixdev.licenser' version '0.6.1'
@@ -36,16 +43,16 @@ ext {
]
}
MAPPING_CHANNEL = 'official'
MAPPING_VERSION = '1.19.2'
MC_VERSION = '1.19.2'
MAPPING_VERSION = '1.19.4'
MC_VERSION = '1.19.4'
MC_NEXT_VERSION = '1.20'
MCP_VERSION = '20220805.130853'
MCP_VERSION = '20230314.122934'
SPI_VERSION = '6.0.0'
ACCESSTRANSFORMERS_VERSION = '8.0.4'
COREMODS_VERSION = '5.0.1'
EVENTBUS_VERSION = '6.0.3'
MODLAUNCHER_VERSION = '10.0.8'
SECUREJARHANDLER_VERSION = '2.1.4'
SECUREJARHANDLER_VERSION = '2.1.6'
BOOTSTRAPLAUNCHER_VERSION = '1.1.2'
ASM_VERSION = '9.5'
INSTALLER_VERSION = '2.1.+'
@@ -56,17 +63,17 @@ ext {
JETBRAINS_ANNOTATIONS_VERSION = '23.0.0'
SLF4J_API_VERSION = '1.8.0-beta4'
APACHE_MAVEN_ARTIFACT_VERSION = '3.8.5'
JARJAR_VERSION = '0.3.19'
// These versions should be kept in sync with the version manifest JSON
// To use a version newer than the version manifest JSON, the dependency must be added to the installer configuration
MOJANG_LOGGING_VERSION = '1.0.0'
LOG4J_VERSION = '2.17.0'
GUAVA_VERSION = '31.0.1-jre'
GSON_VERSION = '2.8.9'
MOJANG_LOGGING_VERSION = '1.1.1'
LOG4J_VERSION = '2.19.0'
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.11.0'
JARJAR_VERSION = '0.3.16'
GIT_INFO = gradleutils.gitInfo
VERSION = gradleutils.getFilteredMCTagOffsetBranchVersion(true, '[0-9]', MC_VERSION)
@@ -1071,11 +1078,38 @@ project(':forge') {
'--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]
}
}
forge_mdk_data {
parent runs.forge_data
taskName 'forge_mdk_data'
environment 'MOD_CLASSES', 'dummy' // Needed to work around FG limitation, FG will replace this!
ideaModule "${rootProject.name}.${project.name}.test"
mods {
tests { sources sourceSets.test }
}
afterEvaluate {
// Clear forge's main datagen args
args.removeAll {
forgeDataArgs.contains(it instanceof File ? (it as File).absolutePath : it.toString())
}
args '--flat', '--all', '--validate',
'--mod', 'mdk_datagen',
'--output', rootProject.file('src/generated_mdk/resources/'),
'--existing', sourceSets.main.resources.srcDirs[0],
'--existing', sourceSets.test.resources.srcDirs[0]
}
}
}
}
@@ -1168,40 +1202,70 @@ project(':forge') {
}
}
task checkATs(type: CheckATs, dependsOn: [extractInheritance, createSrg2Mcp]) {
inheritance = extractInheritance.output
task findFinalizeSpawnTargets(type: BytecodePredicateFinder, dependsOn: ['createJoinedSRG']) {
jar = createJoinedSRG.output
output = rootProject.file('src/main/resources/coremods/finalize_spawn_targets.json')
predicate = {
parent, node, insn ->
'net/minecraft/world/level/BaseSpawner' != parent.name // Ignore this class as we special case it.
&& insn.getOpcode() == Opcodes.INVOKEVIRTUAL
&& insn.name.equals('m_6518_')
&& insn.desc.equals('(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/MobSpawnType;Lnet/minecraft/world/entity/SpawnGroupData;Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/world/entity/SpawnGroupData;')
}
}
tasks.register('validateDeprecations', ValidateDeprecations) {
input = tasks.jar.archiveFile
mcVersion = MC_VERSION
}
tasks.jar.finalizedBy 'validateDeprecations'
tasks.register('checkAll') {
dependsOn 'checkLicenses'
group = 'checks'
}
tasks.register('checkAllAndFix') {
dependsOn 'findFinalizeSpawnTargets', 'findFieldInstanceChecks', 'checkLicenses'
group = 'checks'
}
CheckTask.registerTask(tasks, 'ATs', CheckATs) {
dependsOn extractInheritance, createSrg2Mcp
ats.from patcher.accessTransformers
inheritance = extractInheritance.output
mappings = createSrg2Mcp.output
}
task checkSAS(type: CheckSAS, dependsOn: extractInheritance) {
inheritance = extractInheritance.output
CheckTask.registerTask(tasks, 'SAS', CheckSAS) {
dependsOn extractInheritance
sass.from patcher.sideAnnotationStrippers
inheritance = extractInheritance.output
}
task checkExcs(type: CheckExcs, dependsOn: jar) {
CheckTask.registerTask(tasks, 'Excs', CheckExcs) {
dependsOn jar
binary = jar.archiveFile.get().asFile
excs.from patcher.excs
}
task checkPatchesAndFix(type: CheckPatches, dependsOn: genPatches) {
CheckTask.registerTask(tasks, 'Patches', CheckPatches) {
dependsOn genPatches
patchDir = file("$rootDir/patches")
autoFix = true
}
task checkPatches(type: CheckPatches, dependsOn: genPatches) {
patchDir = file("$rootDir/patches")
autoFix = false
patchesWithS2SArtifact = [
'minecraft/net/minecraft/client/renderer/ViewArea.java.patch',
'minecraft/net/minecraft/data/models/blockstates/Variant.java.patch',
]
}
genPatches {
finalizedBy checkPatches
finalizedBy checkAndFixPatches
autoHeader true
lineEnding = '\n'
}
def baseForgeVersionProperty = project.objects.property(String)
baseForgeVersionProperty.set(project.provider { getLatestForgeVersion(MC_VERSION) }.map { MC_VERSION + '-' + it })
baseForgeVersionProperty.set(project.provider { TeamcityRequests.attemptFindBase(rootDir) ?: getLatestForgeVersion(MC_VERSION) })
baseForgeVersionProperty.finalizeValueOnRead()
def jarCompatibilityTaskSetup = { task ->
task.group = 'jar compatibility'
@@ -1251,8 +1315,6 @@ project(':forge') {
commonLibraries.from(project.configurations.moduleonly)
}
task checkAll(dependsOn: [checkATs, checkSAS, checkExcs, findFieldInstanceChecks, checkPatches, checkLicenses]){}
task launcherJson(type: LauncherJson) {
packedDependencies = [':fmlloader:jar']
doFirst {
@@ -1569,6 +1631,10 @@ project(':forge') {
rename 'gitignore\\.txt', '.gitignore'
rename 'gitattributes\\.txt', '.gitattributes'
}
from(rootProject.file('src/generated_mdk/resources/')) {
into('src/main/resources/')
exclude '**/.cache/'
}
}
license {
@@ -1720,4 +1786,4 @@ if (System.env.TEAMCITY_VERSION) {
println "##teamcity[setParameter name='env.PUBLISHED_JAVA_FML_ARTIFACT_VERSION' value='${project(':fmlonly').version}']"
}
}
}
}
+2
View File
@@ -8,4 +8,6 @@ dependencies {
implementation 'org.ow2.asm:asm-tree:9.5'
implementation 'net.minecraftforge:srgutils:0.5.+'
implementation 'commons-io:commons-io:2.8.0'
implementation 'com.google.code.gson:gson:2.10'
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r'
}
@@ -6,15 +6,10 @@ import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.objectweb.asm.ClassReader
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.MethodNode
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
abstract class BytecodeFinder extends DefaultTask {
@InputFile abstract RegularFileProperty getJar()
@OutputFile abstract RegularFileProperty getOutput()
@@ -33,18 +28,7 @@ abstract class BytecodeFinder extends DefaultTask {
pre()
jar.get().asFile.withInputStream { i ->
new ZipInputStream(i).withCloseable { zin ->
ZipEntry zein
while ((zein = zin.nextEntry) != null) {
if (zein.name.endsWith('.class')) {
def node = new ClassNode(Opcodes.ASM9)
new ClassReader(zin).accept(node, 0)
process(node)
}
}
}
}
Util.processClassNodes(jar.get().asFile, this::process)
post()
outputFile.text = new JsonBuilder(getData()).toPrettyString()
@@ -0,0 +1,32 @@
package net.minecraftforge.forge.tasks
import groovy.transform.CompileStatic
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Internal
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.MethodNode
@CompileStatic
abstract class BytecodePredicateFinder extends BytecodeFinder {
@Internal
abstract Property<Closure<Boolean>> getPredicate()
private final Set<String> matches = new HashSet()
@Override
protected process(ClassNode parent, MethodNode node) {
for (final current : node.instructions) {
if (predicate.get().call(parent, node, current)) {
matches.add(parent.name)
return
}
}
}
@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.')}()
}
}
@@ -1,267 +0,0 @@
package net.minecraftforge.forge.tasks
import net.minecraftforge.srgutils.IMappingFile
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction
import org.objectweb.asm.Opcodes
abstract class CheckATs extends DefaultTask {
@InputFile abstract RegularFileProperty getInheritance()
@InputFiles abstract ConfigurableFileCollection getAts()
@InputFile @Optional abstract RegularFileProperty getMappings()
@TaskAction
protected void exec() {
def mappings = mappings.map { IMappingFile.load(it.asFile) }.getOrNull()
Util.init()
def parse = { line ->
def idx = line.indexOf('#')
def comment = idx == -1 ? null : line.substring(idx)
if (idx != -1) line = line.substring(0, idx - 1)
def (modifier, cls, desc) = (line.trim() + ' ').split(' ', -1)
def key = cls + (desc.isEmpty() ? '' : ' ' + desc)
return [modifier, cls, desc, comment, key]
}
def accessLevel = { access ->
if ((access & Opcodes.ACC_PUBLIC) != 0) return 3
if ((access & Opcodes.ACC_PROTECTED) != 0) return 2
if ((access & Opcodes.ACC_PRIVATE) != 0) return 0
return 1
}
def accessStr = { access ->
if (access.endsWith('-f') || access.endsWith('+f'))
return 4
switch (access.toLowerCase()) {
case 'public': return 3
case 'protected': return 2
case 'default': return 1
case 'private': return 0
default: return -1
}
}
def json = inheritance.get().asFile.json()
ats.each { f ->
TreeMap lines = [:]
def group = null
for (def line : f.readLines()) {
if (line.isEmpty()) continue
if (line.startsWith('#group ')) {
def (modifier, cls, desc, comment, key) = parse.call(line.substring(7))
if (desc != '*' && desc != '*()' && desc != '<init>')
throw new IllegalStateException('Invalid group: ' + line)
group = [modifier: modifier, cls: cls, desc: desc, comment: comment,
existing: [] as Set,
children: [] as TreeSet,
group: true
]
if (lines.containsKey(key))
throw new IllegalStateException('Duplicate group: ' + line)
lines[key] = group
} else if (group != null) {
if (line.startsWith('#endgroup')) {
group = null
} else {
def (modifier, cls, desc, comment, key) = parse.call(line)
group['existing'].add(key)
}
} else if (line.startsWith('#endgroup')) {
throw new IllegalStateException('Invalid group ending: ' + line)
} else if (line.startsWith('#')) {
//Nom
} else {
def (modifier, cls, desc, comment, key) = parse.call(line)
if (lines.containsKey(key)) {
println('Duplicate: ' + line)
continue
}
lines[key] = [modifier: modifier, cls: cls, desc: desc, comment: comment, group: false]
}
}
// Process Groups, this will remove any entries outside the group that is covered by the group
for (def key : new ArrayList<>(lines.keySet())) {
def entry = lines.get(key)
if (entry != null && entry['group']) {
def cls = entry['cls']
def jcls = json.get(cls.replaceAll('\\.', '/'))
if (jcls == null) {
lines.remove(key)
println('Invalid Group: ' + key)
} else if ('*' == entry['desc']) {
if (!jcls.containsKey('fields')) {
lines.remove(key)
println('Invalid Group, Class has no fields: ' + key)
} else {
jcls['fields'].each { field, value ->
def fkey = cls + ' ' + field
if (accessLevel.call(value['access']) < accessStr.call(entry['modifier'])) {
if (lines.containsKey(fkey)) {
lines.remove(fkey)
} else if (!entry['existing'].contains(fkey)) {
println('Added: ' + fkey)
}
entry['children'].add(fkey)
} else if (lines.containsKey(fkey)) {
lines.remove(fkey)
println('Removed: ' + fkey)
}
}
entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) }
}
} else if ('*()' == entry['desc']) {
if (!jcls.containsKey('methods')) {
lines.remove(key)
println('Invalid Group, Class has no methods: ' + key)
} else {
jcls['methods'].each{ mtd, value ->
if (mtd.startsWith('<clinit>') || mtd.startsWith('lambda$'))
return
key = cls + ' ' + mtd.replace(' ', '')
if (accessLevel.call(value['access']) < accessStr.call(entry['modifier'])) {
if (lines.containsKey(key)) {
lines.remove(key)
} else if (!entry['existing'].contains(key)) {
println('Added: ' + key)
}
entry['children'].add(key)
} else if (lines.containsKey(key)) {
lines.remove(key)
println('Removed: ' + key)
}
}
entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) }
}
} else if ('<init>' == entry['desc']) { //Make all public non-abstract subclasses
json.each{ tcls,value ->
if (!value.containsKey('methods') || ((value['access'] & Opcodes.ACC_ABSTRACT) != 0))
return
def parents = [] as Set
def parent = tcls
while (parent != null && json.containsKey(parent)) {
parents.add(parent)
def p = json[parent]
parent = p == null ? null : p['superName']
}
if (parents.contains(cls.replaceAll('\\.', '/'))) {
value['methods'].each{ mtd, v ->
if (mtd.startsWith('<init>')) {
def child = tcls.replaceAll('/', '\\.') + ' ' + mtd.replace(' ', '')
if (accessLevel.call(v['access']) < 3) {
if (lines.containsKey(child)) {
lines.remove(child)
} else if (!entry['existing'].contains(child)) {
println('Added: ' + child)
}
entry['children'].add(child)
} else if (lines.containsKey(child)) {
lines.remove(child)
println('Removed: ' + child)
}
}
}
}
}
entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) }
}
}
}
// Process normal lines, remove invalid and remove narrowing
for (def key : new ArrayList<>(lines.keySet())) {
def entry = lines.get(key)
if (entry != null && !entry['group']) {
def cls = entry['cls']
def jcls = json.get(cls.replaceAll('\\.', '/'))
if (jcls == null) {
lines.remove(key)
println('Invalid: ' + key)
} else if (entry['desc'] == '') {
if (accessLevel.call(jcls['access']) > accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) {
lines.remove(key)
println('Invalid Narrowing: ' + key)
}
} else if (!entry['desc'].contains('(')) {
if (!jcls.containsKey('fields') || !jcls['fields'].containsKey(entry['desc'])) {
lines.remove(key)
println('Invalid: ' + key)
} else {
def value = jcls['fields'][entry['desc']]
if (accessLevel.call(value['access']) > accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) {
lines.remove(key)
println('Invalid Narrowing: ' + key)
println(entry.comment)
}
}
} else {
def jdesc = entry['desc'].replace('(', ' (')
if (!jcls.containsKey('methods') || !jcls['methods'].containsKey(jdesc)) {
lines.remove(key)
println('Invalid: ' + key)
} else {
def value = jcls['methods'][jdesc]
if (accessLevel.call(value['access']) > accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) {
lines.remove(key)
println('Invalid Narrowing: ' + key)
}
}
}
}
}
def data = []
def remapComment = { entry ->
if (!mappings || !entry || !entry.desc) return null
def comment = entry.comment?.substring(1)?.trim()
def jsonCls = json.get(entry.cls.replaceAll('\\.', '/'))
def mappingsClass = mappings?.getClass(jsonCls.name)
if (!mappingsClass) return entry.comment
def idx = entry.desc.indexOf('(')
def mappedName = idx == -1
? mappingsClass.remapField(entry.desc)
: mappingsClass.remapMethod(entry.desc.substring(0, idx), entry.desc.substring(idx))
if (!mappedName) return entry.comment
if (mappedName == '<init>')
mappedName = 'constructor'
if (comment?.startsWith(mappedName))
return '# ' + comment
if (comment && comment.indexOf(' ') != -1) {
def split = comment.split(" - ").toList()
if (split[0].indexOf(' ') != -1)
// The first string is more than one word, so append before it
return "# ${mappedName} - ${comment}"
split.remove(0)
return "# ${mappedName} - ${String.join(' - ', split)}"
}
return '# ' + mappedName
}
lines.each { key,value ->
if (!value.group) {
def comment = remapComment.call(value)
data.add(value.modifier + ' ' + key + (comment ? ' ' + comment : ''))
} else {
data.add('#group ' + value.modifier + ' ' + key + (value.comment == null ? '' : ' ' + value.comment))
value.children.each {
def line = value.modifier + ' ' + it
def (modifier, cls, desc, comment) = parse.call(line)
comment = remapComment.call([modifier: modifier, cls: cls, desc: desc, comment: comment])
data.add(line + (comment ? ' ' + comment : ''))
}
data.add('#endgroup')
}
}
f.text = data.join('\n')
}
}
}
@@ -1,94 +0,0 @@
package net.minecraftforge.forge.tasks
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.TaskAction
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
abstract class CheckExcs extends DefaultTask {
@InputFile abstract RegularFileProperty getBinary()
@InputFiles abstract ConfigurableFileCollection getExcs()
@TaskAction
protected void exec() {
Util.init()
def known = []
binary.get().asFile.withInputStream { i ->
new ZipInputStream(i).withCloseable { zin ->
def visitor = new ClassVisitor(Opcodes.ASM9) {
private String cls
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.cls = name
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
known.add(this.cls + '.' + name + descriptor)
super.visitMethod(access, name, descriptor, signature, exceptions)
}
}
ZipEntry zein
while ((zein = zin.nextEntry) != null) {
if (zein.name.endsWith('.class')) {
ClassReader reader = new ClassReader(zin)
reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES)
}
}
}
}
excs.each { f ->
def lines = []
f.eachLine { line ->
def idx = line.indexOf('#')
if (idx == 0 || line.isEmpty()) {
return
}
def comment = idx == -1 ? null : line.substring(idx)
if (idx != -1) line = line.substring(0, idx - 1)
if (!line.contains('=')) {
println('Invalid: ' + line)
return
}
def (key, value) = line.split('=', 2)
if (!known.contains(key)) {
println('Unknown: ' + line)
return
}
def (cls, desc) = key.split('\\.', 2)
if (!desc.contains('(')) {
println('Invalid: ' + line)
return
}
def name = desc.split('\\(', 2)[0]
desc = '(' + desc.split('\\(', 2)[1]
def (exceptions, args) = value.contains('|') ? value.split('|', 2) : [value, '']
if (args.split(',').length != Type.getArgumentTypes(desc).length) {
println('Invalid: ' + line)
return
}
lines.add(line)
}
f.text = lines.sort().join('\n')
}
}
}
@@ -1,219 +0,0 @@
package net.minecraftforge.forge.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.TaskAction
import java.nio.file.Files
import java.nio.file.Paths
import java.util.regex.Pattern
abstract class CheckPatches extends DefaultTask {
@InputDirectory abstract DirectoryProperty getPatchDir()
@Input boolean autoFix = false
@TaskAction
protected void exec() {
def hasS2SArtifact = [
Paths.get("patches/minecraft/net/minecraft/client/renderer/ViewArea.java.patch"),
Paths.get("patches/minecraft/net/minecraft/data/models/blockstates/Variant.java.patch")
]
def verified = true;
patchDir.get().asFileTree.each { patch ->
def patchPath = project.rootDir.toPath().relativize(patch.toPath())
verified &= verifyPatch(patch, autoFix, patchPath.toString(), hasS2SArtifact.contains(patchPath))
}
if (!verified)
throw new RuntimeException('One or more patches failed verification. Check the log for errors.')
}
def verifyPatch(patch, fix, patchPath, hasS2SArtifact) {
def hunk_start_pattern = Pattern.compile('^@@ -[0-9,]* \\+[0-9,_]* @@$')
def white_space_pattern = Pattern.compile('^[+\\-]\\s*$')
def import_pattern = Pattern.compile('^[+\\-]\\s*import.*')
def field_pattern = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^=;]*)(=.*)?;\\s*$')
def method_pattern = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^(]*)[(]([^)]*)?[)]\\s*[{]\\s*$')
def class_pattern = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final[\\s]*)?(class|interface)([^{]*)[{]\\s*$')
def accessMap = [("private"):0, (null):1, ("protected"):2, ("public"):3]
def hasProblem = false;
def lines = patch.readLines()
def hunksStart = 0
def onlyWhiteSpace = false
def didFix = false
def newLines = []
def whiteSpaceErrors = []
// First two lines are file name ++/-- and we do not care
newLines.add(lines[0] + '\n')
newLines.add(lines[1] + '\n')
int i = 2
for (; i < lines.size(); ++i) {
def line = lines[i]
newLines.add(line + '\n')
if (hunk_start_pattern.matcher(line).find()) {
if (onlyWhiteSpace) {
if (fix || hasS2SArtifact) {
logger.lifecycle("Removing white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath)
def toRemove = i - hunksStart;
while (toRemove-- > 0)
newLines.remove(newLines.size() - 1)
didFix = true
}
else {
logger.lifecycle("Patch contains only white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath)
hasProblem = true
}
}
else {
if (!whiteSpaceErrors.empty)
hasProblem = true
whiteSpaceErrors.each { error -> logger.lifecycle(error) }
whiteSpaceErrors.clear()
}
hunksStart = i
onlyWhiteSpace = true
continue
}
if (line.startsWithAny('+','-')) {
def prefixChange = false
def prevLine = lines[i - 1]
if (line.charAt(0) == (char)'+' && prevLine.charAt(0) == (char)'-') {
def prevTrim = prevLine.substring(1).replaceAll("\\s", "")
def currTrim = line.substring(1).replaceAll("\\s", "")
if (prevTrim.equals(currTrim)) {
prefixChange = true
}
def pMatcher = field_pattern.matcher(prevLine)
def cMatcher = field_pattern.matcher(line)
if (pMatcher.find() && cMatcher.find() &&
pMatcher.group(6) == cMatcher.group(6) && // = ...
pMatcher.group(5) == cMatcher.group(5) && // field name
pMatcher.group(3) == cMatcher.group(3) && // static
(accessMap[pMatcher.group(2)] < accessMap[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
logger.lifecycle("Patch contains access changes or final removal at line {}, file: {}", i + 1, patchPath)
hasProblem = true
}
pMatcher = method_pattern.matcher(prevLine)
cMatcher = method_pattern.matcher(line)
if (pMatcher.find() && cMatcher.find() &&
pMatcher.group(6) == cMatcher.group(6) && // params
pMatcher.group(5) == cMatcher.group(5) && // <T> void name
pMatcher.group(3) == cMatcher.group(3) && // static
(accessMap[pMatcher.group(2)] < accessMap[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
logger.lifecycle("Patch contains access changes or final removal at line {}, file: {}", i + 1, patchPath)
hasProblem = true
}
pMatcher = class_pattern.matcher(prevLine)
cMatcher = class_pattern.matcher(line)
if (pMatcher.find() && cMatcher.find() &&
pMatcher.group(6) == cMatcher.group(6) && // ClassName<> extends ...
pMatcher.group(5) == cMatcher.group(5) && // class | interface
pMatcher.group(3) == cMatcher.group(3) && // static
(accessMap[pMatcher.group(2)] < accessMap[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
logger.lifecycle("Patch contains access changes or final removal at line {}, file: {}", i + 1, patchPath)
hasProblem = true
}
}
if (line.charAt(0) == (char)'-' && i + 1 < lines.size()) {
def nextLine = lines[i + 1]
if (nextLine.charAt(0) == (char)'+') {
def nextTrim = nextLine.substring(1).replaceAll("\\s", "")
def currTrim = line.substring(1).replaceAll("\\s", "")
if (nextTrim.equals(currTrim)) {
prefixChange = true
}
}
}
def isWhiteSpaceChange = white_space_pattern.matcher(line).find()
if (!prefixChange && !isWhiteSpaceChange) {
onlyWhiteSpace = hasS2SArtifact && import_pattern.matcher(line).find()
}
else if (isWhiteSpaceChange) {
def prevLineChange = prevLine.startsWithAny('+','-')
def nextLineChange = i + 1 < lines.size() && lines[i + 1].startsWithAny('+','-')
if (!prevLineChange && !nextLineChange) {
whiteSpaceErrors.add(String.format("Patch contains white space change in valid hunk at line %d (cannot auto fix), file: %s", i + 1, patchPath))
}
}
if (line.contains("\t")) {
if (!fix) {
logger.lifecycle("Patch contains tabs on line {}, file: {}", i + 1, patchPath)
hasProblem = true
}
else {
logger.lifecycle("Fixing tabs on line {}, file: {}", i + 1, patchPath)
line = line.replaceAll('\t', ' ')
newLines.remove(newLines.size() - 1)
newLines.add(line + '\n')
didFix = true
}
}
if (import_pattern.matcher(line).find() && !hasS2SArtifact) {
logger.lifecycle("Patch contains import change on line {}, file: {}", i + 1, patchPath)
hasProblem = true
}
}
}
if (onlyWhiteSpace) {
if (fix || hasS2SArtifact) {
logger.lifecycle("Removing white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath)
def toRemove = i - hunksStart;
while (toRemove-- > 0)
newLines.remove(newLines.size() - 1)
didFix = true
}
else {
logger.lifecycle("Patch contains only white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath)
hasProblem = true
}
}
else {
if (!whiteSpaceErrors.empty)
hasProblem = true
whiteSpaceErrors.each { error -> logger.lifecycle(error) }
}
if (didFix) {
if (newLines.size() <= 2) {
logger.lifecycle("Patch is now empty removing, file: {}", patchPath)
Files.delete(patch.toPath())
}
else {
if (!hasS2SArtifact)
logger.lifecycle("*** Updating patch file. Please run setup then genPatches again. ***")
patch.withWriter('UTF-8') { writer ->
newLines.each { l -> writer.write(l) }
}
}
}
return !hasProblem
}
}
@@ -1,107 +0,0 @@
package net.minecraftforge.forge.tasks
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.TaskAction
abstract class CheckSAS extends DefaultTask {
@InputFile abstract RegularFileProperty getInheritance()
@InputFiles abstract ConfigurableFileCollection getSass()
@TaskAction
protected void exec() {
Util.init()
def json = inheritance.get().asFile.json()
sass.each { f ->
def lines = []
f.eachLine { line ->
if (line[0] == '\t') return //Skip any tabbed lines, those are ones we add
def idx = line.indexOf('#')
if (idx == 0 || line.isEmpty()) {
lines.add(line)
return
}
def comment = idx == -1 ? null : line.substring(idx)
if (idx != -1) line = line.substring(0, idx - 1)
def (cls, name, desc) = (line.trim().replace('(', ' (') + ' ').split(' ', -1)
cls = cls.replaceAll('\\.', '/')
if (json[cls] == null) {
println('Invalid: ' + line)
} else if (name.isEmpty()) { //Class SAS
def toAdd = []
def clsSided = isSided(json[cls])
def sided = clsSided
/* TODO: MergeTool doesn't do fields
if (json[cls]['fields'] != null) {
for (entry in json[cls]['fields']) {
if (isSided(entry.value)) {
sided = true
toAdd.add('\t' + cls + ' ' + entry.key)
}
}
}
*/
if (json[cls]['methods'] != null) {
for (entry in json[cls]['methods']) {
if (isSided(entry.value)) {
sided = true
toAdd.add('\t' + cls + ' ' + entry.key.replaceAll(' ', ''))
findChildMethods(json, cls, entry.key).each { lines.add('\t' + it) }
findChildMethods(json, cls, entry.key).each { println(line + ' -- ' + it) }
} else if (clsSided) {
findChildMethods(json, cls, entry.key).each { lines.add('\t' + it) }
findChildMethods(json, cls, entry.key).each { println(line + ' -- ' + it) }
}
}
}
if (sided) {
lines.add(cls + (comment == null ? '' : ' ' + comment))
lines.addAll(toAdd.sort())
} else
println('Invalid: ' + line)
} else if (desc.isEmpty()) { // Fields
/* TODO: MergeTool doesn't do fields
if (json[cls]['fields'] != null && isSided(json[cls]['fields'][name]))
lines.add(cls + ' ' + name + (comment == null ? '' : ' ' + comment))
else
*/
println('Invalid: ' + line)
} else { // Methods
if (json[cls]['methods'] == null || !isSided(json[cls]['methods'][name + ' ' + desc]))
println('Invalid: ' + line)
else {
lines.add(cls + ' ' + name + desc + (comment == null ? '' : ' ' + comment))
findChildMethods(json, cls, name + ' ' + desc).each { println(line + ' -- ' + it) }
findChildMethods(json, cls, name + ' ' + desc).each { lines.add('\t' + it) }
}
}
}
f.text = lines.join('\n')
}
}
protected static isSided(json) {
if (json == null || json['annotations'] == null)
return false
for (ann in json['annotations']) {
if ('Lnet/minecraftforge/api/distmarker/OnlyIn;'.equals(ann.desc))
return true
}
return false
}
protected static findChildMethods(json, cls, desc)
{
return json.values().findAll{ it.methods != null && it.methods[desc] != null && it.methods[desc].override == cls && isSided(it.methods[desc]) }
.collect { it.name + ' ' + desc.replace(' ', '') } as TreeSet
}
}
@@ -0,0 +1,49 @@
package net.minecraftforge.forge.tasks
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import groovy.transform.CompileStatic
import groovy.transform.TupleConstructor
@CompileStatic
@TupleConstructor
final class InheritanceData implements Annotatable {
String name
int access
String superName
List<String> interfaces = []
Map<String, Method> methods = [:]
Map<String, Field> fields = [:]
List<Annotation> annotations = []
@TupleConstructor
static final class Method implements Annotatable {
int access
String override
List<Annotation> annotations = []
}
@TupleConstructor
static final class Field implements Annotatable {
int access
String desc
List<Annotation> annotations = []
}
@TupleConstructor
static final class Annotation {
String desc
}
private static final Gson GSON = new Gson()
static Map<String, InheritanceData> parse(File file) {
try (final reader = file.newReader()) {
return GSON.fromJson(reader, new TypeToken<Map<String, InheritanceData>>() {})
}
}
}
@CompileStatic
interface Annotatable {
List<InheritanceData.Annotation> getAnnotations()
}
@@ -6,7 +6,7 @@ import org.gradle.api.provider.SetProperty
abstract class InstallerJar extends Zip {
@Input @Optional abstract SetProperty<String> getPackedDependencies()
InstallerJar() {
archiveClassifier.set('installer')
archiveExtension.set('jar') // Needs to be Zip task to not override Manifest, so set extension
@@ -15,7 +15,7 @@ abstract class InstallerJar extends Zip {
def installerJson = project.tasks.installerJson
def launcherJson = project.tasks.launcherJson
def downloadInstaller = project.tasks.downloadInstaller
dependsOn(installerJson, launcherJson, downloadInstaller)
from(installerJson, launcherJson)
@@ -24,13 +24,13 @@ abstract class InstallerJar extends Zip {
from(project.zipTree(downloadInstaller.output)) {
duplicatesStrategy = 'exclude'
}
if (!System.env.MAVEN_USER) {
if (!System.env.TEAMCITY_VERSION) {
dependsOn(project.universalJar)
from(project.universalJar) {
into '/maven/' + project.group.replace('.', '/') + '/' + project.universalJar.archiveBaseName.get() + '/' + project.version + '/'
}
packedDependencies.get().forEach {
def jarTask = project.rootProject.getTasks().findByPath(it)
dependsOn(jarTask)
@@ -0,0 +1,74 @@
package net.minecraftforge.forge.tasks
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import groovy.transform.CompileStatic
import org.eclipse.jgit.api.Git
import javax.annotation.Nullable
import java.util.stream.Collectors
@CompileStatic
class TeamcityRequests {
public static final Gson GSON = new Gson()
@Nullable
static <T> T jsonRequest(TypeToken<T> clazz, String url) throws Exception {
final HttpURLConnection conn = (HttpURLConnection) URI.create(url).toURL().openConnection()
conn.setRequestProperty('Accept', 'application/json')
conn.setReadTimeout(5 * 1000)
conn.setConnectTimeout(5 * 1000)
conn.connect()
if (conn.responseCode !== 200) {
return null
}
try (final InputStream is = conn.getInputStream()) {
GSON.fromJson(new InputStreamReader(is), clazz.getType())
}
}
@Nullable
static Map<String, Build> buildsByCommit() throws IOException {
final Map<String, Build> builds = [:]
jsonRequest(new TypeToken<Builds>() {}, "https://teamcity.minecraftforge.net/guestAuth/app/rest/builds?fields=build:(revisions,number)&locator=buildType:(id:MinecraftForge_MinecraftForge_MinecraftForge_MinecraftForge__Build),count:300,status:SUCCESS")
?.build?.forEach {
it.revisions.revision.each { rev -> builds[rev.version] = it }
}
return builds
}
@Nullable
static String attemptFindBase(File gitPath) {
try (final git = Git.open(gitPath)) {
final Map<String, Build> buildByCommit = buildsByCommit()
for (final commit in git.log().setMaxCount(100).call()) {
// Find the first commit which was built on CI
final build = buildByCommit[commit.id.name]
if (build) {
return build.number
}
}
} catch (Exception ignored) {}
return null
}
static final class Builds {
public List<Build> build
}
static final class Build {
public String number
public Revisions revisions
}
static final class Revisions {
public int count
public List<Revision> revision
}
static final class Revision {
public String version
}
}
@@ -2,13 +2,20 @@ package net.minecraftforge.forge.tasks
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import org.objectweb.asm.ClassReader
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.ClassNode
import java.io.File
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
public class Util {
static final ASM_LEVEL = Opcodes.ASM9
static void init() {
File.metaClass.sha1 = { ->
MessageDigest md = MessageDigest.getInstance('SHA-1')
@@ -143,8 +150,24 @@ public class Util {
}
}
public static String getLatestForgeVersion(mcVersion) {
def json = new JsonSlurper().parseText(new URL("https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json").getText("UTF-8"))
return json.promos["$mcVersion-latest"]
static String getLatestForgeVersion(mcVersion) {
final json = new JsonSlurper().parseText(new URL('https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json').getText('UTF-8'))
final ver = json.promos["$mcVersion-latest"]
ver === null ? null : (mcVersion + '-' + ver)
}
static void processClassNodes(File file, Closure process) {
file.withInputStream { i ->
new ZipInputStream(i).withCloseable { zin ->
ZipEntry zein
while ((zein = zin.nextEntry) != null) {
if (zein.name.endsWith('.class')) {
def node = new ClassNode(ASM_LEVEL)
new ClassReader(zin).accept(node, 0)
process(node)
}
}
}
}
}
}
@@ -0,0 +1,92 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.forge.tasks
import net.minecraftforge.srgutils.MinecraftVersion
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.TaskAction
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
abstract class ValidateDeprecations extends DefaultTask {
@InputFile
abstract RegularFileProperty getInput()
@Input
abstract Property<String> getMcVersion()
ValidateDeprecations() {
this.onlyIf { !System.env.TEAMCITY_VERSION }
}
@TaskAction
protected void exec() {
def mcVer = MinecraftVersion.from(mcVersion.get())
def errors = []
Util.processClassNodes(input.get().asFile) {
processNode(mcVer, errors, it)
}
if (!errors.isEmpty()) {
errors.forEach {
this.logger.error("Deprecated ${it[0]} is marked for removal in ${it[1]} but is not yet removed")
}
throw new IllegalStateException("Found deprecated members marked for removal but not yet removed in ${mcVer}; see log for details")
}
}
protected processNode(MinecraftVersion mcVer, List<String> errors, ClassNode node) {
node.visibleAnnotations?.each { annotation ->
ValidateDeprecations.processAnnotations(annotation, mcVer, errors) {
"class ${node.name}"
}
}
node.fields?.each { field ->
field.visibleAnnotations?.each { annotation ->
ValidateDeprecations.processAnnotations(annotation, mcVer, errors) {
"field ${node.name}#${field.name}"
}
}
}
node.methods?.each { method ->
method.visibleAnnotations?.each { annotation ->
ValidateDeprecations.processAnnotations(annotation, mcVer, errors) {
"method ${node.name}#${method.name}${method.desc}"
}
}
}
}
private static void processAnnotations(AnnotationNode annotation, MinecraftVersion mcVer, List<String> errors, Closure context) {
def values = annotation.values
if (values === null)
return
int forRemoval = values.indexOf('forRemoval')
int since = values.indexOf('since')
if (annotation.desc == 'Ljava/lang/Deprecated;' && forRemoval !== -1 && since !== -1 && values.size() >= 4 && values[forRemoval + 1] === true) {
def oldVersion = MinecraftVersion.from(values[since + 1])
def split = ValidateDeprecations.splitDots(oldVersion.toString())
if (split.length < 2)
return
def removeVersion = MinecraftVersion.from("${split[0]}.${split[1] + 1}")
if (removeVersion <= mcVer)
errors.add([context(), removeVersion])
}
}
private static int[] splitDots(String version) {
String[] pts = version.split('\\.')
int[] values = new int[pts.length]
for (int x = 0; x < pts.length; x++)
values[x] = Integer.parseInt(pts[x])
return values
}
}
@@ -0,0 +1,310 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
import groovy.transform.TupleConstructor
import net.minecraftforge.forge.tasks.InheritanceData
import net.minecraftforge.srgutils.IMappingFile
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional
import org.objectweb.asm.Opcodes
@CompileStatic
abstract class CheckATs extends CheckTask {
@InputFile abstract RegularFileProperty getInheritance()
@InputFiles abstract ConfigurableFileCollection getAts()
@InputFile @Optional abstract RegularFileProperty getMappings()
@Override
void check(Reporter reporter, boolean fix) {
final IMappingFile mappings = mappings.map { RegularFile it -> IMappingFile.load(it.asFile) }.getOrNull()
final inheritance = InheritanceData.parse(this.inheritance.get().asFile)
ats.each {
final lines = process(it, reporter, inheritance)
if (fix) {
it.text = joinBack(lines, inheritance, mappings).join('\n')
}
}
}
private static TreeMap<String, ATParser.Entry> process(File file, Reporter reporter, Map<String, InheritanceData> inheritance) {
final TreeMap<String, ATParser.Entry> lines = ATParser.parse(file.readLines(), reporter)
final Map<String, ATParser.Entry> constructorGroups = [:]
final itr = lines.entrySet().iterator()
final toRemove = []
while (itr.hasNext()) {
final next = itr.next()
String key = next.key
final entry = next.value
if (entry === null) continue
final binaryName = entry.cls.replaceAll('\\.', '/')
// Process Groups, this will remove any entries outside the group that is covered by the group
if (entry.group) {
final jcls = inheritance[binaryName]
if (jcls === null) {
itr.remove()
reporter.report("Invalid group: $key")
} else if ('*' == entry.desc) {
if (!jcls.fields) {
itr.remove()
reporter.report("Invalid group, class has no fields: $key")
} else {
jcls.fields.each { field, value ->
final fkey = entry.cls + ' ' + field
if (accessLevel(value.access) < accessStr(entry.modifier)) {
if (lines.containsKey(fkey)) {
toRemove.add(fkey)
} else if (!entry.existing.contains(fkey)) {
reporter.report("Missing group entry: $fkey")
}
entry.children.add(fkey)
} else if (lines.containsKey(fkey)) {
toRemove.add(fkey)
reporter.report("Found invalid group entry: $fkey")
}
}
entry.existing.findAll { it !in entry.children }.each { println('Removed: ' + it) }
}
} else if ('*()' == entry.desc) {
if (!jcls.methods) {
itr.remove()
reporter.report("Invalid group, class has no methods: $key")
} else {
jcls.methods.each { mtd, value ->
if (mtd.startsWith('<clinit>') || mtd.startsWith('lambda$')) return
key = entry.cls + ' ' + mtd.replace(' ', '')
if (accessLevel(value.access) < accessStr(entry.modifier)) {
if (lines.containsKey(key)) {
toRemove.add(key)
} else if (!entry.existing.contains(key)) {
reporter.report("Missing group entry: $key")
}
entry.children.add(key)
} else if (lines.containsKey(key)) {
toRemove.add(key)
reporter.report("Found invalid group entry: $key")
}
}
entry.existing.findAll { it !in entry.children }.each { println('Removed: ' + it) }
}
} else if ('<init>' == entry.desc) { //Make all public non-abstract subclasses
constructorGroups.put(binaryName, entry)
}
}
// Process normal lines, remove invalid and remove narrowing
else {
def jcls = inheritance.get(binaryName)
if (jcls === null) {
itr.remove()
reporter.report("Invalid: $key")
} else if (entry.desc == '') {
if (accessLevel(jcls.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) {
itr.remove()
reporter.report("Invalid Narrowing: $key")
}
} else if (!entry.desc.contains('(')) {
if (!jcls.fields || !jcls.fields.containsKey(entry.desc)) {
itr.remove()
reporter.report("Invalid: $key")
} else {
final value = jcls.fields[entry.desc]
if (accessLevel(value.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) {
itr.remove()
reporter.report("Invalid Narrowing: $key - ${entry.comment}")
}
}
} else {
final jdesc = entry.desc.replace('(', ' (')
if (!jcls.methods || !jcls.methods.containsKey(jdesc)) {
itr.remove()
reporter.report("Invalid: $key")
} else {
final value = jcls.methods[jdesc]
if (accessLevel(value.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) {
itr.remove()
reporter.report("Invalid Narrowing: $key")
}
}
}
}
}
inheritance.each { tcls, value ->
if (!value.methods || ((value.access & Opcodes.ACC_ABSTRACT) !== 0)) return
String parent = tcls
while (parent !== null) {
constructorGroups[parent]?.tap { entry ->
value.methods.each { mtd, v ->
if (mtd.startsWith('<init>')) {
final child = tcls.replaceAll('/', '\\.') + ' ' + mtd.replace(' ', '')
if (accessLevel(v.access) < 3) {
if (lines.containsKey(child)) {
toRemove.add(child)
} else if (child !in entry.existing) {
reporter.report("Missing group entry: $child")
}
entry.children.add(child)
} else if (lines.containsKey(child)) {
toRemove.add(child)
reporter.report("Found invalid group entry: $child")
}
}
}
}
parent = inheritance[parent]?.superName
}
}
constructorGroups.values().each { entry -> entry.existing.findAll { it !in entry.children }.each{ reporter.report("Found invalid group entry: $it") } }
toRemove.each(lines.&remove)
return lines
}
private static List<String> joinBack(TreeMap<String, ATParser.Entry> lines, Map<String, InheritanceData> inheritance, IMappingFile mappings) {
final data = []
final remapComment = { ATParser.Entry entry ->
if (!mappings || !entry || !entry.desc) return null
final comment = entry.comment?.substring(1)?.trim()
final jsonCls = inheritance.get(entry.cls.replaceAll('\\.', '/'))
final mappingsClass = mappings?.getClass(jsonCls.name)
if (mappingsClass === null) return entry.comment
final idx = entry.desc.indexOf('(')
String mappedName = idx == -1
? mappingsClass.remapField(entry.desc)
: mappingsClass.remapMethod(entry.desc.substring(0, idx), entry.desc.substring(idx))
if (!mappedName) return entry.comment
if (mappedName == '<init>')
mappedName = 'constructor'
if (comment?.startsWith(mappedName))
return '# ' + comment
if (comment && comment.indexOf(' ') !== -1) {
def split = comment.split(' - ').toList()
if (split[0].indexOf(' ') !== -1)
// The first string is more than one word, so append before it
return "# ${mappedName} - ${comment}"
split.remove(0)
return "# ${mappedName} - ${String.join(' - ', split)}"
}
return '# ' + mappedName
}
lines.each { key, value ->
if (!value.group) {
def comment = remapComment.call(value)
data.add(value.modifier + ' ' + key + (comment ? ' ' + comment : ''))
} else {
data.add(('#group ' + value.modifier + ' ' + key + ' ' + (value.comment ?: '')).trim())
value.children.each {
final line = value.modifier + ' ' + it
final entry = ATParser.parseEntry(line)
final comment = remapComment(entry)
data.add(line + (comment ? ' ' + comment : ''))
}
data.add('#endgroup')
}
}
return data
}
static int accessStr(String access) {
if (access.endsWith('-f') || access.endsWith('+f')) return 4
switch (access.toLowerCase()) {
case 'public': return 3
case 'protected': return 2
case 'default': return 1
case 'private': return 0
default: return -1
}
}
static int accessLevel(int access) {
if ((access & Opcodes.ACC_PUBLIC) !== 0) return 3
if ((access & Opcodes.ACC_PROTECTED) !== 0) return 2
if ((access & Opcodes.ACC_PRIVATE) !== 0) return 0
return 1
}
}
@CompileStatic
class ATParser {
static TreeMap<String, Entry> parse(List<String> lines, CheckTask.Reporter reporter) {
TreeMap<String, Entry> outLines = new TreeMap<>()
Entry group = null
for (final line : lines) {
if (line.isEmpty()) continue
if (line.startsWith('#group ')) {
final entry = parseEntry(line.substring(7))
if (entry.desc != '*' && entry.desc != '*()' && entry.desc != '<init>') {
reporter.report("Invalid group: $line", false)
}
entry.group = true
entry.children = []
entry.existing = []
group = entry
if (outLines.containsKey(entry.key)) {
reporter.report("Duplicate group: $line", false)
}
outLines[entry.key] = group
} else if (group !== null) {
if (line.startsWith('#endgroup')) {
group = null
} else {
final key = parseEntry(line).key
group.existing.add(key)
}
} else if (line.startsWith('#endgroup')) {
reporter.report("Invalid group ending: $line", false)
} else if (line.startsWith('#')) {
//Nom
} else {
final entry = parseEntry(line)
if (outLines.containsKey(entry.key)) {
reporter.report("Found duplicate: $line")
continue
}
outLines[entry.key] = entry
}
}
return outLines
}
static Entry parseEntry(String line) {
final idx = line.indexOf('#')
final String comment = idx === -1 ? null : line.substring(idx)
if (idx !== -1) line = line.substring(0, idx - 1)
final data = (line.trim() + ' ').split(' ', -1)
new Entry(data[0], data[1], data[2], comment)
}
@TupleConstructor
static final class Entry {
String modifier, cls, desc, comment
Set<String> existing
TreeSet<String> children
boolean group = false
@Lazy
String key = {cls + (desc.isEmpty() ? '' : ' ' + desc)}()
Object getAt(String key) {
return getProperty(key)
}
}
}
@@ -0,0 +1,93 @@
package net.minecraftforge.forge.tasks.checks
import net.minecraftforge.forge.tasks.Util
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Type
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
abstract class CheckExcs extends CheckTask {
@InputFile abstract RegularFileProperty getBinary()
@InputFiles abstract ConfigurableFileCollection getExcs()
@Override
void check(Reporter reporter, boolean fix) {
final Set<String> known = []
collectKnown(known)
excs.each { f ->
final lines = []
f.eachLine { line ->
def idx = line.indexOf('#')
if (idx == 0 || line.isEmpty()) {
return
}
if (idx != -1) line = line.substring(0, idx - 1)
if (!line.contains('=')) {
reporter.report("Invalid: $line")
return
}
def (String key, String value) = line.split('=', 2)
if (!known.contains(key)) {
reporter.report("Unknown: $line")
return
}
String desc = key.split('\\.', 2)[1]
if (!desc.contains('(')) {
reporter.report("Invalid: $line")
return
}
desc = '(' + desc.split('\\(', 2)[1]
def (exceptions, String args) = value.contains('|') ? value.split('|', 2) : [value, '']
if (args.split(',').length !== Type.getArgumentTypes(desc).length) {
reporter.report("Invalid: $line")
return
}
lines.add(line)
}
if (fix) f.text = lines.sort().join('\n')
}
}
private void collectKnown(Collection<String> known) {
binary.get().asFile.withInputStream { i ->
new ZipInputStream(i).withCloseable { zin ->
final visitor = new ClassVisitor(Util.ASM_LEVEL) {
private String cls
@Override
void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.cls = name
}
@Override
MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
known.add(this.cls + '.' + name + descriptor)
super.visitMethod(access, name, descriptor, signature, exceptions)
}
}
ZipEntry zein
while ((zein = zin.nextEntry) !== null) {
if (zein.name.endsWith('.class')) {
ClassReader reader = new ClassReader(zin)
reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES)
}
}
}
}
}
}
@@ -0,0 +1,9 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
@CompileStatic
enum CheckMode {
CHECK,
FIX
}
@@ -0,0 +1,182 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.Optional
import java.nio.file.Files
import java.nio.file.Path
import java.util.regex.Pattern
@CompileStatic
abstract class CheckPatches extends CheckTask {
private static final Pattern HUNK_START_PATTERN = Pattern.compile('^@@ -[0-9,]* \\+[0-9,_]* @@$')
private static final Pattern WHITESPACE_PATTERN = Pattern.compile('^[+\\-]\\s*$')
private static final Pattern IMPORT_PATTERN = Pattern.compile('^[+\\-]\\s*import.*')
private static final Pattern FIELD_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^=;]*)(=.*)?;\\s*$')
private static final Pattern METHOD_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^(]*)[(]([^)]*)?[)]\\s*[{]\\s*$')
private static final Pattern CLASS_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final[\\s]*)?(class|interface)([^{]*)[{]\\s*$')
private static final Map<String, Integer> ACCESS_MAP = [private: 0, protected: 2, public: 3].tap { it.put(null, 1) }
@InputDirectory abstract DirectoryProperty getPatchDir()
@Input @Optional abstract ListProperty<String> getPatchesWithS2SArtifact()
@Override
void check(Reporter reporter, boolean fix) {
final patchDir = getPatchDir().get().asFile.toPath()
Files.walk(patchDir).withCloseable {
it.filter(Files.&isRegularFile).forEach { path ->
final String relativeName = patchDir.relativize(path).toString()
verifyPatch(path, reporter, fix, relativeName, patchesWithS2SArtifact.get().contains(relativeName.replace('\\', '/')))
}
}
}
def accessChange(previous, current) {
//return ACCESS_MAP[previous] < ACCESS_MAP[current]
return previous != current
}
void verifyPatch(Path patch, Reporter reporter, boolean fix, String patchPath, boolean hasS2SArtifact) {
final oldFixedErrors = reporter.fixed.size()
final lines = Files.readAllLines(patch)
int hunksStart = 0
boolean onlyWhiteSpace = false
final List<String> newLines = []
// First two lines are file name ++/-- and we do not care
newLines.add(lines[0] + '\n')
newLines.add(lines[1] + '\n')
int i
for (i = 2; i < lines.size(); ++i) {
def line = lines[i]
newLines.add(line + '\n')
if (HUNK_START_PATTERN.matcher(line).find()) {
if (onlyWhiteSpace) {
if (!hasS2SArtifact)
reporter.report("Patch contains only white space hunk starting at line ${hunksStart + 1}, file: $patchPath")
int toRemove = i - hunksStart
while (toRemove-- > 0)
newLines.remove(newLines.size() - 1)
}
hunksStart = i
onlyWhiteSpace = true
continue
}
if (line.startsWithAny('+','-')) {
def prefixChange = false
def prevLine = lines[i - 1]
if (line.charAt(0) == (char)'+' && prevLine.charAt(0) == (char)'-') {
def prevTrim = prevLine.substring(1).replaceAll("\\s", "")
def currTrim = line.substring(1).replaceAll("\\s", "")
if (prevTrim == currTrim) {
prefixChange = true
}
def pMatcher = FIELD_PATTERN.matcher(prevLine)
def cMatcher = FIELD_PATTERN.matcher(line)
if (pMatcher.find() && cMatcher.find() &&
pMatcher.group(6) == cMatcher.group(6) && // = ...
pMatcher.group(5) == cMatcher.group(5) && // field name
pMatcher.group(3) == cMatcher.group(3) && // static
(accessChange(pMatcher.group(2), cMatcher.group(2)) || pMatcher.group(4) != cMatcher.group(4))) {
reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false)
}
pMatcher = METHOD_PATTERN.matcher(prevLine)
cMatcher = METHOD_PATTERN.matcher(line)
if (pMatcher.find() && cMatcher.find() &&
pMatcher.group(6) == cMatcher.group(6) && // params
pMatcher.group(5) == cMatcher.group(5) && // <T> void name
pMatcher.group(3) == cMatcher.group(3) && // static
(accessChange(pMatcher.group(2), cMatcher.group(2)) || pMatcher.group(4) != cMatcher.group(4))) {
reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false)
}
pMatcher = CLASS_PATTERN.matcher(prevLine)
cMatcher = CLASS_PATTERN.matcher(line)
if (pMatcher.find() && cMatcher.find() &&
pMatcher.group(6) == cMatcher.group(6) && // ClassName<> extends ...
pMatcher.group(5) == cMatcher.group(5) && // class | interface
pMatcher.group(3) == cMatcher.group(3) && // static
(accessChange(pMatcher.group(2), cMatcher.group(2)) || pMatcher.group(4) != cMatcher.group(4))) {
reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false)
}
}
if (line.charAt(0) == (char)'-' && i + 1 < lines.size()) {
final nextLine = lines[i + 1]
if (nextLine.charAt(0) == (char)'+') {
final nextTrim = nextLine.substring(1).replaceAll("\\s", "")
final currTrim = line.substring(1).replaceAll("\\s", "")
if (nextTrim == currTrim) {
prefixChange = true
}
}
}
final isWhiteSpaceChange = WHITESPACE_PATTERN.matcher(line).find()
if (!prefixChange && !isWhiteSpaceChange) {
onlyWhiteSpace = hasS2SArtifact && IMPORT_PATTERN.matcher(line).find()
} else if (isWhiteSpaceChange) {
final prevLineChange = prevLine.startsWithAny('+','-')
final nextLineChange = i + 1 < lines.size() && lines[i + 1].startsWithAny('+','-')
if (!prevLineChange && !nextLineChange) {
reporter.report("Patch contains white space change in valid hunk at line ${i + 1}, file: $patchPath", false)
}
}
if (line.contains('\t')) {
reporter.report("Patch contains tabs on line ${i + 1}, file: $patchPath")
line = line.replaceAll('\t', ' ')
newLines.remove(newLines.size() - 1)
newLines.add(line + '\n')
}
if (IMPORT_PATTERN.matcher(line).find() && !hasS2SArtifact) {
reporter.report("Patch contains import change on line ${i + 1}, file: $patchPath", false)
}
}
}
if (onlyWhiteSpace) {
if (!hasS2SArtifact)
reporter.report("Patch contains only white space hunk starting at line ${hunksStart + 1}, file: $patchPath")
def toRemove = i - hunksStart;
while (toRemove-- > 0)
newLines.remove(newLines.size() - 1)
}
if ((reporter.fixed.size() > oldFixedErrors && fix) || hasS2SArtifact) {
if (newLines.size() <= 2) {
logger.lifecycle("Patch is now empty removing, file: {}", patchPath)
Files.delete(patch)
}
else {
if (!hasS2SArtifact)
logger.lifecycle("*** Updating patch file. Please run setup then genPatches again. ***")
Files.newBufferedWriter(patch).withCloseable {
newLines.each { l -> it.write(l) }
}
}
}
}
}
@@ -0,0 +1,110 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
import net.minecraftforge.forge.tasks.Annotatable
import net.minecraftforge.forge.tasks.InheritanceData
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
@CompileStatic
abstract class CheckSAS extends CheckTask {
@InputFile abstract RegularFileProperty getInheritance()
@InputFiles abstract ConfigurableFileCollection getSass()
@Override
void check(Reporter reporter, boolean fix) {
final inheritance = InheritanceData.parse(this.inheritance.get().asFile)
sass.each { f ->
final lines = []
f.eachLine { line ->
if (line[0] == '\t') return // Skip any tabbed lines, those are ones we add
final idx = line.indexOf('#')
if (idx == 0 || line.isEmpty()) {
lines.add(line)
return
}
def comment = idx == -1 ? null : line.substring(idx)
if (idx != -1) line = line.substring(0, idx - 1)
final spl = (line.trim().replace('(', ' (') + ' ').split(' ', -1)
def (String cls, String name, String desc) = [spl[0], spl[1], spl[2]]
cls = cls.replaceAll('\\.', '/')
if (inheritance[cls] === null) {
reporter.report("Invalid: $line")
} else if (name.isEmpty()) { //Class SAS
final toAdd = []
final clsSided = isSided(inheritance[cls])
boolean sided = clsSided
/* TODO: MergeTool doesn't do fields
if (json[cls]['fields'] != null) {
for (entry in json[cls]['fields']) {
if (isSided(entry.value)) {
sided = true
toAdd.add('\t' + cls + ' ' + entry.key)
}
}
} */
final clsInh = inheritance[cls]
if (clsInh.methods) {
for (entry in clsInh.methods) {
if (isSided(entry.value)) {
sided = true
toAdd.add('\t' + cls + ' ' + entry.key.replaceAll(' ', ''))
findChildMethods(inheritance, cls, entry.key).each { lines.add('\t' + it) }
findChildMethods(inheritance, cls, entry.key).each { println(line + ' -- ' + it) }
} else if (clsSided) {
findChildMethods(inheritance, cls, entry.key).each { lines.add('\t' + it) }
findChildMethods(inheritance, cls, entry.key).each { println(line + ' -- ' + it) }
}
}
}
if (sided) {
lines.add(cls + (comment == null ? '' : ' ' + comment))
lines.addAll(toAdd.sort())
} else {
reporter.report("Invalid: $line")
}
} else if (desc.isEmpty()) { // Fields
/* TODO: MergeTool doesn't do fields
if (json[cls]['fields'] != null && isSided(json[cls]['fields'][name]))
lines.add(cls + ' ' + name + (comment == null ? '' : ' ' + comment))
else */
reporter.report("Invalid: $line")
} else { // Methods
final clsInh = inheritance[cls]
if (clsInh.methods === null || !isSided(clsInh.methods[name + ' ' + desc]))
reporter.report("Invalid: $line")
else {
lines.add(cls + ' ' + name + desc + (comment == null ? '' : ' ' + comment))
findChildMethods(inheritance, cls, name + ' ' + desc).each { println(line + ' -- ' + it) }
findChildMethods(inheritance, cls, name + ' ' + desc).each { lines.add('\t' + it) }
}
}
}
if (fix) f.text = lines.join('\n')
}
}
protected static isSided(Annotatable annotatable) {
if (annotatable === null) return false
for (ann in annotatable.annotations) {
if ('Lnet/minecraftforge/api/distmarker/OnlyIn;' == ann.desc)
return true
}
return false
}
protected static findChildMethods(Map<String, InheritanceData> json, String cls, String desc) {
return json.values().findAll{ it.methods != null && it.methods[desc] != null && it.methods[desc].override == cls && isSided(it.methods[desc]) }
.collect { it.name + ' ' + desc.replace(' ', '') } as TreeSet
}
}
@@ -0,0 +1,109 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
import net.minecraftforge.forge.tasks.Util
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.logging.LogLevel
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.VerificationTask
@CompileStatic
abstract class CheckTask extends DefaultTask implements VerificationTask {
@Input
abstract Property<CheckMode> getMode()
private boolean ignoreFailures = false
@Input
@Override
boolean getIgnoreFailures() {
return ignoreFailures
}
@Override
void setIgnoreFailures(boolean ignoreFailures) {
this.ignoreFailures = ignoreFailures
}
@TaskAction
void run() {
Util.init()
final doFix = getMode().get() === CheckMode.FIX
final Reporter reporter = new Reporter(doFix)
check(reporter, doFix)
if (reporter.messages) {
if (getMode().get() === CheckMode.CHECK) {
logger.error("Check task '{}' found errors:\n{}", name, reporter.messages.join('\n'))
if (!ignoreFailures) {
throw new IllegalArgumentException("${reporter.messages.size()} errors were found!")
}
} else {
if (logger.isEnabled(LogLevel.DEBUG)) {
logger.warn("Check task '{}' found {} errors and fixed {}:\n{}", name, reporter.messages.size(), reporter.fixed.size(), reporter.fixed.join('\n'))
} else {
logger.warn("Check task '{}' found {} errors and fixed {}.", name, reporter.messages.size(), reporter.fixed.size())
}
if (reporter.notFixed) {
logger.error('{} errors could not be fixed:\n{}', reporter.notFixed.size(), reporter.notFixed.join('\n'))
if (!ignoreFailures) {
throw new IllegalArgumentException("${reporter.notFixed.size()} errors which cannot be fixed were found!")
}
}
}
}
}
abstract void check(Reporter reporter, boolean fix)
@CompileStatic
static final class Reporter {
final boolean trackFixed
Reporter(boolean trackFixed) {
this.trackFixed = trackFixed
}
public final List<String> messages = []
public final List<String> fixed = []
public final List<String> notFixed = []
void report(String message, boolean canBeFixed = true) {
messages.add(message)
if (trackFixed) {
if (canBeFixed) {
fixed.add(message)
} else {
notFixed.add(message)
}
}
}
}
static <T extends CheckTask> void registerTask(TaskContainer tasks, String taskName, @DelegatesTo.Target('type') Class<T> clazz,
@DelegatesTo(genericTypeIndex = 0, target = 'type') Closure configuration) {
taskName = taskName.capitalize()
tasks.register("check$taskName", clazz) { CheckTask task ->
configuration.setDelegate((T) task)
configuration.call(task)
task.mode.set(CheckMode.CHECK)
task.group = 'checks'
}
tasks.named('checkAll').configure { it.dependsOn("check$taskName") }
tasks.register("checkAndFix$taskName", clazz) { CheckTask task ->
configuration.setDelegate((T) task)
configuration.call(task)
task.mode.set(CheckMode.FIX)
task.group = 'checks'
}
tasks.named('checkAllAndFix').configure { it.dependsOn("checkAndFix$taskName") }
}
}
+3 -1
View File
@@ -7,7 +7,7 @@ Contributing to Forge
3) Follow the code style of the class you're working in (braces on newlines & spaces instead of tabs in Forge classes, inline brackets in patches, etc).
For more information, refer to [the wiki](https://github.com/MinecraftForge/MinecraftForge/wiki/If-you-want-to-contribute-to-Forge)
For more information, refer to [the wiki][Wiki].
Contributor License Agreement
@@ -16,3 +16,5 @@ Contributor License Agreement
- You assign copyright ownership of your contributions to the patches codebase (everything under patches) to Forge, where it will be licensed under the LGPLv2.1 license.
"Forge" is Forge Development LLC, a legally incorporated entity in Oregon, USA.
[Wiki]: https://github.com/MinecraftForge/MinecraftForge/wiki/If-you-want-to-contribute-to-Forge
+28 -19
View File
@@ -2,17 +2,16 @@
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.19-&query=$.promos["1.19.2-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)](https://files.minecraftforge.net)
[![Latest Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Latest&prefix=1.19.2-&query=$.promos["1.19.2-latest"]&color=blue&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYnVnIGZhLXctMTYiIGFyaWEtaGlkZGVuPSJ0cnVlIiBkYXRhLWljb249ImJ1ZyIgZGF0YS1wcmVmaXg9ImZhcyIgZm9jdXNhYmxlPSJmYWxzZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUxMS45ODggMjg4LjljLS40NzggMTcuNDMtMTUuMjE3IDMxLjEtMzIuNjUzIDMxLjFINDI0djE2YzAgMjEuODY0LTQuODgyIDQyLjU4NC0xMy42IDYxLjE0NWw2MC4yMjggNjAuMjI4YzEyLjQ5NiAxMi40OTcgMTIuNDk2IDMyLjc1OCAwIDQ1LjI1NS0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDBsLTU0LjczNi01NC43MzZDMzQ1Ljg4NiA0NjcuOTY1IDMxNC4zNTEgNDgwIDI4MCA0ODBWMjM2YzAtNi42MjctNS4zNzMtMTItMTItMTJoLTI0Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0NGMtMzQuMzUxIDAtNjUuODg2LTEyLjAzNS05MC42MzYtMzIuMTA4bC01NC43MzYgNTQuNzM2Yy0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDAtMTIuNDk2LTEyLjQ5Ny0xMi40OTYtMzIuNzU4IDAtNDUuMjU1bDYwLjIyOC02MC4yMjhDOTIuODgyIDM3OC41ODQgODggMzU3Ljg2NCA4OCAzMzZ2LTE2SDMyLjY2NkMxNS4yMyAzMjAgLjQ5MSAzMDYuMzMuMDEzIDI4OC45LS40ODQgMjcwLjgxNiAxNC4wMjggMjU2IDMyIDI1Nmg1NnYtNTguNzQ1bC00Ni42MjgtNDYuNjI4Yy0xMi40OTYtMTIuNDk3LTEyLjQ5Ni0zMi43NTggMC00NS4yNTUgMTIuNDk4LTEyLjQ5NyAzMi43NTgtMTIuNDk3IDQ1LjI1NiAwTDE0MS4yNTUgMTYwaDIyOS40ODlsNTQuNjI3LTU0LjYyN2MxMi40OTgtMTIuNDk3IDMyLjc1OC0xMi40OTcgNDUuMjU2IDAgMTIuNDk2IDEyLjQ5NyAxMi40OTYgMzIuNzU4IDAgNDUuMjU1TDQyNCAxOTcuMjU1VjI1Nmg1NmMxNy45NzIgMCAzMi40ODQgMTQuODE2IDMxLjk4OCAzMi45ek0yNTcgMGMtNjEuODU2IDAtMTEyIDUwLjE0NC0xMTIgMTEyaDIyNEMzNjkgNTAuMTQ0IDMxOC44NTYgMCAyNTcgMHoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=
)](https://files.minecraftforge.net) [![Discord](https://img.shields.io/discord/313125603924639766.svg?color=%237289da&label=Discord&logo=discord&logoColor=%237289da)](https://discord.gg/UvedJ9m) [![Support](https://img.shields.io/badge/Patreon-Support-orange.svg?logo=Patreon)](https://www.patreon.com/LexManos)
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.19.4-&query=$.promos["1.19.4-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.19.4-&query=$.promos["1.19.4-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.4 | Legacy |
| 1.19.2 | Legacy |
| 1.19.x | Active |
| 1.18.x | LTS |
* [Download]
* [Forum]
@@ -21,7 +20,7 @@ Forge is a free, open-source modding API all of your favourite mods use!
# Installing Forge
Go to [the Forge website](https://files.minecraftforge.net)
Go to [the Forge website][Download]
and select the Minecraft version you wish to get Forge for from the list.
You can download the installer for the *Recommended Build* or the
@@ -30,38 +29,48 @@ You can download the installer for the *Recommended Build* or the
into your vanilla launcher environment, where you can then create a new
profile using that version and play the game!
For support and questions, visit [the Support Forum](https://www.minecraftforge.net/forum/forum/18-support-bug-reports/) or [the Forge Discord server](https://discord.gg/forge).
[Here is a short video from Rorax showing how to install and setup Forge.](https://www.youtube.com/watch?v=lB3ArN_-3Oc)
For support and questions, visit [the Support Forum][Support-Forum] or [the Forge Discord server][Discord].
# Creating Mods
[See the "Getting Started" section in the Forge Documentation](https://mcforge.readthedocs.io/en/latest/gettingstarted/).
[See the "Getting Started" section in the Forge Documentation][Getting-Started].
# Contribute to Forge
If you wish to actually inspect Forge, submit PRs or otherwise work
with Forge itself, you're in the right place!
[See the guide to setting up a Forge workspace](http://mcforge.readthedocs.io/en/latest/forgedev/).
[See the guide to setting up a Forge workspace][ForgeDev].
### Pull requests
[See the "Making Changes and Pull Requests" section in the Forge documentation](https://mcforge.readthedocs.io/en/latest/forgedev/#making-changes-and-pull-requests).
[See the "Making Changes and Pull Requests" section in the Forge documentation][Pull-Requests].
Please read the contributing guidelines found [here](CONTRIBUTING.md) before making a pull request.
Please read the contributing guidelines found [here][Contributing] before making a pull request.
### Contributor License Agreement
We require all contributors to acknowledge the [Forge Contributor License Agreement](https://cla-assistant.io/MinecraftForge/MinecraftForge).
We require all contributors to acknowledge the [Forge Contributor License Agreement][CLA].
Please ensure you have a valid email address associated with your GitHub account to do this. If you have previously
signed it, you should be OK.
#### Donate
*Forge is a large project with many collaborators working on it around the clock. Forge is and will always remain free
to use and modify. However, it costs money to run such a large project as this, so please consider
[becoming a patron](https://www.patreon.com/LexManos).*
[becoming a patron][Patreon].*
[Contributing]: ./CONTRIBUTING.md
[CLA]: https://cla-assistant.io/MinecraftForge/MinecraftForge
[Download]: https://files.minecraftforge.net/
[Forum]: https://www.minecraftforge.net/forum/
[Discord]: https://discord.gg/UvedJ9m
[Documentation]: https://mcforge.readthedocs.io
[Forum]: https://forums.minecraftforge.net/
[Support-Forum]: https://forums.minecraftforge.net/forum/18-support-bug-reports/
[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
[Patreon]: https://www.patreon.com/LexManos
+15 -8
View File
@@ -1,7 +1,7 @@
Getting Support Surrounding Forge
=================================
**If you do not want to read the following wall of text, no matter its relevance for helping get your issue resolved quickly, please refer to the [Forge Forums](http://www.minecraftforge.net/forum/forum/18-support-bug-reports/) for support.**
**If you do not want to read the following wall of text, no matter its relevance for helping get your issue resolved quickly, please refer to the [Forge Forums][Bug-Reports] for support.**
Before reporting an issue on this repository's bug tracker, please carefully read this document. There are various resources that are more likely to be able to help you with certain kinds of issues. This document provides instructions on how to determine which place best to report to.
@@ -44,7 +44,7 @@ You can generally tell pretty quickly what caused the error at the head of your
1. If you can find the name of a mod in the trace, that mod most likely is the culprit.
- Be aware that you may find multiple mods as participants of a crash. In that case, you can either report to both or try to reason (using the names in the stacktrace) about which mod is at fault.
2. If you can only find Forge (usually starting with `net.minecraftforge`) alongside Vanilla (usually starting with `net.minecraft`) references, chances are that you have found a bug in Forge. In this case, use one of the various [support channels](#forge-support) listed below.
- The occurence of a single call to Forge somewhere in the stacktrace *does not* warrant a report to Forge if there are mods present in the crash report.
- The occurrence of a single call to Forge somewhere in the stacktrace *does not* warrant a report to Forge if there are mods present in the crash report.
3. If there's no mention of Forge or a mod, but any Vanilla class, you might have found a Vanilla bug. You should probably still report it via the Forge channels first, however, due the nature of modded environments and the rarity of Vanilla crash bugs.
4. Always check whether there's a mention of coremods at the beginning of a crash report. If there is and you can't find any clear culprit, consider reporting to these coremods first, since they are free to change any of the code executed in your environment.
@@ -74,10 +74,10 @@ Please note that this list has to be considered incomplete as there is a large n
If you're certain you've found an issue that is directly caused by Forge, you have the following three platforms available for dealing with it, with decreasing relevance for end-users:
1. **Forge Forums:** There's a dedicated [Support & Bug Report](http://www.minecraftforge.net/forum/forum/18-support-bug-reports/) section on the Forge forums, which is led by team of moderators and volunteers who will try to deal with your reports as quickly as possible. *As a player you should probably report here first.*
- For modders, there also is a separate [Modder Support](http://www.minecraftforge.net/forum/forum/70-modder-support/) section where you can ask all your development-related questions. *You should prefer this section for coding help.*
2. **Forge Discord Server:** You can find a lot of help on the official [Discord Server](https://discord.gg/forge), especially smaller issues that you quickly need help with. *Be aware that the channel is more suited towards development questions, so while you may get support there as a player, you should almost always prefer the forums.*
3. **Forge Issue Tracker:** If you're absolutely certain that you've found an issue in the Forge codebase (e.g. you're a modder yourself and have analyzed the stacktrace and relevant code), don't refrain from reporting it on this repository's [issue tracker](https://github.com/MinecraftForge/MinecraftForge/issues). *Please do not use the issues as a means of getting coding help. For developers, the IRC and the [official documentation](https://mcforge.readthedocs.io) are the best sources of information.*
1. **Forge Forums:** There's a dedicated [Support & Bug Report][Bug-Reports] section on the Forge forums, which is led by team of moderators and volunteers who will try to deal with your reports as quickly as possible. *As a player you should probably report here first.*
- For modders, there also is a separate [Modder Support][Modder-Support] section where you can ask all your development-related questions. *You should prefer this section for coding help.*
2. **Forge Discord Server:** You can find a lot of help on the official [Discord Server][Discord], especially smaller issues that you quickly need help with. *Be aware that the channel is more suited towards development questions, so while you may get support there as a player, you should almost always prefer the forums.*
3. **Forge Issue Tracker:** If you're absolutely certain that you've found an issue in the Forge codebase (e.g. you're a modder yourself and have analyzed the stacktrace and relevant code), don't refrain from reporting it on this repository's [issue tracker](https://github.com/MinecraftForge/MinecraftForge/issues). *Please do not use the issues as a means of getting coding help. For developers, the IRC and the [official documentation][Documentation] are the best sources of information.*
### Mod Support
@@ -99,7 +99,7 @@ Now that you've identified the source of your issue and decided on the correct s
### Preventing Duplicates and Unnecessary Reports
Use the target issue tracker's search functionality to look for issues which have descriptions similar to yours, but make sure to also search for already closed issues. If you can find one, verify it is in fact the same (or a very similar issue) and simply comment on it if you have anything else to contribute to the discussion, such as other steps to reproduce it. *Do not create duplicates of an existing issue, since it only puts extra effort on maintainers to recognize and close/mark them.*
Additionally, always check the *latest* changelogs of Forge and participating mods to find out if the bug was already noticed and fixed by the developer. Often the maintainer may only support newer versions of the game, so also check the changelogs for versions of Minecraft you don't actually actively play. If the issue was adressed already, *do not report it*, even if the fix only exists for a newer version of the game, since the developer probably had a good reason to do so. If your issue only exists with versions of a mod that are for very old versions of the game, also refrain from reporting them, considering most modders only support the two latest versions of Minecraft, if at all.
Additionally, always check the *latest* changelogs of Forge and participating mods to find out if the bug was already noticed and fixed by the developer. Often the maintainer may only support newer versions of the game, so also check the changelogs for versions of Minecraft you don't actually actively play. If the issue was addressed already, *do not report it*, even if the fix only exists for a newer version of the game, since the developer probably had a good reason to do so. If your issue only exists with versions of a mod that are for very old versions of the game, also refrain from reporting them, considering most modders only support the two latest versions of Minecraft, if at all.
### Required Information
If you're certain that your issue went unnoticed up until now, you can get to actually writing up the report. Below you can find a list of the *required* information any bug report should contain:
@@ -144,4 +144,11 @@ After you have written your bug report, it is time to *wait*. Please be *patient
Every project has a different way of dealing with issues, so don't wonder if there's no comment on it, but somebody "labels" it with certain markers. This helps developers to more easily keep track of bugs and priorities. When any further input from you is required, you will be asked specifically. Do not bug anyone through other channels about resolving your issue.
Once your issue is closed, you will know whether it was valid or turned out to be irreproducible. If you included all of the data from above in your report, chances are that the latter won't be the case.
Once your issue is closed, you will know whether it was valid or turned out to be unreproducible. If you included all of the data from above in your report, chances are that the latter won't be the case.
[Bug-Reports]: https://forums.minecraftforge.net/forum/18-support-bug-reports/
[Modder-Support]: https://forums.minecraftforge.net/forum/70-modder-support/
[Discord]: https://discord.minecraftforge.net/
[Documentation]: https://docs.minecraftforge.net/
@@ -5,33 +5,93 @@
package net.minecraftforge.fml;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
public class CrashReportCallables {
public class CrashReportCallables
{
private static final Logger LOGGER = LogUtils.getLogger();
private static final List<ISystemReportExtender> crashCallables = Collections.synchronizedList(new ArrayList<>());
/**
* Register a custom {@link ISystemReportExtender}
*/
public static void registerCrashCallable(ISystemReportExtender callable)
{
crashCallables.add(callable);
}
public static void registerCrashCallable(String headerName, Supplier<String> reportGenerator) {
registerCrashCallable(new ISystemReportExtender() {
/**
* Register a {@link ISystemReportExtender system report extender} with the given header name and content
* generator, which will always be appended to the system report
* @param headerName The name of the system report entry
* @param reportGenerator The report generator to be called when a crash report is built
*/
public static void registerCrashCallable(String headerName, Supplier<String> reportGenerator)
{
registerCrashCallable(new ISystemReportExtender()
{
@Override
public String getLabel() {
public String getLabel()
{
return headerName;
}
@Override
public String get() {
public String get()
{
return reportGenerator.get();
}
});
}
public static List<ISystemReportExtender> allCrashCallables() {
/**
* Register a {@link ISystemReportExtender system report extender} with the given header name and content
* generator, which will only be appended to the system report when the given {@link BooleanSupplier} returns true
* @param headerName The name of the system report entry
* @param reportGenerator The report generator to be called when a crash report is built
* @param active The supplier of the flag to be checked when a crash report is built
*/
public static void registerCrashCallable(String headerName, Supplier<String> reportGenerator, BooleanSupplier active)
{
registerCrashCallable(new ISystemReportExtender()
{
@Override
public String getLabel()
{
return headerName;
}
@Override
public String get()
{
return reportGenerator.get();
}
@Override
public boolean isActive()
{
try
{
return active.getAsBoolean();
}
catch (Throwable t)
{
LOGGER.warn("CrashCallable '{}' threw an exception while checking the active flag, disabling", headerName, t);
return false;
}
}
});
}
public static List<ISystemReportExtender> allCrashCallables()
{
return List.copyOf(crashCallables);
}
}
@@ -11,9 +11,11 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import com.google.common.base.Stopwatch;
@@ -53,17 +55,29 @@ public class DeferredWorkQueue
public void runTasks() {
if (tasks.isEmpty()) return;
LOGGER.debug(LOADING, "Dispatching synchronous work for work queue {}: {} jobs", modLoadingStage, tasks.size());
RuntimeException aggregate = new RuntimeException();
Stopwatch timer = Stopwatch.createStarted();
tasks.forEach(t->makeRunnable(t, Runnable::run));
tasks.forEach(t -> makeRunnable(t, Runnable::run, aggregate));
timer.stop();
LOGGER.debug(LOADING, "Synchronous work queue completed in {}", timer);
if (aggregate.getSuppressed().length > 0) {
LOGGER.fatal(
LOADING,
"Synchronous work queue completed exceptionally in {}, see suppressed exceptions for details:",
timer,
aggregate
);
//TODO 1.20: throw aggregate exception
} else {
LOGGER.debug(LOADING, "Synchronous work queue completed in {}", timer);
}
}
private static void makeRunnable(TaskInfo ti, Executor executor) {
private static void makeRunnable(TaskInfo ti, Executor executor, RuntimeException aggregate) {
executor.execute(() -> {
Stopwatch timer = Stopwatch.createStarted();
ModLoadingContext.get().setActiveContainer(ti.owner);
try {
ti.future.exceptionally(t -> captureException(ti.owner.getModId(), aggregate, t));
ti.task.run();
} finally {
ModLoadingContext.get().setActiveContainer(null);
@@ -75,13 +89,38 @@ public class DeferredWorkQueue
});
}
private static <T> T captureException(String modId, RuntimeException aggregate, Throwable throwable) {
if (throwable instanceof CompletionException ce) {
throwable = ce.getCause();
}
aggregate.addSuppressed(throwable);
LOGGER.error("Mod '{}' encountered an error in a deferred task:", modId, throwable);
return null;
}
public CompletableFuture<Void> enqueueWork(final ModContainer modInfo, final Runnable work) {
return CompletableFuture.runAsync(work, r->tasks.add(new TaskInfo(modInfo, r)));
return enqueueWork(modInfo, taskInfo -> CompletableFuture.runAsync(work, r -> taskInfo.task = r));
}
public <T> CompletableFuture<T> enqueueWork(final ModContainer modInfo, final Supplier<T> work) {
return CompletableFuture.supplyAsync(work, r->tasks.add(new TaskInfo(modInfo, r)));
return enqueueWork(modInfo, taskInfo -> CompletableFuture.supplyAsync(work, r -> taskInfo.task = r));
}
record TaskInfo(ModContainer owner, Runnable task) {}
private <T> CompletableFuture<T> enqueueWork(final ModContainer modInfo, Function<TaskInfo, CompletableFuture<T>> futureGen) {
TaskInfo taskInfo = new TaskInfo(modInfo);
CompletableFuture<T> future = futureGen.apply(taskInfo);
taskInfo.future = future;
tasks.add(taskInfo);
return future;
}
private static class TaskInfo {
private final ModContainer owner;
private Runnable task;
private CompletableFuture<?> future;
private TaskInfo(ModContainer owner) {
this.owner = owner;
}
}
}
@@ -10,4 +10,9 @@ import java.util.function.Supplier;
public interface ISystemReportExtender extends Supplier<String>
{
String getLabel();
default boolean isActive()
{
return true;
}
}
@@ -22,13 +22,7 @@ import net.minecraftforge.forgespi.locating.IModFile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
@@ -88,6 +82,7 @@ public class ModLoader
private boolean loadingStateValid;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private final Optional<Consumer<String>> statusConsumer = StartupMessageManager.modLoaderConsumer();
private final Set<IModLoadingState> completedStates = new HashSet<>();
private ModList modList;
private ModLoader()
@@ -206,6 +201,7 @@ public class ModLoader
statusConsumer.ifPresent(c->c.accept(state.message().apply(this.modList)));
state.inlineRunnable().ifPresent(a->a.accept(this.modList));
state.buildTransition(syncExecutor, parallelExecutor).ifPresent(t->waitForTransition(state, syncExecutor, ticker, t));
completedStates.add(state);
}
private void dispatchAndHandleError(IModLoadingState state, ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor, final Runnable ticker, Function<Executor, CompletableFuture<Void>> preSyncTask, Function<Executor, CompletableFuture<Void>> postSyncTask) {
@@ -216,6 +212,7 @@ public class ModLoader
statusConsumer.ifPresent(c->c.accept(state.message().apply(this.modList)));
state.inlineRunnable().ifPresent(a->a.accept(this.modList));
state.buildTransition(syncExecutor, parallelExecutor, preSyncTask, postSyncTask).ifPresent(t->waitForTransition(state, syncExecutor, ticker, t));
completedStates.add(state);
}
private void waitForTransition(final IModLoadingState state, final ModWorkManager.DrivenExecutor syncExecutor, final Runnable ticker, final CompletableFuture<Void> transition) {
@@ -256,10 +253,22 @@ public class ModLoader
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (containers.size() != modInfoMap.size()) {
var modIds = modInfoMap.values().stream().map(IModInfo::getModId).sorted().collect(Collectors.toList());
var containerIds = containers.stream().map(c -> c != null ? c.getModId() : "(null)").sorted().collect(Collectors.toList());
LOGGER.fatal(LOADING,"File {} constructed {} mods: {}, but had {} mods specified: {}",
modFile.getFilePath(),
containers.size(), containers.stream().map(c -> c != null ? c.getModId() : "(null)").sorted().collect(Collectors.toList()),
modInfoMap.size(), modInfoMap.values().stream().map(IModInfo::getModId).sorted().collect(Collectors.toList()));
containers.size(), containerIds,
modInfoMap.size(), modIds);
var missingClasses = new ArrayList<>(modIds);
missingClasses.removeAll(containerIds);
LOGGER.fatal(LOADING, "The following classes are missing, but are reported in the mods.toml: {}", missingClasses);
var missingMods = new ArrayList<>(containerIds);
missingMods.removeAll(modIds);
LOGGER.fatal(LOADING, "The following mods are missing, but have classes in the jar: {}", missingMods);
loadingExceptions.add(new ModLoadingException(null, ModLoadingStage.CONSTRUCT, "fml.modloading.missingclasses", null, modFile.getFilePath()));
}
// remove errored mod containers
@@ -290,6 +299,11 @@ public class ModLoader
return get().loadingStateValid;
}
public boolean hasCompletedState(final String stateName) {
IModLoadingState state = stateManager.findState(stateName);
return completedStates.contains(state);
}
public <T extends Event & IModBusEvent> void runEventGenerator(Function<ModContainer, T> generator) {
if (!loadingStateValid) {
LOGGER.error("Cowardly refusing to send event generator to a broken mod state");
@@ -305,6 +319,17 @@ public class ModLoader
}
ModList.get().forEachModInOrder(mc -> mc.acceptEvent(e));
}
public <T extends Event & IModBusEvent> T postEventWithReturn(T e) {
if (!loadingStateValid) {
LOGGER.error("Cowardly refusing to send event {} to a broken mod state", e.getClass().getName());
return e;
}
ModList.get().forEachModInOrder(mc -> mc.acceptEvent(e));
return e;
}
public <T extends Event & IModBusEvent> void postEventWrapContainerInModOrder(T event) {
postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null));
}
public <T extends Event & IModBusEvent> void postEventWithWrapInModOrder(T e, BiConsumer<ModContainer, T> pre, BiConsumer<ModContainer, T> post) {
if (!loadingStateValid) {
LOGGER.error("Cowardly refusing to send event {} to a broken mod state", e.getClass().getName());
@@ -39,4 +39,13 @@ public class ModStateManager {
nodes.forEach(n->graph.putEdge(lookup.getOrDefault(n.previous(), dummy), n));
return TopologicalSort.topologicalSort(graph, Comparator.comparingInt(nodes::indexOf)).stream().filter(st->st!=dummy).toList();
}
public IModLoadingState findState(final String stateName) {
return stateMap.values()
.stream()
.flatMap(Collection::stream)
.filter(mls -> mls.name().equals(stateName))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown IModLoadingState: " + stateName));
}
}
@@ -66,8 +66,12 @@ public class ConfigTracker {
private void closeConfig(final ModConfig config, final Path configBasePath) {
if (config.getConfigData() != null) {
LOGGER.trace(CONFIG, "Closing config file type {} at {} for {}", config.getType(), config.getFileName(), config.getModId());
config.save();
// stop the filewatcher before we save the file and close it, so reload doesn't fire
config.getHandler().unload(configBasePath, config);
var unloading = IConfigEvent.unloading(config);
if (unloading != null)
config.fireEvent(unloading);
config.save();
config.setConfigData(null);
}
}
@@ -10,8 +10,11 @@ import net.minecraftforge.fml.Bindings;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
public interface IConfigEvent {
record ConfigConfig(Function<ModConfig, IConfigEvent> loading, Function<ModConfig, IConfigEvent> reloading) {}
record ConfigConfig(Function<ModConfig, IConfigEvent> loading, Function<ModConfig, IConfigEvent> reloading, @Nullable Function<ModConfig, IConfigEvent> unloading) {}
ConfigConfig CONFIGCONFIG = Bindings.getConfigConfiguration().get();
static IConfigEvent reloading(ModConfig modConfig) {
@@ -20,6 +23,9 @@ public interface IConfigEvent {
static IConfigEvent loading(ModConfig modConfig) {
return CONFIGCONFIG.loading().apply(modConfig);
}
@Nullable static IConfigEvent unloading(ModConfig modConfig) {
return CONFIGCONFIG.unloading() == null ? null : CONFIGCONFIG.unloading().apply(modConfig);
}
ModConfig getConfig();
@SuppressWarnings("unchecked")
@@ -69,7 +69,7 @@ public class FMLConfig
LOGGER.trace(CORE, "Version check is {}", FMLConfig.runVersionCheck());
LOGGER.trace(CORE, "Default config paths at {}", FMLConfig.defaultConfigPath());
}
FMLPaths.getOrCreateGameRelativePath(Paths.get(FMLConfig.defaultConfigPath()), "default config directory");
FMLPaths.getOrCreateGameRelativePath(Paths.get(FMLConfig.defaultConfigPath()));
}
public static boolean splashScreenEnabled() {
@@ -9,6 +9,8 @@ import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.IEnvironment;
import org.slf4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@@ -57,9 +59,13 @@ public enum FMLPaths
for (FMLPaths path : FMLPaths.values())
{
path.absolutePath = rootPath.resolve(path.relativePath).toAbsolutePath().normalize();
if (path.isDirectory)
if (path.isDirectory && !Files.isDirectory(path.absolutePath))
{
FileUtils.getOrCreateDirectory(path.absolutePath, path.name());
try {
Files.createDirectories(path.absolutePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (LOGGER.isDebugEnabled(CORE))
{
@@ -68,8 +74,26 @@ public enum FMLPaths
}
}
/**
* @deprecated Use {@link #getOrCreateGameRelativePath(Path)} instead.
*/
@Deprecated(forRemoval = true, since = "1.19.4")
public static Path getOrCreateGameRelativePath(Path path, String name) {
return FileUtils.getOrCreateDirectory(FMLPaths.GAMEDIR.get().resolve(path), name);
return getOrCreateGameRelativePath(path);
}
public static Path getOrCreateGameRelativePath(Path path) {
Path gameFolderPath = FMLPaths.GAMEDIR.get().resolve(path);
if (!Files.isDirectory(gameFolderPath)) {
try {
Files.createDirectories(gameFolderPath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return gameFolderPath;
}
public Path relative() {
@@ -10,37 +10,27 @@ import org.slf4j.Logger;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class FileUtils
{
private static final Logger LOGGER = LogUtils.getLogger();
/**
* @deprecated Use normal Java NIO methods instead
*/
@Deprecated(forRemoval = true, since = "1.19.4")
public static Path getOrCreateDirectory(Path dirPath, String dirLabel) {
if (!Files.isDirectory(dirPath.getParent())) {
getOrCreateDirectory(dirPath.getParent(), "parent of "+dirLabel);
}
if (!Files.isDirectory(dirPath))
{
LOGGER.debug(CORE, "Making {} directory : {}", dirLabel, dirPath);
try {
Files.createDirectory(dirPath);
Files.createDirectories(dirPath);
} catch (IOException e) {
if (e instanceof FileAlreadyExistsException) {
LOGGER.error(CORE, "Failed to create {} directory - there is a file in the way", dirLabel);
} else {
LOGGER.error(CORE, "Problem with creating {} directory (Permissions?)", dirLabel, e);
}
throw new RuntimeException("Problem creating directory", e);
}
LOGGER.debug(CORE, "Created {} directory : {}", dirLabel, dirPath);
} else {
LOGGER.debug(CORE, "Found existing {} directory : {}", dirLabel, dirPath);
}
return dirPath;
}
@@ -54,7 +44,7 @@ public class FileUtils
return "";
}
}
public static boolean matchFileName(String path, String... matches) {
// Extract file name from path
String name = path.substring(Math.min(path.lastIndexOf(File.separatorChar) + 1, path.length()));
@@ -11,17 +11,11 @@ import net.minecraftforge.forgespi.language.IModInfo;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
import static java.util.stream.Collectors.*;
public class UniqueModListBuilder
{
@@ -72,34 +66,28 @@ public class UniqueModListBuilder
// Its theoretically possible that some mod has somehow moved an id to a secondary place, thus causing a dupe.
// We can't handle this
final List<String> dupedModErrors = modIds.values().stream()
final List<IModInfo> dupedMods = modIds.values().stream()
.filter(modInfos -> modInfos.size() > 1)
.map(mods -> String.format("\tMod ID: '%s' from mod files: %s",
mods.get(0).getModId(),
mods.stream()
.map(modInfo -> modInfo.getOwningFile().getFile().getFileName()).collect(joining(", "))
)).toList();
.map(modInfos -> modInfos.get(0))
.toList();
if (!dupedModErrors.isEmpty()) {
LOGGER.error(LOADING, "Found duplicate mods:\n{}", dupedModErrors.stream().collect(joining("\n")));
throw new EarlyLoadingException("Duplicate mods found", null, dupedModErrors.stream()
.map(s -> new EarlyLoadingException.ExceptionData(s))
.toList());
if (!dupedMods.isEmpty()) {
final List<EarlyLoadingException.ExceptionData> duplicateModErrors = dupedMods.stream()
.map(dm -> new EarlyLoadingException.ExceptionData("fml.modloading.dupedmod", dm, Objects.toString(dm)))
.toList();
throw new EarlyLoadingException("Duplicate mods found", null, duplicateModErrors);
}
final List<String> dupedLibErrors = versionedLibIds.values().stream()
final List<ModFile> dupedLibs = versionedLibIds.values().stream()
.filter(modFiles -> modFiles.size() > 1)
.map(mods -> String.format("\tLibrary: '%s' from files: %s",
getModId(mods.get(0)),
mods.stream()
.map(modFile -> modFile.getFileName()).collect(joining(", "))
)).toList();
.map(modFiles -> modFiles.get(0))
.toList();
if (!dupedLibErrors.isEmpty()) {
LOGGER.error(LOADING, "Found duplicate plugins or libraries:\n{}", dupedLibErrors.stream().collect(joining("\n")));
throw new EarlyLoadingException("Duplicate plugins or libraries found", null, dupedLibErrors.stream()
.map(s -> new EarlyLoadingException.ExceptionData(s))
.toList());
if (!dupedLibs.isEmpty()) {
final List<EarlyLoadingException.ExceptionData> duplicateLibErrors = dupedLibs.stream()
.map(dm -> new EarlyLoadingException.ExceptionData("fml.modloading.dupedlib.versioned", dm, Objects.toString(dm)))
.toList();
throw new EarlyLoadingException("Duplicate plugins or libraries found", null, duplicateLibErrors);
}
// Collect unique mod files by module name. This will be used for deduping purposes
@@ -31,7 +31,6 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
// The extra jar is on the classpath, so try and pull it out of the legacy classpath
var legacyCP = Objects.requireNonNull(System.getProperty("legacyClassPath"), "Missing legacyClassPath, cannot find client-extra").split(File.pathSeparator);
var extra = findJarOnClasspath(legacyCP, "client-extra");
mcstream.add(extra);
// The MC code/Patcher edits are in exploded directories
final var modstream = Stream.<List<Path>>builder();
@@ -42,6 +41,7 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
minecraft.stream().distinct().forEach(mcstream::add);
mods.values().forEach(modstream::add);
mcstream.add(extra);
var mcFilter = getMcFilter(extra, minecraft, modstream);
return new LocatedPaths(mcstream.build().toList(), mcFilter, modstream.build().toList(), getFmlStuff(legacyCP));
}
@@ -19,6 +19,7 @@ public final class CyclePresentException extends IllegalArgumentException {
* @param cycles the cycles present
*/
CyclePresentException(Set<Set<?>> cycles) {
super("Cycles present in graph: " + cycles);
this.cycles = cycles;
}
@@ -42,6 +42,6 @@ public class FMLOnlyBindings implements IBindingsProvider {
@Override
public Supplier<IConfigEvent.ConfigConfig> getConfigConfiguration() {
return ()->new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new);
return ()->new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new, ModConfigEvent.Unloading::new);
}
}
@@ -52,7 +52,7 @@ public class ClientModLoader
}
}
}
public static void begin(final Minecraft minecraft, final PackRepository defaultResourcePacks, final ReloadableResourceManager mcResourceManager, ClientPackSource metadataSerializer)
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));
@@ -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;
}
}
@@ -1,3 +1,4 @@
{
"load_fml_client": "coremods/load_fml_client.js"
"load_fml_client": "coremods/load_fml_client.js",
"load_fml_server": "coremods/load_fml_server.js"
}
@@ -15,16 +15,13 @@ function initializeCoreMod() {
var meth = ASMAPI.getMethodNode();
var resourcePackRepository = ASMAPI.mapField("f_91038_")
var resourceManager = ASMAPI.mapField("f_91036_")
var clientPackSource = ASMAPI.mapField("f_91037_")
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.visitVarInsn(Opcodes.ALOAD, 0);
meth.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/client/Minecraft", clientPackSource, "Lnet/minecraft/client/resources/ClientPackSource;");
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;Lnet/minecraft/client/resources/ClientPackSource;)V", false)
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)) {
@@ -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 -3
View File
@@ -22,7 +22,7 @@ minecraft {
// 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://github.com/ParchmentMC/Parchment/wiki/Getting-Started
// 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.
@@ -132,8 +132,6 @@ dependencies {
minecraft '@FORGE_GROUP@:@FORGE_NAME@:@FORGE_VERSION@'
// Real mod deobf dependency examples - these get remapped to your current mappings
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}:api") // Adds JEI API as a compile dependency
// runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}") // Adds the full JEI mod as a runtime dependency
// implementation fg.deobf("com.tterrag.registrate:Registrate:MC${mc_version}-${registrate_version}") // Adds registrate as a dependency
// Examples using mod jars from ./libs
@@ -3,7 +3,7 @@ package com.example.examplemod;
import com.mojang.logging.LogUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
@@ -11,15 +11,13 @@ import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.Material;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.CreativeModeTabEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.InterModComms;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
@@ -42,7 +40,7 @@ public class ExampleMod
// Creates a new Block with the id "examplemod:example_block", combining the namespace and path
public static final RegistryObject<Block> EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> new Block(BlockBehaviour.Properties.of(Material.STONE)));
// Creates a new BlockItem with the id "examplemod:example_block", combining the namespace and path
public static final RegistryObject<Item> EXAMPLE_BLOCK_ITEM = ITEMS.register("example_block", () -> new BlockItem(EXAMPLE_BLOCK.get(), new Item.Properties().tab(CreativeModeTab.TAB_BUILDING_BLOCKS)));
public static final RegistryObject<Item> EXAMPLE_BLOCK_ITEM = ITEMS.register("example_block", () -> new BlockItem(EXAMPLE_BLOCK.get(), new Item.Properties()));
public ExampleMod()
{
@@ -58,6 +56,9 @@ public class ExampleMod
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
// Register the item to a creative tab
modEventBus.addListener(this::addCreative);
}
private void commonSetup(final FMLCommonSetupEvent event)
@@ -67,6 +68,12 @@ public class ExampleMod
LOGGER.info("DIRT BLOCK >> {}", ForgeRegistries.BLOCKS.getKey(Blocks.DIRT));
}
private void addCreative(CreativeModeTabEvent.BuildContents event)
{
if (event.getTab() == CreativeModeTabs.BUILDING_BLOCKS)
event.accept(EXAMPLE_BLOCK_ITEM);
}
// You can use SubscribeEvent and let the Event Bus discover methods to call
@SubscribeEvent
public void onServerStarting(ServerStartingEvent event)
+1 -1
View File
@@ -22,7 +22,7 @@ modId="examplemod" #mandatory
version="${file.jarVersion}" #mandatory
# A display name for the mod
displayName="Example Mod" #mandatory
# A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/
# A URL to query for updates for this mod. See the JSON update specification https://docs.minecraftforge.net/en/latest/misc/updatechecker/
#updateJSONURL="https://change.me.example.invalid/updates.json" #optional
# A URL for the "homepage" for this mod, displayed in the mod UI
#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional
-8
View File
@@ -1,8 +0,0 @@
{
"pack": {
"description": "examplemod resources",
"pack_format": 9,
"forge:resource_pack_format": 9,
"forge:data_pack_format": 10
}
}
@@ -1,7 +1,7 @@
--- a/com/mojang/blaze3d/platform/GlStateManager.java
+++ b/com/mojang/blaze3d/platform/GlStateManager.java
@@ -539,9 +_,16 @@
f_84077_[f_84076_].f_84800_ = false;
@@ -500,9 +_,16 @@
}
+ /* Stores the last values sent into glMultiTexCoord2f */
@@ -9,7 +9,7 @@
if (monitor != null) {
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;
@@ -303,6 +_,7 @@
@@ -260,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;
@@ -1,7 +1,7 @@
--- a/com/mojang/blaze3d/vertex/BufferBuilder.java
+++ b/com/mojang/blaze3d/vertex/BufferBuilder.java
@@ -486,4 +_,14 @@
this.f_166822_ = p_166829_;
this.f_166822_ = p_254368_;
}
}
+
@@ -1,11 +1,11 @@
--- a/com/mojang/blaze3d/vertex/PoseStack.java
+++ b/com/mojang/blaze3d/vertex/PoseStack.java
@@ -11,7 +_,7 @@
import net.minecraftforge.api.distmarker.OnlyIn;
import org.joml.Quaternionf;
@OnlyIn(Dist.CLIENT)
-public class PoseStack {
+public class PoseStack implements net.minecraftforge.client.extensions.IForgePoseStack {
private final Deque<PoseStack.Pose> f_85834_ = Util.m_137469_(Queues.newArrayDeque(), (p_85848_) -> {
Matrix4f matrix4f = new Matrix4f();
matrix4f.m_27624_();
Matrix3f matrix3f = new Matrix3f();
@@ -20,7 +20,7 @@
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_.m_111303_();
@@ -101,12 +_,14 @@
@@ -99,11 +_,13 @@
f5 = afloat[k] * p_86001_;
}
@@ -28,12 +28,11 @@
+ int l = applyBakedLighting(p_86002_[k], bytebuffer);
float f9 = bytebuffer.getFloat(16);
float f10 = bytebuffer.getFloat(20);
Vector4f vector4f = new Vector4f(f, f1, f2, 1.0F);
vector4f.m_123607_(matrix4f);
- this.m_5954_(vector4f.m_123601_(), vector4f.m_123615_(), vector4f.m_123616_(), f3, f4, f5, 1.0F, f9, f10, p_86003_, l, vector3f.m_122239_(), vector3f.m_122260_(), vector3f.m_122269_());
+ applyBakedNormals(vector3f, bytebuffer, p_85996_.m_85864_());
Vector4f vector4f = matrix4f.transform(new Vector4f(f, f1, f2, 1.0F));
- 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.m_5954_(vector4f.m_123601_(), vector4f.m_123615_(), vector4f.m_123616_(), f3, f4, f5, vertexAlpha, f9, f10, p_86003_, l, vector3f.m_122239_(), vector3f.m_122260_(), vector3f.m_122269_());
+ this.m_5954_(vector4f.x(), vector4f.y(), vector4f.z(), f3, f4, f5, vertexAlpha, f9, f10, p_86003_, l, vector3f.x(), vector3f.y(), vector3f.z());
}
} catch (Throwable throwable1) {
if (memorystack != null) {
}
@@ -1,13 +0,0 @@
--- a/com/mojang/math/Matrix3f.java
+++ b/com/mojang/math/Matrix3f.java
@@ -588,4 +_,10 @@
public Matrix3f m_8183_() {
return new Matrix3f(this);
}
+
+ public void multiplyBackward(Matrix3f other) {
+ Matrix3f copy = other.m_8183_();
+ copy.m_8178_(this);
+ this.m_8169_(copy);
+ }
}
@@ -1,43 +0,0 @@
--- a/com/mojang/math/Matrix4f.java
+++ b/com/mojang/math/Matrix4f.java
@@ -559,4 +_,40 @@
matrix4f.f_27614_ = p_27656_;
return matrix4f;
}
+
+ // Forge start
+ public Matrix4f(float[] values) {
+ f_27603_ = values[0];
+ f_27604_ = values[1];
+ f_27605_ = values[2];
+ f_27606_ = values[3];
+ f_27607_ = values[4];
+ f_27608_ = values[5];
+ f_27609_ = values[6];
+ f_27610_ = values[7];
+ f_27611_ = values[8];
+ f_27612_ = values[9];
+ f_27613_ = values[10];
+ f_27614_ = values[11];
+ f_27615_ = values[12];
+ f_27616_ = values[13];
+ f_27617_ = values[14];
+ f_27618_ = values[15];
+ }
+
+ public void multiplyBackward(Matrix4f other) {
+ Matrix4f copy = other.m_27658_();
+ copy.m_27644_(this);
+ this.m_162210_(copy);
+ }
+
+ public void setTranslation(float x, float y, float z) {
+ this.f_27603_ = 1.0F;
+ this.f_27608_ = 1.0F;
+ this.f_27613_ = 1.0F;
+ this.f_27618_ = 1.0F;
+ this.f_27606_ = x;
+ this.f_27610_ = y;
+ this.f_27614_ = z;
+ }
}
@@ -1,15 +1,15 @@
--- a/com/mojang/math/Transformation.java
+++ b/com/mojang/math/Transformation.java
@@ -6,7 +_,7 @@
import net.minecraft.Util;
import org.apache.commons.lang3.tuple.Triple;
@@ -14,7 +_,7 @@
import org.joml.Vector3f;
import org.joml.Vector3fc;
-public final class Transformation {
+public final class Transformation implements net.minecraftforge.client.extensions.IForgeTransformation {
private final Matrix4f f_121078_;
private boolean f_121079_;
@Nullable
@@ -145,6 +_,19 @@
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.f_121078_);
}
@@ -22,10 +22,10 @@
+ private void checkNormalTransform() {
+ if (normalTransform == null) {
+ normalTransform = new Matrix3f(f_121078_);
+ normalTransform.m_8187_();
+ normalTransform.m_8155_();
+ normalTransform.invert();
+ normalTransform.transpose();
+ }
+ }
public Transformation m_175937_(Transformation p_175938_, float p_175939_) {
Vector3f vector3f = this.m_175940_();
Vector3f vector3f = this.m_252829_();
@@ -1,28 +0,0 @@
--- a/com/mojang/math/Vector3f.java
+++ b/com/mojang/math/Vector3f.java
@@ -138,7 +_,7 @@
public boolean m_122278_() {
float f = this.f_122228_ * this.f_122228_ + this.f_122229_ * this.f_122229_ + this.f_122230_ * this.f_122230_;
- if ((double)f < 1.0E-5D) {
+ if (f < Float.MIN_NORMAL) { //Forge: Fix MC-239212
return false;
} else {
float f1 = Mth.m_14195_(f);
@@ -207,4 +_,16 @@
public String toString() {
return "[" + this.f_122228_ + ", " + this.f_122229_ + ", " + this.f_122230_ + "]";
}
+
+ public Vector3f(float[] values) {
+ set(values);
+ }
+ public void set(float[] values) {
+ this.f_122228_ = values[0];
+ this.f_122229_ = values[1];
+ this.f_122230_ = values[2];
+ }
+ public void setX(float x) { this.f_122228_ = x; }
+ public void setY(float y) { this.f_122229_ = y; }
+ public void setZ(float z) { this.f_122230_ = z; }
}
@@ -1,18 +0,0 @@
--- a/com/mojang/math/Vector4f.java
+++ b/com/mojang/math/Vector4f.java
@@ -147,4 +_,15 @@
public String toString() {
return "[" + this.f_123589_ + ", " + this.f_123590_ + ", " + this.f_123591_ + ", " + this.f_123592_ + "]";
}
+
+ public void set(float[] values) {
+ this.f_123589_ = values[0];
+ this.f_123590_ = values[1];
+ this.f_123591_ = values[2];
+ this.f_123592_ = values[3];
+ }
+ public void setX(float x) { this.f_123589_ = x; }
+ public void setY(float y) { this.f_123590_ = y; }
+ public void setZ(float z) { this.f_123591_ = z; }
+ public void setW(float w) { this.f_123592_ = w; }
}
@@ -13,6 +13,6 @@
+ return super.m_7933_(key, scanCode, modifiers);
+ }
+
public void m_6305_(PoseStack p_88679_, int p_88680_, int p_88681_, float p_88682_) {
public void m_86412_(PoseStack p_88679_, int p_88680_, int p_88681_, float p_88682_) {
this.m_7333_(p_88679_);
m_93215_(p_88679_, this.f_96547_, this.f_200947_.getFirst(), 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
@@ -47,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,6 +1,6 @@
--- a/net/minecraft/SharedConstants.java
+++ b/net/minecraft/SharedConstants.java
@@ -211,6 +_,7 @@
@@ -205,6 +_,7 @@
}
static {
@@ -9,25 +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 m_183988_() {
return f_137443_;
public static ExecutorService m_183991_() {
return f_137444_;
}
@@ -248,7 +_,7 @@
@@ -238,7 +_,7 @@
try {
type = DataFixers.m_14512_().getSchema(DataFixUtils.makeKey(SharedConstants.m_183709_().getWorldVersion())).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) {
- 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;
}
@@ -692,7 +_,8 @@
public static <T, R> Function<T, R> m_143827_(final Function<T, R> p_143828_) {
return new Function<T, R>() {
- private final Map<T, R> f_211548_ = Maps.newHashMap();
+ // FORGE: Allow using memoized functions from multiple threads.
+ private final Map<T, R> f_211548_ = Maps.newConcurrentMap();
public R apply(T p_214691_) {
return this.f_211548_.computeIfAbsent(p_214691_, p_143828_);
@@ -1,6 +1,6 @@
--- a/net/minecraft/advancements/Advancement.java
+++ b/net/minecraft/advancements/Advancement.java
@@ -127,7 +_,7 @@
@@ -144,7 +_,7 @@
return this.f_138305_;
}
@@ -9,7 +9,7 @@
@Nullable
private ResourceLocation f_138332_;
@Nullable
@@ -308,7 +_,14 @@
@@ -325,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_) + "}";
}
@@ -1,10 +0,0 @@
--- a/net/minecraft/advancements/AdvancementList.java
+++ b/net/minecraft/advancements/AdvancementList.java
@@ -90,6 +_,7 @@
}
}
+ net.minecraftforge.common.AdvancementLoadFix.buildSortedTrees(this.f_139327_);
f_139325_.info("Loaded {} advancements", (int)this.f_139326_.size());
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/advancements/critereon/ItemPredicate.java
+++ b/net/minecraft/advancements/critereon/ItemPredicate.java
@@ -28,6 +_,8 @@
@@ -29,6 +_,8 @@
import net.minecraft.world.level.ItemLike;
public class ItemPredicate {
@@ -9,7 +9,7 @@
public static final ItemPredicate f_45028_ = new ItemPredicate();
@Nullable
private final TagKey<Item> f_45029_;
@@ -80,7 +_,7 @@
@@ -81,7 +_,7 @@
return false;
} else {
if (this.f_45033_.length > 0) {
@@ -18,7 +18,7 @@
for(EnchantmentPredicate enchantmentpredicate : this.f_45033_) {
if (!enchantmentpredicate.m_30476_(map)) {
@@ -107,6 +_,11 @@
@@ -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");
@@ -30,7 +30,7 @@
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")) {
@@ -214,6 +_,14 @@
@@ -215,6 +_,14 @@
} else {
return new ItemPredicate[0];
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/Camera.java
+++ b/net/minecraft/client/Camera.java
@@ -211,6 +_,18 @@
@@ -206,6 +_,18 @@
this.f_90549_ = false;
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/ClientRecipeBook.java
+++ b/net/minecraft/client/ClientRecipeBook.java
@@ -51,7 +_,7 @@
@@ -54,7 +_,7 @@
for(Recipe<?> recipe : p_90643_) {
if (!recipe.m_5598_() && !recipe.m_142505_()) {
RecipeBookCategories recipebookcategories = m_90646_(recipe);
@@ -9,12 +9,12 @@
if (s.isEmpty()) {
map.computeIfAbsent(recipebookcategories, (p_90645_) -> {
return Lists.newArrayList();
@@ -103,6 +_,8 @@
} else if (recipetype == RecipeType.f_44113_) {
return RecipeBookCategories.SMITHING;
} else {
+ RecipeBookCategories categories = net.minecraftforge.client.RecipeBookManager.findCategories((RecipeType) recipetype, p_90647_);
+ if (categories != null) return categories;
f_90618_.warn("Unknown recipe category: {}/{}", LogUtils.defer(() -> {
return Registry.f_122864_.m_7981_(p_90647_.m_6671_());
}), LogUtils.defer(p_90647_::m_6423_));
@@ -141,6 +_,8 @@
} else if (recipetype == RecipeType.f_44113_) {
return RecipeBookCategories.SMITHING;
} else {
+ RecipeBookCategories categories = net.minecraftforge.client.RecipeBookManager.findCategories((RecipeType) recipetype, p_90647_);
+ if (categories != null) return categories;
f_90618_.warn("Unknown recipe category: {}/{}", LogUtils.defer(() -> {
return BuiltInRegistries.f_256990_.m_7981_(p_90647_.m_6671_());
}), LogUtils.defer(p_90647_::m_6423_));
@@ -1,9 +1,9 @@
--- a/net/minecraft/client/KeyboardHandler.java
+++ b/net/minecraft/client/KeyboardHandler.java
@@ -299,7 +_,9 @@
@@ -328,7 +_,9 @@
}
}
Screen screen = this.f_90867_.f_91080_;
- if (p_90897_ == 1 && (!(this.f_90867_.f_91080_ instanceof KeyBindsScreen) || ((KeyBindsScreen)screen).f_193976_ <= Util.m_137550_() - 20L)) {
+
+ if ((!(this.f_90867_.f_91080_ instanceof KeyBindsScreen) || ((KeyBindsScreen)screen).f_193976_ <= Util.m_137550_() - 20L)) {
@@ -11,7 +11,7 @@
if (this.f_90867_.f_91066_.f_92105_.m_90832_(p_90895_, p_90896_)) {
this.f_90867_.m_91268_().m_85438_();
this.f_90867_.f_91066_.m_231829_().m_231514_(this.f_90867_.m_91268_().m_85440_());
@@ -317,6 +_,8 @@
@@ -346,6 +_,8 @@
});
return;
}
@@ -20,9 +20,9 @@
}
if (this.f_90867_.m_240477_().m_93316_()) {
@@ -339,11 +_,15 @@
@@ -368,11 +_,15 @@
Screen.m_96579_(() -> {
if (p_90897_ != 1 && (p_90897_ != 2 || !this.f_90868_)) {
if (p_90897_ != 1 && p_90897_ != 2) {
if (p_90897_ == 0) {
- aboolean[0] = screen.m_7920_(p_90895_, p_90896_, p_90898_);
+ aboolean[0] = net.minecraftforge.client.ForgeHooksClient.onScreenKeyReleasedPre(screen, p_90895_, p_90896_, p_90898_);
@@ -38,7 +38,7 @@
}
}, "keyPressed event handler", screen.getClass().getCanonicalName());
@@ -396,22 +_,26 @@
@@ -425,22 +_,26 @@
}
}
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/Minecraft.java
+++ b/net/minecraft/client/Minecraft.java
@@ -244,7 +_,7 @@
@@ -247,7 +_,7 @@
import org.slf4j.Logger;
@OnlyIn(Dist.CLIENT)
@@ -9,16 +9,7 @@
static Minecraft f_90981_;
private static final Logger f_90982_ = LogUtils.getLogger();
public static final boolean f_91002_ = Util.m_137581_() == Util.OS.OSX;
@@ -398,7 +_,7 @@
this.f_91029_ = p_91084_.f_101908_.f_101928_;
this.f_90986_ = p_91084_.f_101905_.f_101944_;
this.f_91037_ = new ClientPackSource(new File(this.f_91069_, "server-resource-packs"), p_91084_.f_101907_.m_101925_());
- this.f_91038_ = new PackRepository(Minecraft::m_167933_, this.f_91037_, new FolderRepositorySource(this.f_90985_, PackSource.f_10527_));
+ this.f_91038_ = new PackRepository(Minecraft::createClientPackAdapter, this.f_91037_, new FolderRepositorySource(this.f_90985_, PackSource.f_10527_));
this.f_91030_ = p_91084_.f_101905_.f_101945_;
this.f_231338_ = new YggdrasilAuthenticationService(this.f_91030_);
this.f_91048_ = this.f_231338_.createMinecraftSessionService();
@@ -406,7 +_,6 @@
@@ -412,7 +_,6 @@
this.f_231339_ = SignatureValidator.m_216358_(this.f_231338_.getServicesKey());
this.f_90998_ = p_91084_.f_101905_.f_101942_;
f_90982_.info("Setting user: {}", (Object)this.f_90998_.m_92546_());
@@ -26,7 +17,7 @@
this.f_91033_ = p_91084_.f_101908_.f_101926_;
this.f_91034_ = !p_91084_.f_101908_.f_101929_;
this.f_91035_ = !p_91084_.f_101908_.f_101930_;
@@ -458,7 +_,7 @@
@@ -462,7 +_,7 @@
this.f_90990_.m_85380_(this.f_91066_.m_232035_().m_231551_());
this.f_91067_ = new MouseHandler(this);
@@ -35,28 +26,30 @@
this.f_91068_ = new KeyboardHandler(this);
this.f_91068_.m_90887_(this.f_90990_.m_85439_());
RenderSystem.m_69580_(this.f_91066_.f_92035_, false);
@@ -466,6 +_,7 @@
@@ -470,6 +_,7 @@
this.f_91042_.m_83931_(0.0F, 0.0F, 0.0F, 0.0F);
this.f_91042_.m_83954_(f_91002_);
this.f_91036_ = new ReloadableResourceManager(PackType.CLIENT_RESOURCES);
+ net.minecraftforge.client.loading.ClientModLoader.begin(this, this.f_91038_, this.f_91036_, this.f_91037_);
+ net.minecraftforge.client.loading.ClientModLoader.begin(this, this.f_91038_, this.f_91036_);
this.f_91038_.m_10506_();
this.f_91066_.m_92145_(this.f_91038_);
this.f_91039_ = new LanguageManager(this.f_91066_.f_92075_);
@@ -510,10 +_,12 @@
@@ -514,10 +_,13 @@
this.f_91063_ = new GameRenderer(this, this.f_90994_.m_234586_(), this.f_91036_, this.f_90993_);
this.f_91036_.m_7217_(this.f_91063_);
this.f_91036_.m_7217_(this.f_91063_.m_247116_());
this.f_91060_ = new LevelRenderer(this, this.f_90994_, this.f_167845_, this.f_90993_);
+ net.minecraftforge.fml.ModLoader.get().postEvent(new net.minecraftforge.client.event.RenderLevelStageEvent.RegisterStageEvent());
this.f_91036_.m_7217_(this.f_91060_);
this.m_91271_();
- this.m_91271_();
+ // Forge: We delay this to allow registration of modded creative mode tabs
+ // this.createSearchTrees();
this.f_91036_.m_7217_(this.f_90997_);
this.f_91061_ = new ParticleEngine(this.f_91073_, this.f_90987_);
+ net.minecraftforge.client.ForgeHooksClient.onRegisterParticleProviders(f_91061_);
this.f_91036_.m_7217_(this.f_91061_);
this.f_91053_ = new PaintingTextureManager(this.f_90987_);
this.f_91036_.m_7217_(this.f_91053_);
@@ -522,7 +_,9 @@
@@ -526,7 +_,9 @@
this.f_91047_ = new GpuWarnlistManager();
this.f_91036_.m_7217_(this.f_91047_);
this.f_91036_.m_7217_(this.f_205120_);
@@ -67,36 +60,23 @@
this.f_91064_ = new DebugRenderer(this);
this.f_238717_ = new RealmsDataFetcher(RealmsClient.m_239151_(this));
RenderSystem.m_69900_(this::m_91113_);
@@ -541,6 +_,21 @@
@@ -545,6 +_,7 @@
TinyFileDialogs.tinyfd_messageBox("Minecraft", stringbuilder.toString(), "ok", "error", false);
}
+ net.minecraftforge.gametest.ForgeGameTestHooks.registerGametests();
+ net.minecraftforge.fml.ModLoader.get().postEvent(new net.minecraftforge.client.event.RegisterClientReloadListenersEvent(this.f_91036_));
+ net.minecraftforge.fml.ModLoader.get().postEvent(new net.minecraftforge.client.event.EntityRenderersEvent.RegisterLayerDefinitions());
+ net.minecraftforge.fml.ModLoader.get().postEvent(new net.minecraftforge.client.event.EntityRenderersEvent.RegisterRenderers());
+ net.minecraftforge.client.textures.TextureAtlasSpriteLoaderManager.init();
+ net.minecraftforge.client.gui.ClientTooltipComponentManager.init();
+ net.minecraftforge.client.EntitySpectatorShaderManager.init();
+ net.minecraftforge.client.ForgeHooksClient.onRegisterKeyMappings(f_91066_);
+ net.minecraftforge.client.RecipeBookManager.init();
+ net.minecraftforge.client.gui.overlay.GuiOverlayManager.init();
+ net.minecraftforge.client.DimensionSpecialEffectsManager.init();
+ net.minecraftforge.client.NamedRenderTypeManager.init();
+ net.minecraftforge.client.ColorResolverManager.init();
+ net.minecraftforge.client.ItemDecoratorHandler.init();
+
+ net.minecraftforge.client.ForgeHooksClient.initClientHooks(this, this.f_91036_);
this.f_90990_.m_85409_(this.f_91066_.m_231817_().m_231551_());
this.f_90990_.m_85424_(this.f_91066_.m_232123_().m_231551_());
this.f_90990_.m_85426_();
@@ -563,23 +_,23 @@
@@ -568,26 +_,26 @@
}
this.f_167847_.m_168556_();
+ if (net.minecraftforge.client.loading.ClientModLoader.completeModLoading()) return; // Do not overwrite the error screen
+ // FORGE: Move opening initial screen to after startup and events are enabled.
+ if (s != null) {
+ ConnectScreen.m_169267_(new TitleScreen(), this, new ServerAddress(s, i), (ServerData)null);
+ ServerAddress serveraddress = new ServerAddress(s, i);
+ ConnectScreen.m_169267_(new TitleScreen(), this, serveraddress, new ServerData(I18n.m_118938_("selectServer.defaultName"), serveraddress.toString(), false));
+ } else if (this.m_239929_()) {
+ this.m_91152_(BanNoticeScreen.m_239967_((p_238185_) -> {
+ if (p_238185_) {
@@ -105,14 +85,17 @@
+
+ this.m_91152_(new TitleScreen(true));
+ }, this.m_239210_()));
+ } else if (this.f_91066_.f_263744_) {
+ this.m_91152_(new AccessibilityOnboardingScreen(this.f_91066_));
+ } else {
+ this.m_91152_(new TitleScreen(true));
+ }
});
}, false));
- if (s != null) {
- ServerAddress serveraddress = new ServerAddress(s, i);
- reloadinstance.m_7237_().thenRunAsync(() -> {
- ConnectScreen.m_169267_(new TitleScreen(), this, new ServerAddress(s, i), (ServerData)null);
- ConnectScreen.m_169267_(new TitleScreen(), this, serveraddress, new ServerData(I18n.m_118938_("selectServer.defaultName"), serveraddress.toString(), false));
- }, this);
- } else if (this.m_239929_()) {
- this.m_91152_(BanNoticeScreen.m_239967_((p_238185_) -> {
@@ -122,13 +105,15 @@
-
- this.m_91152_(new TitleScreen(true));
- }, this.m_239210_()));
- } else if (this.f_91066_.f_263744_) {
- this.m_91152_(new AccessibilityOnboardingScreen(this.f_91066_));
- } else {
- this.m_91152_(new TitleScreen(true));
- }
}
@@ -622,6 +_,8 @@
@@ -639,6 +_,8 @@
private UserApiService m_193585_(YggdrasilAuthenticationService p_193586_, GameConfig p_193587_) {
try {
@@ -137,7 +122,7 @@
return p_193586_.createUserApiService(p_193587_.f_101905_.f_101942_.m_92547_());
} catch (AuthenticationException authenticationexception) {
f_90982_.error("Failed to verify authentication", (Throwable)authenticationexception);
@@ -634,7 +_,7 @@
@@ -651,7 +_,7 @@
}
private void m_91239_(Throwable p_91240_) {
@@ -146,7 +131,45 @@
this.m_91241_(p_91240_, (Component)null);
} else {
Util.m_137559_(p_91240_);
@@ -792,13 +_,13 @@
@@ -743,7 +_,10 @@
}
public void m_91271_() {
- this.f_90997_.m_235232_(SearchRegistry.f_119941_, (p_231451_) -> {
+ var nameSearchKeys = net.minecraftforge.client.CreativeModeTabSearchRegistry.getNameSearchKeys();
+ var tagSearchKeys = net.minecraftforge.client.CreativeModeTabSearchRegistry.getTagSearchKeys();
+ for (var nameSearchKey : nameSearchKeys.values())
+ this.f_90997_.m_235232_(nameSearchKey, (p_231451_) -> {
return new FullTextSearchTree<>((p_210797_) -> {
return p_210797_.m_41651_((Player)null, TooltipFlag.Default.f_256752_.m_257777_()).stream().map((p_210807_) -> {
return ChatFormatting.m_126649_(p_210807_.getString()).trim();
@@ -754,7 +_,8 @@
return Stream.of(BuiltInRegistries.f_257033_.m_7981_(p_91317_.m_41720_()));
}, p_231451_);
});
- this.f_90997_.m_235232_(SearchRegistry.f_119942_, (p_231430_) -> {
+ for (var tagSearchKey : tagSearchKeys.values())
+ this.f_90997_.m_235232_(tagSearchKey, (p_231430_) -> {
return new IdSearchTree<>((p_231353_) -> {
return p_231353_.m_204131_().map(TagKey::f_203868_);
}, p_231430_);
@@ -774,9 +_,12 @@
});
}, p_231389_);
});
- CreativeModeTabs.m_258007_().m_257882_((p_255439_) -> {
- this.m_231374_(SearchRegistry.f_119941_, p_255439_);
- this.m_231374_(SearchRegistry.f_119942_, p_255439_);
+ nameSearchKeys.forEach((tab, nameSearchKey) -> {
+ var tagSearchKey = tagSearchKeys.get(tab);
+ tab.m_257882_(contents -> {
+ this.m_231374_(nameSearchKey, contents);
+ this.m_231374_(tagSearchKey, contents);
+ });
});
}
@@ -828,13 +_,13 @@
Bootstrap.m_135875_(p_91333_.m_127526_());
if (p_91333_.m_127527_() != null) {
Bootstrap.m_135875_("#@!@# Game crashed! Crash report saved to: #@!@# " + p_91333_.m_127527_());
@@ -163,7 +186,7 @@
}
}
@@ -922,10 +_,6 @@
@@ -959,10 +_,6 @@
f_90982_.error("setScreen called from non-game thread");
}
@@ -174,7 +197,7 @@
if (p_91153_ == null && this.f_91073_ == null) {
p_91153_ = new TitleScreen();
} else if (p_91153_ == null && this.f_91074_.m_21224_()) {
@@ -936,6 +_,19 @@
@@ -973,6 +_,19 @@
}
}
@@ -192,9 +215,9 @@
+ }
+
this.f_91080_ = p_91153_;
BufferUploader.m_166835_();
if (p_91153_ != null) {
@@ -1079,11 +_,14 @@
if (this.f_91080_ != null) {
this.f_91080_.m_274333_();
@@ -1118,9 +_,12 @@
RenderSystem.m_69481_();
this.f_91026_.m_7238_();
if (!this.f_91079_) {
@@ -202,14 +225,12 @@
+ net.minecraftforge.event.ForgeEventFactory.onRenderTickStart(this.realPartialTick);
this.f_91026_.m_6182_("gameRenderer");
this.f_91063_.m_109093_(this.f_91012_ ? this.f_91013_ : this.f_90991_.f_92518_, i, p_91384_);
this.f_91026_.m_6182_("toasts");
this.f_91003_.m_94920_(new PoseStack());
this.f_91026_.m_7238_();
+ net.minecraftforge.event.ForgeEventFactory.onRenderTickEnd(this.realPartialTick);
}
if (this.f_91056_ != null) {
@@ -1210,10 +_,12 @@
@@ -1243,10 +_,12 @@
this.f_90990_.m_85378_((double)i);
if (this.f_91080_ != null) {
this.f_91080_.m_6574_(this, this.f_90990_.m_85445_(), this.f_90990_.m_85446_());
@@ -222,7 +243,7 @@
this.f_91063_.m_109097_(this.f_90990_.m_85441_(), this.f_90990_.m_85442_());
this.f_91067_.m_91599_();
}
@@ -1491,6 +_,7 @@
@@ -1520,6 +_,7 @@
}
public void m_91395_() {
@@ -230,7 +251,7 @@
this.f_91019_ = false;
}
@@ -1520,10 +_,18 @@
@@ -1549,10 +_,18 @@
if (p_91387_ && this.f_91077_ != null && this.f_91077_.m_6662_() == HitResult.Type.BLOCK) {
BlockHitResult blockhitresult = (BlockHitResult)this.f_91077_;
BlockPos blockpos = blockhitresult.m_82425_();
@@ -252,41 +273,36 @@
this.f_91074_.m_6674_(InteractionHand.MAIN_HAND);
}
}
@@ -1548,14 +_,20 @@
return false;
} else {
boolean flag = false;
- switch (this.f_91077_.m_6662_()) {
+ var inputEvent = net.minecraftforge.client.ForgeHooksClient.onClickInput(0, this.f_91066_.f_92096_, InteractionHand.MAIN_HAND);
+ HitResult.Type hitType = this.f_91077_.m_6662_();
+ if(this.f_91077_ instanceof EntityHitResult entityHit) { // Forge: Perform attack range checks here, instead of in GameRenderer#pick, so extended-reach interactions work.
+ if(!f_91074_.canHit(entityHit.m_82443_(), 0)) hitType = HitResult.Type.MISS; // No padding in client code.
+ }
+ if (!inputEvent.isCanceled())
+ switch(hitType) {
case ENTITY:
this.f_91072_.m_105223_(this.f_91074_, ((EntityHitResult)this.f_91077_).m_82443_());
break;
case BLOCK:
BlockHitResult blockhitresult = (BlockHitResult)this.f_91077_;
BlockPos blockpos = blockhitresult.m_82425_();
- if (!this.f_91073_.m_8055_(blockpos).m_60795_()) {
+ if (!this.f_91073_.m_46859_(blockpos)) {
this.f_91072_.m_105269_(blockpos, blockhitresult.m_82434_());
if (this.f_91073_.m_8055_(blockpos).m_60795_()) {
flag = true;
@@ -1568,8 +_,10 @@
}
@@ -1581,6 +_,8 @@
return false;
} else {
boolean flag = false;
+ var inputEvent = net.minecraftforge.client.ForgeHooksClient.onClickInput(0, this.f_91066_.f_92096_, InteractionHand.MAIN_HAND);
+ if (!inputEvent.isCanceled())
switch (this.f_91077_.m_6662_()) {
case ENTITY:
this.f_91072_.m_105223_(this.f_91074_, ((EntityHitResult)this.f_91077_).m_82443_());
@@ -1588,7 +_,7 @@
case BLOCK:
BlockHitResult blockhitresult = (BlockHitResult)this.f_91077_;
BlockPos blockpos = blockhitresult.m_82425_();
- if (!this.f_91073_.m_8055_(blockpos).m_60795_()) {
+ if (!this.f_91073_.m_46859_(blockpos)) {
this.f_91072_.m_105269_(blockpos, blockhitresult.m_82434_());
if (this.f_91073_.m_8055_(blockpos).m_60795_()) {
flag = true;
@@ -1601,8 +_,10 @@
}
this.f_91074_.m_36334_();
+ net.minecraftforge.common.ForgeHooks.onEmptyLeftClick(this.f_91074_);
this.f_91074_.m_36334_();
+ net.minecraftforge.common.ForgeHooks.onEmptyLeftClick(this.f_91074_);
}
+ if (inputEvent.shouldSwingHand())
this.f_91074_.m_6674_(InteractionHand.MAIN_HAND);
return flag;
}
+ if (inputEvent.shouldSwingHand())
this.f_91074_.m_6674_(InteractionHand.MAIN_HAND);
return flag;
}
@@ -1584,6 +_,11 @@
@@ -1618,6 +_,11 @@
}
for(InteractionHand interactionhand : InteractionHand.values()) {
@@ -296,16 +312,9 @@
+ return;
+ }
ItemStack itemstack = this.f_91074_.m_21120_(interactionhand);
if (this.f_91077_ != null) {
switch (this.f_91077_.m_6662_()) {
@@ -1594,13 +_,14 @@
return;
}
+ if(!this.f_91074_.canInteractWith(entityhitresult.m_82443_(), 0)) break; //Forge: Entity may be traced via attack range, but the player may not have sufficient reach. No padding in client code.
InteractionResult interactionresult = this.f_91072_.m_105230_(this.f_91074_, entity, entityhitresult, interactionhand);
if (!interactionresult.m_19077_()) {
interactionresult = this.f_91072_.m_105226_(this.f_91074_, entity, interactionhand);
if (!itemstack.m_246617_(this.f_91073_.m_246046_())) {
return;
@@ -1638,7 +_,7 @@
}
if (interactionresult.m_19077_()) {
@@ -314,7 +323,7 @@
this.f_91074_.m_6674_(interactionhand);
}
@@ -1612,7 +_,7 @@
@@ -1650,7 +_,7 @@
int i = itemstack.m_41613_();
InteractionResult interactionresult1 = this.f_91072_.m_233732_(this.f_91074_, interactionhand, blockhitresult);
if (interactionresult1.m_19077_()) {
@@ -323,7 +332,7 @@
this.f_91074_.m_6674_(interactionhand);
if (!itemstack.m_41619_() && (itemstack.m_41613_() != i || this.f_91072_.m_105290_())) {
this.f_91063_.f_109055_.m_109320_(interactionhand);
@@ -1628,6 +_,9 @@
@@ -1666,6 +_,9 @@
}
}
@@ -333,7 +342,7 @@
if (!itemstack.m_41619_()) {
InteractionResult interactionresult2 = this.f_91072_.m_233721_(this.f_91074_, interactionhand);
if (interactionresult2.m_19077_()) {
@@ -1654,6 +_,8 @@
@@ -1692,6 +_,8 @@
--this.f_91011_;
}
@@ -342,7 +351,7 @@
this.f_91026_.m_6180_("gui");
this.f_240378_.m_240688_();
this.f_91065_.m_193832_(this.f_91012_);
@@ -1749,6 +_,7 @@
@@ -1787,6 +_,7 @@
this.f_91005_.m_120596_();
@@ -350,7 +359,7 @@
try {
this.f_91073_.m_104726_(() -> {
return true;
@@ -1764,6 +_,7 @@
@@ -1802,6 +_,7 @@
throw new ReportedException(crashreport);
}
@@ -358,7 +367,7 @@
}
this.f_91026_.m_6182_("animateTick");
@@ -1783,6 +_,8 @@
@@ -1821,6 +_,8 @@
this.f_91026_.m_6182_("keyboard");
this.f_91068_.m_90931_();
this.f_91026_.m_7238_();
@@ -367,7 +376,7 @@
}
private boolean m_91278_() {
@@ -1978,6 +_,7 @@
@@ -2017,6 +_,7 @@
}
public void m_91156_(ClientLevel p_91157_) {
@@ -375,7 +384,7 @@
ProgressScreen progressscreen = new ProgressScreen(true);
progressscreen.m_6309_(Component.m_237115_("connect.joining"));
this.m_91362_(progressscreen);
@@ -2011,10 +_,12 @@
@@ -2050,10 +_,12 @@
IntegratedServer integratedserver = this.f_91007_;
this.f_91007_ = null;
this.f_91063_.m_109150_();
@@ -388,15 +397,15 @@
if (integratedserver != null) {
this.f_91026_.m_6180_("waitForServer");
@@ -2029,6 +_,7 @@
@@ -2067,6 +_,7 @@
this.f_243981_.m_246151_();
this.f_91065_.m_93089_();
this.f_91008_ = null;
this.f_91010_ = false;
+ net.minecraftforge.client.ForgeHooksClient.handleClientLevelClosing(f_91073_);
this.f_91004_.m_90740_();
}
@@ -2124,6 +_,7 @@
this.f_91073_ = null;
@@ -2173,6 +_,7 @@
private void m_91280_() {
if (this.f_91077_ != null && this.f_91077_.m_6662_() != HitResult.Type.MISS) {
@@ -404,7 +413,7 @@
boolean flag = this.f_91074_.m_150110_().f_35937_;
BlockEntity blockentity = null;
HitResult.Type hitresult$type = this.f_91077_.m_6662_();
@@ -2136,10 +_,7 @@
@@ -2185,10 +_,7 @@
}
Block block = blockstate.m_60734_();
@@ -416,7 +425,7 @@
if (flag && Screen.m_96637_() && blockstate.m_155947_()) {
blockentity = this.f_91073_.m_7702_(blockpos);
@@ -2150,7 +_,7 @@
@@ -2199,7 +_,7 @@
}
Entity entity = ((EntityHitResult)this.f_91077_).m_82443_();
@@ -425,27 +434,7 @@
if (itemstack == null) {
return;
}
@@ -2688,8 +_,8 @@
return this.f_90993_;
}
- private static Pack m_167933_(String p_167934_, Component p_167935_, boolean p_167936_, Supplier<PackResources> p_167937_, PackMetadataSection p_167938_, Pack.Position p_167939_, PackSource p_167940_) {
- int i = p_167938_.m_10374_();
+ private static Pack createClientPackAdapter(String p_167934_, Component p_167935_, boolean p_167936_, Supplier<PackResources> p_167937_, PackMetadataSection p_167938_, Pack.Position p_167939_, PackSource p_167940_, boolean hidden) {
+ int i = p_167938_.getPackFormat(PackType.CLIENT_RESOURCES);
Supplier<PackResources> supplier = p_167937_;
if (i <= 3) {
supplier = m_91330_(p_167937_);
@@ -2699,7 +_,7 @@
supplier = m_91352_(supplier);
}
- return new Pack(p_167934_, p_167935_, p_167936_, supplier, p_167938_, PackType.CLIENT_RESOURCES, p_167939_, p_167940_);
+ return new Pack(p_167934_, p_167935_, p_167936_, supplier, p_167938_, PackType.CLIENT_RESOURCES, p_167939_, p_167940_, hidden);
}
private static Supplier<PackResources> m_91330_(Supplier<PackResources> p_91331_) {
@@ -2716,6 +_,19 @@
@@ -2737,6 +_,19 @@
public void m_91312_(int p_91313_) {
this.f_91051_.m_119410_(p_91313_);
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/MouseHandler.java
+++ b/net/minecraft/client/MouseHandler.java
@@ -69,6 +_,7 @@
@@ -73,6 +_,7 @@
this.f_91510_ = -1;
}
@@ -8,7 +8,7 @@
boolean[] aboolean = new boolean[]{false};
if (this.f_91503_.m_91265_() == null) {
if (this.f_91503_.f_91080_ == null) {
@@ -82,11 +_,19 @@
@@ -86,11 +_,19 @@
if (flag) {
screen.m_169415_();
Screen.m_96579_(() -> {
@@ -30,7 +30,7 @@
}, "mouseReleased event handler", screen.getClass().getCanonicalName());
}
}
@@ -110,19 +_,26 @@
@@ -114,19 +_,26 @@
}
}
}
@@ -60,7 +60,7 @@
} else if (this.f_91503_.f_91074_ != null) {
if (this.f_91518_ != 0.0D && Math.signum(d0) != Math.signum(this.f_91518_)) {
this.f_91518_ = 0.0D;
@@ -135,6 +_,7 @@
@@ -139,6 +_,7 @@
}
this.f_91518_ -= (double)i;
@@ -68,7 +68,7 @@
if (this.f_91503_.f_91074_.m_5833_()) {
if (this.f_91503_.f_91065_.m_93085_().m_94768_()) {
this.f_91503_.f_91065_.m_93085_().m_205380_(-i);
@@ -203,7 +_,9 @@
@@ -207,7 +_,9 @@
double d2 = (p_91563_ - this.f_91507_) * (double)this.f_91503_.m_91268_().m_85445_() / (double)this.f_91503_.m_91268_().m_85443_();
double d3 = (p_91564_ - this.f_91508_) * (double)this.f_91503_.m_91268_().m_85446_() / (double)this.f_91503_.m_91268_().m_85444_();
Screen.m_96579_(() -> {
@@ -79,7 +79,7 @@
}, "mouseDragged event handler", screen.getClass().getCanonicalName());
}
@@ -286,6 +_,14 @@
@@ -290,6 +_,14 @@
public double m_91594_() {
return this.f_91508_;
@@ -1,16 +1,16 @@
--- a/net/minecraft/client/Options.java
+++ b/net/minecraft/client/Options.java
@@ -539,7 +_,8 @@
@@ -544,7 +_,8 @@
}, new OptionInstance.LazyEnum<>(() -> {
return Stream.concat(Stream.of(""), Minecraft.m_91087_().m_91106_().m_194525_().stream()).toList();
}, (p_232011_) -> {
- return Minecraft.m_91087_().m_91396_() && p_232011_ != "" && !Minecraft.m_91087_().m_91106_().m_194525_().contains(p_232011_) ? Optional.empty() : Optional.of(p_232011_);
+ // FORGE: fix incorrect string comparison - PR #8767
+ return Minecraft.m_91087_().m_91396_() && (p_232011_ == null || !p_232011_.isEmpty()) && !Minecraft.m_91087_().m_91106_().m_194525_().contains(p_232011_) ? Optional.empty() : Optional.of(p_232011_);
}, Codec.STRING), "", (p_231994_) -> {
}, Codec.STRING), "", (p_275584_) -> {
SoundManager soundmanager = Minecraft.m_91087_().m_91106_();
soundmanager.m_194526_();
@@ -796,6 +_,7 @@
@@ -868,6 +_,7 @@
}
public Options(Minecraft p_92138_, File p_92139_) {
@@ -18,9 +18,9 @@
this.f_92060_ = p_92138_;
this.f_92110_ = new File(p_92139_, "options.txt");
boolean flag = p_92138_.m_91103_();
@@ -910,11 +_,20 @@
p_168428_.m_213982_("chatPreview", this.f_231796_);
p_168428_.m_213982_("onlyShowSecureChat", this.f_231798_);
@@ -989,11 +_,20 @@
p_168428_.m_213982_("telemetryOptInExtra", this.f_260461_);
this.f_263744_ = p_168428_.m_142682_("onboardAccessibility", this.f_263744_);
+ processOptionsForge(p_168428_);
+ }
@@ -41,7 +41,7 @@
}
}
@@ -935,6 +_,9 @@
@@ -1012,6 +_,9 @@
}
public void m_92140_() {
@@ -51,7 +51,7 @@
try {
if (!this.f_92110_.exists()) {
return;
@@ -979,7 +_,8 @@
@@ -1040,7 +_,8 @@
}
}
@@ -61,15 +61,15 @@
@Nullable
private String m_168458_(String p_168459_) {
return compoundtag1.m_128441_(p_168459_) ? compoundtag1.m_128461_(p_168459_) : null;
@@ -1082,6 +_,7 @@
@@ -1203,6 +_,7 @@
}
public void m_92169_() {
public void m_92172_() {
+ if (net.minecraftforge.client.loading.ClientModLoader.isLoading()) return; //Don't save settings before mods add keybindigns and the like to prevent them from being deleted.
try {
final PrintWriter printwriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.f_92110_), StandardCharsets.UTF_8));
if (this.f_92060_.f_91074_ != null) {
int i = 0;
@@ -1229,6 +_,23 @@
@@ -1267,6 +_,23 @@
}
p_92146_.m_10509_(set);
@@ -18,14 +18,16 @@
private final List<ItemStack> f_92261_;
private RecipeBookCategories(ItemStack... p_92267_) {
@@ -54,11 +_,15 @@
case SMOKER:
return f_92256_;
@@ -59,7 +_,7 @@
list = f_92256_;
break;
default:
- return ImmutableList.of();
- throw new IncompatibleClassChangeError();
+ return net.minecraftforge.client.RecipeBookManager.getCustomCategoriesOrEmpty(p_92270_);
}
}
return list;
@@ -67,5 +_,9 @@
public List<ItemStack> m_92268_() {
return this.f_92261_;
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/Screenshot.java
+++ b/net/minecraft/client/Screenshot.java
@@ -55,13 +_,23 @@
@@ -56,13 +_,23 @@
file2 = new File(file1, p_92307_);
}
@@ -10,7 +10,7 @@
private final Map<Block, Set<Property<?>>> f_92572_ = Maps.newHashMap();
public static BlockColors m_92574_() {
@@ -73,11 +_,12 @@
@@ -80,11 +_,12 @@
blockcolors.m_92589_((p_92596_, p_92597_, p_92598_, p_92599_) -> {
return p_92597_ != null && p_92598_ != null ? 2129968 : 7455580;
}, Blocks.f_50196_);
@@ -19,16 +19,16 @@
}
public int m_92582_(BlockState p_92583_, Level p_92584_, BlockPos p_92585_) {
- BlockColor blockcolor = this.f_92571_.m_7942_(Registry.f_122824_.m_7447_(p_92583_.m_60734_()));
- BlockColor blockcolor = this.f_92571_.m_7942_(BuiltInRegistries.f_256975_.m_7447_(p_92583_.m_60734_()));
+ BlockColor blockcolor = this.f_92571_.get(net.minecraftforge.registries.ForgeRegistries.BLOCKS.getDelegateOrThrow(p_92583_.m_60734_()));
if (blockcolor != null) {
return blockcolor.m_92566_(p_92583_, (BlockAndTintGetter)null, (BlockPos)null, 0);
} else {
@@ -87,13 +_,15 @@
@@ -94,13 +_,15 @@
}
public int m_92577_(BlockState p_92578_, @Nullable BlockAndTintGetter p_92579_, @Nullable BlockPos p_92580_, int p_92581_) {
- BlockColor blockcolor = this.f_92571_.m_7942_(Registry.f_122824_.m_7447_(p_92578_.m_60734_()));
- BlockColor blockcolor = this.f_92571_.m_7942_(BuiltInRegistries.f_256975_.m_7447_(p_92578_.m_60734_()));
+ BlockColor blockcolor = this.f_92571_.get(net.minecraftforge.registries.ForgeRegistries.BLOCKS.getDelegateOrThrow(p_92578_.m_60734_()));
return blockcolor == null ? -1 : blockcolor.m_92566_(p_92578_, p_92579_, p_92580_, p_92581_);
}
@@ -37,7 +37,7 @@
+ @Deprecated
public void m_92589_(BlockColor p_92590_, Block... p_92591_) {
for(Block block : p_92591_) {
- this.f_92571_.m_122664_(p_92590_, Registry.f_122824_.m_7447_(block));
- this.f_92571_.m_122664_(p_92590_, BuiltInRegistries.f_256975_.m_7447_(block));
+ this.f_92571_.put(net.minecraftforge.registries.ForgeRegistries.BLOCKS.getDelegateOrThrow(block), p_92590_);
}
@@ -19,7 +19,7 @@
}
public int m_92676_(ItemStack p_92677_, int p_92678_) {
- ItemColor itemcolor = this.f_92674_.m_7942_(Registry.f_122827_.m_7447_(p_92677_.m_41720_()));
- ItemColor itemcolor = this.f_92674_.m_7942_(BuiltInRegistries.f_257033_.m_7447_(p_92677_.m_41720_()));
+ ItemColor itemcolor = this.f_92674_.get(net.minecraftforge.registries.ForgeRegistries.ITEMS.getDelegateOrThrow(p_92677_.m_41720_()));
return itemcolor == null ? -1 : itemcolor.m_92671_(p_92677_, p_92678_);
}
@@ -0,0 +1,20 @@
--- a/net/minecraft/client/gui/Font.java
+++ b/net/minecraft/client/gui/Font.java
@@ -33,7 +_,7 @@
import org.joml.Vector3f;
@OnlyIn(Dist.CLIENT)
-public class Font {
+public class Font implements net.minecraftforge.client.extensions.IForgeFont {
private static final float f_168643_ = 0.01F;
private static final Vector3f f_92712_ = new Vector3f(0.0F, 0.0F, 0.03F);
public static final int f_193827_ = 8;
@@ -260,6 +_,8 @@
public StringSplitter m_92865_() {
return this.f_92714_;
}
+
+ @Override public Font self() { return this; }
@OnlyIn(Dist.CLIENT)
public static enum DisplayMode {
@@ -1,14 +1,6 @@
--- a/net/minecraft/client/gui/Gui.java
+++ b/net/minecraft/client/gui/Gui.java
@@ -389,6 +_,7 @@
if (this.f_92986_.f_91076_ != null && this.f_92986_.f_91076_ instanceof LivingEntity && f >= 1.0F) {
flag = this.f_92986_.f_91074_.m_36333_() > 5.0F;
flag &= this.f_92986_.f_91076_.m_6084_();
+ flag &= this.f_92986_.f_91074_.canHit(this.f_92986_.f_91076_, 0);
}
int j = this.f_92978_ / 2 - 7 + 16;
@@ -441,6 +_,10 @@
@@ -428,6 +_,10 @@
for(MobEffectInstance mobeffectinstance : Ordering.natural().reverse().sortedCopy(collection)) {
MobEffect mobeffect = mobeffectinstance.m_19544_();
@@ -19,15 +11,15 @@
if (mobeffectinstance.m_19575_()) {
int i = this.f_92977_;
int j = 1;
@@ -469,6 +_,7 @@
@@ -456,6 +_,7 @@
}
}
+ if (renderer.renderGuiIcon(mobeffectinstance, this, p_93029_, i, j, this.m_93252_(), f)) continue;
+ if (renderer.renderGuiIcon(mobeffectinstance, this, p_93029_, i, j, 0, f)) continue;
TextureAtlasSprite textureatlassprite = mobeffecttexturemanager.m_118732_(mobeffect);
int l = i;
int i1 = j;
@@ -598,12 +_,13 @@
float f1 = f;
@@ -583,12 +_,13 @@
public void m_93069_(PoseStack p_93070_) {
this.f_92986_.m_91307_().m_6180_("selectedItemName");
if (this.f_92993_ > 0 && !this.f_92994_.m_41619_()) {
@@ -43,9 +35,9 @@
int j = (this.f_92977_ - i) / 2;
int k = this.f_92978_ - 59;
if (!this.f_92986_.f_91072_.m_105205_()) {
@@ -619,7 +_,13 @@
RenderSystem.m_69478_();
RenderSystem.m_69453_();
@@ -602,7 +_,13 @@
if (l > 0) {
m_93172_(p_93070_, j - 2, k - 2, j + i + 2, k + 9 + 2, this.f_92986_.f_91066_.m_92143_(0));
- this.m_93082_().m_92763_(p_93070_, mutablecomponent, (float)j, (float)k, 16777215 + (l << 24));
+ Font font = net.minecraftforge.client.extensions.common.IClientItemExtensions.of(f_92994_).getFont(f_92994_, net.minecraftforge.client.extensions.common.IClientItemExtensions.FontContext.SELECTED_ITEM_NAME);
@@ -55,10 +47,10 @@
+ j = (this.f_92977_ - font.m_92852_(highlightTip)) / 2;
+ font.m_92763_(p_93070_, highlightTip, (float)j, (float)k, 16777215 + (l << 24));
+ }
RenderSystem.m_69461_();
}
}
@@ -1133,7 +_,7 @@
@@ -1054,7 +_,7 @@
ItemStack itemstack = this.f_92986_.f_91074_.m_150109_().m_36056_();
if (itemstack.m_41619_()) {
this.f_92993_ = 0;
@@ -6,5 +6,5 @@
if (!p_93294_ || mapdecoration.m_77809_()) {
+ if (mapdecoration.render(k)) { k++; continue; }
p_93292_.m_85836_();
p_93292_.m_85837_((double)(0.0F + (float)mapdecoration.m_77804_() / 2.0F + 64.0F), (double)(0.0F + (float)mapdecoration.m_77805_() / 2.0F + 64.0F), (double)-0.02F);
p_93292_.m_85845_(Vector3f.f_122227_.m_122240_((float)(mapdecoration.m_77806_() * 360) / 16.0F));
p_93292_.m_252880_(0.0F + (float)mapdecoration.m_77804_() / 2.0F + 64.0F, 0.0F + (float)mapdecoration.m_77805_() / 2.0F + 64.0F, -0.02F);
p_93292_.m_252781_(Axis.f_252403_.m_252977_((float)(mapdecoration.m_77806_() * 360) / 16.0F));
@@ -0,0 +1,11 @@
--- a/net/minecraft/client/gui/components/AbstractButton.java
+++ b/net/minecraft/client/gui/components/AbstractButton.java
@@ -32,7 +_,7 @@
RenderSystem.m_69482_();
m_275833_(p_275468_, this.m_252754_(), this.m_252907_(), this.m_5711_(), this.m_93694_(), 20, 4, 200, 20, 0, this.m_274533_());
RenderSystem.m_157429_(1.0F, 1.0F, 1.0F, 1.0F);
- int i = this.f_93623_ ? 16777215 : 10526880;
+ int i = getFGColor();
this.m_274375_(p_275468_, minecraft.f_91062_, i | Mth.m_14167_(this.f_93625_ * 255.0F) << 24);
}
@@ -1,7 +1,7 @@
--- a/net/minecraft/client/gui/components/AbstractSelectionList.java
+++ b/net/minecraft/client/gui/components/AbstractSelectionList.java
@@ -536,6 +_,13 @@
@@ -515,6 +_,13 @@
return new ScreenRectangle(this.f_93393_, this.f_93390_, this.f_93392_ - this.f_93393_, this.f_93391_ - this.f_93390_);
}
+ public int getWidth() { return this.f_93388_; }
@@ -12,5 +12,5 @@
+ public int getRight() { return this.f_93392_; }
+
@OnlyIn(Dist.CLIENT)
public abstract static class Entry<E extends AbstractSelectionList.Entry<E>> implements GuiEventListener {
protected abstract static class Entry<E extends AbstractSelectionList.Entry<E>> implements GuiEventListener {
/** @deprecated */
@@ -1,15 +1,6 @@
--- a/net/minecraft/client/gui/components/AbstractWidget.java
+++ b/net/minecraft/client/gui/components/AbstractWidget.java
@@ -85,7 +_,7 @@
this.m_93228_(p_93676_, this.f_93620_, this.f_93621_, 0, 46 + i * 20, this.f_93618_ / 2, this.f_93619_);
this.m_93228_(p_93676_, this.f_93620_ + this.f_93618_ / 2, this.f_93621_, 200 - this.f_93618_ / 2, 46 + i * 20, this.f_93618_ / 2, this.f_93619_);
this.m_7906_(p_93676_, minecraft, p_93677_, p_93678_);
- int j = this.f_93623_ ? 16777215 : 10526880;
+ int j = getFGColor();
m_93215_(p_93676_, font, this.m_6035_(), this.f_93620_ + this.f_93618_ / 2, this.f_93621_ + (this.f_93619_ - 8) / 2, j | Mth.m_14167_(this.f_93625_ * 255.0F) << 24);
}
@@ -180,6 +_,10 @@
@@ -231,6 +_,10 @@
this.f_93618_ = p_93675_;
}
@@ -20,9 +11,9 @@
public void m_93650_(float p_93651_) {
this.f_93625_ = p_93651_;
}
@@ -202,6 +_,19 @@
@@ -261,6 +_,19 @@
protected void m_93692_(boolean p_93693_) {
public void m_93692_(boolean p_93693_) {
this.f_93616_ = p_93693_;
+ }
+
@@ -6,10 +6,10 @@
int k = i / 2 - 91;
+ var event = net.minecraftforge.client.ForgeHooksClient.onCustomizeBossEventProgress(p_93705_, this.f_93698_.m_91268_(), lerpingbossevent, k, j, 10 + this.f_93698_.f_91062_.f_92710_);
+ if (!event.isCanceled()) {
RenderSystem.m_157429_(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.m_157456_(0, f_93697_);
this.m_93706_(p_93705_, k, j, lerpingbossevent);
@@ -42,7 +_,8 @@
Component component = lerpingbossevent.m_18861_();
@@ -41,7 +_,8 @@
int i1 = i / 2 - l / 2;
int j1 = j - 9;
this.f_93698_.f_91062_.m_92763_(p_93705_, component, (float)i1, (float)j1, 16777215);
@@ -0,0 +1,29 @@
--- a/net/minecraft/client/gui/components/Button.java
+++ b/net/minecraft/client/gui/components/Button.java
@@ -29,6 +_,11 @@
this.f_252416_ = p_259552_;
}
+ protected Button(Builder builder) {
+ this(builder.f_252538_, builder.f_252462_, builder.f_252510_, builder.f_252447_, builder.f_252401_, builder.f_252468_, builder.f_252431_);
+ m_257544_(builder.f_256855_); // Forge: Make use of the Builder tooltip
+ }
+
public void m_5691_() {
this.f_93717_.m_93750_(this);
}
@@ -92,9 +_,11 @@
}
public Button m_253136_() {
- Button button = new Button(this.f_252538_, this.f_252462_, this.f_252510_, this.f_252447_, this.f_252401_, this.f_252468_, this.f_252431_);
- button.m_257544_(this.f_256855_);
- return button;
+ return build(Button::new);
+ }
+
+ public Button build(java.util.function.Function<Builder, Button> builder) {
+ return builder.apply(this);
}
}
@@ -3,7 +3,7 @@
@@ -409,6 +_,7 @@
list.add("");
list.add(ChatFormatting.UNDERLINE + "Targeted Entity");
list.add(String.valueOf((Object)Registry.f_122826_.m_7981_(entity.m_6095_())));
list.add(String.valueOf((Object)BuiltInRegistries.f_256780_.m_7981_(entity.m_6095_())));
+ entity.m_6095_().m_204041_().m_203616_().forEach(t -> list.add("#" + t.f_203868_()));
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/gui/components/toasts/ToastComponent.java
+++ b/net/minecraft/client/gui/components/toasts/ToastComponent.java
@@ -103,6 +_,7 @@
@@ -102,6 +_,7 @@
}
public void m_94922_(Toast p_94923_) {
@@ -0,0 +1,11 @@
--- a/net/minecraft/client/gui/screens/ChatScreen.java
+++ b/net/minecraft/client/gui/screens/ChatScreen.java
@@ -225,7 +_,7 @@
this.f_96541_.f_91074_.f_108617_.m_246175_(p_242400_);
}
- return true;
+ return f_96541_.f_91080_ == this; // FORGE: Prevent closing the screen if another screen has been opened.
}
}
@@ -1,11 +0,0 @@
--- a/net/minecraft/client/gui/screens/ConnectScreen.java
+++ b/net/minecraft/client/gui/screens/ConnectScreen.java
@@ -73,6 +_,8 @@
}
if (!optional.isPresent()) {
+ ConnectScreen.f_95683_.error("Couldn't connect to server: Unknown host \"{}\"", p_169266_.m_171863_());
+ net.minecraftforge.network.DualStackUtils.logInitialPreferences();
p_169265_.execute(() -> {
p_169265_.m_91152_(new DisconnectedScreen(ConnectScreen.this.f_95686_, CommonComponents.f_130661_, ConnectScreen.f_169260_));
});
@@ -1,10 +0,0 @@
--- a/net/minecraft/client/gui/screens/DeathScreen.java
+++ b/net/minecraft/client/gui/screens/DeathScreen.java
@@ -36,6 +_,7 @@
})));
this.f_169295_.add(this.m_142416_(new Button(this.f_96543_ / 2 - 100, this.f_96544_ / 4 + 96, 200, 20, Component.m_237115_("deathScreen.titleScreen"), (p_95925_) -> {
if (this.f_95908_) {
+ m_95931_(true);
this.m_95934_();
} else {
ConfirmScreen confirmscreen = new ConfirmScreen(this::m_95931_, Component.m_237115_("deathScreen.quit.confirm"), CommonComponents.f_237098_, Component.m_237115_("deathScreen.titleScreen"), Component.m_237115_("deathScreen.respawn"));
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/gui/screens/MenuScreens.java
+++ b/net/minecraft/client/gui/screens/MenuScreens.java
@@ -40,6 +_,10 @@
@@ -41,6 +_,10 @@
private static final Map<MenuType<?>, MenuScreens.ScreenConstructor<?, ?>> f_96196_ = Maps.newHashMap();
public static <T extends AbstractContainerMenu> void m_96201_(@Nullable MenuType<T> p_96202_, Minecraft p_96203_, int p_96204_, Component p_96205_) {
@@ -11,9 +11,9 @@
if (p_96202_ == null) {
f_96195_.warn("Trying to open invalid screen with name: {}", (Object)p_96205_.getString());
} else {
@@ -47,9 +_,10 @@
@@ -48,9 +_,10 @@
if (screenconstructor == null) {
f_96195_.warn("Failed to create screen for menu type: {}", (Object)Registry.f_122863_.m_7981_(p_96202_));
f_96195_.warn("Failed to create screen for menu type: {}", (Object)BuiltInRegistries.f_256818_.m_7981_(p_96202_));
} else {
- screenconstructor.m_96209_(p_96205_, p_96202_, p_96203_, p_96204_);
+ return java.util.Optional.of(screenconstructor);
@@ -1,10 +1,9 @@
--- a/net/minecraft/client/gui/screens/OptionsScreen.java
+++ b/net/minecraft/client/gui/screens/OptionsScreen.java
@@ -140,4 +_,12 @@
m_93215_(p_96249_, this.f_96547_, this.f_96539_, this.f_96543_ / 2, 15, 16777215);
super.m_6305_(p_96249_, p_96250_, p_96251_, p_96252_);
@@ -151,6 +_,14 @@
super.m_86412_(p_96249_, p_96250_, p_96251_, p_96252_);
}
+
+ @Override
+ public void m_7379_() {
+ // We need to consider 2 potential parent screens here:
@@ -12,4 +11,7 @@
+ // 2. From the pause menu, in which case exit back to game
+ this.f_96541_.m_91152_(this.f_96235_ instanceof PauseScreen ? null : this.f_96235_);
+ }
}
+
private Button m_260993_(Component p_261565_, Supplier<Screen> p_262119_) {
return Button.m_253074_(p_261565_, (p_261361_) -> {
this.f_96541_.m_91152_(p_262119_.get());
@@ -1,14 +0,0 @@
--- a/net/minecraft/client/gui/screens/PauseScreen.java
+++ b/net/minecraft/client/gui/screens/PauseScreen.java
@@ -77,9 +_,10 @@
this.f_96541_.m_91152_(new SocialInteractionsScreen());
}));
}
+ this.m_142416_(new Button(this.f_96543_ / 2 - 102, this.f_96544_ / 4 + 120 - 16, 204, 20, Component.m_237115_("fml.menu.mods"), modButton -> this.f_96541_.m_91152_(new net.minecraftforge.client.gui.ModListScreen(this))));
Component component = this.f_96541_.m_91090_() ? Component.m_237115_("menu.returnToMenu") : Component.m_237115_("menu.disconnect");
- this.m_142416_(new Button(this.f_96543_ / 2 - 102, this.f_96544_ / 4 + 120 + -16, 204, 20, component, (p_96315_) -> {
+ this.m_142416_(new Button(this.f_96543_ / 2 - 102, this.f_96544_ / 4 + 144 + -16, 204, 20, component, (p_96315_) -> {
boolean flag = this.f_96541_.m_91090_();
boolean flag1 = this.f_96541_.m_91294_();
p_96315_.f_93623_ = false;
Loaded 100 of 1032 files, more files were not shown because too many files have changed in this diff. Show more