feat: 1.21 port

This commit is contained in:
2025-09-01 02:06:58 +08:00
parent 62eee93fea
commit 7a0e44b88c
16 changed files with 92 additions and 112 deletions

View File

@@ -7,5 +7,5 @@ dependencies {
// which get remapped to the correct annotations on each platform.
// Do NOT use other classes from Fabric Loader.
modImplementation "net.fabricmc:fabric-loader:$rootProject.fabric_loader_version"
compileOnly("com.mojang:authlib:4.0.43")
compileOnly("com.mojang:authlib:6.0.54")
}

View File

@@ -3,16 +3,13 @@ package net.magicterra.skinfix.inject;
import com.mojang.authlib.Environment;
import com.mojang.authlib.EnvironmentParser;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.HttpAuthenticationService;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.minecraft.UserApiService;
import com.mojang.authlib.minecraft.client.MinecraftClient;
import com.mojang.authlib.yggdrasil.ServicesKeySet;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilEnvironment;
import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
import com.mojang.authlib.yggdrasil.YggdrasilServicesKeyInfo;
import com.mojang.authlib.yggdrasil.YggdrasilUserApiService;
import java.net.Proxy;
import java.net.URL;
@@ -37,13 +34,14 @@ public class InjectYggdrasilAuthenticationService extends YggdrasilAuthenticatio
super(proxy, environment);
this.environment = environment;
final URL publicKeySetUrl = HttpAuthenticationService.constantURL("https://mc.gardel.top/minecraftservices/publickeys");
this.servicesKeySet = YggdrasilServicesKeyInfo.get(publicKeySetUrl, this);
final MinecraftClient client = MinecraftClient.unauthenticated(proxy);
final URL publicKeySetUrl = constantURL("https://mc.gardel.top/minecraftservices/publickeys");
this.servicesKeySet = YggdrasilServicesKeyInfo.get(publicKeySetUrl, client);
}
@Override
public MinecraftSessionService createMinecraftSessionService() {
return new InjectYggdrasilMinecraftSessionService(this, environment);
return new InjectYggdrasilMinecraftSessionService(servicesKeySet, getProxy(), environment);
}
@Override
@@ -53,21 +51,6 @@ public class InjectYggdrasilAuthenticationService extends YggdrasilAuthenticatio
@Override
public GameProfileRepository createProfileRepository() {
return new YggdrasilGameProfileRepository(this, Environment.create(
environment.getAuthHost(),
"https://mc.gardel.top/api",
environment.getSessionHost(),
environment.getServicesHost(),
environment.getName()));
}
@Override
public UserApiService createUserApiService(String accessToken) throws AuthenticationException {
return new YggdrasilUserApiService(accessToken, getProxy(), Environment.create(
environment.getAuthHost(),
environment.getAccountsHost(),
environment.getSessionHost(),
"https://mc.gardel.top/minecraftservices",
environment.getName()));
return new YggdrasilGameProfileRepository(getProxy(), new Environment(environment.sessionHost(), "https://mc.gardel.top/minecraftservices", environment.name()));
}
}

View File

@@ -1,22 +1,20 @@
package net.magicterra.skinfix.inject;
import com.google.common.collect.Iterables;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.mojang.authlib.Environment;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.InsecurePublicKeyException;
import com.mojang.authlib.SignatureState;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.minecraft.MinecraftProfileTextures;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.yggdrasil.ServicesKeySet;
import com.mojang.authlib.yggdrasil.ServicesKeyType;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
import java.lang.reflect.Field;
import java.net.Proxy;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -28,12 +26,15 @@ import org.slf4j.LoggerFactory;
* @since 2025-04-26 22:11
*/
public class InjectYggdrasilMinecraftSessionService extends YggdrasilMinecraftSessionService {
private static final Logger LOGGER = LoggerFactory.getLogger(InjectYggdrasilMinecraftSessionService.class);
private static final Logger LOGGER = LoggerFactory.getLogger(YggdrasilMinecraftSessionService.class);
private final Gson gson;
protected InjectYggdrasilMinecraftSessionService(YggdrasilAuthenticationService service, Environment env) {
super(service, env);
private final ServicesKeySet servicesKeySet;
protected InjectYggdrasilMinecraftSessionService(ServicesKeySet servicesKeySet, Proxy proxy, Environment env) {
super(servicesKeySet, proxy, env);
this.servicesKeySet = servicesKeySet;
try {
Field field = YggdrasilMinecraftSessionService.class.getDeclaredField("gson");
field.setAccessible(true);
@@ -44,52 +45,47 @@ public class InjectYggdrasilMinecraftSessionService extends YggdrasilMinecraftSe
}
@Override
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) throws InsecurePublicKeyException {
final Property textureProperty = Iterables.getFirst(profile.getProperties().get("textures"), null);
if (textureProperty == null) {
return new HashMap<>();
}
final String value = requireSecure ? getSecurePropertyValue(textureProperty) : textureProperty.getValue();
public MinecraftProfileTextures unpackTextures(Property packedTextures) {
final String value = packedTextures.value();
final SignatureState signatureState = getPropertySignatureState(packedTextures);
final MinecraftTexturesPayload result;
try {
final String json = new String(Base64.getDecoder().decode(value), StandardCharsets.UTF_8);
result = gson.fromJson(json, MinecraftTexturesPayload.class);
} catch (final JsonParseException e) {
} catch (final JsonParseException | IllegalArgumentException e) {
LOGGER.error("Could not decode textures payload", e);
return new HashMap<>();
return MinecraftProfileTextures.EMPTY;
}
if (result == null || result.getTextures() == null) {
return new HashMap<>();
if (result == null || result.textures() == null || result.textures().isEmpty()) {
return MinecraftProfileTextures.EMPTY;
}
for (final Map.Entry<MinecraftProfileTexture.Type, MinecraftProfileTexture> entry : result.getTextures().entrySet()) {
final Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures = result.textures();
for (final Map.Entry<MinecraftProfileTexture.Type, MinecraftProfileTexture> entry : textures.entrySet()) {
final String url = entry.getValue().getUrl();
if (!InjectTextureUrlChecker.isAllowedTextureDomain(url)) {
LOGGER.error("Textures payload contains blocked domain: {}", url);
return new HashMap<>();
return MinecraftProfileTextures.EMPTY;
}
}
return result.getTextures();
return new MinecraftProfileTextures(
textures.get(MinecraftProfileTexture.Type.SKIN),
textures.get(MinecraftProfileTexture.Type.CAPE),
textures.get(MinecraftProfileTexture.Type.ELYTRA),
signatureState
);
}
@Override
public String getSecurePropertyValue(final Property property) throws InsecurePublicKeyException {
private SignatureState getPropertySignatureState(final Property property) {
if (!property.hasSignature()) {
LOGGER.error("Signature is missing from Property {}", property.getName());
throw new InsecurePublicKeyException.MissingException();
return SignatureState.UNSIGNED;
}
final ServicesKeySet servicesKeySet = getAuthenticationService().getServicesKeySet();
if (servicesKeySet.keys(ServicesKeyType.PROFILE_PROPERTY).stream().noneMatch(key -> key.validateProperty(property))) {
LOGGER.error("Property {} has been tampered with (signature invalid)", property.getName());
throw new InsecurePublicKeyException.InvalidException("Property has been tampered with (signature invalid)");
return SignatureState.INVALID;
}
return property.getValue();
return SignatureState.SIGNED;
}
}

View File

@@ -1,14 +1,13 @@
{
"required": true,
"package": "net.magicterra.skinfix.mixin",
"compatibilityLevel": "JAVA_17",
"compatibilityLevel": "JAVA_21",
"minVersion": "0.8",
"client": [
"MinecraftMixin"
],
"mixins": [
"MinecraftMixin",
"ProfilePublicKeyDataMixin"
],
"mixins": [],
"injectors": {
"defaultRequire": 1
}