Compare commits

...
214 Commits
Author SHA1 Message Date
FalsePattern 21fb3becd8 make conditional targeted mods use BooleanSupplier instead 2025-07-19 12:13:14 +02:00
ah-OOG-ah 203172bddb Add conditional mod exclusions
Syntax sugar for cases where you may only want to exclude a mod if a config is set (or not set).
2025-07-17 00:53:55 -04:00
Alexdoru 4cf9abd071 fix NPE in LateMixinOrchestrationMixin + don't mutate original mixins list 2025-07-12 11:06:21 -07:00
楓 9e2cd35b5a Merge mixin classes in config file instead of overwriting it (#50) 2025-07-12 09:23:35 -07:00
Alexdoru 092d0843d1 Add general purpose Mixins / ASM transformers registration system (#48) 2025-07-12 09:20:40 -07:00
ah-OOG-ah 55b9660726 Bump ASM (#47) 2025-06-30 12:29:01 -07:00
Ven 05878bfde1 Added support for OptiFineDevTweaker (#44) 2025-06-17 22:05:56 -04:00
amy 43ac78f2dc Use debug level for printing enabled GTNH mixins (#41) 2025-06-17 21:37:00 -04:00
ah-OOG-ahandRaven Szewczyk 1cbd6a0ecd Bump mixin to UniMix 0.15.4 (#39)
* Bump mixin

* Update github actions

---------

Co-authored-by: Raven Szewczyk <git@eigenraven.me>
2025-03-02 13:41:48 +00:00
ah-OOG-ah a97b3ce85c Update actions/upload-artifact (#40) 2025-02-19 08:41:45 -05:00
Jason Mitchell 56a008ce92 Update release.yml
V3 is being deprecated
2024-11-05 16:43:35 -08:00
Jason Mitchell 02a9a1a10d Move GTNH LateMixins to pre-init and fix compat with some mods that are triggering FMLConstructionEvent to load too early 2024-10-29 16:37:16 -07:00
Jason Mitchell 5fbb67e26f New mixins 0.8.7, mixinextras 0.4.1, and ASM 9.7.1 (#30)
* UniMix -> 0.15.0+mixin.0.8.7
MixinExtras -> 0.4.0

* maybe fix 1.8+

* GTNHMixins fix

* specify jdk

* rm

* mixin 0.15.3+mixin.0.8.7 and asm 9.7.1

* mixin extras 0.4.1
2024-10-14 18:17:32 -07:00
makamys 918a80ae53 [skip ci] README: Tweak badge styling 2024-03-21 19:32:43 +01:00
makamys 5788d7ec1e Bump ASM to 9.6
Mirroring change in Fabric Mixin
2024-03-19 02:51:57 +01:00
makamys e29c36c603 Bump UniMix and Fabric Mixin versions 2024-03-19 02:08:54 +01:00
makamys aed49ce829 Fix INIT mixins not loading with the LiteLoader mod in non-MultiMC 2024-03-19 02:08:51 +01:00
makamys 39a31c66da Remove trailing spaces 2024-02-14 20:24:25 +01:00
makamys d2c55ed6e3 Add maven publish CI workflow 2024-02-14 20:19:21 +01:00
makamys 614e8b9569 Adapt to GTNH maven 2024-02-14 20:19:18 +01:00
makamys 45d16b96ab Set up maven publishing 2024-02-14 19:30:18 +01:00
makamys 3563c869b5 [skip ci] Be more specific about GT6 version used in testing.md
Had this stashed and forgot to commit it
2024-02-14 19:29:59 +01:00
makamys 64a4084138 [skip ci] Use legacy curseforge in readme 2024-02-14 09:55:11 +01:00
drori200 acdd68819f Update mixinextras minor version 2024-02-09 03:38:37 +01:00
makamys 0fff377da8 docs: Make alternate mixin flavors less prominent
Since nobody uses them, and they aren't even published on Jitpack (because I don't want to increase build times unnecessarily)
2023-12-21 21:19:27 +01:00
makamys 1b130b1314 Use same version for all maven publications
For consistency with Jitpack, which uses the git tag for all publications.
2023-12-21 20:48:53 +01:00
makamys 608639acf3 Remap un-shadowed ASM package in mixin plugins
Needed for Zume
2023-12-21 17:11:18 +01:00
makamys 44f89624b0 Remove half-implemented selective remapping functionality
It's in the way
2023-12-20 11:03:03 +01:00
makamys 4f5d454d03 Update MixinExtras to 0.3.2 2023-12-19 08:01:44 +01:00
glowredman bbed619396 Use LoadableMod.hasValidMetaData() to filter out libs 2023-12-18 18:04:01 +01:00
glowredman d66394b2ea Add LiteLoader compat 2023-12-18 18:04:01 +01:00
makamys 220d040ebd [skip ci] Fix typo in license 2023-12-15 08:23:35 +01:00
makamys b42622470f [skip ci] README: Add image illustrating modularity, update info 2023-12-15 08:23:34 +01:00
makamys 3bc74ae73d [skip ci] Update the reference in the readme too 2023-12-12 07:59:13 +01:00
makamys 839425440e [skip ci] Link example mod in developer-usage.md 2023-12-12 07:56:13 +01:00
drori200 c2e86f3aae Update gradle.properties 2023-11-22 03:38:44 +01:00
drori200 48d55f2b44 Update build.gradle 2023-11-22 03:38:44 +01:00
drori200 de08ce459e Use MavenCentral 2023-11-22 03:38:44 +01:00
drori200 43a764c055 Pull mixinextras from MavenCentral 2023-11-22 03:38:44 +01:00
drori200 dad688c742 latest mixinextras rc 2023-11-22 03:38:44 +01:00
makamys f524e7de7f Fix typo in logger name of GTNHMixins and GasStation modules 2023-10-29 02:05:52 +01:00
makamys 181924abd1 Fix all obf jar not being included in maven publication 2023-10-29 02:42:33 +02:00
makamys 8bc2051af9 Give legacy GTNH MixinExtras directory a more descriptive name 2023-10-29 02:06:18 +02:00
makamys 87e71b1ba3 Fix legacy GTNHMixins still being indexed by IDEs 2023-10-29 02:04:23 +02:00
makamys ae12182d30 Fix wrong punctuation of sentences containing parentheses 2023-10-29 00:51:55 +02:00
makamys 189bf0b5e9 README: expand on documentation of extras 2023-10-29 00:51:52 +02:00
makamys 6b528532a2 Rebuild delegated transformer list at the beginning of late phase
Fixes mixin processor not running transformers added after DEFAULT phase
2023-10-29 00:15:48 +02:00
makamys 062eab7754 Fix project_url not getting substituted in mixin module's mcmod.info 2023-10-29 00:07:06 +02:00
makamys c053f33017 README: Remove outdated info about 1.8+ compat stub 2023-10-22 09:24:02 +02:00
makamys 016cf16396 README: Document Future Compat 2023-10-21 14:05:41 +02:00
makamys 9cff9105af Remove leftover GTNHMixins MixinExtras things 2023-10-21 14:01:58 +02:00
makamys 41aecb728a Make ASMRemapperTransformer skip uninteresting classes
Reduces ASMRemapperTransformer time from 2337ms to 220ms in gtnh.
2023-10-21 13:58:52 +02:00
makamys 8a8444d7f8 Remove duplicate MixinExtras license file 2023-10-21 12:58:28 +02:00
makamys cd232ec4a0 Shadow MixinExtras source 2023-10-21 12:58:03 +02:00
makamys 14e8ce7cb2 Document LaunchClassLoaderUtils some more 2023-10-21 12:32:51 +02:00
makamys 6c55d33c18 Move GTNH MixinExtras generator to GTNHMixins module
It shouldn't be around if you're not using the GTNHMixins module
2023-10-21 11:50:22 +02:00
makamys fcd416e31b Only generate class if it's not present 2023-10-21 11:50:22 +02:00
makamys fe11aee64c Fix GTNH MixinExtras generator crashing in dev env with NEI 2023-10-21 11:50:21 +02:00
makamys d5c96de372 Rename fix to hack because that's what it is 2023-10-21 11:50:21 +02:00
makamys 798b3fb1c9 Update developer usage 2023-10-21 11:50:21 +02:00
makamys ee81c389fa Oops, FML's deobf property isn't set yet. Just use a jvm arg then. 2023-10-21 11:50:21 +02:00
makamys 1237e86768 Don't allow empty changelog in publish script 2023-10-21 11:50:21 +02:00
makamys d268a991ad README: Add hint about compilation 2023-10-21 11:50:21 +02:00
makamys f1a63f392d Move 1.8+ compat stub into separate module (fixes #19) 2023-10-21 11:50:21 +02:00
makamys 7ee32c62d3 Merge devcompat module into compat
It was kept separate to make it easy to apply the dev compat tweaks in dev
environments without having to mess with config files, but this is unnecessary
now that you can set config values via JVM flags.
2023-10-21 11:50:16 +02:00
makamys ddd608a8f8 Give transformers more unique and descriptive names 2023-10-21 03:04:02 +02:00
makamys a4bddd8dbf Allow setting properties using JVM flags 2023-10-21 02:34:43 +02:00
makamys a892f6d119 Disable reobf in modules that don't need it 2023-10-21 02:34:40 +02:00
makamys 58bb4137f0 Fix the LWJGL3ify compat ForgeGradle style
Reobfuscating the MixinExtras module is what breaks it. So don't do that, and
don't run the reobf task for the all jar either, just collect the contents
of all the obf jars.
2023-10-21 01:39:36 +02:00
makamys 28b93dd4f7 Reject RFG, return to FG
RFG's performance is unsuitable for this project, see
https://github.com/GTNewHorizons/RetroFuturaGradle/issues/49

Reverts the following commits:

ba1f02d Begin RFG move. Common module compiles
b589106 Mixin module works now I think
46a5c6d Use VERSION tag in Mixin module
2409b24 Mixingasm seems to work now
f0b355c Sources jar get
23e833f Remove redundant calls
2e78938 Update gradle in hopes that it will magically fix all my problems
80e0723 Port all the modules
28e98d5 Port All module (not the same thing)
3a09260 Fix common module build failing
1088934 Get rid of bloat from buildscript template
bff36c3 Porting is done now maybe
27e3058 Fix what I broke
bbc0b48 zulu?
9b4bde7 I guess I need Java 17 too
c3af516 RFG needs alot of ram apparently

But keeps these:

9152c1a Don't build alternate flavors by default
24f6c06 Move compat module's MixinExtras temp jar to temp dir
2023-10-21 00:32:29 +02:00
makamys c3af5167df RFG needs alot of ram apparently 2023-10-19 11:49:56 +02:00
makamys 9b4bde7e95 I guess I need Java 17 too 2023-10-19 11:33:48 +02:00
makamys bbc0b48175 zulu? 2023-10-19 11:31:05 +02:00
makamys 27e3058a53 Fix what I broke 2023-10-19 11:13:56 +02:00
makamys 24f6c06631 Move compat module's MixinExtras temp jar to temp dir 2023-10-19 10:44:51 +02:00
makamys bff36c3067 Porting is done now maybe 2023-10-19 10:22:24 +02:00
makamys 108893429f Get rid of bloat from buildscript template 2023-10-19 10:04:04 +02:00
makamys 3a09260a99 Fix common module build failing 2023-10-19 10:02:22 +02:00
makamys 9152c1a5ca Don't build alternate flavors by default
Because I'm tired of having to append -PnoAltMixin to the command
every time
2023-10-19 09:51:42 +02:00
makamys 28e98d501f Port All module (not the same thing) 2023-10-19 09:49:47 +02:00
makamys 80e07234f5 Port all the modules 2023-10-19 09:27:49 +02:00
makamys 2e7893816f Update gradle in hopes that it will magically fix all my problems 2023-10-19 08:37:12 +02:00
makamys 23e833fd1e Remove redundant calls 2023-10-19 07:14:32 +02:00
makamys f0b355c782 Sources jar get 2023-10-19 06:46:30 +02:00
makamys 2409b246b3 Mixingasm seems to work now 2023-10-19 06:34:39 +02:00
makamys 46a5c6d167 Use VERSION tag in Mixin module 2023-10-19 06:27:59 +02:00
makamys b5891061e0 Mixin module works now I think 2023-10-19 06:02:15 +02:00
makamys ba1f02d541 Begin RFG move. Common module compiles 2023-10-19 01:49:42 +02:00
makamys 9aa07d509c Update Mixingasm config to mention @MixinSafeTransformer 2023-10-18 19:31:19 +02:00
makamys 8e17e96c3e Remove duplicated Mixingasm assets in GasStation module 2023-10-18 19:31:18 +02:00
makamys 8e3e7e49e5 Make compat module use new Mixingasm API
(But keep using the old one as a fallback.)
2023-10-17 05:47:10 +02:00
makamys 0e54b7bf08 Add better Mixingasm API that doesn't create a runtime dependency 2023-10-17 05:47:10 +02:00
makamys 1db6d9b000 Fix generator crashing in GTNH 2023-10-17 05:47:10 +02:00
makamys 9354ebd81a Update readme 2023-10-17 05:47:10 +02:00
makamys df12ab3ef3 Generate GTNH-relocated MixinExtras at runtime
This preserves backwards compatibility while making new mods fail to compile if
they try to reference the old package.
2023-10-17 05:47:09 +02:00
makamys 2ae66ed7a6 Document changes 2023-10-17 05:47:09 +02:00
makamys 46d1e77770 Remove leftover GTNH-MixinExtras references 2023-10-17 05:47:09 +02:00
drori200 e3c8d83108 Remove MixinExtras from GTNHMixin (#18)
* Remove MixinExtras from GTNHMixin

* Bump GTNHMixin version

* Version bump

branch was out of date
2023-10-17 05:45:07 +02:00
Jason Mitchell cec3048ed4 Merge pull request #15 from drori200/patch-1
Update MixinExtras to latest
2023-10-13 17:34:30 -07:00
drori200 f5800a69c2 Update to MixinExtras stable 0.2.0 2023-10-14 03:24:05 +03:00
drori200 b143241008 Revert "Update build.gradle"
This reverts commit 0daba06b88.
2023-10-14 03:17:08 +03:00
drori200 0daba06b88 Update build.gradle 2023-09-29 19:45:59 +03:00
drori200 ba453f04c5 Update build.gradle 2023-09-29 19:42:05 +03:00
drori200 8e2d6beb3f Correct gtnh's mixinextra AP path 2023-09-29 02:11:25 +03:00
drori200 c0f712fb9d Update GTNHMixin's MixinExtra version 2023-09-29 02:05:54 +03:00
drori200 2376d1ce50 Correct the MixinExtras ap path 2023-09-29 01:35:48 +03:00
drori200 37edc3537a Update MixinExtras to latest rc 2023-09-26 06:57:35 +03:00
makamys 618bd0de47 testing.md: Update FPLib and RPGS, fix some inaccuracies 2023-06-21 08:58:27 +02:00
makamys d4213cf71a Add forge-network-fix to testing.md 2023-06-21 07:44:39 +02:00
Egor 92db4df839 Fix mixin-booter-legacy module not having a manifest. 2023-06-21 07:42:53 +02:00
makamys 278edd177d Change remaining - to + in README 2023-06-11 06:08:27 +02:00
makamys 1ed02bc811 Change jar name prefix to +
Because - is annoying to work with in the command line
2023-06-11 05:44:01 +02:00
makamys 2a331ee00f Remove build-zip.sh since it's not used anymore 2023-06-11 05:30:32 +02:00
makamys 5d5aff4efe Remove - prefix from maven artifact id 2023-06-11 05:30:11 +02:00
makamys e54253eb95 Make update checker pluralize nouns properly 2023-06-10 22:26:23 +02:00
makamys adfa0b9061 Move more versions to gradle.properties for consistency 2023-06-10 22:22:47 +02:00
makamys 12ea25c3f2 Make update checker print a summary and use return codes 2023-06-10 21:59:25 +02:00
makamys 96fcda3db2 Bump GTNHMixins version
(There were no new changes to merge, this is just a metadata change.)
2023-06-10 21:56:17 +02:00
makamys 524ee60d9a Add update check script 2023-06-10 21:51:21 +02:00
makamys 4144ea5913 Move dependency versions to gradle.properties
This will make checking for updates easier
2023-06-10 21:46:46 +02:00
makamys b8953cc38c Use tags everywhere in source urls in credits
This will make checking for updates easier
2023-06-10 21:19:47 +02:00
makamys f20c78c96b Set acceptableRemoteVersions = "*" in all modules 2023-06-01 19:07:04 +02:00
makamys f595016981 README: Hint at the existence of 1.8+ support 2023-05-24 08:03:22 +02:00
makamys 4355a4a481 Add Forge maven 2023-05-24 07:31:03 +02:00
makamys a68db4a990 Document changes 2023-05-24 07:14:33 +02:00
makamys b8830d9375 Add ComparableVersion abstraction layer 2023-05-24 07:14:33 +02:00
makamys 23386a3ea8 Add 1.8+ Mod and IFMLPlugin annotations and stub IFMLPlugin interface 2023-05-24 07:14:21 +02:00
makamys 4b1b1423fd Fix Mixingasm standalone jar being broken 2023-05-24 05:36:13 +02:00
makamys 967f3a5d30 Bump fabric-mixin version 2023-05-22 05:16:40 +02:00
makamys 717ffa5a3a Fix typo in testing.md 2023-05-22 04:54:23 +02:00
makamys 078c5eb28f Update UniMix to 0.12.2 2023-05-22 04:27:21 +02:00
makamys d9b583d874 Update MixinExtras to 0.2.0-beta.8 2023-05-22 04:18:58 +02:00
makamys 1c12590813 Add it to the release workflow too 2023-05-03 06:41:31 +02:00
makamys 77670d9644 Add sources CI artifact 2023-05-03 06:35:21 +02:00
makamys 478ea715d2 docs: Add hot swap instructions, bump UniMix version [skip ci] 2023-05-03 06:30:41 +02:00
makamys 1336eb46b4 Ignore modrinth publish errors [skip ci]
Modrinth uploads keep erroring despite the upload succeeding
2023-05-02 20:17:48 +02:00
makamys 907693bf4f Add missing newline 2023-05-02 18:48:41 +02:00
makamys 67a2e95c33 Remove duplicate java agent mod candidates 2023-05-02 18:47:10 +02:00
makamys c7aff3a584 Bump UniMix version 2023-05-02 18:47:10 +02:00
makamys 9d02189012 README: Elaborate on the dev compat module's nature 2023-05-02 18:47:10 +02:00
makamys fe3108cdcc README: Fix dead link 2023-04-29 00:24:06 +02:00
makamys dfafab8d86 Change _ to - in supplementary files 2023-04-16 00:09:07 +02:00
makamys d3417fb89b Update to ASM 9.5 2023-04-15 23:29:42 +02:00
makamys f320a0766d Use - prefix instead of _ (fixes #9) 2023-04-15 23:29:42 +02:00
makamys 5456aef979 README: expand note about jar naming (cc #9) [skip ci] 2023-03-30 19:48:18 +02:00
makamys 282a24c93f docs: mention GTNH ExampleMod, add missing runServer config [skip ci] 2023-03-29 22:17:13 +02:00
makamys 94fd027bb0 Change minotaur version to 2.+
We can have a little non-reproducibility, as a treat
2023-03-29 01:16:34 +02:00
makamys 57493511b2 Add --stacktrace to publish workflow 2023-03-29 01:03:10 +02:00
makamys 75124cfbd6 Include dev jars in releases 2023-03-29 00:22:06 +02:00
makamys ddf5f48ca4 Include dev jar in artifacts 2023-03-29 00:15:18 +02:00
makamys bc8cb2bae3 README: add MixinTweaker argument to example snippet 2023-03-28 23:47:20 +02:00
makamys bf85e0449a README: fix broken CREDITS link 2023-03-28 23:44:51 +02:00
makamys e7b52d21db Show full jar name in conflicting jar warning 2023-03-28 23:43:25 +02:00
makamys af589d5f98 Move sanity check to individual modules 2023-03-28 23:43:21 +02:00
makamys ff22e0a7b6 Add warning if Mixin did not get initialized 2023-03-28 20:59:13 +02:00
makamys e1b148d5c7 Add warning if Mixin is getting bootstrapped from a foreign jar 2023-03-23 16:04:43 +01:00
makamys 0993658b73 Refactor sanity check to allow multiple warnings 2023-03-23 15:09:45 +01:00
makamys 0107566517 Fix jitpack maybe 2023-03-23 14:52:38 +01:00
makamys d29d950f3d Exclude modlauncher services from META-INF
Fixes a (harmless) debug error that happens when services are instantiated
2023-03-20 10:21:12 +01:00
makamys abba979a3d Fix typo 2023-03-18 12:51:15 +01:00
makamys 71e38dacaf Update versions in developer-usage.md 2023-03-05 21:01:43 +01:00
makamys 89d6ea5550 Sync MBL with upstream
No functional changes, they just got our LateMixin fix with slightly different
formatting.
2023-03-05 20:57:54 +01:00
makamys db8a3f9065 Sanitize modids so they can be safely put into method names 2023-03-04 20:46:16 +01:00
makamys b851599c94 Bump UniMix and Fabric-Mixin version 2023-03-04 20:21:53 +01:00
makamys b2e92b3850 Merge pull request #6 from MetaLabsMc/master
Fix LateMixin loading in mixin-booter-legacy
2023-03-04 18:45:18 +01:00
makamys fef6985f0e Add some more comments to MixinModDecorator#apply 2023-03-04 18:23:46 +01:00
makamys ca18dc40cb Fix modid decorator not parsing v2 mcmod.infos properly
This was causing mods like BugTorch 1.2.5 and ModernSplash 2c17c9a to appear as
(unknown) in error messages.
2023-03-04 18:23:46 +01:00
Mark Sheremeta c324e7f4d4 Fix LateMixin loading in mixin-booter-legacy 2023-03-04 00:16:41 +01:00
makamys e3633cede4 README: add Discord [skip ci] 2023-02-27 08:51:00 +01:00
makamys 13dc9d4066 Create config dir if it doesn't exist
Fixes config not getting written if the config dir doesn't exist at the start
of the run.
2023-02-25 22:51:11 +01:00
makamys 3b047eb51b Include mod name in MR/CF release names [skip ci]
Versions with short names are hard to click in CurseForge's UI
2023-02-17 10:37:46 +01:00
makamys ff943a3980 Upload zip in publish workflow for ease of grab 2023-02-17 10:12:03 +01:00
makamys f8aa353f61 Seems legit 2023-02-17 10:05:15 +01:00
makamys 165317b6c4 I know, I'll push a test tag for testing 2023-02-17 09:47:14 +01:00
makamys 2ef5c5e022 Fix yaml maybe 2023-02-17 09:45:40 +01:00
makamys d662bedbf4 Let me run it 2023-02-17 09:43:04 +01:00
makamys fdec26a280 Disable draft tag check [skip ci] 2023-02-17 09:41:18 +01:00
makamys 898bd7d8db I hope my YAML is syntactically valid now 2023-02-17 09:35:04 +01:00
makamys 3fe2a8ba61 Add (dry) Modrinth & CurseForge publish workflow 2023-02-17 09:26:06 +01:00
makamys 60a38e6609 Overhaul publish script 2023-02-17 09:25:48 +01:00
makamys ee3ac092d3 Update comment of enhanceCrashReports 2023-02-17 01:38:14 +01:00
makamys ae146d9749 Add debug-level warnings when integrity checks are disabled 2023-02-17 01:38:14 +01:00
makamys cbaccfa7c3 Revise version strings to be (mostly) semver-compliant
The GasStation module still has an underline in its version string, nothing I
can do about that.
2023-02-17 01:38:14 +01:00
makamys 2e355bb4f0 Add gtnh to SpongeMixins module's version in its mcmod.info 2023-02-17 01:38:14 +01:00
makamys 5ce0d5e6e0 Add missing modid to MixinExtras module's mcmod.info
Also changed modid to `mixinextras`
2023-02-17 01:38:13 +01:00
makamys 5217f5855d Add @Mod annotation to each module
Fixes issues stemming from Forge being unable to detect the mod. Namely:
- The "FML has found a non-mod file" warning
- The mod not showing up in the mod list GUI
- The mod not showing up in crash reports
2023-02-17 01:38:13 +01:00
makamys ceba1109ea Refactor config parsing to only write if config is dirty 2023-02-17 01:38:13 +01:00
makamys ddb02e692f Update description of compat module 2023-02-17 01:38:13 +01:00
makamys defecbc5bc Add all mixins in stack trace classes to crash report 2023-02-17 01:38:13 +01:00
makamys f4308e1a13 Remove leftover debug objects 2023-02-17 01:38:13 +01:00
makamys bcad088a29 Show mixin errors for all classes in stack trace 2023-02-17 01:38:12 +01:00
makamys 653047d47b Fix reobf not running when running publish task
This caused duplicate classes to appear in the all jar.
2023-02-17 01:38:12 +01:00
makamys 11d66bc58b Ensure PropertyToken#comment is mutable 2023-02-17 01:38:12 +01:00
makamys f9aa965147 Change developer-usage.md to use dev jars 2023-02-17 01:38:12 +01:00
makamys c03412daa3 Rename disableIntegrityChecks to enableIntegrityChecks
Because nobody likes double negatives
2023-02-17 01:38:12 +01:00
makamys 7bf758543e Exclude common module from publishing and dev and source jar creation 2023-02-17 01:38:12 +01:00
makamys cf6402e0bf Exclude dev and common jars from CI 2023-02-17 01:38:11 +01:00
makamys 1777a71096 Add dev jars 2023-02-17 01:38:11 +01:00
makamys c47e2aefbc Instead of deleting old config, just rename it
Deleting people's configs isn't nice, even if it's just a single boolean.
2023-02-17 01:38:11 +01:00
makamys a05507f043 Add default value to config comments 2023-02-17 01:38:11 +01:00
makamys 56b2f4c651 Use common config parser in all modules 2023-02-17 01:38:10 +01:00
makamys fee71e8be9 Refactor config parsing into common module 2023-02-17 01:38:10 +01:00
makamys c8bd6bea07 Make compat module configurable 2023-02-17 01:38:10 +01:00
makamys a1f6b03dc8 Add crash report enhancer 2023-02-17 01:38:10 +01:00
makamys 006f3490a6 README: Add CurseForge badge [skip ci] 2023-02-15 17:59:57 +01:00
makamys d7818d0f2e README: Add Modrinth badge [skip ci] 2023-02-15 03:16:12 +01:00
makamys a311cb111d Let's try that again [skip ci] 2023-02-14 23:20:21 +01:00
makamys 9a6db5e25b Remove GTNHMixinsLite from comparison chart [skip ci]
It's no longer relevant, it was essentially a prototype for UniMixins
2023-02-14 23:13:52 +01:00
makamys 7bfcb47d8a README: add GasMix link now that the repo is public again [skip ci] 2023-02-14 22:31:42 +01:00
makamys deec1015cd README: fix broken link [skip ci] 2023-02-14 22:26:00 +01:00
makamys bf1f3cd742 README: revise "Background" section [skip ci] 2023-02-14 22:24:15 +01:00
makamys 07f2ad4af6 Fix typo in SpongeMixins module's license 2023-02-14 20:45:02 +01:00
makamys 1f829353ea Summarize module licenses inside LICENSE 2023-02-14 20:44:58 +01:00
makamys be33c33182 Don't include alternate Mixin module flavors in release 2023-02-14 20:15:53 +01:00
127 changed files with 4203 additions and 784 deletions

No files matched your search

+23 -5
View File
@@ -8,17 +8,24 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 1.8
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 8
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
build-scan-publish: true
build-scan-terms-of-use-url: "https://gradle.com/terms-of-service"
build-scan-terms-of-use-agree: "yes"
validate-wrappers: true
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Set up Gradle build
@@ -28,9 +35,20 @@ jobs:
- name: Copy artifacts to a single directory
run: |
mkdir build/tmp/ci -p
mkdir build/tmp/ci-dev -p
mkdir build/tmp/ci-sources -p
cp module-*/build/libs/* build/tmp/ci
rm build/tmp/ci/*-sources.jar
- uses: actions/upload-artifact@v3
mv build/tmp/ci/*-all-*-dev.jar build/tmp/ci-dev
mv build/tmp/ci/*-compat-*-dev.jar build/tmp/ci-dev
rm build/tmp/ci/*-dev.jar
mv build/tmp/ci-dev/* build/tmp/ci
rm build/tmp/ci/*-common-*.jar
mv build/tmp/ci/*-sources.jar build/tmp/ci-sources
- uses: actions/upload-artifact@v4
with:
name: Package
path: build/tmp/ci
- uses: actions/upload-artifact@v4
with:
name: Package-sources
path: build/tmp/ci-sources
+42 -5
View File
@@ -16,19 +16,26 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: Set release version
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- uses: actions/checkout@v3
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 1.8
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 8
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
build-scan-publish: true
build-scan-terms-of-use-url: "https://gradle.com/terms-of-service"
build-scan-terms-of-use-agree: "yes"
validate-wrappers: true
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Set up Gradle build
@@ -38,8 +45,23 @@ jobs:
- name: Copy artifacts to a single directory
run: |
mkdir build/tmp/ci -p
mkdir build/tmp/ci-dev -p
mkdir build/tmp/ci-sources -p
cp module-*/build/libs/* build/tmp/ci
rm build/tmp/ci/*-sources.jar
mv build/tmp/ci/*-all-*-dev.jar build/tmp/ci-dev
mv build/tmp/ci/*-compat-*-dev.jar build/tmp/ci-dev
rm build/tmp/ci/*-dev.jar
mv build/tmp/ci-dev/* build/tmp/ci
rm build/tmp/ci/*-common-*.jar
mv build/tmp/ci/*-sources.jar build/tmp/ci-sources
- uses: actions/upload-artifact@v4
with:
name: Package
path: build/tmp/ci
- uses: actions/upload-artifact@v4
with:
name: Package-sources
path: build/tmp/ci-sources
- name: Read changelog from last draft release
run: |
gh api "/repos/${GITHUB_REPOSITORY}/releases?per_page=1" \
@@ -48,6 +70,7 @@ jobs:
DRAFT_TAG=$(cat /tmp/last-release.json | jq '.tag_name' -r)
[ $DRAFT_TAG == $RELEASE_VERSION ] || (echo "Error: Last draft's tag does not match pushed tag" && false)
cat /tmp/last-release.json | jq '.body' -r > /tmp/changelog.md
[ $(cat /tmp/changelog.md | wc -w) -gt 0 ] || (echo "Error: Draft is missing a changelog" && false)
cat /tmp/last-release.json | jq '.id' > /tmp/draft_id.txt
env:
GH_TOKEN: ${{ github.token }}
@@ -56,6 +79,20 @@ jobs:
with:
files: build/tmp/ci/*.jar
body_path: /tmp/changelog.md
- name: Modrinth and CurseForge release
run: |
cp /tmp/changelog.md publish/changelog.md
(cd publish; ./gradlew publishModrinth --stacktrace) || true
(cd publish; ./gradlew publishCurseforge --stacktrace)
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
- name: Publish to GTNH maven
run: ./gradlew publish
env:
MAVEN_URL: ${{ secrets.MAVEN_URL }}
MAVEN_USER: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
- name: Delete last draft release
run: |
gh api -X DELETE "/repos/${GITHUB_REPOSITORY}/releases/$(cat /tmp/draft_id.txt)"
+25 -3
View File
@@ -1,4 +1,26 @@
UniMixins is licensed under the Unlicense, as specified in LICENSE.UNLICENSE,
with the exception of certain modules (found in directories named module-*),
which are licensed under different terms. Refer to CREDITS and LICENSE inside
these directories for more information.
with the exception of the following modules, which are licensed under different
terms.
Refer to CREDITS, LICENSE, and README.original.md (whichever of these exist)
inside their directories for full information, including reproductions of their
original license texts.
## SpongeMixins
Directory: module-spongemixins
LICENSE: LGPLv3
## MixinBooterLegacy
Directory: module-mixinbooterlegacy
LICENSE: LGPLv2.1
## GasStation
Directory: module-gasstation
LICENSE: LGPLv3+SNEED
## GTNHMixins
Directory: module-gtnhmixins
LICENSE: LGPLv3
+44 -19
View File
@@ -1,18 +1,24 @@
[![downloads](https://img.shields.io/badge/-⬇%20releases-brightgreen?style=flat-square)](https://github.com/LegacyModdingMC/UniMixins/releases)
[![builds](https://img.shields.io/badge/-🛈%20builds-blue?style=flat-square)](https://makamys.github.io/docs/CI-Downloads/CI-Downloads.html)
[![CurseForge](https://shields.io/badge/CurseForge-555555?logo=curseforge&logoColor=white&style=flat-square)](https://legacy.curseforge.com/minecraft/mc-mods/unimixins)
[![modrinth](https://shields.io/badge/modrinth-555555?logo=modrinth&logoColor=white&style=flat-square)](https://modrinth.com/mod/unimixins)
[![Discord](https://shields.io/badge/Discord-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/AJxEFs6YBN)
# UniMixins
UniMixins is a Mixin loader for Minecraft 1.7.10 designed for maximum compatibility. It aims to combine the features of as many mixin loaders as possible, and its modular nature makes it possible to use along other loaders if desired.
UniMixins is a Mixin loader for Minecraft 1.7.10 (with partial 1.8.9~1.12.2 support) designed for maximum compatibility. It aims to combine the features of as many mixin loaders as possible, and its modular nature makes it possible to use along other loaders if desired.
## Background
Numerous Mixin loaders exist for 1.7.10, and each one provides a differing set of extra features we refer to as *"extras"* on top of providing Mixin.
Since each of these loaders is "glued together", different ones cannot be used at the same time due to unavoidable feature overlap. UniMixins breaks this tradition by offering each feature as a separate module (but a combined jar is still provided for convenience.)
Since each of these loaders is monolithic, different ones cannot be used at the same time due to unavoidable feature overlap. To avoid this problem, UniMixins offers each feature as a separate module (but a combined jar is still provided for convenience).
## Comparison table
Below is a table comparing the feature sets of known Mixin loaders.
Note: A more detailed version of this table is available [here](https://legacymoddingmc.github.io/wiki/#comparison-of-1.7.10-mixin-loaders/).
Note: A more detailed version of this table is available [here](https://legacymoddingmc.github.io/wiki/comparison-of-1.7.10-mixin-loaders/).
<picture>
<source srcset="docs/comparison-chart-dark.png" media="(prefers-color-scheme: dark)">
@@ -24,7 +30,11 @@ Note: A more detailed version of this table is available [here](https://legacymo
## Usage
Download the `-all` jar, and put it in your mods directory. Remove any other conflicting Mixin loaders (refer to the above table). It's important to keep the `_` at the beginning of the file name to avoid [an issue](https://github.com/tox1cozZ/mixin-booter-legacy/issues/1) with mods that shade mixin.
Download the `-all` jar, and put it in your mods directory. Remove any other conflicting Mixin loaders (refer to the above table).
> **Note**: It's important for the jar to be named so that it comes first alphabetically, to avoid [a sorting issue](https://github.com/tox1cozZ/mixin-booter-legacy/issues/1) with mods that embed Mixin. This is why the jar starts with a `+` character. You may need to change this to a `!` character if you encounter issues with renamed jars.
>
> For reference, here is the sorting order of some notable characters: `!` < `+` < `0` < `[` < `_` < `a`
### Advanced usage
@@ -39,7 +49,7 @@ The game will crash if modules are missing dependencies. This can be disabled in
### Developer usage
See [docs/developer-usage.md](docs/developer-usage.md) to see how to migrate an existing build script to depend on UniMixins.
See [docs/developer-usage.md](docs/developer-usage.md).
## List of modules
@@ -48,31 +58,40 @@ All modules depend on the Mixin module. Note that the dependents don't necessari
### Included in `all`-jar
* **Mixin (UniMix)**: Provides [a Mixin fork](https://github.com/LegacyModdingMC/UniMix) maintained by the UniMixins developers, based on [the Fabric fork](https://github.com/FabricMC/Mixin).
* **SpongeMixins**: Provides an emulation of [SpongeMixins](https://github.com/GTNewHorizons/SpongeMixins)'s extras.
* **MixinBooterLegacy**: Provides an emulation of [MixinBooterLegacy](https://github.com/tox1cozZ/mixin-booter-legacy)'s extras, and a relocated MixinExtras.
* **SpongeMixins** <sup>[1.7.10 only]</sup>: Provides an emulation of [SpongeMixins](https://github.com/GTNewHorizons/SpongeMixins)'s extras.
* **MixinBooterLegacy** <sup>[1.7.10 only]</sup>: Provides an emulation of [MixinBooterLegacy](https://github.com/tox1cozZ/mixin-booter-legacy)'s extras, and a relocated MixinExtras.
* **MixinExtras**: Provides [MixinExtras](https://github.com/LlamaLad7/MixinExtras).
* **GasStation**: Provides an emulation of [GasStation](https://github.com/FalsePattern/GasStation)'s extras.
* **GasStation** <sup>[1.7.10 only]</sup>: Provides an emulation of [GasStation](https://github.com/FalsePattern/GasStation)'s extras.
* Depends on SpongeMixins.
* Depends on MixinBooterLegacy.
* Depends on MixinExtras
* **GTNHMixins**: Provides an emulation of [GTNHMixins](https://github.com/GTNewHorizons/GTNHMixins)'s extras, and a relocated MixinExtras.
* **GTNHMixins** <sup>[1.7.10 only]</sup>: Provides an emulation of [GTNHMixins](https://github.com/GTNewHorizons/GTNHMixins)'s extras, and a relocated MixinExtras.
* Depends on SpongeMixins.
* **Compat**: Improves compatibility by fixing incorrect references to Mixin classes (specifically the shaded ASM) in mods.
* **Compat**: Various compatibility tweaks:
* Remaps invalid references to Mixin classes (specifically the shaded ASM) in mods.
* Adds information about mixins to crash reports. <sup>[1.7.10 only]</sup>
* **Mixingasm**: Improves Mixin's compatibility with ASM transformers.
### Optional
* **Future Compat** <sup>[1.8.9+ only]</sup>: Makes many of the modules usable on 1.8.9+. **Needed to run the mod on 1.8+.** (Not needed for Mixingasm.)
### Very Optional
These are not included in releases, they mainly exist for testing purposes. (Note: You can only use a single Mixin module at the same time.)
* **Mixin (SpongePowered)**: Provides [the upstream fork](https://github.com/SpongePowered/Mixin) of Mixin.
* **Mixin (Fabric)**: Provides [the Fabric fork](https://github.com/FabricMC/Mixin) of Mixin.
* **Mixin (GasMix)**: Provides the GasStation fork of Mixin.
* **Mixin (GasMix)**: Provides [the GasStation fork](https://github.com/FalsePattern/GasMix) of Mixin.
* **Mixin (GTNH)**: Provides [the GTNH fork](https://github.com/GTNewHorizons/SpongePoweredMixin) of Mixin.
* **DevCompat**: Compatibility tweaks for dev environments. Not needed if using UniMix or GTNH's Mixin fork.
## FAQ
### Why another mixin loader? Haven't you seen that one xkcd strip?
There is currently far too much fragmentation among Mixin loaders on 1.7.10, with different mods requiring different incompatible loaders. This is partly due to conflicts between their developers, and partly because of technical reasons (the loaders are designed in a monolithic way, which invites incompatibility.) This project has neither of those issues.
Before UniMixins was created, there was far too much fragmentation among Mixin loaders on 1.7.10, with different mods requiring different incompatible loaders. This was partly due to conflicts between their developers, and partly because of technical reasons (the loaders were designed in a monolithic way, which invites incompatibility). This project has neither of those issues.
<img title="Ah yes, just as I remember it." src="docs/images/modularity.png">
### Why are you using the Fabric fork of Mixin? Isn't this a Forge mod?
@@ -80,20 +99,25 @@ The Fabric fork of Mixin is more actively maintained than the upstream fork, and
### What do the "extras" do anyway?
* SpongeMixins, Grimoire, MixinBooterLegacy, GasStation and GTNHMixins offer various ways to let you mix into the classes of non-coremods (something Mixin does not natively support.)
* Mixingasm fixes [a Mixin issue](https://github.com/SpongePowered/Mixin/issues/309) that breaks some ASM transformers due to Mixin altering how they are called.
* MixinExtras adds new Mixin features that mods can use to write less intrusive mixins.
* SpongeMixins, Grimoire, MixinBooterLegacy, GasStation and GTNHMixins offer various ways to let you mix into the classes of non-coremods. (This is something Mixin does not natively support, since all of its phases occur before Forge discovers non-coremods and puts them on the classpath.)
- **MixinBooterLegacy**, **GasStation** and **GTNHMixins** add a "late" mixin phase that is fired right before the first mod construction event is dispatched. At this point all mods have been discovered.
- **SpongeMixins** lets you manually add mods to the classpath early using their file names. An old, hacky solution.
- We recommend new mods to use **GTNHMixins** as it's the most actively used and maintained option.
* **Mixingasm** fixes [a Mixin issue](https://github.com/SpongePowered/Mixin/issues/309) that breaks some ASM transformers due to Mixin altering how they are called.
* **MixinExtras** adds new Mixin features that allow mods to write less intrusive mixins.
### Mixin? mixin? SpongeMixins? SpongePowered? What's the difference?
* [Mixin](https://github.com/SpongePowered/Mixin): a Java library that lets you transform Java classes in a fluent way.
* [SpongePowered](https://spongepowered.org/): the organization that created Mixin (who also developed a modding API called Sponge.)
* [SpongePowered](https://spongepowered.org/): the organization that created Mixin (who also developed a modding API called Sponge).
* [mixin](https://github.com/SpongePowered/Mixin/wiki/Introduction-to-Mixins---Understanding-Mixin-Architecture#4-only-you-mixins-can-save-mankind): a special Java class defining a "change" that should be applied to a class. These get applied by Mixin.
* [SpongeMixins](https://github.com/TimeConqueror/SpongeMixins): a Mixin loader created by TimeConqueror that has a very generic name.
## Contributing
When editing the source code of modules, please document your changes in the [CREDITS](CREDITS) file. This allows people to easily see what we have changed, and lets other developers know what changes they have to keep in mind when syncing with upstream.
When editing the source code of modules, please document your changes in the module's CREDITS file. This allows people to easily see what we have changed, and lets other developers know what changes they have to keep in mind when syncing with upstream.
The project needs to be compiled using Java 8. For example: `JAVA_HOME=/path/to/jdk8 ./gradlew build`
### Useful commands
@@ -102,6 +126,7 @@ When editing the source code of modules, please document your changes in the [CR
### Build flags
* `-Plocal`: use the locally built version of `unimix` when building the Mixin module.
* Invoke `./gradlew publishToMavenLocal` in the UniMix repo first to install a local build.
* `-PenableAltMixin`: build alternate flavors of the Mixin module, not just UniMix.
### Roadmap
@@ -109,7 +134,7 @@ A rough roadmap of the project is available [here](https://gist.github.com/makam
### Test cases
See [docs/testing.md](testing.md) for a list of mods we have used to test the functionality of various UniMixins modules.
See [docs/testing.md](docs/testing.md) for a list of mods we have used to test the functionality of various UniMixins modules.
## License
-15
View File
@@ -1,15 +0,0 @@
#!/bin/bash
# Creates a modular zip in the build directory.
#
# To test with local UniMix, set ALL_BUILD_EXTRA_ARG=-Plocal
set -e
./gradlew clean
./gradlew build
mkdir build -p
OUT_ZIP=build/unimixins-modular-1.7.10-$(git describe --tags --dirty).zip
rm -f $OUT_ZIP
zip -j $OUT_ZIP $(find -name "_unimixins-*.jar" | grep "build/libs" | grep -v sources | grep -v "module-all" | xargs)
+68 -13
View File
@@ -26,11 +26,16 @@ allprojects {
apply plugin: 'maven-publish'
// For some reason this is needed when using Gradle 6.9 (but not if using 4.4.1)
repositories {
mavenCentral()
mavenLocal()
maven {
name 'Overmind forge repo mirror'
url 'https://gregtech.overminddl1.com/'
}
mavenLocal()
maven {
name = "forge"
url = "https://maven.minecraftforge.net/"
}
maven {
name = 'sponge'
url = 'https://repo.spongepowered.org/maven/'
@@ -49,8 +54,22 @@ allprojects {
}
apply from: "${project.rootDir}/buildscript/forge-1.7.gradle"
def projectVersion = version
if(project.name.startsWith("module-") && project.name != "module-mixin") {
dependencies {
// TODO only include IFMLLoadingPlugin, Mod and ComparableVersion
compileOnly "net.minecraftforge:forge:1.12.2-14.23.5.2860:universal"
}
// When publishing locally, publish every module and use the -1.7.10 suffix.
// Otherwise (for GTNH maven) only publish the -all module and don't use the suffix or the -all name or the unimixins group.
ext.localPublish = System.getenv("MAVEN_USER") == null;
ext.publishModuleToMaven = project.name == "module-all" || localPublish
ext.mavenIdSuffix = localPublish ? "-1.7.10" : ""
ext.mavenGroupId = localPublish ? project.group : project.group.replace(".unimixins", "")
if(project.name.startsWith("module-") && project.name != "module-mixin" && project.name != "module-common") {
if(project.name != "module-all") {
task sourcesJar(type: Jar) {
from sourceSets.main.allSource
@@ -59,23 +78,62 @@ allprojects {
}
afterEvaluate {
if(project.name != "module-all") {
task devJar(type: Jar) {
classifier = 'dev'
def jarTask = (jar.enabled ? jar : shadowJar)
from zipTree(jarTask.getOutputs().getFiles().getSingleFile())
manifest = jarTask.manifest
dependsOn jarTask
}
}
reobf.dependsOn devJar
artifacts {
archives tasks."sourcesJar"
archives tasks."devJar"
}
if(publishModuleToMaven) {
publishing {
publications {
create("maven", MavenPublication) {
artifact jar.enabled ? jar : shadowJar
artifact source: tasks."sourcesJar", classifier: 'sources'
artifact source: tasks."devJar", classifier: 'dev'
artifactId = archivesBaseName.substring(1).replace("-1.7.10", mavenIdSuffix).replace("-all", localPublish ? "-all" : "")
version = projectVersion
groupId = mavenGroupId
}
}
}
}
publishing {
publications {
create("maven", MavenPublication) {
artifact jar.enabled ? jar : shadowJar
artifact source: tasks."sourcesJar", classifier: 'sources'
artifactId = archivesBaseName
version = jar.version
project.tasks.forEach {
if(it.name.startsWith("publish") && it.name.contains("PublicationTo")) {
it.dependsOn reobf
}
}
}
}
publishing {
repositories {
if (System.getenv("MAVEN_USER") != null) {
maven {
url = System.getenv("MAVEN_URL")
allowInsecureProtocol = System.getenv("MAVEN_URL").startsWith("http://") // Mostly for the GTNH maven
credentials {
username = System.getenv("MAVEN_USER") ?: "NONE"
password = System.getenv("MAVEN_PASSWORD") ?: "NONE"
}
}
}
}
}
// Put license, credits and readme in jar
delete "$projectDir/build/tmp/license"
@@ -88,6 +146,3 @@ allprojects {
sourceSets.main.resources.srcDir("build/tmp/license")
}
jar.enabled = false
reobf.enabled = false
Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 46 KiB

+15 -15
View File
@@ -1,18 +1,18 @@
| | SpongeMixins | Grimoire | MBL | GasStation | GTNHMixins | GTNHMixinsLite | UniMixins |
| -- | -- | -- | -- | -- | -- | -- | -- |
| Mixin 0.7 | ✅ (0.7.11/0.7.12) | ✅ (0.7.11) | | ✴️ (Partial compat) | | | ✴️ (Partial compat) |
| Mixin 0.8 | | | ✅ (0.8.2) | ✅ (0.8.5-GasMix) | ✅ (0.8.5-GTNH) | | ✅ (0.8.5-UniMix)
| ASM (legacy package) | ✅ (5.2) | ✅ (5.2) | | ✅ (5.2) | | | ✅ (9.4) |
| ASM (relocated) | | | ✅ (5.2) | | ✅ (9.4) | | ✴️ (Emulated) |
| SpongeMixins extras | ✅ | | | ✅ | ✅ | | ✅ |
| Grimoire extras | | ✅ | | | | | |
| MixinBooterLegacy extras | | | ✅ | ✅ | | | ✅ |
| GasStation extras <sup>[NOP]</sup> | | | | ✅ | | | ✅ |
| MixinExtras (upstream package) | | | | ✅ | | | ✅ |
| MixinExtras (MixinBooterLegacy package) | | | ✅ | | | | ✅ |
| MixinExtras (GTNH package) | | | | | ✅ | ✅ | ✅ |
| GTNHMixins extras | | | | | ✅ | ✅ | ✅ |
| Mixingasm <sup>[SEP]</sup> | | | | ✅ | | | ✅ |
| | SpongeMixins | Grimoire | MBL | GasStation | GTNHMixins | UniMixins |
| -- | -- | -- | -- | -- | -- | -- |
| Mixin 0.7 | ✅ (0.7.11/0.7.12) | ✅ (0.7.11) | | ✴️ (Partial compat) | | ✴️ (Partial compat) |
| Mixin 0.8 | | | ✅ (0.8.2) | ✅ (0.8.5-GasMix) | ✅ (0.8.5-GTNH) | ✅ (0.8.5-UniMix)
| ASM (legacy package) | ✅ (5.2) | ✅ (5.2) | | ✅ (5.2) | | ✅ (9.4) |
| ASM (relocated) | | | ✅ (5.2) | | ✅ (9.4) | ✴️ (Emulated) |
| SpongeMixins extras | ✅ | | | ✅ | ✅ | ✅ |
| Grimoire extras | | ✅ | | | | |
| MixinBooterLegacy extras | | | ✅ | ✅ | | ✅ |
| GasStation extras <sup>[NOP]</sup> | | | | ✅ | | ✅ |
| MixinExtras (upstream package) | | | | ✅ | | ✅ |
| MixinExtras (MixinBooterLegacy package) | | | ✅ | | | ✅ |
| MixinExtras (GTNH package) | | | | | ✅ | ✅ |
| GTNHMixins extras | | | | | ✅ | ✅ |
| Mixingasm <sup>[SEP]</sup> | | | | ✅ | | ✅ |
* <sup>[NOP]</sup>: Module does not do anything. [(issue)](https://github.com/FalsePattern/GasStation/issues/15)
* <sup>[SEP]</sup>: Module is available as a separate mod.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 47 KiB

+51 -42
View File
@@ -1,59 +1,68 @@
# Developer usage
To migrate a build script to UniMixins, merge the following into your build script.
An example mod showcasing the mod's various features is available in the `example-*` branches of the repo:
- [`example-fg-g4`](https://github.com/LegacyModdingMC/UniMixins/tree/example-fg-g4) - ForgeGradle-based build script with Gradle 4
- [`example-fg-g6`](https://github.com/LegacyModdingMC/UniMixins/tree/example-fg-g6) - ForgeGradle-based build script with Gradle 6
> If using old Gradle, replace each occurrence of `implementation` with `compile`, and ignore the `annotationProcessor` line (in old Gradle, `compile` dependencies are automatically put on the annotation processor classpath).
Below are instructions for migrating build scripts to use UniMixins.
```gradle
repositories {
maven {
url 'https://jitpack.io'
}
}
## RetroFuturaGradle
def unimixinsVersion = "insert version here"
The easiest way to use UniMixins is to use [GTNH's ExampleMod](https://github.com/GTNewHorizons/ExampleMod1.7.10), which has UniMixins integration built-in.
dependencies {
annotationProcessor("com.github.LegacyModdingMC.UniMixins:_unimixins-all-1.7.10:$unimixinsVersion")
implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-all-1.7.10:$unimixinsVersion")
}
## ForgeGradle
// Exclude conflicting transitive dependencies.
// Only needed if you depend on a mod that depends on one of these.
// This may need to be at the end of the build script.
configurations.compile.dependencies.each {
if (it instanceof ExternalModuleDependency) {
it.exclude module: "SpongeMixins"
it.exclude module: "SpongePoweredMixin"
it.exclude module: "00gasstation-mc1.7.10"
it.exclude module: "gtnhmixins"
}
}
```
Refer to the example mod's build script ([Gradle 4](https://github.com/LegacyModdingMC/UniMixins/blob/example-fg-g4/build.gradle#L79) | [Gradle 6](https://github.com/LegacyModdingMC/UniMixins/blob/example-fg-g6/build.gradle#L91)).
You can also depend on modules individually:
```gradle
dependencies {
// One of these
implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-mixin-1.7.10:$unimixinsVersion+unimix-0.11.5+mixin.0.8.5")
//implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-mixin-1.7.10:$unimixinsVersion+spongepowered-0.8.5")
//implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-mixin-1.7.10:$unimixinsVersion+fabric-0.11.4+mixin.0.8.5")
//implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-mixin-1.7.10:$unimixinsVersion+gasmix-0.8.5-gasstation_7")
//implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-mixin-1.7.10:$unimixinsVersion+gtnh-0.8.5-GTNH-2")
implementation("com.github.LegacyModdingMC.UniMixins:unimixins-mixin-1.7.10:$unimixinsVersion")
// You don't need all the modules, only the ones your mod requires.
implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-compat-1.7.10:$unimixinsVersion")
implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-spongemixins-1.7.10:$unimixinsVersion+gtnh-2.0.1")
implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-mixinbooterlegacy-1.7.10:$unimixinsVersion+1.2.0")
implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-gasstation-1.7.10:$unimixinsVersion+0.5.1")
implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-mixinextras-1.7.10:$unimixinsVersion+0.1.1")
implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-gtnhmixins-1.7.10:$unimixinsVersion+2.1.9")
implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-mixingasm-1.7.10:$unimixinsVersion+0.2.2")
// Needed if not using UniMix or GTNH's Mixin fork
//implementation("com.github.LegacyModdingMC.UniMixins:_unimixins-devcompat-1.7.10:$unimixinsVersion")
implementation("com.github.LegacyModdingMC.UniMixins:unimixins-compat-1.7.10:$unimixinsVersion:dev")
implementation("com.github.LegacyModdingMC.UniMixins:unimixins-spongemixins-1.7.10:$unimixinsVersion:dev")
implementation("com.github.LegacyModdingMC.UniMixins:unimixins-mixinbooterlegacy-1.7.10:$unimixinsVersion:dev")
implementation("com.github.LegacyModdingMC.UniMixins:unimixins-gasstation-1.7.10:$unimixinsVersion:dev")
implementation("com.github.LegacyModdingMC.UniMixins:unimixins-mixinextras-1.7.10:$unimixinsVersion:dev")
implementation("com.github.LegacyModdingMC.UniMixins:unimixins-gtnhmixins-1.7.10:$unimixinsVersion:dev")
implementation("com.github.LegacyModdingMC.UniMixins:unimixins-mixingasm-1.7.10:$unimixinsVersion:dev")
}
```
<details>
<summary>Testing info</summary>
If you want to depend on a local build, you need to use `io.github.legacymoddingmc.unimixins` instead of `com.github.LegacyModdingMC.UniMixins`.
And here's how you depend on the Very Optional modules (not published on Jitpack).
```gradle
//implementation("io.github.legacymoddingmc.unimixins:unimixins-mixin-1.7.10-spongepowered:$unimixinsVersion")
//implementation("io.github.legacymoddingmc.unimixins:unimixins-mixin-1.7.10-fabric:$unimixinsVersion")
//implementation("io.github.legacymoddingmc.unimixins:unimixins-mixin-1.7.10-gasmix:$unimixinsVersion")
//implementation("io.github.legacymoddingmc.unimixins:unimixins-mixin-1.7.10-gtnh:$unimixinsVersion")
```
Note: If you are *not* using UniMix or GTNH's Mixin fork, you will need to add the compat module and set the `-Dunimixins.compat.hackClasspathModDiscovery=true` JVM flag, or Forge may fail to discover your mod or its dependencies.
</details>
## Tricks
### Hot swapping
Mixin hot swapping can be enabled in a similar way to how it works on [Fabric](https://fabricmc.net/wiki/tutorial:mixin_hotswaps). You just have to add the UniMixins jar (or the matching UniMix jar) as a java agent.
Here's a buildscript snippet that will set it up for you (tested with the GTNH ExampleMod):
```gradle
afterEvaluate {
File uni = configurations.compileClasspath.findAll { it.name.contains("unimixins-all-") || it.name.contains("unimixins-mixin-") || it.name.contains("unimixins-0.") }.first()
runClient {
extraJvmArgs.add(
'-javaagent:' + uni.getPath()
)
}
}
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

+712
View File
@@ -0,0 +1,712 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="272mm"
height="98.300003mm"
viewBox="0 0 272 98.300003"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="modularity.svg"
inkscape:export-filename="modularity.png"
inkscape:export-xdpi="300"
inkscape:export-ydpi="300"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="mm"
height="9.83cm"
inkscape:zoom="1.8116734"
inkscape:cx="707.08107"
inkscape:cy="100.45961"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="false" />
<defs
id="defs2">
<pattern
inkscape:collect="always"
xlink:href="#Strips1_4"
id="pattern73765"
patternTransform="matrix(-0.41147911,-0.41147911,2.1055851,-2.1055851,28.02852,-37.612396)" />
<pattern
inkscape:collect="always"
patternUnits="userSpaceOnUse"
width="5"
height="1"
patternTransform="translate(0,0) scale(10,10)"
id="Strips1_4"
inkscape:stockid="Stripes 1:4"
inkscape:isstock="true">
<rect
style="fill:black;stroke:none"
x="0"
y="-0.5"
width="1"
height="2"
id="rect2974" />
</pattern>
<rect
x="67.893028"
y="19.871132"
width="217.25429"
height="52.39732"
id="rect6184" />
<filter
style="color-interpolation-filters:sRGB"
inkscape:label="Duochrome"
id="filter75351"
x="0"
y="0"
width="1"
height="1">
<feColorMatrix
type="luminanceToAlpha"
result="colormatrix1"
id="feColorMatrix75331" />
<feFlood
flood-opacity="0.501961"
flood-color="rgb(229,229,229)"
result="flood1"
id="feFlood75333" />
<feComposite
in2="colormatrix1"
operator="out"
result="composite1"
id="feComposite75335" />
<feFlood
flood-opacity="1"
flood-color="rgb(255,255,0)"
result="flood2"
id="feFlood75337" />
<feComposite
in2="colormatrix1"
result="composite2"
operator="in"
id="feComposite75339" />
<feComposite
in="composite2"
in2="composite1"
k2="1"
k3="1"
operator="arithmetic"
result="composite3"
id="feComposite75341"
k1="0"
k4="0" />
<feColorMatrix
in="composite3"
type="matrix"
values="2 -1 0 0 0 0 2 -1 0 0 -1 0 2 0 0 0 0 0 1 0 "
result="colormatrix2"
id="feColorMatrix75343" />
<feComposite
in="colormatrix2"
in2="composite3"
operator="arithmetic"
k2="0"
result="composite4"
id="feComposite75345"
k1="0"
k3="0"
k4="0" />
<feBlend
in="composite4"
in2="composite3"
mode="normal"
result="blend"
id="feBlend75347" />
<feComposite
in2="SourceGraphic"
operator="in"
id="feComposite75349" />
</filter>
<rect
x="67.893028"
y="19.871132"
width="191.31142"
height="45.221634"
id="rect6184-8" />
<rect
x="67.893028"
y="19.871132"
width="322.4542"
height="53.027752"
id="rect6184-8-4" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="display:inline;fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7"
width="63.608315"
height="8.1022758"
x="13.800014"
y="46.707821" />
<rect
style="display:inline;fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8"
width="19.243679"
height="8.0107975"
x="36.178707"
y="59.424469" />
<rect
style="display:inline;fill:url(#pattern73765);fill-opacity:1;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter75351);stop-color:#000000"
id="rect1104-0-0"
width="21.420921"
height="8.0959282"
x="34.857468"
y="46.708447"
ry="0" />
<rect
style="display:inline;fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-5"
width="21.420921"
height="8.0959272"
x="34.857468"
y="46.708447" />
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,0.12802956,1.2560112)"
id="text6182"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.3333px;line-height:1.05;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';text-align:center;letter-spacing:-0.5px;white-space:pre;shape-inside:url(#rect6184);display:inline;fill:#000000;fill-opacity:1;stroke:none"
x="80.298035"
y="0"><tspan
x="73.532947"
y="35.357673"
id="tspan326524">EXISTING MIXIN LOADERS
</tspan><tspan
x="106.52431"
y="57.757637"
id="tspan326526">ARE MONOLITHIC.</tspan></text>
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,90.941647,2.4661355)"
id="text6182-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.3333px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';text-align:center;letter-spacing:-0.5px;white-space:pre;shape-inside:url(#rect6184-8);display:inline;fill:#000000;fill-opacity:1;stroke:none"
x="80.298035"
y="0"><tspan
x="70.094129"
y="37.491004"
id="tspan326528">UNIMIXINS IS MODULAR.</tspan></text>
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,166.16598,1.3341978)"
id="text6182-6-7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:21.3333px;line-height:1.05;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script Bold';text-align:center;letter-spacing:-0.6px;white-space:pre;shape-inside:url(#rect6184-8-4);display:inline;fill:#000000;fill-opacity:1;stroke:none"
x="80.298035"
y="0"><tspan
x="90.91602"
y="35.357673"
id="tspan326532"><tspan
style="font-weight:normal;-inkscape-font-specification:'xkcd Script'"
id="tspan326530">UNIMIXINS MODULES CAN EVEN BE </tspan></tspan><tspan
x="72.625018"
y="57.757637"
id="tspan326536"><tspan
style="font-weight:normal;-inkscape-font-specification:'xkcd Script'"
id="tspan326534">ADDED ON TOP OF ANOTHER LOADER.</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;line-height:1.05;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';text-align:center;letter-spacing:-0.185208px;text-anchor:middle;white-space:pre;inline-size:82.321;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="44.71722"
y="24.065899"
id="text32884"
transform="translate(0.52916667,1.5875)"><tspan
x="44.71722"
y="24.065899"
id="tspan326538">THEY CANNOT BE LOADED AT THE SAME TIME DUE TO<tspan
y="24.065899"
id="tspan326540"> </tspan></tspan><tspan
x="44.71722"
y="27.770067"
id="tspan326542">FEATURE OVERLAP, EVEN THOUGH THEIR INDIVIDUAL FEATURES<tspan
y="27.770067"
id="tspan326544"> </tspan></tspan><tspan
x="44.71722"
y="31.474235"
id="tspan326546">WOULD BE ABLE TO CO-EXIST.</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;line-height:1.05;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';text-align:center;letter-spacing:-0.132292px;text-anchor:middle;white-space:pre;inline-size:89.1681;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="134.89743"
y="23.701571"
id="text32884-1"
transform="translate(0,1.0583333)"><tspan
x="134.89743"
y="23.701571"
id="tspan326548">EACH FEATURE IS IN ITS OWN &quot;BOX&quot;, ALLOWING THEM TO BE<tspan
y="23.701571"
id="tspan326550"> </tspan></tspan><tspan
x="134.89743"
y="27.405738"
id="tspan326552">COMBINED FREELY WITHOUT ANY ARBITRARY RESTRICTIONS.</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.89278px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';text-align:center;letter-spacing:-0.132292px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="225.63924"
y="24.60577"
id="text32884-1-9"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.89278px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';text-align:center;text-anchor:middle;stroke-width:0.264583"
x="225.57309"
y="24.60577"
id="tspan116154-7">(THIS KIND OF SUPPORT IS NOT FULLY IMPLEMENTED THOUGH.)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-0.079375px;display:inline;fill:#3f48cc;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="23.71513"
y="42.778732"
id="text63580"><tspan
sodipodi:role="line"
id="tspan63578"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#3f48cc;fill-opacity:1;stroke-width:0.264583"
x="23.71513"
y="42.778732">GTNHMIXINS</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="27.273415"
y="50.070621"
id="text63580-6"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="27.273415"
y="50.070621"
id="tspan76477">MIXIN</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="27.233727"
y="53.160957"
id="tspan189479">0.8-GTNH</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="45.815434"
y="62.721596"
id="text63580-6-89"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="45.815434"
y="62.721596"
id="tspan104620">SPONGEMIXINS</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="45.775745"
y="65.811935"
id="tspan199017">EXTRAS</tspan></text>
<rect
style="display:inline;fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8-3"
width="19.243679"
height="8.0107975"
x="13.745136"
y="72.354515" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="23.381863"
y="75.724663"
id="text63580-6-89-5"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="23.381863"
y="75.724663"
id="tspan105830">GTNHMIXINS</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="23.342175"
y="78.815002"
id="tspan195563">EXTRAS</tspan></text>
<rect
style="display:inline;fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8-3-4"
width="19.243679"
height="8.0107975"
x="58.160408"
y="72.281502" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="67.79715"
y="75.72467"
id="text63580-6-89-5-1"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="67.79715"
y="75.72467"
id="tspan106818">GASSTATION</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="67.757462"
y="78.81501"
id="tspan197827">EXTRAS</tspan></text>
<rect
style="display:inline;fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8-3-4-9"
width="19.243679"
height="8.0107975"
x="118.16634"
y="38.274223" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="127.80308"
y="41.717392"
id="text63580-6-89-5-1-2"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="127.80308"
y="41.717392"
id="tspan116603">MIXIN</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="127.76339"
y="44.807728"
id="tspan206609">0.8-UNIMIX</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="63.996986"
y="50.144955"
id="text63580-6-8"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="63.996986"
y="50.144955"
id="tspan76477-8">MIXIN</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="63.957298"
y="53.235291"
id="tspan192367">0.8-GASMIX</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-0.079375px;display:inline;fill:#ed1c24;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="50.883247"
y="42.739273"
id="text63580-0"><tspan
sodipodi:role="line"
id="tspan63578-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ed1c24;fill-opacity:1;stroke-width:0.264583"
x="50.883247"
y="42.739273">GASSTATION</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-0.079375px;display:inline;fill:#ed1c24;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="141.59998"
y="43.174267"
id="text63580-0-3"><tspan
sodipodi:role="line"
id="tspan63578-4-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#22b14c;fill-opacity:1;stroke-width:0.264583"
x="141.59998"
y="43.174267">MIXIN MODULE</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8-3-4-9-9"
width="19.243679"
height="8.0107975"
x="118.16634"
y="51.642021" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="127.80305"
y="55.08519"
id="text63580-6-89-5-1-2-1"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="127.80305"
y="55.08519"
id="tspan132092">SPONGEMIXINS</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="127.76336"
y="58.175526"
id="tspan207303">EXTRAS</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-0.079375px;display:inline;fill:#ed1c24;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="141.59996"
y="56.542065"
id="text63580-0-3-3"><tspan
sodipodi:role="line"
id="tspan63578-4-0-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#22b14c;fill-opacity:1;stroke-width:0.264583"
x="141.59996"
y="56.542065">SPONGEMIXINS MODULE</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8-3-4-9-9-3"
width="19.243679"
height="8.0107975"
x="118.16635"
y="64.523148" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="127.80308"
y="67.966316"
id="text63580-6-89-5-1-2-1-5"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="127.80308"
y="67.966316"
id="tspan126306">GTNHMIXINS</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="127.76339"
y="71.056656"
id="tspan207993">EXTRAS</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-0.079375px;display:inline;fill:#ed1c24;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="141.59998"
y="69.423195"
id="text63580-0-3-3-4"><tspan
sodipodi:role="line"
id="tspan63578-4-0-1-2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#22b14c;fill-opacity:1;stroke-width:0.264583"
x="141.59998"
y="69.423195">GTNHMIXINS MODULE</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8-3-4-9-9-3-8"
width="19.243679"
height="8.0107975"
x="118.16635"
y="77.124695" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="127.80308"
y="80.567863"
id="text63580-6-89-5-1-2-1-5-7"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="127.80308"
y="80.567863"
id="tspan128146">GASSTATION</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="127.76339"
y="83.658203"
id="tspan208641">EXTRAS</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-0.079375px;display:inline;fill:#ed1c24;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="141.59998"
y="82.024742"
id="text63580-0-3-3-4-9"><tspan
sodipodi:role="line"
id="tspan63578-4-0-1-2-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#22b14c;fill-opacity:1;stroke-width:0.264583"
x="141.59998"
y="82.024742">GASSTATION MODULE</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8-3-4-9-0"
width="19.243679"
height="8.0107975"
x="207.60686"
y="41.695419" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="217.24359"
y="45.138588"
id="text63580-6-89-5-1-2-5"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="217.24359"
y="45.138588"
id="tspan116603-60">MIXIN</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="217.2039"
y="48.228924"
id="tspan213995">0.7</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-0.079375px;display:inline;fill:#ed1c24;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="211.10968"
y="38.747059"
id="text63580-0-3-1"><tspan
sodipodi:role="line"
id="tspan63578-4-0-2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#a349a4;fill-opacity:1;stroke-width:0.264583"
x="211.10968"
y="38.747059">GRIMOIRE</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8-3-4-9-9-5"
width="19.243679"
height="8.0107975"
x="207.60686"
y="55.063217" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="217.24358"
y="58.506386"
id="text63580-6-89-5-1-2-1-2"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="217.24358"
y="58.506386"
id="tspan138056">GRIMOIRE</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="217.20389"
y="61.596722"
id="tspan214617">EXTRAS</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000046;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0-7-8-3-4-9-9-3-1"
width="19.243679"
height="8.0107975"
x="207.60687"
y="67.944344" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;line-height:1.2;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-0.079375px;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="217.24359"
y="71.387512"
id="text63580-6-89-5-1-2-1-5-1"><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="217.24359"
y="71.387512"
id="tspan126306-5">GTNHMIXINS</tspan><tspan
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.57528px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke-width:0.264583"
x="217.2039"
y="74.477852"
id="tspan215145">EXTRAS</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;line-height:1.25;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-0.079375px;display:inline;fill:#ed1c24;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="231.0405"
y="72.844391"
id="text63580-0-3-3-4-1"><tspan
sodipodi:role="line"
id="tspan63578-4-0-1-2-7"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;font-family:'xkcd Script';-inkscape-font-specification:'xkcd Script';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#22b14c;fill-opacity:1;stroke-width:0.264583"
x="231.0405"
y="72.844391">GTNHMIXINS MODULE</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="134.45549"
y="11.566064"
id="text109002"><tspan
sodipodi:role="line"
id="tspan109000"
style="stroke-width:0.264583"
x="134.45549"
y="11.566064"></tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect261836"
width="87.778175"
height="94.800415"
x="1.5918018"
y="1.5918018" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect261836-0"
width="87.778175"
height="94.800415"
x="92.015823"
y="1.5918018" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect261836-0-8"
width="87.778175"
height="94.800415"
x="182.40633"
y="1.6533977" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Layer 2"
sodipodi:insensitive="true">
<rect
style="display:inline;fill:#3f48cc;fill-opacity:0.17;stroke:#3f48cc;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-0"
width="45.063374"
height="37.911438"
x="11.578111"
y="44.065044" />
<rect
style="display:inline;fill:#ed1c24;fill-opacity:0.17;stroke:#ed1c24;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104"
width="45.040031"
height="37.936855"
x="34.465179"
y="44.840611" />
<rect
style="display:inline;fill:#2fa755;fill-opacity:0.17;stroke:#22b14c;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-3"
width="23.043865"
height="11.087069"
x="116.1311"
y="36.860748" />
<rect
style="display:inline;fill:#2fa755;fill-opacity:0.17;stroke:#22b14c;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-3-0"
width="23.043865"
height="11.087069"
x="116.1311"
y="50.228546" />
<rect
style="display:inline;fill:#2fa755;fill-opacity:0.17;stroke:#22b14c;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-3-0-9"
width="23.043865"
height="11.087069"
x="116.1311"
y="63.109657" />
<rect
style="display:inline;fill:#2fa755;fill-opacity:0.17;stroke:#22b14c;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-3-0-9-0"
width="23.043865"
height="11.087069"
x="116.1311"
y="75.71122" />
<rect
style="display:inline;fill:#a349a4;fill-opacity:0.17;stroke:#a349a4;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-3-7"
width="23.043869"
height="24.047258"
x="205.57162"
y="40.281944" />
<rect
style="display:inline;fill:#2fa755;fill-opacity:0.17;stroke:#22b14c;stroke-width:0.4;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stop-color:#000000"
id="rect1104-3-0-9-4"
width="23.043865"
height="11.087069"
x="205.57162"
y="66.530869" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 44 KiB

+12 -11
View File
@@ -15,8 +15,9 @@ All test cases assume default configs for mods.
* Witchery is not a coremod, so if this mixin works, that means [adding it to the classpath early](https://github.com/jss2a98aj/BugTorch/blob/95480e11c5ea922f4cc2a7540ca944ef4a9aeaec/src/main/java/jss/bugtorch/core/BugTorchMixinPlugin.java#L149-L166) was successful.
* [x] MixinBooterLegacy
* ❔ Early loader
* No mods are known to use this.
* [x] Early loader
* [x] [forge-network-fix @ 340585e](https://github.com/tox1cozZ/forge-network-fix/tree/340585e66937fc14df0cf9ac22b19f5b1263c4b7)
* [x] `Mixing MixinFMLIndexedMessageToMessageCodec from mixin.forgenetworkfix.json` appears in `fml-server-latest.log`
* ❔ Late loader
* No mods are known to use this.
* ❔ MixinExtras (MixinBooterLegacy package name)
@@ -24,7 +25,7 @@ All test cases assume default configs for mods.
* [x] GasStation
* [x] Mod annotation
* [x] [RightProperGUIScale 2.0.2](https://github.com/basdxz/RightProperGUIScale/releases/download/2.0.2/rightproperguiscale-mc1.7.10-2.0.2.jar) + [FalsePatternLib 0.10.14](https://github.com/FalsePattern/FalsePatternLib/releases/download/0.10.14/falsepatternlib-mc1.7.10-0.10.14.jar)
* [x] [RightProperGUIScale 2.1.0](https://github.com/basdxz/RightProperGUIScale/releases/download/2.1.0/RightProperGUIScale-mc1.7.10-2.1.0.jar) + [FalsePatternLib 0.11.4](https://github.com/FalsePattern/FalsePatternLib/releases/download/0.11.4/falsepatternlib-mc1.7.10-0.11.4.jar)
* [x] GUI Scale slider can be set to fractional values.
* The mod hard depends on GasStation in [its mod annotation](https://github.com/basdxz/RightProperGUIScale/blob/a14814a00f7f360491f72367c40fcb31341abf7e/src/main/java/com/github/basdxz/rightproperguiscale/Tags.java#L17).
* ❔ Early loader
@@ -38,25 +39,25 @@ All test cases assume default configs for mods.
* [x] GTNHMixins
* [x] Mod annotation
* [x] [Hodgepodge 2.0.27](https://github.com/GTNewHorizons/Hodgepodge/releases/download/2.0.27/hodgepodge-1.7.10-2.0.27.jar) + [GTNHLib 0.1.1](https://github.com/GTNewHorizons/GTNHLib/releases/download/0.0.11/gtnhlib-1.7.10-0.0.11.jar)
* [x] Chat background is transparent
* [x] [Hodgepodge 2.0.27](https://github.com/GTNewHorizons/Hodgepodge/releases/download/2.0.27/hodgepodge-1.7.10-2.0.27.jar) + [GTNHLib 0.0.11](https://github.com/GTNewHorizons/GTNHLib/releases/download/0.0.11/gtnhlib-1.7.10-0.0.11.jar)
* [x] Chat background is transparent when unfocused
* The mod hard depends on GTNHMixins in [its mod annotation](https://github.com/GTNewHorizons/Hodgepodge/blob/035015166f138ab56c5d5c5acd8d13dad79f95fc/src/main/java/com/mitchej123/hodgepodge/Hodgepodge.java#L21).
* [x] Early loader
* [x] [Hodgepodge 2.0.27](https://github.com/GTNewHorizons/Hodgepodge/releases/download/2.0.27/hodgepodge-1.7.10-2.0.27.jar) + [GTNHLib 0.1.1](https://github.com/GTNewHorizons/GTNHLib/releases/download/0.0.11/gtnhlib-1.7.10-0.0.11.jar)
* [x] Chat background is transparent
* [x] [Hodgepodge 2.0.27](https://github.com/GTNewHorizons/Hodgepodge/releases/download/2.0.27/hodgepodge-1.7.10-2.0.27.jar) + [GTNHLib 0.0.11](https://github.com/GTNewHorizons/GTNHLib/releases/download/0.0.11/gtnhlib-1.7.10-0.0.11.jar)
* [x] Chat background is transparent when unfocused
* An early loader is used to [load all early mixins](https://github.com/GTNewHorizons/Hodgepodge/blob/035015166f138ab56c5d5c5acd8d13dad79f95fc/src/main/java/com/mitchej123/hodgepodge/core/HodgepodgeCore.java#L35-L50), [including this one](https://github.com/GTNewHorizons/Hodgepodge/blob/2.0.27/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java#L98-L103).
* [x] Late loader
* [x] [Hodgepodge 2.0.27](https://github.com/GTNewHorizons/Hodgepodge/releases/download/2.0.27/hodgepodge-1.7.10-2.0.27.jar) + [GTNHLib 0.1.1](https://github.com/GTNewHorizons/GTNHLib/releases/download/0.0.11/gtnhlib-1.7.10-0.0.11.jar) + [Baubles 1.0.1.16](https://github.com/GTNewHorizons/Baubles/releases/download/1.0.1.16/Baubles-1.0.1.16.jar)
* [x] [Hodgepodge 2.0.27](https://github.com/GTNewHorizons/Hodgepodge/releases/download/2.0.27/hodgepodge-1.7.10-2.0.27.jar) + [GTNHLib 0.0.11](https://github.com/GTNewHorizons/GTNHLib/releases/download/0.0.11/gtnhlib-1.7.10-0.0.11.jar) + [Baubles 1.0.1.16](https://github.com/GTNewHorizons/Baubles/releases/download/1.0.1.16/Baubles-1.0.1.16.jar)
* [x] When the inventory is opened while a potion effect is active, the inventory GUI stays centered.
* A late loader is used to [load all late mixins](https://github.com/GTNewHorizons/Hodgepodge/blob/2.0.27/src/main/java/com/mitchej123/hodgepodge/HodgepodgeLateMixins.java), [including this one](https://github.com/GTNewHorizons/Hodgepodge/blob/2.0.27/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java#L489-L494).
* [x] MixinExtras (GTNHMixins package name)
* [x] [Hodgepodge 2.0.27](https://github.com/GTNewHorizons/Hodgepodge/releases/download/2.0.27/hodgepodge-1.7.10-2.0.27.jar) + [GTNHLib 0.1.1](https://github.com/GTNewHorizons/GTNHLib/releases/download/0.0.11/gtnhlib-1.7.10-0.0.11.jar)
* [x] Chat background is transparent
* [x] [Hodgepodge 2.0.27](https://github.com/GTNewHorizons/Hodgepodge/releases/download/2.0.27/hodgepodge-1.7.10-2.0.27.jar) + [GTNHLib 0.0.11](https://github.com/GTNewHorizons/GTNHLib/releases/download/0.0.11/gtnhlib-1.7.10-0.0.11.jar)
* [x] Chat background is transparent when unfocused
* The [mixin](https://github.com/GTNewHorizons/Hodgepodge/blob/2.0.27/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinGuiNewChat_TransparentChat.java) implementing this feature uses `@WrapWithCondition`.
* [x] Mixingasm
* [x] Transformer exclusion
* [x] [GregTech 6](https://gregtech.overminddl1.com/com/gregoriust/gregtech/gregtech_1.7.10/6.14.20/gregtech_1.7.10-6.14.20.jar) + [CoreTweaks 0.3.0.1+nomixin](https://github.com/makamys/CoreTweaks/releases/download/0.3.0.1/CoreTweaks-1.7.10-0.3.0.1+nomixin.jar) + [BugTorch 1.1.8](https://github.com/jss2a98aj/BugTorch/releases/download/1.1.8/bugtorch-1.7.10-1.1.8.jar)
* [x] [GregTech 6.14.20](https://gregtech.overminddl1.com/com/gregoriust/gregtech/gregtech_1.7.10/6.14.20/gregtech_1.7.10-6.14.20.jar) + [CoreTweaks 0.3.0.1+nomixin](https://github.com/makamys/CoreTweaks/releases/download/0.3.0.1/CoreTweaks-1.7.10-0.3.0.1+nomixin.jar) + [BugTorch 1.1.8](https://github.com/jss2a98aj/BugTorch/releases/download/1.1.8/bugtorch-1.7.10-1.1.8.jar)
* [x] Game reaches title screen
* Without Mixingasm, this mod combination causes an early crash.
+1 -1
View File
@@ -1,6 +1,6 @@
# Mod properties
modid=unimixins
archives_base=_unimixins
archives_base=+unimixins
# http://maven.apache.org/guides/mini/guide-naming-conventions.html
group=io.github.legacymoddingmc.unimixins
+1
View File
@@ -1,2 +1,3 @@
install:
- ./gradlew setupCIWorkspace
- ./gradlew publishToMavenLocal
+89 -58
View File
@@ -5,14 +5,25 @@ archivesBaseName = archivesBaseName.replace("-1.7.10", "-all-1.7.10")
// The order of these determines the order embedded core plugins get initialized in
ext.moduleNames = ["module-mixin", "module-compat", "module-mixingasm", "module-spongemixins", "module-mixinbooterlegacy", "module-gasstation", "module-gtnhmixins", "module-mixinextras"]
def getModuleJar(name, source = false) {
def jarTaskName = name == "module-mixin" ? ":module-mixin:shadow${source ? 'Sources' : ''}JarUnimix" : ":$name:${source ? 'sourcesJar' : 'jar'}"
return tasks.getByPath(jarTaskName).getOutputs().getFiles().getSingleFile()
def getJarTaskName(name, classifier) {
if(name == "module-mixin") {
return ":module-mixin:shadow${classifier == 'sources' ? 'Sources' : ''}JarUnimix"
} else {
def candidate = ":" + name + ":" + (classifier + "Jar").uncapitalize()
if(classifier == 'dev' && (tasks.findByPath(candidate) == null)) {
return getJarTaskName(name, '')
} else {
return candidate
}
}
}
def getModuleJars(source = false) {
return moduleNames.collect { getModuleJar(it, source) }
def getModuleJar(name, classifier = '') {
return tasks.getByPath(getJarTaskName(name, classifier)).getOutputs().getFiles().getSingleFile()
}
def getModuleJars(classifier = '') {
return moduleNames.collect { getModuleJar(it, classifier) }
}
import java.util.jar.Manifest;
@@ -75,9 +86,8 @@ task createMergedAnnotationProcessorConfig() {
doLast {
// MixinExtras's APs have to be registered before Mixin's,
// so we manually merge the APs to ensure this is the case.
file(path).text = """com.llamalad7.mixinextras.MixinExtrasAP
file(path).text = """com.llamalad7.mixinextras.ap.MixinExtrasAP
io.github.tox1cozz.mixinextras.MixinExtrasAnnotationProcessor
com.gtnewhorizon.mixinextras.MixinExtrasAP
org.spongepowered.tools.obfuscation.MixinObfuscationProcessorInjection
org.spongepowered.tools.obfuscation.MixinObfuscationProcessorTargets
"""
@@ -86,64 +96,85 @@ org.spongepowered.tools.obfuscation.MixinObfuscationProcessorTargets
}
}
jar {
dependsOn tasks.createMergedMcmodInfo
dependsOn tasks.createEmbeddedCorePluginsFile
dependsOn tasks.createMergedAnnotationProcessorConfig
moduleNames.each {
def name = it
def j = getModuleJar(it)
into ""
from(zipTree(j)){
exclude 'mcmod.info'
exclude 'META-INF/MANIFEST.MF'
exclude 'CREDITS', 'LICENSE*', 'README*'
exclude 'META-INF/services/javax.annotation.processing.Processor'
into("")
}
from(zipTree(j)){
include 'CREDITS', 'LICENSE*', 'README*'
into("META-INF/licenses/$name")
}
}
from tasks.createMergedMcmodInfo.getOutputs().getFiles().getSingleFile()
from(tasks.createEmbeddedCorePluginsFile.getOutputs().getFiles().getSingleFile()) {
into 'META-INF'
}
from(tasks.createMergedAnnotationProcessorConfig.getOutputs().getFiles().getSingleFile()) {
into 'META-INF/services'
}
manifest {
attributes (
'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
'FMLCorePluginContainsFMLMod': true,
'ForceLoadAsMod': true,
'FMLCorePlugin': 'io.github.legacymoddingmc.unimixins.all.AllCore',
'MixinConfigs': 'mixins.gtnhmixins.json,mixins.gasstation.json,mixins.gtnhmixins.json,mixingasm.mixin.json',
'Premain-Class': 'org.spongepowered.tools.agent.MixinAgent',
'Agent-Class': 'org.spongepowered.tools.agent.MixinAgent',
'Can-Redefine-Classes': true,
'Can-Retransform-Classes': true,
)
}
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
doFirst {
manifest.attributes (
'Implementation-Version': getManifest(getModuleJar("module-mixin")).getValue("Implementation-Version")
)
shadowJar {
classifier = ''
}
task devJar(type: ShadowJar) {
classifier = 'dev'
}
tasks.withType(ShadowJar) {
if(name == "shadowJar" || name == "devJar") {
dependsOn tasks.createMergedMcmodInfo
dependsOn tasks.createEmbeddedCorePluginsFile
dependsOn tasks.createMergedAnnotationProcessorConfig
from sourceSets.main.output
configurations = [project.configurations.shadow]
relocate 'io.github.legacymoddingmc.unimixins.common', 'io.github.legacymoddingmc.unimixins.all.repackage.common'
(["module-common"] + moduleNames).each {
def name = it
def j = getModuleJar(it, classifier)
into ""
from(zipTree(j)){
exclude 'mcmod.info'
exclude 'META-INF/MANIFEST.MF'
exclude 'CREDITS', 'LICENSE*', 'README*'
exclude 'META-INF/services/javax.annotation.processing.Processor'
into("")
}
from(zipTree(j)){
include 'CREDITS', 'LICENSE*', 'README*'
into("META-INF/licenses/$name")
}
}
from tasks.createMergedMcmodInfo.getOutputs().getFiles().getSingleFile()
from(tasks.createEmbeddedCorePluginsFile.getOutputs().getFiles().getSingleFile()) {
into 'META-INF'
}
from(tasks.createMergedAnnotationProcessorConfig.getOutputs().getFiles().getSingleFile()) {
into 'META-INF/services'
}
manifest {
attributes (
'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
'FMLCorePluginContainsFMLMod': true,
'ForceLoadAsMod': true,
'FMLCorePlugin': 'io.github.legacymoddingmc.unimixins.all.AllCore',
'MixinConfigs': 'mixins.gtnhmixins.json,mixins.gasstation.json,mixins.gtnhmixins.json,mixingasm.mixin.json',
'Premain-Class': 'org.spongepowered.tools.agent.MixinAgent',
'Agent-Class': 'org.spongepowered.tools.agent.MixinAgent',
'Can-Redefine-Classes': true,
'Can-Retransform-Classes': true,
)
}
doFirst {
manifest.attributes (
'Implementation-Version': getManifest(getModuleJar("module-mixin")).getValue("Implementation-Version")
)
}
}
}
jar {
enabled = false
dependsOn shadowJar, devJar
}
task sourcesJar(type: ShadowJar) {
dependsOn jar
dependsOn devJar
classifier = 'sources'
moduleNames.each {
def j = getModuleJar(it, true)
def j = getModuleJar(it, 'sources')
from(zipTree(j)){
exclude 'mcmod.info'
exclude 'META-INF/MANIFEST.MF'
@@ -152,7 +183,7 @@ task sourcesJar(type: ShadowJar) {
}
}
from(zipTree(tasks.jar.getOutputs().getFiles().getSingleFile())) {
from(zipTree(tasks.devJar.getOutputs().getFiles().getSingleFile())) {
include "CREDITS"
include "META-INF/licenses/**"
}
@@ -1,15 +1,10 @@
package io.github.legacymoddingmc.unimixins.all;
import java.io.File;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
import net.minecraft.launchwrapper.Launch;
import org.apache.commons.io.FileUtils;
import net.minecraft.launchwrapper.IClassTransformer;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -22,21 +17,7 @@ public class AllCore implements IFMLLoadingPlugin {
private static List<Class<?>> embeddedCorePluginClasses = new ArrayList<>();
private static List<IFMLLoadingPlugin> embeddedCorePluginInstances = new ArrayList<>();
private static List<String> CONCERNING_JAR_PREFIXES = Arrays.asList(
"gtnhmixins-",
"gasstation-",
"mixinbooterlegacy-",
"spongemixins-",
"mixingasm-"
);
private static final Pattern LETTER = Pattern.compile("[a-z]");
static {
if(isIntegrityCheckEnabled()) {
doSanityCheck();
}
try {
for(String s : IOUtils.toString(AllCore.class.getResource("/META-INF/unimixins-all.EmbeddedFMLCorePlugins.txt")).split(" ")) {
Class<?> cls = Class.forName(s);
@@ -48,59 +29,6 @@ public class AllCore implements IFMLLoadingPlugin {
}
}
private static void doSanityCheck() {
List<String> concerningJars = new ArrayList<>();
for(URL url : Launch.classLoader.getSources()) {
String path = url.getPath();
if(path.endsWith(".jar")) {
String[] components = path.split("/");
String name = components[components.length - 1].toLowerCase();
Matcher matcher = LETTER.matcher(name);
if(matcher.find()) {
int firstLetterIndex = matcher.start();
name = name.substring(firstLetterIndex);
if (anyPrefixesMatch(name, CONCERNING_JAR_PREFIXES)) {
concerningJars.add(name);
}
}
}
}
if(!concerningJars.isEmpty()) {
// Any throwables we throw here will get caught, so all we can do is warn.
String theWarning = "Detected incompatible jars: " + concerningJars;
LOGGER.warn("=======================================================================================");
LOGGER.warn("WARNING / WARNING / WARNING / WARNING / WARNING / WARNING / WARNING / WARNING / WARNING");
LOGGER.warn("=======================================================================================");
LOGGER.error(theWarning);
LOGGER.error("The game will almost certainly crash!");
LOGGER.fatal("======================================================================================");
throw new Error(theWarning); // Attention grabbing stack trace
}
}
private static boolean anyPrefixesMatch(String s, Collection<String> prefixes) {
for(String p : prefixes) {
if(s.startsWith(p)) {
return true;
}
}
return false;
}
private static boolean isIntegrityCheckEnabled() {
// I'm scared to load Forge classes this early so we're doing this
File file = new File(Launch.minecraftHome, "config/unimixins.cfg");
if(file.exists()) {
try {
return !FileUtils.readFileToString(file).contains("B:disableIntegrityChecks=true");
} catch(Exception e){}
}
return true;
}
public AllCore() {
LOGGER.info("Instantiating AllCore");
@@ -143,9 +71,36 @@ public class AllCore implements IFMLLoadingPlugin {
@Override
public String getAccessTransformerClass() {
return null;
return "io.github.legacymoddingmc.unimixins.all.AllCore$CombinedAccessTransformer";
}
public static class CombinedAccessTransformer implements IClassTransformer {
private final List<IClassTransformer> delegates = new ArrayList<>();
public CombinedAccessTransformer() {
for(IFMLLoadingPlugin plugin : embeddedCorePluginInstances) {
String atClass = plugin.getAccessTransformerClass();
if(atClass != null) {
try {
delegates.add((IClassTransformer) Class.forName(atClass).newInstance());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
for(IClassTransformer delegate : delegates) {
basicClass = delegate.transform(name, transformedName, basicClass);
}
return basicClass;
}
@Override
public String toString() {
return delegates.toString();
}
}
}
@@ -5,6 +5,7 @@ import cpw.mods.fml.common.Mod;
/**
* This is only here so unimixins shows up in the mod list GUI as a single mod.
*/
@Mod(modid = "unimixins", version = "@VERSION@")
@Mod(modid = "unimixins", version = "@VERSION@", acceptableRemoteVersions = "*")
@net.minecraftforge.fml.common.Mod(modid = "unimixins", version = "@VERSION@", acceptableRemoteVersions = "*")
public class AllModule {
}
File renamed without changes.
+4
View File
@@ -0,0 +1,4 @@
archivesBaseName = archivesBaseName.replace("-1.7.10", "-common-1.7.10" )
// Obfuscation is unnecessary since this module does not reference Minecraft classes
reobf.enabled = false
@@ -0,0 +1,53 @@
package io.github.legacymoddingmc.unimixins.common.abstraction;
public class ComparableVersion {
private final IComparableVersionImpl impl;
public ComparableVersion(String version) {
if(ComparableVersion.class.getResource("/cpw/mods/fml/common/versioning/ComparableVersion.class") != null) {
impl = new ComparableVersionOld(version);
} else {
impl = new ComparableVersionNew(version);
}
}
public int compareTo(ComparableVersion other) {
return impl.compareTo(other.impl);
}
public interface IComparableVersionImpl {
int compareTo(IComparableVersionImpl other);
}
public static class ComparableVersionOld implements IComparableVersionImpl {
private final cpw.mods.fml.common.versioning.ComparableVersion internal;
public ComparableVersionOld(String version) {
this.internal = new cpw.mods.fml.common.versioning.ComparableVersion(version);
}
public int compareTo(IComparableVersionImpl other) {
if(other instanceof ComparableVersionOld) {
return internal.compareTo(((ComparableVersionOld) other).internal);
} else {
throw new IllegalArgumentException();
}
}
}
public static class ComparableVersionNew implements IComparableVersionImpl {
private final net.minecraftforge.fml.common.versioning.ComparableVersion internal;
public ComparableVersionNew(String version) {
this.internal = new net.minecraftforge.fml.common.versioning.ComparableVersion(version);
}
public int compareTo(IComparableVersionImpl other) {
if(other instanceof ComparableVersionNew) {
return internal.compareTo(((ComparableVersionNew) other).internal);
} else {
throw new IllegalArgumentException();
}
}
}
}
@@ -0,0 +1,68 @@
package io.github.legacymoddingmc.unimixins.common.config;
import java.io.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.*;
public class AnnotatedProperties {
public static void load(File file, Class<?> cls) {
PropertyCollection properties = PropertyCollection.fromFile(file);
try {
for (Field f : cls.getFields()) {
if (f.isAnnotationPresent(ConfigString.class)) {
ConfigString ann = f.getAnnotation(ConfigString.class);
List<String> comment = new ArrayList<>(Arrays.asList(ann.com().split("\n")));
String def = ann.def();
String cat = ann.cat();
comment.removeIf(s -> s.startsWith("[default:"));
comment.add("[default: " + def + "]");
String key = cat + "." + f.getName();
PropertyToken prop = properties.get(key);
if (prop == null) {
prop = new PropertyToken(comment, key, def);
prop.setDirty();
} else {
prop.setComment(comment);
}
setFieldValue(f, prop.getValue());
properties.put(key, prop);
}
}
} catch(Exception e) {
e.printStackTrace();
}
properties.writeToFile(file);
}
private static void setFieldValue(Field f, String value) throws Exception {
if(f.getType() == boolean.class) {
f.set(null, Boolean.parseBoolean(value));
} else {
throw new UnsupportedOperationException();
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ConfigString {
/** Default value */
String def();
/** Comment */
String com() default "";
/** Category (the property name gets prefixed with this) */
String cat();
}
}
@@ -0,0 +1,19 @@
package io.github.legacymoddingmc.unimixins.common.config;
import net.minecraft.launchwrapper.Launch;
import java.io.File;
public class ConfigUtil {
public static void load(Class<?> cls) {
File configDir = new File(Launch.minecraftHome, "config");
File oldConfigFile = new File(configDir, "unimixins.cfg");
if(oldConfigFile.exists()) {
oldConfigFile.renameTo(new File(configDir, "unimixins.old.delete_me.cfg"));
}
AnnotatedProperties.load(new File(configDir, "unimixins.properties"), cls);
}
}
@@ -0,0 +1,87 @@
package io.github.legacymoddingmc.unimixins.common.config;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
class PropertyCollection {
// We use a TreeMap so keys are automatically sorted
private Map<String, PropertyToken> properties = new TreeMap<>();
public static PropertyCollection fromFile(File file) {
PropertyCollection coll = new PropertyCollection();
coll.readPropertyTokens(file);
return coll;
}
private void readPropertyTokens(File file) {
List<String> comment = new ArrayList<>();
String prop = "";
List<String> keyOrder = new ArrayList<>();
if (file.exists()) {
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
while (true) {
String line = br.readLine();
if (line == null) break;
if (line.isEmpty()) continue;
line = line.trim();
if (line.startsWith("#")) {
comment.add(line.substring(1).trim());
} else {
prop = line;
PropertyToken token = new PropertyToken(comment, prop);
properties.put(token.getKey(), token);
keyOrder.add(token.getKey());
comment.clear();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void writeToFile(File file) {
writePropertyTokens(file);
}
private void writePropertyTokens(File file) {
boolean dirty = properties.values().stream().anyMatch(PropertyToken::isDirty);
if(dirty) {
file.getParentFile().mkdirs();
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
for (Map.Entry<String, PropertyToken> e : properties.entrySet()) {
PropertyToken token = e.getValue();
for (String commentLine : token.getComment()) {
bw.write("# " + commentLine + "\n");
}
bw.write(token.getKey() + "=" + token.getValue() + "\n\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public PropertyToken get(String key) {
return properties.get(key);
}
public void put(String key, PropertyToken token) {
PropertyToken old = properties.get(key);
if (!token.equals(old)) {
properties.put(key, token);
token.setDirty();
}
}
}
@@ -0,0 +1,81 @@
package io.github.legacymoddingmc.unimixins.common.config;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
public class PropertyToken {
private List<String> comment;
private String key;
private String value;
private boolean dirty;
public PropertyToken(List<String> comment, String propertyString) {
this.comment = ImmutableList.copyOf(comment);
String[] pair = propertyString.split("=");
if (pair.length != 2) {
throw new RuntimeException();
} else {
key = pair[0];
value = pair[1];
}
}
public PropertyToken(List<String> comment, String key, String value) {
this.comment = new ArrayList<>(comment);
this.key = key;
this.value = value;
}
public List<String> getComment() {
return comment;
}
public void setComment(List<String> comment) {
if(!comment.equals(this.comment)) {
this.comment = ImmutableList.copyOf(comment);
this.dirty = true;
}
}
public String getKey() {
return key;
}
public void setKey(String key) {
if(!key.equals(this.key)) {
this.key = key;
this.dirty = true;
}
}
public String getValue() {
return value;
}
public void setValue(String value) {
if(!value.equals(this.value)) {
this.value = value;
this.dirty = true;
}
}
public boolean isDirty() {
return dirty;
}
public void setDirty() {
dirty = true;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PropertyToken) {
PropertyToken o = (PropertyToken) obj;
return comment.equals(o.comment) && key.equals(o.key) && value.equals(o.value);
}
return super.equals(obj);
}
}
@@ -0,0 +1,8 @@
package io.github.legacymoddingmc.unimixins.common.sanitycheck;
import io.github.legacymoddingmc.unimixins.common.config.AnnotatedProperties;
public class SanityCheckConfig {
@AnnotatedProperties.ConfigString(def = "true", com = "Throw an error if an invalid combination of modules is detected. Only disable this if you know what you're doing.", cat = "_common")
public static boolean enableIntegrityChecks;
}
@@ -0,0 +1,101 @@
package io.github.legacymoddingmc.unimixins.common.sanitycheck;
import io.github.legacymoddingmc.unimixins.common.config.ConfigUtil;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SanityCheckHelper {
private static final Logger LOGGER = LogManager.getLogger("unimixins");
private static final Pattern LETTER = Pattern.compile("[a-z]");
static {
ConfigUtil.load(SanityCheckConfig.class);
}
public static boolean isEnabled() {
if(!SanityCheckConfig.enableIntegrityChecks && !Launch.blackboard.containsKey("unimixins.warnedAboutDisabledIntegrityChecks")) {
LOGGER.debug("Skipping sanity checks because integrity checks are disabled in the config.");
// only warn once
Launch.blackboard.put("unimixins.warnedAboutDisabledIntegrityChecks", "true");
}
return SanityCheckConfig.enableIntegrityChecks;
}
public static void showBigWarning(String warning) {
showBigWarning(Arrays.asList(warning));
}
public static void showBigWarning(List<String>... warningses) {
List<String> allWarnings = new ArrayList<>();
for(List<String> l : warningses) {
allWarnings.addAll(l);
}
if(!allWarnings.isEmpty()) {
// Any throwables we throw here will get caught, so all we can do is warn.
LOGGER.warn("=======================================================================================");
LOGGER.warn("WARNING / WARNING / WARNING / WARNING / WARNING / WARNING / WARNING / WARNING / WARNING");
LOGGER.warn("=======================================================================================");
for(String w : allWarnings) {
LOGGER.error(" - " + w);
}
LOGGER.error("The game will almost certainly crash!");
LOGGER.fatal("======================================================================================");
throw new Error(String.join("; ", allWarnings)); // Attention grabbing stack trace
}
}
public static void warnIfJarPrefixesExist(List<String> concerningPrefixes) {
showBigWarning(checkIfJarPrefixesExist(concerningPrefixes));
}
public static List<String> checkIfJarPrefixesExist(List<String> concerningPrefixes) {
List<String> concerningJars = getJarsMatchingPrefix(concerningPrefixes);
if(!concerningJars.isEmpty()) {
return Arrays.asList("Detected incompatible jars: " + concerningJars);
}
return Arrays.asList();
}
private static List<String> getJarsMatchingPrefix(List<String> concerningPrefixes) {
List<String> matches = new ArrayList<>();
for(URL url : Launch.classLoader.getSources()) {
String path = url.getPath();
if(path.endsWith(".jar")) {
String[] components = path.split("/");
String name = components[components.length - 1].toLowerCase();
Matcher matcher = LETTER.matcher(name);
if(matcher.find()) {
int firstLetterIndex = matcher.start();
if (anyPrefixesMatch(name.substring(firstLetterIndex), concerningPrefixes)) {
matches.add(name);
}
}
}
}
return matches;
}
private static boolean anyPrefixesMatch(String s, Collection<String> prefixes) {
for(String p : prefixes) {
if(s.startsWith(p)) {
return true;
}
}
return false;
}
}
+12 -2
View File
@@ -1,10 +1,19 @@
dependencies {
compileOnly('org.spongepowered:mixin:0.8.5')
shadow(project(":module-common")) {
transitive = false
}
}
archivesBaseName = archivesBaseName.replace("-1.7.10", "-compat-1.7.10")
jar {
apply plugin: "com.github.johnrengelman.shadow"
shadowJar {
configurations = [project.configurations.shadow]
classifier = ''
relocate 'io.github.legacymoddingmc.unimixins.common', 'io.github.legacymoddingmc.unimixins.compat.repackage.common'
manifest {
attributes (
'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
@@ -15,4 +24,5 @@ jar {
}
}
reobf.enabled = false
jar.dependsOn shadowJar
jar.enabled = false
@@ -0,0 +1,12 @@
package io.github.legacymoddingmc.unimixins.compat;
import io.github.legacymoddingmc.unimixins.common.config.AnnotatedProperties;
public class CompatConfig {
@AnnotatedProperties.ConfigString(def = "true", com = "Remap references to Mixin's shaded ASM.", cat = "compat")
public static boolean enableRemapper;
@AnnotatedProperties.ConfigString(def = "true", com = "Include mixin errors and list of applied mixins in crash reports.", cat = "compat")
public static boolean enhanceCrashReports;
@AnnotatedProperties.ConfigString(def = "true", com = "Make Mixin's INIT phase listener more reliable. Fixes INIT phase mixins not working with certain launchers with the LiteLoader mod.", cat = "compat")
public static boolean improveInitPhaseDetection;
}
@@ -1,12 +1,18 @@
package io.github.legacymoddingmc.unimixins.compat;
import java.util.Map;
import java.util.*;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
import io.github.legacymoddingmc.unimixins.common.config.ConfigUtil;
import io.github.legacymoddingmc.unimixins.compat.asm.IgnoreDuplicateJarsTransformer;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixins;
@MCVersion("1.7.10")
public class CompatCore implements IFMLLoadingPlugin {
@@ -16,13 +22,27 @@ public class CompatCore implements IFMLLoadingPlugin {
public CompatCore() {
LOGGER.info("Instantiating CompatCore");
// We register the transformer this way to register it as early as possible.
Launch.classLoader.registerTransformer(relativeClassName("asm.ASMRemapperTransformer"));
ConfigUtil.load(CompatConfig.class);
if(CompatConfig.enableRemapper) {
// We register the transformer this way to register it as early as possible.
Launch.classLoader.registerTransformer(relativeClassName("asm.ASMRemapperTransformer"));
}
}
@Override
public String[] getASMTransformerClass() {
return new String[] {};
List<String> classes = new ArrayList<>();
if(CompatConfig.enhanceCrashReports) {
classes.add("io.github.legacymoddingmc.unimixins.compat.asm.EnhanceCrashReportsTransformer");
}
if(IgnoreDuplicateJarsTransformer.wantsToRun()) {
classes.add("io.github.legacymoddingmc.unimixins.compat.asm.IgnoreDuplicateJarsTransformer");
}
if(Boolean.parseBoolean(System.getProperty("unimixins.compat.hackClasspathModDiscovery", "false"))) {
classes.add("io.github.legacymoddingmc.unimixins.compat.asm.HackClasspathModDiscoveryTransformer");
}
return classes.toArray(new String[0]);
}
private static String relativeClassName(String relName) {
@@ -42,16 +62,64 @@ public class CompatCore implements IFMLLoadingPlugin {
return null;
}
/**
* <p>This is the earliest point after the Mixin environment's initialization where we can inject code.
* <p>We register our error handler here.
*/
@Override
public void injectData(Map<String, Object> data) {
if(CompatConfig.enhanceCrashReports) {
Mixins.registerErrorHandlerClass("io.github.legacymoddingmc.unimixins.compat.MixinErrorHandler");
}
}
@Override
public String getAccessTransformerClass() {
return null;
return "io.github.legacymoddingmc.unimixins.compat.CompatCore$DummyTransformer";
}
/** Coremod Access Transformers are initialized at the beginning of {@link cpw.mods.fml.common.launcher.FMLDeobfTweaker#injectIntoClassLoader}.
* We use this as an injection point, this transformer doesn't actually transform anything. */
@SuppressWarnings("unused")
public static class DummyTransformer implements IClassTransformer {
public DummyTransformer() {
if (CompatConfig.improveInitPhaseDetection) {
setFmlLoggerToVerbose();
}
}
/** Mixin detects the start of the INIT phase by listening for the "Validating minecraft" FML log message.
* (See <tt>MixinAppender</tt> in {@link org.spongepowered.asm.launch.platform.MixinPlatformAgentFMLLegacy}.)
*
* <p>It sets the verbosity to ALL in an attempt to ensure the message gets logged, but this is not always the
* case. We add an extra check here, in a method that gets called right before that log message.</p>
*/
private static void setFmlLoggerToVerbose() {
Logger fmlLog;
try {
fmlLog = FMLRelaunchLog.log.getLogger();
if (!(fmlLog instanceof org.apache.logging.log4j.core.Logger)) {
return;
}
} catch (NoClassDefFoundError e) {
return;
}
org.apache.logging.log4j.core.Logger fmlCoreLog = (org.apache.logging.log4j.core.Logger)fmlLog;
if(fmlCoreLog.getLevel() != Level.ALL) {
LOGGER.info("Correcting FML log level from " + fmlCoreLog.getLevel() + " to ALL");
} else {
LOGGER.debug("FML log level was already ALL, doing nothing");
}
fmlCoreLog.setLevel(Level.ALL);
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
return basicClass;
}
}
}
@@ -0,0 +1,11 @@
package io.github.legacymoddingmc.unimixins.compat;
import cpw.mods.fml.common.Mod;
/**
* This is only here to help Forge detect the mod.
*/
@Mod(modid = "unimixins-compat", version = "@VERSION@", acceptableRemoteVersions = "*")
@net.minecraftforge.fml.common.Mod(modid = "unimixins-compat", version = "@VERSION@", acceptableRemoteVersions = "*")
public class CompatModule {
}
@@ -0,0 +1,109 @@
package io.github.legacymoddingmc.unimixins.compat;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import org.spongepowered.asm.mixin.transformer.ClassInfo;
import java.lang.reflect.Field;
import java.util.*;
import static io.github.legacymoddingmc.unimixins.compat.CompatCore.LOGGER;
public class CrashReportEnhancer {
public static void addMixinsToCrashReport(CrashReport crashReport, CrashReportCategory category) {
try {
Collection<String> classesInStackTrace = getClassesInStackTrace(crashReport);
addMixinErrorsToCrashReport(category, classesInStackTrace);
addMixinsToCrashReport(category, classesInStackTrace);
} catch (Throwable t) {
// If an error happens here, the game will silently crash, so we must make sure that does not happen.
LOGGER.error("Encountered error while enhancing crash report");
t.printStackTrace();
}
}
// Inspired by https://github.com/comp500/mixintrace
private static void addMixinsToCrashReport(CrashReportCategory category, Collection<String> classesInStackTrace) {
String msg = "";
for(String s : classesInStackTrace) {
Collection<IMixinInfo> mixins = getMixinsForClass(s);
if(!mixins.isEmpty()) {
msg += "\n\t\t" + s + ":";
for (IMixinInfo mi : mixins) {
msg += "\n\t\t\t" + mi;
}
}
}
if(!msg.isEmpty()) {
category.addCrashSection("Mixins in Stacktrace", msg);
}
}
// Mixins#getMixinsForClass doesn't work properly, so we have to do this
// see https://github.com/SpongePowered/Mixin/pull/529
private static Collection<IMixinInfo> getMixinsForClass(String s) {
try {
ClassInfo ci = ClassInfo.fromCache(s);
if (ci != null) {
Field f = ClassInfo.class.getDeclaredField("mixins");
if (f != null) {
f.setAccessible(true);
return (Set<IMixinInfo>) f.get(ci);
}
}
} catch (Exception e) {
}
return Collections.emptySet();
}
private static void addMixinErrorsToCrashReport(CrashReportCategory category, Collection<String> classesInStackTrace) {
Map<String, List<String>> errors = new HashMap<>();
for (String s : classesInStackTrace) {
List<String> e = MixinErrorHandler.getErrorsForClass(s);
if(!e.isEmpty()) {
errors.put(s, e);
}
}
if (!errors.isEmpty()) {
String msg = "";
for(Map.Entry<String, List<String>> e : errors.entrySet()) {
String cls = e.getKey();
List<String> clsErrors = e.getValue();
msg += "\n\t\t" + cls + ":";
for(String clsError : e.getValue()) {
msg += "\n\t\t\t" + clsError;
}
}
category.addCrashSection(
String.format("Mixin Errors in Stacktrace"),
msg);
}
}
private static Collection<String> getClassesInStackTrace(CrashReport crashReport) {
Set<String> classes = new HashSet<>();
Throwable th = crashReport.getCrashCause();
while(th != null) {
if(th instanceof ClassNotFoundException) {
String msg = th.getMessage();
if(msg != null && !msg.isEmpty()) {
classes.add(msg);
}
}
for(StackTraceElement elem : th.getStackTrace()) {
classes.add(elem.getClassName());
}
th = th.getCause();
}
return classes;
}
}
@@ -0,0 +1,35 @@
package io.github.legacymoddingmc.unimixins.compat;
import com.google.common.collect.Lists;
import org.spongepowered.asm.mixin.extensibility.IMixinConfig;
import org.spongepowered.asm.mixin.extensibility.IMixinErrorHandler;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import java.util.*;
public class MixinErrorHandler implements IMixinErrorHandler {
private static Map<String, List<String>> caughtMixinErrors = new HashMap<>();
@Override
public ErrorAction onPrepareError(IMixinConfig config, Throwable th, IMixinInfo mixin, ErrorAction action) {
for(String className : mixin.getTargetClasses()) {
putError(className, th);
}
return null;
}
@Override
public ErrorAction onApplyError(String targetClassName, Throwable th, IMixinInfo mixin, ErrorAction action) {
putError(targetClassName, th);
return null;
}
private static void putError(String className, Throwable th) {
caughtMixinErrors.computeIfAbsent(className, (String k) -> new ArrayList<String>()).add(th.toString());
}
public static List<String> getErrorsForClass(String className) {
return caughtMixinErrors.getOrDefault(className, Collections.EMPTY_LIST);
}
}
@@ -0,0 +1,16 @@
package io.github.legacymoddingmc.unimixins.compat.api;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** <p>Decorate your class with this annotation to make UniMixins remap references to <code>org.objectweb.asm</code> to
* the ASM package name used by the current Mixin environment.</p>
* <p>This is needed if you have a mixin config plugin compiled against this package name, and you want to pass the
* ClassNode reference to a different class, since otherwise UniMixins only remaps references inside the mixin
* config plugin class.</p> */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RemapASMForMixin {
}
@@ -10,45 +10,45 @@ import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.commons.RemappingClassAdapter;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>Some classes use Mixin internals, so we need to make sure they use the correct type names, and remap them if they don't.</p>
* <p>We do this for every class for maximum compatibility.</p>
* <p>The ASM package name used by Mixin differs between mixin loaders. This transformer remaps references to ASM to use the
* correct package name for the current runtime environment, allowing mods to work regardless of what mixin loader they
* were compiled against.</p>
* <p>The following packages are remapped:</p>
* <li><code>org.spongepowered.libraries.org.objectweb.asm</code> (MixinBooterLegacy, GTNHMixins) in all classes</li>
* <li><code>org.spongepowered.asm.lib</code> (Mixin 0.7, GasStation, UniMixins) in all classes</li>
* <li><code>org.objectweb.asm</code> (Mixin 0.8) in classes implementing {@link org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin} or annotated with {@link io.github.legacymoddingmc.unimixins.compat.api.RemapASMForMixin}</li>
*/
// TODO Measure the performance impact of this and switch to doing it selectively if it would improve things.
public class ASMRemapperTransformer implements IClassTransformer {
/** A class will be remapped if it implements one of these interfaces. */
private static Set<String> interfaceWhitelist = new HashSet<>();
/** A class will be remapped if its name starts with one of these strings. */
private static Set<String> packageWhitelist = new HashSet<>();
private static final boolean remapSelectively = Boolean.parseBoolean(System.getProperty("unimixins.compat.remapSelectively", "false"));
private static final String ASM_PACKAGE_UNSHADED = "org/objectweb/asm/";
private static final String ASM_PACKAGE_LEGACY = "org/spongepowered/asm/lib/";
private static final String ASM_PACKAGE_MBL = "org/spongepowered/libraries/org/objectweb/asm/";
private static final List<String> ASM_PACKAGE_PREFIXES = Arrays.asList(
"org/spongepowered/libraries/org/objectweb/asm/",
"org/spongepowered/asm/lib/"
ASM_PACKAGE_UNSHADED,
ASM_PACKAGE_LEGACY,
ASM_PACKAGE_MBL
);
private static final List<byte[]> SHADED_ASM_PACKAGE_PREFIXES_RAW = Arrays.asList(
ASM_PACKAGE_LEGACY.getBytes(StandardCharsets.UTF_8),
ASM_PACKAGE_MBL.getBytes(StandardCharsets.UTF_8)
);
private static String realASMPackagePrefix;
public static void registerInterface(String itf) {
interfaceWhitelist.add(itf.replace('.', '/'));
}
public static void registerPackage(String pkg) {
packageWhitelist.add(pkg + ".");
}
public ASMRemapperTransformer() {
registerInterface("org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin");
}
private static List<byte[]> wrongASMPackagePrefixesRaw;
private static List<String> wrongASMPackagePrefixes;
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
@@ -61,35 +61,41 @@ public class ASMRemapperTransformer implements IClassTransformer {
|| transformedName.startsWith("org.objectweb.asm.")
) return basicClass;
ClassReader classReader = new ClassReader(basicClass);
boolean foundWrongAsm = containsAnyPattern(basicClass, getWrongASMPackagePrefixesRaw());
ClassNode classNode = new ClassNode();
ClassReader classReaderForNode = new ClassReader(basicClass);
classReaderForNode.accept(classNode, 0);
if(!foundWrongAsm) return basicClass;
boolean doRemap = false;
boolean foundShadedAsm = containsAnyPattern(basicClass, SHADED_ASM_PACKAGE_PREFIXES_RAW);
if(remapSelectively) {
if(!doRemap) {
boolean doRemap = foundShadedAsm;
if(!doRemap) {
ClassNode classNode = new ClassNode();
ClassReader classReaderForNode = new ClassReader(basicClass);
classReaderForNode.accept(classNode, 0);
if(classNode.interfaces != null) {
for (String itf : classNode.interfaces) {
if (interfaceWhitelist.contains(itf)) {
if (itf.equals("org/spongepowered/asm/mixin/extensibility/IMixinConfigPlugin")) {
doRemap = true;
break;
}
}
}
if(!doRemap) {
for (String pkg : packageWhitelist) {
if(transformedName.startsWith(pkg)) {
doRemap = true;
if(classNode.visibleAnnotations != null) {
for (AnnotationNode ann : classNode.visibleAnnotations) {
if (ann.desc.equals("Lio/github/legacymoddingmc/unimixins/compat/api/RemapASMForMixin;")) {
doRemap = true;
break;
}
}
}
}
} else {
doRemap = Bytes.indexOf(basicClass, getFakeASMPackagePrefix().getBytes()) != -1;
}
if(doRemap) {
ClassReader classReader = new ClassReader(basicClass);
LOGGER.info("Transforming class " + transformedName + " to fit current mixin environment.");
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
RemappingClassAdapter remapAdapter = new SpongepoweredASMRemappingAdapter(classWriter);
@@ -99,6 +105,18 @@ public class ASMRemapperTransformer implements IClassTransformer {
return basicClass;
}
private static boolean containsAnyPattern(byte[] array, List<byte[]> patterns) {
if(array == null) {
return false;
}
for(byte[] pattern : patterns) {
if(Bytes.indexOf(array, pattern) != -1) {
return true;
}
}
return false;
}
private static String getRealASMPackagePrefix() {
if(realASMPackagePrefix == null) {
try {
@@ -129,10 +147,20 @@ public class ASMRemapperTransformer implements IClassTransformer {
return realASMPackagePrefix;
}
private static String getFakeASMPackagePrefix() {
return ASM_PACKAGE_PREFIXES.get(ASM_PACKAGE_PREFIXES.get(0).equals(getRealASMPackagePrefix()) ? 1 : 0);
private static List<String> getWrongASMPackagePrefixes() {
if(wrongASMPackagePrefixes == null) {
wrongASMPackagePrefixes = ASM_PACKAGE_PREFIXES.stream().filter(x -> !x.equals(getRealASMPackagePrefix())).collect(Collectors.toList());
}
return wrongASMPackagePrefixes;
}
private static List<byte[]> getWrongASMPackagePrefixesRaw() {
if(wrongASMPackagePrefixesRaw == null) {
wrongASMPackagePrefixesRaw = getWrongASMPackagePrefixes().stream().map(x -> x.getBytes(StandardCharsets.UTF_8)).collect(Collectors.toList());
}
return wrongASMPackagePrefixesRaw;
}
private static class SpongepoweredASMRemappingAdapter extends RemappingClassAdapter {
public SpongepoweredASMRemappingAdapter(ClassWriter classWriter) {
super(classWriter, SpongepoweredASMRemapper.INSTANCE);
@@ -0,0 +1,85 @@
package io.github.legacymoddingmc.unimixins.compat.asm;
import io.github.legacymoddingmc.unimixins.compat.CrashReportEnhancer;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.*;
import static org.objectweb.asm.Opcodes.*;
import static io.github.legacymoddingmc.unimixins.compat.CompatCore.LOGGER;
/**
* <p>Inserts a hook at the end of FMLCommonHandler#enhanceCrashReport.
* <p>We use a transformer instead of a mixin because FML is a minefield for mixin errors.
*/
public class EnhanceCrashReportsTransformer implements IClassTransformer {
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if(basicClass == null) return null;
if(name.equals("cpw.mods.fml.common.FMLCommonHandler")) {
return transformFMLCommonHandler(basicClass);
}
return basicClass;
}
/**
* <pre>
* public void enhanceCrashReport(CrashReport crashReport, CrashReportCategory category)
* {
* for (ICrashCallable call: crashCallables)
* {
* category.addCrashSectionCallable(call.getLabel(), call);
* }
* + EnhanceCrashReportsTransformer.Hooks.postEnhanceCrashReport();
* }
* </pre>
*/
private static byte[] transformFMLCommonHandler(byte[] bytes) {
LOGGER.info("Transforming FMLCommonHandler to add hook for enhancing crash reports");
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);
for(MethodNode m : classNode.methods) {
if (m.name.equals("enhanceCrashReport")) {
AbstractInsnNode injectionTarget = null;
for(int i = 0; i < m.instructions.size(); i++) {
AbstractInsnNode ain = m.instructions.get(i);
if(ain instanceof InsnNode) {
InsnNode in = (InsnNode)ain;
if(in.getOpcode() == RETURN) {
injectionTarget = in;
break;
}
}
}
if(injectionTarget != null) {
InsnList inject = new InsnList();
//ALOAD crashReport
inject.add(new VarInsnNode(ALOAD, 1));
//ALOAD category
inject.add(new VarInsnNode(ALOAD, 2));
//INVOKESTATIC foo/bar.postEnhanceCrashReport(Lnet/minecraft/crash/CrashReport;Lnet/minecraft/crash/CrashReportCategory;)V
inject.add(new MethodInsnNode(INVOKESTATIC, "io/github/legacymoddingmc/unimixins/compat/asm/EnhanceCrashReportsTransformer$Hooks", "postEnhanceCrashReport", "(Lnet/minecraft/crash/CrashReport;Lnet/minecraft/crash/CrashReportCategory;)V", false));
m.instructions.insertBefore(injectionTarget, inject);
}
}
}
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
classNode.accept(writer);
return writer.toByteArray();
}
public static class Hooks {
public static void postEnhanceCrashReport(CrashReport report, CrashReportCategory category) {
CrashReportEnhancer.addMixinsToCrashReport(report, category);
}
}
}
@@ -1,8 +1,7 @@
package io.github.legacymoddingmc.unimixins.devcompat.asm;
package io.github.legacymoddingmc.unimixins.compat.asm;
import static io.github.legacymoddingmc.unimixins.devcompat.DevCompatCore.LOGGER;
import static io.github.legacymoddingmc.unimixins.compat.CompatCore.LOGGER;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
@@ -18,7 +17,7 @@ import java.util.*;
* <p>Fixes classpath mods not getting loaded in dev env when running via the runClient Gradle task.</p>
*/
public class ModDiscovererTransformer implements IClassTransformer {
public class HackClasspathModDiscoveryTransformer implements IClassTransformer {
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
@@ -34,7 +33,7 @@ public class ModDiscovererTransformer implements IClassTransformer {
}
private byte[] doTransformModDiscoverer(byte[] bytes) {
LOGGER.info("ModDiscovererTransformer: Transforming ModDiscoverer#findClasspathMods to ignore reparseable coremods.");
LOGGER.info("HackClasspathModDiscoveryTransformer: Transforming ModDiscoverer#findClasspathMods to ignore reparseable coremods.");
try {
ClassNode classNode = new ClassNode();
@@ -49,7 +48,7 @@ public class ModDiscovererTransformer implements IClassTransformer {
if(i.getOpcode() == INVOKESTATIC) {
MethodInsnNode mi = (MethodInsnNode)i;
if(mi.owner.equals("cpw/mods/fml/relauncher/CoreModManager") && mi.name.equals("getReparseableCoremods") && mi.desc.equals("()Ljava/util/List;")) {
m.instructions.insertBefore(mi, new MethodInsnNode(INVOKESTATIC, "io/github/legacymoddingmc/unimixins/devcompat/asm/ModDiscovererTransformer$Hooks", "redirectGetReparseableCoremods", mi.desc));
m.instructions.insertBefore(mi, new MethodInsnNode(INVOKESTATIC, "io/github/legacymoddingmc/unimixins/compat/asm/HackClasspathModDiscoveryTransformer$Hooks", "redirectGetReparseableCoremods", mi.desc));
it.remove();
break;
}
@@ -0,0 +1,139 @@
package io.github.legacymoddingmc.unimixins.compat.asm;
import cpw.mods.fml.common.discovery.ModCandidate;
import cpw.mods.fml.common.discovery.ModDiscoverer;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.*;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.util.*;
import static io.github.legacymoddingmc.unimixins.compat.CompatCore.LOGGER;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
/**
* Makes the mod discoverer ignore duplicate UniMixins jars that come from java agents. This allows UniMixins to be
* safely used as a Java agent instead of having to hunt down the UniMix jar and add that as one.
*/
public class IgnoreDuplicateJarsTransformer implements IClassTransformer {
private static Set<File> uniMixinsJavaAgentJars;
public static boolean wantsToRun() {
return !getUniMixinsJavaAgentJars().isEmpty();
}
private static Set<File> getUniMixinsJavaAgentJars() {
if(uniMixinsJavaAgentJars == null) {
uniMixinsJavaAgentJars = new HashSet<>();
try {
for (String arg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
if (arg.startsWith("-javaagent:")) {
String agent = arg.substring("-javaagent:".length());
File agentFile = new File(agent);
String name = agentFile.getName();
if (name.toLowerCase().contains("unimixins")) {
uniMixinsJavaAgentJars.add(agentFile);
}
}
}
} catch(Exception e) {
LOGGER.error("Failed to enumerate command line java agents");
e.printStackTrace();
}
}
return uniMixinsJavaAgentJars;
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if(basicClass == null) {
return null;
}
if(transformedName.equals("cpw.mods.fml.common.discovery.ModDiscoverer")) {
basicClass = doTransformModDiscoverer(basicClass);
}
return basicClass;
}
private byte[] doTransformModDiscoverer(byte[] bytes) {
LOGGER.info("IgnoreDuplicateJarsTransformer: Transforming ModDiscoverer#identifyMods to ignore duplicate UniMixins jars coming from Java agents.");
try {
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);
for(MethodNode m : classNode.methods) {
if(m.name.equals("identifyMods")) {
InsnList patch = new InsnList();
patch.add(new VarInsnNode(ALOAD, 0));
patch.add(new MethodInsnNode(INVOKESTATIC, "io/github/legacymoddingmc/unimixins/compat/asm/IgnoreDuplicateJarsTransformer$Hooks", "preIdentifyMods", "(Lcpw/mods/fml/common/discovery/ModDiscoverer;)V"));
m.instructions.insert(patch);
}
}
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
classNode.accept(writer);
return writer.toByteArray();
} catch(Exception e) {
e.printStackTrace();
}
return bytes;
}
@SuppressWarnings("unused")
public static class Hooks {
public static void preIdentifyMods(ModDiscoverer dis) {
try {
List<ModCandidate> candidates = getModCandidates(dis);
Map<File, List<ModCandidate>> modCandidatesByFile = new HashMap<>();
for(ModCandidate mc : candidates) {
modCandidatesByFile.computeIfAbsent(getModContainer(mc), x -> new ArrayList<>()).add(mc);
}
for(Map.Entry<File, List<ModCandidate>> e : modCandidatesByFile.entrySet()) {
File file = e.getKey();
List<ModCandidate> mcs = e.getValue();
if(getUniMixinsJavaAgentJars().contains(file)) {
Iterator<ModCandidate> it = mcs.iterator();
while(mcs.size() > 1 && it.hasNext()) {
ModCandidate mc = it.next();
// The java agent duplicate can only be a classpath mod
if (mc.isClasspath()) {
LOGGER.info("Removing duplicate Java agent mod candidate: " + file.getName());
candidates.remove(mc);
mcs.remove(mc);
}
}
}
}
} catch(Exception e) {
LOGGER.error("Failed to remove duplicate mod candidates, the Mixin Java agent will cause a duplicate mod error. Add the UniMix jar as the java agent instead.");
e.printStackTrace();
}
}
private static List<ModCandidate> getModCandidates(ModDiscoverer dis) throws Exception {
Field f = dis.getClass().getDeclaredField("candidates");
f.setAccessible(true);
return (List<ModCandidate>)f.get(dis);
}
private static File getModContainer(ModCandidate mc) throws Exception {
Field f = ModCandidate.class.getDeclaredField("modContainer");
f.setAccessible(true);
return (File)f.get(mc);
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
{
"modid": "unimixins-compat",
"name": "UniMixins: Compatibility",
"description": "UniMixins module that remaps mods to use the correct names for Mixin packages.",
"description": "UniMixins module that improves compatibility and helps debug crashes.",
"version": "${version}",
"mcversion": "${minecraft_version}",
"url": "${project_url}",
+24
View File
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
+21
View File
@@ -0,0 +1,21 @@
archivesBaseName = archivesBaseName.replace("-1.7.10", "-compatfuture-1.7.10")
// Obfuscation is unnecessary since this module does not reference Minecraft classes
reobf.enabled = false
shadowJar {
archiveClassifier = ""
configurations = []
relocate 'io.github.legacymoddingmc.unimixins.compat.stub.cpw', 'cpw'
manifest {
attributes (
'FMLCorePluginContainsFMLMod': true,
'ForceLoadAsMod': true,
'FMLCorePlugin': 'io.github.legacymoddingmc.unimixins.compatfuture.CompatFutureCore'
)
}
}
jar.dependsOn shadowJar
jar.enabled = false
@@ -0,0 +1,35 @@
package io.github.legacymoddingmc.unimixins.compatfuture;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import java.util.Map;
/**
* This is only here so Forge puts the mod on the classpath during coremod loading.
*/
public class CompatFutureCore implements IFMLLoadingPlugin {
@Override
public String[] getASMTransformerClass() {
return new String[0];
}
@Override
public String getModContainerClass() {
return null;
}
@Override
public String getSetupClass() {
return null;
}
@Override
public void injectData(Map<String, Object> data) {
}
@Override
public String getAccessTransformerClass() {
return null;
}
}
@@ -0,0 +1,11 @@
package io.github.legacymoddingmc.unimixins.compatfuture;
import cpw.mods.fml.common.Mod;
/**
* This is only here to help Forge detect the mod.
*/
@Mod(modid = "unimixins-compatfuture", version = "@VERSION@", acceptableRemoteVersions = "*")
@net.minecraftforge.fml.common.Mod(modid = "unimixins-compatfuture", version = "@VERSION@", acceptableRemoteVersions = "*")
public class CompatFutureModule {
}
@@ -0,0 +1,5 @@
package io.github.legacymoddingmc.unimixins.compat.stub.cpw.mods.fml.relauncher;
/** Stub for 1.8+ compat. (This gets relocated to the <code>cpw.mods.fml.relauncher</code> package.) */
public interface IFMLLoadingPlugin extends net.minecraftforge.fml.relauncher.IFMLLoadingPlugin {
}
@@ -1,8 +1,8 @@
[
{
"modid": "unimixins-devcompat",
"name": "UniMixins: Dev Compatibility",
"description": "UniMixins module that applies compatibility tweaks for dev environments.",
"modid": "unimixins-compatfuture",
"name": "UniMixins: Future Compatibility",
"description": "UniMixins module that makes the mod usable on 1.8.9 ~ 1.12.2.",
"version": "${version}",
"mcversion": "${minecraft_version}",
"url": "${project_url}",
-18
View File
@@ -1,18 +0,0 @@
dependencies {
compileOnly('org.spongepowered:mixin:0.8.5')
}
archivesBaseName = archivesBaseName.replace("-1.7.10", "-devcompat-1.7.10")
jar {
manifest {
attributes (
'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
'FMLCorePluginContainsFMLMod': true,
'ForceLoadAsMod': true,
'FMLCorePlugin': 'io.github.legacymoddingmc.unimixins.devcompat.DevCompatCore'
)
}
}
reobf.enabled = false
@@ -1,53 +0,0 @@
package io.github.legacymoddingmc.unimixins.devcompat;
import java.util.Map;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@MCVersion("1.7.10")
public class DevCompatCore implements IFMLLoadingPlugin {
public static final Logger LOGGER = LogManager.getLogger("unimixins");
public DevCompatCore() {
LOGGER.info("Instantiating DevCompatCore");
}
@Override
public String[] getASMTransformerClass() {
return new String[] {relativeClassName("asm.ModDiscovererTransformer")};
}
private static String relativeClassName(String relName) {
String name = DevCompatCore.class.getName();
name = name.substring(0, name.lastIndexOf('.') + 1);
name += relName;
return name;
}
@Override
public String getModContainerClass() {
return null;
}
@Override
public String getSetupClass() {
return null;
}
@Override
public void injectData(Map<String, Object> data) {
}
@Override
public String getAccessTransformerClass() {
return null;
}
}
+1
View File
@@ -9,5 +9,6 @@ Modifications:
- Removed Mixin and MixinExtras bootstrap from GasStationCore.
- Disabled dev mixin plugin because UniMixins's Compat module does the same thing.
- Added call to "unimixins.mixinModidDecorator.refresh" after loading early mixins in GasStationCore and late mixins in LoadControllerMixin
- Added 1.8+ @Mod and @IFMLPlugin annotations
License: LGPL3+SNEED (see LICENSE, COPYING and COPYING.LESSER)
+17 -3
View File
@@ -1,11 +1,18 @@
dependencies {
compileOnly('org.spongepowered:mixin:0.8.5-gasstation_7')
compileOnly("org.spongepowered:mixin:$gasMixVersion")
compileOnly project(':module-mixinbooterlegacy')
shadow(project(":module-common")) {
transitive = false
}
}
archivesBaseName = archivesBaseName.replace("-1.7.10", "-gasstation-1.7.10")
jar {
shadowJar {
configurations = [project.configurations.shadow]
classifier = ''
relocate 'io.github.legacymoddingmc.unimixins.common', 'io.github.legacymoddingmc.unimixins.gasstation.repackage.common'
manifest {
attributes (
'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
@@ -17,8 +24,15 @@ jar {
}
}
jar {
enabled = false
dependsOn shadowJar
}
// Obfuscation is unnecessary since this module does not reference Minecraft classes
reobf.enabled = false
ext.uniVersion = version
ext.extraVersion = "0.5.1"
ext.mcversion = "1.7.10"
version += "+" + ext.extraVersion
+2
View File
@@ -0,0 +1,2 @@
extraVersion=0.5.1
gasMixVersion=0.8.5-gasstation_7
@@ -6,5 +6,9 @@ import cpw.mods.fml.common.Mod;
version = Tags.VERSION,
name = Tags.MODNAME,
acceptableRemoteVersions = "*")
@net.minecraftforge.fml.common.Mod(modid = Tags.MODID,
version = Tags.VERSION,
name = Tags.MODNAME,
acceptableRemoteVersions = "*")
public class GasStation {
}
@@ -19,6 +19,9 @@ import java.util.Map;
@IFMLLoadingPlugin.SortingIndex(Integer.MIN_VALUE + 5)
@IFMLLoadingPlugin.Name(GasStationCore.PLUGIN_NAME)
@IFMLLoadingPlugin.TransformerExclusions("com.falsepattern.gasstation.core")
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.SortingIndex(Integer.MIN_VALUE + 5)
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.Name(GasStationCore.PLUGIN_NAME)
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions("com.falsepattern.gasstation.core")
public class GasStationCore implements IFMLLoadingPlugin {
public static final String PLUGIN_NAME = Tags.MODNAME + " Core Plugin";
public static final Logger LOGGER = LogManager.getLogger(PLUGIN_NAME);
@@ -1,38 +1,24 @@
package io.github.legacymoddingmc.unimixins.gasstation;
import cpw.mods.fml.common.versioning.ComparableVersion;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.common.config.Configuration;
import io.github.legacymoddingmc.unimixins.common.sanitycheck.SanityCheckHelper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class GasStationModule {
private static final Logger LOGGER = LogManager.getLogger("unimixin-gasstation");
private static final Logger LOGGER = LogManager.getLogger("unimixins-gasstation");
public static void init() {
if(!skipIntegrityChecks()) {
if(SanityCheckHelper.isEnabled()) {
SanityCheckHelper.warnIfJarPrefixesExist(Arrays.asList("gasstation-", "mixinbooterlegacy-", "spongemixins-", "mixingasm-"));
checkComponentIntegrity();
}
}
private static boolean skipIntegrityChecks() {
Configuration config = new Configuration(new File(Launch.minecraftHome, "config/unimixins.cfg"));
config.load();
boolean disableIntegrityChecks = config.getBoolean("disableIntegrityChecks", "general", false, "Don't throw an error if an invalid combination of modules is detected. For advanced users.");
if(config.hasChanged()) {
config.save();
}
return disableIntegrityChecks;
}
// Logic copied from FalsePatternLib for interoperability
private static void checkComponentIntegrity() {
List<String> missingComponents = new ArrayList<>();
@@ -1,6 +0,0 @@
# Classes listed in this file will be considered "untrusted" by Mixingasm and
# added to the mixin environment's transformer exclusion list.
# See transformer_inclusion_list_default.txt for more info.
# --- Untrusted transformers ---
@@ -1,6 +0,0 @@
# Classes listed in this file will be considered "trusted" by Mixingasm and
# may be spared from the mixin environment's transformer exclusion list.
# See transformer_inclusion_list_default.txt for more info.
# --- Additional trusted transformers ---
@@ -1,26 +0,0 @@
:replaceableFile # This file will get OVERWRITTEN at launch, nullifying any changes made to it, unless you delete this line. It is recommended to edit transformer_inclusion_list.txt and transformer_exclusion_list.txt instead, though.
# Mixingasm adds all transformers it doesn't "trust" to be Mixin-safe to the
# mixin environment's transformer exclusion list, avoiding running them an
# additional time when processing mixins at startup.
# This fixes issues that can arise from non-Mixin-aware transformers being run
# in that way. For example, some transformers break when run more than once.
# A transformer is considered "trusted" if one of these are true...
# * It's matched by a pattern in this file
# * It's matched by a pattern in transformer_inclusion_list.txt
# * It was added to Mixingasm's dynamic inclusion list by a mod using the API at startup
# * The transformer implements IMixinSafeTransformer
# AND the transformer is not matched by a pattern in transformer_exclusion_list.txt
# '*' can be used as a wildcard character.
# --- Trusted transformers ---
cpw.mods.fml.common.asm.*
net.minecraftforge.*
codechicken.core.asm.*
codechicken.lib.asm.*
org.spongepowered.asm.*
@@ -3,7 +3,7 @@
"modid": "gasstation",
"name": "UniMixins: GasStation",
"description": "UniMixins module that provides GasStation extras.\nDepends on: SpongeMixins, MixinBooterLegacy, MixinExtras",
"version": "${extraVersion}+uni-${uniVersion}",
"version": "${extraVersion}+uni.${uniVersion}",
"mcversion": "${mcversion}",
"url": "${project_url}",
"updateUrl": "",
+8 -6
View File
@@ -1,14 +1,16 @@
Source: https://github.com/GTNewHorizons/GTNHMixins/tree/2.1.12
Source: https://github.com/GTNewHorizons/GTNHMixins/tree/2.1.13
Classes used:
com.gtnewhorizon.gtnhmixins.*
Modifications:
- removed Mixin environment initialization in GTNHMixinsCore
- added Mixin 0.7 support
- made MixinExtras initialization get suppressed if Mixin version is below 0.8.5
- made reflection support Mixin 0.7.11
- added call to "unimixins.mixinModidDecorator.refresh" after loading early mixins in GTNHMixinsCore and late mixins in LateMixinOrchestrationMixin
- Removed Mixin environment initialization in GTNHMixinsCore
- Added Mixin 0.7 support
- Made reflection support Mixin 0.7.11
- Added call to "unimixins.mixinModidDecorator.refresh" after loading early mixins in GTNHMixinsCore and late mixins in LateMixinOrchestrationMixin
- Made delegated transformer list get set to null before loading late mixins
- Added 1.8+ @Mod and @IFMLPlugin annotations
- Removed MixinExtras
License: LGPL3 (see LICENSE)
+34 -5
View File
@@ -1,19 +1,46 @@
def mixinExtrasVersion = "0.1.1"
configurations.create("shadowMixinExtras")
dependencies {
compileOnly('org.spongepowered:mixin:0.8.5')
compileOnly('com.github.LlamaLad7:MixinExtras:0.1.1')
shadow('com.github.LlamaLad7:MixinExtras:0.1.1')
compileOnly('org.spongepowered:mixin:0.8.7')
shadow(project(":module-common")) {
transitive = false
}
compileOnly(project(":module-mixingasm")) {
transitive = false
}
compileOnly("com.github.LlamaLad7:MixinExtras:$mixinExtrasVersion")
shadowMixinExtras("com.github.LlamaLad7:MixinExtras:$mixinExtrasVersion")
}
archivesBaseName = archivesBaseName.replace("-1.7.10", "-gtnhmixins-1.7.10")
apply plugin: "com.github.johnrengelman.shadow"
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
task mixinExtrasJar(type: ShadowJar) {
destinationDirectory = file("build/tmp")
archiveClassifier.set("mixinExtras")
archiveVersion = mixinExtrasVersion
configurations = [project.configurations.shadowMixinExtras]
relocate 'com.llamalad7.mixinextras', 'com.gtnewhorizon.mixinextras'
relocate 'org.objectweb.asm', 'org.spongepowered.asm.lib'
}
shadowJar {
classifier = ''
configurations = [project.configurations.shadow]
relocate 'com.llamalad7.mixinextras', 'com.gtnewhorizon.mixinextras'
relocate 'org.objectweb.asm', 'org.spongepowered.asm.lib'
relocate 'io.github.legacymoddingmc.unimixins.common', 'io.github.legacymoddingmc.unimixins.gtnhmixins.repackage.common'
from(zipTree(tasks.mixinExtrasJar.archiveFile)) {
eachFile({
setPath("data/legacy_gtnh_mixinextras/" + getPath().replaceAll("\\.class", ".klass"))
})
}
manifest {
attributes (
@@ -31,10 +58,12 @@ jar {
enabled = false
}
shadowJar.dependsOn mixinExtrasJar
// Obfuscation is unnecessary since this module does not reference Minecraft classes
reobf.enabled = false
ext.uniVersion = version
ext.extraVersion = "2.1.9"
ext.extraModid = "gtnhmixins"
ext.mcversion = "1.7.10"
version += "+" + ext.extraVersion
+1
View File
@@ -0,0 +1 @@
extraVersion=2.2.0
@@ -1,13 +1,37 @@
package com.gtnewhorizon.gtnhmixins;
import com.gtnewhorizon.gtnhmixins.core.GTNHMixinsCore;
import cpw.mods.fml.common.Mod;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Mod(modid = GTNHMixins.MODID, version = GTNHMixins.VERSION, name = GTNHMixins.NAME, acceptableRemoteVersions = "*")
@net.minecraftforge.fml.common.Mod(modid = GTNHMixins.MODID, version = GTNHMixins.VERSION, name = GTNHMixins.NAME, acceptableRemoteVersions = "*")
public class GTNHMixins {
public static final String NAME = "GTNHMixins";
public static final String MODID = "gtnhmixins";
public static final String VERSION = "GRADLETOKEN_VERSION";
public static final Logger LOGGER = LogManager.getLogger(NAME);
}
/**
* Logs at level info in de-obfuscated environment, level debug otherwise
*/
public static void log(String message) {
if (GTNHMixinsCore.isObf()) {
LOGGER.debug(message);
} else {
LOGGER.info(message);
}
}
/**
* Logs at level info in de-obfuscated environment, level debug otherwise
*/
public static void log(String message, Object... params) {
if (GTNHMixinsCore.isObf()) {
LOGGER.debug(message, params);
} else {
LOGGER.info(message, params);
}
}
}
@@ -1,5 +1,6 @@
package com.gtnewhorizon.gtnhmixins;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.Set;
@@ -11,7 +12,7 @@ import java.util.Set;
* If you want to add mixins that affect vanilla or forge, use and consult {@link IEarlyMixinLoader}
* <p>
* Implement this in any arbitrary class, along with the annotation @{@link LateMixin}. The annotation will cause the class to be constructed when mixins are
* ready to be queued. Return all of the mixin classes (under the mixinConfig's package) that you want to queue and send to Mixin library.
* ready to be queued. Return additional mixin classes if you want to dynamically enable or disable them, or an empty list if there is none.
*/
public interface ILateMixinLoader {
@@ -22,7 +23,7 @@ public interface ILateMixinLoader {
/**
* @param loadedMods Set of loaded modids, for use in discrimination of what mixins load
* @return mixin configurations to be queued and sent to Mixin library.
* @return additional mixin configurations to be queued and sent to Mixin library.
*/
List<String> getMixins(Set<String> loadedMods);
}
@Nonnull List<String> getMixins(Set<String> loadedMods);
}
@@ -1,20 +1,27 @@
package com.gtnewhorizon.gtnhmixins;
import io.github.legacymoddingmc.unimixins.common.abstraction.ComparableVersion;
import net.minecraft.launchwrapper.Launch;
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.transformer.Config;
import org.spongepowered.asm.mixin.transformer.ext.Extensions;
import org.spongepowered.asm.service.MixinService;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflection {
/* Reflection Fun */
public static final Class<?> pluginWrapperClass, mixinsClass, configClass, mixinConfigClass, mixinTransformerClass, mixinProcessorClass;
public static final Field coreModInstanceField, configField, mixinClassesField, processorField, extensionsField;
public static final Class<?> pluginWrapperClass, mixinsClass, configClass, mixinConfigClass, mixinTransformerClass, mixinProcessorClass, mixinServiceLaunchWrapperClass, mixinEnvironmentClass;
public static final Field coreModInstanceField, configField, mixinClassesField, processorField, extensionsField, delegatedTransformersField;
public static final Method registerConfigurationMethod, selectConfigsMethod, prepareConfigsMethod;
private static final boolean mixin_0_7;
static {
try {
mixin_0_7 = new ComparableVersion((String)Launch.blackboard.get("mixin.initialised")).compareTo(new ComparableVersion("0.8")) < 0;
/* We're referencing classes here in FML or SpongePowered Mixins, these _should_ be safe to call early */
/* PluginWrapper */
@@ -30,15 +37,17 @@ public class Reflection {
/* MixinTransformer */
mixinTransformerClass = Class.forName("org.spongepowered.asm.mixin.transformer.MixinTransformer");
Field processor = null;
try {
processor = mixinTransformerClass.getDeclaredField("processor");
processor.setAccessible(true);
} catch(NoSuchFieldException e){}
processorField = processor;
if(!mixin_0_7) {
// 0.8.5
processorField = mixinTransformerClass.getDeclaredField("processor");
processorField.setAccessible(true);
} else {
// 0.7.11
processorField = null;
}
/* MixinProcessor */
if(processorField != null) {
if(!mixin_0_7) {
// 0.8.5
mixinProcessorClass = Class.forName("org.spongepowered.asm.mixin.transformer.MixinProcessor");
selectConfigsMethod = mixinProcessorClass.getDeclaredMethod("selectConfigs", MixinEnvironment.class);
@@ -63,6 +72,20 @@ public class Reflection {
extensionsField.setAccessible(true);
}
/* MixinEnvironment / MixinServiceLaunchWrapper */
mixinServiceLaunchWrapperClass = Class.forName("org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper");
mixinEnvironmentClass = Class.forName("org.spongepowered.asm.mixin.MixinEnvironment");
if(!mixin_0_7) {
// 0.8.5
delegatedTransformersField = mixinServiceLaunchWrapperClass.getDeclaredField("delegatedTransformers");
delegatedTransformersField.setAccessible(true);
} else {
// 0.7.11
delegatedTransformersField = mixinEnvironmentClass.getDeclaredField("transformers");
delegatedTransformersField.setAccessible(true);
}
/* Config */
configClass = Class.forName("org.spongepowered.asm.mixin.transformer.Config");
configField = configClass.getDeclaredField("config");
@@ -80,7 +103,7 @@ public class Reflection {
public static void invokeSelectConfigs(Object transformer, MixinEnvironment env) {
try {
if(processorField != null) {
if(!mixin_0_7) {
// 0.8.5
Object processor = Reflection.processorField.get(transformer);
@@ -97,7 +120,7 @@ public class Reflection {
public static void invokePrepareConfigs(Object transformer, MixinEnvironment env) {
try {
if(processorField != null) {
if(!mixin_0_7) {
// 0.8.5
Object processor = Reflection.processorField.get(transformer);
@@ -111,4 +134,18 @@ public class Reflection {
throw new RuntimeException(e);
}
}
public static void setDelegatedTransformersField(Object newValue) {
try {
if(!mixin_0_7) {
// 0.8.5
delegatedTransformersField.set(MixinService.getService(), newValue);
} else {
// 0.7.11
delegatedTransformersField.set(MixinEnvironment.getCurrentEnvironment(), newValue);
}
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,195 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Phase;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Side;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
@SuppressWarnings("ForLoopReplaceableByForEach")
public abstract class AbstractBuilder {
private static final BooleanSupplier TRUE = () -> true;
protected @Nullable List<String> commonClasses;
protected @Nullable List<String> clientClasses;
protected @Nullable List<String> serverClasses;
protected @Nullable List<ITargetMod> requiredMods;
protected @Nullable List<BooleanSupplier> requiredModsCond;
protected @Nullable List<ITargetMod> excludedMods;
protected @Nullable List<BooleanSupplier> excludedModsCond;
protected @Nullable Phase phase;
protected @Nonnull Supplier<Boolean> applyIf = () -> true;
protected AbstractBuilder addCommonClasses(@Nonnull String... classes) {
Objects.requireNonNull(classes);
if (commonClasses == null) commonClasses = new ArrayList<>(4);
Collections.addAll(commonClasses, classes);
return this;
}
protected AbstractBuilder addClientClasses(@Nonnull String... classes) {
Objects.requireNonNull(classes);
if (clientClasses == null) clientClasses = new ArrayList<>(4);
Collections.addAll(clientClasses, classes);
return this;
}
protected AbstractBuilder addServerClasses(@Nonnull String... classes) {
Objects.requireNonNull(classes);
if (serverClasses == null) serverClasses = new ArrayList<>(4);
Collections.addAll(serverClasses, classes);
return this;
}
protected AbstractBuilder addSidedClasses(@Nonnull Side side, @Nonnull String... classes) {
Objects.requireNonNull(side);
switch (side) {
case COMMON:
return addCommonClasses(classes);
case CLIENT:
return addClientClasses(classes);
case SERVER:
return addServerClasses(classes);
default:
throw new IllegalArgumentException();
}
}
protected AbstractBuilder setApplyIf(@Nonnull Supplier<Boolean> applyIf) {
Objects.requireNonNull(applyIf);
this.applyIf = applyIf;
return this;
}
protected AbstractBuilder addRequiredMod(@Nonnull ITargetMod mod) {
return addRequiredModIf(mod, TRUE);
}
protected AbstractBuilder addRequiredModIf(@Nonnull ITargetMod mod, @Nonnull BooleanSupplier condition) {
Objects.requireNonNull(mod);
Objects.requireNonNull(condition);
if (requiredMods == null) requiredMods = new ArrayList<>(2);
if (requiredModsCond == null) requiredModsCond = new ArrayList<>(2);
requiredMods.add(mod);
requiredModsCond.add(condition);
return this;
}
protected AbstractBuilder addExcludedMod(@Nonnull ITargetMod mod) {
return addExcludedModIf(mod, TRUE);
}
protected AbstractBuilder addExcludedModIf(@Nonnull ITargetMod mod, @Nonnull BooleanSupplier condition) {
Objects.requireNonNull(mod);
Objects.requireNonNull(condition);
if (excludedMods == null) excludedMods = new ArrayList<>(2);
if (excludedModsCond == null) excludedModsCond = new ArrayList<>(2);
excludedMods.add(mod);
excludedModsCond.add(condition);
return this;
}
protected void addClassesForCurrentSide(List<String> toLoad, List<String> toNotLoad) {
final boolean isClient = FMLLaunchHandler.side().isClient();
if (commonClasses != null) toLoad.addAll(commonClasses);
if (clientClasses != null) {
if (isClient) toLoad.addAll(clientClasses);
else toNotLoad.addAll(clientClasses);
}
if (serverClasses != null) {
if (!isClient) toLoad.addAll(serverClasses);
else toNotLoad.addAll(serverClasses);
}
}
protected void addAllClassesTo(List<String> list) {
if (commonClasses != null) list.addAll(commonClasses);
if (clientClasses != null) list.addAll(clientClasses);
if (serverClasses != null) list.addAll(serverClasses);
}
protected void addAllTargetsTo(Set<ITargetMod> set) {
if (requiredMods != null) {
set.addAll(requiredMods);
}
if (excludedMods != null) {
set.addAll(excludedMods);
}
}
protected boolean shouldLoad(Set<ITargetMod> loadedTargets) {
return allRequiredModsPresent(loadedTargets) && noExcludedModsPresent(loadedTargets);
}
protected boolean allRequiredModsPresent(Set<ITargetMod> loadedTargets) {
if (requiredMods == null) return true;
boolean hasCond = requiredModsCond != null;
for (int i = 0; i < requiredMods.size(); i++) {
if (hasCond && !requiredModsCond.get(i).getAsBoolean())
continue;
if (!loadedTargets.contains(requiredMods.get(i))) return false;
}
return true;
}
protected boolean noExcludedModsPresent(Set<ITargetMod> loadedTargets) {
if (excludedMods == null) return true;
boolean hasCond = excludedModsCond != null;
for (int i = 0; i < excludedMods.size(); i++) {
if (hasCond && !excludedModsCond.get(i).getAsBoolean())
continue;
if (loadedTargets.contains(excludedMods.get(i))) return false;
}
return true;
}
protected static void loadClasses(List<AbstractBuilder> builders, Set<ITargetMod> loadedTargets, List<String> toLoad, List<String> toNotLoad) {
for (int i = 0; i < builders.size(); i++) {
AbstractBuilder builder = builders.get(i);
if (builder.shouldLoad(loadedTargets)) {
builder.addClassesForCurrentSide(toLoad, toNotLoad);
} else {
builder.addAllClassesTo(toNotLoad);
}
}
}
protected static Set<ITargetMod> getLoadedTargetedMods(List<AbstractBuilder> builders, Phase loadingPhase, Set<String> loadedCoreMods, Set<String> loadedMods) {
Set<ITargetMod> targets = new HashSet<>();
for (int i = 0; i < builders.size(); i++) {
builders.get(i).addAllTargetsTo(targets);
}
Iterator<ITargetMod> iterator = targets.iterator();
List<ITargetMod> notPresent = new ArrayList<>();
while (iterator.hasNext()) {
ITargetMod target = iterator.next();
TargetModBuilder builder = target.getBuilder();
TargetModBuilder.validateBuilder(builder, target, loadingPhase);
if (!builder.isTargetPresent(loadedCoreMods, loadedMods)) {
notPresent.add(target);
iterator.remove();
}
}
if (loadingPhase == null) {
GTNHMixins.log("ITargetMods found: {}", targets.toString());
GTNHMixins.log("ITargetMods not found: {}", notPresent.toString());
} else {
GTNHMixins.log("ITargetMods found during Phase {}: {}", loadingPhase, targets.toString());
GTNHMixins.log("ITargetMods not found during Phase {}: {}", loadingPhase, notPresent.toString());
}
return targets;
}
}
@@ -0,0 +1,18 @@
package com.gtnewhorizon.gtnhmixins.builders;
public interface IBaseTransformer {
/**
* Phase is only used for early and late mixins from gtnh mixins
*/
enum Phase {
EARLY,
LATE
}
enum Side {
COMMON,
CLIENT,
SERVER
}
}
@@ -0,0 +1,141 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* The IMixins interface provides a flexible way of declaring and registering mixins during runtime.
* Registration is done based on conditions such as user defined logic, looking configs, loading side (CLIENT, COMMON, SERVER)
* or looking at the presence or absence of mods declared using the {@link com.gtnewhorizon.gtnhmixins.builders.ITargetMod} interface.
* This interface must be implemented on an enum.
* <pre>
* {@code
* public enum Mixins implements IMixins {
*
* EXAMPLE_MIXIN(new MixinBuilder("Hello") // optional comment
* .addRequiredMod(TargetMod.IC2) // optional
* .addExcludedMod(...) // optional
* .setPhase(Phase.EARLY) // optional, required for gtnh mixins
* .addCommonMixins("........") // mixins that needs to be both on client and server
* .addClientMixins("........") // mixins only for the client
* .addServerMixins("........") // mixins only for the dedicated server
* .setApplyIf(() -> Config.yourconfig....)); // optional
* // it is required to have at least one mixin class
*
* private final MixinBuilder builder;
*
* Mixins(MixinBuilder builder) {
* this.builder = builder;
* }
*
* @Override
* public MixinBuilder getBuilder() {
* return this.builder;
* }
* }
* }
* </pre>
* <p>
* If you do not need very complex logic in your enum, you can instantiate
* the MixinBuilder in the enum constructor and declare simplistic enum entries.
*
* <pre>
* {@code
* public enum Mixins implements IMixins {
*
* CLIENT_MIXIN1(Side.CLIENT, "MixinClientClass1"),
* CLIENT_MIXIN2(Side.CLIENT, "MixinClientClass2"),
* COMMON_MIXIN(Side.COMMON, "MixinCommonClass");
*
* private final MixinBuilder builder;
*
* Mixins(Side side, String... mixins) {
* builder = new MixinBuilder().addSidedMixins(side, mixins);
* }
*
* @Override
* public MixinBuilder getBuilder() {
* return this.builder;
* }
* }
* }
* </pre>
*/
@SuppressWarnings("unused")
public interface IMixins extends IBaseTransformer {
@Nonnull
MixinBuilder getBuilder();
/**
* Returns the list of mixins that should be loaded by your {@link org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin} implementation.
* <p>
* You may call it as such :
* <pre>
* {@code
* @Override
* public List<String> getMixins() {
* return IMixins.getMixins(YourMixinEnum.class);
* }
* }
* </pre>
*/
static <E extends Enum<E> & IMixins> List<String> getMixins(Class<E> mixinsEnum) {
final List<String> toLoad = new ArrayList<>();
final List<String> toNotLoad = new ArrayList<>();
MixinBuilder.loadMixins(mixinsEnum, toLoad, toNotLoad);
GTNHMixins.log("Not loading the following mixins: {}", toNotLoad);
for (String mixin : toLoad) {
GTNHMixins.log("Loading {}", mixin);
}
return toLoad;
}
/**
* Returns the list of mixins that should be loaded by your {@link com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader} implementation.
* Note that you also need to implement {@link cpw.mods.fml.relauncher.IFMLLoadingPlugin} for early mixins to work.
* <p>
* You may call it as such :
* <pre>
* {@code
* @Override
* public List<String> getMixins(Set<String> loadedCoreMods) {
* return IMixins.getEarlyMixins(YourMixinEnum.class, loadedCoreMods);
* }
* }
* </pre>
*/
static <E extends Enum<E> & IMixins> List<String> getEarlyMixins(Class<E> mixinsEnum, Set<String> loadedCoreMods) {
final List<String> toLoad = new ArrayList<>();
final List<String> toNotLoad = new ArrayList<>();
MixinBuilder.loadEarlyMixins(mixinsEnum, loadedCoreMods, toLoad, toNotLoad);
GTNHMixins.log("Not loading the following EARLY mixins: {}", toNotLoad);
return toLoad;
}
/**
* Returns the list of mixins that should be loaded by your {@link com.gtnewhorizon.gtnhmixins.ILateMixinLoader} implementation.
* Note that you also need to annotate your class with {@link com.gtnewhorizon.gtnhmixins.LateMixin} for late mixins to work.
* <p>
* You may call it as such :
* <pre>
* {@code
* @Override
* public List<String> getMixins(Set<String> loadedMods) {
* return IMixins.getLateMixins(YourMixinEnum.class, loadedMods);
* }
* }
* </pre>
*/
static <E extends Enum<E> & IMixins> List<String> getLateMixins(Class<E> mixinsEnum, Set<String> loadedMods) {
final List<String> toLoad = new ArrayList<>();
final List<String> toNotLoad = new ArrayList<>();
MixinBuilder.loadLateMixins(mixinsEnum, loadedMods, toLoad, toNotLoad);
GTNHMixins.log("Not loading the following LATE mixins: {}", toNotLoad.toString());
return toLoad;
}
}
@@ -0,0 +1,127 @@
package com.gtnewhorizon.gtnhmixins.builders;
import net.minecraftforge.fml.common.versioning.ComparableVersion;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.function.Predicate;
/**
* This interface is used to identify mods targeted by the {@link com.gtnewhorizon.gtnhmixins.builders.IMixins}
* and {@link com.gtnewhorizon.gtnhmixins.builders.ITransformers} system. It provides a flexible mechanism for
* conditionally applying mixins or asm transformers based on the presence or absence of other mods during runtime.
* You can declare your targeted mods in an enum, although it's not required to be an enum.
* <pre>
* {@code
* public enum TargetMods implements ITargetMod {
*
* COFHCORE("cofh.asm.LoadingPlugin", "CoFHCore"),
* OPTIFINE("optifine.OptiFineForgeTweaker", "Optifine");
*
* private final TargetModBuilder builder;
*
* TargetMods(String coreModClass, String modId) {
* this.builder = new TargetModBuilder().setCoreModClass(coreModClass).setModId(modId);
* }
*
* @Override
* public getBuidler() {
* return builder;
* }
* }
* }
* </pre>
*/
public interface ITargetMod {
@Nonnull
TargetModBuilder getBuilder();
class Predicates {
public static Predicate<String> equals(String str) {
return s -> s.equals(str);
}
public static Predicate<String> contains(String str) {
return s -> s.contains(str);
}
public static Predicate<String> endsWith(String str) {
return s -> s.endsWith(str);
}
public static Predicate<String> startsWith(String str) {
return s -> s.startsWith(str);
}
public static Predicate<String> matches(String str) {
return s -> s.matches(str);
}
/**
* Returns a predicate that is true if the version provided is greater than the version it is compared to.
*/
public static Predicate<String> versionGreater(String version) {
return s -> new ComparableVersion(version).compareTo(new ComparableVersion(s)) > 0;
}
/**
* Returns a predicate that is true if the version provided is lower than the version it is compared to.
*/
public static Predicate<String> versionLower(String version) {
return s -> new ComparableVersion(version).compareTo(new ComparableVersion(s)) < 0;
}
/**
* If the class targeted by this test has a {@link cpw.mods.fml.common.Mod} annotation, this test will parse the annotation and
* test it against the predicates. At least one of the predicates must be non-null, this test will return true if all the predicates are true.
*/
public static Predicate<ClassNode> testModAnnotation(Predicate<String> modIdTest, Predicate<String> modNameTest, Predicate<String> modVersionTest) {
if (modIdTest == null && modNameTest == null && modVersionTest == null) {
throw new IllegalArgumentException("At least one of the Mod Annotation test predicates must be non null");
}
return cn -> {
if (cn.visibleAnnotations == null) return false;
for (AnnotationNode annotation : cn.visibleAnnotations) {
if ("Lcpw/mods/fml/common/Mod;".equals(annotation.desc)) {
if (annotation.values != null) {
String modId = "";
String modName = "";
String modVersion = "";
final List<Object> values = annotation.values;
final int size = values.size();
for (int i = 0; i < size - 1; i += 2) {
Object name = values.get(i);
Object value = values.get(i + 1);
if (name instanceof String && value instanceof String) {
if ("modid".equals(name)) {
modId = (String) value;
} else if ("name".equals(name)) {
modName = (String) value;
} else if ("version".equals(name)) {
modVersion = (String) value;
}
}
}
boolean test = true;
if (modIdTest != null) {
test = /*test &&*/ modIdTest.test(modId);
}
if (modNameTest != null) {
test = test && modNameTest.test(modName);
}
if (modVersionTest != null) {
test = test && modVersionTest.test(modVersion);
}
return test;
}
}
}
return false;
};
}
}
}
@@ -0,0 +1,95 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
/**
* The ITransformers interface provides a flexible way of declaring and registering {@link net.minecraft.launchwrapper.IClassTransformer} during runtime.
* Registration is done based on conditions such as user defined logic, looking configs, loading side (CLIENT, COMMON, SERVER)
* or looking at the presence or absence of mods declared using the {@link com.gtnewhorizon.gtnhmixins.builders.ITargetMod} interface.
* This interface must be implemented on an enum.
* <pre>
* {@code
* public enum ASMTransformers implements ITransformers {
*
* EXAMPLE_TRANSFORMER(new TransformerBuilder("Hello") // optional comment
* .addRequiredMod(TargetMod.IC2) // optional
* .addExcludedMod(...) // optional
* .addCommonTransformers("........") // transformers that needs to be both on client and server
* .addClientTransformers("........") // transformers only for the client
* .addServerTransformers("........") // transformers only for the dedicated server
* .setApplyIf(() -> Config.yourconfig....)); // optional
* // it is required to have at least one transformers class
*
* private final TransformerBuilder builder;
*
* ASMTransformers(TransformerBuilder builder) {
* this.builder = builder;
* }
*
* @Override
* public TransformerBuilder getBuilder() {
* return this.builder;
* }
* }
* }
* </pre>
* <p>
* If you do not need very complex logic in your enum, you can instantiate
* the TransformerBuilder in the enum constructor and declare simplistic enum entries.
*
* <pre>
* {@code
* public enum ASMTransformers implements ITransformers {
*
* CLIENT_TRANSFORMER1(Side.CLIENT, "TransformerClientClass1"),
* CLIENT_TRANSFORMER2(Side.CLIENT, "TransformerClientClass2"),
* COMMON_TRANSFORMER(Side.COMMON, "TransformerCommonClass");
*
* private final TransformerBuilder builder;
*
* ASMTransformers(Side side, String... transformers) {
* builder = new TransformerBuilder().addSidedTransformers(side, transformers);
* }
*
* @Override
* public TransformerBuilder getBuilder() {
* return this.builder;
* }
* }
* }
* </pre>
*/
@SuppressWarnings("unused")
public interface ITransformers extends IBaseTransformer {
@Nonnull
TransformerBuilder getBuilder();
/**
* Returns the list of transformers that should be loaded by your {@link cpw.mods.fml.relauncher.IFMLLoadingPlugin} implementation.
* <p>
* You may call it as such :
* <pre>
* {@code
* @Override
* public String[] getASMTransformerClass() {
* return ITransformers.getTransformers(YourTransformerEnum.class);
* }
* }
* </pre>
*/
static <E extends Enum<E> & ITransformers> String[] getTransformers(Class<E> transformerEnum) {
final List<String> toLoad = new ArrayList<>();
final List<String> toNotLoad = new ArrayList<>();
TransformerBuilder.loadTransformers(transformerEnum, toLoad, toNotLoad);
GTNHMixins.log("Not loading the following transformers: {}", toNotLoad);
for (String transformer : toLoad) {
GTNHMixins.log("Loading ITransformer {}", transformer);
}
return toLoad.toArray(new String[0]);
}
}
@@ -0,0 +1,153 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Phase;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Side;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
@SuppressWarnings("unused")
public class MixinBuilder extends AbstractBuilder {
public MixinBuilder() {}
public MixinBuilder(String description) {}
/**
* Add mixin classes that should be loaded both on the client and server
*/
public MixinBuilder addCommonMixins(@Nonnull String... mixins) {
return (MixinBuilder) super.addCommonClasses(mixins);
}
/**
* Add mixin classes that should only be loaded on the client
*/
public MixinBuilder addClientMixins(@Nonnull String... mixins) {
return (MixinBuilder) super.addClientClasses(mixins);
}
/**
* Add mixin classes that should only be loaded on the dedicated server
*/
public MixinBuilder addServerMixins(@Nonnull String... mixins) {
return (MixinBuilder) super.addServerClasses(mixins);
}
/**
* Add mixins classes that should be loaded on the specified side
*/
public MixinBuilder addSidedMixins(@Nonnull Side side, @Nonnull String... mixins) {
return (MixinBuilder) super.addSidedClasses(side, mixins);
}
/**
* Specify a condition that needs to be true for this mixin to load, such as checking for a config boolean.
*/
@Override
public MixinBuilder setApplyIf(@Nonnull Supplier<Boolean> applyIf) {
return (MixinBuilder) super.setApplyIf(applyIf);
}
/**
* Specify mods that are required to be present for this mixin to load
*/
@Override
public MixinBuilder addRequiredMod(@Nonnull ITargetMod mod) {
return (MixinBuilder) super.addRequiredMod(mod);
}
/**
* Specify mods that are required to be present for this mixin to load and the condition is true. The condition is evaluated lazily during mixin loading.
*/
@Override
public MixinBuilder addRequiredModIf(@Nonnull ITargetMod mod, @Nonnull BooleanSupplier condition) {
return (MixinBuilder) super.addExcludedModIf(mod, condition);
}
/**
* Specify mods that will disable this mixin if they are present
*/
@Override
public MixinBuilder addExcludedMod(@Nonnull ITargetMod mod) {
return (MixinBuilder) super.addExcludedMod(mod);
}
/**
* Specify mods that will disable this mixin if they are present and the condition is true. The condition is evaluated lazily during mixin loading.
*/
@Override
public MixinBuilder addExcludedModIf(@Nonnull ITargetMod mod, @Nonnull BooleanSupplier condition) {
return (MixinBuilder) super.addExcludedModIf(mod, condition);
}
/**
* Mixins registered from a {@link com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader} need to set this to
* {@link com.gtnewhorizon.gtnhmixins.builders.IMixins.Phase#EARLY}.
* <p>
* Mixins registered from a {@link com.gtnewhorizon.gtnhmixins.ILateMixinLoader} need to set this to
* {@link com.gtnewhorizon.gtnhmixins.builders.IMixins.Phase#LATE}.
* <p>
* Mixins registered from a {@link org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin} need to leave this
* null.
*/
public MixinBuilder setPhase(Phase phase) {
this.phase = phase;
return this;
}
protected static <E extends Enum<E> & IMixins> void loadMixins(Class<E> mixinsEnum, List<String> toLoad, List<String> toNotLoad) {
List<AbstractBuilder> builders = getEnabledBuildersForPhase(mixinsEnum, null, toNotLoad);
Set<ITargetMod> loadedTargets = getLoadedTargetedMods(builders, null, Collections.emptySet(), Collections.emptySet());
loadClasses(builders, loadedTargets, toLoad, toNotLoad);
}
protected static <E extends Enum<E> & IMixins> void loadEarlyMixins(Class<E> mixinsEnum, Set<String> loadedCoreMods, List<String> toLoad, List<String> toNotLoad) {
List<AbstractBuilder> builders = getEnabledBuildersForPhase(mixinsEnum, Phase.EARLY, toNotLoad);
Set<ITargetMod> loadedTargets = getLoadedTargetedMods(builders, Phase.EARLY, loadedCoreMods, Collections.emptySet());
loadClasses(builders, loadedTargets, toLoad, toNotLoad);
}
protected static <E extends Enum<E> & IMixins> void loadLateMixins(Class<E> mixinsEnum, Set<String> loadedMods, List<String> toLoad, List<String> toNotLoad) {
List<AbstractBuilder> builders = getEnabledBuildersForPhase(mixinsEnum, Phase.LATE, toNotLoad);
Set<ITargetMod> loadedTargets = getLoadedTargetedMods(builders, Phase.LATE, Collections.emptySet(), loadedMods);
loadClasses(builders, loadedTargets, toLoad, toNotLoad);
}
private static <E extends Enum<E> & IMixins> List<AbstractBuilder> getEnabledBuildersForPhase(Class<E> mixinsEnum, Phase loadingPhase, List<String> toNotLoad) {
final E[] constants = mixinsEnum.getEnumConstants();
List<AbstractBuilder> list = new ArrayList<>(constants.length + 1);
for (E mixin : constants) {
MixinBuilder builder = mixin.getBuilder();
validateBuilder(builder, mixin, loadingPhase != null);
if (builder.phase != loadingPhase) continue;
if (builder.applyIf.get()) {
list.add(builder);
} else {
builder.addAllClassesTo(toNotLoad);
}
}
return list;
}
private static void validateBuilder(MixinBuilder builder, Enum<?> mixin, boolean requirePhase) {
if (builder == null) {
throw new NullPointerException("Builder is null for IMixins : " + mixin.name());
}
int count = 0;
if (builder.commonClasses != null) count += builder.commonClasses.size();
if (builder.clientClasses != null) count += builder.clientClasses.size();
if (builder.serverClasses != null) count += builder.serverClasses.size();
if (count == 0) {
throw new IllegalArgumentException("No mixin class registered for IMixins : " + mixin.name());
}
if (requirePhase && builder.phase == null) {
throw new IllegalArgumentException("No Phase specified for IMixins : " + mixin.name());
}
}
}
@@ -0,0 +1,162 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Phase;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.service.MixinService;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Set;
import java.util.function.Predicate;
@SuppressWarnings("unused")
public class TargetModBuilder implements ITargetMod {
private String coreModClass;
private String modId;
private String targetClass;
private Predicate<ClassNode> classNodeTest;
/**
* Fully qualified name of the class that implements the IFMLLoadingPlugin interface in this targeted mod. This can
* only be used by GTNH mixins early mixins system.
* <p>
* For example : "com.gtnewhorizons.angelica.loading.AngelicaTweaker"
*/
public TargetModBuilder setCoreModClass(String coreModClass) {
this.coreModClass = coreModClass;
return this;
}
/**
* The "modid" of the targeted mod, found in the @Mod(modid=) annotation. This can only be used by GTNH mixins late
* mixins system.
*/
public TargetModBuilder setModId(String modId) {
this.modId = modId;
return this;
}
/**
* Fully qualified name of a class of your choice contained in the targeted mod. Typically, its main mod class.
* <p>
* For example : "com.gtnewhorizons.angelica.AngelicaMod"
*/
public TargetModBuilder setTargetClass(String targetClass) {
this.targetClass = targetClass;
return this;
}
/**
* A conditional check that will test the raw (un-transformed) bytecode of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
*/
public TargetModBuilder setClassNodeTest(Predicate<ClassNode> classNodeTest) {
this.classNodeTest = classNodeTest;
return this;
}
/**
* A conditional check that will test the contents of the @Mod annotation of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
* Tests if the modId of the target class is equals to the modId provided.
*/
public TargetModBuilder testModID(String modId) {
this.classNodeTest = Predicates.testModAnnotation(Predicates.equals(modId), null, null);
return this;
}
/**
* A conditional check that will test the contents of the @Mod annotation of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
* Tests if the modId and modVersion of the target class are equal to the modId and modVersion provided.
*/
public TargetModBuilder testModVersion(String modId, String modVersion) {
this.classNodeTest = Predicates.testModAnnotation(Predicates.equals(modId), null, Predicates.equals(modVersion));
return this;
}
/**
* A conditional check that will test the contents of the @Mod annotation of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
* Tests if the modId of the target class is equals to the modId provided and tests the modVersion with the provided test.
*/
public TargetModBuilder testModVersion(String modId, Predicate<String> modVersionTest) {
this.classNodeTest = Predicates.testModAnnotation(Predicates.equals(modId), null, modVersionTest);
return this;
}
/**
* A conditional check that will test the contents of the @Mod annotation of the class specified in
* {@link TargetModBuilder#setTargetClass}. Requires you to specify a class to target via {@link TargetModBuilder#setTargetClass}.
* Some arguments can be null but not all at once.
*/
public TargetModBuilder testModAnnotation(Predicate<String> modIdTest, Predicate<String> modNameTest, Predicate<String> modVersionTest) {
this.classNodeTest = Predicates.testModAnnotation(modIdTest, modNameTest, modVersionTest);
return this;
}
protected static void validateBuilder(TargetModBuilder builder, ITargetMod target, Phase phaseIn) {
if (builder == null) {
throw new NullPointerException("TargetModBuilder is null for ITargetMod " + target);
}
if (builder.modId == null && builder.coreModClass == null && builder.targetClass == null && builder.classNodeTest == null) {
throw new IllegalArgumentException("No information at all provided by ITargetMod " + target);
}
if (phaseIn == Phase.EARLY) {
if (builder.coreModClass == null && builder.targetClass == null && builder.classNodeTest == null) {
throw new IllegalArgumentException("Not enough information provided by ITargetMod " + target + " used by early mixins");
}
} else if (phaseIn == Phase.LATE) {
if (builder.modId == null && builder.targetClass == null && builder.classNodeTest == null) {
throw new IllegalArgumentException("Not enough information provided by ITargetMod " + target + " used by late mixins");
}
} else {
if (builder.targetClass == null && builder.classNodeTest == null) {
throw new IllegalArgumentException("Not enough information provided by ITargetMod " + target);
}
}
if (builder.classNodeTest != null && builder.targetClass == null) {
throw new IllegalArgumentException("ITargetMod " + target + " uses a ClassNode test but doesn't specify the target class");
}
}
private boolean foundTargetClass;
protected boolean isTargetPresent(Set<String> loadedCoreMods, Set<String> loadedMods) {
// 1. check coremod class (early mixins only)
if (!loadedCoreMods.isEmpty() && this.coreModClass != null && loadedCoreMods.contains(this.coreModClass)) {
return true;
}
// 2. check modID (late mixins only)
if (!loadedMods.isEmpty() && this.modId != null && loadedMods.contains(this.modId)) {
return true;
}
// 3. check class
if (this.targetClass != null) {
if (foundTargetClass) {
return true;
}
try {
ClassNode classNode = MixinService.getService().getBytecodeProvider().getClassNode(this.targetClass, false);
if (this.classNodeTest == null) {
foundTargetClass = true;
return true;
}
// 4. test bytecode of this class
final boolean test = this.classNodeTest.test(classNode);
if (test) {
foundTargetClass = true;
return true;
}
} catch (ClassNotFoundException | IOException ignored) {}
}
return false;
}
@Nonnull
@Override
public TargetModBuilder getBuilder() {
return this;
}
}
@@ -0,0 +1,119 @@
package com.gtnewhorizon.gtnhmixins.builders;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer.Side;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
public class TransformerBuilder extends AbstractBuilder {
public TransformerBuilder() {}
public TransformerBuilder(String description) {}
/**
* Add transformer classes that should be loaded both on the client and server
*/
public TransformerBuilder addCommonTransformers(@Nonnull String... transformers) {
return (TransformerBuilder) super.addCommonClasses(transformers);
}
/**
* Add transformer classes that should only be loaded on the client
*/
public TransformerBuilder addClientTransformers(@Nonnull String... transformers) {
return (TransformerBuilder) super.addClientClasses(transformers);
}
/**
* Add transformer classes that should only be loaded on the dedicated server
*/
public TransformerBuilder addServerTransformers(@Nonnull String... transformers) {
return (TransformerBuilder) super.addServerClasses(transformers);
}
/**
* Add transformer classes that should be loaded on the specified side
*/
public TransformerBuilder addSidedTransformers(@Nonnull Side side, @Nonnull String... transformers) {
return (TransformerBuilder) super.addSidedClasses(side, transformers);
}
/**
* Specify a condition that needs to be true for this transformer to load, such as checking for a config boolean.
*/
@Override
public TransformerBuilder setApplyIf(@Nonnull Supplier<Boolean> applyIf) {
return (TransformerBuilder) super.setApplyIf(applyIf);
}
/**
* Specify mods that are required to be present for this transformer to load
*/
public TransformerBuilder addRequiredMod(@Nonnull ITargetMod mod) {
return (TransformerBuilder) super.addRequiredMod(mod);
}
/**
* Specify mods that are required to be present for this transformer to load and the condition is true. The condition is evaluated lazily during transformer loading.
*/
@Override
public TransformerBuilder addRequiredModIf(@Nonnull ITargetMod mod, @Nonnull BooleanSupplier condition) {
return (TransformerBuilder) super.addExcludedModIf(mod, condition);
}
/**
* Specify mods that will disable this transformer if they are present
*/
public TransformerBuilder addExcludedMod(@Nonnull ITargetMod mod) {
return (TransformerBuilder) super.addExcludedMod(mod);
}
/**
* Specify mods that will disable this transformer if they are present and the condition is true. The condition is evaluated lazily during transformer loading.
*/
@Override
public TransformerBuilder addExcludedModIf(@Nonnull ITargetMod mod, @Nonnull BooleanSupplier condition) {
return (TransformerBuilder) super.addExcludedModIf(mod, condition);
}
protected static <E extends Enum<E> & ITransformers> void loadTransformers(Class<E> transformerEnum, List<String> toLoad, List<String> toNotLoad) {
List<AbstractBuilder> builders = getEnabledBuildersForPhase(transformerEnum, toNotLoad);
Set<ITargetMod> loadedTargets = getLoadedTargetedMods(builders, null, Collections.emptySet(), Collections.emptySet());
loadClasses(builders, loadedTargets, toLoad, toNotLoad);
}
private static <E extends Enum<E> & ITransformers> List<AbstractBuilder> getEnabledBuildersForPhase(Class<E> transformerEnum, List<String> toNotLoad) {
final E[] constants = transformerEnum.getEnumConstants();
List<AbstractBuilder> list = new ArrayList<>(constants.length + 1);
for (E transformer : constants) {
TransformerBuilder builder = transformer.getBuilder();
validateBuilder(builder, transformer);
if (builder.applyIf.get()) {
list.add(builder);
} else {
builder.addAllClassesTo(toNotLoad);
}
}
return list;
}
private static void validateBuilder(TransformerBuilder builder, Enum<?> transformer) {
if (builder == null) {
throw new NullPointerException("Builder is null for ITransformer : " + transformer.name());
}
int count = 0;
if (builder.commonClasses != null) count += builder.commonClasses.size();
if (builder.clientClasses != null) count += builder.clientClasses.size();
if (builder.serverClasses != null) count += builder.serverClasses.size();
if (count == 0) {
throw new IllegalArgumentException("No transformer class registered for ITransformer : " + transformer.name());
}
}
}
@@ -2,17 +2,16 @@ package com.gtnewhorizon.gtnhmixins.core;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Runnables;
import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import com.gtnewhorizon.gtnhmixins.Reflection;
import com.llamalad7.mixinextras.MixinExtrasBootstrap;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import io.github.legacymoddingmc.unimixins.gtnhmixins.GTNHMixinsModule;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.transformer.Config;
import io.github.legacymoddingmc.unimixins.gtnhmixins.GTNHMixinsModule;
import org.spongepowered.asm.service.MixinService;
import java.util.ArrayList;
import java.util.HashSet;
@@ -24,29 +23,29 @@ import java.util.Set;
@IFMLLoadingPlugin.SortingIndex(Integer.MIN_VALUE + 5)
@IFMLLoadingPlugin.Name(GTNHMixinsCore.PLUGIN_NAME)
@IFMLLoadingPlugin.TransformerExclusions("com.gtnewhorizon.gtnhmixins.core")
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.SortingIndex(Integer.MIN_VALUE + 5)
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.Name(GTNHMixinsCore.PLUGIN_NAME)
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions("com.gtnewhorizon.gtnhmixins.core")
public class GTNHMixinsCore implements IFMLLoadingPlugin {
public static final String PLUGIN_NAME = "GTNHMixins Core Plugin";
public static final Logger LOGGER = LogManager.getLogger(PLUGIN_NAME);
private static boolean isObf;
/**
* Core mods we need to Manually look for:
* (classNameToFind, coreModNameToUse)
* (classNameToFind, coreModNameToUse)
*/
public static final Map<String, String> MANUALLY_IDENTIFIED_COREMODS = ImmutableMap.<String, String>builder()
.put("optifine.OptiFineForgeTweaker", "optifine.OptiFineForgeTweaker")
.put("codechicken.lib.asm.ModularASMTransformer", "codechicken.lib")
.put("org.bukkit.World", "Bukkit")
.build();
.put("optifine.OptiFineForgeTweaker", "optifine.OptiFineForgeTweaker")
.put("codechicken.lib.asm.ModularASMTransformer", "codechicken.lib")
.put("org.bukkit.World", "Bukkit")
.build();
static {
LOGGER.info("Initializing GTNHMixins Core");
GTNHMixins.LOGGER.debug("Initializing GTNHMixins Core");
GTNHMixinsModule.init();
if(GTNHMixinsModule.isMixinExtrasEnabled()) {
MixinExtrasBootstrap.init();
}
}
@Override
public String[] getASMTransformerClass() {
return new String[0];
@@ -64,23 +63,22 @@ public class GTNHMixinsCore implements IFMLLoadingPlugin {
private Set<String> getLoadedCoremods(List<?> coremodList) {
final Set<String> loadedCoremods = new HashSet<>();
// Manual "Coremod" identification. Be sure you're not loading classes too early
MANUALLY_IDENTIFIED_COREMODS.forEach((clsName, coreModIdentifier) -> {
try {
Class.forName(clsName);
MixinService.getService().getBytecodeProvider().getClassNode(clsName, false);
loadedCoremods.add(coreModIdentifier);
} catch (ClassNotFoundException ignored) {}
} catch (Exception ignored) {}
});
// Grab a list of tweakers (fastcraft)
for (Object tweak : (ArrayList<?>)Launch.blackboard.get("Tweaks")) {
for (Object tweak : (ArrayList<?>) Launch.blackboard.get("Tweaks")) {
final Object obj;
try {
obj = Reflection.pluginWrapperClass.isInstance(tweak) ? Reflection.coreModInstanceField.get(tweak) : tweak;
obj = Reflection.pluginWrapperClass.isInstance(tweak) ? Reflection.coreModInstanceField.get(tweak) : tweak;
loadedCoremods.add(obj.toString().split("@")[0]);
} catch(Exception ignored) {}
} catch (Exception ignored) {}
}
// Now coremods
for (Object coremod : coremodList) {
@@ -91,43 +89,49 @@ public class GTNHMixinsCore implements IFMLLoadingPlugin {
}
return loadedCoremods;
}
@Override
public void injectData(Map<String, Object> data) {
LOGGER.info("Examining core mod list");
isObf = (boolean) data.get("runtimeDeobfuscationEnabled");
GTNHMixins.log("Searching coremodList for IEarlyMixinLoaders");
final Object coremodList = data.get("coremodList");
if (coremodList instanceof List) {
final Set<String> loadedCoremods = getLoadedCoremods((List<?>) coremodList);
LOGGER.info("LoadedCoreMods {}", loadedCoremods.toString());
for (Object coremod : (List<?>)coremodList) {
GTNHMixins.log("LoadedCoreMods {}", loadedCoremods.toString());
for (Object coremod : (List<?>) coremodList) {
// Identify any coremods that are `IEarlyMixinLoader`, and inject any relevant mixins
try {
Object theMod = Reflection.coreModInstanceField.get(coremod);
if (theMod instanceof IEarlyMixinLoader) {
final IEarlyMixinLoader loader = (IEarlyMixinLoader)theMod;
GTNHMixins.log("Loading mixins from IEarlyMixinLoader [{}]", theMod.getClass().getName());
final IEarlyMixinLoader loader = (IEarlyMixinLoader) theMod;
final String mixinConfig = loader.getMixinConfig();
final Config config = Config.create(mixinConfig);
final Config config = Config.create(mixinConfig, null);
final List<String> mixins = loader.getMixins(loadedCoremods);
for(String mixin : mixins) {
LOGGER.info("Loading [{}] {}", mixinConfig, mixin);
for (String mixin : mixins) {
GTNHMixins.log("Loading [{}] {}", mixinConfig, mixin);
}
Reflection.mixinClassesField.set(Reflection.configField.get(config), mixins);
Reflection.registerConfigurationMethod.invoke(null, config);
}
} catch (Exception e) {
LOGGER.error("Unexpected error", e);
} catch (ReflectiveOperationException e) {
GTNHMixins.LOGGER.error("Unexpected error", e);
}
}
}
((Runnable)Launch.blackboard.getOrDefault("unimixins.mixinModidDecorator.refresh", Runnables.doNothing())).run();
((Runnable) Launch.blackboard.getOrDefault("unimixins.mixinModidDecorator.refresh", Runnables.doNothing())).run();
}
@Override
public String getAccessTransformerClass() {
return null;
}
public static boolean isObf() {
return isObf;
}
}
@@ -5,13 +5,14 @@ import com.gtnewhorizon.gtnhmixins.GTNHMixins;
import com.gtnewhorizon.gtnhmixins.ILateMixinLoader;
import com.gtnewhorizon.gtnhmixins.LateMixin;
import com.gtnewhorizon.gtnhmixins.Reflection;
import com.gtnewhorizon.gtnhmixins.core.GTNHMixinsCore;
import cpw.mods.fml.common.LoadController;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.LoaderState;
import cpw.mods.fml.common.ModClassLoader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.discovery.ASMDataTable;
import cpw.mods.fml.common.event.FMLConstructionEvent;
import net.minecraft.launchwrapper.Launch;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.injection.At;
@@ -21,66 +22,79 @@ import org.spongepowered.asm.mixin.transformer.Config;
import org.spongepowered.asm.mixin.transformer.Proxy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@Mixin(value = FMLConstructionEvent.class, remap = false)
@Mixin(value = LoadController.class, remap = false)
public class LateMixinOrchestrationMixin {
private static boolean finished=false;
@Inject(method = "<init>", at = @At(value = "RETURN"))
private void beforeConstructing(Object[] eventData, CallbackInfo ci) throws Throwable {
// We would normally target something in LoadController, but FastCraft causes that class to be loaded too early to mixin,
// so we instead hijack the very first FMLConstructionEvent that's constructed and run the logic there.
if (finished)
@SuppressWarnings("unchecked")
@Inject(method = "distributeStateMessage(Lcpw/mods/fml/common/LoaderState;[Ljava/lang/Object;)V", at = @At(value = "HEAD"))
private void beforeConstructing(LoaderState state, Object[] eventData, CallbackInfo ci) throws Throwable {
// This state is where Forge adds mod files to ModClassLoader
if (state != LoaderState.CONSTRUCTING) {
return;
}
finished = true;
GTNHMixins.log("Searching ASMData for ILateMixinLoaders");
GTNHMixins.LOGGER.info("Instantiating all @LateMixin annotated and ILateMixinLoader implemented classes...");
ModClassLoader modClassLoader = (ModClassLoader)eventData[0];
ASMDataTable asmDataTable = (ASMDataTable)eventData[1];
ModClassLoader modClassLoader = (ModClassLoader) eventData[0];
ASMDataTable asmDataTable = (ASMDataTable) eventData[1];
final Loader loader = Loader.instance();
final Set<String> loadedMods = loader.getIndexedModList().keySet();
GTNHMixinsCore.LOGGER.info("LoadedMods {}", loadedMods.toString());
final Set<String> loadedModsTemp = new HashSet<>();
loadedModsTemp.addAll(loader.getIndexedModList().keySet());
loadedModsTemp.addAll(getLiteLoaderMods());
final Set<String> loadedMods = Collections.unmodifiableSet(loadedModsTemp);
GTNHMixins.log("LoadedMods {}", loadedMods.toString());
for (ASMDataTable.ASMData asmData : asmDataTable.getAll(LateMixin.class.getName())) {
modClassLoader.addFile(asmData.getCandidate().getModContainer()); // Add to path before `newInstance`
final String mixinClassName = asmData.getClassName().replace('/', '.');
GTNHMixins.LOGGER.info("Instantiating {} for its mixins.", mixinClassName);
Class<?> lateMixinClass = Class.forName(mixinClassName);
if(!ILateMixinLoader.class.isAssignableFrom(lateMixinClass)) {
if (!ILateMixinLoader.class.isAssignableFrom(lateMixinClass)) {
GTNHMixins.LOGGER.error("Class {} has the @LateMixin annotation, but does not implement the ILateMixinLoader interface!", mixinClassName);
continue;
}
ILateMixinLoader lateLoader = (ILateMixinLoader)lateMixinClass.newInstance();
ILateMixinLoader lateLoader = (ILateMixinLoader) lateMixinClass.newInstance();
GTNHMixins.log("Loading mixins from ILateMixinLoader [{}]", lateLoader.getClass().getName());
final String mixinConfig = lateLoader.getMixinConfig();
GTNHMixins.LOGGER.info("Adding {} mixin configuration.", mixinConfig);
final Config config = Config.create(mixinConfig);
final List<String> mixins = lateLoader.getMixins(loadedMods);
for(String mixin : mixins) {
GTNHMixins.LOGGER.info("Loading [{}] {}", mixinConfig, mixin);
final Config config = Config.create(mixinConfig, null);
final Object o = Reflection.mixinClassesField.get(Reflection.configField.get(config));
final List<String> mixins;
if (o instanceof List) {
mixins = (List<String>) o;
} else {
mixins = new ArrayList<>();
Reflection.mixinClassesField.set(Reflection.configField.get(config), mixins);
}
mixins.addAll(lateLoader.getMixins(loadedMods));
for (String mixin : mixins) {
GTNHMixins.log("Loading [{}] {}", mixinConfig, mixin);
}
Reflection.mixinClassesField.set(Reflection.configField.get(config), mixins);
Reflection.registerConfigurationMethod.invoke(null, config);
}
((Runnable)Launch.blackboard.getOrDefault("unimixins.mixinModidDecorator.refresh", Runnables.doNothing())).run();
((Runnable) Launch.blackboard.getOrDefault("unimixins.mixinModidDecorator.refresh", Runnables.doNothing())).run();
// Add all mods to the classpath now so they're available for mixin
for (ModContainer container : loader.getActiveModList()) {
modClassLoader.addFile(container.getSource());
}
// Force rebuild of the transformer list
Reflection.setDelegatedTransformersField(null);
Field transformerField = Proxy.class.getDeclaredField("transformer");
transformerField.setAccessible(true);
@SuppressWarnings("OptionalGetWithoutIsPresent")
Object transformer = transformerField.get(Launch.classLoader.getTransformers().stream().filter(Proxy.class::isInstance).findFirst().get());
@@ -88,4 +102,28 @@ public class LateMixinOrchestrationMixin {
Reflection.invokeSelectConfigs(transformer, env);
Reflection.invokePrepareConfigs(transformer, env);
}
}
@SuppressWarnings("unchecked")
private static Set<String> getLiteLoaderMods() {
Set<String> mods = new HashSet<>();
try {
Class<?> LiteLoaderTweaker = Class.forName("com.mumfrey.liteloader.launch.LiteLoaderTweaker");
Method hasValidMetaData = Class.forName("com.mumfrey.liteloader.interfaces.LoadableMod").getMethod("hasValidMetaData");
Object instance = FieldUtils.readDeclaredStaticField(LiteLoaderTweaker, "instance", true);
Object bootstrap = FieldUtils.readDeclaredField(instance, "bootstrap", true);
Object enumerator = FieldUtils.readDeclaredField(bootstrap, "enumerator", true);
Map<String, ?> enabledContainers = (Map<String, ?>) FieldUtils.readDeclaredField(enumerator, "enabledContainers", true);
GTNHMixins.log("LiteLoader present, adding its mods to the list");
for (Entry<String, ?> e : enabledContainers.entrySet()) {
if ((boolean) hasValidMetaData.invoke(e.getValue())) {
mods.add(e.getKey());
}
}
} catch (ClassNotFoundException e) {
GTNHMixins.log("LiteLoader not present");
} catch (Exception e) {
GTNHMixins.LOGGER.error("Failed to get LiteLoader mods.", e);
}
return mods;
}
}
@@ -0,0 +1,8 @@
package io.github.legacymoddingmc.unimixins.gtnhmixins;
import io.github.legacymoddingmc.unimixins.common.config.AnnotatedProperties;
public class GTNHMixinsConfig {
@AnnotatedProperties.ConfigString(def = "true", com = "Provide the deprecated `com.gtnewhorizons.mixinextras` relocation of MixinExtras 0.1.1. May be required by some older mods.", cat = "gtnhmixins")
public static boolean enableLegacyGTNHMixinExtrasPackage;
}
@@ -1,33 +1,48 @@
package io.github.legacymoddingmc.unimixins.gtnhmixins;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import cpw.mods.fml.common.versioning.ComparableVersion;
import io.github.legacymoddingmc.unimixins.common.abstraction.ComparableVersion;
import io.github.legacymoddingmc.unimixins.common.config.ConfigUtil;
import io.github.legacymoddingmc.unimixins.common.sanitycheck.SanityCheckHelper;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.common.config.Configuration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class GTNHMixinsModule {
private static final Logger LOGGER = LogManager.getLogger("unimixin-gtnhmixins");
public static final Logger LOGGER = LogManager.getLogger("unimixins-gtnhmixins");
public static void init() {
if(!skipIntegrityChecks()) {
if(SanityCheckHelper.isEnabled()) {
SanityCheckHelper.warnIfJarPrefixesExist(Arrays.asList("gasstation-", "mixinbooterlegacy-", "spongemixins-"));
checkComponentIntegrity();
}
registerASMRemapPackage("com.gtnewhorizon.mixinextras");
ConfigUtil.load(GTNHMixinsConfig.class);
if(isLegacyGTNHMixinExtrasEnabled()) {
Launch.classLoader.registerTransformer("io.github.legacymoddingmc.unimixins.gtnhmixins.asm.LegacyGTNHMixinExtrasGenerator");
try {
Class.forName("com.gtnewhorizon.mixinextras.MixinExtrasBootstrap").getMethod("init").invoke(null);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Failed to initialize MixinExtrasBootstrap");
}
}
}
public static boolean isMixinExtrasEnabled() {
public static boolean isLegacyGTNHMixinExtrasEnabled() {
if(!GTNHMixinsConfig.enableLegacyGTNHMixinExtrasPackage) return false;
String requiredVersion = "0.8.5";
String mixinVersion = (String)Launch.blackboard.get("mixin.initialised");
if(mixinVersion != null && new ComparableVersion(mixinVersion).compareTo(new ComparableVersion(requiredVersion)) >= 0) {
LOGGER.debug("Intializing MixinExtras");
LOGGER.debug("Initializing MixinExtras");
return true;
} else if(skipIntegrityChecks()){
} else if(!SanityCheckHelper.isEnabled()){
LOGGER.warn("Skipping MixinExtras because Mixin version (" + mixinVersion + ") is lower than the required (" + requiredVersion + ")");
return false;
} else {
@@ -35,27 +50,6 @@ public class GTNHMixinsModule {
}
}
private static void registerASMRemapPackage(String pkg) {
try {
Class.forName("io.github.legacymoddingmc.unimixins.compat.asm.ASMRemapperTransformer").getMethod("registerPackage", String.class).invoke(null, pkg);
} catch(Exception e) {
LOGGER.warn("Failed to register package " + pkg + " for ASM remapping, probably because the compat module is missing. " + e);
}
}
private static boolean skipIntegrityChecks() {
Configuration config = new Configuration(new File(Launch.minecraftHome, "config/unimixins.cfg"));
config.load();
boolean disableIntegrityChecks = config.getBoolean("disableIntegrityChecks", "general", false, "Don't throw an error if an invalid combination of modules is detected. For advanced users.");
if(config.hasChanged()) {
config.save();
}
return disableIntegrityChecks;
}
private static void checkComponentIntegrity() {
List<String> missingComponents = new ArrayList<>();
@@ -0,0 +1,50 @@
package io.github.legacymoddingmc.unimixins.gtnhmixins.asm;
import static io.github.legacymoddingmc.unimixins.gtnhmixins.GTNHMixinsModule.LOGGER;
import io.github.legacymoddingmc.unimixins.gtnhmixins.util.LaunchClassLoaderUtils;
import makamys.mixingasm.api.MixinSafeTransformer;
import makamys.mixingasm.api.TransformerInclusions;
import net.minecraft.launchwrapper.IClassTransformer;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
@MixinSafeTransformer
public class LegacyGTNHMixinExtrasGenerator implements IClassTransformer {
static {
if(LegacyGTNHMixinExtrasGenerator.class.getResource("/makamys/mixingasm/api/TransformerInclusions.class") != null
&& LegacyGTNHMixinExtrasGenerator.class.getResource("/makamys/mixingasm/api/MixinSafeTransformer.class") == null) {
// Mixingasm < 0.3 compat
TransformerInclusions.getTransformerInclusionList().add("io.github.legacymoddingmc.unimixins.compat.asm.LegacyGTNHMixinExtrasGenerator");
}
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if(basicClass == null && name.startsWith("com.gtnewhorizon.mixinextras") && !name.startsWith("org.apache.commons.io.")) {
try {
LOGGER.trace("Generating class " + name);
byte[] bytes = getClassBytes(name);
if(bytes != null) {
// If NEI is on the class path before CCL (which would cause the game to crash immediately in
// production, but is possible in a dev env), ClassHeirachyTransformer will try to call getClassBytes,
// and if it fails, it will call Class.forName for the class being loaded, causing a
// ClassCircularityError.
// So we need to populate the cache to avoid that from happening.
LaunchClassLoaderUtils.putInResourceCache(name, bytes);
return bytes;
}
} catch (IOException e) {
throw new RuntimeException("Failed to generate class " + name);
}
}
return basicClass;
}
private byte[] getClassBytes(String name) throws IOException {
final String resourcePath = "/data/legacy_gtnh_mixinextras/" + name.replace('.', '/').concat(".klass");
return IOUtils.toByteArray(LegacyGTNHMixinExtrasGenerator.class.getResourceAsStream(resourcePath));
}
}
@@ -0,0 +1,36 @@
package io.github.legacymoddingmc.unimixins.gtnhmixins.util;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
public class LaunchClassLoaderUtils {
private static Map<String,byte[]> resourceCache;
private static Set<String> negativeResourceCache;
static {
try {
Field resourceCacheField = LaunchClassLoader.class.getDeclaredField("resourceCache");
resourceCacheField.setAccessible(true);
resourceCache = (Map<String, byte[]>) resourceCacheField.get(Launch.classLoader);
Field negativeResourceCacheField = LaunchClassLoader.class.getDeclaredField("negativeResourceCache");
negativeResourceCacheField.setAccessible(true);
negativeResourceCache = (Set<String>) negativeResourceCacheField.get(Launch.classLoader);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/** Use with care, ArchaicFix weakens the resource cache once it receives the LoadComplete event, making
* this method cease to function! But it's fine for our purposes since all mixins load before that.
* It's either this or reflecting every time. */
public static void putInResourceCache(String key, byte[] value) {
resourceCache.put(key, value);
negativeResourceCache.remove(key);
}
}
@@ -1 +0,0 @@
com.gtnewhorizon.mixinextras.MixinExtrasAP
@@ -3,7 +3,7 @@
"modid": "${extraModid}",
"name": "UniMixins: GTNHMixins",
"description": "UniMixins module that provides GTNHMixins extras and MixinExtras with the GTNH package name.\nDepends on: SpongeMixins",
"version": "${extraVersion}+uni-${uniVersion}",
"version": "${extraVersion}+uni.${uniVersion}",
"mcversion": "${mcversion}",
"url": "${project_url}",
"updateUrl": "",
@@ -2,7 +2,7 @@
"required": true,
"minVersion": "0.8.3-GTNH",
"compatibilityLevel": "JAVA_8",
"target": "@env(DEFAULT)",
"target": "@env(PREINIT)",
"package": "com.gtnewhorizon.gtnhmixins.mixins",
"refmap": "mixins.gtnhmixins.refmap.json",
"injectors": {
@@ -11,4 +11,4 @@
"mixins": [
"LateMixinOrchestrationMixin"
]
}
}
+55 -32
View File
@@ -15,12 +15,17 @@ archivesBaseName = archivesBaseName.replace("-1.7.10", "-mixin-1.7.10")
configurations.archives.artifacts.clear()
dependencies {
compileOnly("org.spongepowered:mixin:0.8.5")
compileOnly("org.spongepowered:mixin:$spongepoweredMixinVersion")
compileOnly(project(":module-common")) {
transitive = false
}
}
apply plugin: "com.github.johnrengelman.shadow"
["spongepowered", "fabric", "gasmix", "gtnh", "unimix"].each {
def flavors = (!project.hasProperty("enableAltMixin") ? [] : ["spongepowered", "fabric", "gasmix", "gtnh"]) + ["unimix"]
flavors.each {
def mixinFlavor = it
def _flavor = it.capitalize()
def mixinFlavorClassifier = "UNKNOWN"
@@ -31,6 +36,12 @@ apply plugin: "com.github.johnrengelman.shadow"
configurations.create("shadowBridge$_flavor")
configurations.create("shadowSources$_flavor")
dependencies {
add("shadow$_flavor", project(":module-common"), {
transitive = false
})
}
if (mixinFlavor == "fabric" || mixinFlavor == "spongepowered" || mixinFlavor == "unimix" || mixinFlavor == "gtnh") {
// Fabric / SpongePowered / UniMix / GTNH
@@ -38,7 +49,7 @@ apply plugin: "com.github.johnrengelman.shadow"
def uniLocal = mixinFlavor == "unimix" && project.hasProperty("local")
def uniJitpack = mixinFlavor == "unimix" && !uniLocal
mixinVersion = mixinFlavor == "fabric" ? "0.11.4+mixin.0.8.5" : "0.11.9+mixin.0.8.5"
mixinVersion = mixinFlavor == "fabric" ? fabricMixinVersion : unimixMixinVersion
if (uniLocal) {
mixinVersion += "-local"
}
@@ -53,15 +64,15 @@ apply plugin: "com.github.johnrengelman.shadow"
def mixinDepVersion = uniJitpack ? mixinVersion.replace('+', '_') : mixinVersion
def mixinDep = "$mixinGroup:$mixinArtifactId:$mixinDepVersion"
mixinFlavorClassifier = "$mixinFlavor-$mixinVersion"
mixinFlavorClassifier = "$mixinFlavor.${mixinVersion.replace('+', '-')}"
dependencies {
add("shadow$_flavor", "$mixinDep", {
exclude group: "org.ow2.asm"
})
add("shadow$_flavor", 'org.ow2.asm:asm-tree:9.4')
add("shadow$_flavor", 'org.ow2.asm:asm-commons:9.4')
add("shadow$_flavor", 'org.ow2.asm:asm-util:9.4')
add("shadow$_flavor", "org.ow2.asm:asm-tree:$asmVersion")
add("shadow$_flavor", "org.ow2.asm:asm-commons:$asmVersion")
add("shadow$_flavor", "org.ow2.asm:asm-util:$asmVersion")
add("shadowBridge$_flavor", "$mixinDep", {
transitive = false
@@ -72,29 +83,28 @@ apply plugin: "com.github.johnrengelman.shadow"
})
}
} else if(mixinFlavor == "spongepowered") {
mixinVersion = "0.8.5"
mixinVersion = spongepoweredMixinVersion
mixinFlavorClassifier = "spongepowered-$mixinVersion"
mixinFlavorClassifier = "spongepowered.$mixinVersion"
dependencies {
add("shadow$_flavor", "org.spongepowered:mixin:$mixinVersion")
add("shadowBridge$_flavor", "org.spongepowered:mixin:$mixinVersion", {
transitive = false
})
add("shadow$_flavor", 'com.google.guava:guava:21.0')
add("shadow$_flavor", 'com.google.code.gson:gson:2.2.4')
add("shadow$_flavor", 'org.ow2.asm:asm-tree:9.2')
add("shadow$_flavor", 'org.ow2.asm:asm-commons:9.2')
add("shadow$_flavor", 'org.ow2.asm:asm-util:9.2')
add("shadow$_flavor", "com.google.guava:guava:21.0")
add("shadow$_flavor", "com.google.code.gson:gson:2.2.4")
add("shadow$_flavor", "org.ow2.asm:asm-tree:$asmVersion")
add("shadow$_flavor", "org.ow2.asm:asm-commons:$asmVersion")
add("shadow$_flavor", "org.ow2.asm:asm-util:$asmVersion")
add("shadowSources$_flavor", "org.spongepowered:mixin:$mixinVersion:sources")
}
} else if(mixinFlavor == "gtnh") {
// Adapted from GTNHMixins's build script
mixinVersion = "0.8.5-GTNH-2"
mixinFlavorClassifier = "gtnh-$mixinVersion"
def asmVersion = "9.4"
mixinVersion = gtnhMixinVersion
mixinFlavorClassifier = "gtnh.$mixinVersion"
dependencies {
add("shadow$_flavor", "org.spongepowered:mixin:$mixinVersion")
@@ -102,8 +112,8 @@ apply plugin: "com.github.johnrengelman.shadow"
add("shadow$_flavor", "org.ow2.asm:asm-commons:$asmVersion")
add("shadow$_flavor", "org.ow2.asm:asm-util:$asmVersion")
add("shadow$_flavor", "com.google.guava:guava:21.0")
add("shadowBridge$_flavor", 'org.spongepowered:mixin:0.8.5')
add("shadowSources$_flavor", 'org.spongepowered:mixin:0.8.5:sources')
add("shadowBridge$_flavor", "org.spongepowered:mixin:$spongepoweredMixinVersion")
add("shadowSources$_flavor", "org.spongepowered:mixin:$spongepoweredMixinVersion:sources")
}
}
@@ -165,12 +175,21 @@ apply plugin: "com.github.johnrengelman.shadow"
exclude "mcmod.info"
}
relocate 'io.github.legacymoddingmc.unimixins.common', 'io.github.legacymoddingmc.unimixins.mixin.repackage.common'
dependsOn "mixinJar$_flavor"
dependsOn "bridgeJar$_flavor"
dependsOn "mixinJar$_flavor"
dependsOn "bridgeJar$_flavor"
from zipTree(tasks."mixinJar$_flavor".archiveFile).matching { exclude 'module-info.class' }
from zipTree(tasks."mixinJar$_flavor".archiveFile).matching {
exclude 'module-info.class'
eachFile {
if(it.path.startsWith("META-INF/services/")) {
it.filter({l -> !l.startsWith("org.spongepowered.asm.service.modlauncher.") ? l : null})
}
}
}
from zipTree(tasks."bridgeJar$_flavor".archiveFile).matching { include 'org/spongepowered/asm/bridge/*' }
doLast {
@@ -183,9 +202,9 @@ apply plugin: "com.github.johnrengelman.shadow"
} else if (mixinFlavor == "gasmix") {
// GasMix
mixinVersion = "0.8.5-gasstation_7"
mixinVersion = gasmixMixinVersion
mixinFlavorClassifier = "gasmix-$mixinVersion"
mixinFlavorClassifier = "gasmix.$mixinVersion"
dependencies {
add("shadow$_flavor", "org.spongepowered:mixin:$mixinVersion")
@@ -200,6 +219,8 @@ apply plugin: "com.github.johnrengelman.shadow"
version = versionBase + "+" + mixinFlavorClassifier
classifier = ''
configurations = [project.configurations."shadow$_flavor"]
relocate 'io.github.legacymoddingmc.unimixins.common', 'io.github.legacymoddingmc.unimixins.mixin.repackage.common'
}
jar.dependsOn("shadowJar$_flavor")
@@ -212,7 +233,7 @@ apply plugin: "com.github.johnrengelman.shadow"
from 'src/main/resources/mcmod.info'
into "build/tmp/mcmod.${mixinFlavor}.info"
filter {
line -> line.replaceAll('@MIXIN_CLASSIFIER@', mixinFlavorClassifier).replaceAll('@MIXIN_SOURCE_CAPITALIZED@', mixinFlavorCapitalized).replaceAll('@VERSION@', "$versionBase+$mixinFlavorClassifier")
line -> line.replaceAll('@MIXIN_CLASSIFIER@', mixinFlavorClassifier).replaceAll('@MIXIN_SOURCE_CAPITALIZED@', mixinFlavorCapitalized).replaceAll('@VERSION@', "$versionBase+$mixinFlavorClassifier").replaceAll('@PROJECT_URL@', project.project_url)
}
}
@@ -244,21 +265,23 @@ apply plugin: "com.github.johnrengelman.shadow"
}
jar.dependsOn("shadowSourcesJar$_flavor")
if(publishModuleToMaven){
publishing {
publications {
create("maven$_flavor", MavenPublication) {
artifact tasks."shadowJar$_flavor"
artifact tasks."shadowSourcesJar$_flavor"
publishing {
publications {
create("maven$_flavor", MavenPublication) {
artifact tasks."shadowJar$_flavor"
artifact tasks."shadowSourcesJar$_flavor"
artifactId = archivesBaseName
version = tasks."shadowJar$_flavor".version
artifactId = archivesBaseName.substring(1).replace("-1.7.10", mavenIdSuffix) + (mixinFlavor == "unimix" ? "" : "-" + mixinFlavor)
groupId = mavenGroupId
}
}
}
}
}
// Obfuscation is useless because we're not compiling code referencing Minecraft classes
// Obfuscation is unnecessary since this module does not reference Minecraft classes
reobf.enabled = false
jar.enabled = false
+7
View File
@@ -0,0 +1,7 @@
fabricMixinVersion=0.15.3+mixin.0.8.7
unimixMixinVersion=0.15.4+mixin.0.8.7
spongepoweredMixinVersion=0.8.7
gtnhMixinVersion=0.8.7-GTNH-2
gasmixMixinVersion=0.8.7-gasstation_7
asmVersion=9.8
@@ -1,6 +1,10 @@
package io.github.legacymoddingmc.unimixins.mixin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import io.github.legacymoddingmc.unimixins.common.sanitycheck.SanityCheckHelper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
/**
@@ -10,6 +14,18 @@ import java.util.*;
@IFMLLoadingPlugin.MCVersion("1.7.10")
public class MixinCore implements IFMLLoadingPlugin {
public static final Logger LOGGER = LogManager.getLogger("unimixins");
static {
if(SanityCheckHelper.isEnabled()) {
SanityCheckHelper.showBigWarning(
MixinSanityCheck.checkMixinHasInitialized(),
MixinSanityCheck.checkMixinContainer(),
SanityCheckHelper.checkIfJarPrefixesExist(Arrays.asList("gasstation-", "mixinbooterlegacy-", "spongemixins-"))
);
}
}
@Override
public String[] getASMTransformerClass() {
return new String[0];
@@ -7,7 +7,6 @@ import com.google.gson.JsonElement;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mixins;
import org.spongepowered.asm.mixin.transformer.Config;
@@ -30,10 +29,13 @@ public final class MixinModidDecorator {
private MixinModidDecorator() {}
public static void apply() {
// FML has added all mods containing the Mixin tweaker to the class path at this point, so we only need to scan
// the class path one time.
if(jarNameToModid == null) {
jarNameToModid = createJarNameToModidMap();
// A method handle that can be used anywhere to trigger a refresh
// A method handle that can be used anywhere to trigger a refresh. This should be called whenever mixin
// configs are added later.
Launch.blackboard.put("unimixins.mixinModidDecorator.refresh", (Runnable)MixinModidDecorator::apply);
}
@@ -63,7 +65,7 @@ public final class MixinModidDecorator {
if (!metas.isEmpty()) {
String modid = metas.get(0).modid;
if (modid != null) {
map.put(fileName, modid);
map.put(fileName, createUniqueModid(modid, map.values()));
}
}
}
@@ -74,13 +76,47 @@ public final class MixinModidDecorator {
return map;
}
/**
* Creates a modid that is unique and is a valid Java identifier. Required because Fabric's Mixin may put the modid
* in method names.
*/
private static String createUniqueModid(String modid, Collection<String> registeredModids) {
String sanitizedModid = "";
// Replace invalid characters with a underscore
// Valid characters are the same as in Fabric, but we allow capitalized letters too, and we don't allow dashes
for(int i = 0; i < modid.length(); i++) {
char c = modid.charAt(i);
boolean valid = i == 0 ? isValidLetter(c) : isValidLetter(c) || isValidDigit(c);
sanitizedModid += valid ? c : '_';
}
modid = sanitizedModid;
// Add a numeric suffix in case of a name conflict
int discriminator = 1;
while(registeredModids.contains(modid)) {
discriminator++;
modid = sanitizedModid + "_" + discriminator;
}
return modid;
}
private static boolean isValidLetter(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
private static boolean isValidDigit(char c) {
return (c >= '0' && c <= '9') || c == '_';
}
private static List<ModMetadata> parseMcmodInfo(URL url) {
try {
JsonElement root = new Gson().fromJson(new InputStreamReader(url.openStream()), JsonElement.class);
if (root.isJsonArray()) {
return Arrays.asList(new Gson().fromJson(new InputStreamReader(url.openStream()), ModMetadata[].class));
} else {
return Arrays.asList(new Gson().fromJson(new InputStreamReader(url.openStream()), ModMetadata.class));
return ((ModMetadataCollection)new Gson().fromJson(new InputStreamReader(url.openStream()), ModMetadataCollection.class)).modList;
}
} catch (Exception e) {
logger.warn("Failed to parse mcmod.info at " + url + ": " + e);
@@ -103,4 +139,8 @@ public final class MixinModidDecorator {
String modid;
}
}
private static class ModMetadataCollection {
List<ModMetadata> modList;
}
}
@@ -6,5 +6,6 @@ import cpw.mods.fml.common.Mod;
* An empty mod class provided to make it impossible to load multiple editions
* of the module at the same time.
*/
@Mod(modid = "unimixins-mixin", version = "@VERSION@")
@Mod(modid = "unimixins-mixin", version = "@VERSION@", acceptableRemoteVersions = "*")
@net.minecraftforge.fml.common.Mod(modid = "unimixins-mixin", version = "@VERSION@", acceptableRemoteVersions = "*")
public class MixinModule {}
@@ -0,0 +1,54 @@
package io.github.legacymoddingmc.unimixins.mixin;
import net.minecraft.launchwrapper.Launch;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import static io.github.legacymoddingmc.unimixins.mixin.MixinCore.LOGGER;
public class MixinSanityCheck {
static List<String> checkMixinHasInitialized() {
if(!Launch.blackboard.containsKey("mixin.initialised")) {
return Arrays.asList("Mixin has not been initialized! If this is a dev environment, make sure org.spongepowered.asm.launch.MixinTweaker was added via the --tweakClass program argument.");
}
return Arrays.asList();
}
static List<String> checkMixinContainer() {
String mixinCoreJarName = getJarName(MixinSanityCheck.class.getResource("/io/github/legacymoddingmc/unimixins/mixin/MixinCore.class"));
String mixinTweakerJarName = getJarName(MixinSanityCheck.class.getResource("/org/spongepowered/asm/launch/MixinTweaker.class"));
LOGGER.debug("Name of jar containing MixinCore: " + mixinCoreJarName);
LOGGER.debug("Name of jar containing MixinTweaker: " + mixinTweakerJarName);
if(!mixinCoreJarName.equals(mixinTweakerJarName)) {
boolean isSuspectedToBeDueToNaming =
mixinCoreJarName != null && mixinTweakerJarName != null
&& mixinCoreJarName.compareToIgnoreCase(mixinTweakerJarName) > 0;
return Arrays.asList("A different version of Mixin (the one inside " + stringOr(mixinTweakerJarName, "unknown") + ") is getting loaded instead of UniMixins's one (inside " + stringOr(mixinCoreJarName, "unknown") + ")!" + (isSuspectedToBeDueToNaming ? " This is probably because because UniMixins's jar name comes later alphabetically. Try renaming it to come first (for example, by adding a '!' character at the beginning of the file name)." : ""));
}
return Arrays.asList();
}
private static String stringOr(String s, String alternative) {
return s == null ? alternative : s;
}
private static String getJarName(URL url) {
if(url != null) {
String urlStr = url.toString();
if (urlStr.contains("!/")) {
int exclamationIdx = urlStr.indexOf("!/");
String preExclamationSubstring = urlStr.substring(0, exclamationIdx);
int lastSlash = preExclamationSubstring.lastIndexOf('/');
if (lastSlash != -1) {
return preExclamationSubstring.substring(lastSlash + 1);
}
}
}
return null;
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
"description": "UniMixins module that provides the @MIXIN_SOURCE_CAPITALIZED@ fork of Mixin (version: @MIXIN_CLASSIFIER@).",
"version": "@VERSION@",
"mcversion": "1.7.10",
"url": "${project_url}",
"url": "@PROJECT_URL@",
"updateUrl": "",
"authorList": ["makamys"],
"credits": "",
+3 -1
View File
@@ -1,4 +1,4 @@
Source: https://github.com/tox1cozZ/mixin-booter-legacy/tree/1.2.0
Source: https://github.com/tox1cozZ/mixin-booter-legacy/tree/1.2.1
Classes used:
@@ -9,5 +9,7 @@ Modifications:
- Changed shaded ASM package name to legacy one
- Changed mixin config to use PREINIT phase
- Added call to "unimixins.mixinModidDecorator.refresh" after loading early mixins in MixinBooterLegacyPlugin and late mixins in LoadControllerMixin
- Added 1.8+ @Mod and @IFMLPlugin annotations
- Set acceptableRemoteVersions = "*" in @Mod annotation
License: LGPL 2.1 (see LICENSE)
+19 -2
View File
@@ -10,11 +10,21 @@ repositories {
dependencies {
compileOnly('org.spongepowered:mixin:0.8.5-gasstation_7')
shadow(project(":module-common")) {
transitive = false
}
}
archivesBaseName = archivesBaseName.replace("-1.7.10", "-mixinbooterlegacy-1.7.10")
jar {
apply plugin: "com.github.johnrengelman.shadow"
shadowJar {
classifier = ''
configurations = [project.configurations.shadow]
relocate 'io.github.legacymoddingmc.unimixins.common', 'io.github.legacymoddingmc.unimixins.mixinbooterlegacy.repackage.common'
manifest {
attributes (
'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
@@ -25,8 +35,15 @@ jar {
}
}
jar {
dependsOn shadowJar
enabled = false
}
// Obfuscation is unnecessary since this module does not reference Minecraft classes
reobf.enabled = false
ext.uniVersion = version
ext.extraVersion = "1.2.0"
ext.mcversion = "1.7.10"
version += "+" + extraVersion
@@ -0,0 +1 @@
extraVersion=1.2.1
@@ -0,0 +1,13 @@
package io.github.legacymoddingmc.unimixins.mixinbooterlegacy;
import io.github.legacymoddingmc.unimixins.common.sanitycheck.SanityCheckHelper;
import java.util.Arrays;
public class MixinBooterLegacyModule {
public static void init() {
if(SanityCheckHelper.isEnabled()) {
SanityCheckHelper.warnIfJarPrefixesExist(Arrays.asList("mixinbooterlegacy-"));
}
}
}
@@ -1,13 +1,13 @@
package io.github.tox1cozz.mixinbooterlegacy;
import com.google.common.util.concurrent.Runnables;
import cpw.mods.fml.common.DummyModContainer;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.Name;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.SortingIndex;
import io.github.tox1cozz.mixinextras.MixinExtrasBootstrap;
import io.github.legacymoddingmc.unimixins.mixinbooterlegacy.MixinBooterLegacyModule;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -20,12 +20,15 @@ import java.util.Map;
@Name("MixinBooterLegacy")
@MCVersion("1.7.10")
@SortingIndex(Integer.MIN_VALUE + 1)
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.Name("MixinBooterLegacy")
@net.minecraftforge.fml.relauncher.IFMLLoadingPlugin.SortingIndex(Integer.MIN_VALUE + 1)
public final class MixinBooterLegacyPlugin implements IFMLLoadingPlugin {
public static final Logger LOGGER = LogManager.getLogger("MixinBooter");
static {
LOGGER.info("MixinBootstrap Initializing...");
MixinBooterLegacyModule.init();
// Initialize MixinExtras
MixinExtrasBootstrap.init();
Mixins.addConfiguration("mixin.mixinbooterlegacy.json");
@@ -80,7 +83,8 @@ public final class MixinBooterLegacyPlugin implements IFMLLoadingPlugin {
return null;
}
@Mod(modid = "mixinbooterlegacy", name = "MixinBooterLegacy", version = "@VERSION@")
@Mod(modid = "mixinbooterlegacy", name = "MixinBooterLegacy", version = "@VERSION@", acceptableRemoteVersions = "*")
@net.minecraftforge.fml.common.Mod(modid = "mixinbooterlegacy", name = "MixinBooterLegacy", version = "@VERSION@", acceptableRemoteVersions = "*")
public static class Container {
}
@@ -39,12 +39,12 @@ public class LoadControllerMixin {
MixinBooterLegacyPlugin.LOGGER.info("Instantiating all ILateMixinLoader implemented classes...");
for (ASMDataTable.ASMData asmData : asmDataTable.getAll(LateMixin.class.getName().replace('.', '/'))) {
for (ASMDataTable.ASMData asmData : asmDataTable.getAll(LateMixin.class.getName())) {
modClassLoader.addFile(asmData.getCandidate().getModContainer()); // Add to path before `newInstance`
Class<?> clazz = Class.forName(asmData.getClassName().replace('/', '.'));
MixinBooterLegacyPlugin.LOGGER.info("Instantiating {} for its mixins.", clazz);
if (!clazz.isAssignableFrom(ILateMixinLoader.class)) {
if (!ILateMixinLoader.class.isAssignableFrom(clazz)) {
throw new MixinInitialisationError(String.format("The class %s has the LateMixin annotation, but does not implement the ILateMixinLoader interface.", clazz.getName()));
}
@@ -101,4 +101,4 @@ public class LoadControllerMixin {
prepareConfigsMethod.invoke(processor, env, extensions);
}
}
}
}
@@ -3,7 +3,7 @@
"modid": "mixinbooterlegacy",
"name": "UniMixins: MixinBooterLegacy",
"description": "UniMixins module that provides MixinBooterLegacy extras and MixinExtras with MixinBooterLegacy's package name.",
"version": "${extraVersion}+uni-${uniVersion}",
"version": "${extraVersion}+uni.${uniVersion}",
"mcversion": "${mcversion}",
"url": "${project_url}",
"updateUrl": "",
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 LlamaLad7
Copyright (c) 2022-present LlamaLad7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+21 -3
View File
@@ -1,12 +1,15 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
configurations {
shadowBridge
shadowSources
}
ext.uniVersion = version
ext.extraVersion = "0.1.1"
ext.extraVersion = mixinExtrasVersion
dependencies {
shadow("com.github.LlamaLad7:MixinExtras:$extraVersion")
shadow("io.github.llamalad7:mixinextras-common:$extraVersion")
shadowSources("io.github.llamalad7:mixinextras-common:$extraVersion:sources")
}
archivesBaseName = archivesBaseName.replace("-1.7.10", "-mixinextras-1.7.10")
@@ -30,7 +33,22 @@ shadowJar {
}
}
task("shadowSources", type: ShadowJar) {
from sourceSets.main.allSource
classifier = "sources"
configurations = [project.configurations."shadowSources"]
}
sourcesJar {
enabled = false
dependsOn shadowSources
}
jar {
enabled = false
dependsOn shadowJar
}
// ForgeGradle's reobfuscator breaks classes containing CONSTANT_InterfaceMethodRefs, which MixinExtras happens to contain
// Also obfuscation is unnecessary anyway since this module does not reference Minecraft classes
reobf.enabled = false
+1
View File
@@ -0,0 +1 @@
mixinExtrasVersion=0.4.1
@@ -0,0 +1,12 @@
package io.github.legacymoddingmc.unimixins.mixinextras;
import cpw.mods.fml.common.Mod;
/**
* This is only here to help Forge detect the mod.
*/
@Mod(modid = "mixinextras", version = "@VERSION@", acceptableRemoteVersions = "*")
@net.minecraftforge.fml.common.Mod(modid = "mixinextras", version = "@VERSION@", acceptableRemoteVersions = "*")
public class MixinExtrasModule {
}
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 LlamaLad7
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,8 +1,9 @@
[
{
"modid": "mixinextras",
"name": "UniMixins: MixinExtras",
"description": "UniMixins module that provides MixinExtras (version: ${extraVersion}).",
"version": "${extraVersion}+uni-${uniVersion}",
"version": "${extraVersion}+uni.${uniVersion}",
"mcversion": "${minecraft_version}",
"url": "${project_url}",
"updateUrl": "",
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff. Show more