Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package customskinloader.bootstrap;

public final class ModLoaderInfo {
public static final String NAME_PROPERTY = "customskinloader.modLoader.name";
public static final String VERSION_PROPERTY = "customskinloader.modLoader.version";

private ModLoaderInfo() {
}

public static void publish(String name, String version) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Mod loader name must not be empty");
}
if (version == null || version.trim().isEmpty()) {
throw new IllegalArgumentException("Mod loader version must not be empty");
}

System.setProperty(NAME_PROPERTY, name);
System.setProperty(VERSION_PROPERTY, version);
Comment on lines +18 to +19
BootstrapLogger.LOGGER.info("Detected mod loader " + name + " " + version);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package customskinloader.bootstrap.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;

public class ReflectionUtils {
private static final Map<String, Class<?>> CLASSES = new ConcurrentHashMap<>();
private static final Map<Pair<Class<?>, String>, Field> FIELDS = new ConcurrentHashMap<>();
private static final Map<Triple<Class<?>, String, List<Class<?>>>, Method> METHODS = new ConcurrentHashMap<>();

public static Class<?> findClass(String className) {
return CLASSES.computeIfAbsent(className, k -> {
try {
return Class.forName(k);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
});
}

public static Field findField(Class<?> clazz, String fieldName) {
return FIELDS.computeIfAbsent(Pair.of(clazz, fieldName), k -> {
try {
Field f = k.getLeft().getDeclaredField(k.getRight());
f.setAccessible(true);
return f;
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
});
}

public static Method findMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
return METHODS.computeIfAbsent(Triple.of(clazz, methodName, Arrays.asList(parameterTypes)), k -> {
try {
Method m = k.getLeft().getDeclaredMethod(k.getMiddle(), k.getRight().toArray(new Class<?>[0]));
m.setAccessible(true);
return m;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Set;

import customskinloader.bootstrap.BootstrapLogger;
import customskinloader.bootstrap.ModLoaderInfo;
import customskinloader.bootstrap.installer.CommonJarInstaller;
import customskinloader.bootstrap.transformer.ClassTransformationReport;
import customskinloader.bootstrap.transformer.TransformerBootstrapSupport;
Expand All @@ -35,6 +36,7 @@ private TransformerBootstrap() {

public static void initialize() {
LOGGER.info("Initializing CustomSkinLoader Bootstrap for Fabric");
publishModLoaderInfo();
releaseRuntimeArtifacts();
int targetCount = 0;
for (String targetClassName : SUPPORT.collectTargetClassNames()) {
Expand All @@ -44,6 +46,10 @@ public static void initialize() {
LOGGER.info("Initialized CustomSkinLoader Bootstrap for Fabric with " + targetCount + " reflected Mixin target(s)");
}

private static void publishModLoaderInfo() {
ModLoaderInfo.publish("Fabric", FabricLoader.getInstance().getModContainer("fabricloader").map(m -> m.getMetadata().getVersion().getFriendlyString()).orElse("unknown"));
}

private static void releaseRuntimeArtifacts() {
try {
Path commonJar = CommonJarInstaller.releaseCommonJar(FabricLoader.getInstance().getGameDir(), "intermediary");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import java.util.Map;

import customskinloader.bootstrap.BootstrapLogger;
import customskinloader.bootstrap.ModLoaderInfo;
import customskinloader.bootstrap.installer.CommonJarInstaller;
import customskinloader.bootstrap.transformer.ClassTransformationReport;
import customskinloader.bootstrap.transformer.TransformerBootstrapSupport;
import net.minecraft.launchwrapper.LaunchClassLoader;
import net.minecraftforge.fml.relauncher.FMLInjectionData;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.tree.ClassNode;

Expand All @@ -22,6 +24,9 @@ private TransformerBootstrap() {

public static void injectData(Map<String, Object> data) {
try {
Object[] version = FMLInjectionData.data();
ModLoaderInfo.publish("Forge", version[0] + "." + version[1] + "." + version[2] + "." + version[3]);

Path runtimeDirectory = resolveRuntimeDirectory(data);
LOGGER.info("Initializing CustomSkinLoader Bootstrap for Forge v1 in " + BootstrapLogger.formatPath(runtimeDirectory));
Path commonJar = CommonJarInstaller.releaseCommonJar(runtimeDirectory, "srg");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package customskinloader.bootstrap.forge.v2;

import customskinloader.bootstrap.util.ModLocatorUtils;
import net.minecraftforge.fml.loading.FMLPaths;
import net.minecraftforge.fml.loading.moddiscovery.ModsFolderLocator;

import customskinloader.bootstrap.util.ModLocatorUtils;

public final class CommonModLocator extends ModsFolderLocator {
public CommonModLocator() {
ModLocatorUtils.initialize(this, ModsFolderLocator.class, FMLPaths.GAMEDIR.get(), "srg");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public String name() {
@Override
public void initialize(IEnvironment environment) {
LOGGER.info("Initializing CustomSkinLoader Bootstrap transformation service");
TransformerBootstrap.publishModLoaderInfo();
try {
List<Path> extraLocators = ModDirTransformerDiscoverer.getExtraLocators();
Path servicePath = Paths.get(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).toAbsolutePath().normalize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,59 @@
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import cpw.mods.modlauncher.ArgumentHandler;
import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.ITransformer;
import cpw.mods.modlauncher.api.ITransformerVotingContext;
import cpw.mods.modlauncher.api.TransformerVoteResult;
import customskinloader.bootstrap.BootstrapLogger;
import customskinloader.bootstrap.ModLoaderInfo;
import customskinloader.bootstrap.transformer.ClassTransformationReport;
import customskinloader.bootstrap.transformer.TransformerBootstrapSupport;
import customskinloader.bootstrap.util.ReflectionUtils;
import net.minecraftforge.fml.loading.FMLLoader;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.tree.ClassNode;

final class TransformerBootstrap {
private static final Logger LOGGER = BootstrapLogger.LOGGER;
private static final TransformerBootstrapSupport SUPPORT = new TransformerBootstrapSupport(TransformerBootstrap.class,
// official: neoforge 1.20.2-20.2.3 ~ 1.21.3-21.3.97
// srg: forge *, neoforge 1.21.4-21.4.0 ~ 1.21.9-21.9.13
Launcher.INSTANCE.environment().findLaunchHandler("forgeclient")
.map(handle -> handle.getClass().getName().startsWith("net.neoforged.")).orElse(false) ? "official" : "srg");

private TransformerBootstrap() {
}

public static void publishModLoaderInfo() {
try {
Object argumentHandler = ReflectionUtils.findField(Launcher.class, "argumentHandler").get(Launcher.INSTANCE);
String[] args = (String[]) ReflectionUtils.findField(ArgumentHandler.class, "args").get(argumentHandler);
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (Objects.equals(arg, "--fml.forgeVersion")) {
// forge 1.13.2-25.0.216 ~ 1.20.2-48.1.0
ModLoaderInfo.publish("Forge", args[i + 1]);
return;
} else if (Objects.equals(arg, "--fml.neoForgeVersion")) {
// neoforge 1.20.2-20.2.3 ~ 1.21.9-21.9.13
ModLoaderInfo.publish("NeoForge", args[i + 1]);
return;
}
}

// forge 1.20.3-49.0.1+
ModLoaderInfo.publish("Forge", FMLLoader.class.getPackage().getImplementationVersion());
} catch (Exception e) {
LOGGER.warn("Could not determine mod loader", e);
ModLoaderInfo.publish("Forge", "unknown");
}
}

public static List<ITransformer> buildTransformers() {
List<ITransformer> transformers = new ArrayList<>();
for (String targetClassName : SUPPORT.collectTargetClassNames()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public CommonModLocator() {
ModLocatorUtils.initialize(this, ModsFolderLocator.class, FMLPaths.GAMEDIR.get(), "official");
}

@Override
public String name() {
return ModLocatorUtils.LOCATOR_NAME;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package customskinloader.bootstrap.neoforge.v2;

import customskinloader.bootstrap.BootstrapLogger;
import customskinloader.bootstrap.ModLoaderInfo;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforgespi.transformation.ClassProcessorProvider;
import org.apache.logging.log4j.Logger;

// NeoForge 21.9.14+
public final class ClassProcessorProviderImpl implements ClassProcessorProvider {
private static final Logger LOGGER = BootstrapLogger.LOGGER;

@Override
public void createProcessors(Context context, Collector collector) {
// Always register the bridge processor so late transformer registrations can still be observed.
LOGGER.info("Registering CustomSkinLoader Bootstrap NeoForge class processor");
ModLoaderInfo.publish("NeoForge", FMLLoader.getCurrent().getVersionInfo().neoForgeVersion());
collector.add(TransformerBootstrap.createProcessor());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.neoforged.fml.loading.FMLPaths;
import net.neoforged.fml.loading.moddiscovery.locators.ModsFolderLocator;

// NeoForge 20.6.14+
public final class CommonModLocator extends ModsFolderLocator {
public CommonModLocator() {
ModLocatorUtils.initialize(this, ModsFolderLocator.class, FMLPaths.GAMEDIR.get(), "official");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ private static Logger initLogger() {
logger.info("Operating System: " + System.getProperty("os.name") + " (" + System.getProperty("os.arch") + ") version " + System.getProperty("os.version"));
logger.info("Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor"));
logger.info("Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor"));
logger.info("Minecraft: " + MinecraftUtil.getMinecraftMainVersion());
logger.info("Minecraft: " + MinecraftUtil.getMinecraftMainVersion() + " (" + MinecraftUtil.getLoaderName() + " " + MinecraftUtil.getLoaderVersion() + ")");
return logger;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ public static HttpResponce makeHttpRequest(HttpRequest request, int redirectTime
CustomSkinLoader.logger.debug("Try to read cache '" + request.cacheFile + "'.");
return loadFromCache(request, new HttpResponce());
}
CustomSkinLoader.logger.debug("Try to request '" + request.url + (request.userAgent == null ? "'." : "' with user agent '" + request.userAgent + "'."));
String userAgent = UserAgentUtil.expandUserAgent(request.userAgent);
CustomSkinLoader.logger.debug("Try to request '" + request.url + "' with user agent '" + userAgent + "'.");
//Check Cache
if (StringUtils.isNotEmpty(request.payload) || CustomSkinLoader.config.forceDisableCache) {
request.cacheTime = -1;//No Cache
Expand Down Expand Up @@ -156,9 +157,7 @@ public static HttpResponce makeHttpRequest(HttpRequest request, int redirectTime
c.setRequestProperty("If-None-Match", cacheInfo.etag);
}
c.setRequestProperty("Accept-Encoding", "gzip");
if (request.userAgent != null) {
c.setRequestProperty("User-Agent", request.userAgent);
}
c.setRequestProperty("User-Agent", userAgent);
if (StringUtils.isNotEmpty(request.payload)) {
CustomSkinLoader.logger.info("Payload: " + request.payload);
c.setRequestMethod("POST");
Expand Down
20 changes: 11 additions & 9 deletions Common/src/main/java/customskinloader/utils/MinecraftUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
import com.mojang.authlib.GameProfile;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.resources.SkinManager;

/**
* @author Alexander Xia
Expand All @@ -24,13 +22,6 @@ public static File getMinecraftDataDir() {
return Minecraft.getInstance().gameDirectory;
}

public static TextureManager getTextureManager() {
return Minecraft.getInstance().getTextureManager();
}

public static SkinManager getSkinManager() {
return Minecraft.getInstance().getSkinManager();
}
private static String minecraftMainVersion = null;

public static String getMinecraftMainVersion() {
Expand Down Expand Up @@ -67,6 +58,17 @@ public static String getMinecraftMainVersion() {
return "unknown";
}

private static final String MOD_LOADER_NAME_PROPERTY = "customskinloader.modLoader.name";
private static final String MOD_LOADER_VERSION_PROPERTY = "customskinloader.modLoader.version";

public static String getLoaderName() {
return System.getProperty(MOD_LOADER_NAME_PROPERTY, "unknown");
}

public static String getLoaderVersion() {
return System.getProperty(MOD_LOADER_VERSION_PROPERTY, "unknown");
}

// (domain|ip)(:port)
public static String getServerAddress() {
ServerData data = Minecraft.getInstance().getCurrentServer();
Expand Down
55 changes: 55 additions & 0 deletions Common/src/main/java/customskinloader/utils/UserAgentUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package customskinloader.utils;

import customskinloader.CustomSkinLoader;

public final class UserAgentUtil {
public static final String DEFAULT_USER_AGENT_PLACEHOLDER = "{DEFAULT_USER_AGENT}";
public static final String CSL_VERSION_PLACEHOLDER = "{CSL_VERSION}";
public static final String MINECRAFT_VERSION_PLACEHOLDER = "{MINECRAFT_VERSION}";
public static final String MOD_LOADER_NAME_PLACEHOLDER = "{MOD_LOADER_NAME}";
public static final String MOD_LOADER_VERSION_PLACEHOLDER = "{MOD_LOADER_VERSION}";
public static final String JAVA_VERSION_PLACEHOLDER = "{JAVA_VERSION}";

private UserAgentUtil() {
}

public static String expandUserAgent(String userAgent) {
String modVersion = sanitizeComponent(CustomSkinLoader.CustomSkinLoader_FULL_VERSION);
String minecraftVersion = sanitizeComponent(MinecraftUtil.getMinecraftMainVersion());
String loaderName = sanitizeComponent(MinecraftUtil.getLoaderName());
String loaderVersion = sanitizeComponent(MinecraftUtil.getLoaderVersion());
String javaVersion = sanitizeComponent(System.getProperty("java.version", "unknown"));

String defaultUserAgent = "CustomSkinLoader/" + modVersion
+ " (Minecraft/" + minecraftVersion
+ "; " + loaderName + "/" + loaderVersion
+ "; Java/" + javaVersion + ")";

if (userAgent == null) {
return defaultUserAgent;
}
return userAgent
.replace(DEFAULT_USER_AGENT_PLACEHOLDER, defaultUserAgent)
.replace(CSL_VERSION_PLACEHOLDER, modVersion)
.replace(MINECRAFT_VERSION_PLACEHOLDER, minecraftVersion)
.replace(MOD_LOADER_NAME_PLACEHOLDER, loaderName)
.replace(MOD_LOADER_VERSION_PLACEHOLDER, loaderVersion)
.replace(JAVA_VERSION_PLACEHOLDER, javaVersion);
}

private static String sanitizeComponent(String value) {
if (value == null || value.trim().isEmpty()) {
return "unknown";
}
String component = value.trim();
StringBuilder sanitized = new StringBuilder(component.length());
for (int i = 0; i < component.length(); i++) {
char c = component.charAt(i);
sanitized.append(c >= '0' && c <= '9'
|| c >= 'A' && c <= 'Z'
|| c >= 'a' && c <= 'z'
|| "!#$%&'*+-.^_`|~".indexOf(c) >= 0 ? c : '_');
}
return sanitized.toString();
}
}