Compare commits

3 Commits
1.21 ... 1.20.1

Author SHA1 Message Date
6a8b93c004 fix: logo 显示问题 2025-09-02 02:21:46 +08:00
1e2724acec fix: update version 2025-09-01 02:13:28 +08:00
7819376ac3 fix: wrong mixin dist 2025-09-01 02:07:51 +08:00
16 changed files with 111 additions and 91 deletions

View File

@@ -1,7 +1,7 @@
plugins { plugins {
id 'dev.architectury.loom' version '1.11-SNAPSHOT' apply false id 'dev.architectury.loom' version '1.11-SNAPSHOT' apply false
id 'architectury-plugin' version '3.4-SNAPSHOT' id 'architectury-plugin' version '3.4-SNAPSHOT'
id 'com.gradleup.shadow' version '8.3.6' apply false id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
} }
architectury { architectury {
@@ -31,10 +31,6 @@ subprojects {
// for more information about repositories. // for more information about repositories.
} }
loom {
silentMojangMappingsLicense()
}
dependencies { dependencies {
minecraft "net.minecraft:minecraft:$rootProject.minecraft_version" minecraft "net.minecraft:minecraft:$rootProject.minecraft_version"
mappings loom.officialMojangMappings() mappings loom.officialMojangMappings()
@@ -46,12 +42,12 @@ subprojects {
// If you remove this line, sources will not be generated. // If you remove this line, sources will not be generated.
withSourcesJar() withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_21 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_17
} }
tasks.withType(JavaCompile).configureEach { tasks.withType(JavaCompile).configureEach {
it.options.release = 21 it.options.release = 17
} }
// Configure Maven publishing. // Configure Maven publishing.
@@ -69,7 +65,6 @@ subprojects {
// Notice: This block does NOT have the same function as the block in the top level. // Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for // The repositories here will be used for publishing your artifact, not for
// retrieving dependencies. // retrieving dependencies.
mavenLocal()
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
plugins { plugins {
id 'com.gradleup.shadow' id 'com.github.johnrengelman.shadow'
} }
architectury { architectury {
@@ -46,5 +46,5 @@ shadowJar {
} }
remapJar { remapJar {
inputFile.set shadowJar.archiveFile input.set shadowJar.archiveFile
} }

View File

@@ -25,9 +25,9 @@
"skinfix.mixins.json" "skinfix.mixins.json"
], ],
"depends": { "depends": {
"fabricloader": ">=0.17", "fabricloader": ">=0.17.2",
"minecraft": ">=1.21", "minecraft": "~1.20.1",
"java": ">=21" "java": ">=17"
}, },
"suggests": { "suggests": {
"another-mod": "*" "another-mod": "*"

View File

@@ -1,10 +1,16 @@
plugins { plugins {
id 'com.gradleup.shadow' id 'com.github.johnrengelman.shadow'
}
loom {
forge {
mixinConfig "skinfix.mixins.json"
}
} }
architectury { architectury {
platformSetupLoomIde() platformSetupLoomIde()
neoForge() forge()
} }
configurations { configurations {
@@ -14,7 +20,7 @@ configurations {
} }
compileClasspath.extendsFrom common compileClasspath.extendsFrom common
runtimeClasspath.extendsFrom common runtimeClasspath.extendsFrom common
developmentNeoForge.extendsFrom common developmentForge.extendsFrom common
// Files in this configuration will be bundled into your mod using the Shadow plugin. // Files in this configuration will be bundled into your mod using the Shadow plugin.
// Don't use the `shadow` configuration from the plugin itself as it's meant for excluding files. // Don't use the `shadow` configuration from the plugin itself as it's meant for excluding files.
@@ -24,25 +30,18 @@ configurations {
} }
} }
repositories {
maven {
name = 'NeoForged'
url = 'https://maven.neoforged.net/releases'
}
}
dependencies { dependencies {
neoForge "net.neoforged:neoforge:$rootProject.neoforge_version" forge "net.minecraftforge:forge:$rootProject.forge_version"
common(project(path: ':common', configuration: 'namedElements')) { transitive false } common(project(path: ':common', configuration: 'namedElements')) { transitive false }
shadowBundle project(path: ':common', configuration: 'transformProductionNeoForge') shadowBundle project(path: ':common', configuration: 'transformProductionForge')
} }
processResources { processResources {
inputs.property 'version', project.version inputs.property 'version', project.version
filesMatching('META-INF/neoforge.mods.toml') { filesMatching('META-INF/mods.toml') {
expand version: project.version expand version: project.version
} }
} }
@@ -53,5 +52,5 @@ shadowJar {
} }
remapJar { remapJar {
inputFile.set shadowJar.archiveFile input.set shadowJar.archiveFile
} }

1
forge/gradle.properties Normal file
View File

@@ -0,0 +1 @@
loom.platform=forge

View File

@@ -0,0 +1,12 @@
package net.magicterra.skinfix.forge;
import net.magicterra.skinfix.SkinFixMod;
import net.minecraftforge.fml.common.Mod;
@Mod(SkinFixMod.MOD_ID)
public final class SkinFixModForge {
public SkinFixModForge() {
// Run our common setup.
SkinFixMod.init();
}
}

View File

@@ -1,5 +1,5 @@
modLoader = "javafml" modLoader = "javafml"
loaderVersion = "[1,)" loaderVersion = "[47,)"
#issueTrackerURL = "" #issueTrackerURL = ""
license = "MIT" license = "MIT"
@@ -14,18 +14,16 @@ Fix Minecraft does not load skin png not prefix with minecraft.net
logoFile = "logo_skinfix.png" logoFile = "logo_skinfix.png"
[[dependencies.skinfix]] [[dependencies.skinfix]]
modId = "neoforge" modId = "forge"
type="required" mandatory = true
versionRange = "[21.0.0-beta,)" versionRange = "[47,)"
ordering = "NONE" ordering = "NONE"
side = "BOTH" side = "BOTH"
[[dependencies.skinfix]] [[dependencies.skinfix]]
modId = "minecraft" modId = "minecraft"
type="required" mandatory = true
versionRange = "[1.21,1.21.5)" versionRange = "[1.20.1,)"
ordering = "NONE" ordering = "NONE"
side = "BOTH" side = "BOTH"
[[mixins]]
config="skinfix.mixins.json"

View File

@@ -0,0 +1,6 @@
{
"pack": {
"description": "skinfix resources",
"pack_format": 15
}
}

View File

@@ -5,9 +5,9 @@ org.gradle.parallel=true
mod_version=1.0.0 mod_version=1.0.0
maven_group=net.magicterra maven_group=net.magicterra
archives_name=skinfix archives_name=skinfix
enabled_platforms=fabric,neoforge enabled_platforms=fabric,forge
# Minecraft properties # Minecraft properties
minecraft_version=1.21 minecraft_version=1.20.1
# Dependencies # Dependencies
fabric_loader_version=0.17.2 fabric_loader_version=0.17.2
neoforge_version=21.0.167 forge_version=1.20.1-47.4.6

View File

@@ -1 +0,0 @@
loom.platform=neoforge

View File

@@ -1,12 +0,0 @@
package net.magicterra.skinfix.neoforge;
import net.magicterra.skinfix.SkinFixMod;
import net.neoforged.fml.common.Mod;
@Mod(SkinFixMod.MOD_ID)
public final class SkinFixModNeoForge {
public SkinFixModNeoForge() {
// Run our common setup.
SkinFixMod.init();
}
}

View File

@@ -11,4 +11,4 @@ rootProject.name = 'skinfix'
include 'common' include 'common'
include 'fabric' include 'fabric'
include 'neoforge' include 'forge'