Compare commits

..
377 Commits
Author SHA1 Message Date
LexManos 664a2c06ad 1.19.4 RB 2 2023-09-21 11:52:10 -07:00
Paint_Ninjaandmalte0811 e85295a837 Fix Level leak in debug HUD (#9700)
Co-authored-by: malte0811 <[email protected]>
2023-09-02 17:02:03 -07:00
Paint_Ninjaanddhyces 21582ef641 Keep order of sources in PackRepository (#9703)
Co-authored-by: dhyces <[email protected]>
2023-09-02 16:21:21 -07:00
Paint_Ninja 020ec3d51b Fix DelegatingPackResources searching resource path twice (#9698) 2023-08-30 14:10:38 -07:00
Jonathan Colmenares 774b79e609 Fix parameter names in IForgeDimensionSpecialEffects.adjustLightmapColors to better describe what they are. (#9657) 2023-07-17 09:45:29 -07:00
Jonathan Colmenares ee6022b006 Add mods button to pause menu (#9654) 2023-07-17 08:41:11 -07:00
brandon3055 37efd2b8ae Fixed FluidUtil#tryFillContainer returning invalid result when simulating #9358 2023-07-15 10:52:26 -07:00
Random832 cf3d42ef38 Fix conflicting keybindings not having the correct click count set. #9360 2023-07-15 10:44:29 -07:00
RealMangoRage 03a505a3f2 Fixed AdvancementsScreen.java to render next/previous page buttons now. (#9650) 2023-07-14 19:22:33 -07:00
ChampionAsh5357 bdc856edf5 Properly Handle Fluid Updates while in a Boat Fix #9256 (#9428) 2023-07-14 19:06:07 -07:00
Silver_David 6eebea0896 New hook in IClientBlockExtensions to handle enabling tinting on breaking particles. #9446 2023-07-14 16:08:51 -07:00
Technici4n 9218e08955 Add EmptyEnergyStorage (#9487) 2023-07-14 11:34:12 -07:00
Paint_Ninja 7006fe4f29 Support IPv6 address compression for logged IPs 2023-07-14 11:30:57 -07:00
Paint_Ninja 301e7252b0 Add option to advertise dedicated servers to LAN. (#9502) 2023-07-14 07:07:36 -07:00
ChampionAsh5357 2b5852072c Add particle description data provider (#9510) 2023-07-13 19:35:07 -07:00
bconlon 0b38373a45 AlterGroundEvent for modifying block placement performed by AlterGroundDecorator (#9636) 2023-07-13 17:30:45 -07:00
Paint_Ninja 07e538f3dd Improve logging for server connections (#9622) 2023-07-11 11:04:45 -07:00
cech12 b39e300c1b Fix placing fluids into waterlogged blocks with FluidUtil::tryPlaceFluid. To behave more like vanilla MC-127110 (#9519) 2023-07-11 11:04:26 -07:00
LexManos 50db51a366 Add proper duplicate mod error message that includes the mod id(s) and mod files. (#9474) 2023-06-20 14:18:14 -07:00
LexManos df80acc49d Make 1.20.x the main TC branch. 2023-06-20 14:07:42 -07:00
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
LexManos 617ecb7ad3 43.2 Recommended Build. 2022-12-07 23:20:28 -08:00
Su5eD dd8239c255 Allow discovering services from the mods folder that use java's modular definition. (#9143) 2022-11-30 14:50:30 -08:00
Brennan Ward bdddc8c5d4 Make Datapack Registries support ICondition(s) (#9113) 2022-11-30 14:45:00 -08:00
Marc Hermans c1c619a109 Enable additional build types to handle pull request validation. (#9159) 2022-11-30 14:08:05 -08:00
sciwhiz12 f7eea35cb9 Check source permission level before selector permission (#9147)
In some situations, such as execution of a function by an advancement as
part of its reward, a command source stack may have a backing source of
a ServerPlayer which may lack the entity selector permission and have an
explicit permission level that should allow the use of entity selectors,
through CommandSourceStack#withPermission.

We now check if the permission level of the command source stack is
sufficient for entity selectors _before_ checking if the source is a
player and if they have the requisite permission.

This means that an operator permission level of 2 will always override
the Forge entity selector permission.

Fixes #9137
2022-11-30 14:07:18 -08:00
J.T. McQuigg a2434e1607 Fix fires spreading too/igniting custom portal frames. (#9142) 2022-11-30 14:06:22 -08:00
Minecraftschurli b806ecd0ff Add supplier to FlowerBlock so it works with custom MobEffects (#9139) 2022-11-30 14:04:15 -08:00
KnightMiner 8358b800e9 Fix some logical bugs related to the Grindstone Event (#9089) 2022-11-30 14:00:59 -08:00
Néstor Amador b25a092cf1 Call baked model's getModelData before getRenderTypes (#9163) 2022-11-28 17:53:19 +01:00
Jonathan Coates d812fdd0a3 Make Util.memoize thread-safe (#9155) 2022-11-23 09:18:04 +01:00
Néstor Amador 2d3ee8a228 Rendering tweaks and fixes: Part 4 (#9065) 2022-11-23 09:17:43 +01:00
matyrobbrt 97a9ca65f1 Fix Transformation loading PoseStack (#9083) 2022-11-21 20:49:41 +01:00
Néstor Amador f043fbbc51 Add simple block appearance API (#9066) 2022-11-21 20:48:30 +01:00
SizableShrimp a19ab3a8d4 Fix invalidated modded packets when on LAN (#9157) 2022-11-20 18:34:21 -08:00
Dennis C bc0517a404 Improve extensibility of DetectorRailBlock and PoweredRailBlock (#9130) 2022-11-08 12:18:48 -08:00
Su5eD dc9c0d7e35 Fix launch handler minecraft classpath locator (#9120) 2022-11-08 12:17:48 -08:00
matyrobbrt 1a2207b3b2 Add HitResult to EntityTeleportEvent$EnderPearl (#9135) 2022-11-08 12:17:18 -08:00
sciwhiz12 84ab9764e3 Throw aggregate exception for erroneous registry event dispatch (#9111)
This means that exceptions occurring during the dispatch of the registry
events, such as those from the suppliers of RegistryObjects, properly
cause a crash rather than merely being logged and allowing the game to
reach the main menu.

Fixes #8720
2022-11-08 12:17:00 -08:00
Nexus-Dino bef68991f0 Add missing semi-colon near the Dist import statement in example mod. 2022-11-08 12:16:49 -08:00
Nexus-Dino 526c13d662 Fix ClientModEvents example not subscribing to client-sided events (#9097) 2022-10-21 13:22:57 -07:00
matyrobbrt 67ffb78c23 Use GitHub action to lock issues with the spam label (#9087) 2022-10-21 13:22:31 -07:00
sciwhiz12 8f407c3a22 Remove structures slave map to Feature registry (#9091) 2022-10-21 13:22:18 -07:00
Dennis C 8980252e16 Improve logging of missing or unsupported dependencies (#9104) 2022-10-21 13:21:49 -07:00
DBotThePony 3baee80485 [1.19.x] Fix ValueSpec caching the return value incorrectly (#9046) 2022-10-14 17:43:21 +01:00
eerussianguy ca9599bb4d [1.19.x] Add event for registering spawn placements, and modifying existing (#9024) 2022-10-14 17:39:14 +01:00
eerussianguy 051db7907a [1.19.x] Add event for items being stacked or swapped in a GUI. (#9050) 2022-10-14 17:30:12 +01:00
Magnus Jensen bdba5ef2d3 [1.19.x] Fix PlayerInteractEvent.EntityInteractSpecific not cancelling on a server (#9079) 2022-10-14 17:18:17 +01:00
DBotThePony 838b9c0eda Fix canceling phantom spawns preventing any further attempts that tick. (#9041) 2022-10-13 11:40:56 -07:00
Magnus Jensen be584c54aa Rename fluid type milk translation keys (#9077) 2022-10-12 12:54:34 -07:00
ChampionAsh5357 1b3640b42d Fix minecart speed with water (#9076) 2022-10-12 12:51:13 -07:00
Sara Freimer be5dab5801 Add a cancellable event that gets fired when a Totem of Undying is used (#9069) 2022-10-12 12:46:08 -07:00
Marc Hermans aae628cd18 Fix performance issue and logging when resource caching is enabled (#9029) 2022-10-12 12:00:25 -07:00
Magnus Jensen e7bd08649f Fix NPE when feeding wolves and cats (#9074) 2022-10-07 19:55:40 -07:00
Paint_Ninja e1017a4c7e Fix logically breaking change to ForgeConfigSpec.Builder#comment where modders could not add a empty line to the start of comments. (#9061) 2022-10-03 11:50:32 -07:00
SizableShrimp 6f8ba9d295 Fix ServiceLoader bug
Signed-off-by: SizableShrimp <[email protected]>
2022-09-29 22:11:12 -07:00
SizableShrimp ac94478ae7 Fix ClientChatReceivedEvent for system messages
Signed-off-by: SizableShrimp <[email protected]>
2022-09-29 22:11:12 -07:00
DBotThePony 515c6082e0 Make ForgeConfigSpec$Builder.comment able to be called multiple times for the same entry. (#9056) 2022-09-27 23:23:59 -07:00
ChampionAsh5357 a753598113 Fix control modifier for mac with KeyMappings using Alt instead of Super (#9057) 2022-09-27 11:15:11 -07:00
J.T. McQuigg bc6c4be4e4 Fix is_desert tag not being applied correctly. (#9051) 2022-09-26 11:08:24 -07:00
Andy Dingley 98823a8f07 Fix mob griefing event for SmallFireballs not using owner entity. (#9038) 2022-09-21 13:24:25 -07:00
David Dempsey ecd8f1e54d Fix minecarts on rails not properly slowing down in water (#9033) 2022-09-21 13:20:03 -07:00
LexManos b0caea8e20 Change codestyle for BookShelves tag. Closes #9027
Add IS_CAVE tag Closes #8885
Add IS_DESERT tag Closes #8979
Simplify Mangrove Swamp tags Closes #8980
2022-09-09 11:27:39 -07:00
nikita488 e0477f8776 Allow faces of an "elements" model to have disabled ambient occlusion (#9019) 2022-09-08 16:17:44 +01:00
bconlon 94e1e54b53 [1.19.x] Recipe ID-based grouping between modded and vanilla recipes. (#8876) 2022-09-08 16:13:07 +01:00
nitro_dynamite18 9ce72331b7 Update fence_gates/wooden (#8936) 2022-09-08 16:12:26 +01:00
Jais Djurhuus-Kempel 1c31f42dae [1.19.x] Added event for growing fungus (#8981) 2022-09-08 16:11:59 +01:00
agnor99 0978a94358 Added Bookshelves block tag (#8991) 2022-09-08 16:11:39 +01:00
GizmoTheMoonPig e956a9c9f3 Create a Forge EntityType Tag for Bosses (#9017) 2022-09-08 16:10:48 +01:00
Monica S 82026d11a4 Allow mods to specify shader import namespace (#9021) 2022-09-08 16:10:31 +01:00
ferriarnus 6d233ac75c Grindstone Events (#8934)
One to modify the output, and one to modify the input.
2022-09-08 15:55:43 +01:00
Reece Mackie 73c1934e7e Fix the serialized names of the enum (#9014) 2022-09-04 19:46:54 +01:00
Technici4n bf1194ec75 Fix tryEmptyContainerAndStow duping fluids with stackable containers (#9004) 2022-09-03 21:48:07 +01:00
Ted Senft 22d2740725 Add mod mismatch event (#8989) 2022-09-03 21:26:43 +01:00
Minecraftschurli 02f26dc7a1 [1.19.x] add methods with more context to tree growers (#8956) 2022-09-03 20:24:00 +01:00
28Pollux28 db14cbb0e0 [1.19.X] Adding more precise events for Advancements (#8360) 2022-09-03 20:17:16 +01:00
Luke BemishandLexManos a2b35cae92 Default IItemHandler capability for shulker box itemstacks (#8827)
Co-authored-by: LexManos <[email protected]>
2022-09-03 20:16:25 +01:00
Take Weiland aab0e4c82e [1.19] Add hook for items to remain in the hotbar when picking blocks/entities (#8872) 2022-09-03 20:12:13 +01:00
Reece Mackieandsciwhiz12 6609708d14 [1.19.x] Block Model Builder Root Transform Support (#8860)
Co-authored-by: sciwhiz12 <[email protected]>
2022-09-03 20:11:46 +01:00
Budschie ed198f062c [1.19.x] Make LivingSetAttackTargetEvent compatible with the Brain/Behavior system. (Port of PR #8918) (#8954) 2022-09-03 20:11:10 +01:00
thedarkcolour 4e2d656e9d [1.19.x] Add IForgeBlock#onTreeGrow to replace IForgeBlock#onPlantGrow from 1.16 (#8999) 2022-09-03 20:07:09 +01:00
bconlon 2a4230868f [1.19.x] Moved Player.resetAttackStrengthTicker() to the end of Player.attack() (#9000) 2022-09-03 20:06:35 +01:00
Sirttas cf778b1759 fix misplaced patch in sapling block (#9005) 2022-09-03 20:05:37 +01:00
Brennan Ward 1b6daddd40 Fix failed entity interactions consuming the click. (#9007) 2022-09-03 20:00:57 +01:00
sciwhiz12 d7b137d144 Fix entity selector permission check to check original source (#8995)
Permission checks should be against the command source and not the
target entity, as is done in vanilla.

Fixes #8994
2022-08-25 11:42:11 -07:00
한현빈 b5d5855559 Hotfix for 1.19.2 item animation bug (#8987)
* [HOT FIX]: Fixes #8985 by no-oping for vanilla models instead of throwing error

Signed-off-by: tmvkrpxl0 <[email protected]>
2022-08-20 11:38:24 +02:00
LexManos c0aa8dd808 Add ability to Auto register capabilities via annotation (#8972) 2022-08-17 15:06:35 -07:00
LexManos 4f2f5c02a0 1.19.2 RB 2022-08-17 14:58:14 -07:00
agnor99 7a6843fa4a Added ItemDecorator API (#8794) 2022-08-17 23:39:56 +02:00
한현빈 f0ecf16c6b [1.19.x] Custom usage animations for items (#8932) 2022-08-17 23:08:13 +02:00
Take Weiland 8998fa71b2 Allow registering custom ColorResolvers (#8880) 2022-08-17 22:51:05 +02:00
Dennis C 4e677a7558 [1.19] Allow custom outline rendering on EntityRenderers and BlockEntityRenderers (#8938) 2022-08-17 00:01:33 +02:00
sciwhiz12 07254b36d7 Redirect checks for entity selector use to a permission (#8947)
This allows greater flexibility for configuring servers with
operator-like permissions to user groups through the permissions API and
their permissions handler of choice without needing to grant the
vanilla operator permission to any player.

The new permission is "forge:use_entity_selectors", which is granted by
default to players with permission level 2 (GAMEMASTERS) and above.

The hook falls back to checking the permission level if the source of
the command is not a ServerPlayer, such as for command blocks and
functions.
2022-08-16 14:11:54 -07:00
Brennan Ward a51b8d256d Allow FakePlayer to report its position (#8963) 2022-08-16 14:09:09 -07:00
Sara Freimer 75fcd9448e Add alternate version of renderEntityInInventory to allow for directly specifying the angles (#8961) 2022-08-16 13:57:30 -07:00
eerussianguy 6b45999702 Add cancellable ToastAddEvent (#8952) 2022-08-16 13:48:55 -07:00
eerussianguy a4f52f4057 Modify ScreenEvent.RenderInventoryMobEffects to allow moving the effect stack left or right (#8951) 2022-08-16 13:47:57 -07:00
Brennan Ward b8b3201372 Fix Enchantment#doPostHurt and Enchantment#doPostAttack being called twice for players. Fixes MC-248272 (#8948) 2022-08-16 13:40:10 -07:00
LexManos 41deadc1e2 Remove reflective implementation of ICustomPacket. (#8973)
Make vanilla custom packets able to be sent multiple times. Closes #8969
2022-08-16 13:17:25 -07:00
LexManos 82e8d06e54 Filter name spaces to directories only. Closes #8413 2022-08-14 12:22:38 -07:00
Marc Hermans 51db5f2ff7 Fix a corner case where the UMLB can not extract a version from a library. (#8967) 2022-08-14 12:18:22 -07:00
Alex O'Neill 2ec9e515ff Fix worlds with removed dimension types unable to load. (#8959) Closes #8800 2022-08-14 12:17:22 -07:00
Alex O'Neill 0579572148 Fix issue where unknown chunk generators would cause DFU to fail. (#8957) 2022-08-10 23:13:44 -07:00
TheMuso 83f4e6353c Fix comments and documentation that were missed during the review of #8712 (#8945) 2022-08-09 12:49:00 -07:00
Luis 130953e519 Make AnvilUpdateEvent fire even if the second input is empty, which means it fires even if only changing the item name. (#8905) 2022-08-09 12:48:46 -07:00
Sirttas 7981093591 Fix LivingEntity#isBlocking to use ToolActions#SHIELD_BLOCK instead of UseAnim#BLOCK (#8933) 2022-08-09 12:27:26 -07:00
Joseph B 79112d6208 Add Custom HolderSet Types allowing for logical combining of sets. (#8928) 2022-08-09 12:25:12 -07:00
Sara Freimer a3eb6263f4 Add values to VersionSupportMatrix to support loading mods that restrict versions to 1.19.1 on 1.19.2 (#8946) 2022-08-09 12:15:58 -07:00
Redstone_Dubstep d91bc9c73d Fix certain particles not updating their bounding box when their position changes (#8925) 2022-08-08 15:09:13 +02:00
LexManos c807665ab9 Update EventBus to address concurrency issue in ModLauncher Factory. Closes #8924 2022-08-07 17:35:15 -07:00
LexManos 7b97c181b9 1.19.2 2022-08-05 16:35:02 -07:00
Brennan Ward c42b0847d2 Remove calls to getStepHeight in Player#maybeBackOffFromEdge (#8927) 2022-08-05 11:35:47 -07:00
Sirttas f182cb00c1 Add forge tags for tools and armors, these DO NOT replace ToolActions, and are designed just for Recipes. (#8914) 2022-08-05 11:32:53 -07:00
J.T. McQuigg 3181b5da23 Add Biomes.BEACH to Tags (#8892) 2022-08-05 11:31:32 -07:00
Mysticdrew 983c119477 Let NetworkInstance.isRemotePresent check minecraft:register for channel IDs. (#8921) 2022-08-05 11:31:11 -07:00
Sara Freimer 874ff1bba2 Add an event for when the chunk ticket level is updated (#8909) 2022-08-05 11:21:14 -07:00
TheMuso 06d9d89f98 Re-add PotentialSpawns event (#8712) 2022-08-05 09:29:02 -07:00
Dennis C c713bad8c3 Fix misplaced patch in ItemEntityRenderer breaking ItemEntityRenderer#shouldBob() (#8919) 2022-08-04 22:14:28 -07:00
Marc Hermans fe19491822 [1.19] [HotFix] Fix the dedicated server not having access to the JiJ filesystems. (#8931) 2022-08-03 22:32:32 +01:00
SizableShrimp c80e8a1aca Match Mojang's action bar fix for MC-72687 (#8917) 2022-07-29 01:09:33 -07:00
SizableShrimp 6845c094ab Forge 1.19.1
Load natives from classpath
Make command argument types a forge registry
Add `EntityMobGriefingEvent` to `Allay#wantsToPickUp`
Overhaul `ServerChatEvent` to use `ChatDecorator` system
Remove `ClientChatEvent#setMessage` for now
Gradle 7.5

Signed-off-by: SizableShrimp <[email protected]>
2022-07-28 00:20:31 -05:00
LexManos 3c1126a7b4 Mark 1.19 RB 2022-07-26 18:49:28 -07:00
Néstor Amador 326e0329b9 Allow faces of an "elements" model to be made emissive (#8890) 2022-07-26 15:01:29 -07:00
coehlrich f9d0daa91c Fix invalid channel names sent from the server causing the network thread to error. (#8902) 2022-07-26 14:55:05 -07:00
LexManos 094200e687 Fix PlayerEvent.BreakSpeed using magic block position to signify invalid position. Closes #8906 2022-07-25 16:20:44 -07:00
Marc Hermans bb4818630c Fix cases where URIs would not work properly with JarInJar (#8900) 2022-07-23 00:46:45 -07:00
Jonathan Colmenares 7af3155b39 Add new hook to allow modification of lightmap via Dimension special effects (#8863) 2022-07-22 12:22:23 -07:00
Marc Hermans d988b4a811 Fix Forge's packet handling on play messages. (#8875) 2022-07-22 12:08:29 -07:00
zml 18f5bdb7ca Add API for tab list header/footer (#8803) 2022-07-21 21:01:21 -07:00
TelepathicGrunt f41d493552 Allow modded blocks overriding canStickTo prevent sticking to vanilla blocks/other modded blocks (#8837) 2022-07-21 13:06:02 -07:00
Néstor Amador 8cd2c9c63f Multiple tweaks and fixes to the recent changes in the client refactor PR: Part 3 (#8864)
Fix weighted baked models not respecting children render types
Allow fluid container model to use base texture as particle
Fix inverted behavior in composite model building. Fixes #8871
2022-07-20 15:25:07 -07:00
matyrobbrt 427b335cac Fix crossbows not firing ArrowLooseEvent (#8887) 2022-07-20 14:35:37 -07:00
Emma, Cypress, Rose ⚘ 2049096c6f Add User-Agent header to requests made by the update checker (#8881)
Format: Java-http-client/<Java version> MinecraftForge/<ForgeVer> <ModId>/<ModVersion>
2022-07-20 10:57:57 -07:00
Marc Hermans 98d4fa6dc2 Output the full path in a crash report so it is easier to find the outer mod when a crash in Jar-In-Jar occurs. (#8856) 2022-07-20 10:56:38 -07:00
Take Weiland 5306471aec Clean up the pick item ("middle mouse click") patches (#8870) 2022-07-20 10:56:16 -07:00
Kai aa8818b049 [1.19.x] Hotfix for test mods while the refactor is ongoing 2022-07-17 14:49:56 +01:00
Caltinor 2d86f17363 add event to SugarCaneBlock (#8877) 2022-07-16 02:21:27 +01:00
Brennan Ward 3a36a01913 Fix Global Loot Modifiers not using Dispatch Codec (#8859) 2022-07-12 11:19:35 -07:00
Nicholas Smit 47d70a7f31 Allow block render types to be set in datagen (#8852) 2022-07-12 11:19:03 -07:00
Sara Freimer d5b5e6f4db Fix renderBreakingTexture not using the target's model data (#8849) 2022-07-12 17:04:29 +02:00
Néstor Amador e76d814439 Multiple tweaks and fixes to the recent changes in the client refactor PR: Part 2 (#8854)
* Add getter for the component names in an unbaked geometry
* Fix render type hint not being copied in BlockGeometryBakingContext
* Ensure BlockRenderDispatches's renderSingleBlock uses the correct buffer
2022-07-12 17:04:03 +02:00
SizableShrimp b8b40e1f54 [1.19.x] Apply general renames, A SRG is provided for modders. (#8840)
See https://gist.github.com/SizableShrimp/882a671ff74256d150776da08c89ef72
2022-07-11 18:27:48 -07:00
LexManos 5e8c786406 Fix mob block breaking AI not working correctly when chunk 0,0 is unloaded. Closes #8853 2022-07-11 12:41:06 -07:00
Néstor Amador 5b8224f839 Fix crash when breaking blocks with multipart models and remove caching. Closes #8850 2022-07-11 11:24:21 -07:00
Silver_David cd621eb081 Fixed CompositeModel.Baked.Builder.build() passing arguments in the wrong order (#8846) 2022-07-10 11:11:07 -04:00
Néstor Amador b1113f0363 Make cutout mipmaps explicitly opt-in for item/entity rendering (#8845)
* Make cutout mipmaps explicitly opt-in for item/entity rendering

* Default render type domain to "minecraft" in model datagens
2022-07-10 11:10:57 -04:00
Silver_David daf5933b79 Fixed multipart block models not using the new model driven render type system. (#8844) 2022-07-10 11:10:43 -04:00
Marc Hermans 47fd2e8604 Update to the latest JarJar to fix a collision issue where multiple jars could provide an exact match. (#8847) 2022-07-10 11:07:55 -04:00
covers1624 05297fd72f Add FML config to disable DFU optimizations client-side. (#8842)
* Add client-side command line argument to disable DFU optimizations.

* Switch to using FMLConfig value instead.
2022-07-09 20:01:40 -04:00
CreativeMD f20cf19f94 [1.19] Fixed broken BufferBuilder.putBulkData(ByteBuffer) added by Forge (#8819)
* Fixes BufferBuilder.putBulkData(ByteBuffer)

* use nextElementByte

* Fixed merge conflict
2022-07-09 19:55:16 -04:00
Robert Kämpf 061cd3da53 [1.19.x] Fix shulker boxes allowing input of items, that return false for Item#canFitInsideContainerItems, through hoppers. (#8823)
* Make ShulkerBoxBlockEntity#canPlaceItemThroughFace delegate to Item#canFitInsideContainerItems.

* Switch to using Or and add comment.

* Switch Or to And.
2022-07-09 12:44:33 -04:00
Silver_David 03202a8ab4 [1.19.x] Added RenderLevelStageEvent to replace RenderLevelLastEvent (#8820)
* Ported RenderLevelStageEvent from 1.18.2

* Updated to fix merge conflicts
2022-07-09 12:36:47 -04:00
Nicholas Smit 8a67be4afd [1.19.x] Fix door datagenerator (#8821)
* Fix door datagenerator

Fix datagenerator for door blocks. Successor to #8687, addresses comments made there about statement complexity.

* Fix extra space around parameter

Fix extra space before comma around a parameter.
2022-07-09 12:21:06 -04:00
Drullkus 4c1ab67f33 Create PieceBeardifierModifier to re-enable piecewise beardifier definitions (#8798) 2022-07-09 12:12:55 -04:00
Dennis C 7e06a2105f Allow blocks to provide a dynamic MaterialColor for display on maps (#8812) 2022-07-09 12:10:02 -04:00
Flemmli97andFlemmli97 4a0c8be742 [1.19.x] BiomeTags Fixes/Improvements (#8711)
* dimension specific tag fix

* remove forge:is_beach cause vanilla has it already

* remove forge tags for new 1.19 vanilla tags (savanna, beach, overworld, end)

Co-authored-by: Flemmli97 <[email protected]>
2022-07-09 11:59:27 -04:00
Cyborgmas 0c030ee5e1 1.19 - Remove GlobalLootModifierSerializer and move to Codecs (#8721)
* convert GLM serializer class to codec

* cleanup

* GLM list needs to be sorted

* datagen

* simplify serialization

* fix test mods (oops)

* properly use suppliers for codec as they are registry obj
2022-07-09 11:51:41 -04:00
Random832 1134fac54c Implement item hooks for potions and enchantments (#8718)
* Implement item hooks for potions and enchantments

* code style fixes
2022-07-09 11:48:51 -04:00
desht ac44d72530 Re-apply missing patch to ServerLevel.EntityCallbacks#onTrackingEnd() (#8828) 2022-07-09 11:27:33 -04:00
agnor99 12468bd8d2 Double Bar Rendering fixed (#8806) (#8807)
* Double Bar Rendering fixed (#8806)

* Added requested changes by sciwhiz12
2022-07-09 11:22:15 -04:00
Néstor Amador a98eeb9d08 Multiple tweaks and fixes to the recent changes in the client refactor PR (#8836)
* Add an easy way to get the NamedGuiOverlay from a vanilla overlay

* Fix static member ordering crash in UnitTextureAtlasSprite

* Allow boss bar rendering to be cancelled

* Make fluid container datagen use the new name
2022-07-09 11:20:55 -04:00
Jonathan Colmenares b558b132cd Add FogMode to ViewportEvent.RenderFog (#8825) 2022-07-09 11:20:40 -04:00
Sara Freimer c102633566 Provide additional context to the getFieldOfView event (#8830) 2022-07-09 11:20:29 -04:00
bdew cce002ad29 Pass renderType to IForgeBakedModel.useAmbientOcclusion (#8834) 2022-07-09 11:20:18 -04:00
noeppi_noeppi b1ed321438 Load custom ITransformationServices from the classpath in dev (#8818)
* Add a classpath transformer discoverer to load custom transformation services from the classpath

* Update ClasspathTransformerDiscoverer to 1.18

* Update license year

* Update license header

* Fix the other license headers

* Update ClasspathTransformerDiscoverer to 1.19
2022-07-09 11:05:47 -04:00
Jonathan Coates c7c2478cb0 Handle modded packets on the network thread (#8703)
* Handle modded packets on the network thread

 - On the server we simply need to remove the call to
   ensureRunningOnSameThread.

 - On the client side, we now handle the packet at the very start of the
   call. We make sure we're running from a network thread to prevent
   calling the handling code twice.

   While this does mean we no longer call .release(), in practice this
   doesn't cause any leaks as ClientboundCustomPayloadPacket releases
   for us.

* Clarify behaviour a little in the documentation

* Javadoc formatting

* Add a helper method for handling packets on the main thread

Also rename the network thread one. Should make it clearer the expected
behaviour of the two, and make it clearer there's a potentially breaking
change.

* Add back consumer() methods

Also document EventNetworkChannel, to clarify the thread behaviour
there.

* Add since = "1.19" to deprecated annotations
2022-07-09 11:02:44 -04:00
Marc Hermans b70d99489b Cache resource listing calls in resource packs (#8829)
* Make the resource lookups cached.

* Include configurability and handle patch cleanup.

* Document and comment the cache manager.

* Make thread selection configurable.

* Implement a configurable loading mechanic that falls back to default behaviour when the config is not bound yet.

* Use boolean supplier and fix wildcard import.

* Clean up the VPR since this is more elegant.

* Clean up the VPR since this is more elegant.

* Address review comments.

* Address more review comments.

* Fix formatting on `getSource`

* Address comments by ichtt

* Adapt to pups requests.

* Stupid idea.

* Attempt this again with a copy on write list.

* Fix a concurrency and loading issue.

* Fix #8813
Checks if the paths are valid resource paths.

* Move the new methods on vanilla Patch.
2022-07-09 10:58:11 -04:00
cpw 57f45eaca0 Update SJH and JIJ 2022-07-09 10:42:30 -04:00
Marc Hermans 52fcc4d649 Fix #8833 (#8835) 2022-07-09 10:09:27 -04:00
Néstor Amador 959ab9abc9 Fix backwards fabulous check in SimpleBakedModel (#8832)
Yet another blunder we missed during the review of #8786.
2022-07-09 10:09:16 -04:00
Néstor Amador c3a91872fe Make texture atlas in StandaloneGeometryBakingContext configurable (#8831) 2022-07-09 10:08:43 -04:00
Néstor AmadorandDennis C 4d074f0a1e [1.19.X] Client code cleanup, updates, and other refactors (#8786)
* Revert "Allow safely registering RenderType predicates at any time (#8685)"

This reverts commit be7275443f.

* Renderable API refactors
- Rename "render values" to "context"
- Rename SimpleRenderable to CompositeRenderable to better reflect its use
- Remove IMultipartRenderValues since it doesn't have any real use
- Add extensive customization options to BakedModelRenderable

* ClientRegistry and MinecraftForgeClient refactors
- Add sprite loader manager and registration event
- Add spectator shader manager and registration event
- Add client tooltip factory manager and registration event
- Add recipe book manager and registration event
- Add key mapping registration event
- Remove ClientRegistry, as everything has been moved out of it
- Remove registration methods from MinecraftForgeClient, as they have dedicated events now

* Dimension special effects refactors
- Fold handlers into an extension class and remove public mutable fields
- Add dimension special effects manager and registration event

* HUD overlay refactors
- Rename to IGuiOverlay match vanilla (instead of Ingame)
- Add overlay manager and registration event
- Move vanilla overlays to a standalone enum

* Model loader refactors
- Rename IModelLoader to IGeometryLoader
- Add loader manager and registration event
- Fold all model events into one
- Move registration of additionally loaded models to an event
- Remove ForgeModelBakery and related classes as they served no purpose anymore

* Render properties refactors
- Rename all render properties to client extensions and relocate accordingly
- Move lookups to the respective interfaces

* Model data refactors
- Convert model data to a final class backed by an immutable map and document mutability requirements. This addresses several thread-safety issues in the current implementation which could result in race conditions
- Transfer ownership of the data manager to the client level. This addresses several issues that arise when multiple levels are used at once

* GUI and widget refactors
- Move all widgets to the correct package
- Rename GuiUtils and children to match vanilla naming

* New vertex pipeline API
- Move to vanilla's VertexConsumer
- Roll back recent PR making VertexConsumer format-aware. This is the opposite of what vanilla does, and should not be relevant with the updated lighting pipeline

* Lighting pipeline refactors
- Move to dedicated lighting package
- Separate flat and smooth lighters
- Convert from a vertex pipeline transformer to a pure vertex source (input is baked quads)

* Model geometry API refactors
- Rename IModelGeometry to IUnbakedGeometry
- Rename IModelConfiguration to IGeometryBakingContext
- Rename other elements to match vanilla naming
- Remove current changes to ModelState, as they do not belong there. Transforms should be specified through vanilla's system. ModelState is intended to transfer state from the blockstate JSON
- Remove multipart geometries and geometry parts. After some discussion, these should not be exposed. Instead, geometries should be baked with only the necessary parts enabled

* Make render types a first-class citizen in baked models
- Add named render types (block + entity + fabulous entity)
- Add named render type manager + registration event
- Make BakedModel aware of render types and transfer control over which ones are used to it instead of ItemBlockRenderTypes (fallback)
- (additional) Add concatenated list view. A wrapper for multiple lists that iterates through them in order without the cost of merging them. Useful for merging lists of baked quads

* General event refactors
- Several renames to either match vanilla or improve clarity
- Relocate client chat event dispatching out of common code

* Forge model type refactors
- Rename SeparatePerspectiveModel to SeparateTransformsModel
- Rename ItemModelMesherForge to ForgeItemModelShaper
- Rename DynamicBucketModel to DynamicFluidContainerModel
- Prefix all OBJ-related classes with "Obj" and decouple parsing from construction
- Extract ElementsModel from model loader registry
- Add EmptyModel (baked, unbaked and loader)
- Refactor CompositeModel to take over ItemMultiLayerBakedModel
- Remove FluidModel as it's not used and isn't compatible with the new fluid rendering in modern versions
- Move model loader registration to a proper event handler
- Update names of several JSON fields (backwards-compatible)
- Update datagens to match

* Miscellaneous changes and overlapping patches
- Dispatch all new registration events
- Convert ExtendedServerListData to a record
- Add/remove hooks from ForgeHooksClient as necessary

* Update test mods

* Fix VertexConsumerWrapper returning parent instead of itself

* Additional event cleanup pass

As discussed on Discord:
- Remove "@hidden" and "@see <callsite>" javadoc annotations from all client events and replace them with @ApiStatus.Internal annotation
- Make all events that shouldn't be fired directly into abstract classes with protected constructors
- Another styling pass, just in case (caught some missed classes)

* Add proper deprecation javadocs and de-dupe some vertex consumer code

* Replace sets of chunk render types with a faster BitSet-backed collection

This largely addresses potential performance concerns that using a plain HashSet might involve by making lookups and iteration as linear as they can likely be (aside from using a plain byte/int/long for bit storage). Further performance concerns related to the implementation may be addressed separately, as all the implementation details are hidden from the end user

* Requested changes

- Remove MinecraftForgeClient and move members to Minecraft, IForgeMinecraft and StencilManager
- Allow non-default elements to be passed into VertexConsumer and add support to derived classes
- Move array instantiation out of quad processing in lighting pipeline
- Fix flipped fluid container model
- Set default UV1 to the correct values in the remapping pipeline
- Minor documentation changes

* Add/update EXC entries and fix AT comment

* Add test mod as per Orion's request

* Additional requested changes

* Allow custom model types to request the particle texture to be loaded

* Even more requested changes

* Improve generics in ConcatenatedListView and add missing fallbacks

* Fix fluid render types being bound to the fluid and not its holder

* Remove non-contractual nullability in ChunkRenderTypeSet and add isEmpty

Additionally, introduce chunk render type checks in ItemBlockRenderTypes

Co-authored-by: Dennis C <[email protected]>
2022-07-07 18:34:51 -04:00
Paint_Ninja c2225ab1da Implement full support for IPv6 (#8742) 2022-07-01 15:38:53 -07:00
Dennis C bb03b1a7d1 Fix certain user-configured options being overwritten incorrectly due to validators. (#8780) 2022-06-28 13:23:35 -07:00
Dennis C be7275443f Allow safely registering RenderType predicates at any time (#8685) 2022-06-27 21:18:53 -07:00
Dennis C dc1c973684 Fix crash after loading error due to fluid texture gathering and config lookup (#8802) 2022-06-27 21:16:54 -07:00
Marc Hermans 06d568408c Remove the configuration option for handling empty tags in ingredients. (#8799)
Now empty tags are considered broken in all states.
2022-06-27 21:16:20 -07:00
TelepathicGrunt 2256e88fb4 Fix MC-105317 Structure blocks do not rotate entities correctly when loading (#8792) 2022-06-27 21:15:22 -07:00
Jonathan Coates 9b962c8ff0 Fire ChunkWatchEvents after sending packets (#8747) 2022-06-27 21:09:23 -07:00
cech12 7656b75992 Add item handler capability to chest boats (#8787) 2022-06-27 21:04:42 -07:00
Joseph B 58a5449c2f Add getter for correct BiomeSpecialEffectsBuilder to BiomeInfo$Builder (#8781) 2022-06-27 21:01:01 -07:00
Michael Bunting 702abe0665 Fix BlockToolModificationEvent missing cancelable annotation (#8778) 2022-06-27 20:59:47 -07:00
Sara Freimer 2070b77fd4 Fix ticking chunk tickets from forge's chunk manager not causing chunks to fully tick (#8775) 2022-06-27 20:58:57 -07:00
Dennis C fd00822015 Fix default audio device config loading string comparison issue (#8767) 2022-06-27 20:54:54 -07:00
dhyces 17565c0ea2 Fix missed vanilla method overrides in ForgeRegistry (#8766) 2022-06-27 20:48:11 -07:00
Budschie 22668ad2c2 Add MinecraftServer reference to ServerTickEvent (#8765) 2022-06-27 20:45:35 -07:00
Joseph B 0dcc3d700d Fix TagsProviders for datapack registries not recognizing existing files (#8761) 2022-06-27 20:43:50 -07:00
Dennis C 2ab3d9b8a3 Add callback after a BlockState was changed and the neighbors were updated (#8686) 2022-06-27 20:21:34 -07:00
Ketok d578d8f6df Add biome tag entries for 1.19 biomes (#8684) 2022-06-27 20:08:21 -07:00
eerussianguy fd61016a7e Make fishing rods use tool actions for relevant logic (#8681) 2022-06-27 20:07:51 -07:00
cpw 7f77d24f45 Update BootstrapLauncher to 1.1.1 and remove the forced
merge of text2speech since new BSL does it.

Signed-off-by: cpw <[email protected]>
2022-06-20 22:27:08 -04:00
cpw 3ebdcb34d1 Merge text2speech libs together so the natives are part of the jar
Signed-off-by: cpw <[email protected]>
2022-06-20 18:19:45 -04:00
King Lemming 05240fecf7 Make Forge ConfigValues implement Supplier. (#8776) 2022-06-20 23:07:48 +01:00
cpw d5860d7cfa Fix merge derp in AbstractModProvider and logic derp in ModDiscoverer
Signed-off-by: cpw <[email protected]>
2022-06-20 16:32:46 -04:00
cpw 7bb97dafda Add "send to mods in order" method to ModList and use it (#8759)
* Add "send to mods in order" method to ModList and use it in RegistryEvents and DataGen..
* Also preserve order in runAll
* Do better comparator thanks @pupnewfster
* postEvent as well.
2022-06-20 16:11:33 -04:00
cpw ca90babc4a Update SJH to 2.0.2.. (#8774)
* Update SJH to 2.0.3..
2022-06-20 16:11:13 -04:00
cpw 4b813e4319 Sanity check the version specified in the mod file (#8749)
* Sanity check the version specified in the mod file to
make sure it's compatible with JPMS standards for
version strings.

Closes #8748

Requires SPI 6
2022-06-20 16:09:19 -04:00
Marc Hermans fa84c22662 Fix SP-Devtime world loading crash due to missing server configs (#8757) 2022-06-17 11:38:41 -07:00
sciwhiz12 2ae640b530 Remove ForgeWorldPreset and related code (#8756)
Vanilla has a working replacement.
2022-06-17 08:56:12 +01:00
sciwhiz12 4f95a20c5e Change ConfigValue#get() to throw if called before config loaded (#8236)
This prevents silent issues where a mod gets the value of the setting
before configs are loaded, which means the default value is always
returned.

As there may be situations where the getting the config setting before
configs are loaded is needed, and it is not preferable to hardcode the
default value, the original behavior is made available through #getRaw.

Implements and closes #7716

* Remove getRaw() method

This is effectively replaced with the expression `spec.isLoaded() ?
configValue.get() : configValue.getDefault()`.

* Remove forceSystemNanoTime config setting

As implemented, it never had any effect as any place where the config
value would be queried happens before the configs are loaded.
2022-06-17 08:31:45 +01:00
sciwhiz12 915d570b17 Fix EnumArgument to use enum names for suggestions (#8728)
Previously, the suggestions used the string representation of the enum
through Enum#toString, which can differ from the name of the enum as
required by Enum#valueOf, causing invalid suggestions (both in gui and
through the error message).
2022-06-15 14:15:32 -07:00
Marc Hermans 5979636ee5 Jar-In-Jar (#8715) 2022-06-15 12:27:47 -07:00
Silverminer007 5acfc6ae3c [1.19] Fix data-gen output path of custom data-pack registries (#8724) 2022-06-15 16:22:00 +01:00
ChampionAsh5357 48341120fb Fix player dive and surface animations in custom fluids (#8738) 2022-06-15 14:02:16 +01:00
ChampionAsh5357 ff45bb4c27 [1.19.x] Affect ItemEntity Motion in Custom Fluids (#8737) 2022-06-15 14:01:57 +01:00
KnightMiner eabba46595 [1.19] Add support for items to add enchantments without setting them in NBT (#8719) 2022-06-15 00:52:58 +01:00
Joseph B 683bf99fe4 [1.19.x] Add stock biome modifier types for adding features and spawns (#8697) 2022-06-15 00:39:03 +01:00
ChampionAsh5357 2a05c7c01d [1.19.x] Fluid API Overhaul (#8695) 2022-06-14 23:07:51 +01:00
Sara Freimer e386229b66 Replace StructureSpawnListGatherEvent with StructureModifiers (#8717) 2022-06-14 12:05:07 -07:00
Magnus Jensen 59b8ec5625 Use stack sensitive translation key by default for FluidAttributes. (#8707) 2022-06-14 10:18:27 -07:00
ferriarnus 50ae5db357 Delete LootItemRandomChanceCondition which added looting bonus enchantment incorrectly. (#8733) 2022-06-14 10:17:38 -07:00
cpw 3aa99a1250 Update EventBus to 6.0, ModLauncher to 10.0.1 and BootstrapLauncher to 1.1 (#8725) 2022-06-14 10:15:46 -07:00
coehlrich 00c8513129 Replace support bot with support action (#8700) 2022-06-13 17:36:16 -07:00
Brennan Ward 9c1404fecc Fix Reach Distance / Attack Range being clamped at 6.0 (#8699) 2022-06-13 17:33:02 +01:00
Joseph B c11686ab6c [1.19.x] Fix mods' worldgen data not being loaded when creating new singleplayer worlds (#8693) 2022-06-13 17:21:21 +01:00
SizableShrimp 2818984f6c [1.19.x] Fix experimental confirmation screen (#8727)
Signed-off-by: SizableShrimp <[email protected]>
2022-06-13 17:20:50 +01:00
TelepathicGrunt db225ed04a Move is_mountain to forge's tag instead of vanilla's (#8726) 2022-06-13 02:39:04 +01:00
ChampionAsh5357 0ec4c34a34 [1.19.x] Add CommandBuildContext to Register Command Events (#8716) 2022-06-12 23:36:25 +01:00
Sara Freimer 597ebc3267 Only rewrite datagen cache when needed (#8709) 2022-06-10 20:35:40 -07:00
cpw e16f54eebe Implement a simple feature system for Forge (#8670)
* Implement a simple feature system for Forge. Allows mods to demand certain features are available in the loading system. An example for java_version is provided, but not expected to be used widely. This is more targeted to properties of the display, such as GL version and glsl profile.

Requires https://github.com/MinecraftForge/ForgeSPI/pull/13 to be merged first in ForgeSPI, and the SPI to be updated appropriately in build.gradle files.

* rebase onto 1.19 and add in SPI update
2022-06-09 17:00:39 -04:00
cpw 27480c15fa displayTest option in mods.toml (#8656)
* displayTest option in mods.toml
* "MATCH_VERSION" (or none) is existing match version string behaviour
* "IGNORE_SERVER_VERSION" accepts anything and sends special SERVERONLY string
* "IGNORE_ALL_VERSION" accepts anything and sends an empty string
* "NONE" allows the mod to supply their own displaytest using the IExtensionPoint mechanism.


* Update display test with feedback and added the mods.toml discussion in mdk.
2022-06-09 16:39:00 -04:00
cpw df0dea59f3 Update forgeSPI to v5 (#8696) 2022-06-09 15:59:48 -04:00
gigaherz d7bc564512 Make IVertexConsumers such as the lighting pipeline, be aware of which format they are dealing with. (#8692)
Also fix Lighting pipeline ignoring the overlay coords from the block renderer.
2022-06-09 21:39:16 +02:00
ferriarnus e6a73ef36c Fixed misaligned patch to invalidateCaps in Entity (#8705) 2022-06-09 11:41:51 -07:00
SizableShrimp 1ba7c6aecd Fix readAdditionalLevelSaveData (#8704) 2022-06-09 11:40:42 -07:00
AzureDoom 7c5e95222e Fixes setPos to syncPacketPositionCodec (#8702) 2022-06-09 11:40:21 -07:00
SizableShrimp 38df3d2a15 Fix wrong param passed to PlayLevelSoundEvent.AtEntity (#8688) 2022-06-09 11:39:35 -07:00
ferriarnus 3beaa817f3 Override initialize in SlotItemHandler, so it uses the itemhandler instead of container (#8679) 2022-06-09 11:39:04 -07:00
SizableShrimp 3ebd7ba0ef Update MDK for 1.19 changes (#8675) 2022-06-09 11:37:52 -07:00
LexManos 1ba9c34650 Add helper to RecipeType, and fix eclipse compiler error in test class. 2022-06-09 11:36:52 -07:00
cpw 2f9e726aea Update modlauncher to latest (#8691) 2022-06-08 16:02:50 -07:00
Sara Freimer ade2b8cc80 Fix getting entity data serializer id crashing due to improper port to new registry system (#8678) 2022-06-08 09:14:41 -07:00
Sara Freimer 9b144f4541 Fire registry events in the order vanilla registers to registries (#8677)
Custom registries are still fired in alphabetical order, after all vanilla registries.
Move forge's data_serializers registry to forge namespace.
2022-06-08 09:08:55 -07:00
cpw 0c35436217 Add method with pre/post wrap to allow setting/clearing mod context. (#8682)
Fixes ActiveContainer in ModContext not being present in registry events. Closes #8680
2022-06-08 08:38:53 -07:00
SizableShrimp 0e66b7b217 Fix the Curlie oopsie
Signed-off-by: SizableShrimp <[email protected]>
2022-06-07 17:08:56 -04:00
4ed9117799 Forge 1.19
* Bump pack.mcmeta formats
* 1.19 biome modifiers
* Mark ClientPlayerNetworkEvent.LoggedOutEvent's getters as nullable
* Add docs and package-info to client extension interfaces package
* Move RenderBlockOverlayEvent hooks to ForgeHooksClient
* Add package-infos to client events package
* Rename SoundLoadEvent to SoundEngineLoadEvent

This reduces confusion from consumers which may think the
name SoundLoadEvent refers to an individual sound being loaded rather
than the sound engine.
* Document and change SoundLoadEvent to fire on mod bus

Previously, it fired on both the mod bus and the Forge bus, which is
confusing for consumers.
* Delete SoundSetupEvent

Looking at its original implementation shows that there isn't an
appropriate place in the new sound code to reinsert the event, and the
place of 'sound engine/manager initialization event' is taken already by SoundLoadEvent.
* Perform some cleanup on client events

 - Removed nullable annotations from ClientPlayerNetworkEvent
 - Renamed #getPartialTicks methods to #getPartialTick, to be consistent
  with vanilla's naming of the partial tick
 - Cleanup documentation to remove line breaks, use the
  spelling 'cancelled' over
  'canceled', and improve docs on existing and
   new methods.
* Remove EntityEvent.CanUpdate

Closes MinecraftForge/MinecraftForge#6394
* Switch to Jetbrains nullability annotations
* New PlayLevelSoundEvent; replaces old PlaySoundAtEntityEvent
* Remove ForgeWorldPresetScreens
* Remove IForgeRegistryEntry
* Remove use of List<Throwable> in FML's CompletableFutures
* Add docs to mod loading stages, stages, and phases
* Gradle 7.4.2
* Use SLF4J in FMLLoader and other subprojects
* Switch dynamic versions in subprojects to pinned ones
* Switch ForgeRoot and MDK to FG plugin markers
* Configure Forge javadoc task

The task now uses a custom stylesheet with MCForge elements, and
configured to combine the generation from the four FML subprojects
(fmlloader, fmlcore, javafmllanguage, mclanguage) and the Forge project
into the javadoc output.
* Update docs/md files, for 1.19 update and the move away from IRC to Discord.
* Make "Potentially dangerous alternative prefix" a debug warning, not info.

Co-authored-by: Curle <[email protected]>
Co-authored-by: sciwhiz12 <[email protected]>
Signed-off-by: SizableShrimp <[email protected]>
2022-06-07 15:31:26 -05:00
2211 changed files with 55510 additions and 37297 deletions

No files matched your search

-16
View File
@@ -1,16 +0,0 @@
# Configuration for support-requests - https://github.com/dessant/support-requests
# Label used to mark issues as support requests
supportLabel: Forum
# Comment to post on issues marked as support requests. Add a link
# to a support page, or set to `false` to disable
supportComment: >
👋 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](http://www.minecraftforge.net/forum/forum/18-support-bug-reports/).
Please create a new topic on the support forum with this issue, and the conversation can
continue there.
# Whether to close issues marked as support requests
close: true
# Whether to lock issues marked as support requests
lock: true
+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 }}
+2
View File
@@ -33,6 +33,8 @@
/projects/**/*.launch
/repo/
src/generated/resources/.cache/
src/generated_test/resources/.cache/
src/generated_mdk/resources/.cache/
#Patch rejects
/patches-/
+4 -4
View File
@@ -37,8 +37,8 @@ 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("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("docker_gradle_version", "7.5.1", label = "Gradle version", description = "The version of Gradle to use during execution of Gradle tasks.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("git_main_branch", "1.20.x", label = "Git Main Branch", description = "The git main or default branch to use in VCS operations.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("git_branch_spec", """
+:refs/heads/(main*)
+:refs/heads/(master*)
@@ -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
+192 -41
View File
@@ -1,14 +1,3 @@
buildscript {
repositories {
mavenLocal()
maven { url = 'https://maven.minecraftforge.net/' }
mavenCentral()
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:[5.1.39,)'
}
}
import groovy.json.JsonBuilder
import net.minecraftforge.forge.tasks.checks.CheckATs
import net.minecraftforge.forge.tasks.checks.CheckExcs
@@ -30,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'
@@ -37,6 +27,8 @@ plugins {
id 'net.minecraftforge.gradleutils' version '2.+'
id 'eclipse'
id 'de.undercouch.download' version '5.1.3'
id 'net.minecraftforge.gradle.patcher' version '[5.1.77,5.2.0)' apply false
id 'net.minecraftforge.gradle.mcp' version '[5.1.77,5.2.0)' apply false
}
Util.init() //Init all our extension methods!
@@ -51,33 +43,48 @@ ext {
]
}
MAPPING_CHANNEL = 'official'
MAPPING_VERSION = '1.18.2'
MC_VERSION = '1.18.2'
MCP_VERSION = '20220404.173914'
SPI_VERSION = '4.0.15-4.x'
MAPPING_VERSION = '1.19.4'
MC_VERSION = '1.19.4'
MC_NEXT_VERSION = '1.20'
MCP_VERSION = '20230314.122934'
SPI_VERSION = '6.0.0'
ACCESSTRANSFORMERS_VERSION = '8.0.4'
COREMODS_VERSION = '5.0.1'
EVENTBUS_VERSION = '5.0.3'
MODLAUNCHER_VERSION = '9.1.3'
SECUREJARHANDLER_VERSION = '1.0.3'
BOOTSTRAPLAUNCHER_VERSION = '1.0.0'
ASM_VERSION = '9.2'
EVENTBUS_VERSION = '6.0.3'
MODLAUNCHER_VERSION = '10.0.8'
SECUREJARHANDLER_VERSION = '2.1.6'
BOOTSTRAPLAUNCHER_VERSION = '1.1.2'
ASM_VERSION = '9.5'
INSTALLER_VERSION = '2.1.+'
MIXIN_VERSION = '0.8.5'
JARJAR_VERSION = '0.3.0'
TERMINALCONSOLEAPPENDER_VERSION = '1.2.0'
JLINE_VERSION = '3.12.+'
NIGHTCONFIG_VERSION = '3.6.4'
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.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'
GIT_INFO = gradleutils.gitInfo
VERSION = gradleutils.getFilteredMCTagOffsetBranchVersion(true, '[0-9]', MC_VERSION)
// FML_VERSION = gradleutils.getFilteredMCTagOffsetBranchVersion(true, 'FML', MC_VERSION)
FORGE_VERSION = VERSION.substring(MC_VERSION.length() + 1)
SPECIAL_SOURCE = 'net.md-5:SpecialSource:1.10.0'
BINPATCH_TOOL = 'net.minecraftforge:binarypatcher:1.0.12:fatjar'
BINPATCH_TOOL = 'net.minecraftforge:binarypatcher:1.1.1:fatjar'
INSTALLER_TOOLS = 'net.minecraftforge:installertools:1.3.0'
JAR_SPLITTER = 'net.minecraftforge:jarsplitter:1.1.4'
FART = 'net.minecraftforge:ForgeAutoRenamingTool:0.1.22:all'
MIN_TAG_FOR_CHANGELOG = "39.0"
LAST_RB = GIT_INFO.tag.rsplit('.', 1)[1] as int >= 1 ? GIT_INFO.tag + '.0' : null
MIN_TAG_FOR_CHANGELOG = '41.0'
}
println('Version: ' + VERSION +
@@ -96,7 +103,7 @@ def extraTxts = [
]
task downloadVersionManifest(type: Download) {
src 'https://launchermeta.mojang.com/mc/game/version_manifest_v2.json'
src 'https://piston-meta.mojang.com/mc/game/version_manifest_v2.json'
dest file('build/versions/version_manifest.json')
useETag 'all'
onlyIfModified true
@@ -145,6 +152,10 @@ subprojects {
name = 'forge'
url = 'https://maven.minecraftforge.net/'
}
maven {
name = 'minecraft'
url = 'https://libraries.minecraft.net/'
}
}
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
@@ -258,6 +269,7 @@ def sharedDeps = {
moduleonly "org.ow2.asm:asm-util:${ASM_VERSION}"
moduleonly "org.ow2.asm:asm-analysis:${ASM_VERSION}"
moduleonly "cpw.mods:bootstraplauncher:${BOOTSTRAPLAUNCHER_VERSION}"
moduleonly "net.minecraftforge:JarJarFileSystems:${JARJAR_VERSION}"
installer "cpw.mods:securejarhandler:${SECUREJARHANDLER_VERSION}"
installer "org.ow2.asm:asm:${ASM_VERSION}"
@@ -266,22 +278,23 @@ def sharedDeps = {
installer "org.ow2.asm:asm-util:${ASM_VERSION}"
installer "org.ow2.asm:asm-analysis:${ASM_VERSION}"
installer "net.minecraftforge:accesstransformers:${ACCESSTRANSFORMERS_VERSION}"
installer 'org.antlr:antlr4-runtime:4.9.1' //Dep of AccessTransformer
installer 'org.antlr:antlr4-runtime:4.9.1' // Dep of AccessTransformer
installer "net.minecraftforge:eventbus:${EVENTBUS_VERSION}"
installer "net.minecraftforge:forgespi:${SPI_VERSION}"
installer "net.minecraftforge:coremods:${COREMODS_VERSION}"
installer "cpw.mods:modlauncher:${MODLAUNCHER_VERSION}"
installer 'net.minecraftforge:unsafe:0.2.+'
installer 'com.electronwill.night-config:core:3.6.4'
installer 'com.electronwill.night-config:toml:3.6.4'
installer 'org.apache.maven:maven-artifact:3.6.3'
installer "com.electronwill.night-config:core:${NIGHTCONFIG_VERSION}"
installer "com.electronwill.night-config:toml:${NIGHTCONFIG_VERSION}"
installer "org.apache.maven:maven-artifact:${APACHE_MAVEN_ARTIFACT_VERSION}"
installer 'net.jodah:typetools:0.8.3'
installer 'net.minecrell:terminalconsoleappender:1.2.0'
installer 'org.jline:jline-reader:3.12.+' //Dep of TerminaalConsoleeAppender
installer 'org.jline:jline-terminal:3.12.+' //Dep of TerminaalConsoleeAppender
installer "net.minecrell:terminalconsoleappender:${TERMINALCONSOLEAPPENDER_VERSION}"
installer "org.jline:jline-reader:${JLINE_VERSION}" // Dep of TerminalConsoleAppender
installer "org.jline:jline-terminal:${JLINE_VERSION}" // Dep of TerminalConsoleAppender
installer "org.spongepowered:mixin:${MIXIN_VERSION}"
installer 'org.openjdk.nashorn:nashorn-core:15.3'
installer "net.minecraftforge:JarJar:${JARJAR_VERSION}"
installer "net.minecraftforge:JarJarSelector:${JARJAR_VERSION}"
installer "net.minecraftforge:JarJarMetadata:${JARJAR_VERSION}"
/*
installer 'org.lwjgl:lwjgl:3.2.2'
@@ -374,7 +387,8 @@ def sharedFmlonlyForge = { Project prj ->
environment 'MOD_CLASSES', '{source_roots}'
environment 'MCP_MAPPINGS', '{mcp_mappings}'
property 'nativesDirectory', '{natives}'
// Natives are loaded from the classpath now in all cases
// property 'nativesDirectory', '{natives}'
if (prj.name == 'forge') {
property 'forge.enableGameTest', 'true'
}
@@ -439,7 +453,7 @@ def sharedFmlonlyForge = { Project prj ->
// SecureJarHandler bootstrap values.
run.property 'ignoreList', prj.configurations.moduleonly.files.collect {it.name.replaceAll(/([-_]([.\d]*\d+)|\.jar$)/, '') }.join(',') + ",client-extra,fmlcore,javafmllanguage,lowcodelanguage,mclanguage,${prj.name}-"
// FIXME: Without this jna doesn't work at runtime. Someone figure out why please?
run.property 'mergeModules', 'jna-5.10.0.jar,jna-platform-5.10.0.jar,java-objc-bridge-1.0.0.jar'
run.property 'mergeModules', 'jna-5.10.0.jar,jna-platform-5.10.0.jar'
if (userdevRuns.contains(run)) {
run.property 'legacyClassPath.file', '{minecraft_classpath_file}'
run.jvmArgs '-p', '{modules}'
@@ -450,6 +464,7 @@ def sharedFmlonlyForge = { Project prj ->
run.jvmArgs '--add-modules', 'ALL-MODULE-PATH'
// Additions to these JVM module args should be mirrored to server_files/args.txt and other similar blocks in the buildscript
run.jvmArgs '--add-opens', 'java.base/java.util.jar=cpw.mods.securejarhandler'
run.jvmArgs '--add-opens', 'java.base/java.lang.invoke=cpw.mods.securejarhandler'
run.jvmArgs '--add-exports', 'java.base/sun.security.util=cpw.mods.securejarhandler'
run.jvmArgs '--add-exports', 'jdk.naming.dns/com.sun.jndi.dns=java.naming'
}
@@ -458,7 +473,8 @@ def sharedFmlonlyForge = { Project prj ->
run.workingDirectory prj.file('run')
if (run.name.contains('client') || run.name.contains('data')) {
run.property 'nativesDirectory', prj.extractNatives.output.get().asFile
// Natives are loaded from the classpath now in all cases
// run.property 'nativesDirectory', prj.extractNatives.output.get().asFile
run.args '--assetsDir', prj.downloadAssets.output
run.args '--assetIndex', "{asset_index}"
}
@@ -640,6 +656,7 @@ project(':fmlonly') {
'--add-modules', 'ALL-MODULE-PATH',
// Additions to these JVM module args should be mirrored to server_files/args.txt and other similar blocks in the buildscript
'--add-opens', 'java.base/java.util.jar=cpw.mods.securejarhandler',
'--add-opens', 'java.base/java.lang.invoke=cpw.mods.securejarhandler',
'--add-exports', 'java.base/sun.security.util=cpw.mods.securejarhandler',
'--add-exports', 'jdk.naming.dns/com.sun.jndi.dns=java.naming'
]
@@ -867,6 +884,10 @@ project(':forge') {
apply plugin: 'net.minecraftforge.gradle.patcher'
apply plugin: 'org.cadixdev.licenser'
applyPatches {
level 'WARNING'
}
sourceSets {
main {
java {
@@ -1059,11 +1080,40 @@ project(':forge') {
'--mod', 'tag_based_tool_types',
'--mod', 'custom_transformtype_test',
'--mod', 'data_pack_registries_test',
'--mod', 'biome_modifiers_test',
'--mod', 'structure_modifiers_test',
'--mod', 'custom_preset_editor_test',
'--output', rootProject.file('src/generated_test/resources/'),
'--existing', sourceSets.main.resources.srcDirs[0],
'--existing', sourceSets.test.resources.srcDirs[0]
}
}
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]
}
}
}
}
@@ -1156,12 +1206,31 @@ project(':forge') {
}
}
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 'findFieldInstanceChecks', 'checkLicenses'
dependsOn 'findFinalizeSpawnTargets', 'findFieldInstanceChecks', 'checkLicenses'
group = 'checks'
}
@@ -1200,7 +1269,7 @@ project(':forge') {
}
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'
@@ -1277,6 +1346,7 @@ project(':forge') {
'--add-modules', 'ALL-MODULE-PATH',
// Additions to these JVM module args should be mirrored to server_files/args.txt and other similar blocks in the buildscript
'--add-opens', 'java.base/java.util.jar=cpw.mods.securejarhandler',
'--add-opens', 'java.base/java.lang.invoke=cpw.mods.securejarhandler',
'--add-exports', 'java.base/sun.security.util=cpw.mods.securejarhandler',
'--add-exports', 'jdk.naming.dns/com.sun.jndi.dns=java.naming'
]
@@ -1560,11 +1630,15 @@ project(':forge') {
MAPPING_CHANNEL: MAPPING_CHANNEL,
MAPPING_VERSION: MAPPING_VERSION,
FORGE_SPEC_VERSION: SPEC_VERSION.split("\\.")[0],
MC_NEXT_VERSION: "1."+((MC_VERSION.split("[\\.-]")[1] as int)+1)
MC_NEXT_VERSION: MC_NEXT_VERSION
])
rename 'gitignore\\.txt', '.gitignore'
rename 'gitattributes\\.txt', '.gitattributes'
}
from(rootProject.file('src/generated_mdk/resources/')) {
into('src/main/resources/')
exclude '**/.cache/'
}
}
license {
@@ -1601,6 +1675,83 @@ project(':forge') {
sourceSets.test.java.srcDirs.each { extractMappedNew.addTarget it }
}
tasks.named('javadoc', Javadoc).configure {
description 'Generates the combined javadocs for the FML projects and the main Forge project'
var includedProjects = [ ':fmlcore', ':fmlloader', ':javafmllanguage', ':mclanguage' ]
source includedProjects.collect { project(it).sourceSets.main.allJava }
classpath = classpath + files(includedProjects.collect { project(it).sourceSets.main.compileClasspath })
var docsDir = rootProject.file('src/docs/')
inputs.dir(docsDir)
.withPropertyName('docs resources directory')
.withPathSensitivity(PathSensitivity.RELATIVE)
.optional()
failOnError = false
// Exclude the Minecraft classes if not enabled
if (!project.hasProperty('generateAllDocumentation')) {
exclude 'net/minecraft/**'
exclude 'com/mojang/**'
}
exclude 'mcp/**'
options.addStringOption('Xdoclint:all,-missing', '-public')
options {
stylesheetFile = new File(docsDir, 'stylesheet.css')
tags = [
'apiNote:a:<em>API Note:</em>',
'implSpec:a:<em>Implementation Requirements:</em>',
'implNote:a:<em>Implementation Note:</em>'
]
groups = [
'Forge Mod Loader': [
'net.minecraftforge.fml.common.asm*',
'net.minecraftforge.fml.loading*',
'net.minecraftforge.fml.server*'
],
'FML Core': [
'net.minecraftforge.fml',
'net.minecraftforge.fml.config*',
'net.minecraftforge.fml.event*',
'net.minecraftforge.fml.util*'
],
'FML Common': [
'net.minecraftforge.fml.core',
'net.minecraftforge.fml.event.config',
'net.minecraftforge.fml.event.lifecycle'
],
'FML Java/MC Language Providers': [
'net.minecraftforge.fml.common',
'net.minecraftforge.fml.javafmlmod',
'net.minecraftforge.fml.mclanguageprovider'
],
'Minecraft Forge API': [
'net.minecraftforge*'
]
]
author = false
noSince = true
noHelp = true
bottom = "Minecraft Forge is an open source modding API for Minecraft: Java Edition, licensed under the Lesser GNU General Public License, version 2.1."
windowTitle = "Minecraft Forge API ${VERSION}"
docTitle = "Minecraft Forge API - ${FORGE_VERSION} for Minecraft ${MC_VERSION}"
header = "<div style=\"margin-top: 9px;padding: 5px 6px;\"><strong>${FORGE_VERSION} for Minecraft ${MC_VERSION}</strong></div>"
}
doLast {
project.copy {
from docsDir
exclude '/stylesheet.css'
into destinationDir
}
}
}
publishing {
publications {
mavenJava(MavenPublication) {
@@ -1639,4 +1790,4 @@ if (System.env.TEAMCITY_VERSION) {
println "##teamcity[setParameter name='env.PUBLISHED_JAVA_FML_ARTIFACT_VERSION' value='${project(':fmlonly').version}']"
}
}
}
}
+5 -3
View File
@@ -2,10 +2,12 @@ repositories {
maven { url = 'https://maven.minecraftforge.net/' }
mavenCentral()
}
dependencies {
implementation 'org.ow2.asm:asm:9.1'
implementation 'org.ow2.asm:asm-tree:9.1'
implementation 'net.minecraftforge:srgutils:0.4.+'
implementation 'org.ow2.asm:asm:9.5'
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.')}()
}
}
@@ -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
}
}
@@ -1,5 +1,6 @@
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
@@ -7,7 +8,6 @@ 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.Opcodes
import org.objectweb.asm.Type
import java.util.zip.ZipEntry
@@ -67,7 +67,7 @@ abstract class CheckExcs extends CheckTask {
private void collectKnown(Collection<String> known) {
binary.get().asFile.withInputStream { i ->
new ZipInputStream(i).withCloseable { zin ->
final visitor = new ClassVisitor(Opcodes.ASM9) {
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) {
@@ -35,6 +35,11 @@ abstract class CheckPatches extends CheckTask {
}
}
}
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()
@@ -87,7 +92,7 @@ abstract class CheckPatches extends CheckTask {
pMatcher.group(6) == cMatcher.group(6) && // = ...
pMatcher.group(5) == cMatcher.group(5) && // field name
pMatcher.group(3) == cMatcher.group(3) && // static
(ACCESS_MAP[pMatcher.group(2)] < ACCESS_MAP[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
(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)
}
@@ -98,7 +103,7 @@ abstract class CheckPatches extends CheckTask {
pMatcher.group(6) == cMatcher.group(6) && // params
pMatcher.group(5) == cMatcher.group(5) && // <T> void name
pMatcher.group(3) == cMatcher.group(3) && // static
(ACCESS_MAP[pMatcher.group(2)] < ACCESS_MAP[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
(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)
}
@@ -109,7 +114,7 @@ abstract class CheckPatches extends CheckTask {
pMatcher.group(6) == cMatcher.group(6) && // ClassName<> extends ...
pMatcher.group(5) == cMatcher.group(5) && // class | interface
pMatcher.group(3) == cMatcher.group(3) && // static
(ACCESS_MAP[pMatcher.group(2)] < ACCESS_MAP[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
(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)
}
}
+6 -4
View File
@@ -1,13 +1,13 @@
Contributing to Forge
=====================
1) Keep patches to Minecraft classes minimal. If you need a lot of things done, you may either add to relevant forge classes or make a new class.
1) Keep patches to Minecraft classes together. If you need a lot of things done, you may either add to relevant forge classes or make a new class. Try not to spread out your patches across multiple disjoint lines, as this makes maintenance of your patches difficult.
2) An example mod must be provided for all PRs adding events/hooks to Forge. Whether it's a full mod using your proposed hooks, or just a pastebin containing a simple example, an example mod is required. Please also explain in detail what your new hook does.
2) TODO: Test Mods
3) Follow Forge coding guidelines (braces on newlines, spaces instead of tabs, etc)
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
+35 -23
View File
@@ -2,16 +2,17 @@
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.18.2-&query=$.promos["1.18.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.18.2-&query=$.promos["1.18.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.18.x | Active |
| 1.16.x | LTS |
| Version | Support |
|---------| ------------- |
| 1.20.x | Active |
| 1.19.4 | Legacy |
| 1.19.2 | Legacy |
* [Download]
* [Forum]
@@ -20,7 +21,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
@@ -29,37 +30,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/).
[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). Please ensure you have a valid email address
associated with your GitHub account to do this. If you have previously
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).*
*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][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
+17 -9
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,17 +74,18 @@ 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 IRC Channel:** You can find a lot of help on the official [`#minecraftforge`](irc://irc.esper.net:6667/minecraftforge) channel on EsperNet. Especially smaller issues that you quickly need help with could be reported there. *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
A lot of mods also use GitHub or other source management systems and their integrated issue trackers. The following options will give you the best chances of reporting your issue in the right place:
- **CurseForge:** Many mods are hosted on the [CurseForge platform](https://minecraft.curseforge.com) nowadays. Chances are you already downloaded them from there or as a part of a modpack hosted there. Projects may specify an issue reporting link on their own page, so simply look for it on the mod's project page and you should be directed to the preferred means of error reporting.
- **Searching for a repository directly:** You may need to try and find a repository directly on source control sites. The most common ones are [GitHub](https://github.com) and [BitBucket](https://bitbucket.org). Both come with search capabilities, so simply try to search for the mod's name.
- **Modrinth:** Some modders use [Modrinth](https://modrinth.com/mods) to host their mods, but be mindful that not all mods on this platform are for the Forge modloader.
- **Searching for a repository directly:** You may need to try and find a repository directly on source control sites. The most common ones are [GitHub](https://github.com), [GitLab](https://gitlab.com), and [BitBucket](https://bitbucket.org). Both come with search capabilities, so simply try to search for the mod's name.
### Vanilla Support
@@ -98,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:
@@ -143,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 -3
View File
@@ -10,10 +10,12 @@ apply plugin: 'org.javamodularity.moduleplugin'
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
dependencies {
compileOnly('org.jetbrains:annotations:21.0.1')
compileOnly("org.jetbrains:annotations:${JETBRAINS_ANNOTATIONS_VERSION}")
api("net.minecraftforge:eventbus:${EVENTBUS_VERSION}")
implementation(project(':fmlloader'))
api('net.minecraftforge:eventbus:5.0.+')
implementation('commons-io:commons-io:2.5')
implementation("commons-io:commons-io:${COMMONS_IO_VERSION}")
}
task sourcesJar(type: Jar) {
@@ -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;
}
}
}
@@ -8,41 +8,92 @@ package net.minecraftforge.fml;
import java.util.function.BiPredicate;
import java.util.function.Supplier;
/**
* An extension point for a mod container.
*
* <p>An extension point can be registered for a mod container using {@link ModContainer#registerExtensionPoint(Class, Supplier)}
* and retrieved (if present) using {@link ModContainer#getCustomExtension(Class)}. An extension point allows a mod to
* supply an arbitrary value as a record class to another mod or framework through their mod container class, avoiding
* the use of {@link InterModComms} or other external frameworks to pass around these values.</p>
*
* <p>The usual way to declare an extension point is to implement this interface on a record class, with the type
* parameter being a reference to the class itself. For example, {@code record MyExtension(...) extends
* IExtensionPoint<MyExtension>} would declare an extension point which supplies a {@code MyExtension} object. However,
* there is no hard requirement that an extension point's type parameter must be in reference to itself; the type
* parameter may reference another record class instead.</p>
*
* @param <T> the type of the record which is held by the extension point
*/
@SuppressWarnings("unused") // Type parameter T
public interface IExtensionPoint<T extends Record>
{
/**
* Compatibility display test for the mod.
* Used for displaying compatibility with remote servers with the same mod, and on disk saves.
* Extension point for the compatibility display test used on the server selection screen.
*
* The supplier provides my "local" version for sending across the impl or writing to disk
* The predicate tests the version from a remote instance or save for acceptability (Boolean is true for impl, false for local save)
* and returns true if the version is compatible.
* <em>Note: "server" and "client" refers to the {@linkplain net.minecraftforge.api.distmarker.Dist#DEDICATED_SERVER dedicated server}
* and {@linkplain net.minecraftforge.api.distmarker.Dist#CLIENT game client} physical distributions, rather than the
* {@linkplain LogicalSide logical server and client}.</em>
*
* <p>Return {@code net.minecraftforge.network.NetworkConstants#IGNORESERVERONLY} in the supplier, if you wish to be ignored
* as a server side only mod.</p>
* <p>Return true in the predicate for all values of the input string (when impl boolean is true) if you are client side,
* and don't care about matching any potential server version.</p>
* <p>The {@link Supplier} provides the local compatibility version, which is sent from the server to the client
* for multiplayer connections or stored to disk for the world save. The {@link BiPredicate} accepts the remote
* compatibility version and a boolean indicating whether the remote version is from the server or a world save,
* where {@code true} means it is from the server and {@code false} means it is from the world save. The return
* value of the predicate determines whether the remote version is "compatible" for the purposes of the display test.</p>
*
* <p>
* Examples: A server only mod
* </p>
* <p>The local compatibility version may be of the value {@link net.minecraftforge.network.NetworkConstants#IGNORESERVERONLY},
* in which case clients will ignore the mod's presence if it is present on the server but not on the client.
* However, the remote version test predicate must still accept this value as a remote version in order to display
* as compatible if the mod is present on the client.</p>
*
* <p><strong>The compatibility display test does not necessarily indicate the success or failure of an actual
* connection attempt.</strong> Factors such as display test extension misconfiguration, truncation of ping data,
* difference of registry data or network channels between server and client, and others may cause the result of the
* compatibility test to not reflect the actual likelihood of a connection successfully being established between
* the server and the client.</p>
*
* <p>An example declaration of a display test extension registration for a regular mod (requires to be present on
* server and client) is as follows:</p>
* <pre>{@code
* registerExtensionPoint(DisplayTest.class, ()->new DisplayTest(
* ()->NetworkConstants.IGNORESERVERONLY, // ignore me, I'm a server only mod
* (s,b)->true // i accept anything from the server or the save, if I'm asked
* )
* String compatibilityVersion = "1"; // Could be linked with a network channel version or mod version
* ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class,
* () -> new IExtensionPoint.DisplayTest(
* () -> compatibilityVersion,
* (remoteVersion, isFromServer) -> remoteVersion.equals(compatibilityVersion)
* )
* );
* }</pre>
*
* <p>
* Examples: A client only mod
* </p>
* <p>An example declaration of a display test extension registration for a <em>server-side-only</em> mod (does not
* require to be present on the client) is as follows:</p>
* <pre>{@code
* registerExtensionPoint(DisplayTest.class, ()->new DisplayTest(
* ()->"anything. i don't care", // if i'm actually on the server, this string is sent but i'm a client only mod, so it won't be
* (remoteversionstring,networkbool)->networkbool // i accept anything from the server, by returning true if it's asking about the server
* )
* ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class,
* () -> new IExtensionPoint.DisplayTest(
* // Ignore this mod if not present on the client
* () -> NetworkConstants.IGNORESERVERONLY,
* // If present on the client, accept any version if from a server
* (remoteVersion, isFromServer) -> isFromServer
* )
* );
* }</pre>
*
* <p>An example declaration of a display test extension registration for a <em>client-side-only</em> mod (does not
* require to be present on the server) is as follows:</p>
* <pre>{@code
* ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class,
* () -> new IExtensionPoint.DisplayTest(
* // Send any version from server to client, since we will be accepting any version as well
* () -> "dQw4w9WgXcQ",
* // Accept any version on the client, from server or from save
* (remoteVersion, isFromServer) -> true
* )
* );
* }</pre>
*
* @see net.minecraftforge.network.ServerStatusPing
* @see net.minecraftforge.client.ForgeHooksClient#processForgeListPingData(net.minecraft.network.protocol.status.ServerStatus, net.minecraft.client.multiplayer.ServerData)
*/
record DisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) implements IExtensionPoint<DisplayTest> {}
@SuppressWarnings("JavadocReference") // reference to NetworkConstants, ForgeHooksClient
record DisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) implements IExtensionPoint<DisplayTest> {
public static final String IGNORESERVERONLY = "OHNOES\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31";
}
}
@@ -8,23 +8,79 @@ package net.minecraftforge.fml;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* A mod loading state. During mod loading, the mod loader transitions between states in a defined sorted list of states,
* grouped into various {@link ModLoadingPhase phases}.
*
* @see IModStateProvider
*/
public interface IModLoadingState {
/**
* {@return the name of this state}
*/
String name();
/**
* {@return the name of the state immediately previous to this state} This may be a blank name, which indicates this
* is either the first mod loading state or an exceptional mod loading state (such as a situation where errors
* prevent the loading process from continuing normally).
*/
String previous();
/**
* {@return the mod loading phase this state belongs to} For exceptional mod loading states, this should be
* {@link ModLoadingPhase#ERROR}.
*/
ModLoadingPhase phase();
/**
* {@return a function returning a human-friendly message for this state}
*/
Function<ModList, String> message();
/**
* {@return an optional runnable, which runs before starting the transition from this state to the next}
* @see #buildTransition(Executor, Executor, Function, Function)
*/
Optional<Consumer<ModList>> inlineRunnable();
default <T extends Event & IModBusEvent> Optional<CompletableFuture<List<Throwable>>> buildTransition(final Executor syncExecutor, final Executor parallelExecutor) {
return buildTransition(syncExecutor, parallelExecutor, e->CompletableFuture.runAsync(()->{}, e), e->CompletableFuture.runAsync(()->{}, e));
/**
* Builds the transition task for this state with a blank pre-sync and post-sync task.
*
* @param syncExecutor a synchronous executor
* @param parallelExecutor a parallel executor
* @param <T> a type of event fired on the mod-specific event bus
* @return a transition task for this state
* @see #buildTransition(Executor, Executor, Function, Function)
*/
default <T extends Event & IModBusEvent>
Optional<CompletableFuture<Void>> buildTransition(final Executor syncExecutor,
final Executor parallelExecutor) {
return buildTransition(syncExecutor, parallelExecutor,
e -> CompletableFuture.runAsync(() -> {}, e),
e -> CompletableFuture.runAsync(() -> {}, e));
}
<T extends Event & IModBusEvent> Optional<CompletableFuture<List<Throwable>>> buildTransition(Executor syncExecutor, Executor parallelExecutor, Function<Executor, CompletableFuture<Void>> preSyncTask, Function<Executor, CompletableFuture<Void>> postSyncTask);
/**
* Builds the transition task for this state. The pre-sync and post-sync task functions allow the transition builder
* to run these tasks on the same executor as the actual event dispatch and pre/post hooks.
*
* @param syncExecutor a synchronous executor
* @param parallelExecutor a parallel executor
* @param preSyncTask a function which returns a task to run before event pre-dispatch hook
* @param postSyncTask a function which returns a task to run after event post-dispatch hook
* @param <T> a type of event fired on the mod-specific event bus
* @return a transition task for this state
*/
<T extends Event & IModBusEvent>
Optional<CompletableFuture<Void>> buildTransition(final Executor syncExecutor,
final Executor parallelExecutor,
final Function<Executor, CompletableFuture<Void>> preSyncTask,
final Function<Executor, CompletableFuture<Void>> postSyncTask);
}
@@ -7,6 +7,15 @@ package net.minecraftforge.fml;
import java.util.List;
/**
* Provides a list of mod loading states which the mod loader may transition between.
*
* <p>There may be multiple mod state providers in a single application, where all states from each provider is
* combined into a single list and ordered.</p>
*/
public interface IModStateProvider {
/**
* {@return the list of mod loading states known to this provider}
*/
List<IModLoadingState> getAllStates();
}
@@ -9,7 +9,6 @@ import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@@ -24,18 +23,26 @@ public interface IModStateTransition {
return new NoopTransition();
}
default <T extends Event & IModBusEvent> CompletableFuture<List<Throwable>> build(Executor syncExecutor, Executor parallelExecutor, Function<Executor, CompletableFuture<Void>> preSyncTask, Function<Executor, CompletableFuture<Void>> postSyncTask) {
List<CompletableFuture<List<Throwable>>> cfs = new ArrayList<>();
default <T extends Event & IModBusEvent>
CompletableFuture<Void> build(final Executor syncExecutor,
final Executor parallelExecutor,
final Function<Executor, CompletableFuture<Void>> preSyncTask,
final Function<Executor, CompletableFuture<Void>> postSyncTask) {
List<CompletableFuture<Void>> futures = new ArrayList<>();
this.eventFunctionStream().get()
.map(f->(EventGenerator<T>)f)
.reduce((head, tail)-> addCompletableFutureTaskForModDispatch(syncExecutor, parallelExecutor, cfs, head, ModLoadingStage::currentState, tail))
.ifPresent(last-> addCompletableFutureTaskForModDispatch(syncExecutor, parallelExecutor, cfs, last, nextModLoadingStage(), null));
.map(f -> (EventGenerator<T>) f)
.reduce((head, tail) -> addCompletableFutureTaskForModDispatch(syncExecutor, parallelExecutor, futures, head, ModLoadingStage::currentState, tail))
.ifPresent(last -> addCompletableFutureTaskForModDispatch(syncExecutor, parallelExecutor, futures, last, nextModLoadingStage(), null));
final CompletableFuture<Void> preSyncTaskCF = preSyncTask.apply(syncExecutor);
final CompletableFuture<List<Throwable>> eventDispatchCF = ModList.gather(cfs).thenCompose(ModList::completableFutureFromExceptionList);
final CompletableFuture<List<Throwable>> postEventDispatchCF = preSyncTaskCF.thenComposeAsync(n -> eventDispatchCF, parallelExecutor).thenApply(r -> {
postSyncTask.apply(syncExecutor);
return r;
});
final CompletableFuture<Void> eventDispatchCF = ModList.gather(futures).thenCompose(ModList::completableFutureFromExceptionList);
final CompletableFuture<Void> postEventDispatchCF = preSyncTaskCF
.thenComposeAsync(n -> eventDispatchCF, parallelExecutor)
.thenApply(r -> {
postSyncTask.apply(syncExecutor);
return null;
});
return this.finalActivityGenerator().apply(syncExecutor, postEventDispatchCF);
}
@@ -43,18 +50,31 @@ public interface IModStateTransition {
return ModLoadingStage::nextState;
}
private <T extends Event & IModBusEvent> EventGenerator<T> addCompletableFutureTaskForModDispatch(final Executor syncExecutor, final Executor parallelExecutor, final List<CompletableFuture<List<Throwable>>> completeableFutures, final EventGenerator<T> eventGenerator, BiFunction<ModLoadingStage, Throwable, ModLoadingStage> nextState, final EventGenerator<T> nextGenerator) {
completeableFutures.add(((BiFunction<Executor, EventGenerator<T>, CompletableFuture<List<Throwable>>>)preDispatchHook()).apply(threadSelector().apply(syncExecutor, parallelExecutor), eventGenerator));
completeableFutures.add(ModList.get().futureVisitor(eventGenerator, nextState).apply(threadSelector().apply(syncExecutor, parallelExecutor)));
completeableFutures.add(((BiFunction<Executor, EventGenerator<T>, CompletableFuture<List<Throwable>>>)postDispatchHook()).apply(threadSelector().apply(syncExecutor, parallelExecutor), eventGenerator));
private <T extends Event & IModBusEvent>
EventGenerator<T> addCompletableFutureTaskForModDispatch(final Executor syncExecutor,
final Executor parallelExecutor,
final List<CompletableFuture<Void>> completableFutures,
final EventGenerator<T> eventGenerator,
final BiFunction<ModLoadingStage, Throwable, ModLoadingStage> nextState,
final EventGenerator<T> nextGenerator) {
final Executor selectedExecutor = threadSelector().apply(syncExecutor, parallelExecutor);
var preDispatchHook = (BiFunction<Executor, EventGenerator<T>, CompletableFuture<Void>>) preDispatchHook();
completableFutures.add(preDispatchHook.apply(selectedExecutor, eventGenerator));
completableFutures.add(ModList.get().futureVisitor(eventGenerator, nextState).apply(threadSelector().apply(syncExecutor, parallelExecutor)));
var postDispatchHook = (BiFunction<Executor, EventGenerator<T>, CompletableFuture<Void>>) postDispatchHook();
completableFutures.add(postDispatchHook.apply(selectedExecutor, eventGenerator));
return nextGenerator;
}
Supplier<Stream<EventGenerator<?>>> eventFunctionStream();
ThreadSelector threadSelector();
BiFunction<Executor, CompletableFuture<List<Throwable>>, CompletableFuture<List<Throwable>>> finalActivityGenerator();
BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<List<Throwable>>> preDispatchHook();
BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<List<Throwable>>> postDispatchHook();
BiFunction<Executor, CompletableFuture<Void>, CompletableFuture<Void>> finalActivityGenerator();
BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<Void>> preDispatchHook();
BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<Void>> postDispatchHook();
interface EventGenerator<T extends Event & IModBusEvent> extends Function<ModContainer, T> {
static <FN extends Event & IModBusEvent> EventGenerator<FN> fromFunction(Function<ModContainer, FN> fn) {
@@ -76,17 +96,17 @@ record NoopTransition() implements IModStateTransition {
}
@Override
public BiFunction<Executor, CompletableFuture<List<Throwable>>, CompletableFuture<List<Throwable>>> finalActivityGenerator() {
public BiFunction<Executor, CompletableFuture<Void>, CompletableFuture<Void>> finalActivityGenerator() {
return (e, t) -> t.thenApplyAsync(Function.identity(), e);
}
@Override
public BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<List<Throwable>>> preDispatchHook() {
return (t, f)-> CompletableFuture.completedFuture(Collections.emptyList());
public BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<Void>> preDispatchHook() {
return (t, f)-> CompletableFuture.completedFuture(null);
}
@Override
public BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<List<Throwable>>> postDispatchHook() {
return (t, f)-> CompletableFuture.completedFuture(Collections.emptyList());
public BiFunction<Executor, ? extends EventGenerator<?>, CompletableFuture<Void>> postDispatchHook() {
return (t, f)-> CompletableFuture.completedFuture(null);
}
}
@@ -10,4 +10,9 @@ import java.util.function.Supplier;
public interface ISystemReportExtender extends Supplier<String>
{
String getLabel();
default boolean isActive()
{
return true;
}
}
@@ -5,12 +5,41 @@
package net.minecraftforge.fml;
import net.minecraftforge.api.distmarker.Dist;
/**
* A logical side of the Minecraft game.
* <p>
* The {@linkplain Dist#CLIENT client distribution} has a copy of the logical client and the logical server, while the
* {@linkplain Dist#DEDICATED_SERVER dedicated server distribution} only holds
* the logical server.
*
* @see Dist
*/
public enum LogicalSide
{
CLIENT, SERVER;
/**
* The logical client of the Minecraft game, which interfaces with the player's inputs and renders the player's
* viewpoint.
* <p>
* The logical client is only shipped with the client distribution of the game.
*
* @see Dist#CLIENT
*/
CLIENT,
/**
* The logical server of the Minecraft game, responsible for connecting to clients and running the simulation logic
* on the level.
* <p>
* The logical server is shipped with both client and dedicated server distributions. The client distribution runs
* the logical server for singleplayer mode and LAN play.
*
* @see Dist#DEDICATED_SERVER
*/
SERVER;
/**
* @return if the logical side is a server.
* {@return if this logical side is the server}
*/
public boolean isServer()
{
@@ -18,7 +47,7 @@ public enum LogicalSide
}
/**
* @return if the logical side is a client.
* {@return if the logical side is the client}
*/
public boolean isClient()
{
@@ -56,9 +56,24 @@ public abstract class ModContainer
this.namespace = this.modId;
this.modInfo = info;
this.modLoadingStage = ModLoadingStage.CONSTRUCT;
// default displaytest extension checks for version string match
registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(()->this.modInfo.getVersion().toString(),
(incoming, isNetwork)->Objects.equals(incoming, this.modInfo.getVersion().toString())));
final String displayTestString = info.getConfig().<String>getConfigElement("displayTest").orElse("MATCH_VERSION"); // missing defaults to DEFAULT type
Supplier<IExtensionPoint.DisplayTest> displayTestSupplier = switch (displayTestString) {
case "MATCH_VERSION" -> // default displaytest checks for version string match
() -> new IExtensionPoint.DisplayTest(() -> this.modInfo.getVersion().toString(),
(incoming, isNetwork) -> Objects.equals(incoming, this.modInfo.getVersion().toString()));
case "IGNORE_SERVER_VERSION" -> // Ignores any version information coming from the server - use for server only mods
() -> new IExtensionPoint.DisplayTest(() -> IExtensionPoint.DisplayTest.IGNORESERVERONLY, (incoming, isNetwork) -> true);
case "IGNORE_ALL_VERSION" -> // Ignores all information and provides no information
() -> new IExtensionPoint.DisplayTest(() -> "", (incoming, isNetwork) -> true);
case "NONE" -> null; // NO display test at all - use this if you're going to do your own display test
default -> // any other value throws an exception
throw new IllegalArgumentException("Invalid displayTest value supplied in mods.toml");
};
if (displayTestSupplier != null)
registerExtensionPoint(IExtensionPoint.DisplayTest.class, displayTestSupplier);
else
extensionPoints.remove(IExtensionPoint.DisplayTest.class);
}
/**
@@ -21,7 +21,7 @@ import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -54,6 +54,7 @@ public class ModList
private List<ModContainer> mods;
private Map<String, ModContainer> indexedMods;
private List<ModFileScanData> modFileScanData;
private List<ModContainer> sortedContainers;
private ModList(final List<ModFile> modFiles, final List<ModInfo> sortedList)
{
@@ -111,7 +112,7 @@ public class ModList
return this.fileById.get(modid);
}
<T extends Event & IModBusEvent> Function<Executor, CompletableFuture<List<Throwable>>> futureVisitor(
<T extends Event & IModBusEvent> Function<Executor, CompletableFuture<Void>> futureVisitor(
final IModStateTransition.EventGenerator<T> eventGenerator,
final BiFunction<ModLoadingStage, Throwable, ModLoadingStage> stateChange) {
return executor -> gather(
@@ -120,12 +121,12 @@ public class ModList
.collect(Collectors.toList()))
.thenComposeAsync(ModList::completableFutureFromExceptionList, executor);
}
static CompletionStage<List<Throwable>> completableFutureFromExceptionList(List<? extends Map.Entry<?, Throwable>> t) {
static CompletionStage<Void> completableFutureFromExceptionList(List<? extends Map.Entry<?, Throwable>> t) {
if (t.stream().noneMatch(e->e.getValue()!=null)) {
return CompletableFuture.completedFuture(Collections.emptyList());
return CompletableFuture.completedFuture(null);
} else {
final List<Throwable> throwables = t.stream().filter(e -> e.getValue() != null).map(Map.Entry::getValue).collect(Collectors.toList());
CompletableFuture<List<Throwable>> cf = new CompletableFuture<>();
CompletableFuture<Void> cf = new CompletableFuture<>();
final RuntimeException accumulator = new RuntimeException();
cf.completeExceptionally(accumulator);
throwables.forEach(exception -> {
@@ -156,6 +157,7 @@ public class ModList
void setLoadedMods(final List<ModContainer> modContainers)
{
this.mods = modContainers;
this.sortedContainers = modContainers.stream().sorted(Comparator.comparingInt(c->sortedList.indexOf(c.getModInfo()))).toList();
this.indexedMods = modContainers.stream().collect(Collectors.toMap(ModContainer::getModId, Function.identity()));
}
@@ -219,6 +221,10 @@ public class ModList
indexedMods.forEach(modContainerConsumer);
}
public void forEachModInOrder(Consumer<ModContainer> containerConsumer) {
this.sortedContainers.forEach(containerConsumer);
}
public <T> Stream<T> applyForEachModContainer(Function<ModContainer, T> function) {
return indexedMods.values().stream().map(function);
}
@@ -8,23 +8,21 @@ package net.minecraftforge.fml;
import com.google.common.collect.ImmutableList;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
import net.minecraftforge.fml.loading.FMLEnvironment;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.LoadingModList;
import net.minecraftforge.fml.loading.moddiscovery.InvalidModIdentifier;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import net.minecraftforge.fml.loading.progress.StartupMessageManager;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.IModLanguageProvider;
import net.minecraftforge.forgespi.locating.ForgeFeature;
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.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;
@@ -84,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()
@@ -97,7 +96,7 @@ public class ModLoader
.map(file -> new ModLoadingWarning(null, ModLoadingStage.VALIDATE, InvalidModIdentifier.identifyJarProblem(file.getFilePath()).orElse("fml.modloading.brokenfile"), file.getFileName()))
.collect(Collectors.toList());
FMLLoader.getLoadingModList().getModFiles().stream()
.filter(ModFileInfo::missingLicense) //Search for files with missing licenses
.filter(ModFileInfo::missingLicense)
.filter(modFileInfo -> modFileInfo.getMods().stream().noneMatch(thisModInfo -> this.loadingExceptions.stream().map(ModLoadingException::getModInfo).anyMatch(otherInfo -> otherInfo == thisModInfo))) //Ignore files where any other mod already encountered an error
.map(modFileInfo -> new ModLoadingException(null, ModLoadingStage.VALIDATE, "fml.modloading.missinglicense", null, modFileInfo.getFile()))
.forEach(this.loadingExceptions::add);
@@ -134,6 +133,7 @@ public class ModLoader
* @param periodicTask Optional periodic task to perform on the main thread while other activities run
*/
public void gatherAndInitializeMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) {
ForgeFeature.registerFeature("java_version", ForgeFeature.VersionFeatureTest.forVersionString(IModInfo.DependencySide.SERVER, System.getProperty("java.version")));
loadingStateValid = true;
statusConsumer.ifPresent(c->c.accept("Waiting for scan to complete"));
FMLLoader.backgroundScanHandler.waitForScanToComplete(periodicTask);
@@ -146,6 +146,22 @@ public class ModLoader
loadingStateValid = false;
throw new LoadingFailedException(loadingExceptions);
}
statusConsumer.ifPresent(c->c.accept("Validating features"));
List<? extends ForgeFeature.Bound> failedBounds = loadingModList.getMods().stream()
.map(ModInfo::getForgeFeatures)
.flatMap(Collection::stream)
.filter(bound -> !ForgeFeature.testFeature(FMLEnvironment.dist, bound))
.toList();
if (!failedBounds.isEmpty()) {
LOGGER.fatal(CORE, "Failed to validate feature bounds for mods");
modList.setLoadedMods(Collections.emptyList());
loadingStateValid = false;
throw new LoadingFailedException(failedBounds.stream()
.map(fb -> new ModLoadingException(fb.modInfo(), ModLoadingStage.CONSTRUCT, "fml.modloading.feature.missing", null, fb.featureName(), fb.featureBound(), ForgeFeature.featureValue(fb)))
.toList());
}
statusConsumer.ifPresent(c->c.accept("Building Mod List"));
final List<ModContainer> modContainers = loadingModList.getModFiles().stream()
.map(ModFileInfo::getFile)
@@ -185,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) {
@@ -195,9 +212,10 @@ 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<List<Throwable>> transition) {
private void waitForTransition(final IModLoadingState state, final ModWorkManager.DrivenExecutor syncExecutor, final Runnable ticker, final CompletableFuture<Void> transition) {
while (!transition.isDone()) {
syncExecutor.drive(ticker);
}
@@ -235,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
@@ -269,12 +299,17 @@ 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");
return;
}
ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(generator.apply(mc)));
ModList.get().forEachModInOrder(mc -> mc.acceptEvent(generator.apply(mc)));
}
public <T extends Event & IModBusEvent> void postEvent(T e) {
@@ -282,7 +317,29 @@ public class ModLoader
LOGGER.error("Cowardly refusing to send event {} to a broken mod state", e.getClass().getName());
return;
}
ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(e));
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());
return;
}
ModList.get().forEachModInOrder(mc -> {
pre.accept(mc, e);
mc.acceptEvent(e);
post.accept(mc, e);
});
}
public List<ModLoadingWarning> getWarnings()
@@ -5,13 +5,18 @@
package net.minecraftforge.fml;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.config.IConfigSpec;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.loading.FMLLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Supplier;
public class ModLoadingContext
{
private static final Logger LOGGER = LogUtils.getLogger();
private static final ThreadLocal<ModLoadingContext> context = ThreadLocal.withInitial(ModLoadingContext::new);
private Object languageExtension;
private ModLoadingStage stage;
@@ -46,10 +51,24 @@ public class ModLoadingContext
}
public void registerConfig(ModConfig.Type type, IConfigSpec<?> spec) {
if (spec.isEmpty())
{
// This handles the case where a mod tries to register a config, without any options configured inside it.
LOGGER.debug("Attempted to register an empty config for type {} on mod {}", type, getActiveContainer().getModId());
return;
}
getActiveContainer().addConfig(new ModConfig(type, spec, getActiveContainer()));
}
public void registerConfig(ModConfig.Type type, IConfigSpec<?> spec, String fileName) {
if (spec.isEmpty())
{
// This handles the case where a mod tries to register a config, without any options configured inside it.
LOGGER.debug("Attempted to register an empty config for type {} on mod {} using file name {}", type, getActiveContainer().getModId(), fileName);
return;
}
getActiveContainer().addConfig(new ModConfig(type, spec, getActiveContainer(), fileName));
}
@@ -5,10 +5,34 @@
package net.minecraftforge.fml;
/**
* Phases of mod loading, for grouping mod loading states.
*/
public enum ModLoadingPhase {
/**
* Special phase for exceptional situations and error handling, where mod loading cannot continue normally.
*
* <p>There is conventionally only one state with this phase.</p>
*/
ERROR, // Special state for error handling
/**
* Phase for discovering and gathering mods for loading.
*/
GATHER,
/**
* Phase for the loading of mods found from mod discovery.
*
* @see #GATHER
*/
LOAD,
/**
* Phase after mod loading has completed, for post-loading tasks.
*
* @see #LOAD
*/
COMPLETE,
DONE;
/**
* Marker phase for the last state in the full mod loading process.
*/
DONE
}
@@ -5,16 +5,53 @@
package net.minecraftforge.fml;
/**
* Mod loading stage of mod containers during the mod loading process. These will have a corresponding {@link ModLoadingState}
* in the basic mod loading process provided by FML.
*
* <p>Each mod loading stage has a global {@link DeferredWorkQueue}, which is populated during the execution of the state
* associated with this stage and emptied at the end of the state's execution.</p>
*/
public enum ModLoadingStage
{
/**
* Special stage for exceptional situations and error handling.
*/
ERROR,
/**
* Validation of the mod list.
* TODO: figure out where this is used and why this exists instead of CONSTRUCT being the first normal stage
*/
VALIDATE,
/**
* Default stage of mod containers after construction.
*/
CONSTRUCT,
/**
* Common (non-side-specific) setup and initialization.
*/
COMMON_SETUP,
/**
* Side-specific setup and initialization.
*
* @see net.minecraftforge.api.distmarker.Dist
*/
SIDED_SETUP,
/**
* Stage for enqueuing {@link net.minecraftforge.fml.InterModComms} messages for later processing.
*/
ENQUEUE_IMC,
/**
* Stage for processing received messages though {@link net.minecraftforge.fml.InterModComms}.
*/
PROCESS_IMC,
/**
* Marks the completion of mod loading for this container.
*/
COMPLETE,
/**
* Marks the completion of the full mod loading process.
*/
DONE;
private final DeferredWorkQueue deferredWorkQueue;
@@ -23,14 +60,27 @@ public enum ModLoadingStage
deferredWorkQueue = new DeferredWorkQueue(this);
}
/**
* {@return the next stage after this stage, or {@link #ERROR} if the exception is not {@code null}}
*
* @param exception the exception that occurred during this stage, may be {@code null}
*/
ModLoadingStage nextState(Throwable exception) {
return exception != null ? ERROR : values()[this.ordinal()+1];
}
/**
* {@return this stage, or {@link #ERROR} if the exception is not {@code null}}
*
* @param exception the exception that occurred during this stage, may be {@code null}
*/
public ModLoadingStage currentState(Throwable exception) {
return exception != null ? ERROR : this;
}
/**
* {@return the deferred work queue for this stage}
*/
public DeferredWorkQueue getDeferredWorkQueue() {
return deferredWorkQueue;
}
@@ -8,31 +8,91 @@ package net.minecraftforge.fml;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
public record ModLoadingState(String name, String previous, Function<ModList, String> message, ModLoadingPhase phase, Optional<Consumer<ModList>> inlineRunnable, Optional<IModStateTransition> transition) implements IModLoadingState {
/**
* Implementation of the {@link IModLoadingState} interface.
*
* @param name the name of this state
* @param previous the name of the state immediately previous to this state
* @param message a function returning a human-friendly message for this state
* @param phase the mod loading phase this state belongs to
* @param inlineRunnable an optional runnable, which runs before starting the transition from this state to the next
* @param transition optional state transition information
*/
public record ModLoadingState(String name, String previous,
Function<ModList, String> message,
ModLoadingPhase phase,
Optional<Consumer<ModList>> inlineRunnable,
Optional<IModStateTransition> transition) implements IModLoadingState {
@Override
public <T extends Event & IModBusEvent> Optional<CompletableFuture<List<Throwable>>> buildTransition(final Executor syncExecutor, final Executor parallelExecutor, Function<Executor, CompletableFuture<Void>> preSyncTask, Function<Executor, CompletableFuture<Void>> postSyncTask) {
return transition.map(t->t.build(syncExecutor, parallelExecutor, preSyncTask, postSyncTask));
public <T extends Event & IModBusEvent>
Optional<CompletableFuture<Void>> buildTransition(final Executor syncExecutor,
final Executor parallelExecutor,
final Function<Executor, CompletableFuture<Void>> preSyncTask,
final Function<Executor, CompletableFuture<Void>> postSyncTask) {
return transition.map(t -> t.build(syncExecutor, parallelExecutor, preSyncTask, postSyncTask));
}
/**
* {@return an empty mod loading state} The mod loading state has a blank human-readable message, no inline runnable,
* and no state transition information.
*
* @param name the name of the state
* @param previous the name of the immediately previous state to this state
* @param phase the mod loading phase the state belongs to
*/
public static ModLoadingState empty(final String name, final String previous, final ModLoadingPhase phase) {
return new ModLoadingState(name, previous, ml->"", phase, Optional.empty(), Optional.empty());
return new ModLoadingState(name, previous, ml -> "", phase, Optional.empty(), Optional.empty());
}
public static ModLoadingState withTransition(final String name, final String previous, final ModLoadingPhase phase, IModStateTransition transition) {
return new ModLoadingState(name, previous, ml->"Processing transition "+name, phase, Optional.empty(), Optional.of(transition));
/**
* Returns a mod loading state with state transition information and a default human-friendly message of
* {@code Processing transition [name]}.
*
* @param name the name of the state
* @param previous the name of the immediately previous state to this state
* @param phase the mod loading phase the state belongs to
* @param transition the state transition information
* @return a mod loading state with state transition information and a default message
*/
public static ModLoadingState withTransition(final String name, final String previous, final ModLoadingPhase phase,
final IModStateTransition transition) {
return new ModLoadingState(name, previous, ml -> "Processing transition " + name, phase, Optional.empty(), Optional.of(transition));
}
public static ModLoadingState withTransition(final String name, final String previous, final Function<ModList, String> message, final ModLoadingPhase phase, IModStateTransition transition) {
/**
* Returns a mod loading state with state transition information and a custom human-friendly message function.
*
* @param name the name of the state
* @param previous the name of the immediately previous state to this state
* @param message a function returning a human-friendly message for this state
* @param phase the mod loading phase the state belongs to
* @param transition the state transition information
* @return a mod loading state with state transition information and a custom message
*/
public static ModLoadingState withTransition(final String name, final String previous,
final Function<ModList, String> message, final ModLoadingPhase phase,
final IModStateTransition transition) {
return new ModLoadingState(name, previous, message, phase, Optional.empty(), Optional.of(transition));
}
public static ModLoadingState withInline(final String name, final String previous, final ModLoadingPhase phase, Consumer<ModList> inline) {
return new ModLoadingState(name, previous, ml->"Processing work "+name, phase, Optional.of(inline), Optional.empty());
/**
* Returns a mod loading state with an inline runnable and a default human-friendly message of {@code Processing
* work [name]}.
*
* @param name the name of the state
* @param previous the name of the immediately previous state to this state
* @param phase the mod loading phase the state belongs to
* @param inline an optional runnable, which runs before starting the transition from this state to the next
* @return a mod loading state with an inline runnable and default message
*/
public static ModLoadingState withInline(final String name, final String previous, final ModLoadingPhase phase,
final Consumer<ModList> inline) {
return new ModLoadingState(name, previous, ml -> "Processing work " + name, phase, Optional.of(inline), Optional.empty());
}
}
@@ -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));
}
}
@@ -10,11 +10,11 @@ import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.file.FileWatcher;
import com.electronwill.nightconfig.core.io.ParsingException;
import com.electronwill.nightconfig.core.io.WritingMode;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.FMLConfig;
import net.minecraftforge.fml.loading.FMLPaths;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
@@ -24,7 +24,7 @@ import java.util.function.Function;
import static net.minecraftforge.fml.config.ConfigTracker.CONFIG;
public class ConfigFileTypeHandler {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
static ConfigFileTypeHandler TOML = new ConfigFileTypeHandler();
private static final Path defaultConfigPath = FMLPaths.GAMEDIR.get().resolve(FMLConfig.defaultConfigPath());
@@ -7,18 +7,18 @@ package net.minecraftforge.fml.config;
import com.electronwill.nightconfig.core.CommentedConfig;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class ConfigTracker {
private static final Logger LOGGER = LogManager.getLogger();
static final Marker CONFIG = MarkerManager.getMarker("CONFIG");
private static final Logger LOGGER = LogUtils.getLogger();
static final Marker CONFIG = MarkerFactory.getMarker("CONFIG");
public static final ConfigTracker INSTANCE = new ConfigTracker();
private final ConcurrentHashMap<String, ModConfig> fileMap;
private final EnumMap<ModConfig.Type, Set<ModConfig>> configSets;
@@ -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")
+25 -19
View File
@@ -11,30 +11,36 @@ dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
configurations.forEach{ it.transitive = false }
dependencies {
implementation('net.sf.jopt-simple:jopt-simple:5.0.4')
compileOnly('org.jetbrains:annotations:23.0.0')
compileOnly("org.jetbrains:annotations:${JETBRAINS_ANNOTATIONS_VERSION}")
api("org.ow2.asm:asm:${ASM_VERSION}")
api("org.ow2.asm:asm-tree:${ASM_VERSION}")
api("org.ow2.asm:asm-commons:${ASM_VERSION}")
api("net.minecraftforge:forgespi:${SPI_VERSION}")
api('org.apache.logging.log4j:log4j-api:2.17.0')
api('org.slf4j:slf4j-api:1.8.0-beta4')
api('com.google.guava:guava:21.0')
api('com.google.code.gson:gson:2.8.9')
api('org.apache.maven:maven-artifact:3.8.1')
api('org.apache.commons:commons-lang3:3.12.0')
api('com.electronwill.night-config:core:3.6.4')
api('com.electronwill.night-config:toml:3.6.4')
api('cpw.mods:modlauncher:9.0.+')
api('net.minecraftforge:coremods:5.0.+')
api "net.minecraftforge:JarJar:${JARJAR_VERSION}"
api("org.apache.logging.log4j:log4j-api:${LOG4J_VERSION}")
api("org.slf4j:slf4j-api:${SLF4J_API_VERSION}")
api("com.google.guava:guava:${GUAVA_VERSION}")
api("com.google.code.gson:gson:${GSON_VERSION}")
api("org.apache.maven:maven-artifact:${APACHE_MAVEN_ARTIFACT_VERSION}")
api("org.apache.commons:commons-lang3:${APACHE_COMMONS_LANG3_VERSION}")
api("com.electronwill.night-config:core:${NIGHTCONFIG_VERSION}")
api("com.electronwill.night-config:toml:${NIGHTCONFIG_VERSION}")
api("cpw.mods:modlauncher:${MODLAUNCHER_VERSION}")
api("net.minecraftforge:coremods:${COREMODS_VERSION}")
api("com.mojang:logging:${MOJANG_LOGGING_VERSION}")
api "net.minecraftforge:JarJarSelector:${JARJAR_VERSION}"
api "net.minecraftforge:JarJarMetadata:${JARJAR_VERSION}"
implementation("net.sf.jopt-simple:jopt-simple:${JOPT_SIMPLE_VERSION}")
implementation("cpw.mods:securejarhandler:${SECUREJARHANDLER_VERSION}")
implementation('org.apache.logging.log4j:log4j-core:2.17.0')
annotationProcessor('org.apache.logging.log4j:log4j-core:2.17.0')
implementation('net.minecraftforge:accesstransformers:8.0.4')
implementation('net.minecrell:terminalconsoleappender:1.2.0')
// implementation('org.jline:jline:3.12.+')
testCompileOnly('org.jetbrains:annotations:21.0.0')
implementation("net.minecraftforge:accesstransformers:${ACCESSTRANSFORMERS_VERSION}")
implementation("net.minecrell:terminalconsoleappender:${TERMINALCONSOLEAPPENDER_VERSION}")
// implementation("org.jline:jline:${JLINE_VERSION}")
implementation("org.apache.logging.log4j:log4j-core:${LOG4J_VERSION}")
annotationProcessor("org.apache.logging.log4j:log4j-core:${LOG4J_VERSION}")
testCompileOnly("org.jetbrains:annotations:${JETBRAINS_ANNOTATIONS_VERSION}")
testRuntimeOnly("cpw.mods:bootstraplauncher:${BOOTSTRAPLAUNCHER_VERSION}")
}
@@ -7,7 +7,11 @@ package net.minecraftforge.fml.common.asm;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
@@ -20,7 +24,17 @@ import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
* Will also de-finalize all fields in on class level annotations.
*/
public class ObjectHolderDefinalize implements ILaunchPluginService {
// Hardcoded map of vanilla classes that should have object holders for each field of the given registry type.
// IMPORTANT: Updates to this collection must be reflected in ObjectHolderRegistry. Duplicated cuz classloaders, yay!
// Classnames are validated in ObjectHolderRegistry.
private static final Map<String, VanillaObjectHolderData> VANILLA_OBJECT_HOLDERS = Stream.of(
new VanillaObjectHolderData("net.minecraft.world.level.block.Blocks", "block", "net.minecraft.world.level.block.Block"),
new VanillaObjectHolderData("net.minecraft.world.item.Items", "item", "net.minecraft.world.item.Item"),
new VanillaObjectHolderData("net.minecraft.world.item.enchantment.Enchantments", "enchantment", "net.minecraft.world.item.enchantment.Enchantment"),
new VanillaObjectHolderData("net.minecraft.world.effect.MobEffects", "mob_effect", "net.minecraft.world.effect.MobEffect"),
new VanillaObjectHolderData("net.minecraft.core.particles.ParticleTypes", "particle_type", "net.minecraft.core.particles.ParticleType"),
new VanillaObjectHolderData("net.minecraft.sounds.SoundEvents", "sound_event", "net.minecraft.sounds.SoundEvent")
).collect(Collectors.toMap(VanillaObjectHolderData::holderClass, Function.identity()));
private final String OBJECT_HOLDER = "Lnet/minecraftforge/registries/ObjectHolder;"; //Don't directly reference this to prevent class loading.
@Override
@@ -28,7 +42,6 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
return "object_holder_definalize";
}
private static final EnumSet<Phase> YAY = EnumSet.of(Phase.AFTER);
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
@@ -73,10 +86,8 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
changes.compareAndSet(false, prev != f.access);
});
if (hasHolder(classNode.visibleAnnotations)) //Class level, de-finalize all fields and add @ObjectHolder to them!
if (VANILLA_OBJECT_HOLDERS.containsKey(classType.getClassName())) //Class level, de-finalize all fields and add @ObjectHolder to them!
{
@SuppressWarnings("unused")
String value = getValue(classNode.visibleAnnotations);
classNode.fields.stream().filter(f -> ((f.access & flags) == flags) && f.desc.startsWith("L")).forEach(f ->
{
int prev = f.access;
@@ -95,4 +106,5 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
return changes.get() ? ComputeFlags.SIMPLE_REWRITE : ComputeFlags.NO_REWRITE;
}
private record VanillaObjectHolderData(String holderClass, String registryName, String registryType) {}
}
@@ -9,9 +9,7 @@ import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;
import net.minecraftforge.fml.loading.AdvancedLogMessageAdapter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.mojang.logging.LogUtils;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
@@ -21,6 +19,9 @@ import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
/**
* Modifies specified enums to allow runtime extension by making the $VALUES field non-final and
@@ -28,7 +29,7 @@ import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
*/
public class RuntimeEnumExtender implements ILaunchPluginService {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final Type STRING = Type.getType(String.class);
private final Type ENUM = Type.getType(Enum.class);
private final Type MARKER_IFACE = Type.getType("Lnet/minecraftforge/common/IExtensibleEnum;");
@@ -63,43 +64,49 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
final int flags = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
FieldNode values = classNode.fields.stream().filter(f -> f.desc.contentEquals(array.getDescriptor()) && ((f.access & flags) == flags)).findFirst().orElse(null);
if (!classNode.interfaces.contains(MARKER_IFACE.getInternalName())) {
return ComputeFlags.NO_REWRITE;
}
//Static methods named "create" with first argument as a string
List<MethodNode> candidates = classNode.methods.stream()
.filter(m -> ((m.access & Opcodes.ACC_STATIC) != 0) && m.name.equals("create"))
.collect(Collectors.toList());
if (candidates.isEmpty()) {
throw new IllegalStateException("IExtensibleEnum has no candidate factory methods: " + classType.getClassName());
}
candidates.forEach(mtd ->
{
Type[] args = Type.getArgumentTypes(mtd.desc);
if (args.length == 0 || !args[0].equals(STRING)) {
LOGGER.fatal(()->new AdvancedLogMessageAdapter(sb-> {
if (LOGGER.isErrorEnabled(LogUtils.FATAL_MARKER))
{
StringBuilder sb = new StringBuilder();
sb.append("Enum has create method without String as first parameter:\n");
sb.append(" Enum: ").append(classType.getDescriptor()).append("\n");
sb.append(" Target: ").append(mtd.name).append(mtd.desc).append("\n");
}));
LOGGER.error(LogUtils.FATAL_MARKER, sb.toString());
}
throw new IllegalStateException("Enum has create method without String as first parameter: " + mtd.name + mtd.desc);
}
Type ret = Type.getReturnType(mtd.desc);
if (!ret.equals(classType)) {
LOGGER.fatal(()->new AdvancedLogMessageAdapter(sb-> {
if (LOGGER.isErrorEnabled(LogUtils.FATAL_MARKER))
{
StringBuilder sb = new StringBuilder();
sb.append("Enum has create method with incorrect return type:\n");
sb.append(" Enum: ").append(classType.getDescriptor()).append("\n");
sb.append(" Target: ").append(mtd.name).append(mtd.desc).append("\n");
sb.append(" Found: ").append(ret.getClassName()).append(", Expected: ").append(classType.getClassName());
}));
LOGGER.error(LogUtils.FATAL_MARKER, sb.toString());
}
throw new IllegalStateException("Enum has create method with incorrect return type: " + mtd.name + mtd.desc);
}
Type[] ctrArgs = new Type[args.length + 1];
ctrArgs[0] = STRING;
ctrArgs[1] = Type.INT_TYPE;
@@ -111,23 +118,29 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
MethodNode ctr = classNode.methods.stream().filter(m -> m.name.equals("<init>") && m.desc.equals(desc)).findFirst().orElse(null);
if (ctr == null)
{
LOGGER.fatal(()->new AdvancedLogMessageAdapter(sb-> {
if (LOGGER.isErrorEnabled(LogUtils.FATAL_MARKER))
{
StringBuilder sb = new StringBuilder();
sb.append("Enum has create method with no matching constructor:\n");
sb.append(" Enum: ").append(classType.getDescriptor()).append("\n");
sb.append(" Candidate: ").append(mtd.desc).append("\n");
sb.append(" Target: ").append(desc).append("\n");
classNode.methods.stream().filter(m -> m.name.equals("<init>")).forEach(m -> sb.append(" : ").append(m.desc).append("\n"));
}));
LOGGER.error(LogUtils.FATAL_MARKER, sb.toString());
}
throw new IllegalStateException("Enum has create method with no matching constructor: " + desc);
}
if (values == null)
{
LOGGER.fatal(()->new AdvancedLogMessageAdapter(sb-> {
if (LOGGER.isErrorEnabled(LogUtils.FATAL_MARKER))
{
StringBuilder sb = new StringBuilder();
sb.append("Enum has create method but we could not find $VALUES. Found:\n");
classNode.fields.stream().filter(f -> (f.access & Opcodes.ACC_STATIC) != 0).
forEach(m -> sb.append(" ").append(m.name).append(" ").append(m.desc).append("\n"));
}));
LOGGER.error(LogUtils.FATAL_MARKER, sb.toString());
}
throw new IllegalStateException("Enum has create method but we could not find $VALUES");
}
@@ -1,48 +0,0 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.util.StringBuilderFormattable;
import java.util.function.Consumer;
public class AdvancedLogMessageAdapter implements Message, StringBuilderFormattable {
private static final long serialVersionUID = -8291754833965087436L;
private static final Object[] EMPTY = new Object[0];
private final Consumer<StringBuilder> logMessageBuilder;
public AdvancedLogMessageAdapter(final Consumer<StringBuilder> logMessageBuilder) {
this.logMessageBuilder = logMessageBuilder;
}
@Override
public String getFormattedMessage() {
return "";
}
@Override
public String getFormat() {
return "";
}
@Override
public Object[] getParameters() {
return EMPTY;
}
@Override
public Throwable getThrowable() {
return null;
}
@Override
public void formatTo(final StringBuilder buffer) {
logMessageBuilder.accept(buffer);
}
}
@@ -5,8 +5,8 @@
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import java.net.URI;
import java.net.URISyntaxException;
@@ -16,7 +16,7 @@ import java.nio.file.Paths;
public class ClasspathLocatorUtils {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
public static Path findJarPathFor(final String resourceName, final String jarName, final URL resource) {
try {
@@ -31,7 +31,7 @@ public class ClasspathLocatorUtils {
//LOGGER.debug(CORE, "Found JAR {} at path {}", jarName, path.toString());
return path;
} catch (NullPointerException | URISyntaxException e) {
LOGGER.fatal(LogMarkers.SCAN, "Failed to find JAR for class {} - {}", resourceName, jarName);
LOGGER.error(LogMarkers.SCAN, "Failed to find JAR for class {} - {}", resourceName, jarName);
throw new RuntimeException("Unable to locate "+resourceName+" - "+jarName, e);
}
}
@@ -10,8 +10,8 @@ import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.file.FileNotFoundAction;
import com.electronwill.nightconfig.core.io.ParsingException;
import com.electronwill.nightconfig.core.io.WritingMode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -21,7 +21,7 @@ import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class FMLConfig
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private static FMLConfig INSTANCE = new FMLConfig();
private static ConfigSpec configSpec = new ConfigSpec();
static {
@@ -29,6 +29,7 @@ public class FMLConfig
configSpec.define("maxThreads", -1);
configSpec.define("versionCheck", Boolean.TRUE);
configSpec.define("defaultConfigPath", "defaultconfigs");
configSpec.define("disableOptimizedDFU", Boolean.TRUE);
}
private CommentedFileConfig configData;
@@ -60,12 +61,15 @@ public class FMLConfig
{
final Path configFile = FMLPaths.FMLCONFIG.get();
INSTANCE.loadFrom(configFile);
LOGGER.trace(CORE, "Loaded FML config from {}", FMLPaths.FMLCONFIG.get());
LOGGER.trace(CORE, "Splash screen is {}", FMLConfig::splashScreenEnabled);
LOGGER.trace(CORE, "Max threads for mod loading computed at {}", FMLConfig::loadingThreadCount);
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");
if (LOGGER.isTraceEnabled(CORE))
{
LOGGER.trace(CORE, "Loaded FML config from {}", FMLPaths.FMLCONFIG.get());
LOGGER.trace(CORE, "Splash screen is {}", FMLConfig.splashScreenEnabled());
LOGGER.trace(CORE, "Max threads for mod loading computed at {}", FMLConfig.loadingThreadCount());
LOGGER.trace(CORE, "Version check is {}", FMLConfig.runVersionCheck());
LOGGER.trace(CORE, "Default config paths at {}", FMLConfig.defaultConfigPath());
}
FMLPaths.getOrCreateGameRelativePath(Paths.get(FMLConfig.defaultConfigPath()));
}
public static boolean splashScreenEnabled() {
@@ -85,4 +89,8 @@ public class FMLConfig
public static String defaultConfigPath() {
return INSTANCE.configData.<String>getOptional("defaultConfigPath").orElse("defaultconfigs");
}
public static boolean isOptimizedDFUDisabled() {
return INSTANCE.configData.<Boolean>getOptional("disableOptimizedDFU").orElse(Boolean.TRUE);
}
}
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.*;
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
@@ -21,8 +22,7 @@ import net.minecraftforge.fml.loading.targets.CommonLaunchHandler;
import net.minecraftforge.forgespi.Environment;
import net.minecraftforge.forgespi.coremod.ICoreModProvider;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -35,7 +35,7 @@ import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
public class FMLLoader
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private static AccessTransformerService accessTransformer;
private static ModDiscoverer modDiscoverer;
private static ICoreModProvider coreModProvider;
@@ -62,47 +62,47 @@ public class FMLLoader
final Package modLauncherPackage = ITransformationService.class.getPackage();
LOGGER.debug(CORE,"FML found ModLauncher version : {}", modLauncherPackage.getImplementationVersion());
if (!modLauncherPackage.isCompatibleWith("4.0")) {
LOGGER.fatal(CORE,"Found incompatible ModLauncher specification : {}, version {} from {}", modLauncherPackage.getSpecificationVersion(), modLauncherPackage.getImplementationVersion(), modLauncherPackage.getImplementationVendor());
LOGGER.error(CORE, "Found incompatible ModLauncher specification : {}, version {} from {}", modLauncherPackage.getSpecificationVersion(), modLauncherPackage.getImplementationVersion(), modLauncherPackage.getImplementationVendor());
throw new IncompatibleEnvironmentException("Incompatible modlauncher found "+modLauncherPackage.getSpecificationVersion());
}
accessTransformer = (AccessTransformerService) environment.findLaunchPlugin("accesstransformer").orElseThrow(()-> {
LOGGER.fatal(CORE,"Access Transformer library is missing, we need this to run");
LOGGER.error(CORE, "Access Transformer library is missing, we need this to run");
return new IncompatibleEnvironmentException("Missing AccessTransformer, cannot run");
});
final Package atPackage = accessTransformer.getClass().getPackage();
LOGGER.debug(CORE,"FML found AccessTransformer version : {}", atPackage.getImplementationVersion());
if (!atPackage.isCompatibleWith("1.0")) {
LOGGER.fatal(CORE,"Found incompatible AccessTransformer specification : {}, version {} from {}", atPackage.getSpecificationVersion(), atPackage.getImplementationVersion(), atPackage.getImplementationVendor());
LOGGER.error(CORE, "Found incompatible AccessTransformer specification : {}, version {} from {}", atPackage.getSpecificationVersion(), atPackage.getImplementationVersion(), atPackage.getImplementationVendor());
throw new IncompatibleEnvironmentException("Incompatible accesstransformer found "+atPackage.getSpecificationVersion());
}
eventBus = environment.findLaunchPlugin("eventbus").orElseThrow(()-> {
LOGGER.fatal(CORE,"Event Bus library is missing, we need this to run");
LOGGER.error(CORE, "Event Bus library is missing, we need this to run");
return new IncompatibleEnvironmentException("Missing EventBus, cannot run");
});
final Package eventBusPackage = eventBus.getClass().getPackage();
LOGGER.debug(CORE,"FML found EventBus version : {}", eventBusPackage.getImplementationVersion());
if (!eventBusPackage.isCompatibleWith("1.0")) {
LOGGER.fatal(CORE,"Found incompatible EventBus specification : {}, version {} from {}", eventBusPackage.getSpecificationVersion(), eventBusPackage.getImplementationVersion(), eventBusPackage.getImplementationVendor());
LOGGER.error(CORE, "Found incompatible EventBus specification : {}, version {} from {}", eventBusPackage.getSpecificationVersion(), eventBusPackage.getImplementationVersion(), eventBusPackage.getImplementationVendor());
throw new IncompatibleEnvironmentException("Incompatible eventbus found "+eventBusPackage.getSpecificationVersion());
}
runtimeDistCleaner = (RuntimeDistCleaner)environment.findLaunchPlugin("runtimedistcleaner").orElseThrow(()-> {
LOGGER.fatal(CORE,"Dist Cleaner is missing, we need this to run");
LOGGER.error(CORE, "Dist Cleaner is missing, we need this to run");
return new IncompatibleEnvironmentException("Missing DistCleaner, cannot run!");
});
LOGGER.debug(CORE, "Found Runtime Dist Cleaner");
var coreModProviders = ServiceLoaderUtils.streamWithErrorHandling(ServiceLoader.load(FMLLoader.class.getModule().getLayer(), ICoreModProvider.class), sce -> LOGGER.fatal(CORE, "Failed to load a coremod library, expect problems", sce)).toList();
var coreModProviders = ServiceLoaderUtils.streamWithErrorHandling(ServiceLoader.load(FMLLoader.class.getModule().getLayer(), ICoreModProvider.class), sce -> LOGGER.error(CORE, "Failed to load a coremod library, expect problems", sce)).toList();
if (coreModProviders.isEmpty()) {
LOGGER.fatal(CORE, "Found no coremod provider. Cannot run");
LOGGER.error(CORE, "Found no coremod provider. Cannot run");
throw new IncompatibleEnvironmentException("No coremod library found");
} else if (coreModProviders.size() > 1) {
LOGGER.fatal(CORE, "Found multiple coremod providers : {}. Cannot run", coreModProviders.stream().map(p -> p.getClass().getName()).collect(Collectors.toList()));
LOGGER.error(CORE, "Found multiple coremod providers : {}. Cannot run", coreModProviders.stream().map(p -> p.getClass().getName()).collect(Collectors.toList()));
throw new IncompatibleEnvironmentException("Multiple coremod libraries found");
}
@@ -114,7 +114,7 @@ public class FMLLoader
LOGGER.debug(CORE, "Found ForgeSPI package implementation version {}", Environment.class.getPackage().getImplementationVersion());
LOGGER.debug(CORE, "Found ForgeSPI package specification {}", Environment.class.getPackage().getSpecificationVersion());
if (Integer.parseInt(Environment.class.getPackage().getSpecificationVersion()) < 2) {
LOGGER.fatal(CORE, "Found an out of date ForgeSPI implementation: {}, loading cannot continue", Environment.class.getPackage().getSpecificationVersion());
LOGGER.error(CORE, "Found an out of date ForgeSPI implementation: {}, loading cannot continue", Environment.class.getPackage().getSpecificationVersion());
throw new IncompatibleEnvironmentException("ForgeSPI is out of date, we cannot continue");
}
@@ -122,7 +122,7 @@ public class FMLLoader
Class.forName("com.electronwill.nightconfig.core.Config", false, environment.getClass().getClassLoader());
Class.forName("com.electronwill.nightconfig.toml.TomlFormat", false, environment.getClass().getClassLoader());
} catch (ClassNotFoundException e) {
LOGGER.fatal(CORE, "Failed to load NightConfig");
LOGGER.error(CORE, "Failed to load NightConfig");
throw new IncompatibleEnvironmentException("Missing NightConfig");
}
}
@@ -134,12 +134,12 @@ public class FMLLoader
final Optional<ILaunchHandlerService> launchHandler = environment.findLaunchHandler(launchTarget);
LOGGER.debug(CORE, "Using {} as launch service", launchTarget);
if (launchHandler.isEmpty()) {
LOGGER.fatal(CORE,"Missing LaunchHandler {}, cannot continue", launchTarget);
LOGGER.error(CORE, "Missing LaunchHandler {}, cannot continue", launchTarget);
throw new RuntimeException("Missing launch handler: " + launchTarget);
}
if (!(launchHandler.get() instanceof CommonLaunchHandler)) {
LOGGER.fatal(CORE, "Incompatible Launch handler found - type {}, cannot continue", launchHandler.get().getClass().getName());
LOGGER.error(CORE, "Incompatible Launch handler found - type {}, cannot continue", launchHandler.get().getClass().getName());
throw new RuntimeException("Incompatible launch handler found");
}
commonLaunchHandler = (CommonLaunchHandler)launchHandler.get();
@@ -5,10 +5,12 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.IEnvironment;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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;
@@ -22,7 +24,7 @@ public enum FMLPaths
CONFIGDIR("config"),
FMLCONFIG(false, CONFIGDIR, "fml.toml");
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final Path relativePath;
private final boolean isDirectory;
private Path absolutePath;
@@ -57,16 +59,41 @@ 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))
{
LOGGER.debug(CORE, "Path {} is {}", path, path.absolutePath);
}
LOGGER.debug(CORE,"Path {} is {}", ()-> path, ()-> path.absolutePath);
}
}
/**
* @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() {
@@ -5,14 +5,14 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.*;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionSpecBuilder;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.forgespi.Environment;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.Arrays;
@@ -26,7 +26,7 @@ import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class FMLServiceProvider implements ITransformationService
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private ArgumentAcceptingOptionSpec<String> modsOption;
private ArgumentAcceptingOptionSpec<String> modListsOption;
private ArgumentAcceptingOptionSpec<String> mavenRootsOption;
@@ -5,41 +5,32 @@
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.mojang.logging.LogUtils;
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 = LogManager.getLogger();
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.fatal(CORE,"Failed to create {} directory - there is a file in the way", dirLabel);
} else {
LOGGER.fatal(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;
}
@@ -53,4 +44,16 @@ 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()));
// Check if it contains any of the desired keywords
for (String match : matches) {
if (name.contains(match)) {
return true;
}
}
return false;
}
}
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.IModuleLayerManager;
import cpw.mods.modlauncher.util.ServiceLoaderUtils;
@@ -12,11 +13,10 @@ import net.minecraftforge.fml.loading.progress.StartupMessageManager;
import net.minecraftforge.forgespi.language.IModLanguageProvider;
import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
import org.slf4j.Logger;
import java.net.URISyntaxException;
import java.nio.file.Files;
@@ -38,7 +38,7 @@ import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
public class LanguageLoadingProvider
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final List<IModLanguageProvider> languageProviders = new ArrayList<>();
private final ServiceLoader<IModLanguageProvider> serviceLoader;
private final Map<String, ModLanguageWrapper> languageProviderMap = new HashMap<>();
@@ -81,7 +81,7 @@ public class LanguageLoadingProvider
loadLanguageProviders();
}
private void loadLanguageProviders() {
LOGGER.debug(CORE, "Found {} language providers", ServiceLoaderUtils.streamServiceLoader(()->serviceLoader, sce->LOGGER.fatal("Problem with language loaders")).count());
LOGGER.debug(CORE, "Found {} language providers", ServiceLoaderUtils.streamServiceLoader(()->serviceLoader, sce->LOGGER.error("Problem with language loaders")).count());
serviceLoader.forEach(languageProviders::add);
languageProviders.forEach(lp -> {
@@ -94,7 +94,7 @@ public class LanguageLoadingProvider
Optional<String> implementationVersion = JarVersionLookupHandler.getImplementationVersion(lp.getClass());
String impl = implementationVersion.orElse(Files.isDirectory(lpPath) ? FMLLoader.versionInfo().forgeVersion().split("\\.")[0] : null);
if (impl == null) {
LOGGER.fatal(CORE, "Found unversioned language provider {}", lp.name());
LOGGER.error(CORE, "Found unversioned language provider {}", lp.name());
throw new RuntimeException("Failed to find implementation version for language provider "+ lp.name());
}
LOGGER.debug(CORE, "Found language provider {}, version {}", lp.name(), impl);
@@ -134,7 +134,7 @@ public class LanguageLoadingProvider
}
public IModLanguageProvider findLanguage(ModFile mf, String modLoader, VersionRange modLoaderVersion) {
final String languageFileName = mf.getLocator() instanceof ExplodedDirectoryLocator ? "in-development" : mf.getFileName();
final String languageFileName = mf.getProvider() instanceof ExplodedDirectoryLocator ? "in-development" : mf.getFileName();
final ModLanguageWrapper mlw = languageProviderMap.get(modLoader);
if (mlw == null) {
LOGGER.error(LOADING,"Missing language {} version {} wanted by {}", modLoader, modLoaderVersion, languageFileName);
@@ -5,13 +5,13 @@
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class LauncherVersion {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private static final String launcherVersion;
static {
@@ -5,14 +5,14 @@
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
public class LibraryFinder {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private static Path libsPath;
static Path findLibsPath() {
if (libsPath == null) {
@@ -10,11 +10,18 @@ import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import net.minecraftforge.forgespi.locating.IModFile;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
/**
@@ -28,7 +35,7 @@ public class LoadingModList
private final List<ModInfo> sortedList;
private final Map<String, ModFileInfo> fileById;
private final List<EarlyLoadingException> preLoadErrors;
private List<ModFile> brokenFiles;
private List<IModFile> brokenFiles;
private LoadingModList(final List<ModFile> modFiles, final List<ModInfo> sortedList)
{
@@ -154,11 +161,11 @@ public class LoadingModList
return preLoadErrors;
}
public void setBrokenFiles(final List<ModFile> brokenFiles) {
public void setBrokenFiles(final List<IModFile> brokenFiles) {
this.brokenFiles = brokenFiles;
}
public List<ModFile> getBrokenFiles() {
public List<IModFile> getBrokenFiles() {
return this.brokenFiles;
}
}
@@ -5,12 +5,12 @@
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
public class LogMarkers {
public static final Marker CORE = MarkerManager.getMarker("CORE");
public static final Marker LOADING = MarkerManager.getMarker("LOADING");
public static final Marker SCAN = MarkerManager.getMarker("SCAN");
public static final Marker SPLASH = MarkerManager.getMarker("SPLASH");
public static final Marker CORE = MarkerFactory.getMarker("CORE");
public static final Marker LOADING = MarkerFactory.getMarker("LOADING");
public static final Marker SCAN = MarkerFactory.getMarker("SCAN");
public static final Marker SPLASH = MarkerFactory.getMarker("SPLASH");
}
@@ -5,10 +5,10 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.INameMappingService;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.BufferedReader;
import java.io.IOException;
@@ -20,7 +20,7 @@ import java.util.function.BiConsumer;
import java.util.function.BiFunction;
public class MCPNamingService implements INameMappingService {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private HashMap<String, String> methods;
private HashMap<String, String> fields;
@@ -5,20 +5,29 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import cpw.mods.jarhandling.SecureJar;
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
import cpw.mods.modlauncher.api.NamedPath;
import cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService;
import org.apache.logging.log4j.LogManager;
import org.slf4j.Logger;
import java.io.File;
import java.io.IOException;
import java.lang.module.ModuleDescriptor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipFile;
import java.util.Set;
public class ModDirTransformerDiscoverer implements ITransformerDiscoveryService {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Set<String> SERVICES = Set.of(
"cpw.mods.modlauncher.api.ITransformationService",
"net.minecraftforge.forgespi.locating.IModLocator",
"net.minecraftforge.forgespi.locating.IDependencyLocator"
);
@Override
public List<NamedPath> candidates(final Path gameDirectory) {
ModDirTransformerDiscoverer.scan(gameDirectory);
@@ -40,7 +49,7 @@ public class ModDirTransformerDiscoverer implements ITransformerDiscoveryService
try (var walk = Files.walk(modsDir, 1)){
walk.forEach(ModDirTransformerDiscoverer::visitFile);
} catch (IOException | IllegalStateException ioe) {
LogManager.getLogger().error("Error during early discovery", ioe);
LOGGER.error("Error during early discovery", ioe);
}
}
@@ -48,14 +57,11 @@ public class ModDirTransformerDiscoverer implements ITransformerDiscoveryService
if (!Files.isRegularFile(path)) return;
if (!path.toString().endsWith(".jar")) return;
if (LamdbaExceptionUtils.uncheck(() -> Files.size(path)) == 0) return;
try (ZipFile zf = new ZipFile(new File(path.toUri()))) {
if (zf.getEntry("META-INF/services/cpw.mods.modlauncher.api.ITransformationService") != null) {
found.add(new NamedPath(zf.getName(), path));
} else if (zf.getEntry("META-INF/services/net.minecraftforge.forgespi.locating.IModLocator") != null) {
found.add(new NamedPath(zf.getName(), path));
}
} catch (IOException ioe) {
LogManager.getLogger().error("Zip Error when loading jar file {}", path, ioe);
}
SecureJar jar = SecureJar.from(path);
jar.moduleDataProvider().descriptor().provides().stream()
.map(ModuleDescriptor.Provides::service)
.filter(SERVICES::contains)
.forEach(s -> found.add(new NamedPath(s, path)));
}
}
@@ -5,9 +5,9 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStream;
@@ -24,7 +24,7 @@ import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class ModJarURLHandler extends URLStreamHandler
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
// modjar://modid/path/to/file
@Override
protected URLConnection openConnection(URL url) {
@@ -7,8 +7,10 @@ package net.minecraftforge.fml.loading;
import com.google.common.graph.GraphBuilder;
import com.google.common.graph.MutableGraph;
import com.mojang.logging.LogUtils;
import cpw.mods.jarhandling.SecureJar;
import net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator;
import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.fml.loading.EarlyLoadingException.ExceptionData;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
@@ -16,10 +18,9 @@ import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import net.minecraftforge.fml.loading.toposort.CyclePresentException;
import net.minecraftforge.fml.loading.toposort.TopologicalSort;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.slf4j.Logger;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
@@ -33,7 +34,7 @@ import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
public class ModSorter
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final UniqueModListBuilder uniqueModListBuilder;
private List<ModFile> modFiles;
private List<ModInfo> sortedList;
@@ -98,11 +99,10 @@ public class ModSorter
catch (CyclePresentException e)
{
Set<Set<ModFileInfo>> cycles = e.getCycles();
LOGGER.error(LOADING, () -> new AdvancedLogMessageAdapter(buffer ->
buffer.append("Mod Sorting failed.\n")
.append("Detected Cycles: ")
.append(cycles)
.append('\n')));
if (LOGGER.isErrorEnabled(LOADING))
{
LOGGER.error(LOADING, "Mod Sorting failed.\nDetected Cycles: {}\n", cycles);
}
var dataList = cycles.stream()
.<ModFileInfo>mapMulti(Iterable::forEach)
.<IModInfo>mapMulti((mf,c)->mf.getMods().forEach(c))
@@ -160,9 +160,10 @@ public class ModSorter
systemMods.add("minecraft");
// Find mod file from MinecraftLocator to define the system mods
modFiles.stream()
.filter(modFile -> modFile.getLocator().getClass() == MinecraftLocator.class)
.filter(modFile -> modFile.getProvider().getClass() == MinecraftLocator.class)
.map(ModFile::getSecureJar)
.map(SecureJar::getManifest)
.map(SecureJar::moduleDataProvider)
.map(SecureJar.ModuleDataProvider::getManifest)
.map(Manifest::getMainAttributes)
.map(mf -> mf.getValue("FML-System-Mods"))
.filter(Objects::nonNull)
@@ -16,14 +16,11 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
import com.google.common.collect.Streams;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.api.distmarker.OnlyIns;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -33,11 +30,14 @@ import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
public class RuntimeDistCleaner implements ILaunchPluginService
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Marker DISTXFORM = MarkerManager.getMarker("DISTXFORM");
private static final Logger LOGGER = LogUtils.getLogger();
private static final Marker DISTXFORM = MarkerFactory.getMarker("DISTXFORM");
private static String DIST;
private static final String ONLYIN = Type.getDescriptor(OnlyIn.class);
private static final String ONLYINS = Type.getDescriptor(OnlyIns.class);
@@ -53,7 +53,7 @@ public class RuntimeDistCleaner implements ILaunchPluginService
AtomicBoolean changes = new AtomicBoolean();
if (remove(classNode.visibleAnnotations, DIST))
{
LOGGER.fatal(DISTXFORM, "Attempted to load class {} for invalid dist {}", classNode.name, DIST);
LOGGER.error(DISTXFORM, "Attempted to load class {} for invalid dist {}", classNode.name, DIST);
throw new RuntimeException("Attempted to load class "+ classNode.name + " for invalid dist "+ DIST);
}
@@ -5,8 +5,7 @@
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.PrintStream;
/**
@@ -5,21 +5,27 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.forgespi.language.IModInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.slf4j.Logger;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import static 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;
public class UniqueModListBuilder
{
private final static Logger LOGGER = LogManager.getLogger();
private final static Logger LOGGER = LogUtils.getLogger();
private final List<ModFile> modFiles;
@@ -66,28 +72,35 @@ 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<IModInfo> dupedMods = modIds.values().stream()
final List<String> dupedModErrors = modIds.values().stream()
.filter(modInfos -> modInfos.size() > 1)
.map(modInfos -> modInfos.get(0))
.toList();
.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();
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);
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());
}
final List<ModFile> dupedLibs = versionedLibIds.values().stream()
.filter(modFiles -> modFiles.size() > 1)
.map(modFiles -> modFiles.get(0))
.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);
final List<String> dupedLibErrors = 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();
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());
}
// Collect unique mod files by module name. This will be used for deduping purposes
@@ -106,16 +119,17 @@ public class UniqueModListBuilder
if (modInfoList.size() > 1) {
LOGGER.debug("Found {} mods for first modid {}, selecting most recent based on version data", modInfoList.size(), fullList.getKey());
modInfoList.sort(Comparator.comparing(this::getVersion).reversed());
LOGGER.debug("Selected file {} for modid {} with version {}", modInfoList.get(0).getFileName(), fullList.getKey(), modInfoList.get(0).getModInfos().get(0).getVersion());
LOGGER.debug("Selected file {} for modid {} with version {}", modInfoList.get(0).getFileName(), fullList.getKey(), this.getVersion(modInfoList.get(0)));
}
return modInfoList.get(0);
}
private ArtifactVersion getVersion(final ModFile mf)
{
if (mf.getModFileInfo() == null) {
if (mf.getModFileInfo() == null || mf.getModInfos() == null || mf.getModInfos().isEmpty()) {
return mf.getJarVersion();
}
return mf.getModInfos().get(0).getVersion();
}
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading;
import net.minecraftforge.forgespi.language.MavenVersionAdapter;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
@@ -18,16 +19,12 @@ public class VersionSupportMatrix {
private static final HashMap<String, List<ArtifactVersion>> overrideVersions = new HashMap<>();
static {
final ArtifactVersion version = new DefaultArtifactVersion(FMLLoader.versionInfo().mcVersion());
// if (MavenVersionAdapter.createFromVersionSpec("[1.16.4,1.16.5]").containsVersion(version)) {
// // 1.16.4 is Compatible with 1.16.3
// add("languageloader.javafml", "34");
// add("mod.minecraft", "1.16.3");
// add("mod.forge", "34.1.42");
// // 1.16.5 is Compatible with 1.16.4, and thus 1.16.3
// add("languageloader.javafml", "35");
// add("mod.minecraft", "1.16.4");
// add("mod.forge", "35.1.37");
// }
if (MavenVersionAdapter.createFromVersionSpec("[1.19.2]").containsVersion(version)) {
// 1.19.2 is Compatible with 1.19.1
add("languageloader.javafml", "42");
add("mod.minecraft", "1.19.1");
add("mod.forge", "42.0.9");
}
}
private static void add(String key, String value) {
overrideVersions.computeIfAbsent(key, k -> new ArrayList<>()).add(new DefaultArtifactVersion(value));
@@ -5,9 +5,11 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import net.minecraftforge.forgespi.locating.IDependencyLocator;
import net.minecraftforge.forgespi.locating.IModFile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
import org.slf4j.Logger;
import java.io.FileNotFoundException;
import java.io.InputStream;
@@ -15,9 +17,9 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
public abstract class AbstractJarFileDependencyLocator extends AbstractJarFileLocator
public abstract class AbstractJarFileDependencyLocator extends AbstractJarFileModProvider implements IDependencyLocator
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
protected Optional<InputStream> loadResourceFromModFile(final IModFile modFile, final Path path) {
try {
@@ -36,11 +38,11 @@ public abstract class AbstractJarFileDependencyLocator extends AbstractJarFileLo
protected Optional<IModFile> loadModFileFrom(final IModFile file, final Path path) {
try {
final Path pathInModFile = file.findResource(path.toString());
return createMod(pathInModFile);
return Optional.of(createMod(pathInModFile).file());
}
catch (Exception e) {
LOGGER.error("Failed to load mod file {} from {}", path, file.getFileName());
return Optional.empty();
throw new ModFileLoadingException("Failed to load mod file "+file.getFileName());
}
}
@@ -5,25 +5,18 @@
package net.minecraftforge.fml.loading.moddiscovery;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModLocator;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class AbstractJarFileModLocator extends AbstractJarFileLocator implements IModLocator
public abstract class AbstractJarFileModLocator extends AbstractJarFileModProvider implements IModLocator
{
@Override
public List<IModFile> scanMods()
public List<IModLocator.ModFileOrException> scanMods()
{
return scanCandidates()
.map(this::createMod)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
return scanCandidates().map(this::createMod).toList();
}
public abstract Stream<Path> scanCandidates();
@@ -5,11 +5,11 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import cpw.mods.jarhandling.SecureJar;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.forgespi.locating.IModFile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
@@ -18,9 +18,9 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
public abstract class AbstractJarFileLocator extends AbstractModLocator
public abstract class AbstractJarFileModProvider extends AbstractModProvider
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
@Override
public void scanFile(final IModFile file, final Consumer<Path> pathConsumer) {
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import cpw.mods.jarhandling.JarMetadata;
import cpw.mods.jarhandling.SecureJar;
import net.minecraftforge.fml.loading.LogMarkers;
@@ -13,10 +14,12 @@ import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModLocator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraftforge.forgespi.locating.IModProvider;
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
import org.slf4j.Logger;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -24,42 +27,42 @@ import java.util.Optional;
import java.util.function.Function;
import java.util.jar.Manifest;
public abstract class AbstractModLocator implements IModLocator
public abstract class AbstractModProvider implements IModProvider
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
protected static final String MODS_TOML = "META-INF/mods.toml";
protected static final String MANIFEST = "META-INF/MANIFEST.MF";
protected Optional<IModFile> createMod(Path... path) {
protected IModLocator.ModFileOrException createMod(Path... path) {
var mjm = new ModJarMetadata();
var sj = SecureJar.from(
Manifest::new,
jar -> jar.findFile(MODS_TOML).isPresent() ? mjm : JarMetadata.from(jar, path),
jar -> jar.moduleDataProvider().findFile(MODS_TOML).isPresent() ? mjm : JarMetadata.from(jar, path),
(root, p) -> true,
path
);
IModFile mod;
var type = sj.getManifest().getMainAttributes().getValue(ModFile.TYPE);
var type = sj.moduleDataProvider().getManifest().getMainAttributes().getValue(ModFile.TYPE);
if (type == null) {
type = getDefaultJarModType();
}
if (sj.findFile(MODS_TOML).isPresent()) {
if (sj.moduleDataProvider().findFile(MODS_TOML).isPresent()) {
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MODS_TOML, type, path);
mod = new ModFile(sj, this, ModFileParser::modsTomlParser);
} else if (type != null) {
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MANIFEST, type, path);
mod = new ModFile(sj, this, this::manifestParser, type);
} else {
return Optional.empty();
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found "+ Arrays.toString(path)));
}
mjm.setModFile(mod);
return Optional.of(mod);
return new IModLocator.ModFileOrException(mod, null);
}
protected IModFileInfo manifestParser(final IModFile mod) {
Function<String, Optional<String>> cfg = name -> Optional.ofNullable(mod.getSecureJar().getManifest().getMainAttributes().getValue(name));
Function<String, Optional<String>> cfg = name -> Optional.ofNullable(mod.getSecureJar().moduleDataProvider().getManifest().getMainAttributes().getValue(name));
var license = cfg.apply("LICENSE").orElse("");
var dummy = new IConfigurable() {
@Override
@@ -5,11 +5,11 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.LoadingModList;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.forgespi.language.ModFileScanData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.time.Duration;
import java.time.Instant;
@@ -28,7 +28,7 @@ public class BackgroundScanHandler
ERRORED
}
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final ExecutorService modContentScanner;
private final List<ModFile> pendingFiles;
private final List<ModFile> scannedFiles;
@@ -6,15 +6,18 @@
package net.minecraftforge.fml.loading.moddiscovery;
import net.minecraftforge.fml.loading.ClasspathLocatorUtils;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.LogMarkers;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
@@ -22,8 +25,9 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
public class ClasspathLocator extends AbstractJarFileModLocator {
private static final Logger LOGGER = LogManager.getLogger();
public class ClasspathLocator extends AbstractJarFileModLocator
{
private static final Logger LOGGER = LogUtils.getLogger();
private final List<Path> legacyClasspath = Arrays.stream(System.getProperty("legacyClassPath", "").split(File.pathSeparator)).map(Path::of).toList();
private boolean enabled = false;
@@ -46,7 +50,7 @@ public class ClasspathLocator extends AbstractJarFileModLocator {
return paths.build();
} catch (IOException e) {
LOGGER.fatal(LogMarkers.SCAN, "Error trying to find resources", e);
LOGGER.error(LogMarkers.SCAN, "Error trying to find resources", e);
throw new RuntimeException(e);
}
}
@@ -69,4 +73,22 @@ public class ClasspathLocator extends AbstractJarFileModLocator {
var launchTarget = (String) arguments.get("launchTarget");
enabled = launchTarget != null && launchTarget.contains("dev");
}
private Path findJarPathFor(final String resourceName, final String jarName, final URL resource) {
try {
Path path;
final URI uri = resource.toURI();
if (uri.getScheme().equals("jar") && uri.getRawSchemeSpecificPart().contains("!/")) {
int lastExcl = uri.getRawSchemeSpecificPart().lastIndexOf("!/");
path = Paths.get(new URI(uri.getRawSchemeSpecificPart().substring(0, lastExcl)));
} else {
path = Paths.get(new URI("file://"+uri.getRawSchemeSpecificPart().substring(0, uri.getRawSchemeSpecificPart().length()-resourceName.length())));
}
//LOGGER.debug(CORE, "Found JAR {} at path {}", jarName, path.toString());
return path;
} catch (NullPointerException | URISyntaxException e) {
LOGGER.error(LogMarkers.SCAN, "Failed to find JAR for class {} - {}", resourceName, jarName);
throw new RuntimeException("Unable to locate "+resourceName+" - "+jarName, e);
}
}
}
@@ -5,11 +5,11 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModLocator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
@@ -22,7 +22,7 @@ import java.util.function.Consumer;
import java.util.stream.Stream;
public class ExplodedDirectoryLocator implements IModLocator {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
public record ExplodedMod(String modid, List<Path> paths) {}
@@ -30,14 +30,14 @@ public class ExplodedDirectoryLocator implements IModLocator {
private final Map<ExplodedMod, IModFile> mods = new HashMap<>();
@Override
public List<IModFile> scanMods() {
public List<IModLocator.ModFileOrException> scanMods() {
explodedMods.forEach(explodedMod ->
ModJarMetadata.buildFile(this,
jar->jar.findFile("/META-INF/mods.toml").isPresent(),
jar->jar.moduleDataProvider().findFile("/META-INF/mods.toml").isPresent(),
(a,b) -> true,
explodedMod.paths().toArray(Path[]::new))
.ifPresentOrElse(f->mods.put(explodedMod, f), () -> LOGGER.warn(LogMarkers.LOADING, "Failed to find exploded resource mods.toml in directory {}", explodedMod.paths().get(0).toString())));
return List.copyOf(mods.values());
return mods.values().stream().map(mf->new IModLocator.ModFileOrException(mf, null)).toList();
}
@Override
@@ -6,16 +6,22 @@
package net.minecraftforge.fml.loading.moddiscovery;
import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
import java.util.Locale;
import java.util.Optional;
public class InvalidModFileException extends RuntimeException
public class InvalidModFileException extends ModFileLoadingException
{
private final IModFileInfo modFileInfo;
public InvalidModFileException(String message, IModFileInfo modFileInfo)
{
super(String.format(Locale.ENGLISH, "%s (%s)", message, ((ModFileInfo)modFileInfo).getFile().getFileName()));
super(String.format(Locale.ROOT, "%s (%s)", message, Optional.ofNullable(modFileInfo).map(mf->mf.getFile().getFileName()).orElse("MISSING FILE NAME")));
this.modFileInfo = modFileInfo;
}
public IModFileInfo getBrokenFile() {
return modFileInfo;
}
}
@@ -5,27 +5,34 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.EarlyLoadingException;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
import net.minecraftforge.jarjar.selection.JarSelector;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
@Override
public String name()
@@ -34,20 +41,15 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
}
@Override
public List<IModFile> scanMods(final Iterable<IModFile> loadedMods) {
public List<IModFile> scanMods(final Iterable<IModFile> loadedMods)
{
final List<IModFile> sources = Lists.newArrayList();
loadedMods.forEach(sources::add);
final List<IModFile> dependenciesToLoad = JarSelector.detectAndSelect(sources, this::loadResourceFromModFile, this::loadModFileFrom, this::identifyMod, this::exception);
final List<IModFile> dependenciesToLoad = JarSelector.detectAndSelect(
sources,
this::loadResourceFromModFile,
this::loadModFileFrom,
this::identifyMod,
this::exception
);
if (dependenciesToLoad.isEmpty()) {
if (dependenciesToLoad.isEmpty())
{
LOGGER.info("No dependencies to load found. Skipping!");
return Collections.emptyList();
}
@@ -68,46 +70,89 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
return IModFile.Type.GAMELIBRARY.name();
}
@SuppressWarnings("resource")
@Override
protected Optional<IModFile> loadModFileFrom(final IModFile file, final Path path)
{
try
{
final Path pathInModFile = file.findResource(path.toString());
final URI filePathUri = new URI("jij:" + (pathInModFile.toAbsolutePath().toUri().getRawSchemeSpecificPart())).normalize();
final Map<String, ?> outerFsArgs = ImmutableMap.of("packagePath", pathInModFile);
final FileSystem zipFS = FileSystems.newFileSystem(filePathUri, outerFsArgs);
final Path pathInFS = zipFS.getPath("/");
return Optional.of(createMod(pathInFS).file());
}
catch (Exception e)
{
LOGGER.error("Failed to load mod file {} from {}", path, file.getFileName());
final RuntimeException exception = new ModFileLoadingException("Failed to load mod file " + file.getFileName());
exception.initCause(e);
throw exception;
}
}
protected EarlyLoadingException exception(Collection<JarSelector.ResolutionFailureInformation<IModFile>> failedDependencies)
{
final List<EarlyLoadingException.ExceptionData> errors = failedDependencies.stream()
.filter(entry -> !entry.sources().isEmpty()) //Should never be the case, but just to be sure
.map(entry -> new EarlyLoadingException.ExceptionData(
getErrorTranslationKey(entry),
entry.identifier().group() + ":" + entry.identifier().artifact(),
entry.sources()
.stream()
.flatMap(this::getModWithVersionRangeStream)
.map(this::formatError)
.collect(Collectors.joining(", ")))).toList();
.filter(entry -> !entry.sources().isEmpty()) //Should never be the case, but just to be sure
.map(this::buildExceptionData)
.toList();
return new EarlyLoadingException(failedDependencies.size() + " Dependency restrictions were not met.", null, errors);
}
@NotNull
private EarlyLoadingException.ExceptionData buildExceptionData(final JarSelector.ResolutionFailureInformation<IModFile> entry)
{
return new EarlyLoadingException.ExceptionData(
getErrorTranslationKey(entry),
entry.identifier().group() + ":" + entry.identifier().artifact(),
entry.sources()
.stream()
.flatMap(this::getModWithVersionRangeStream)
.map(this::formatError)
.collect(Collectors.joining(", "))
);
}
@NotNull
private String getErrorTranslationKey(final JarSelector.ResolutionFailureInformation<IModFile> entry)
{
return entry.failureReason() == JarSelector.FailureReason.VERSION_RESOLUTION_FAILED
? "fml.dependencyloading.conflictingdependencies"
: "fml.dependencyloading.mismatchedcontaineddependencies";
return entry.failureReason() == JarSelector.FailureReason.VERSION_RESOLUTION_FAILED ?
"fml.dependencyloading.conflictingdependencies" :
"fml.dependencyloading.mismatchedcontaineddependencies";
}
@NotNull
private Stream<ModWithVersionRange> getModWithVersionRangeStream(final JarSelector.SourceWithRequestedVersionRange<IModFile> file)
{
return file.sources().stream().map(IModFile::getModFileInfo)
.flatMap(modFileInfo -> modFileInfo.getMods().stream())
.map(modInfo -> new ModWithVersionRange(modInfo, file.requestedVersionRange(), file.includedVersion()));
return file.sources()
.stream()
.map(IModFile::getModFileInfo)
.flatMap(modFileInfo -> modFileInfo.getMods().stream())
.map(modInfo -> new ModWithVersionRange(modInfo, file.requestedVersionRange(), file.includedVersion()));
}
@NotNull
private String formatError(final ModWithVersionRange modWithVersionRange)
{
return "\u00a7e" + modWithVersionRange.modInfo().getModId() + "\u00a7r - \u00a74"
+ modWithVersionRange.versionRange().toString() + "\u00a74 - \u00a72"
+ modWithVersionRange.artifactVersion().toString() + "\u00a72";
return "\u00a7e" + modWithVersionRange.modInfo().getModId() + "\u00a7r - \u00a74" + modWithVersionRange.versionRange().toString() + "\u00a74 - \u00a72" + modWithVersionRange.artifactVersion().toString() + "\u00a72";
}
private record ModWithVersionRange(IModInfo modInfo, VersionRange versionRange, ArtifactVersion artifactVersion) {}
@Override
protected String identifyMod(final IModFile modFile)
{
if (modFile.getModFileInfo() == null || modFile.getModInfos().isEmpty())
{
return modFile.getFileName();
}
return modFile.getModInfos().stream().map(IModInfo::getModId).collect(Collectors.joining());
}
private record ModWithVersionRange(IModInfo modInfo, VersionRange versionRange, ArtifactVersion artifactVersion)
{}
}
@@ -15,7 +15,8 @@ import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MavenDirectoryLocator extends AbstractJarFileModLocator {
public class MavenDirectoryLocator extends AbstractJarFileModLocator
{
private List<Path> modCoords;
@Override
@@ -6,6 +6,7 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.electronwill.nightconfig.core.Config;
import com.mojang.logging.LogUtils;
import cpw.mods.jarhandling.SecureJar;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.LogMarkers;
@@ -13,21 +14,24 @@ import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModLocator;
import net.minecraftforge.forgespi.locating.ModFileFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MinecraftLocator extends AbstractModLocator implements IModLocator {
private static final Logger LOGGER = LogManager.getLogger();
public class MinecraftLocator extends AbstractModProvider implements IModLocator
{
private static final Logger LOGGER = LogUtils.getLogger();
@Override
public List<IModFile> scanMods() {
public List<IModLocator.ModFileOrException> scanMods() {
final var launchHandler = FMLLoader.getLaunchHandler();
var baseMC = launchHandler.getMinecraftPaths();
var mcjar = ModJarMetadata.buildFile(j->ModFileFactory.FACTORY.build(j, this, this::buildMinecraftTOML), j->true, baseMC.minecraftFilter(), baseMC.minecraftPaths().toArray(Path[]::new)).orElseThrow();
@@ -36,12 +40,11 @@ public class MinecraftLocator extends AbstractModLocator implements IModLocator
.map(sj -> new ModFile(sj, this, ModFileParser::modsTomlParser))
.collect(Collectors.<IModFile>toList());
var othermods = baseMC.otherModPaths().stream()
.map(p -> createMod(p.toArray(Path[]::new)).orElse(null))
.filter(Objects::nonNull)
.toList();
.map(p -> createMod(p.toArray(Path[]::new)))
.filter(Objects::nonNull);
artifacts.add(mcjar);
artifacts.addAll(othermods);
return artifacts;
return Stream.concat(artifacts.stream().map(f -> new ModFileOrException(f, null)), othermods).toList();
}
private IModFileInfo buildMinecraftTOML(final IModFile iModFile) {
@@ -7,6 +7,7 @@ package net.minecraftforge.fml.loading.moddiscovery;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.IModuleLayerManager;
import cpw.mods.modlauncher.util.ServiceLoaderUtils;
@@ -15,35 +16,48 @@ import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.fml.loading.UniqueModListBuilder;
import net.minecraftforge.fml.loading.progress.StartupMessageManager;
import net.minecraftforge.forgespi.Environment;
import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.locating.IDependencyLocator;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModLocator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
public class ModDiscoverer {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final ServiceLoader<IModLocator> modLocators;
private final ServiceLoader<IDependencyLocator> dependencyLocators;
private final List<IModLocator> modLocatorList;
private final List<IDependencyLocator> dependencyLocatorList;
public ModDiscoverer(Map<String, ?> arguments) {
Launcher.INSTANCE.environment().computePropertyIfAbsent(Environment.Keys.MODDIRECTORYFACTORY.get(), v->ModsFolderLocator::new);
Launcher.INSTANCE.environment().computePropertyIfAbsent(Environment.Keys.PROGRESSMESSAGE.get(), v-> StartupMessageManager.locatorConsumer().orElseGet(()-> s->{}));
final var moduleLayerManager = Launcher.INSTANCE.environment().findModuleLayerManager().orElseThrow();
modLocators = ServiceLoader.load(moduleLayerManager.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow(), IModLocator.class);
dependencyLocators = ServiceLoader.load(moduleLayerManager.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow(), IDependencyLocator.class);
modLocatorList = ServiceLoaderUtils.streamServiceLoader(()-> modLocators, sce->LOGGER.error("Failed to load mod locator list", sce)).collect(Collectors.toList());
modLocatorList.forEach(l->l.initArguments(arguments));
dependencyLocatorList = ServiceLoaderUtils.streamServiceLoader(()-> dependencyLocators, sce->LOGGER.error("Failed to load dependency locator list", sce)).collect(Collectors.toList());
dependencyLocatorList.forEach(l->l.initArguments(arguments));
if (LOGGER.isDebugEnabled(LogMarkers.CORE))
{
LOGGER.debug(LogMarkers.CORE, "Found Mod Locators : {}", modLocatorList.stream()
.map(modLocator -> "(%s:%s)".formatted(modLocator.name(),
modLocator.getClass().getPackage().getImplementationVersion())).collect(Collectors.joining(",")));
}
if (LOGGER.isDebugEnabled(LogMarkers.CORE))
{
LOGGER.debug(LogMarkers.CORE, "Found Dependency Locators : {}", dependencyLocatorList.stream()
.map(dependencyLocator -> "(%s:%s)".formatted(dependencyLocator.name(),
dependencyLocator.getClass().getPackage().getImplementationVersion())).collect(Collectors.joining(",")));
}
}
public ModValidator discoverMods() {
@@ -51,18 +65,33 @@ public class ModDiscoverer {
List<ModFile> loadedFiles = new ArrayList<>();
List<EarlyLoadingException.ExceptionData> discoveryErrorData = new ArrayList<>();
boolean successfullyLoadedMods = true;
List<IModFileInfo> brokenFiles = new ArrayList<>();
//Loop all mod locators to get the prime mods to load from.
for (IModLocator locator : modLocatorList) {
try {
LOGGER.debug(LogMarkers.SCAN,"Trying locator {}", locator);
var locatedFiles = locator.scanMods();
if (locatedFiles.stream().anyMatch(file -> !(file instanceof ModFile))) {
LOGGER.error(LogMarkers.SCAN, "A mod locator returned a file which is not a ModFile instance!. They will be skipped!");
LOGGER.debug(LogMarkers.SCAN, "Trying locator {}", locator);
var candidates = locator.scanMods();
LOGGER.debug(LogMarkers.SCAN, "Locator {} found {} candidates or errors", locator, candidates.size());
var exceptions = candidates.stream().map(IModLocator.ModFileOrException::ex).filter(Objects::nonNull).toList();
if (!exceptions.isEmpty()) {
LOGGER.debug(LogMarkers.SCAN, "Locator {} found {} invalid mod files", locator, exceptions.size());
brokenFiles.addAll(exceptions.stream().map(e->e instanceof InvalidModFileException ime ? ime.getBrokenFile() : null).filter(Objects::nonNull).toList());
}
var locatedFiles = candidates.stream().map(IModLocator.ModFileOrException::file).filter(Objects::nonNull).collect(Collectors.toList());
var badModFiles = locatedFiles.stream().filter(file -> !(file instanceof ModFile)).toList();
if (!badModFiles.isEmpty()) {
LOGGER.error(LogMarkers.SCAN, "Locator {} returned {} files which is are not ModFile instances! They will be skipped!", locator, badModFiles.size());
brokenFiles.addAll(badModFiles.stream().map(IModFile::getModFileInfo).toList());
}
locatedFiles.removeAll(badModFiles);
LOGGER.debug(LogMarkers.SCAN, "Locator {} found {} valid mod files", locator, locatedFiles.size());
handleLocatedFiles(loadedFiles, locatedFiles);
} catch (InvalidModFileException imfe) {
// We don't generally expect this exception, since it should come from the candidates stream above and be handled in the Locator, but just in case.
LOGGER.error(LogMarkers.SCAN, "Locator {} found an invalid mod file {}", locator, imfe.getBrokenFile(), imfe);
brokenFiles.add(imfe.getBrokenFile());
} catch (EarlyLoadingException exception) {
LOGGER.error(LogMarkers.SCAN, "Failed to load mods with locator {}", locator, exception);
discoveryErrorData.addAll(exception.getAllData());
@@ -90,7 +119,7 @@ public class ModDiscoverer {
//We can continue loading if prime mods loaded successfully.
if (successfullyLoadedMods) {
LOGGER.debug(LogMarkers.SCAN, "Successfully Loaded {} mods. Attempting to load dependencies...", loadedFiles.size());
for (IModLocator locator : modLocatorList) {
for (IDependencyLocator locator : dependencyLocatorList) {
try {
LOGGER.debug(LogMarkers.SCAN,"Trying locator {}", locator);
final List<IModFile> locatedMods = ImmutableList.copyOf(loadedFiles);
@@ -129,7 +158,7 @@ public class ModDiscoverer {
//Validate the loading. With a deduplicated list, we can now successfully process the artifacts and load
//transformer plugins.
var validator = new ModValidator(modFilesMap, discoveryErrorData);
var validator = new ModValidator(modFilesMap, brokenFiles, discoveryErrorData);
validator.stage1Validation();
return validator;
}
@@ -138,7 +167,7 @@ public class ModDiscoverer {
{
var locatedModFiles = locatedFiles.stream().filter(ModFile.class::isInstance).map(ModFile.class::cast).toList();
for (IModFile mf : locatedModFiles) {
LOGGER.info(LogMarkers.SCAN, "Found mod file {} of type {} with provider {}", mf.getFileName(), mf.getType(), mf.getLocator());
LOGGER.info(LogMarkers.SCAN, "Found mod file {} of type {} with provider {}", mf.getFileName(), mf.getType(), mf.getProvider());
StartupMessageManager.modLoaderConsumer().ifPresent(c->c.accept("Found mod file "+mf.getFileName()+" of type "+mf.getType()));
}
loadedFiles.addAll(locatedModFiles);
@@ -6,6 +6,7 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.google.common.collect.ImmutableMap;
import com.mojang.logging.LogUtils;
import cpw.mods.jarhandling.SecureJar;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.LogMarkers;
@@ -15,12 +16,11 @@ import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.IModLanguageProvider;
import net.minecraftforge.forgespi.language.ModFileScanData;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.IModLocator;
import net.minecraftforge.forgespi.locating.IModProvider;
import net.minecraftforge.forgespi.locating.ModFileFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.slf4j.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -39,7 +39,7 @@ public class ModFile implements IModFile {
// Mods either must have a mods.toml or a manifest. We can no longer just put any jar on the classpath.
@Deprecated(forRemoval = true, since = "1.18")
public static final Manifest DEFAULTMANIFEST;
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
static {
DEFAULTMANIFEST = new Manifest();
@@ -54,7 +54,7 @@ public class ModFile implements IModFile {
private final SecureJar jar;
private final Type modFileType;
private final Manifest manifest;
private final IModLocator locator;
private final IModProvider provider;
private IModFileInfo modFileInfo;
private ModFileScanData fileModFileScanData;
private CompletableFuture<ModFileScanData> futureScanResult;
@@ -64,16 +64,16 @@ public class ModFile implements IModFile {
static final Attributes.Name TYPE = new Attributes.Name("FMLModType");
private SecureJar.Status securityStatus;
public ModFile(final SecureJar jar, final IModLocator locator, final ModFileFactory.ModFileInfoParser parser) {
this(jar, locator, parser, parseType(jar));
public ModFile(final SecureJar jar, final IModProvider provider, final ModFileFactory.ModFileInfoParser parser) {
this(jar, provider, parser, parseType(jar));
}
public ModFile(final SecureJar jar, final IModLocator locator, final ModFileFactory.ModFileInfoParser parser, String type) {
this.locator = locator;
public ModFile(final SecureJar jar, final IModProvider provider, final ModFileFactory.ModFileInfoParser parser, String type) {
this.provider = provider;
this.jar = jar;
this.parser = parser;
manifest = this.jar.getManifest();
manifest = this.jar.moduleDataProvider().getManifest();
modFileType = Type.valueOf(type);
jarVersion = Optional.ofNullable(manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION)).orElse("0.0NONE");
this.modFileInfo = ModFileParser.readModList(this, this.parser);
@@ -129,7 +129,7 @@ public class ModFile implements IModFile {
}
public void scanFile(Consumer<Path> pathConsumer) {
locator.scanFile(this, pathConsumer);
provider.scanFile(this, pathConsumer);
}
public void setFutureScanResult(CompletableFuture<ModFileScanData> future) {
@@ -194,8 +194,8 @@ public class ModFile implements IModFile {
}
@Override
public IModLocator getLocator() {
return locator;
public IModProvider getProvider() {
return provider;
}
@Override
@@ -214,7 +214,7 @@ public class ModFile implements IModFile {
}
private static String parseType(final SecureJar jar) {
final Manifest m = jar.getManifest();
final Manifest m = jar.moduleDataProvider().getManifest();
final Optional<String> value = Optional.ofNullable(m.getMainAttributes().getValue(TYPE));
return value.orElse("MOD");
}
@@ -6,6 +6,7 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.google.common.base.Strings;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.fml.loading.StringUtils;
@@ -13,8 +14,7 @@ import net.minecraftforge.forgespi.language.IConfigurable;
import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.MavenVersionAdapter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import javax.security.auth.x500.X500Principal;
import java.net.URL;
import java.security.CodeSigner;
@@ -32,7 +32,7 @@ import java.util.stream.Stream;
public class ModFileInfo implements IModFileInfo, IConfigurable
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final IConfigurable config;
private final ModFile modFile;
private final URL issueURL;
@@ -68,10 +68,13 @@ public class ModFileInfo implements IModFileInfo, IConfigurable
this.mods = modConfigs.stream()
.map(mi-> new ModInfo(this, mi))
.collect(Collectors.toList());
LOGGER.debug(LogMarkers.LOADING, "Found valid mod file {} with {} mods - versions {}",
this.modFile::getFileName,
() -> this.mods.stream().map(IModInfo::getModId).collect(Collectors.joining(",", "{", "}")),
() -> this.mods.stream().map(IModInfo::getVersion).map(Objects::toString).collect(Collectors.joining(",", "{", "}")));
if (LOGGER.isDebugEnabled(LogMarkers.LOADING))
{
LOGGER.debug(LogMarkers.LOADING, "Found valid mod file {} with {} mods - versions {}",
this.modFile.getFileName(),
this.mods.stream().map(IModInfo::getModId).collect(Collectors.joining(",", "{", "}")),
this.mods.stream().map(IModInfo::getVersion).map(Objects::toString).collect(Collectors.joining(",", "{", "}")));
}
}
public ModFileInfo(final ModFile file, final IConfigurable config, final List<LanguageSpec> languageSpecs) {
@@ -8,12 +8,12 @@ package net.minecraftforge.fml.loading.moddiscovery;
import com.electronwill.nightconfig.core.file.FileConfig;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.ModFileFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.IOException;
import java.lang.reflect.Type;
@@ -25,7 +25,7 @@ import java.util.Map;
import java.util.stream.Collectors;
public class ModFileParser {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
public static IModFileInfo readModList(final ModFile modFile, final ModFileFactory.ModFileInfoParser parser) {
return parser.build(modFile);
@@ -5,16 +5,17 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.StringSubstitutor;
import net.minecraftforge.fml.loading.StringUtils;
import net.minecraftforge.forgespi.language.IConfigurable;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.MavenVersionAdapter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraftforge.forgespi.locating.ForgeFeature;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
import org.slf4j.Logger;
import java.net.URL;
import java.util.Collections;
@@ -26,10 +27,11 @@ import java.util.stream.Collectors;
public class ModInfo implements IModInfo, IConfigurable
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private static final DefaultArtifactVersion DEFAULT_VERSION = new DefaultArtifactVersion("1");
private static final Pattern VALID_MODID = Pattern.compile("^[a-z][a-z0-9_]{1,63}$");
private static final Pattern VALID_NAMESPACE = Pattern.compile("^[a-z][a-z0-9_.-]{1,63}$");
private static final Pattern VALID_VERSION = Pattern.compile("^\\d+.*");
private final ModFileInfo owningFile;
private final String modId;
@@ -41,8 +43,11 @@ public class ModInfo implements IModInfo, IConfigurable
private final boolean logoBlur;
private final Optional<URL> updateJSONURL;
private final List<? extends IModInfo.ModVersion> dependencies;
private final List<ForgeFeature.Bound> features;
private final Map<String,Object> properties;
private final IConfigurable config;
private final Optional<URL> modUrl;
public ModInfo(final ModFileInfo owningFile, final IConfigurable config)
{
@@ -52,12 +57,12 @@ public class ModInfo implements IModInfo, IConfigurable
this.modId = config.<String>getConfigElement("modId")
.orElseThrow(() -> new InvalidModFileException("Missing modId", owningFile));
if (!VALID_MODID.matcher(this.modId).matches()) {
LOGGER.fatal("Invalid modId found in file {} - {} does not match the standard: {}", this.owningFile.getFile().getFilePath(), this.modId, VALID_MODID.pattern());
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid modId found in file {} - {} does not match the standard: {}", this.owningFile.getFile().getFilePath(), this.modId, VALID_MODID.pattern());
throw new InvalidModFileException("Invalid modId found : " + this.modId, owningFile);
}
this.namespace = config.<String>getConfigElement("namespace").orElse(this.modId);
if (!VALID_NAMESPACE.matcher(this.namespace).matches()) {
LOGGER.fatal("Invalid override namespace found in file {} - {} does not match the standard: {}", this.owningFile.getFile().getFilePath(), this.namespace, VALID_NAMESPACE.pattern());
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid override namespace found in file {} - {} does not match the standard: {}", this.owningFile.getFile().getFilePath(), this.namespace, VALID_NAMESPACE.pattern());
throw new InvalidModFileException("Invalid override namespace found : " + this.namespace, owningFile);
}
this.version = config.<String>getConfigElement("version")
@@ -81,9 +86,23 @@ public class ModInfo implements IModInfo, IConfigurable
.collect(Collectors.toList()))
.orElse(Collections.emptyList());
this.features = ownFile.map(mfi -> mfi.<Map<String, String>>getConfigElement("features", this.modId)
.stream()
.flatMap(m->m.entrySet().stream())
.map(e->new ForgeFeature.Bound(e.getKey(), e.getValue(), this))
.collect(Collectors.toList())).orElse(Collections.emptyList());
this.properties = ownFile.map(mfi -> mfi.<Map<String, Object>>getConfigElement("modproperties", this.modId)
.orElse(Collections.emptyMap()))
.orElse(Collections.emptyMap());
this.modUrl = config.<String>getConfigElement("modUrl")
.map(StringUtils::toURL);
// verify we have a valid mod version otherwise throw an exception
if (!VALID_VERSION.matcher(this.version.toString()).matches()) {
throw new InvalidModFileException("Illegal version number specified "+this.version, this.getOwningFile());
}
}
@Override
@@ -150,6 +169,11 @@ public class ModInfo implements IModInfo, IConfigurable
return this;
}
@Override
public List<? extends ForgeFeature.Bound> getForgeFeatures() {
return this.features;
}
@Override
public <T> Optional<T> getConfigElement(final String... key) {
return this.config.getConfigElement(key);
@@ -160,6 +184,11 @@ public class ModInfo implements IModInfo, IConfigurable
return null;
}
@Override
public Optional<URL> getModURL() {
return modUrl;
}
class ModVersion implements net.minecraftforge.forgespi.language.IModInfo.ModVersion {
private IModInfo owner;
private final String modId;
@@ -167,6 +196,7 @@ public class ModInfo implements IModInfo, IConfigurable
private final boolean mandatory;
private final Ordering ordering;
private final DependencySide side;
private Optional<URL> referralUrl;
public ModVersion(final IModInfo owner, final IConfigurable config) {
this.owner = owner;
@@ -183,6 +213,8 @@ public class ModInfo implements IModInfo, IConfigurable
this.side = config.<String>getConfigElement("side")
.map(DependencySide::valueOf)
.orElse(DependencySide.BOTH);
this.referralUrl = config.<String>getConfigElement("referralUrl")
.map(StringUtils::toURL);
}
@@ -227,6 +259,11 @@ public class ModInfo implements IModInfo, IConfigurable
{
return owner;
}
@Override
public Optional<URL> getReferralURL() {
return referralUrl;
}
}
}
@@ -5,13 +5,13 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.FMLPaths;
import net.minecraftforge.fml.loading.FileUtils;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.fml.loading.MavenCoordinateResolver;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
@@ -23,7 +23,7 @@ import java.util.Objects;
import java.util.stream.Collectors;
public class ModListHandler {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
/**
* Reads the modList paths specified, and searches each maven root for mods matching. Returns a list of mods
* found.
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.IModuleLayerManager;
import cpw.mods.modlauncher.api.ITransformationService;
import net.minecraftforge.fml.loading.EarlyLoadingException;
@@ -12,29 +13,34 @@ import net.minecraftforge.fml.loading.LoadingModList;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.fml.loading.ModSorter;
import net.minecraftforge.fml.loading.progress.StartupMessageManager;
import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.locating.IModFile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import java.util.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ModValidator {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final Map<IModFile.Type, List<ModFile>> modFiles;
private final List<ModFile> candidatePlugins;
private final List<ModFile> candidateMods;
private LoadingModList loadingModList;
private List<ModFile> brokenFiles;
private List<IModFile> brokenFiles;
private final List<EarlyLoadingException.ExceptionData> discoveryErrorData;
public ModValidator(final Map<IModFile.Type, List<ModFile>> modFiles, final List<EarlyLoadingException.ExceptionData> discoveryErrorData) {
public ModValidator(final Map<IModFile.Type, List<ModFile>> modFiles, final List<IModFileInfo> brokenFiles, final List<EarlyLoadingException.ExceptionData> discoveryErrorData) {
this.modFiles = modFiles;
this.candidateMods = lst(modFiles.get(IModFile.Type.MOD));
this.candidateMods.addAll(lst(modFiles.get(IModFile.Type.GAMELIBRARY)));
this.candidatePlugins = lst(modFiles.get(IModFile.Type.LANGPROVIDER));
this.candidatePlugins.addAll(lst(modFiles.get(IModFile.Type.LIBRARY)));
this.discoveryErrorData = discoveryErrorData;
this.brokenFiles = brokenFiles.stream().map(IModFileInfo::getFile).collect(Collectors.toList()); // mutable list
}
private static List<ModFile> lst(List<ModFile> files) {
@@ -42,8 +48,10 @@ public class ModValidator {
}
public void stage1Validation() {
brokenFiles = validateFiles(candidateMods);
LOGGER.debug(LogMarkers.SCAN,"Found {} mod files with {} mods", candidateMods::size, ()-> candidateMods.stream().mapToInt(mf -> mf.getModInfos().size()).sum());
brokenFiles.addAll(validateFiles(candidateMods));
if (LOGGER.isDebugEnabled(LogMarkers.SCAN)) {
LOGGER.debug(LogMarkers.SCAN, "Found {} mod files with {} mods", candidateMods.size(), candidateMods.stream().mapToInt(mf -> mf.getModInfos().size()).sum());
}
StartupMessageManager.modLoaderConsumer().ifPresent(c->c.accept("Found "+ candidateMods.size()+" modfiles to load"));
}
@@ -53,7 +61,7 @@ public class ModValidator {
for (Iterator<ModFile> iterator = mods.iterator(); iterator.hasNext(); )
{
ModFile modFile = iterator.next();
if (!modFile.getLocator().isValid(modFile) || !modFile.identifyMods()) {
if (!modFile.getProvider().isValid(modFile) || !modFile.identifyMods()) {
LOGGER.warn(LogMarkers.SCAN, "File {} has been ignored - it is invalid", modFile.getFilePath());
iterator.remove();
brokenFiles.add(modFile);
@@ -5,12 +5,12 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.FMLPaths;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.fml.loading.ModDirTransformerDiscoverer;
import net.minecraftforge.fml.loading.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -23,9 +23,10 @@ import static cpw.mods.modlauncher.api.LamdbaExceptionUtils.uncheck;
/**
* Support loading mods located in JAR files in the mods folder
*/
public class ModsFolderLocator extends AbstractJarFileModLocator {
public class ModsFolderLocator extends AbstractJarFileModLocator
{
private static final String SUFFIX = ".jar";
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final Path modFolder;
private final String customName;
@@ -5,12 +5,12 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.forgespi.language.IModLanguageProvider;
import net.minecraftforge.forgespi.language.ModFileScanData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStream;
@@ -19,7 +19,7 @@ import java.nio.file.Path;
import java.util.List;
public class Scanner {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private final ModFile fileToScan;
public Scanner(final ModFile fileToScan) {
@@ -34,8 +34,6 @@ import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFW.glfwCreateWindow;
import static org.lwjgl.opengl.GL11.*;
@@ -5,8 +5,8 @@
package net.minecraftforge.fml.loading.targets;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.Arrays;
@@ -20,7 +20,7 @@ import java.util.function.Supplier;
* Can not use JOptSimple as that doesn't parse out the values for keys unless the spec says it has a value.
*/
class ArgumentList {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
private List<Supplier<String[]>> entries = new ArrayList<>();
private Map<String, EntryValue> values = new HashMap<>();
@@ -5,31 +5,26 @@
package net.minecraftforge.fml.loading.targets;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.LibraryFinder;
import net.minecraftforge.fml.loading.VersionInfo;
import net.minecraftforge.api.distmarker.Dist;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.stream.Stream;
public abstract class CommonClientLaunchHandler extends CommonLaunchHandler {
private static final Logger LOGGER = LogManager.getLogger();
@Override public Dist getDist() { return Dist.CLIENT; }
@Override public String getNaming() { return "srg"; }
@Override public boolean isProduction() { return true; }
@Override
public Callable<Void> launchService(String[] arguments, ModuleLayer layer) {
public ServiceRunner launchService(String[] arguments, ModuleLayer layer) {
return () -> {
Class.forName(layer.findModule("minecraft").orElseThrow(),"net.minecraft.client.main.Main").getMethod("main", String[].class).invoke(null, (Object)arguments);
return null;
};
}
@@ -5,8 +5,8 @@
package net.minecraftforge.fml.loading.targets;
import com.google.common.base.Strings;
import cpw.mods.jarhandling.SecureJar;
import net.minecraftforge.fml.loading.FileUtils;
import java.io.File;
import java.nio.file.Path;
@@ -30,8 +30,7 @@ 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 = Paths.get(Arrays.stream(legacyCP).filter(e -> e.contains("client-extra")).findFirst().orElseThrow(() -> new IllegalStateException("Could not find client-extra in legacy classpath")));
mcstream.add(extra);
var extra = findJarOnClasspath(legacyCP, "client-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));
}
@@ -50,8 +50,6 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
if (getDist().isDedicatedServer())
return arguments;
fixNatives();
if (isData())
return arguments;
@@ -80,11 +78,18 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
protected List<Path> getFmlStuff(String[] classpath) {
// We also want the FML things, fmlcore, javafmllanguage, mclanguage, I don't like hard coding these, but hey whatever works for now.
return Arrays.stream(classpath)
.filter(e -> e.contains("fmlcore") || e.contains("javafmllanguage") || e.contains("lowcodelanguage") || e.contains("mclanguage"))
.filter(e -> FileUtils.matchFileName(e, "fmlcore", "javafmllanguage", "lowcodelanguage", "mclanguage"))
.map(Paths::get)
.toList();
}
protected static Path findJarOnClasspath(String[] classpath, String match) {
return Paths.get(Arrays.stream(classpath)
.filter(e -> FileUtils.matchFileName(e, match))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find " + match + " in classpath")));
}
protected BiPredicate<String, String> getMcFilter(Path extra, List<Path> minecraft, Stream.Builder<List<Path>> mods) {
final var packages = getPackages();
final var extraPath = extra.toString().replace('\\', '/');
@@ -119,18 +124,4 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
// Generate a time-based random number, to mimic how n.m.client.Main works
return Long.toString(System.nanoTime() % (int) Math.pow(10, length));
}
private static void fixNatives() {
String paths = System.getProperty("java.library.path");
String nativesDir = System.getProperty("nativesDirectory");
if (nativesDir == null)
return;
if (Strings.isNullOrEmpty(paths))
paths = nativesDir;
else
paths += File.pathSeparator + nativesDir;
System.setProperty("java.library.path", paths);
}
}
@@ -5,12 +5,12 @@
package net.minecraftforge.fml.loading.targets;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import cpw.mods.modlauncher.api.ITransformingClassLoaderBuilder;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.api.distmarker.Dist;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import java.io.File;
import java.nio.file.Path;
@@ -25,7 +25,7 @@ import java.util.stream.Collectors;
public abstract class CommonLaunchHandler implements ILaunchHandlerService {
public record LocatedPaths(List<Path> minecraftPaths, BiPredicate<String, String> minecraftFilter, List<List<Path>> otherModPaths, List<Path> otherArtifacts) {}
protected static final Logger LOGGER = LogManager.getLogger();
protected static final Logger LOGGER = LogUtils.getLogger();
public abstract Dist getDist();
@@ -6,12 +6,11 @@
package net.minecraftforge.fml.loading.targets;
import cpw.mods.jarhandling.SecureJar;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.LibraryFinder;
import net.minecraftforge.fml.loading.VersionInfo;
import net.minecraftforge.api.distmarker.Dist;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.file.Path;
import java.util.List;
@@ -20,17 +19,14 @@ import java.util.function.BiPredicate;
import java.util.stream.Stream;
public abstract class CommonServerLaunchHandler extends CommonLaunchHandler {
protected static final Logger LOGGER = LogManager.getLogger();
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
@Override public String getNaming() { return "srg"; }
@Override public boolean isProduction() { return true; }
@Override
public Callable<Void> launchService(String[] arguments, ModuleLayer layer) {
public ServiceRunner launchService(String[] arguments, ModuleLayer layer) {
return () -> {
Class.forName(layer.findModule("minecraft").orElseThrow(),"net.minecraft.server.Main").getMethod("main", String[].class).invoke(null, (Object)arguments);
return null;
};
}
@@ -10,8 +10,6 @@ import net.minecraftforge.fml.loading.VersionInfo;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -41,8 +39,4 @@ public abstract class CommonUserdevLaunchHandler extends CommonDevLaunchHandler
}
protected abstract void processStreams(String[] classpath, VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods);
protected static Path findJarOnClasspath(String[] classpath, String match) {
return Paths.get(Arrays.stream(classpath).filter(e -> e.contains(match)).findFirst().orElseThrow(() -> new IllegalStateException("Could not find " + match + " in classpath")));
}
}
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading.targets;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.api.distmarker.Dist;
import java.util.concurrent.Callable;
@@ -13,12 +14,11 @@ public class FMLClientDevLaunchHandler extends CommonDevLaunchHandler {
@Override public Dist getDist() { return Dist.CLIENT; }
@Override
public Callable<Void> launchService(String[] arguments, ModuleLayer layer) {
public ServiceRunner launchService(String[] arguments, ModuleLayer layer) {
return () -> {
var args = preLaunch(arguments, layer);
Class.forName(layer.findModule("minecraft").orElseThrow(), "net.minecraft.client.main.Main").getMethod("main", String[].class).invoke(null, (Object) args);
return null;
};
}
}
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading.targets;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.api.distmarker.Dist;
import java.util.concurrent.Callable;
@@ -17,12 +18,11 @@ public class FMLClientUserdevLaunchHandler extends FMLUserdevLaunchHandler {
public Dist getDist() { return Dist.CLIENT; }
@Override
public Callable<Void> launchService(String[] arguments, ModuleLayer layer) {
public ServiceRunner launchService(String[] arguments, ModuleLayer layer) {
return () -> {
var args = preLaunch(arguments, layer);
Class.forName(layer.findModule("minecraft").orElseThrow(), "net.minecraft.client.main.Main").getMethod("main", String[].class).invoke(null, (Object) args);
return null;
};
}
}
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading.targets;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.api.distmarker.Dist;
import java.util.concurrent.Callable;
@@ -20,12 +21,11 @@ public class FMLDataUserdevLaunchHandler extends FMLUserdevLaunchHandler {
public boolean isData() { return true; }
@Override
public Callable<Void> launchService(String[] arguments, ModuleLayer layer) {
public ServiceRunner launchService(String[] arguments, ModuleLayer layer) {
return () -> {
var args = preLaunch(arguments, layer);
Class.forName(layer.findModule("minecraft").orElseThrow(), "net.minecraft.data.Main").getMethod("main", String[].class).invoke(null, (Object) args);
return null;
};
}
}
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading.targets;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.api.distmarker.Dist;
import java.util.concurrent.Callable;
@@ -13,12 +14,11 @@ public class FMLServerDevLaunchHandler extends CommonDevLaunchHandler {
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
@Override
public Callable<Void> launchService(String[] arguments, ModuleLayer layer) {
public ServiceRunner launchService(String[] arguments, ModuleLayer layer) {
return () -> {
var args = preLaunch(arguments, layer);
Class.forName(layer.findModule("minecraft").orElseThrow(),"net.minecraft.server.Main").getMethod("main", String[].class).invoke(null, (Object)args);
return null;
};
}
}
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading.targets;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.api.distmarker.Dist;
import java.util.concurrent.Callable;
@@ -14,12 +15,11 @@ public class FMLServerUserdevLaunchHandler extends FMLUserdevLaunchHandler {
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
@Override
public Callable<Void> launchService(String[] arguments, ModuleLayer layer) {
public ServiceRunner launchService(String[] arguments, ModuleLayer layer) {
return () -> {
var args = preLaunch(arguments, layer);
Class.forName(layer.findModule("minecraft").orElseThrow(),"net.minecraft.server.Main").getMethod("main", String[].class).invoke(null, (Object)args);
return null;
};
}
}
@@ -5,6 +5,7 @@
package net.minecraftforge.fml.loading.targets;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.api.distmarker.Dist;
import java.util.concurrent.Callable;
@@ -13,12 +14,11 @@ public class ForgeClientDevLaunchHandler extends CommonDevLaunchHandler {
@Override public Dist getDist() { return Dist.CLIENT; }
@Override
public Callable<Void> launchService(String[] arguments, ModuleLayer layer) {
public ServiceRunner launchService(String[] arguments, ModuleLayer layer) {
return () -> {
var args = preLaunch(arguments, layer);
Class.forName(layer.findModule("minecraft").orElseThrow(), "net.minecraft.client.main.Main").getMethod("main", String[].class).invoke(null, (Object) args);
return null;
};
}
}
Loaded 100 of 2211 files, more files were not shown because too many files have changed in this diff. Show more