Merge pull request #29 from Sw3ys/fix/user-properties-26.1

Fix account switcher compatibility on Forge and NeoForge
This commit is contained in:
Aizistral
2026-09-30 11:42:10 +02:00
committed by GitHub
9 changed files with 30 additions and 141 deletions
@@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.aizistral.nochatrestrictions.core.NCRCore;
import com.aizistral.nochatrestrictions.core.WrappedUserApiService;
import com.mojang.authlib.minecraft.UserApiService;
import com.mojang.authlib.minecraft.UserApiService.UserProperties;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import net.minecraft.client.Minecraft;
@@ -26,6 +27,14 @@ public class MixinMinecraft {
NCRCore.LOGGER.info("Successfully supplanted UserApiService with a wrapped version.");
}
// Account switchers fetch the properties themselves and put the result straight into
// userPropertiesFuture, so wrapping the service on its own doesn't always stick.
// Everything that gates chat/multiplayer/realms reads them back through here.
@Inject(method = "userProperties", at = @At("RETURN"), cancellable = true)
public void onGetUserProperties(CallbackInfoReturnable<UserProperties> info) {
info.setReturnValue(NCRCore.FORCED_USER_PROPERTIES);
}
@Inject(method = "isNameBanned", at = @At("HEAD"), cancellable = true)
public void onCheckNameBan(CallbackInfoReturnable<Boolean> info) {
info.setReturnValue(Boolean.FALSE);
@@ -1,45 +0,0 @@
package com.aizistral.nochatrestrictions.mixins;
import java.util.Map;
import java.util.concurrent.Executor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.aizistral.nochatrestrictions.core.NCRCore;
import com.google.common.collect.ImmutableSet;
import com.mojang.authlib.minecraft.TelemetrySession;
import com.mojang.authlib.minecraft.UserApiService.UserFlag;
import com.mojang.authlib.minecraft.UserApiService.UserProperties;
import com.mojang.authlib.yggdrasil.YggdrasilUserApiService;
/**
* Applies the chat/multiplayer restriction removal directly on the concrete
* service class instead of just wrapping it once at {@code Minecraft#createUserApiService}.
*
* In-game account switchers (e.g. IAS) build a brand new {@link YggdrasilUserApiService}
* and swap it onto the Minecraft instance without going through the original wrapping
* path, which is why restrictions used to come back after switching accounts. By hooking
* the service class itself, every instance the game ever uses returns permissive
* properties, regardless of who created it or when.
*/
@Mixin(value = YggdrasilUserApiService.class, remap = false)
public class MixinYggdrasilUserApiService {
static {
NCRCore.LOGGER.info("MixinYggdrasilUserApiService initialized succesfully.");
}
@Inject(method = "fetchProperties", at = @At("RETURN"), cancellable = true)
private void onFetchProperties(CallbackInfoReturnable<UserProperties> info) {
info.setReturnValue(NCRCore.FORCED_USER_PROPERTIES);
}
@Inject(method = "newTelemetrySession", at = @At("HEAD"), cancellable = true)
private void onNewTelemetrySession(Executor executor, CallbackInfoReturnable<TelemetrySession> info) {
info.setReturnValue(TelemetrySession.DISABLED);
}
}
@@ -4,8 +4,7 @@
"minVersion": "0.8",
"mixins": [],
"client": [
"MixinMinecraft",
"MixinYggdrasilUserApiService"
"MixinMinecraft"
],
"injectors": {
"defaultRequire": 1
@@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.aizistral.nochatrestrictions.core.NCRCore;
import com.aizistral.nochatrestrictions.core.WrappedUserApiService;
import com.mojang.authlib.minecraft.UserApiService;
import com.mojang.authlib.minecraft.UserApiService.UserProperties;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import net.minecraft.client.Minecraft;
@@ -26,6 +27,14 @@ public class MixinMinecraft {
NCRCore.LOGGER.info("Successfully supplanted UserApiService with a wrapped version.");
}
// Account switchers fetch the properties themselves and put the result straight into
// userPropertiesFuture, so wrapping the service on its own doesn't always stick.
// Everything that gates chat/multiplayer/realms reads them back through here.
@Inject(method = "userProperties", at = @At("RETURN"), cancellable = true)
public void onGetUserProperties(CallbackInfoReturnable<UserProperties> info) {
info.setReturnValue(NCRCore.FORCED_USER_PROPERTIES);
}
@Inject(method = "isNameBanned", at = @At("HEAD"), cancellable = true)
public void onCheckNameBan(CallbackInfoReturnable<Boolean> info) {
info.setReturnValue(Boolean.FALSE);
@@ -1,45 +0,0 @@
package com.aizistral.nochatrestrictions.mixins;
import java.util.Map;
import java.util.concurrent.Executor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.aizistral.nochatrestrictions.core.NCRCore;
import com.google.common.collect.ImmutableSet;
import com.mojang.authlib.minecraft.TelemetrySession;
import com.mojang.authlib.minecraft.UserApiService.UserFlag;
import com.mojang.authlib.minecraft.UserApiService.UserProperties;
import com.mojang.authlib.yggdrasil.YggdrasilUserApiService;
/**
* Applies the chat/multiplayer restriction removal directly on the concrete
* service class instead of just wrapping it once at {@code Minecraft#createUserApiService}.
*
* In-game account switchers (e.g. IAS) build a brand new {@link YggdrasilUserApiService}
* and swap it onto the Minecraft instance without going through the original wrapping
* path, which is why restrictions used to come back after switching accounts. By hooking
* the service class itself, every instance the game ever uses returns permissive
* properties, regardless of who created it or when.
*/
@Mixin(value = YggdrasilUserApiService.class, remap = false)
public class MixinYggdrasilUserApiService {
static {
NCRCore.LOGGER.info("MixinYggdrasilUserApiService initialized succesfully.");
}
@Inject(method = "fetchProperties", at = @At("RETURN"), cancellable = true)
private void onFetchProperties(CallbackInfoReturnable<UserProperties> info) {
info.setReturnValue(NCRCore.FORCED_USER_PROPERTIES);
}
@Inject(method = "newTelemetrySession", at = @At("HEAD"), cancellable = true)
private void onNewTelemetrySession(Executor executor, CallbackInfoReturnable<TelemetrySession> info) {
info.setReturnValue(TelemetrySession.DISABLED);
}
}
@@ -5,8 +5,7 @@
"refmap": "nochatrestrictions.refmap.json",
"mixins": [],
"client": [
"MixinMinecraft",
"MixinYggdrasilUserApiService"
"MixinMinecraft"
],
"injectors": {
"defaultRequire": 1
@@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.aizistral.nochatrestrictions.core.NCRCore;
import com.aizistral.nochatrestrictions.core.WrappedUserApiService;
import com.mojang.authlib.minecraft.UserApiService;
import com.mojang.authlib.minecraft.UserApiService.UserProperties;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import net.minecraft.client.Minecraft;
@@ -26,6 +27,14 @@ public class MixinMinecraft {
NCRCore.LOGGER.info("Successfully supplanted UserApiService with a wrapped version.");
}
// Account switchers fetch the properties themselves and put the result straight into
// userPropertiesFuture, so wrapping the service on its own doesn't always stick.
// Everything that gates chat/multiplayer/realms reads them back through here.
@Inject(method = "userProperties", at = @At("RETURN"), cancellable = true)
public void onGetUserProperties(CallbackInfoReturnable<UserProperties> info) {
info.setReturnValue(NCRCore.FORCED_USER_PROPERTIES);
}
@Inject(method = "isNameBanned", at = @At("HEAD"), cancellable = true)
public void onCheckNameBan(CallbackInfoReturnable<Boolean> info) {
info.setReturnValue(Boolean.FALSE);
@@ -1,45 +0,0 @@
package com.aizistral.nochatrestrictions.mixins;
import java.util.Map;
import java.util.concurrent.Executor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.aizistral.nochatrestrictions.core.NCRCore;
import com.google.common.collect.ImmutableSet;
import com.mojang.authlib.minecraft.TelemetrySession;
import com.mojang.authlib.minecraft.UserApiService.UserFlag;
import com.mojang.authlib.minecraft.UserApiService.UserProperties;
import com.mojang.authlib.yggdrasil.YggdrasilUserApiService;
/**
* Applies the chat/multiplayer restriction removal directly on the concrete
* service class instead of just wrapping it once at {@code Minecraft#createUserApiService}.
*
* In-game account switchers (e.g. IAS) build a brand new {@link YggdrasilUserApiService}
* and swap it onto the Minecraft instance without going through the original wrapping
* path, which is why restrictions used to come back after switching accounts. By hooking
* the service class itself, every instance the game ever uses returns permissive
* properties, regardless of who created it or when.
*/
@Mixin(value = YggdrasilUserApiService.class, remap = false)
public class MixinYggdrasilUserApiService {
static {
NCRCore.LOGGER.info("MixinYggdrasilUserApiService initialized succesfully.");
}
@Inject(method = "fetchProperties", at = @At("RETURN"), cancellable = true)
private void onFetchProperties(CallbackInfoReturnable<UserProperties> info) {
info.setReturnValue(NCRCore.FORCED_USER_PROPERTIES);
}
@Inject(method = "newTelemetrySession", at = @At("HEAD"), cancellable = true)
private void onNewTelemetrySession(Executor executor, CallbackInfoReturnable<TelemetrySession> info) {
info.setReturnValue(TelemetrySession.DISABLED);
}
}
@@ -5,8 +5,7 @@
"refmap": "nochatrestrictions.refmap.json",
"mixins": [],
"client": [
"MixinMinecraft",
"MixinYggdrasilUserApiService"
"MixinMinecraft"
],
"injectors": {
"defaultRequire": 1