aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/io/github/moulberry/notenoughupdates/loader/KotlinLoadingTweaker.java
blob: 0ce682dd678434d3ec8978aaa6f4cc1a13511615 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * Copyright (C) 2022 Linnea Gräf
 *
 * This file is part of NotEnoughUpdates.
 *
 * NotEnoughUpdates is free software: you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * NotEnoughUpdates is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
 */

package io.github.moulberry.notenoughupdates.loader;

import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;

import java.nio.file.Path;
import java.util.Arrays;

/**
 * <h3>TO OTHER MOD AUTHORS THAT WANT TO BUNDLE KOTLIN AND ARE RUNNING INTO CONFLICTS WITH NEU:</h3>
 *
 * <p>
 * NEU allows you to stop it from loading Kotlin by specifying one of two properties in the
 * {@link Launch#blackboard}. This needs to be done before {@link #injectIntoClassLoader}, so do it in your Tweakers
 * constructor. Most likely you will not need to do this, since different Kotlin versions rarely contain breaking changes
 * and most people do not use any of the newer features.
 * </p>
 *
 * <ul>
 *   	<li>
 *      {@code neu.relinquishkotlin.ifbelow}. Set to an int array, like so: {@code [1, 7, 20]}.
 * 			See {@link #BUNDLED_KOTLIN_VERSION}
 *   	</li>
 *  	<li>
 *      {@code neu.relinquishkotlin.always}. If set to {@code true}, will prevent NEU from loading any kotlin.
 *  		Before you use this, consider: are you sure you are always gonna have a more update version of Kotlin than NEU?
 *  		And, even if you are sure, do you really want to block us from updating our Kotlin, after we so graciously allowed
 *  		you to stop us from breaking your mod.
 *  	</li>
 *  	<li>
 *  	 	Additionally, setting the jvm property {@code neu.relinquishkotlin} to 1, will prevent NEU from loading Kotlin.
 *  	</li>
 * </ul>
 *
 * <p>
 * Sadly the Kotlin stdlib cannot be relocated (and we wouldn't want to do that either, because it would prevent us
 * from interoperating with other Kotlin mods in some ways (for example: reflection)), so in order to minimize conflicts,
 * we allow other mods to stop us from loading Kotlin. Usually this would be handled with a common library provider, but
 * since those come bundled with literal Remote Code Execution, Cosmetics, and unskippable Updates that can just not be
 * considered a good thing by any developer with a conscience nowadays, those are not an option for us. Another option
 * would be a dependency resolution algorithm like Jar-in-Jar, but sadly we have to mod on a 7 year old modding platform,
 * so we have to make due with our crude hack.
 * </p>
 */
public class KotlinLoadingTweaker extends JARLoadingTweaker {

	/**
	 * Full version format: [1, 7, 20] (1.7.20)
	 * RC version format: [1, 7, 20, 1] (1.7.20-rc1)
	 */
	public static final int[] BUNDLED_KOTLIN_VERSION = new int[]{1, 8, 21};

	@Override
	protected Path getFilesToLoad() {
		if ("1".equals(System.getProperty("neu.relinquishkotlin"))) {
			System.out.println("NEU is forced to relinquish Kotlin by user configuration.");
			return null;
		}
		if (Launch.blackboard.get("fml.deobfuscatedEnvironment") == Boolean.TRUE) {
			System.out.println("Skipping NEU Kotlin loading in development environment.");
			return null;
		}
		Object relinquishAlways = Launch.blackboard.get("neu.relinquishkotlin.always");
		if (relinquishAlways == Boolean.TRUE) {
			System.err.println(
				"NEU is forced to blanket relinquish loading Kotlin. This is probably a bad judgement call by another developer.");
			return null;
		}
		Object relinquishIfBelow = Launch.blackboard.get("neu.relinquishkotlin.ifbelow");
		if ((relinquishIfBelow instanceof int[])) {
			int[] requiredVersion = (int[]) relinquishIfBelow;
			if (!areWeBundlingAKotlinVersionHigherThan(requiredVersion)) {
				System.err.println(
					"NEU is relinquishing loading Kotlin because a higher version is requested. This may lead to errors if the advertised Kotlin version is not found. (" +
						Arrays.toString(requiredVersion) + " required, " + Arrays.toString(BUNDLED_KOTLIN_VERSION) +
						" available)");
				return null;
			}
		}

		System.out.println("Attempting to load Kotlin from NEU wrapped libraries.");

		return getShadowedElement("/neu-kotlin-libraries-wrapped");
	}

	@Override
	protected String getTestClass() {
		return "kotlin.KotlinVersion";
	}

	public boolean areWeBundlingAKotlinVersionHigherThan(int[] x) {
		for (int i = 0; ; i++) {
			boolean doWeHaveMoreVersionIdsLeft = i < BUNDLED_KOTLIN_VERSION.length;
			boolean doTheyHaveMoreVersionIdsLeft = i < x.length;
			if (doWeHaveMoreVersionIdsLeft && !doTheyHaveMoreVersionIdsLeft) return false;
			if (doTheyHaveMoreVersionIdsLeft && !doWeHaveMoreVersionIdsLeft) return true;
			if (!doTheyHaveMoreVersionIdsLeft) return true;
			if (x[i] > BUNDLED_KOTLIN_VERSION[i]) return false;
		}
	}

	@Override
	public void injectIntoClassLoader(LaunchClassLoader classLoader) {
		performLoading(classLoader);
	}
}