From fbba392feded22563c91c16919c4212d0e94736e Mon Sep 17 00:00:00 2001 From: Aizistral Date: Tue, 23 Jun 2026 19:36:23 +0200 Subject: [PATCH] Add config for telemetry and profanity filter Closes #20 --- .../nochatrestrictions/config/NCRConfig.java | 79 +++++++++++++++++++ .../core/WrappedUserApiService.java | 59 +++++++++----- .../nochatrestrictions/config/NCRConfig.java | 79 +++++++++++++++++++ .../core/WrappedUserApiService.java | 59 +++++++++----- .../nochatrestrictions/config/NCRConfig.java | 79 +++++++++++++++++++ .../core/WrappedUserApiService.java | 59 +++++++++----- 6 files changed, 360 insertions(+), 54 deletions(-) create mode 100644 Fabric/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java create mode 100644 Forge/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java create mode 100644 NeoForge/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java diff --git a/Fabric/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java b/Fabric/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java new file mode 100644 index 0000000..ee04b0b --- /dev/null +++ b/Fabric/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java @@ -0,0 +1,79 @@ +package com.aizistral.nochatrestrictions.config; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import com.aizistral.nochatrestrictions.core.NCRCore; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import net.minecraft.client.Minecraft; + +public record NCRConfig(boolean allowTelemetry, boolean allowProfanityFilter) { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static @Nullable NCRConfig instance = null; + + private static NCRConfig getDefault() { + return new NCRConfig(false, false); + } + + public static NCRConfig getInstance() { + if (instance == null) { + instance = loadConfig("NoChatRestrictions.json"); + } + + return instance; + } + + private static NCRConfig loadConfig(String fileName) { + NCRCore.LOGGER.debug("Reading config file {}...", fileName); + NCRConfig config = readFile(fileName).orElseGet(NCRConfig::getDefault); + + NCRCore.LOGGER.info("Telemetry Allowed: {}", config.allowTelemetry()); + NCRCore.LOGGER.info("Profanity Filter Allowed: {}", config.allowProfanityFilter()); + + NCRCore.LOGGER.debug("Writing config file {} back to disk..."); + writeFile(fileName, config); + + return config; + } + + private static Optional readFile(String fileName) { + Path file = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(fileName); + + if (!Files.isRegularFile(file)) + return Optional.empty(); + + try (BufferedReader reader = Files.newBufferedReader(file)) { + return Optional.of(GSON.fromJson(reader, NCRConfig.class)); + } catch (Exception ex) { + NCRCore.LOGGER.fatal("Could not read config file: {}", file); + NCRCore.LOGGER.fatal("This likely indicates the file is corrupted. " + + "You can try deleting it to fix this problem. Full stacktrace below:"); + NCRCore.LOGGER.catching(ex); + throw new RuntimeException("Could not read config file: " + file, ex); + } + } + + private static void writeFile(String fileName, NCRConfig config) { + Path file = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(fileName); + + try { + Files.createDirectories(file.getParent()); + try (BufferedWriter writer = Files.newBufferedWriter(file)) { + GSON.toJson(config, writer); + } + } catch (Exception ex) { + NCRCore.LOGGER.fatal("Could not write config file: {}", file); + NCRCore.LOGGER.fatal("Full stacktrace below:"); + NCRCore.LOGGER.catching(ex); + throw new RuntimeException("Could not write config file: " + file,ex); + } + } + +} diff --git a/Fabric/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java b/Fabric/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java index e3e367d..6161462 100644 --- a/Fabric/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java +++ b/Fabric/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java @@ -4,7 +4,11 @@ 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; @@ -12,30 +16,46 @@ import com.mojang.authlib.yggdrasil.request.AbuseReportRequest; import com.mojang.authlib.yggdrasil.response.KeyPairResponse; public class WrappedUserApiService implements UserApiService { - private static final UserProperties FORCED_PROPERTIES; - - static { - 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.TELEMETRY_ENABLED); // not adding this for obvious reasons - // flags.add(UserFlag.OPTIONAL_TELEMETRY_AVAILABLE); // thanks but no thanks - // flags.add(UserFlag.PROFANITY_FILTER_ENABLED) // not adding this one either - - FORCED_PROPERTIES = new UserProperties(flags.build(), Map.of()); - } - private final UserApiService service; + private @Nullable UserProperties properties = null; public WrappedUserApiService(UserApiService service) { this.service = service; } @Override - public UserProperties fetchProperties() { - return FORCED_PROPERTIES; + 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 @@ -50,7 +70,10 @@ public class WrappedUserApiService implements UserApiService { @Override public TelemetrySession newTelemetrySession(Executor executor) { - return TelemetrySession.DISABLED; + if (NCRConfig.getInstance().allowTelemetry()) + return this.service.newTelemetrySession(executor); + else + return TelemetrySession.DISABLED; } // Methods below primarily concern chat reporting. Not doing anything with them diff --git a/Forge/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java b/Forge/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java new file mode 100644 index 0000000..ee04b0b --- /dev/null +++ b/Forge/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java @@ -0,0 +1,79 @@ +package com.aizistral.nochatrestrictions.config; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import com.aizistral.nochatrestrictions.core.NCRCore; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import net.minecraft.client.Minecraft; + +public record NCRConfig(boolean allowTelemetry, boolean allowProfanityFilter) { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static @Nullable NCRConfig instance = null; + + private static NCRConfig getDefault() { + return new NCRConfig(false, false); + } + + public static NCRConfig getInstance() { + if (instance == null) { + instance = loadConfig("NoChatRestrictions.json"); + } + + return instance; + } + + private static NCRConfig loadConfig(String fileName) { + NCRCore.LOGGER.debug("Reading config file {}...", fileName); + NCRConfig config = readFile(fileName).orElseGet(NCRConfig::getDefault); + + NCRCore.LOGGER.info("Telemetry Allowed: {}", config.allowTelemetry()); + NCRCore.LOGGER.info("Profanity Filter Allowed: {}", config.allowProfanityFilter()); + + NCRCore.LOGGER.debug("Writing config file {} back to disk..."); + writeFile(fileName, config); + + return config; + } + + private static Optional readFile(String fileName) { + Path file = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(fileName); + + if (!Files.isRegularFile(file)) + return Optional.empty(); + + try (BufferedReader reader = Files.newBufferedReader(file)) { + return Optional.of(GSON.fromJson(reader, NCRConfig.class)); + } catch (Exception ex) { + NCRCore.LOGGER.fatal("Could not read config file: {}", file); + NCRCore.LOGGER.fatal("This likely indicates the file is corrupted. " + + "You can try deleting it to fix this problem. Full stacktrace below:"); + NCRCore.LOGGER.catching(ex); + throw new RuntimeException("Could not read config file: " + file, ex); + } + } + + private static void writeFile(String fileName, NCRConfig config) { + Path file = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(fileName); + + try { + Files.createDirectories(file.getParent()); + try (BufferedWriter writer = Files.newBufferedWriter(file)) { + GSON.toJson(config, writer); + } + } catch (Exception ex) { + NCRCore.LOGGER.fatal("Could not write config file: {}", file); + NCRCore.LOGGER.fatal("Full stacktrace below:"); + NCRCore.LOGGER.catching(ex); + throw new RuntimeException("Could not write config file: " + file,ex); + } + } + +} diff --git a/Forge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java b/Forge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java index e3e367d..6161462 100644 --- a/Forge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java +++ b/Forge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java @@ -4,7 +4,11 @@ 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; @@ -12,30 +16,46 @@ import com.mojang.authlib.yggdrasil.request.AbuseReportRequest; import com.mojang.authlib.yggdrasil.response.KeyPairResponse; public class WrappedUserApiService implements UserApiService { - private static final UserProperties FORCED_PROPERTIES; - - static { - 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.TELEMETRY_ENABLED); // not adding this for obvious reasons - // flags.add(UserFlag.OPTIONAL_TELEMETRY_AVAILABLE); // thanks but no thanks - // flags.add(UserFlag.PROFANITY_FILTER_ENABLED) // not adding this one either - - FORCED_PROPERTIES = new UserProperties(flags.build(), Map.of()); - } - private final UserApiService service; + private @Nullable UserProperties properties = null; public WrappedUserApiService(UserApiService service) { this.service = service; } @Override - public UserProperties fetchProperties() { - return FORCED_PROPERTIES; + 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 @@ -50,7 +70,10 @@ public class WrappedUserApiService implements UserApiService { @Override public TelemetrySession newTelemetrySession(Executor executor) { - return TelemetrySession.DISABLED; + if (NCRConfig.getInstance().allowTelemetry()) + return this.service.newTelemetrySession(executor); + else + return TelemetrySession.DISABLED; } // Methods below primarily concern chat reporting. Not doing anything with them diff --git a/NeoForge/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java b/NeoForge/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java new file mode 100644 index 0000000..ee04b0b --- /dev/null +++ b/NeoForge/src/main/java/com/aizistral/nochatrestrictions/config/NCRConfig.java @@ -0,0 +1,79 @@ +package com.aizistral.nochatrestrictions.config; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import com.aizistral.nochatrestrictions.core.NCRCore; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import net.minecraft.client.Minecraft; + +public record NCRConfig(boolean allowTelemetry, boolean allowProfanityFilter) { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static @Nullable NCRConfig instance = null; + + private static NCRConfig getDefault() { + return new NCRConfig(false, false); + } + + public static NCRConfig getInstance() { + if (instance == null) { + instance = loadConfig("NoChatRestrictions.json"); + } + + return instance; + } + + private static NCRConfig loadConfig(String fileName) { + NCRCore.LOGGER.debug("Reading config file {}...", fileName); + NCRConfig config = readFile(fileName).orElseGet(NCRConfig::getDefault); + + NCRCore.LOGGER.info("Telemetry Allowed: {}", config.allowTelemetry()); + NCRCore.LOGGER.info("Profanity Filter Allowed: {}", config.allowProfanityFilter()); + + NCRCore.LOGGER.debug("Writing config file {} back to disk..."); + writeFile(fileName, config); + + return config; + } + + private static Optional readFile(String fileName) { + Path file = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(fileName); + + if (!Files.isRegularFile(file)) + return Optional.empty(); + + try (BufferedReader reader = Files.newBufferedReader(file)) { + return Optional.of(GSON.fromJson(reader, NCRConfig.class)); + } catch (Exception ex) { + NCRCore.LOGGER.fatal("Could not read config file: {}", file); + NCRCore.LOGGER.fatal("This likely indicates the file is corrupted. " + + "You can try deleting it to fix this problem. Full stacktrace below:"); + NCRCore.LOGGER.catching(ex); + throw new RuntimeException("Could not read config file: " + file, ex); + } + } + + private static void writeFile(String fileName, NCRConfig config) { + Path file = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(fileName); + + try { + Files.createDirectories(file.getParent()); + try (BufferedWriter writer = Files.newBufferedWriter(file)) { + GSON.toJson(config, writer); + } + } catch (Exception ex) { + NCRCore.LOGGER.fatal("Could not write config file: {}", file); + NCRCore.LOGGER.fatal("Full stacktrace below:"); + NCRCore.LOGGER.catching(ex); + throw new RuntimeException("Could not write config file: " + file,ex); + } + } + +} diff --git a/NeoForge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java b/NeoForge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java index e3e367d..6161462 100644 --- a/NeoForge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java +++ b/NeoForge/src/main/java/com/aizistral/nochatrestrictions/core/WrappedUserApiService.java @@ -4,7 +4,11 @@ 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; @@ -12,30 +16,46 @@ import com.mojang.authlib.yggdrasil.request.AbuseReportRequest; import com.mojang.authlib.yggdrasil.response.KeyPairResponse; public class WrappedUserApiService implements UserApiService { - private static final UserProperties FORCED_PROPERTIES; - - static { - 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.TELEMETRY_ENABLED); // not adding this for obvious reasons - // flags.add(UserFlag.OPTIONAL_TELEMETRY_AVAILABLE); // thanks but no thanks - // flags.add(UserFlag.PROFANITY_FILTER_ENABLED) // not adding this one either - - FORCED_PROPERTIES = new UserProperties(flags.build(), Map.of()); - } - private final UserApiService service; + private @Nullable UserProperties properties = null; public WrappedUserApiService(UserApiService service) { this.service = service; } @Override - public UserProperties fetchProperties() { - return FORCED_PROPERTIES; + 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 @@ -50,7 +70,10 @@ public class WrappedUserApiService implements UserApiService { @Override public TelemetrySession newTelemetrySession(Executor executor) { - return TelemetrySession.DISABLED; + if (NCRConfig.getInstance().allowTelemetry()) + return this.service.newTelemetrySession(executor); + else + return TelemetrySession.DISABLED; } // Methods below primarily concern chat reporting. Not doing anything with them