mirror of
https://github.com/LegacyModdingMC/UniMixins.git
synced 2026-10-27 06:53:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21fb3becd8 | ||
|
|
203172bddb | ||
|
|
4cf9abd071 | ||
|
|
9e2cd35b5a | ||
|
|
092d0843d1 | ||
|
|
55b9660726 | ||
|
|
05878bfde1 | ||
|
|
43ac78f2dc | ||
|
|
1cbd6a0ecd | ||
|
|
a97b3ce85c | ||
|
|
56a008ce92 | ||
|
|
02a9a1a10d | ||
|
|
5fbb67e26f | ||
|
|
918a80ae53 | ||
|
|
5788d7ec1e | ||
|
|
e29c36c603 | ||
|
|
aed49ce829 | ||
|
|
39a31c66da | ||
|
|
d2c55ed6e3 | ||
|
|
614e8b9569 | ||
|
|
45d16b96ab | ||
|
|
3563c869b5 | ||
|
|
64a4084138 | ||
|
|
acdd68819f | ||
|
|
0fff377da8 | ||
|
|
1b130b1314 | ||
|
|
608639acf3 | ||
|
|
44f89624b0 | ||
|
|
4f5d454d03 | ||
|
|
bbed619396 | ||
|
|
d66394b2ea | ||
|
|
220d040ebd | ||
|
|
b42622470f | ||
|
|
3bc74ae73d | ||
|
|
839425440e | ||
|
|
c2e86f3aae | ||
|
|
48d55f2b44 | ||
|
|
de08ce459e | ||
|
|
43a764c055 | ||
|
|
dad688c742 | ||
|
|
f524e7de7f | ||
|
|
181924abd1 | ||
|
|
8bc2051af9 | ||
|
|
87e71b1ba3 | ||
|
|
ae12182d30 | ||
|
|
189bf0b5e9 | ||
|
|
6b528532a2 | ||
|
|
062eab7754 | ||
|
|
c053f33017 | ||
|
|
016cf16396 | ||
|
|
9cff9105af | ||
|
|
41aecb728a | ||
|
|
8a8444d7f8 | ||
|
|
cd232ec4a0 | ||
|
|
14e8ce7cb2 | ||
|
|
6c55d33c18 | ||
|
|
fcd416e31b | ||
|
|
fe11aee64c | ||
|
|
d5c96de372 | ||
|
|
798b3fb1c9 | ||
|
|
ee81c389fa | ||
|
|
1237e86768 | ||
|
|
d268a991ad | ||
|
|
f1a63f392d | ||
|
|
7ee32c62d3 | ||
|
|
ddd608a8f8 | ||
|
|
a4bddd8dbf | ||
|
|
a892f6d119 | ||
|
|
58bb4137f0 | ||
|
|
28b93dd4f7 | ||
|
|
c3af5167df | ||
|
|
9b4bde7e95 | ||
|
|
bbc0b48175 | ||
|
|
27e3058a53 | ||
|
|
24f6c06631 | ||
|
|
bff36c3067 | ||
|
|
108893429f | ||
|
|
3a09260a99 | ||
|
|
9152c1a5ca | ||
|
|
28e98d501f | ||
|
|
80e07234f5 | ||
|
|
2e7893816f | ||
|
|
23e833fd1e | ||
|
|
f0b355c782 | ||
|
|
2409b246b3 | ||
|
|
46a5c6d167 | ||
|
|
b5891061e0 | ||
|
|
ba1f02d541 | ||
|
|
9aa07d509c | ||
|
|
8e17e96c3e | ||
|
|
8e3e7e49e5 | ||
|
|
0e54b7bf08 | ||
|
|
1db6d9b000 | ||
|
|
9354ebd81a | ||
|
|
df12ab3ef3 | ||
|
|
2ae66ed7a6 | ||
|
|
46d1e77770 | ||
|
|
e3c8d83108 | ||
|
|
cec3048ed4 | ||
|
|
f5800a69c2 | ||
|
|
b143241008 | ||
|
|
0daba06b88 | ||
|
|
ba453f04c5 | ||
|
|
8e2d6beb3f | ||
|
|
c0f712fb9d | ||
|
|
2376d1ce50 | ||
|
|
37edc3537a | ||
|
|
618bd0de47 | ||
|
|
d4213cf71a | ||
|
|
92db4df839 | ||
|
|
278edd177d | ||
|
|
1ed02bc811 | ||
|
|
2a331ee00f | ||
|
|
5d5aff4efe | ||
|
|
e54253eb95 | ||
|
|
adfa0b9061 | ||
|
|
12ea25c3f2 | ||
|
|
96fcda3db2 | ||
|
|
524ee60d9a | ||
|
|
4144ea5913 | ||
|
|
b8953cc38c | ||
|
|
f20c78c96b | ||
|
|
f595016981 | ||
|
|
4355a4a481 | ||
|
|
a68db4a990 | ||
|
|
b8830d9375 | ||
|
|
23386a3ea8 | ||
|
|
4b1b1423fd | ||
|
|
967f3a5d30 | ||
|
|
717ffa5a3a | ||
|
|
078c5eb28f | ||
|
|
d9b583d874 | ||
|
|
1c12590813 | ||
|
|
77670d9644 | ||
|
|
478ea715d2 | ||
|
|
1336eb46b4 | ||
|
|
907693bf4f | ||
|
|
67a2e95c33 | ||
|
|
c7aff3a584 | ||
|
|
9d02189012 | ||
|
|
fe3108cdcc | ||
|
|
dfafab8d86 | ||
|
|
d3417fb89b | ||
|
|
f320a0766d | ||
|
|
5456aef979 | ||
|
|
282a24c93f | ||
|
|
94fd027bb0 | ||
|
|
57493511b2 | ||
|
|
75124cfbd6 | ||
|
|
ddf5f48ca4 | ||
|
|
bc8cb2bae3 | ||
|
|
bf85e0449a | ||
|
|
e7b52d21db | ||
|
|
af589d5f98 | ||
|
|
ff22e0a7b6 | ||
|
|
e1b148d5c7 | ||
|
|
0993658b73 | ||
|
|
0107566517 | ||
|
|
d29d950f3d | ||
|
|
abba979a3d | ||
|
|
71e38dacaf | ||
|
|
89d6ea5550 | ||
|
|
db8a3f9065 | ||
|
|
b851599c94 | ||
|
|
b2e92b3850 | ||
|
|
fef6985f0e | ||
|
|
ca18dc40cb | ||
|
|
c324e7f4d4 | ||
|
|
e3633cede4 | ||
|
|
13dc9d4066 | ||
|
|
3b047eb51b | ||
|
|
ff943a3980 | ||
|
|
f8aa353f61 | ||
|
|
165317b6c4 | ||
|
|
2ef5c5e022 | ||
|
|
d662bedbf4 | ||
|
|
fdec26a280 | ||
|
|
898bd7d8db | ||
|
|
3fe2a8ba61 | ||
|
|
60a38e6609 | ||
|
|
ee3ac092d3 | ||
|
|
ae146d9749 | ||
|
|
cbaccfa7c3 | ||
|
|
2e355bb4f0 | ||
|
|
5ce0d5e6e0 | ||
|
|
5217f5855d | ||
|
|
ceba1109ea | ||
|
|
ddb02e692f | ||
|
|
defecbc5bc | ||
|
|
f4308e1a13 | ||
|
|
bcad088a29 | ||
|
|
653047d47b | ||
|
|
11d66bc58b | ||
|
|
f9aa965147 | ||
|
|
c03412daa3 | ||
|
|
7bf758543e | ||
|
|
cf6402e0bf | ||
|
|
1777a71096 | ||
|
|
c47e2aefbc | ||
|
|
a05507f043 | ||
|
|
56b2f4c651 | ||
|
|
fee71e8be9 | ||
|
|
c8bd6bea07 | ||
|
|
a1f6b03dc8 | ||
|
|
006f3490a6 | ||
|
|
d7818d0f2e | ||
|
|
a311cb111d | ||
|
|
9a6db5e25b | ||
|
|
7bfcb47d8a | ||
|
|
deec1015cd | ||
|
|
bf1f3cd742 | ||
|
|
07f2ad4af6 | ||
|
|
1f829353ea | ||
|
|
be33c33182 |
No files matched your search
@@ -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
|
||||
@@ -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)"
|
||||
|
||||
@@ -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
|
||||
@@ -1,18 +1,24 @@
|
||||
[](https://github.com/LegacyModdingMC/UniMixins/releases)
|
||||
[](https://makamys.github.io/docs/CI-Downloads/CI-Downloads.html)
|
||||
[](https://legacy.curseforge.com/minecraft/mc-mods/unimixins)
|
||||
[](https://modrinth.com/mod/unimixins)
|
||||
[](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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 |
@@ -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 "BOX", 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
@@ -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
@@ -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,2 +1,3 @@
|
||||
install:
|
||||
- ./gradlew setupCIWorkspace
|
||||
- ./gradlew publishToMavenLocal
|
||||
+89
-58
@@ -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.
@@ -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
|
||||
+53
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+68
@@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+87
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -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);
|
||||
}
|
||||
}
|
||||
+8
@@ -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;
|
||||
}
|
||||
+101
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
+12
@@ -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;
|
||||
}
|
||||
+74
-6
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+11
@@ -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 {
|
||||
}
|
||||
+109
@@ -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;
|
||||
}
|
||||
}
|
||||
+35
@@ -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);
|
||||
}
|
||||
}
|
||||
+16
@@ -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 {
|
||||
}
|
||||
+67
-39
@@ -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);
|
||||
|
||||
+85
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-6
@@ -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;
|
||||
}
|
||||
+139
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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}",
|
||||
|
||||
@@ -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/>
|
||||
@@ -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
|
||||
+35
@@ -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;
|
||||
}
|
||||
}
|
||||
+11
@@ -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 {
|
||||
}
|
||||
+5
@@ -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 {
|
||||
}
|
||||
+3
-3
@@ -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}",
|
||||
@@ -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
|
||||
-53
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+5
-19
@@ -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<>();
|
||||
|
||||
-6
@@ -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 ---
|
||||
-6
@@ -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 ---
|
||||
-26
@@ -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": "",
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+195
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+95
@@ -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]);
|
||||
}
|
||||
}
|
||||
+153
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+162
@@ -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;
|
||||
}
|
||||
}
|
||||
+119
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-34
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+69
-31
@@ -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;
|
||||
}
|
||||
}
|
||||
+8
@@ -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;
|
||||
}
|
||||
+25
-31
@@ -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<>();
|
||||
|
||||
|
||||
+50
@@ -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));
|
||||
}
|
||||
}
|
||||
+36
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
+17
-1
@@ -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];
|
||||
|
||||
+45
-5
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -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 {}
|
||||
+54
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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": "",
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
+13
@@ -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-"));
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
mixinExtrasVersion=0.4.1
|
||||
+12
@@ -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
Reference in new issue
Block a user