diff --git a/Fabric/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java b/Fabric/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java deleted file mode 100644 index 6161462..0000000 --- a/Fabric/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.aizistral.nochatrestrictions.core; - -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.Executor; - -import org.jetbrains.annotations.Nullable; - -import com.aizistral.nochatrestrictions.config.NCRConfig; -import com.google.common.collect.ImmutableSet; -import com.mojang.authlib.exceptions.AuthenticationException; -import com.mojang.authlib.minecraft.TelemetrySession; -import com.mojang.authlib.minecraft.UserApiService; -import com.mojang.authlib.minecraft.report.AbuseReportLimits; -import com.mojang.authlib.yggdrasil.request.AbuseReportRequest; -import com.mojang.authlib.yggdrasil.response.KeyPairResponse; - -public class WrappedUserApiService implements UserApiService { - private final UserApiService service; - private @Nullable UserProperties properties = null; - - public WrappedUserApiService(UserApiService service) { - this.service = service; - } - - @Override - public UserProperties fetchProperties() throws AuthenticationException { - if (this.properties != null) - return this.properties; - - NCRConfig config = NCRConfig.getInstance(); - UserProperties properties = this.service.fetchProperties(); - ImmutableSet.Builder flags = ImmutableSet.builder(); - - flags.add(UserFlag.CHAT_ALLOWED); // always let the player access chat - flags.add(UserFlag.SERVERS_ALLOWED); // always let the player open multiplayer menu - flags.add(UserFlag.REALMS_ALLOWED); // always let the player open Realms menu - flags.add(UserFlag.FRIENDS_ENABLED); // not sure if we need this, but let it be - // flags.add(UserFlag.CHAT_FRIENDS_ONLY); // not adding this for obvious reasons - - this.addOptionalFlag(UserFlag.ACCEPT_FRIEND_INVITES, flags, properties); // I assume this is user-controller - - if (config.allowTelemetry()) { // weird flex but ok - this.addOptionalFlag(UserFlag.TELEMETRY_ENABLED, flags, properties); - this.addOptionalFlag(UserFlag.OPTIONAL_TELEMETRY_AVAILABLE, flags, properties); - } - - if (config.allowProfanityFilter()) { // never seen anyone actually want this, but sure - this.addOptionalFlag(UserFlag.PROFANITY_FILTER_ENABLED, flags, properties); - } - - return this.properties = new UserProperties(flags.build(), Map.of()); - } - - private void addOptionalFlag(UserFlag flag, ImmutableSet.Builder builder, UserProperties properties) { - if (properties.flag(flag)) { - builder.add(flag); - } - } - - @Override - public boolean isBlockedPlayer(UUID playerID) { - return this.service.isBlockedPlayer(playerID); - } - - @Override - public void refreshBlockList() { - this.service.refreshBlockList(); - } - - @Override - public TelemetrySession newTelemetrySession(Executor executor) { - if (NCRConfig.getInstance().allowTelemetry()) - return this.service.newTelemetrySession(executor); - else - return TelemetrySession.DISABLED; - } - - // Methods below primarily concern chat reporting. Not doing anything with them - // here as that's out of scope for this mod, it's more of a No Chat Reports thing - - @Override - public KeyPairResponse getKeyPair() { - return this.service.getKeyPair(); - } - - @Override - public void reportAbuse(AbuseReportRequest request) { - this.service.reportAbuse(request); - } - - @Override - public boolean canSendReports() { - return this.service.canSendReports(); - } - - @Override - public AbuseReportLimits getAbuseReportLimits() { - return this.service.getAbuseReportLimits(); - } - -} diff --git a/Fabric/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java b/Fabric/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java index c0fa4d0..b5605df 100644 --- a/Fabric/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java +++ b/Fabric/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java @@ -5,26 +5,14 @@ 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.aizistral.nochatrestrictions.core.WrappedUserApiService; -import com.mojang.authlib.minecraft.UserApiService; -import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; - import net.minecraft.client.Minecraft; -import net.minecraft.client.main.GameConfig; @Mixin(Minecraft.class) public class MixinMinecraft { - @Inject(method = "createUserApiService", at = @At("RETURN"), cancellable = true) - private static void onCreateUserApi(YggdrasilAuthenticationService authService, GameConfig gameConfig, - CallbackInfoReturnable info) { - UserApiService returnedService = info.getReturnValue(); - assert returnedService != null; - info.setReturnValue(new WrappedUserApiService(returnedService)); - - NCRCore.LOGGER.info("Successfully supplanted UserApiService with a wrapped version."); - } + // Removal of multiplayer/chat/telemetry restrictions is handled in + // MixinYggdrasilUserApiService, so that it keeps working after in-game + // account switchers (e.g. IAS) replace the UserApiService instance. @Inject(method = "isNameBanned", at = @At("HEAD"), cancellable = true) private void onCheckNameBan(CallbackInfoReturnable info) { diff --git a/Fabric/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinYggdrasilUserApiService.java b/Fabric/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinYggdrasilUserApiService.java new file mode 100644 index 0000000..ff05a1f --- /dev/null +++ b/Fabric/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinYggdrasilUserApiService.java @@ -0,0 +1,76 @@ +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.Unique; +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.config.NCRConfig; +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 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 { + + @Inject(method = "fetchProperties", at = @At("RETURN"), cancellable = true) + private void onFetchProperties(CallbackInfoReturnable info) { + UserProperties original = info.getReturnValue(); + + if (original == null) + return; + + NCRConfig config = NCRConfig.getInstance(); + ImmutableSet.Builder flags = ImmutableSet.builder(); + + flags.add(UserFlag.CHAT_ALLOWED); // always let the player access chat + flags.add(UserFlag.SERVERS_ALLOWED); // always let the player open multiplayer menu + flags.add(UserFlag.REALMS_ALLOWED); // always let the player open Realms menu + flags.add(UserFlag.FRIENDS_ENABLED); // not sure if we need this, but let it be + // flags.add(UserFlag.CHAT_FRIENDS_ONLY); // not adding this for obvious reasons + + this.addOptionalFlag(UserFlag.ACCEPT_FRIEND_INVITES, flags, original); // I assume this is user-controlled + + if (config.allowTelemetry()) { // weird flex but ok + this.addOptionalFlag(UserFlag.TELEMETRY_ENABLED, flags, original); + this.addOptionalFlag(UserFlag.OPTIONAL_TELEMETRY_AVAILABLE, flags, original); + } + + if (config.allowProfanityFilter()) { // never seen anyone actually want this, but sure + this.addOptionalFlag(UserFlag.PROFANITY_FILTER_ENABLED, flags, original); + } + + info.setReturnValue(new UserProperties(flags.build(), Map.of())); + } + + @Inject(method = "newTelemetrySession", at = @At("HEAD"), cancellable = true) + private void onNewTelemetrySession(Executor executor, CallbackInfoReturnable info) { + if (!NCRConfig.getInstance().allowTelemetry()) { + info.setReturnValue(TelemetrySession.DISABLED); + } + } + + @Unique + private void addOptionalFlag(UserFlag flag, ImmutableSet.Builder builder, UserProperties properties) { + if (properties.flag(flag)) { + builder.add(flag); + } + } + +} diff --git a/Fabric/src/main/resources/nochatrestrictions.mixins.json b/Fabric/src/main/resources/nochatrestrictions.mixins.json index 4b9d3fd..3f5e294 100644 --- a/Fabric/src/main/resources/nochatrestrictions.mixins.json +++ b/Fabric/src/main/resources/nochatrestrictions.mixins.json @@ -4,7 +4,8 @@ "minVersion": "0.8", "mixins": [], "client": [ - "MixinMinecraft" + "MixinMinecraft", + "MixinYggdrasilUserApiService" ], "injectors": { "defaultRequire": 1 diff --git a/Forge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java b/Forge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java deleted file mode 100644 index 6161462..0000000 --- a/Forge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.aizistral.nochatrestrictions.core; - -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.Executor; - -import org.jetbrains.annotations.Nullable; - -import com.aizistral.nochatrestrictions.config.NCRConfig; -import com.google.common.collect.ImmutableSet; -import com.mojang.authlib.exceptions.AuthenticationException; -import com.mojang.authlib.minecraft.TelemetrySession; -import com.mojang.authlib.minecraft.UserApiService; -import com.mojang.authlib.minecraft.report.AbuseReportLimits; -import com.mojang.authlib.yggdrasil.request.AbuseReportRequest; -import com.mojang.authlib.yggdrasil.response.KeyPairResponse; - -public class WrappedUserApiService implements UserApiService { - private final UserApiService service; - private @Nullable UserProperties properties = null; - - public WrappedUserApiService(UserApiService service) { - this.service = service; - } - - @Override - public UserProperties fetchProperties() throws AuthenticationException { - if (this.properties != null) - return this.properties; - - NCRConfig config = NCRConfig.getInstance(); - UserProperties properties = this.service.fetchProperties(); - ImmutableSet.Builder flags = ImmutableSet.builder(); - - flags.add(UserFlag.CHAT_ALLOWED); // always let the player access chat - flags.add(UserFlag.SERVERS_ALLOWED); // always let the player open multiplayer menu - flags.add(UserFlag.REALMS_ALLOWED); // always let the player open Realms menu - flags.add(UserFlag.FRIENDS_ENABLED); // not sure if we need this, but let it be - // flags.add(UserFlag.CHAT_FRIENDS_ONLY); // not adding this for obvious reasons - - this.addOptionalFlag(UserFlag.ACCEPT_FRIEND_INVITES, flags, properties); // I assume this is user-controller - - if (config.allowTelemetry()) { // weird flex but ok - this.addOptionalFlag(UserFlag.TELEMETRY_ENABLED, flags, properties); - this.addOptionalFlag(UserFlag.OPTIONAL_TELEMETRY_AVAILABLE, flags, properties); - } - - if (config.allowProfanityFilter()) { // never seen anyone actually want this, but sure - this.addOptionalFlag(UserFlag.PROFANITY_FILTER_ENABLED, flags, properties); - } - - return this.properties = new UserProperties(flags.build(), Map.of()); - } - - private void addOptionalFlag(UserFlag flag, ImmutableSet.Builder builder, UserProperties properties) { - if (properties.flag(flag)) { - builder.add(flag); - } - } - - @Override - public boolean isBlockedPlayer(UUID playerID) { - return this.service.isBlockedPlayer(playerID); - } - - @Override - public void refreshBlockList() { - this.service.refreshBlockList(); - } - - @Override - public TelemetrySession newTelemetrySession(Executor executor) { - if (NCRConfig.getInstance().allowTelemetry()) - return this.service.newTelemetrySession(executor); - else - return TelemetrySession.DISABLED; - } - - // Methods below primarily concern chat reporting. Not doing anything with them - // here as that's out of scope for this mod, it's more of a No Chat Reports thing - - @Override - public KeyPairResponse getKeyPair() { - return this.service.getKeyPair(); - } - - @Override - public void reportAbuse(AbuseReportRequest request) { - this.service.reportAbuse(request); - } - - @Override - public boolean canSendReports() { - return this.service.canSendReports(); - } - - @Override - public AbuseReportLimits getAbuseReportLimits() { - return this.service.getAbuseReportLimits(); - } - -} diff --git a/Forge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java b/Forge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java index 4dc6080..8d0f533 100644 --- a/Forge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java +++ b/Forge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java @@ -5,26 +5,14 @@ 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.aizistral.nochatrestrictions.core.WrappedUserApiService; -import com.mojang.authlib.minecraft.UserApiService; -import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; - import net.minecraft.client.Minecraft; -import net.minecraft.client.main.GameConfig; @Mixin(value = Minecraft.class, remap = false) public class MixinMinecraft { - @Inject(method = "createUserApiService", at = @At("RETURN"), cancellable = true) - private static void onCreateUserApi(YggdrasilAuthenticationService authService, GameConfig gameConfig, - CallbackInfoReturnable info) { - UserApiService returnedService = info.getReturnValue(); - assert returnedService != null; - info.setReturnValue(new WrappedUserApiService(returnedService)); - - NCRCore.LOGGER.info("Successfully supplanted UserApiService with a wrapped version."); - } + // Removal of multiplayer/chat/telemetry restrictions is handled in + // MixinYggdrasilUserApiService, so that it keeps working after in-game + // account switchers (e.g. IAS) replace the UserApiService instance. @Inject(method = "isNameBanned", at = @At("HEAD"), cancellable = true) private void onCheckNameBan(CallbackInfoReturnable info) { diff --git a/Forge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinYggdrasilUserApiService.java b/Forge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinYggdrasilUserApiService.java new file mode 100644 index 0000000..ff05a1f --- /dev/null +++ b/Forge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinYggdrasilUserApiService.java @@ -0,0 +1,76 @@ +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.Unique; +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.config.NCRConfig; +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 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 { + + @Inject(method = "fetchProperties", at = @At("RETURN"), cancellable = true) + private void onFetchProperties(CallbackInfoReturnable info) { + UserProperties original = info.getReturnValue(); + + if (original == null) + return; + + NCRConfig config = NCRConfig.getInstance(); + ImmutableSet.Builder flags = ImmutableSet.builder(); + + flags.add(UserFlag.CHAT_ALLOWED); // always let the player access chat + flags.add(UserFlag.SERVERS_ALLOWED); // always let the player open multiplayer menu + flags.add(UserFlag.REALMS_ALLOWED); // always let the player open Realms menu + flags.add(UserFlag.FRIENDS_ENABLED); // not sure if we need this, but let it be + // flags.add(UserFlag.CHAT_FRIENDS_ONLY); // not adding this for obvious reasons + + this.addOptionalFlag(UserFlag.ACCEPT_FRIEND_INVITES, flags, original); // I assume this is user-controlled + + if (config.allowTelemetry()) { // weird flex but ok + this.addOptionalFlag(UserFlag.TELEMETRY_ENABLED, flags, original); + this.addOptionalFlag(UserFlag.OPTIONAL_TELEMETRY_AVAILABLE, flags, original); + } + + if (config.allowProfanityFilter()) { // never seen anyone actually want this, but sure + this.addOptionalFlag(UserFlag.PROFANITY_FILTER_ENABLED, flags, original); + } + + info.setReturnValue(new UserProperties(flags.build(), Map.of())); + } + + @Inject(method = "newTelemetrySession", at = @At("HEAD"), cancellable = true) + private void onNewTelemetrySession(Executor executor, CallbackInfoReturnable info) { + if (!NCRConfig.getInstance().allowTelemetry()) { + info.setReturnValue(TelemetrySession.DISABLED); + } + } + + @Unique + private void addOptionalFlag(UserFlag flag, ImmutableSet.Builder builder, UserProperties properties) { + if (properties.flag(flag)) { + builder.add(flag); + } + } + +} diff --git a/Forge/src/main/resources/nochatrestrictions.mixins.json b/Forge/src/main/resources/nochatrestrictions.mixins.json index c9e96c5..c74587e 100644 --- a/Forge/src/main/resources/nochatrestrictions.mixins.json +++ b/Forge/src/main/resources/nochatrestrictions.mixins.json @@ -5,7 +5,8 @@ "refmap": "nochatrestrictions.refmap.json", "mixins": [], "client": [ - "MixinMinecraft" + "MixinMinecraft", + "MixinYggdrasilUserApiService" ], "injectors": { "defaultRequire": 1 diff --git a/NeoForge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java b/NeoForge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java deleted file mode 100644 index 6161462..0000000 --- a/NeoForge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.aizistral.nochatrestrictions.core; - -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.Executor; - -import org.jetbrains.annotations.Nullable; - -import com.aizistral.nochatrestrictions.config.NCRConfig; -import com.google.common.collect.ImmutableSet; -import com.mojang.authlib.exceptions.AuthenticationException; -import com.mojang.authlib.minecraft.TelemetrySession; -import com.mojang.authlib.minecraft.UserApiService; -import com.mojang.authlib.minecraft.report.AbuseReportLimits; -import com.mojang.authlib.yggdrasil.request.AbuseReportRequest; -import com.mojang.authlib.yggdrasil.response.KeyPairResponse; - -public class WrappedUserApiService implements UserApiService { - private final UserApiService service; - private @Nullable UserProperties properties = null; - - public WrappedUserApiService(UserApiService service) { - this.service = service; - } - - @Override - public UserProperties fetchProperties() throws AuthenticationException { - if (this.properties != null) - return this.properties; - - NCRConfig config = NCRConfig.getInstance(); - UserProperties properties = this.service.fetchProperties(); - ImmutableSet.Builder flags = ImmutableSet.builder(); - - flags.add(UserFlag.CHAT_ALLOWED); // always let the player access chat - flags.add(UserFlag.SERVERS_ALLOWED); // always let the player open multiplayer menu - flags.add(UserFlag.REALMS_ALLOWED); // always let the player open Realms menu - flags.add(UserFlag.FRIENDS_ENABLED); // not sure if we need this, but let it be - // flags.add(UserFlag.CHAT_FRIENDS_ONLY); // not adding this for obvious reasons - - this.addOptionalFlag(UserFlag.ACCEPT_FRIEND_INVITES, flags, properties); // I assume this is user-controller - - if (config.allowTelemetry()) { // weird flex but ok - this.addOptionalFlag(UserFlag.TELEMETRY_ENABLED, flags, properties); - this.addOptionalFlag(UserFlag.OPTIONAL_TELEMETRY_AVAILABLE, flags, properties); - } - - if (config.allowProfanityFilter()) { // never seen anyone actually want this, but sure - this.addOptionalFlag(UserFlag.PROFANITY_FILTER_ENABLED, flags, properties); - } - - return this.properties = new UserProperties(flags.build(), Map.of()); - } - - private void addOptionalFlag(UserFlag flag, ImmutableSet.Builder builder, UserProperties properties) { - if (properties.flag(flag)) { - builder.add(flag); - } - } - - @Override - public boolean isBlockedPlayer(UUID playerID) { - return this.service.isBlockedPlayer(playerID); - } - - @Override - public void refreshBlockList() { - this.service.refreshBlockList(); - } - - @Override - public TelemetrySession newTelemetrySession(Executor executor) { - if (NCRConfig.getInstance().allowTelemetry()) - return this.service.newTelemetrySession(executor); - else - return TelemetrySession.DISABLED; - } - - // Methods below primarily concern chat reporting. Not doing anything with them - // here as that's out of scope for this mod, it's more of a No Chat Reports thing - - @Override - public KeyPairResponse getKeyPair() { - return this.service.getKeyPair(); - } - - @Override - public void reportAbuse(AbuseReportRequest request) { - this.service.reportAbuse(request); - } - - @Override - public boolean canSendReports() { - return this.service.canSendReports(); - } - - @Override - public AbuseReportLimits getAbuseReportLimits() { - return this.service.getAbuseReportLimits(); - } - -} diff --git a/NeoForge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java b/NeoForge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java index c0fa4d0..b5605df 100644 --- a/NeoForge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java +++ b/NeoForge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinMinecraft.java @@ -5,26 +5,14 @@ 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.aizistral.nochatrestrictions.core.WrappedUserApiService; -import com.mojang.authlib.minecraft.UserApiService; -import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; - import net.minecraft.client.Minecraft; -import net.minecraft.client.main.GameConfig; @Mixin(Minecraft.class) public class MixinMinecraft { - @Inject(method = "createUserApiService", at = @At("RETURN"), cancellable = true) - private static void onCreateUserApi(YggdrasilAuthenticationService authService, GameConfig gameConfig, - CallbackInfoReturnable info) { - UserApiService returnedService = info.getReturnValue(); - assert returnedService != null; - info.setReturnValue(new WrappedUserApiService(returnedService)); - - NCRCore.LOGGER.info("Successfully supplanted UserApiService with a wrapped version."); - } + // Removal of multiplayer/chat/telemetry restrictions is handled in + // MixinYggdrasilUserApiService, so that it keeps working after in-game + // account switchers (e.g. IAS) replace the UserApiService instance. @Inject(method = "isNameBanned", at = @At("HEAD"), cancellable = true) private void onCheckNameBan(CallbackInfoReturnable info) { diff --git a/NeoForge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinYggdrasilUserApiService.java b/NeoForge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinYggdrasilUserApiService.java new file mode 100644 index 0000000..ff05a1f --- /dev/null +++ b/NeoForge/src/main/java/com/aizistral/nochatrestrictions/mixins/MixinYggdrasilUserApiService.java @@ -0,0 +1,76 @@ +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.Unique; +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.config.NCRConfig; +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 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 { + + @Inject(method = "fetchProperties", at = @At("RETURN"), cancellable = true) + private void onFetchProperties(CallbackInfoReturnable info) { + UserProperties original = info.getReturnValue(); + + if (original == null) + return; + + NCRConfig config = NCRConfig.getInstance(); + ImmutableSet.Builder flags = ImmutableSet.builder(); + + flags.add(UserFlag.CHAT_ALLOWED); // always let the player access chat + flags.add(UserFlag.SERVERS_ALLOWED); // always let the player open multiplayer menu + flags.add(UserFlag.REALMS_ALLOWED); // always let the player open Realms menu + flags.add(UserFlag.FRIENDS_ENABLED); // not sure if we need this, but let it be + // flags.add(UserFlag.CHAT_FRIENDS_ONLY); // not adding this for obvious reasons + + this.addOptionalFlag(UserFlag.ACCEPT_FRIEND_INVITES, flags, original); // I assume this is user-controlled + + if (config.allowTelemetry()) { // weird flex but ok + this.addOptionalFlag(UserFlag.TELEMETRY_ENABLED, flags, original); + this.addOptionalFlag(UserFlag.OPTIONAL_TELEMETRY_AVAILABLE, flags, original); + } + + if (config.allowProfanityFilter()) { // never seen anyone actually want this, but sure + this.addOptionalFlag(UserFlag.PROFANITY_FILTER_ENABLED, flags, original); + } + + info.setReturnValue(new UserProperties(flags.build(), Map.of())); + } + + @Inject(method = "newTelemetrySession", at = @At("HEAD"), cancellable = true) + private void onNewTelemetrySession(Executor executor, CallbackInfoReturnable info) { + if (!NCRConfig.getInstance().allowTelemetry()) { + info.setReturnValue(TelemetrySession.DISABLED); + } + } + + @Unique + private void addOptionalFlag(UserFlag flag, ImmutableSet.Builder builder, UserProperties properties) { + if (properties.flag(flag)) { + builder.add(flag); + } + } + +} diff --git a/NeoForge/src/main/resources/nochatrestrictions.mixins.json b/NeoForge/src/main/resources/nochatrestrictions.mixins.json index c9e96c5..c74587e 100644 --- a/NeoForge/src/main/resources/nochatrestrictions.mixins.json +++ b/NeoForge/src/main/resources/nochatrestrictions.mixins.json @@ -5,7 +5,8 @@ "refmap": "nochatrestrictions.refmap.json", "mixins": [], "client": [ - "MixinMinecraft" + "MixinMinecraft", + "MixinYggdrasilUserApiService" ], "injectors": { "defaultRequire": 1