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

@@ -1,7 +1,7 @@
plugins {
id 'dev.architectury.loom' version '1.11-SNAPSHOT' apply false
id 'architectury-plugin' version '3.4-SNAPSHOT'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'com.gradleup.shadow' version '8.3.6' apply false
}
architectury {
@@ -31,6 +31,10 @@ subprojects {
// for more information about repositories.
}
loom {
silentMojangMappingsLicense()
}
dependencies {
minecraft "net.minecraft:minecraft:$rootProject.minecraft_version"
mappings loom.officialMojangMappings()
@@ -42,12 +46,12 @@ subprojects {
// If you remove this line, sources will not be generated.
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
tasks.withType(JavaCompile).configureEach {
it.options.release = 17
it.options.release = 21
}
// Configure Maven publishing.
@@ -65,6 +69,7 @@ subprojects {
// 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
// retrieving dependencies.
mavenLocal()
}
}
}

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
}

View File

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

View File

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

View File

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

View File

@@ -1,12 +0,0 @@
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,6 +0,0 @@
{
"pack": {
"description": "skinfix resources",
"pack_format": 15
}
}

View File

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

View File

@@ -1,16 +1,10 @@
plugins {
id 'com.github.johnrengelman.shadow'
}
loom {
forge {
mixinConfig "skinfix.mixins.json"
}
id 'com.gradleup.shadow'
}
architectury {
platformSetupLoomIde()
forge()
neoForge()
}
configurations {
@@ -20,7 +14,7 @@ configurations {
}
compileClasspath.extendsFrom common
runtimeClasspath.extendsFrom common
developmentForge.extendsFrom common
developmentNeoForge.extendsFrom common
// 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.
@@ -30,18 +24,25 @@ configurations {
}
}
repositories {
maven {
name = 'NeoForged'
url = 'https://maven.neoforged.net/releases'
}
}
dependencies {
forge "net.minecraftforge:forge:$rootProject.forge_version"
neoForge "net.neoforged:neoforge:$rootProject.neoforge_version"
common(project(path: ':common', configuration: 'namedElements')) { transitive false }
shadowBundle project(path: ':common', configuration: 'transformProductionForge')
shadowBundle project(path: ':common', configuration: 'transformProductionNeoForge')
}
processResources {
inputs.property 'version', project.version
filesMatching('META-INF/mods.toml') {
filesMatching('META-INF/neoforge.mods.toml') {
expand version: project.version
}
}
@@ -52,5 +53,5 @@ shadowJar {
}
remapJar {
input.set shadowJar.archiveFile
inputFile.set shadowJar.archiveFile
}

View File

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

View File

@@ -0,0 +1,12 @@
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

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

View File

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