Compare commits

..
197 Commits
Author SHA1 Message Date
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
ChampionAsh5357 270e778ac5 [1.18.x] Fix #8530 (Banner Pattern) NPE (#8666) 2022-06-07 17:43:17 +01:00
Dennis C 9528fd8bcc Revert "[1.18.x] Enable the Forge light pipeline by default (#8629)" (#8663) 2022-06-06 03:31:30 +01:00
Take Weiland 5a75314bb3 Fix eager loading of model loaders during datagen causing NPE (#8659) 2022-06-05 20:33:32 +01:00
Autovw cceb8db74e [1.18.x] Fix 2 crossbow animations not applying to custom variants (#8625) 2022-06-05 16:34:02 +01:00
Redstone_DubstepandCurle 8e7b08b96d [1.18.x] Added new Disconnection screen to display better channel/registry mismatch information (#8402)
Co-authored-by: Curle <[email protected]>
2022-06-05 16:01:17 +01:00
Gegy 194f11069f [1.18] Avoid exclusive synchronisation in block render type lookups (#8476) 2022-06-05 15:56:54 +01:00
sciwhiz12 5ef0d667e0 [1.18] Fix command redirects by replacing Commands#fillUsableCommands (#8616)
Commands#fillUsableCommands does not handle redirect nodes properly. It
blindly pulls from the cached node map for a redirect node, which may
have no result because the target node of the redirect has not been
visited yet.

CommandHelper#mergeCommandNode is better in this regard, because it
visits the target node of the redirect instead of blindly pulling from
the node map.

Fixes #7551
2022-06-05 15:47:52 +01:00
sciwhiz12 8a1c1d7e53 [1.18] Fix tags command suggestion to include dynamic registries (#8638)
Manually entering the names of dynamic registries work as expected. The
suggestions for registries didn't include dynamic registries because
they queried the static registries rather than querying all registries
from the server's `RegistryAccess` (which includes both static
registries and dynamic registries).
2022-06-05 15:45:14 +01:00
cpwandCurle 1353c1d9fa Low code language provider (#8633)
Signed-off-by: cpw <[email protected]>

Co-authored-by: Curle <[email protected]>
2022-06-05 15:44:46 +01:00
SizableShrimp 70a8663605 [1.18.x] Add checkJarCompatibility task (#8644)
Signed-off-by: SizableShrimp <[email protected]>
2022-06-05 15:40:26 +01:00
ChampionAsh5357andCurle f179eed971 [1.18.x] Fix Banner Patterns not being extensible (#8530)
Co-authored-by: Curle <[email protected]>
2022-06-05 15:39:29 +01:00
Flemmli97 d4c8708229 1.18.x Port BiomeDictionary over to biome tags (#8564) 2022-06-05 15:35:06 +01:00
Dennis C 4008057803 [1.18.x] Enable the Forge light pipeline by default (#8629) 2022-06-05 15:28:46 +01:00
997d8e0aa2 [1.18.x] Allow Modders to add custom data-pack registries through RegistryBuilder (#8630)
Co-authored-by: Silverminer007 <[email protected]>
Co-authored-by: Curle <[email protected]>
2022-06-05 15:27:30 +01:00
Take Weiland 15bd5f0d30 [1.18] Allow custom rarities to directly modify the style (#8648) 2022-06-05 15:24:54 +01:00
Take Weiland ccaa20b006 [1.18] Fix reload listener registration in ModelLoaderRegistry (#8650) 2022-06-05 15:24:29 +01:00
Dennis C c48698f680 Fix wrong position in Level#hasChunk() call in IForgeBlockGetter (#8654) 2022-06-05 15:23:49 +01:00
Take Weiland 4285c931e7 Add ItemStack context to Enchantment#getDamageBonus (#8635) 2022-06-01 09:19:18 -07:00
Brennan Ward 03c43cf3da Fix attack cooldown bar showing when out of reach of target. Closes #8639 (#8640) 2022-05-31 11:07:22 -07:00
Take Weiland 8d14797416 Improve TagsUpdatedEvent by adding update case information. (#8636) 2022-05-31 11:03:55 -07:00
sciwhiz12 e4837f65a3 Deprecate patched-in Style methods for removal, vanilla variants exist. (#8615) 2022-05-31 10:38:25 -07:00
sciwhiz12 ee46af66e5 Add pack-type-specific format versions in pack metadata. Fixes #8563 (#8612) 2022-05-31 10:36:44 -07:00
SizableShrimp 2bec354317 Catch and aggregate exceptions during NewRegistryEvent (#8601) 2022-05-31 10:34:06 -07:00
DaemonUmbra 727c7780b3 Added removeErroringEntities config option (#8627)
Counterpart to removeErroringBlockEntities
2022-05-23 20:03:03 +01:00
Take Weiland 5410f9b532 Add missing patches for not lowering mipmap level for small textures (#8622) 2022-05-23 20:02:51 +01:00
gigaherz 4c09d8d774 Make TransformType enum extensible, includes the possibility to specify a fallback type for cases where models don't specify the transform explicitly. (#8566)
Makes it possible to add new perspectives.
2022-05-22 17:55:48 +01:00
LexManos aded6fb847 Fix CheckSAS to actually validate that the SAS line will do something.
This removes all method level lines as they are not necessary now.
2022-05-21 13:16:58 -07:00
Brennan Ward c2ad951336 Add Attack Range attribute and Update Reach Distance (#8478) 2022-05-21 16:44:36 +01:00
Alex Thomson 7fb5dfa3e6 Implement PlayerNegotiationEvent (#8599) 2022-05-14 17:27:54 +01:00
sciwhiz12 0f8c2fa454 Fire EntityTravelToDimensionEvent in all expected cases (#8614)
Fixes #8520
2022-05-08 06:12:21 +01:00
sciwhiz12 dfc2aa9c6a Fix isEdible crash (#8613)
Fixes #8602
2022-05-08 04:41:30 +01:00
LexManos 8865092bb2 Fix typo in Snow Golem patch causing them to destroy blocks they shouldn't. Closes #8611 2022-05-07 12:24:09 -07:00
Sara Freimer abc3353375 Re-introduce an attribute for entity step height in a non-breaking way (#8607) 2022-05-05 19:11:59 +01:00
ChampionAsh5357 e7a8da4d0b Revert step height attribute (#8604) 2022-05-05 14:07:07 +01:00
SizableShrimp f938051373 [1.18.x] Fix ContextNbtProvider serializing the wrong name (#8575) 2022-05-03 20:27:37 +01:00
Dennis C 9db0d72232 Let BaseRailBlock decide if the calculated RailShape for placement or connection to neighbors is valid (#8562) 2022-05-03 19:58:53 +01:00
cech12 9e6e789b73 Fix shears not playing a breakage sound (#8600) 2022-05-03 19:17:05 +01:00
Leon Linhart bc0d7179cc Update IExtensionPoint javadoc to be correct (#8568) 2022-05-03 18:38:28 +01:00
Meldexun aafa34439a Fix IDs of Multi-part entities being desynced across client and server. (#8576) 2022-05-03 18:29:21 +01:00
Brennan Ward 168a3bc6fc Expose ICondition$IContext to modded reload listeners. (#8596) 2022-05-03 18:25:42 +01:00
Silver_David 2fb8b2eb18 Fix undead causing chunkloading leading to extensive lag (#8583) 2022-05-03 18:24:13 +01:00
DaemonUmbra da82705e86 Accomodate Bukkit-like Servers not sending Command Args (#8582)
Note to modded server devs:
Please, please, please implement full compatibility with [Brigadier](https://github.com/Mojang/Brigadier) instead of hacking around it.
2022-05-03 18:21:53 +01:00
Phylogeny a237a39d52 Restore translation key comments to ForgeConfigSpec (#8584) 2022-05-03 18:20:21 +01:00
Meldexun c042dc5542 Fix entity parts being ignored when collecting entities in an AABB (#8588)
Previously entity parts are only taken into consideration when the parent entity is in a chunk that intersects with the AABB
2022-05-03 18:17:26 +01:00
KnightMiner 1493d06b5a Fix EntityInteraction event running twice on client side (#8598) 2022-05-03 18:08:47 +01:00
Vincent Lee 9a11201a9e Introduce an attribute for step height additions (#8389) 2022-05-03 18:01:45 +01:00
KnightMiner c34094ea50 Fix shears passing on a client when interacting with a shearable entity (#8597)
Caused the offhand item to also interact with the entity, possibly sending a packet to the server causing both hands to interact
2022-05-02 10:46:58 -07:00
Alex Thomson c531b2b5c7 Allow Exception passed through CommandEvent to propagate (#8590) 2022-05-02 10:43:49 -07:00
1905 changed files with 41875 additions and 29715 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
+27
View File
@@ -0,0 +1,27 @@
name: Support Requests
on:
issues:
types:
- labeled
- unlabeled
- reopened
permissions:
issues: write
jobs:
support:
runs-on: ubuntu-latest
steps:
- uses: dessant/support-requests@v2
with:
support-label: Forum
issue-comment: >
:wave: We use the issue tracker exclusively for final bug reports and feature requests.
However, this issue appears to be better suited for the
[Forge Support Forums](https://forums.minecraftforge.net/) or
[Forge Discord](https://discord.gg/UvedJ9m). Please create a new topic on the support forum
with this issue or ask in the `#tech-support` channel in the Discord server, and the
conversation can continue there.
lock-issue: true
+1
View File
@@ -33,6 +33,7 @@
/projects/**/*.launch
/repo/
src/generated/resources/.cache/
src/generated_test/resources/.cache/
#Patch rejects
/patches-/
+202 -43
View File
@@ -1,14 +1,3 @@
buildscript {
repositories {
mavenLocal()
maven { url = 'https://maven.minecraftforge.net/' }
mavenCentral()
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:[5.1.26,)'
}
}
import groovy.json.JsonBuilder
import java.nio.file.Files
@@ -17,6 +6,7 @@ import java.util.LinkedHashMap
import net.minecraftforge.forge.tasks.*
import static net.minecraftforge.forge.tasks.Util.*
import net.minecraftforge.gradle.common.tasks.ApplyBinPatches
import net.minecraftforge.gradle.common.tasks.CheckJarCompatibility
import net.minecraftforge.gradle.common.tasks.DownloadMavenArtifact
import net.minecraftforge.gradle.common.tasks.ExtractInheritance
import net.minecraftforge.gradle.common.tasks.SignJar
@@ -30,6 +20,8 @@ plugins {
id 'net.minecraftforge.gradleutils' version '2.+'
id 'eclipse'
id 'de.undercouch.download' version '4.1.2'
id 'net.minecraftforge.gradle.patcher' version '[5.1.50,5.2.0)' apply false
id 'net.minecraftforge.gradle.mcp' version '[5.1.50,5.2.0)' apply false
}
Util.init() //Init all our extension methods!
@@ -44,31 +36,48 @@ ext {
]
}
MAPPING_CHANNEL = 'official'
MAPPING_VERSION = '1.18.2'
MC_VERSION = '1.18.2'
MCP_VERSION = '20220404.173914'
SPI_VERSION = '4.0.10'
MAPPING_VERSION = '1.19.2'
MC_VERSION = '1.19.2'
MC_NEXT_VERSION = '1.20'
MCP_VERSION = '20220805.130853'
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.4'
BOOTSTRAPLAUNCHER_VERSION = '1.1.2'
ASM_VERSION = '9.3'
INSTALLER_VERSION = '2.1.+'
MIXIN_VERSION = '0.8.5'
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'
// These versions should be kept in sync with the version manifest JSON
// To use a version newer than the version manifest JSON, the dependency must be added to the installer configuration
MOJANG_LOGGING_VERSION = '1.0.0'
LOG4J_VERSION = '2.17.0'
GUAVA_VERSION = '31.0.1-jre'
GSON_VERSION = '2.8.9'
APACHE_COMMONS_LANG3_VERSION = '3.12.0'
JOPT_SIMPLE_VERSION = '5.0.4'
COMMONS_IO_VERSION = '2.11.0'
JARJAR_VERSION = '0.3.16'
GIT_INFO = gradleutils.gitInfo
VERSION = gradleutils.getFilteredMCTagOffsetBranchVersion(true, '[0-9]', MC_VERSION)
// 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'
INSTALLER_TOOLS = 'net.minecraftforge:installertools:1.2.10'
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"
MIN_TAG_FOR_CHANGELOG = "41.0"
}
println('Version: ' + VERSION +
@@ -87,7 +96,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
@@ -136,6 +145,10 @@ subprojects {
name = 'forge'
url = 'https://maven.minecraftforge.net/'
}
maven {
name = 'minecraft'
url = 'https://libraries.minecraft.net/'
}
}
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
@@ -249,6 +262,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}"
@@ -257,21 +271,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:JarJarSelector:${JARJAR_VERSION}"
installer "net.minecraftforge:JarJarMetadata:${JARJAR_VERSION}"
/*
installer 'org.lwjgl:lwjgl:3.2.2'
@@ -364,7 +380,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'
}
@@ -418,6 +435,8 @@ def sharedFmlonlyForge = { Project prj ->
run.args '--fml.forgeGroup', prj.group
run.args '--fml.mcpVersion', MCP_VERSION
run.jvmArgs '-Djava.net.preferIPv6Addresses=system'
if (run.name.contains('client')) {
run.client true
} else {
@@ -425,9 +444,9 @@ 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,mclanguage,${prj.name}-"
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}'
@@ -438,6 +457,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'
}
@@ -446,7 +466,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}"
}
@@ -486,7 +507,7 @@ def sharedFmlonlyForge = { Project prj ->
IGNORE_LIST: Util.getArtifacts(prj, prj.configurations.moduleonly, false).values().collect{it.downloads.artifact.path.rsplit('/', 1)[1]}.join(','),
MODULES: 'ALL-MODULE-PATH'
]
prj.task([dependsOn: rootProject.downloadServerRaw], 'makeClasspathFiles') {
doLast {
def CLASS_PATH = Util.getArtifacts(prj, prj.configurations.installer, false).values().collect{"libraries/${it.downloads.artifact.path}"} +
@@ -539,7 +560,7 @@ project(':fmlonly') {
ext {
MCP_ARTIFACT = project(':mcp').mcp.config.get()
PACKED_DEPS = [':fmlcore:jar', ':fmlloader:jar', ':javafmllanguage:jar', ':mclanguage:jar']
PACKED_DEPS = [':fmlcore:jar', ':fmlloader:jar', ':javafmllanguage:jar', ':lowcodelanguage:jar', ':mclanguage:jar']
MAVEN_PATH = "${group.replace('.', '/')}/${project.name}/${VERSION}"
}
@@ -568,6 +589,7 @@ project(':fmlonly') {
implementation project(':fmlcore')
implementation project(':fmlloader')
implementation project(':javafmllanguage')
implementation project(':lowcodelanguage')
implementation project(':mclanguage')
runtimeOnly "cpw.mods:bootstraplauncher:${BOOTSTRAPLAUNCHER_VERSION}"
}
@@ -627,6 +649,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'
]
@@ -845,7 +868,7 @@ project(':fmlonly') {
}
// Since we need the modules in the bootstrap, we need to make sure they are compiled before we do each run
afterEvaluate { prepareRuns.dependsOn(':fmlcore:jar', ':fmlloader:jar', ':javafmllanguage:jar', ':mclanguage:jar') }
afterEvaluate { prepareRuns.dependsOn(PACKED_DEPS) }
}
project(':forge') {
@@ -894,7 +917,7 @@ project(':forge') {
// Essentially, the same as the old, except dropping the first number, and the builds are no longer unique.
MCP_ARTIFACT = project(':mcp').mcp.config.get()
VERSION_JSON = project(':mcp').file('build/mcp/downloadJson/version.json')
PACKED_DEPS = [':fmlcore:jar', ':fmlloader:jar', ':javafmllanguage:jar', ':mclanguage:jar']
PACKED_DEPS = [':fmlcore:jar', ':fmlloader:jar', ':javafmllanguage:jar', ':lowcodelanguage:jar', ':mclanguage:jar']
MAVEN_PATH = "${group.replace('.', '/')}/${project.name}/${VERSION}"
}
@@ -915,6 +938,7 @@ project(':forge') {
implementation project(':fmlcore')
implementation project(':fmlloader')
implementation project(':javafmllanguage')
implementation project(':lowcodelanguage')
implementation project(':mclanguage')
runtimeOnly "cpw.mods:bootstraplauncher:${BOOTSTRAPLAUNCHER_VERSION}"
}
@@ -1043,6 +1067,10 @@ project(':forge') {
'--mod', 'new_model_loader_test',
'--mod', 'remove_tag_datagen_test',
'--mod', 'tag_based_tool_types',
'--mod', 'custom_transformtype_test',
'--mod', 'data_pack_registries_test',
'--mod', 'biome_modifiers_test',
'--mod', 'structure_modifiers_test',
'--output', rootProject.file('src/generated_test/resources/'),
'--existing', sourceSets.main.resources.srcDirs[0],
'--existing', sourceSets.test.resources.srcDirs[0]
@@ -1116,6 +1144,7 @@ project(':forge') {
task extractInheritance(type: ExtractInheritance, dependsOn: [genJoinedBinPatches, downloadLibraries]) {
tool = INSTALLER_TOOLS + ':fatjar'
args.add '--annotations'
input = genJoinedBinPatches.cleanJar
libraries.addAll downloadLibraries.librariesOutput.map { rf -> Files.readAllLines(rf.asFile.toPath()).stream().map(File::new).collect(java.util.stream.Collectors.toList()) }
}
@@ -1173,6 +1202,57 @@ project(':forge') {
lineEnding = '\n'
}
def baseForgeVersion = System.getProperty("forge.compatibility_check_version")
if (baseForgeVersion == null)
baseForgeVersion = "${MC_VERSION}-${GIT_INFO.tag}.0"
if (baseForgeVersion == "null")
baseForgeVersion = null // Allow disabling from system property if not wanted
def baseForgeUniversal = null
if (baseForgeVersion != null)
baseForgeUniversal = project.dependencies.create("net.minecraftforge:forge:${baseForgeVersion}:universal")
def jarCompatibilityTaskSetup = { task ->
task.group = 'jar compatibility'
task.onlyIf {
baseForgeVersion != null
}
task.inputs.property 'baseForgeVersion', baseForgeVersion
}
task downloadBaseCompatibilityBinPatches(type: DownloadBaseCompatibilityBinPatches) {
inputVersion = baseForgeVersion
}
task applyBaseCompatibilityJarBinPatches(type: ApplyBinPatches) {
jarCompatibilityTaskSetup(it)
clean = createJoinedSRG.output
patch = downloadBaseCompatibilityBinPatches.output
output = project.layout.buildDirectory.dir(name).map { it.file('output.jar') }
}
task checkJarCompatibility(type: CheckJarCompatibility) {
jarCompatibilityTaskSetup(it)
if (baseForgeVersion != null) {
def fmlLibs = project.configurations.detachedConfiguration(PACKED_DEPS.collect {
def artifactId = it.split(':')[1]
return project.dependencies.create("net.minecraftforge:${artifactId}:${baseForgeVersion}")
}.toArray(Dependency[]::new))
baseJar = project.tasks.applyBaseCompatibilityJarBinPatches.output
baseLibraries.from(project.configurations.detachedConfiguration(baseForgeUniversal))
baseLibraries.from(project.tasks.createJoinedSRG.output)
baseLibraries.from(fmlLibs)
inputJar = project.tasks.reobfJar.output
concreteLibraries.from(PACKED_DEPS.collect { rootProject.tasks.getByPath(it).archiveFile })
commonLibraries.from(project.configurations.minecraftImplementation)
commonLibraries.from(project.configurations.installer)
commonLibraries.from(project.configurations.moduleonly)
}
}
task launcherJson(type: LauncherJson) {
packedDependencies = [':fmlloader:jar']
doFirst {
@@ -1192,13 +1272,15 @@ project(':forge') {
'--fml.mcVersion', MC_VERSION,
'--fml.forgeGroup', project.group,
'--fml.mcpVersion', MCP_VERSION],
jvm: ["-DignoreList=${forge_client.properties.ignoreList},\${version_name}.jar",
jvm: ['-Djava.net.preferIPv6Addresses=system',
"-DignoreList=${forge_client.properties.ignoreList},\${version_name}.jar",
"-DmergeModules=${forge_client.properties.mergeModules}",
'-DlibraryDirectory=${library_directory}',
'-p', Util.getArtifacts(project, configurations.moduleonly, false).values().collect{'${library_directory}/' + it.downloads.artifact.path}.join('${classpath_separator}'),
'--add-modules', 'ALL-MODULE-PATH',
// Additions to these JVM module args should be mirrored to server_files/args.txt and other similar blocks in the buildscript
'--add-opens', 'java.base/java.util.jar=cpw.mods.securejarhandler',
'--add-opens', 'java.base/java.lang.invoke=cpw.mods.securejarhandler',
'--add-exports', 'java.base/sun.security.util=cpw.mods.securejarhandler',
'--add-exports', 'jdk.naming.dns/com.sun.jndi.dns=java.naming'
]
@@ -1482,7 +1564,7 @@ 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'
@@ -1523,6 +1605,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) {
@@ -1536,7 +1695,7 @@ project(':forge') {
}
}
// Since we need the modules in the bootstrap, we need to make sure they are compiled before we do each run
afterEvaluate { prepareRuns.dependsOn(':fmlcore:jar', ':fmlloader:jar', ':javafmllanguage:jar', ':mclanguage:jar') }
afterEvaluate { prepareRuns.dependsOn(PACKED_DEPS) }
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
+3 -2
View File
@@ -2,8 +2,9 @@ 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 'org.ow2.asm:asm:9.3'
implementation 'org.ow2.asm:asm-tree:9.3'
implementation 'net.minecraftforge:srgutils:0.4.+'
}
@@ -28,36 +28,80 @@ abstract class CheckSAS extends DefaultTask {
}
def comment = idx == -1 ? null : line.substring(idx)
if (idx != -1) line = line.substring(0, idx - 1)
def (cls, desc) = (line.trim() + ' ').split(' ', -1)
def (cls, name, desc) = (line.trim().replace('(', ' (') + ' ').split(' ', -1)
cls = cls.replaceAll('\\.', '/')
desc = desc.replace('(', ' (')
if (json[cls] == null || (!desc.isEmpty() && (json[cls]['methods'] == null || json[cls]['methods'][desc] == null))) {
if (json[cls] == null) {
println('Invalid: ' + line)
return
}
//Class SAS
if (desc.isEmpty()) {
lines.add(cls + (comment == null ? '' : ' ' + comment))
if (json[cls]['methods'] != null)
(json[cls]['methods'] as TreeMap).each {
findChildMethods(json, cls, it.key).each { lines.add('\t' + it) }
} else if (name.isEmpty()) { //Class SAS
def toAdd = []
def clsSided = isSided(json[cls])
def sided = clsSided
/* TODO: MergeTool doesn't do fields
if (json[cls]['fields'] != null) {
for (entry in json[cls]['fields']) {
if (isSided(entry.value)) {
sided = true
toAdd.add('\t' + cls + ' ' + entry.key)
}
}
return
}
//Method SAS
lines.add(cls + ' ' + desc.replace(' ', '') + (comment == null ? '' : ' ' + comment))
findChildMethods(json, cls, desc).each { lines.add('\t' + it) }
}
*/
if (json[cls]['methods'] != null) {
for (entry in json[cls]['methods']) {
if (isSided(entry.value)) {
sided = true
toAdd.add('\t' + cls + ' ' + entry.key.replaceAll(' ', ''))
findChildMethods(json, cls, entry.key).each { lines.add('\t' + it) }
findChildMethods(json, cls, entry.key).each { println(line + ' -- ' + it) }
} else if (clsSided) {
findChildMethods(json, cls, entry.key).each { lines.add('\t' + it) }
findChildMethods(json, cls, entry.key).each { println(line + ' -- ' + it) }
}
}
}
if (sided) {
lines.add(cls + (comment == null ? '' : ' ' + comment))
lines.addAll(toAdd.sort())
} else
println('Invalid: ' + line)
} else if (desc.isEmpty()) { // Fields
/* TODO: MergeTool doesn't do fields
if (json[cls]['fields'] != null && isSided(json[cls]['fields'][name]))
lines.add(cls + ' ' + name + (comment == null ? '' : ' ' + comment))
else
*/
println('Invalid: ' + line)
} else { // Methods
if (json[cls]['methods'] == null || !isSided(json[cls]['methods'][name + ' ' + desc]))
println('Invalid: ' + line)
else {
lines.add(cls + ' ' + name + desc + (comment == null ? '' : ' ' + comment))
findChildMethods(json, cls, name + ' ' + desc).each { println(line + ' -- ' + it) }
findChildMethods(json, cls, name + ' ' + desc).each { lines.add('\t' + it) }
}
}
}
f.text = lines.join('\n')
}
}
protected static isSided(json) {
if (json == null || json['annotations'] == null)
return false
for (ann in json['annotations']) {
if ('Lnet/minecraftforge/api/distmarker/OnlyIn;'.equals(ann.desc))
return true
}
return false
}
protected static findChildMethods(json, cls, desc)
{
return json.values().findAll{ it.methods != null && it.methods[desc] != null && it.methods[desc].override == cls}
return json.values().findAll{ it.methods != null && it.methods[desc] != null && it.methods[desc].override == cls && isSided(it.methods[desc]) }
.collect { it.name + ' ' + desc.replace(' ', '') } as TreeSet
}
}
@@ -0,0 +1,39 @@
package net.minecraftforge.forge.tasks
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.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.nio.file.Files
abstract class DownloadBaseCompatibilityBinPatches extends DefaultTask {
DownloadBaseCompatibilityBinPatches() {
group = 'jar compatibility'
onlyIf {
inputVersion.getOrNull() != null
}
output.convention(project.layout.buildDirectory.dir(name).map { it.file('joined.lzma') })
}
@TaskAction
void run() {
def dep = project.dependencies.create("net.minecraftforge:forge:${inputVersion.get()}:userdev")
def baseForgeUserdev = project.configurations.detachedConfiguration(dep).resolve().iterator().next()
def joinedLzma = project.zipTree(baseForgeUserdev).matching { it.include('joined.lzma') }.singleFile
Files.copy(joinedLzma.toPath(), output.get().asFile.toPath())
}
@Input
@Optional
abstract Property<String> getInputVersion()
@OutputFile
abstract RegularFileProperty getOutput()
}
+3 -3
View File
@@ -1,11 +1,11 @@
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)
+13 -12
View File
@@ -2,16 +2,16 @@
MinecraftForge
=============
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.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=
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.19-&query=$.promos["1.19.2-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)](https://files.minecraftforge.net)
[![Latest Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Latest&prefix=1.19.2-&query=$.promos["1.19.2-latest"]&color=blue&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYnVnIGZhLXctMTYiIGFyaWEtaGlkZGVuPSJ0cnVlIiBkYXRhLWljb249ImJ1ZyIgZGF0YS1wcmVmaXg9ImZhcyIgZm9jdXNhYmxlPSJmYWxzZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUxMS45ODggMjg4LjljLS40NzggMTcuNDMtMTUuMjE3IDMxLjEtMzIuNjUzIDMxLjFINDI0djE2YzAgMjEuODY0LTQuODgyIDQyLjU4NC0xMy42IDYxLjE0NWw2MC4yMjggNjAuMjI4YzEyLjQ5NiAxMi40OTcgMTIuNDk2IDMyLjc1OCAwIDQ1LjI1NS0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDBsLTU0LjczNi01NC43MzZDMzQ1Ljg4NiA0NjcuOTY1IDMxNC4zNTEgNDgwIDI4MCA0ODBWMjM2YzAtNi42MjctNS4zNzMtMTItMTItMTJoLTI0Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0NGMtMzQuMzUxIDAtNjUuODg2LTEyLjAzNS05MC42MzYtMzIuMTA4bC01NC43MzYgNTQuNzM2Yy0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDAtMTIuNDk2LTEyLjQ5Ny0xMi40OTYtMzIuNzU4IDAtNDUuMjU1bDYwLjIyOC02MC4yMjhDOTIuODgyIDM3OC41ODQgODggMzU3Ljg2NCA4OCAzMzZ2LTE2SDMyLjY2NkMxNS4yMyAzMjAgLjQ5MSAzMDYuMzMuMDEzIDI4OC45LS40ODQgMjcwLjgxNiAxNC4wMjggMjU2IDMyIDI1Nmg1NnYtNTguNzQ1bC00Ni42MjgtNDYuNjI4Yy0xMi40OTYtMTIuNDk3LTEyLjQ5Ni0zMi43NTggMC00NS4yNTUgMTIuNDk4LTEyLjQ5NyAzMi43NTgtMTIuNDk3IDQ1LjI1NiAwTDE0MS4yNTUgMTYwaDIyOS40ODlsNTQuNjI3LTU0LjYyN2MxMi40OTgtMTIuNDk3IDMyLjc1OC0xMi40OTcgNDUuMjU2IDAgMTIuNDk2IDEyLjQ5NyAxMi40OTYgMzIuNzU4IDAgNDUuMjU1TDQyNCAxOTcuMjU1VjI1Nmg1NmMxNy45NzIgMCAzMi40ODQgMTQuODE2IDMxLjk4OCAzMi45ek0yNTcgMGMtNjEuODU2IDAtMTEyIDUwLjE0NC0xMTIgMTEyaDIyNEMzNjkgNTAuMTQ0IDMxOC44NTYgMCAyNTcgMHoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=
)](https://files.minecraftforge.net) [![Discord](https://img.shields.io/discord/313125603924639766.svg?color=%237289da&label=Discord&logo=discord&logoColor=%237289da)](https://discord.gg/UvedJ9m) [![Support](https://img.shields.io/badge/Patreon-Support-orange.svg?logo=Patreon)](https://www.patreon.com/LexManos)
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.19.x | Active |
| 1.18.x | LTS |
* [Download]
* [Forum]
@@ -29,7 +29,7 @@ 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/).
For support and questions, visit [the Support Forum](https://www.minecraftforge.net/forum/forum/18-support-bug-reports/) or [the Forge Discord server](https://discord.gg/forge).
[Here is a short video from Rorax showing how to install and setup Forge.](https://www.youtube.com/watch?v=lB3ArN_-3Oc)
@@ -41,7 +41,7 @@ For support and questions, visit [the Support Forum](https://www.minecraftforge.
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/).
### Pull requests
@@ -51,13 +51,14 @@ If you wish to actually inspect Forge, submit PRs or otherwise work
Please read the contributing guidelines found [here](CONTRIBUTING.md) 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](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
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](https://www.patreon.com/LexManos).*
[Download]: https://files.minecraftforge.net/
[Forum]: https://www.minecraftforge.net/forum/
+3 -2
View File
@@ -76,7 +76,7 @@ If you're certain you've found an issue that is directly caused by Forge, you ha
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.*
2. **Forge Discord Server:** You can find a lot of help on the official [Discord Server](https://discord.gg/forge), especially smaller issues that you quickly need help with. *Be aware that the channel is more suited towards development questions, so while you may get support there as a player, you should almost always prefer the forums.*
3. **Forge Issue Tracker:** If you're absolutely certain that you've found an issue in the Forge codebase (e.g. you're a modder yourself and have analyzed the stacktrace and relevant code), don't refrain from reporting it on this repository's [issue tracker](https://github.com/MinecraftForge/MinecraftForge/issues). *Please do not use the issues as a means of getting coding help. For developers, the IRC and the [official documentation](https://mcforge.readthedocs.io) are the best sources of information.*
### Mod Support
@@ -84,7 +84,8 @@ If you're certain you've found an issue that is directly caused by Forge, you ha
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
+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) {
@@ -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.impl.impl.FMLNetworkConstants#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, ()->Pair.of(
* ()->FMLNetworkConstants.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, ()->Pair.of(
* ()->"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);
}
}
@@ -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,18 +8,22 @@ 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.ModFile;
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.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -28,6 +32,7 @@ import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -96,7 +101,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);
@@ -133,6 +138,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);
@@ -145,6 +151,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)
@@ -196,7 +218,7 @@ public class ModLoader
state.buildTransition(syncExecutor, parallelExecutor, preSyncTask, postSyncTask).ifPresent(t->waitForTransition(state, syncExecutor, ticker, t));
}
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);
}
@@ -222,7 +244,7 @@ public class ModLoader
}
}
private List<ModContainer> buildMods(final ModFile modFile)
private List<ModContainer> buildMods(final IModFile modFile)
{
final Map<String, IModInfo> modInfoMap = modFile.getModFileInfo().getMods().stream().collect(Collectors.toMap(IModInfo::getModId, Function.identity()));
@@ -244,7 +266,7 @@ public class ModLoader
return containers.stream().filter(mc -> mc.modLoadingStage != ModLoadingStage.ERROR).collect(Collectors.toList());
}
private ModContainer buildModContainerFromTOML(final ModFile modFile, final Map<String, IModInfo> modInfoMap, final Map.Entry<String, ? extends IModLanguageProvider.IModLanguageLoader> idToProviderEntry) {
private ModContainer buildModContainerFromTOML(final IModFile modFile, final Map<String, IModInfo> modInfoMap, final Map.Entry<String, ? extends IModLanguageProvider.IModLanguageLoader> idToProviderEntry) {
try {
final String modId = idToProviderEntry.getKey();
final IModLanguageProvider.IModLanguageLoader languageLoader = idToProviderEntry.getValue();
@@ -273,7 +295,7 @@ public class ModLoader
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) {
@@ -281,7 +303,18 @@ 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> 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,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());
}
}
@@ -117,14 +117,22 @@ public class VersionChecker
/**
* Returns the response body as a String for the given URL while following redirects
*/
private String openUrlString(URL url) throws IOException, URISyntaxException, InterruptedException {
private String openUrlString(URL url, IModInfo mod) throws IOException, URISyntaxException, InterruptedException {
URL currentUrl = url;
final StringBuilder sb = new StringBuilder();
sb.append("Java-http-client/").append(System.getProperty("java.version")).append(' ');
sb.append("MinecraftForge/").append(FMLLoader.versionInfo().mcAndForgeVersion()).append(' ');
sb.append(mod.getModId()).append('/').append(mod.getVersion());
final String userAgent = sb.toString();
for (int redirects = 0; redirects < MAX_HTTP_REDIRECTS; redirects++)
{
var request = HttpRequest.newBuilder()
.uri(currentUrl.toURI())
.timeout(Duration.ofSeconds(HTTP_TIMEOUT_SECS))
.setHeader("Accept-Encoding", "gzip")
.setHeader("User-Agent", userAgent)
.GET()
.build();
@@ -166,7 +174,7 @@ public class VersionChecker
URL url = mod.getUpdateURL().get();
LOGGER.info("[{}] Starting version check at {}", mod.getModId(), url.toString());
String data = openUrlString(url);
String data = openUrlString(url, mod);
LOGGER.debug("[{}] Received version check data:\n{}", mod.getModId(), data);
@@ -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;
+26 -19
View File
@@ -11,29 +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:4.0.+')
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:forgespi:${SPI_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);
}
}
@@ -0,0 +1,38 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
public class ClasspathLocatorUtils {
private static final Logger LOGGER = LogUtils.getLogger();
public static 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);
}
}
}
@@ -0,0 +1,61 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading;
import cpw.mods.modlauncher.api.NamedPath;
import cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService;
import org.apache.logging.log4j.LogManager;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
public class ClasspathTransformerDiscoverer implements ITransformerDiscoveryService {
private final List<Path> legacyClasspath = Arrays.stream(System.getProperty("legacyClassPath", "").split(File.pathSeparator)).map(Path::of).toList();
@Override
public List<NamedPath> candidates(Path gameDirectory) {
return Collections.emptyList();
}
@Override
public List<NamedPath> candidates(final Path gameDirectory, final String launchTarget) {
if (launchTarget != null && launchTarget.contains("dev")) {
this.scan(gameDirectory);
}
return List.copyOf(found);
}
private final static List<NamedPath> found = new ArrayList<>();
public static List<Path> allExcluded() {
return found.stream().map(np->np.paths()[0]).toList();
}
private void scan(final Path gameDirectory) {
try {
locateTransformers("META-INF/services/cpw.mods.modlauncher.api.ITransformationService");
locateTransformers("META-INF/services/net.minecraftforge.forgespi.locating.IModLocator");
} catch (IOException e) {
LogManager.getLogger().error("Error during discovery of transform services from the classpath", e);
}
}
private void locateTransformers(String resource) throws IOException {
final Enumeration<URL> resources = ClassLoader.getSystemClassLoader().getResources(resource);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
Path path = ClasspathLocatorUtils.findJarPathFor(resource, url.toString(), url);
if (legacyClasspath.stream().anyMatch(path::equals) || !Files.exists(path) || Files.isDirectory(path))
continue;
found.add(new NamedPath(path.toUri().toString(), path));
}
}
}
@@ -47,7 +47,7 @@ public class EarlyLoadingException extends RuntimeException {
return errorMessages;
}
EarlyLoadingException(final String message, final Throwable originalException, List<ExceptionData> errorMessages) {
public EarlyLoadingException(final String message, final Throwable originalException, List<ExceptionData> errorMessages) {
super(message, originalException);
this.errorMessages = errorMessages;
}
@@ -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,11 +61,14 @@ 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);
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()), "default config directory");
}
@@ -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,9 +5,9 @@
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.nio.file.Path;
import java.nio.file.Paths;
@@ -22,7 +22,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;
@@ -61,7 +61,10 @@ public enum FMLPaths
{
FileUtils.getOrCreateDirectory(path.absolutePath, path.name());
}
LOGGER.debug(CORE,"Path {} is {}", ()-> path, ()-> path.absolutePath);
if (LOGGER.isDebugEnabled(CORE))
{
LOGGER.debug(CORE, "Path {} is {}", path, path.absolutePath);
}
}
}
@@ -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,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.io.IOException;
import java.nio.file.FileAlreadyExistsException;
@@ -17,7 +17,7 @@ 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();
public static Path getOrCreateDirectory(Path dirPath, String dirLabel) {
if (!Files.isDirectory(dirPath.getParent())) {
@@ -25,20 +25,20 @@ public class FileUtils
}
if (!Files.isDirectory(dirPath))
{
LOGGER.debug(CORE,"Making {} directory : {}", dirLabel, dirPath);
LOGGER.debug(CORE, "Making {} directory : {}", dirLabel, dirPath);
try {
Files.createDirectory(dirPath);
} catch (IOException e) {
if (e instanceof FileAlreadyExistsException) {
LOGGER.fatal(CORE,"Failed to create {} directory - there is a file in the way", dirLabel);
LOGGER.error(CORE, "Failed to create {} directory - there is a file in the way", dirLabel);
} else {
LOGGER.fatal(CORE,"Problem with creating {} directory (Permissions?)", dirLabel, e);
LOGGER.error(CORE, "Problem with creating {} directory (Permissions?)", dirLabel, e);
}
throw new RuntimeException("Problem creating directory", e);
}
LOGGER.debug(CORE,"Created {} directory : {}", dirLabel, dirPath);
LOGGER.debug(CORE, "Created {} directory : {}", dirLabel, dirPath);
} else {
LOGGER.debug(CORE,"Found existing {} directory : {}", dirLabel, dirPath);
LOGGER.debug(CORE, "Found existing {} directory : {}", dirLabel, dirPath);
}
return dirPath;
}
@@ -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,10 +5,11 @@
package net.minecraftforge.fml.loading;
import com.mojang.logging.LogUtils;
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;
@@ -19,6 +20,8 @@ import java.util.List;
import java.util.zip.ZipFile;
public class ModDirTransformerDiscoverer implements ITransformerDiscoveryService {
private static final Logger LOGGER = LogUtils.getLogger();
@Override
public List<NamedPath> candidates(final Path gameDirectory) {
ModDirTransformerDiscoverer.scan(gameDirectory);
@@ -40,7 +43,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);
}
}
@@ -55,7 +58,7 @@ public class ModDirTransformerDiscoverer implements ITransformerDiscoveryService
found.add(new NamedPath(zf.getName(), path));
}
} catch (IOException ioe) {
LogManager.getLogger().error("Zip Error when loading jar file {}", path, ioe);
LOGGER.error("Zip Error when loading jar file {}", path, ioe);
}
}
}
@@ -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,6 +7,7 @@ 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;
@@ -17,11 +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 net.minecraftforge.forgespi.locating.IModFile;
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;
@@ -35,15 +34,16 @@ 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;
private Map<String, ModInfo> modIdNameLookup;
private Map<String, IModInfo> modIdNameLookup;
private List<ModFile> systemMods;
private ModSorter(final List<ModFile> modFiles)
{
this.modFiles = modFiles;
this.uniqueModListBuilder = new UniqueModListBuilder(modFiles);
}
public static LoadingModList sort(List<ModFile> mods, final List<ExceptionData> errors)
@@ -78,11 +78,12 @@ public class ModSorter
// lambdas are identity based, so sorting them is impossible unless you hold reference to them
final MutableGraph<ModFileInfo> graph = GraphBuilder.directed().build();
AtomicInteger counter = new AtomicInteger();
Map<IModFileInfo, Integer> infos = modFiles.stream()
Map<ModFileInfo, Integer> infos = modFiles.stream()
.map(ModFile::getModFileInfo)
.filter(ModFileInfo.class::isInstance)
.map(ModFileInfo.class::cast)
.collect(toMap(Function.identity(), e -> counter.incrementAndGet()));
infos.keySet().forEach(i -> graph.addNode((ModFileInfo) i));
infos.keySet().forEach(graph::addNode);
modFiles.stream()
.map(ModFile::getModInfos)
.<IModInfo>mapMulti(Iterable::forEach)
@@ -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))
@@ -125,10 +125,9 @@ public class ModSorter
private void addDependency(MutableGraph<ModFileInfo> topoGraph, IModInfo.ModVersion dep)
{
final ModFileInfo self = (ModFileInfo)dep.getOwner().getOwningFile();
final ModInfo targetModInfo = modIdNameLookup.get(dep.getModId());
final IModInfo targetModInfo = modIdNameLookup.get(dep.getModId());
// soft dep that doesn't exist. Just return. No edge required.
if (targetModInfo == null) return;
final ModFileInfo target = targetModInfo.getOwningFile();
if (targetModInfo == null || !(targetModInfo.getOwningFile() instanceof final ModFileInfo target)) return;
if (self == target)
return; // in case a jar has two mods that have dependencies between
switch (dep.getOrdering()) {
@@ -139,19 +138,32 @@ public class ModSorter
private void buildUniqueList()
{
// Collect mod files by module name. This will be used for deduping purposes
final Map<String, List<IModFile>> modFilesByFirstId = modFiles.stream()
.collect(groupingBy(mf -> mf.getModFileInfo().moduleName()));
final UniqueModListBuilder.UniqueModListData uniqueModListData = uniqueModListBuilder.buildUniqueList();
this.modFiles = uniqueModListData.modFiles();
detectSystemMods(uniqueModListData.modFilesByFirstId());
modIdNameLookup = uniqueModListData.modFilesByFirstId().entrySet().stream()
.filter(e -> !e.getValue().get(0).getModInfos().isEmpty())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().get(0).getModInfos().get(0)
));
}
private void detectSystemMods(final Map<String, List<ModFile>> modFilesByFirstId)
{
// Capture system mods (ex. MC, Forge) here, so we can keep them for later
final Set<String> systemMods = new HashSet<>();
// The minecraft mod is always a system mod
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)
@@ -169,47 +181,6 @@ public class ModSorter
throw new IllegalStateException("Failed to find system mod: " + systemMod);
}
}
// Select the newest by artifact version sorting of non-unique files thus identified
this.modFiles = modFilesByFirstId.entrySet().stream()
.map(this::selectNewestModInfo)
.map(Map.Entry::getValue)
.map(ModFile.class::cast)
.collect(toList());
// Transform to the full mod id list
final Map<String, List<ModInfo>> modIds = modFiles.stream()
.map(ModFile::getModInfos)
.flatMap(Collection::stream)
.map(ModInfo.class::cast)
.collect(groupingBy(IModInfo::getModId));
// 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<ModInfo> dupedMods = modIds.values().stream()
.filter(modInfos -> modInfos.size() > 1)
.map(modInfos -> modInfos.get(0))
.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);
}
modIdNameLookup = modIds.entrySet().stream()
.collect(toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
}
private Map.Entry<String, IModFile> selectNewestModInfo(Map.Entry<String, List<IModFile>> fullList) {
List<IModFile> modInfoList = fullList.getValue();
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.<IModFile, ArtifactVersion>comparing(mf -> mf.getModInfos().get(0).getVersion()).reversed());
LOGGER.debug("Selected file {} for modid {} with version {}", modInfoList.get(0).getFileName(), fullList.getKey(), modInfoList.get(0).getModInfos().get(0).getVersion());
}
return Map.entry(fullList.getKey(), modInfoList.get(0));
}
private List<EarlyLoadingException.ExceptionData> verifyDependencyVersions()
@@ -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;
/**
@@ -0,0 +1,133 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
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.maven.artifact.versioning.ArtifactVersion;
import org.slf4j.Logger;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.*;
public class UniqueModListBuilder
{
private final static Logger LOGGER = LogUtils.getLogger();
private final List<ModFile> modFiles;
public UniqueModListBuilder(final List<ModFile> modFiles) {this.modFiles = modFiles;}
public UniqueModListData buildUniqueList()
{
List<ModFile> uniqueModList;
List<ModFile> uniqueLibListWithVersion;
// Collect mod files by module name. This will be used for deduping purposes
final Map<String, List<ModFile>> modFilesByFirstId = modFiles.stream()
.filter(mf -> mf.getModFileInfo() != null)
.collect(groupingBy(UniqueModListBuilder::getModId));
final Map<String, List<ModFile>> libFilesWithVersionByModuleName = modFiles.stream()
.filter(mf -> mf.getModFileInfo() == null)
.collect(groupingBy(UniqueModListBuilder::getModId));
// Select the newest by artifact version sorting of non-unique files thus identified
uniqueModList = modFilesByFirstId.entrySet().stream()
.map(this::selectNewestModInfo)
.toList();
// Select the newest by artifact version sorting of non-unique files thus identified
uniqueLibListWithVersion = libFilesWithVersionByModuleName.entrySet().stream()
.map(this::selectNewestModInfo)
.toList();
// Transform to the full mod id list
final Map<String, List<IModInfo>> modIds = uniqueModList.stream()
.filter(mf -> mf.getModFileInfo() != null) //Filter out non-mod files, we don't care about those for now.....
.map(ModFile::getModInfos)
.flatMap(Collection::stream)
.collect(groupingBy(IModInfo::getModId));
// Transform to the full lib id list
final Map<String, List<ModFile>> versionedLibIds = uniqueLibListWithVersion.stream()
.map(UniqueModListBuilder::getModId)
.collect(Collectors.toMap(
Function.identity(),
libFilesWithVersionByModuleName::get
));
// 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()
.filter(modInfos -> modInfos.size() > 1)
.map(modInfos -> modInfos.get(0))
.toList();
if (!dupedMods.isEmpty()) {
final List<EarlyLoadingException.ExceptionData> duplicateModErrors = dupedMods.stream()
.map(dm -> new EarlyLoadingException.ExceptionData("fml.modloading.dupedmod", dm, Objects.toString(dm)))
.toList();
throw new EarlyLoadingException("Duplicate mods found", null, duplicateModErrors);
}
final List<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);
}
// Collect unique mod files by module name. This will be used for deduping purposes
final Map<String, List<ModFile>> uniqueModFilesByFirstId = uniqueModList.stream()
.collect(groupingBy(UniqueModListBuilder::getModId));
final List<ModFile> loadedList = new ArrayList<>();
loadedList.addAll(uniqueModList);
loadedList.addAll(uniqueLibListWithVersion);
return new UniqueModListData(loadedList, uniqueModFilesByFirstId);
}
private ModFile selectNewestModInfo(Map.Entry<String, List<ModFile>> fullList) {
List<ModFile> modInfoList = fullList.getValue();
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(), this.getVersion(modInfoList.get(0)));
}
return modInfoList.get(0);
}
private ArtifactVersion getVersion(final ModFile mf)
{
if (mf.getModFileInfo() == null || mf.getModInfos() == null || mf.getModInfos().isEmpty()) {
return mf.getJarVersion();
}
return mf.getModInfos().get(0).getVersion();
}
private static String getModId(ModFile modFile) {
if (modFile.getModFileInfo() == null || modFile.getModFileInfo().getMods().isEmpty()) {
return modFile.getSecureJar().name();
}
return modFile.getModFileInfo().moduleName();
}
public record UniqueModListData(List<ModFile> modFiles, Map<String, List<ModFile>> modFilesByFirstId) {}
}
@@ -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));
@@ -0,0 +1,52 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import net.minecraftforge.forgespi.locating.IDependencyLocator;
import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
import org.slf4j.Logger;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
public abstract class AbstractJarFileDependencyLocator extends AbstractJarFileModProvider implements IDependencyLocator
{
private static final Logger LOGGER = LogUtils.getLogger();
protected Optional<InputStream> loadResourceFromModFile(final IModFile modFile, final Path path) {
try {
return Optional.of(Files.newInputStream(modFile.findResource(path.toString())));
}
catch (final FileNotFoundException e) {
LOGGER.debug("Failed to load resource {} from {}, it does not contain dependency information.", path, modFile.getFileName());
return Optional.empty();
}
catch (final Exception e) {
LOGGER.error("Failed to load resource {} from mod {}, cause {}", path, modFile.getFileName(), e);
return Optional.empty();
}
}
protected Optional<IModFile> loadModFileFrom(final IModFile file, final Path path) {
try {
final Path pathInModFile = file.findResource(path.toString());
return Optional.of(createMod(pathInModFile).file());
}
catch (Exception e) {
LOGGER.error("Failed to load mod file {} from {}", path, file.getFileName());
throw new ModFileLoadingException("Failed to load mod file "+file.getFileName());
}
}
protected String identifyMod(final IModFile modFile) {
return modFile.getFileName();
}
}
@@ -0,0 +1,23 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.moddiscovery;
import net.minecraftforge.forgespi.locating.IModLocator;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;
public abstract class AbstractJarFileModLocator extends AbstractJarFileModProvider implements IModLocator
{
@Override
public List<IModLocator.ModFileOrException> scanMods()
{
return scanCandidates().map(this::createMod).toList();
}
public abstract Stream<Path> scanCandidates();
}
@@ -5,32 +5,22 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.mojang.logging.LogUtils;
import cpw.mods.jarhandling.SecureJar;
import cpw.mods.jarhandling.JarMetadata;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.forgespi.language.IConfigurable;
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 org.slf4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class AbstractJarFileLocator extends AbstractModLocator {
private static final Logger LOGGER = LogManager.getLogger();
public abstract class AbstractJarFileModProvider extends AbstractModProvider
{
private static final Logger LOGGER = LogUtils.getLogger();
@Override
public void scanFile(final IModFile file, final Consumer<Path> pathConsumer) {
@@ -43,15 +33,4 @@ public abstract class AbstractJarFileLocator extends AbstractModLocator {
}
LOGGER.debug(LogMarkers.SCAN,"Scan finished: {}", file);
}
@Override
public List<IModFile> scanMods() {
return scanCandidates()
.map(this::createMod)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}
public abstract Stream<Path> scanCandidates();
}
@@ -1,96 +0,0 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.fml.loading.moddiscovery;
import cpw.mods.jarhandling.JarMetadata;
import cpw.mods.jarhandling.SecureJar;
import net.minecraftforge.fml.loading.LogMarkers;
import net.minecraftforge.forgespi.language.IConfigurable;
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 java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.jar.Manifest;
public abstract class AbstractModLocator implements IModLocator {
private static final Logger LOGGER = LogManager.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) {
var mjm = new ModJarMetadata();
var sj = SecureJar.from(
Manifest::new,
jar -> jar.findFile(MODS_TOML).isPresent() ? mjm : JarMetadata.from(jar, path),
(root, p) -> true,
path
);
IModFile mod;
var type = sj.getManifest().getMainAttributes().getValue(ModFile.TYPE);
if (sj.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);
} else {
return Optional.empty();
}
mjm.setModFile(mod);
return Optional.of(mod);
}
protected IModFileInfo manifestParser(final IModFile mod) {
Function<String, Optional<String>> cfg = name -> Optional.ofNullable(mod.getSecureJar().getManifest().getMainAttributes().getValue(name));
var license = cfg.apply("LICENSE").orElse("");
var dummy = new IConfigurable() {
@Override
public <T> Optional<T> getConfigElement(String... key) {
return Optional.empty();
}
@Override
public List<? extends IConfigurable> getConfigList(String... key) {
return Collections.emptyList();
}
};
return new IModFileInfo() {
@Override public List<IModInfo> getMods() { return Collections.emptyList(); }
@Override public List<LanguageSpec> requiredLanguageLoaders() { return Collections.emptyList(); }
@Override public boolean showAsResourcePack() { return false; }
@Override public Map<String, Object> getFileProperties() { return Collections.emptyMap(); }
@Override public String getLicense() { return license; }
@Override public IModFile getFile() { return mod; }
@Override public IConfigurable getConfig() { return dummy; }
// These Should never be called as it's only called from ModJarMetadata.version and we bypass that
@Override public String moduleName() { return mod.getSecureJar().name(); }
@Override public String versionString() { return null; }
@Override public List<String> usesServices() { return null; }
@Override
public String toString() {
return "IModFileInfo(" + mod.getFilePath() + ")";
}
};
}
@Override
public boolean isValid(final IModFile modFile) {
return true;
}
}
@@ -0,0 +1,123 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
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;
import net.minecraftforge.forgespi.language.IConfigurable;
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 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;
import java.util.Optional;
import java.util.function.Function;
import java.util.jar.Manifest;
public abstract class AbstractModProvider implements IModProvider
{
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 IModLocator.ModFileOrException createMod(Path... path) {
var mjm = new ModJarMetadata();
var sj = SecureJar.from(
Manifest::new,
jar -> jar.moduleDataProvider().findFile(MODS_TOML).isPresent() ? mjm : JarMetadata.from(jar, path),
(root, p) -> true,
path
);
IModFile mod;
var type = sj.moduleDataProvider().getManifest().getMainAttributes().getValue(ModFile.TYPE);
if (type == null) {
type = getDefaultJarModType();
}
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 new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found "+ Arrays.toString(path)));
}
mjm.setModFile(mod);
return new IModLocator.ModFileOrException(mod, null);
}
protected IModFileInfo manifestParser(final IModFile mod) {
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
public <T> Optional<T> getConfigElement(String... key) {
return Optional.empty();
}
@Override
public List<? extends IConfigurable> getConfigList(String... key) {
return Collections.emptyList();
}
};
return new DefaultModFileInfo(mod, license, dummy);
}
@Override
public boolean isValid(final IModFile modFile) {
return true;
}
protected String getDefaultJarModType() {
return null;
}
private record DefaultModFileInfo(IModFile mod, String license, IConfigurable configurable) implements IModFileInfo, IConfigurable {
@Override
public <T> Optional<T> getConfigElement(final String... strings)
{
return Optional.empty();
}
@Override
public List<? extends IConfigurable> getConfigList(final String... strings)
{
return null;
}
@Override public List<IModInfo> getMods() { return Collections.emptyList(); }
@Override public List<LanguageSpec> requiredLanguageLoaders() { return Collections.emptyList(); }
@Override public boolean showAsResourcePack() { return false; }
@Override public Map<String, Object> getFileProperties() { return Collections.emptyMap(); }
@Override
public String getLicense() { return license; }
@Override public IModFile getFile() { return mod; }
@Override public IConfigurable getConfig() { return configurable; }
// These Should never be called as it's only called from ModJarMetadata.version and we bypass that
@Override public String moduleName() { return mod.getSecureJar().name(); }
@Override public String versionString() { return null; }
@Override public List<String> usesServices() { return null; }
@Override
public String toString() {
return "IModFileInfo(" + mod.getFilePath() + ")";
}
}
}
@@ -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;
@@ -5,9 +5,10 @@
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;
@@ -24,8 +25,9 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
public class ClasspathLocator extends AbstractJarFileLocator {
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;
@@ -48,7 +50,7 @@ public class ClasspathLocator extends AbstractJarFileLocator {
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);
}
}
@@ -58,7 +60,7 @@ public class ClasspathLocator extends AbstractJarFileLocator {
final Enumeration<URL> resources = ClassLoader.getSystemClassLoader().getResources(resource);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
Path path = findJarPathFor(resource, resource, url);
Path path = ClasspathLocatorUtils.findJarPathFor(resource, resource, url);
if (claimed.stream().anyMatch(path::equals) || !Files.exists(path) || Files.isDirectory(path))
continue;
ret.add(path);
@@ -85,7 +87,7 @@ public class ClasspathLocator extends AbstractJarFileLocator {
//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);
}
}
@@ -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;
}
}
@@ -0,0 +1,158 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
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.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 = LogUtils.getLogger();
@Override
public String name()
{
return "JarInJar";
}
@Override
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);
if (dependenciesToLoad.isEmpty())
{
LOGGER.info("No dependencies to load found. Skipping!");
return Collections.emptyList();
}
LOGGER.info("Found {} dependencies adding them to mods collection", dependenciesToLoad.size());
return dependenciesToLoad;
}
@Override
public void initArguments(final Map<String, ?> arguments)
{
// NO-OP, for now
}
@Override
protected String getDefaultJarModType()
{
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(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";
}
@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()));
}
@NotNull
private String formatError(final ModWithVersionRange modWithVersionRange)
{
return "\u00a7e" + modWithVersionRange.modInfo().getModId() + "\u00a7r - \u00a74" + modWithVersionRange.versionRange().toString() + "\u00a74 - \u00a72" + modWithVersionRange.artifactVersion().toString() + "\u00a72";
}
@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 AbstractJarFileLocator {
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 {
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 {
.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) {
@@ -5,62 +5,171 @@
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;
import net.minecraftforge.fml.loading.EarlyLoadingException;
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 final ServiceLoader<IModLocator> locators;
private final List<IModLocator> locatorList;
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();
locators = ServiceLoader.load(moduleLayerManager.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow(), IModLocator.class);
locatorList = ServiceLoaderUtils.streamServiceLoader(()->locators, sce->LOGGER.error("Failed to load locator list", sce)).collect(Collectors.toList());
locatorList.forEach(l->l.initArguments(arguments));
LOGGER.debug(LogMarkers.CORE,"Found Mod Locators : {}", ()->locatorList.stream().map(iModLocator -> "("+iModLocator.name() + ":" + iModLocator.getClass().getPackage().getImplementationVersion()+")").collect(Collectors.joining(",")));
}
ModDiscoverer(List<IModLocator> locatorList) {
this.locatorList = locatorList;
this.locators = null;
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() {
LOGGER.debug(LogMarkers.SCAN,"Scanning for mods and other resources to load. We know {} ways to find mods", locatorList.size());
var loadedFiles = new ArrayList<>();
for (IModLocator locator : locatorList) {
LOGGER.debug(LogMarkers.SCAN,"Trying locator {}", locator);
var modFiles = locator.scanMods();
for (IModFile mf : modFiles) {
LOGGER.info(LogMarkers.SCAN, "Found mod file {} of type {} with locator {}", mf.getFileName(), mf.getType(), mf.getLocator());
StartupMessageManager.modLoaderConsumer().ifPresent(c->c.accept("Found mod file "+mf.getFileName()+" of type "+mf.getType()));
}
loadedFiles.addAll(modFiles);
}
final var modFilesMap = loadedFiles.stream()
.map(ModFile.class::cast)
.collect(Collectors.groupingBy(IModFile::getType));
LOGGER.debug(LogMarkers.SCAN,"Scanning for mods and other resources to load. We know {} ways to find mods", modLocatorList.size());
List<ModFile> loadedFiles = new ArrayList<>();
List<EarlyLoadingException.ExceptionData> discoveryErrorData = new ArrayList<>();
boolean successfullyLoadedMods = true;
List<IModFileInfo> brokenFiles = new ArrayList<>();
var validator = new ModValidator(modFilesMap);
//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 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());
}
}
//First processing run of the mod list. Any duplicates will cause resolution failure and dependency loading will be skipped.
Map<IModFile.Type, List<ModFile>> modFilesMap = Maps.newHashMap();
try {
final UniqueModListBuilder modsUniqueListBuilder = new UniqueModListBuilder(loadedFiles);
final UniqueModListBuilder.UniqueModListData uniqueModsData = modsUniqueListBuilder.buildUniqueList();
//Grab the temporary results.
//This allows loading to continue to a base state, in case dependency loading fails.
modFilesMap = uniqueModsData.modFiles().stream()
.collect(Collectors.groupingBy(IModFile::getType));
loadedFiles = uniqueModsData.modFiles();
}
catch (EarlyLoadingException exception) {
LOGGER.error(LogMarkers.SCAN, "Failed to build unique mod list after mod discovery.", exception);
discoveryErrorData.addAll(exception.getAllData());
successfullyLoadedMods = false;
}
//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 (IDependencyLocator locator : dependencyLocatorList) {
try {
LOGGER.debug(LogMarkers.SCAN,"Trying locator {}", locator);
final List<IModFile> locatedMods = ImmutableList.copyOf(loadedFiles);
var locatedFiles = locator.scanMods(locatedMods);
if (locatedFiles.stream().anyMatch(file -> !(file instanceof ModFile))) {
LOGGER.error(LogMarkers.SCAN, "A dependency locator returned a file which is not a ModFile instance!. They will be skipped!");
}
handleLocatedFiles(loadedFiles, locatedFiles);
}
catch (EarlyLoadingException exception) {
LOGGER.error(LogMarkers.SCAN, "Failed to load dependencies with locator {}", locator, exception);
discoveryErrorData.addAll(exception.getAllData());
}
}
//Second processing run of the mod list. Any duplicates will cause resolution failure and only the mods list will be loaded.
try {
final UniqueModListBuilder modsAndDependenciesUniqueListBuilder = new UniqueModListBuilder(loadedFiles);
final UniqueModListBuilder.UniqueModListData uniqueModsAndDependenciesData = modsAndDependenciesUniqueListBuilder.buildUniqueList();
//We now only need the mod files map, not the list.
modFilesMap = uniqueModsAndDependenciesData.modFiles().stream()
.collect(Collectors.groupingBy(IModFile::getType));
} catch (EarlyLoadingException exception) {
LOGGER.error(LogMarkers.SCAN, "Failed to build unique mod list after dependency discovery.", exception);
discoveryErrorData.addAll(exception.getAllData());
modFilesMap = loadedFiles.stream().collect(Collectors.groupingBy(IModFile::getType));
}
}
else {
//Failure notify the listeners.
LOGGER.error(LogMarkers.SCAN, "Mod Discovery failed. Skipping dependency discovery.");
}
//Validate the loading. With a deduplicated list, we can now successfully process the artifacts and load
//transformer plugins.
var validator = new ModValidator(modFilesMap, brokenFiles, discoveryErrorData);
validator.stage1Validation();
return validator;
}
private void handleLocatedFiles(final List<ModFile> loadedFiles, final List<IModFile> locatedFiles)
{
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.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,10 +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;
@@ -37,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();
@@ -51,9 +53,9 @@ public class ModFile implements IModFile {
private Throwable scanError;
private final SecureJar jar;
private final Type modFileType;
private final Manifest manifest;
private final IModLocator locator;
private IModFileInfo modFileInfo;
private final Manifest manifest;
private final IModProvider provider;
private IModFileInfo modFileInfo;
private ModFileScanData fileModFileScanData;
private CompletableFuture<ModFileScanData> futureScanResult;
private List<CoreModFile> coreMods;
@@ -62,15 +64,19 @@ 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.locator = locator;
public ModFile(final SecureJar jar, final IModProvider provider, final ModFileFactory.ModFileInfoParser parser) {
this(jar, provider, parser, parseType(jar));
}
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();
final Optional<String> value = Optional.ofNullable(manifest.getMainAttributes().getValue(TYPE));
modFileType = Type.valueOf(value.orElse("MOD"));
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);
}
@Override
@@ -123,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) {
@@ -188,8 +194,8 @@ public class ModFile implements IModFile {
}
@Override
public IModLocator getLocator() {
return locator;
public IModProvider getProvider() {
return provider;
}
@Override
@@ -201,4 +207,15 @@ public class ModFile implements IModFile {
public void setSecurityStatus(final SecureJar.Status status) {
this.securityStatus = status;
}
public ArtifactVersion getJarVersion()
{
return new DefaultArtifactVersion(this.jarVersion);
}
private static String parseType(final SecureJar jar) {
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,27 +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) {
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) {
@@ -40,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"));
}
@@ -50,22 +60,22 @@ public class ModValidator {
final List<ModFile> brokenFiles = new ArrayList<>();
for (Iterator<ModFile> iterator = mods.iterator(); iterator.hasNext(); )
{
ModFile mod = iterator.next();
if (!mod.getLocator().isValid(mod) || !mod.identifyMods()) {
LOGGER.warn(LogMarkers.SCAN, "File {} has been ignored - it is invalid", mod.getFilePath());
ModFile modFile = iterator.next();
if (!modFile.getProvider().isValid(modFile) || !modFile.identifyMods()) {
LOGGER.warn(LogMarkers.SCAN, "File {} has been ignored - it is invalid", modFile.getFilePath());
iterator.remove();
brokenFiles.add(mod);
brokenFiles.add(modFile);
}
}
return brokenFiles;
}
public ITransformationService.Resource getPluginResources() {
return new ITransformationService.Resource(IModuleLayerManager.Layer.PLUGIN, this.candidatePlugins.stream().map(ModFile::getSecureJar).toList());
return new ITransformationService.Resource(IModuleLayerManager.Layer.PLUGIN, this.candidatePlugins.stream().map(IModFile::getSecureJar).toList());
}
public ITransformationService.Resource getModResources() {
return new ITransformationService.Resource(IModuleLayerManager.Layer.GAME, this.candidateMods.stream().map(ModFile::getSecureJar).toList());
return new ITransformationService.Resource(IModuleLayerManager.Layer.GAME, this.candidateMods.stream().map(IModFile::getSecureJar).toList());
}
private List<EarlyLoadingException.ExceptionData> validateLanguages() {
@@ -84,7 +94,11 @@ public class ModValidator {
public BackgroundScanHandler stage2Validation() {
var errors = validateLanguages();
loadingModList = ModSorter.sort(candidateMods, errors);
var allErrors = new ArrayList<>(errors);
allErrors.addAll(this.discoveryErrorData);
loadingModList = ModSorter.sort(candidateMods, allErrors);
loadingModList.addCoreMods();
loadingModList.addAccessTransformers();
loadingModList.setBrokenFiles(brokenFiles);
@@ -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 AbstractJarFileLocator {
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;
};
}
@@ -45,9 +40,10 @@ public abstract class CommonClientLaunchHandler extends CommonLaunchHandler {
var fmlcore = LibraryFinder.findPathForMaven(vers.forgeGroup(), "fmlcore", "", "", vers.mcAndForgeVersion());
var javafmllang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "javafmllanguage", "", "", vers.mcAndForgeVersion());
var lowcodelang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "lowcodelanguage", "", "", vers.mcAndForgeVersion());
var mclang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "mclanguage", "", "", vers.mcAndForgeVersion());
return new LocatedPaths(mcstream.build().toList(), (a,b) -> true, modstream.build().toList(), List.of(fmlcore, javafmllang, mclang));
return new LocatedPaths(mcstream.build().toList(), (a,b) -> true, modstream.build().toList(), List.of(fmlcore, javafmllang, lowcodelang, mclang));
}
protected abstract void processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods);
@@ -5,7 +5,6 @@
package net.minecraftforge.fml.loading.targets;
import com.google.common.base.Strings;
import cpw.mods.jarhandling.SecureJar;
import java.io.File;
@@ -50,8 +49,6 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
if (getDist().isDedicatedServer())
return arguments;
fixNatives();
if (isData())
return arguments;
@@ -80,7 +77,7 @@ 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("mclanguage"))
.filter(e -> e.contains("fmlcore") || e.contains("javafmllanguage") || e.contains("lowcodelanguage") || e.contains("mclanguage"))
.map(Paths::get)
.toList();
}
@@ -119,18 +116,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;
};
}
@@ -55,9 +51,10 @@ public abstract class CommonServerLaunchHandler extends CommonLaunchHandler {
var fmlcore = LibraryFinder.findPathForMaven(vers.forgeGroup(), "fmlcore", "", "", vers.mcAndForgeVersion());
var javafmllang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "javafmllanguage", "", "", vers.mcAndForgeVersion());
var lowcodelang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "lowcodelanguage", "", "", vers.mcAndForgeVersion());
var mclang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "mclanguage", "", "", vers.mcAndForgeVersion());
return new LocatedPaths(mcstream.build().toList(), filter, modstream.build().toList(), List.of(fmlcore, javafmllang, mclang));
return new LocatedPaths(mcstream.build().toList(), filter, modstream.build().toList(), List.of(fmlcore, javafmllang, lowcodelang, mclang));
}
protected abstract BiPredicate<String, String> processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, BiPredicate<String, String> filter, Stream.Builder<List<Path>> mods);
@@ -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;
};
}
}
@@ -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 ForgeClientUserdevLaunchHandler extends ForgeUserdevLaunchHandler {
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;
@@ -15,12 +16,11 @@ public class ForgeDataDevLaunchHandler extends CommonDevLaunchHandler {
@Override 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;
@@ -20,12 +21,11 @@ public class ForgeDataUserdevLaunchHandler extends ForgeUserdevLaunchHandler {
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;
@@ -14,12 +15,11 @@ public class ForgeGametestDevLaunchHandler 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("forge").orElseThrow(), "net.minecraftforge.gametest.GameTestMain").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 ForgeGametestUserdevLaunchHandler extends ForgeUserdevLaunchHandler
@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("forge").orElseThrow(), "net.minecraftforge.gametest.GameTestMain").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 ForgeServerDevLaunchHandler 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 ForgeServerUserdevLaunchHandler extends ForgeUserdevLaunchHandler {
@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;
};
}
}
@@ -4,3 +4,5 @@ splashscreen = true
maxThreads = -1
# Enable forge global version checking
versionCheck = true
# Disables Optimized DFU client-side.
disableOptimizedDFU = true
@@ -1 +1,2 @@
net.minecraftforge.fml.loading.ModDirTransformerDiscoverer
net.minecraftforge.fml.loading.ModDirTransformerDiscoverer
net.minecraftforge.fml.loading.ClasspathTransformerDiscoverer
@@ -0,0 +1 @@
net.minecraftforge.fml.loading.moddiscovery.JarInJarDependencyLocator
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+11 -5
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -32,10 +32,10 @@
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
@@ -205,6 +205,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
Vendored
+8 -6
View File
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
+1 -1
View File
@@ -10,7 +10,7 @@ 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}")
implementation(project(':fmlloader'))
implementation(project(':fmlcore'))
}
@@ -18,14 +18,15 @@ import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.ModFileScanData;
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 java.util.Optional;
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
public class FMLModContainer extends ModContainer
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Marker LOADING = MarkerManager.getMarker("LOADING");
private final ModFileScanData scanResults;
private final IEventBus eventBus;
private Object modInstance;
@@ -37,7 +38,7 @@ public class FMLModContainer extends ModContainer
LOGGER.debug(LOADING,"Creating FMLModContainer instance for {}", className);
this.scanResults = modFileScanResults;
activityMap.put(ModLoadingStage.CONSTRUCT, this::constructMod);
this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).build();
this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).useModLauncher().build();
this.configHandler = Optional.of(ce->this.eventBus.post(ce.self()));
final FMLJavaModLoadingContext contextExtension = new FMLJavaModLoadingContext(this);
this.contextExtension = () -> contextExtension;
-26
View File
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below indicates application support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!--The ID below indicates application support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!--The ID below indicates application support for Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!--The ID below indicates application support for Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!--The ID below indicates application supprort for Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<!-- following stuff is so the EXE doesnt ask for admin access -->
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
Loaded 100 of 1905 files, more files were not shown because too many files have changed in this diff. Show more