From b1d0ed553374d91ecebf538b51fa1b439c827120 Mon Sep 17 00:00:00 2001 From: maximumpower55 Date: Tue, 21 May 2024 15:50:10 -0700 Subject: [PATCH] config improvements --- .../one/devos/nautical/teabridge/Config.java | 87 ++++++++++++------- .../devos/nautical/teabridge/TeaBridge.java | 68 ++++++++++----- .../nautical/teabridge/discord/Discord.java | 22 +++-- .../nautical/teabridge/discord/PKCompat.java | 9 +- .../teabridge/discord/ProtoWebHook.java | 8 +- .../teabridge/duck/PlayerWebHook.java | 4 +- .../teabridge/mixin/CrashReportMixin.java | 4 +- .../mixin/PlayerAdvancementsMixin.java | 4 +- .../teabridge/mixin/PlayerListMixin.java | 4 +- .../ServerGamePacketListenerImplMixin.java | 4 +- .../teabridge/mixin/ServerPlayerMixin.java | 12 +-- .../nautical/teabridge/util/CrashHandler.java | 25 +++--- .../nautical/teabridge/util/LRUHashMap.java | 17 ---- .../nautical/teabridge/util/MoreCodecs.java | 33 +++++++ .../teabridge/util/StyledChatCompat.java | 4 +- 15 files changed, 183 insertions(+), 122 deletions(-) delete mode 100644 src/main/java/one/devos/nautical/teabridge/util/LRUHashMap.java create mode 100644 src/main/java/one/devos/nautical/teabridge/util/MoreCodecs.java diff --git a/src/main/java/one/devos/nautical/teabridge/Config.java b/src/main/java/one/devos/nautical/teabridge/Config.java index 8240640..99085c1 100644 --- a/src/main/java/one/devos/nautical/teabridge/Config.java +++ b/src/main/java/one/devos/nautical/teabridge/Config.java @@ -1,15 +1,24 @@ package one.devos.nautical.teabridge; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.net.URI; +import java.net.URLDecoder; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.function.Function; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonParser; import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; +import one.devos.nautical.teabridge.util.MoreCodecs; public record Config( Discord discord, @@ -21,45 +30,61 @@ public record Config( private static final Gson GSON = new GsonBuilder().setPrettyPrinting().setLenient().disableHtmlEscaping().create(); public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Discord.CODEC.optionalFieldOf("discord", Discord.DEFAULT).forGetter(Config::discord), - Avatars.CODEC.optionalFieldOf("avatars", Avatars.DEFAULT).forGetter(Config::avatars), - Game.CODEC.optionalFieldOf("game", Game.DEFAULT).forGetter(Config::game), - Crashes.CODEC.optionalFieldOf("crashes", Crashes.DEFAULT).forGetter(Config::crashes), + Discord.CODEC.fieldOf("discord").forGetter(Config::discord), + Avatars.CODEC.fieldOf("avatars").forGetter(Config::avatars), + Game.CODEC.fieldOf("game").forGetter(Config::game), + Crashes.CODEC.fieldOf("crashes").forGetter(Config::crashes), Codec.BOOL.optionalFieldOf("debug", false).forGetter(Config::debug) ).apply(instance, Config::new)); - public static Config INSTANCE = new Config(Discord.DEFAULT, Avatars.DEFAULT, Game.DEFAULT, Crashes.DEFAULT, false); - - public static void load() throws Exception { - Path configPath = PlatformUtil.getConfigDir().resolve("teabridge.json"); - if (Files.exists(configPath)) - INSTANCE = CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(Files.newBufferedReader(configPath))).getOrThrow(); - Files.writeString(configPath, GSON.toJson(CODEC.encodeStart(JsonOps.INSTANCE, INSTANCE).getOrThrow()), StandardCharsets.UTF_8); + public static final Config DEFAULT = new Config(Discord.DEFAULT, Avatars.DEFAULT, Game.DEFAULT, Crashes.DEFAULT, false); + + public static DataResult load(Path path) { + try { + if (Files.exists(path)) { + try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + return CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)); + } + } else { + try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8, StandardOpenOption.CREATE)) { + GSON.toJson(CODEC.encodeStart(JsonOps.INSTANCE, DEFAULT).getOrThrow(), writer); + return DataResult.success(DEFAULT); + } + } + } catch (Exception e) { + return DataResult.error(e::getMessage); + } } - public record Discord(String token, String webhook, int pkMessageDelay, boolean pkMessageDelayMilliseconds) { + public record Discord(String token, URI webhook, int pkMessageDelay, boolean pkMessageDelayMilliseconds) { public static final String DEFAULT_TOKEN = ""; - public static final String DEFAULT_WEBHOOK = ""; + public static final URI DEFAULT_WEBHOOK = URI.create(""); public static final int PK_MESSAGE_DELAY = 0; public static final boolean PK_MESSAGE_DELAY_MILLISECONDS = true; public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.STRING.optionalFieldOf("token", DEFAULT_TOKEN).forGetter(Discord::token), - Codec.STRING.optionalFieldOf("webhook", DEFAULT_WEBHOOK).forGetter(Discord::webhook), - Codec.INT.optionalFieldOf("pkMessageDelay", PK_MESSAGE_DELAY).forGetter(Discord::pkMessageDelay), - Codec.BOOL.optionalFieldOf("pkMessageDelayMilliseconds", PK_MESSAGE_DELAY_MILLISECONDS).forGetter(Discord::pkMessageDelayMilliseconds) + Codec.STRING.fieldOf("token").forGetter(Discord::token), + MoreCodecs.URI.fieldOf("webhook").forGetter(Discord::webhook), + Codec.INT.fieldOf("pkMessageDelay").forGetter(Discord::pkMessageDelay), + Codec.BOOL.fieldOf("pkMessageDelayMilliseconds").forGetter(Discord::pkMessageDelayMilliseconds) ).apply(instance, Discord::new)); public static final Discord DEFAULT = new Discord(DEFAULT_TOKEN, DEFAULT_WEBHOOK, PK_MESSAGE_DELAY, PK_MESSAGE_DELAY_MILLISECONDS); } - public record Avatars(String avatarUrl, boolean useTextureId) { - public static final String DEFAULT_AVATAR_URL = "https://api.nucleoid.xyz/skin/face/256/%s"; + public record Avatars(Function avatarUrl, boolean useTextureId) { + public static final Function DEFAULT_AVATAR_URL = a -> URI.create("https://api.nucleoid.xyz/skin/face/256/%s".formatted(a)); public static final boolean DEFAULT_USE_TEXTURE_ID = false; public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.STRING.optionalFieldOf("avatarUrl", DEFAULT_AVATAR_URL).forGetter(Avatars::avatarUrl), - Codec.BOOL.optionalFieldOf("useTextureId", DEFAULT_USE_TEXTURE_ID).forGetter(Avatars::useTextureId) + Codec.STRING + .comapFlatMap( + MoreCodecs.checkedMapper(a -> (Function) b -> URI.create(a.formatted(b))), + b -> URLDecoder.decode(b.apply(URLEncoder.encode("%s", StandardCharsets.UTF_8)).toString(), StandardCharsets.UTF_8) + ) + .fieldOf("avatarUrl") + .forGetter(Avatars::avatarUrl), + Codec.BOOL.fieldOf("useTextureId").forGetter(Avatars::useTextureId) ).apply(instance, Avatars::new)); public static final Avatars DEFAULT = new Avatars(DEFAULT_AVATAR_URL, DEFAULT_USE_TEXTURE_ID); @@ -87,15 +112,15 @@ public record Game( public static final boolean DEFAULT_MIRROR_COMMAND_MESSAGES = true; public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.STRING.optionalFieldOf("serverStartingMessage", DEFAULT_SERVER_STARTING_MESSAGE).forGetter(Game::serverStartingMessage), - Codec.STRING.optionalFieldOf("serverStartMessage", DEFAULT_SERVER_START_MESSAGE).forGetter(Game::serverStartMessage), - Codec.STRING.optionalFieldOf("serverStopMessage", DEFAULT_SERVER_STOP_MESSAGE).forGetter(Game::serverStopMessage), - Codec.STRING.optionalFieldOf("serverCrashMessage", DEFAULT_SERVER_CRASH_MESSAGE).forGetter(Game::serverCrashMessage), - Codec.BOOL.optionalFieldOf("mirrorJoin", true).forGetter(Game::mirrorJoin), - Codec.BOOL.optionalFieldOf("mirrorLeave", true).forGetter(Game::mirrorLeave), - Codec.BOOL.optionalFieldOf("mirrorDeath", true).forGetter(Game::mirrorDeath), - Codec.BOOL.optionalFieldOf("mirrorAdvancements", true).forGetter(Game::mirrorAdvancements), - Codec.BOOL.optionalFieldOf("mirrorCommandMessages", true).forGetter(Game::mirrorCommandMessages) + Codec.STRING.fieldOf("serverStartingMessage").forGetter(Game::serverStartingMessage), + Codec.STRING.fieldOf("serverStartMessage").forGetter(Game::serverStartMessage), + Codec.STRING.fieldOf("serverStopMessage").forGetter(Game::serverStopMessage), + Codec.STRING.fieldOf("serverCrashMessage").forGetter(Game::serverCrashMessage), + Codec.BOOL.fieldOf("mirrorJoin").forGetter(Game::mirrorJoin), + Codec.BOOL.fieldOf("mirrorLeave").forGetter(Game::mirrorLeave), + Codec.BOOL.fieldOf("mirrorDeath").forGetter(Game::mirrorDeath), + Codec.BOOL.fieldOf("mirrorAdvancements").forGetter(Game::mirrorAdvancements), + Codec.BOOL.fieldOf("mirrorCommandMessages").forGetter(Game::mirrorCommandMessages) ).apply(instance, Game::new)); public static final Game DEFAULT = new Game( @@ -115,7 +140,7 @@ public record Crashes(boolean uploadToMclogs) { public static final boolean DEFAULT_UPLOAD_TO_MCLOGS = true; public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.BOOL.optionalFieldOf("uploadToMclogs", DEFAULT_UPLOAD_TO_MCLOGS).forGetter(Crashes::uploadToMclogs) + Codec.BOOL.fieldOf("uploadToMclogs").forGetter(Crashes::uploadToMclogs) ).apply(instance, Crashes::new)); public static final Crashes DEFAULT = new Crashes(DEFAULT_UPLOAD_TO_MCLOGS); diff --git a/src/main/java/one/devos/nautical/teabridge/TeaBridge.java b/src/main/java/one/devos/nautical/teabridge/TeaBridge.java index aa564b0..5d074fb 100644 --- a/src/main/java/one/devos/nautical/teabridge/TeaBridge.java +++ b/src/main/java/one/devos/nautical/teabridge/TeaBridge.java @@ -1,7 +1,10 @@ package one.devos.nautical.teabridge; import java.net.http.HttpClient; +import java.nio.file.Path; +import com.mojang.serialization.DataResult; +import net.fabricmc.loader.api.FabricLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,31 +28,34 @@ public class TeaBridge { public static final Logger LOGGER = LoggerFactory.getLogger("TeaBridge"); - public static final HttpClient CLIENT = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build(); + public static final HttpClient CLIENT = HttpClient.newBuilder().build(); + + public static final String MOD_ID = "teabridge"; + public static final Path CONFIG_PATH = FabricLoader.getInstance().getConfigDir().resolve(MOD_ID + ".json"); + + public static Config config = Config.DEFAULT; public static void initialize() { - try { - Config.load(); - } catch (Exception e) { - LOGGER.warn("Failed to load config using defaults : ", e); - } + Config.load(CONFIG_PATH) + .ifError(e -> LOGGER.error("Failed to load config using defaults : {}", e)) + .ifSuccess(loaded -> config = loaded); Discord.start(); PlatformUtil.registerCommand(TeaBridge::registerCommands); } public static void onServerStarting(MinecraftServer server) { - if (Config.INSTANCE.debug()) TeaBridge.LOGGER.warn("DEBUG MODE IS ENABLED, THIS WILL LOG EVERYTHING WILL CAUSE LAG SPIKES!!!!!!"); - Discord.send(Config.INSTANCE.game().serverStartingMessage()); + if (TeaBridge.config.debug()) TeaBridge.LOGGER.warn("DEBUG MODE IS ENABLED, THIS WILL LOG EVERYTHING WILL CAUSE LAG SPIKES!!!!!!"); + Discord.send(TeaBridge.config.game().serverStartingMessage()); } public static void onServerStart(MinecraftServer server) { ChannelListener.INSTANCE.setServer(server); - Discord.send(Config.INSTANCE.game().serverStartMessage()); + Discord.send(TeaBridge.config.game().serverStartMessage()); } public static void onServerStop(MinecraftServer server) { - if (!CrashHandler.CRASH_VALUE.get()) Discord.send(Config.INSTANCE.game().serverStopMessage()); + if (!CrashHandler.CRASH_VALUE.get()) Discord.send(TeaBridge.config.game().serverStopMessage()); Discord.stop(); } @@ -62,23 +68,37 @@ public static void onChatMessage(PlayerChatMessage message, ServerPlayer sender, } public static void onCommandMessage(PlayerChatMessage message, CommandSourceStack source, ChatType.Bound params) { - if (!Config.INSTANCE.game().mirrorCommandMessages()) return; + if (!TeaBridge.config.game().mirrorCommandMessages()) return; if (!source.isPlayer()) Discord.send(message.signedContent()); } private static void registerCommands(CommandDispatcher dispatcher) { - dispatcher.register(Commands.literal("teabridge").then( - Commands.literal("reloadConfig").executes(command -> { - try { - Config.load(); - command.getSource().sendSuccess(() -> Component.literal("Config reloaded!").withStyle(ChatFormatting.GREEN), false); - } catch (Exception e) { - command.getSource().sendFailure(Component.literal("Failed to reload config!").withStyle(ChatFormatting.RED)); - LOGGER.warn("Failed to reload config : ", e); - } - - return Command.SINGLE_SUCCESS; - }) - )); + dispatcher.register(Commands.literal("teabridge") + .requires(source -> source.hasPermission(2)) + .then( + Commands.literal("reloadConfig") + .executes(command -> { + CommandSourceStack source = command.getSource(); + DataResult loadResult = Config.load(CONFIG_PATH); + loadResult + .ifError(e -> { + source.sendFailure( + Component.literal("Failed to reload config! check log for details") + .withStyle(ChatFormatting.RED) + ); + LOGGER.warn("Failed to reload config : {}", e); + }) + .ifSuccess(loaded -> { + config = loaded; + source.sendSuccess( + () -> Component.literal("Config reloaded!") + .withStyle(ChatFormatting.GREEN), + false + ); + }); + return loadResult.isSuccess() ? Command.SINGLE_SUCCESS : 0; + }) + ) + ); } } diff --git a/src/main/java/one/devos/nautical/teabridge/discord/Discord.java b/src/main/java/one/devos/nautical/teabridge/discord/Discord.java index 600f8bc..a355f4a 100644 --- a/src/main/java/one/devos/nautical/teabridge/discord/Discord.java +++ b/src/main/java/one/devos/nautical/teabridge/discord/Discord.java @@ -19,7 +19,7 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.requests.GatewayIntent; import one.devos.nautical.teabridge.TeaBridge; -import one.devos.nautical.teabridge.Config; +import one.devos.nautical.teabridge.util.MoreCodecs; import org.jetbrains.annotations.Nullable; public class Discord { @@ -28,34 +28,33 @@ public class Discord { public static final ProtoWebHook WEB_HOOK = new ProtoWebHook( () -> selfMember.get().getEffectiveName(), - () -> selfMember.get().getEffectiveAvatarUrl() + () -> URI.create(selfMember.get().getEffectiveAvatarUrl()) ); private static final LinkedBlockingQueue scheduledMessages = new LinkedBlockingQueue<>(); public static void start() { - if (Config.INSTANCE.discord().token().isEmpty()) { + if (TeaBridge.config.discord().token().isEmpty()) { TeaBridge.LOGGER.error("Unable to load, no Discord token is specified!"); return; } - if (Config.INSTANCE.discord().webhook().isEmpty()) { + if (TeaBridge.config.discord().webhook().toString().isEmpty()) { TeaBridge.LOGGER.error("Unable to load, no Discord webhook is specified!"); return; } try { // Get required data from webhook - HttpResponse response = TeaBridge.CLIENT.send(HttpRequest.newBuilder() - .uri(URI.create(Config.INSTANCE.discord().webhook())) + HttpResponse response = TeaBridge.CLIENT.send(HttpRequest.newBuilder(TeaBridge.config.discord().webhook()) .GET() .build(), HttpResponse.BodyHandlers.ofString()); if (response.statusCode() / 100 != 2) throw new Exception("Non-success status code from request " + response); WebHookData webHookData = WebHookData.fromJson(JsonParser.parseString(response.body())).getOrThrow(); - if (Config.INSTANCE.debug()) TeaBridge.LOGGER.warn("Webhook response : " + response.body()); + if (TeaBridge.config.debug()) TeaBridge.LOGGER.warn("Webhook response : " + response.body()); ChannelListener.INSTANCE.setChannel(webHookData.channelId); - jda = JDABuilder.createDefault(Config.INSTANCE.discord().token()) + jda = JDABuilder.createDefault(TeaBridge.config.discord().token()) .enableIntents(GatewayIntent.MESSAGE_CONTENT) .addEventListeners(ChannelListener.INSTANCE, CommandUtils.INSTANCE) .build(); @@ -100,8 +99,7 @@ private static void scheduledSend(ScheduledMessage scheduledMessage) { String displayName = scheduledMessage.displayName; if (jda != null) { try { - HttpResponse response = TeaBridge.CLIENT.send(HttpRequest.newBuilder() - .uri(URI.create(Config.INSTANCE.discord().webhook())) + HttpResponse response = TeaBridge.CLIENT.send(HttpRequest.newBuilder(TeaBridge.config.discord().webhook()) .POST(HttpRequest.BodyPublishers.ofString(webHook.createMessage(message, displayName).toJson().getOrThrow())) .header("Content-Type", "application/json; charset=utf-8") .build(), HttpResponse.BodyHandlers.ofString()); @@ -121,8 +119,8 @@ public static void stop() { private record WebHookData(long guildId, long channelId) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.STRING.fieldOf("guild_id").xmap(Long::parseLong, String::valueOf).forGetter(WebHookData::guildId), - Codec.STRING.fieldOf("channel_id").xmap(Long::parseLong, String::valueOf).forGetter(WebHookData::channelId) + MoreCodecs.fromString(Long::parseUnsignedLong).fieldOf("guild_id").forGetter(WebHookData::guildId), + MoreCodecs.fromString(Long::parseUnsignedLong).fieldOf("channel_id").forGetter(WebHookData::channelId) ).apply(instance, WebHookData::new)); public static DataResult fromJson(JsonElement json) { diff --git a/src/main/java/one/devos/nautical/teabridge/discord/PKCompat.java b/src/main/java/one/devos/nautical/teabridge/discord/PKCompat.java index 0e0c71e..7334bcf 100644 --- a/src/main/java/one/devos/nautical/teabridge/discord/PKCompat.java +++ b/src/main/java/one/devos/nautical/teabridge/discord/PKCompat.java @@ -8,7 +8,6 @@ import java.util.function.BiConsumer; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import one.devos.nautical.teabridge.Config; import one.devos.nautical.teabridge.TeaBridge; @@ -18,7 +17,7 @@ class PKCompat { private static final LinkedBlockingQueue scheduledMessages = new LinkedBlockingQueue<>(); static void initIfEnabled() { - if (!(Config.INSTANCE.discord().pkMessageDelay() > 0)) return; + if (!(TeaBridge.config.discord().pkMessageDelay() > 0)) return; var thread = new Thread(() -> { while (true) { while (scheduledMessages.peek() == null) {} @@ -34,12 +33,12 @@ static void initIfEnabled() { } static void await(MessageReceivedEvent event, BiConsumer handler) { - if (Config.INSTANCE.discord().pkMessageDelay() > 0) { + if (TeaBridge.config.discord().pkMessageDelay() > 0) { scheduledMessages.add(new ScheduledMessage( event, handler, - Config.INSTANCE.discord().pkMessageDelayMilliseconds() ? - Instant.now().plusMillis(Config.INSTANCE.discord().pkMessageDelay()) : Instant.now().plusSeconds(Config.INSTANCE.discord().pkMessageDelay()) + TeaBridge.config.discord().pkMessageDelayMilliseconds() ? + Instant.now().plusMillis(TeaBridge.config.discord().pkMessageDelay()) : Instant.now().plusSeconds(TeaBridge.config.discord().pkMessageDelay()) )); return; } diff --git a/src/main/java/one/devos/nautical/teabridge/discord/ProtoWebHook.java b/src/main/java/one/devos/nautical/teabridge/discord/ProtoWebHook.java index 109e627..80b08b1 100644 --- a/src/main/java/one/devos/nautical/teabridge/discord/ProtoWebHook.java +++ b/src/main/java/one/devos/nautical/teabridge/discord/ProtoWebHook.java @@ -1,5 +1,6 @@ package one.devos.nautical.teabridge.discord; +import java.net.URI; import java.util.List; import java.util.function.Supplier; @@ -8,19 +9,20 @@ import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; import one.devos.nautical.teabridge.util.JsonUtils; +import one.devos.nautical.teabridge.util.MoreCodecs; import org.jetbrains.annotations.Nullable; -public record ProtoWebHook(Supplier username, Supplier avatar) { +public record ProtoWebHook(Supplier username, Supplier avatar) { public Message createMessage(String content, @Nullable String displayName) { return new Message(content, AllowedMentions.INSTANCE, displayName != null ? displayName : this.username.get(), this.avatar.get()); } - public record Message(String content, AllowedMentions allowedMentions, String username, String avatarUrl) { + public record Message(String content, AllowedMentions allowedMentions, String username, URI avatarUrl) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.STRING.fieldOf("content").forGetter(Message::content), AllowedMentions.CODEC.fieldOf("allowed_mentions").forGetter(Message::allowedMentions), Codec.STRING.fieldOf("username").forGetter(Message::username), - Codec.STRING.fieldOf("avatar_url").forGetter(Message::avatarUrl) + MoreCodecs.URI.fieldOf("avatar_url").forGetter(Message::avatarUrl) ).apply(instance, Message::new)); public DataResult toJson() { diff --git a/src/main/java/one/devos/nautical/teabridge/duck/PlayerWebHook.java b/src/main/java/one/devos/nautical/teabridge/duck/PlayerWebHook.java index c9969f1..a1fb3b8 100644 --- a/src/main/java/one/devos/nautical/teabridge/duck/PlayerWebHook.java +++ b/src/main/java/one/devos/nautical/teabridge/duck/PlayerWebHook.java @@ -13,10 +13,10 @@ public interface PlayerWebHook { List ONLINE = Lists.newArrayList(); - ProtoWebHook getWebHook(); + ProtoWebHook proto(); default void send(String message, @Nullable String displayName) { - Discord.send(getWebHook(), message, displayName); + Discord.send(proto(), message, displayName); } default void send(PlayerChatMessage message) { diff --git a/src/main/java/one/devos/nautical/teabridge/mixin/CrashReportMixin.java b/src/main/java/one/devos/nautical/teabridge/mixin/CrashReportMixin.java index eefbce4..3f89bc0 100644 --- a/src/main/java/one/devos/nautical/teabridge/mixin/CrashReportMixin.java +++ b/src/main/java/one/devos/nautical/teabridge/mixin/CrashReportMixin.java @@ -2,13 +2,13 @@ import java.io.File; +import one.devos.nautical.teabridge.TeaBridge; 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 net.minecraft.CrashReport; -import one.devos.nautical.teabridge.Config; import one.devos.nautical.teabridge.util.CrashHandler; @Mixin(CrashReport.class) @@ -16,7 +16,7 @@ public abstract class CrashReportMixin { @Inject(method = "saveToFile", at = @At("HEAD")) private void uploadCrash(File file, CallbackInfoReturnable cir) { CrashHandler.CRASH_VALUE.crash(() -> { - if (Config.INSTANCE.crashes().uploadToMclogs()) CrashHandler.uploadAndSend((CrashReport) (Object) this); + if (TeaBridge.config.crashes().uploadToMclogs()) CrashHandler.uploadAndSend((CrashReport) (Object) this); }); } } diff --git a/src/main/java/one/devos/nautical/teabridge/mixin/PlayerAdvancementsMixin.java b/src/main/java/one/devos/nautical/teabridge/mixin/PlayerAdvancementsMixin.java index f56cc73..bd71803 100644 --- a/src/main/java/one/devos/nautical/teabridge/mixin/PlayerAdvancementsMixin.java +++ b/src/main/java/one/devos/nautical/teabridge/mixin/PlayerAdvancementsMixin.java @@ -1,19 +1,19 @@ package one.devos.nautical.teabridge.mixin; +import one.devos.nautical.teabridge.TeaBridge; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; import net.minecraft.network.chat.Component; import net.minecraft.server.PlayerAdvancements; -import one.devos.nautical.teabridge.Config; import one.devos.nautical.teabridge.discord.Discord; @Mixin(PlayerAdvancements.class) public abstract class PlayerAdvancementsMixin { @ModifyArg(method = "method_53637", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V"), index = 0) private Component mirrorAwardMessage(Component awardMessage) { - if (Config.INSTANCE.game().mirrorAdvancements()) Discord.send(awardMessage.getString()); + if (TeaBridge.config.game().mirrorAdvancements()) Discord.send(awardMessage.getString()); return awardMessage; } } diff --git a/src/main/java/one/devos/nautical/teabridge/mixin/PlayerListMixin.java b/src/main/java/one/devos/nautical/teabridge/mixin/PlayerListMixin.java index b0d1c81..b8d8b19 100644 --- a/src/main/java/one/devos/nautical/teabridge/mixin/PlayerListMixin.java +++ b/src/main/java/one/devos/nautical/teabridge/mixin/PlayerListMixin.java @@ -1,19 +1,19 @@ package one.devos.nautical.teabridge.mixin; +import one.devos.nautical.teabridge.TeaBridge; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; import net.minecraft.network.chat.Component; import net.minecraft.server.players.PlayerList; -import one.devos.nautical.teabridge.Config; import one.devos.nautical.teabridge.discord.Discord; @Mixin(PlayerList.class) public abstract class PlayerListMixin { @ModifyArg(method = "placeNewPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V"), index = 0) private Component mirrorJoinMessage(Component joinMessage) { - if (Config.INSTANCE.game().mirrorJoin()) Discord.send(joinMessage.getString()); + if (TeaBridge.config.game().mirrorJoin()) Discord.send(joinMessage.getString()); return joinMessage; } } diff --git a/src/main/java/one/devos/nautical/teabridge/mixin/ServerGamePacketListenerImplMixin.java b/src/main/java/one/devos/nautical/teabridge/mixin/ServerGamePacketListenerImplMixin.java index ed8d00a..8a51590 100644 --- a/src/main/java/one/devos/nautical/teabridge/mixin/ServerGamePacketListenerImplMixin.java +++ b/src/main/java/one/devos/nautical/teabridge/mixin/ServerGamePacketListenerImplMixin.java @@ -1,5 +1,6 @@ package one.devos.nautical.teabridge.mixin; +import one.devos.nautical.teabridge.TeaBridge; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -10,7 +11,6 @@ import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; -import one.devos.nautical.teabridge.Config; import one.devos.nautical.teabridge.discord.Discord; import one.devos.nautical.teabridge.duck.PlayerWebHook; @@ -27,7 +27,7 @@ private void makePlayerWebhookOnline(CallbackInfo ci) { @ModifyArg(method = "removePlayerFromWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V"), index = 0) private Component mirrorLeaveMessage(Component leaveMessage) { PlayerWebHook.ONLINE.remove((PlayerWebHook) player); - if (Config.INSTANCE.game().mirrorLeave()) Discord.send(leaveMessage.getString()); + if (TeaBridge.config.game().mirrorLeave()) Discord.send(leaveMessage.getString()); return leaveMessage; } } diff --git a/src/main/java/one/devos/nautical/teabridge/mixin/ServerPlayerMixin.java b/src/main/java/one/devos/nautical/teabridge/mixin/ServerPlayerMixin.java index 47de855..ac94d30 100644 --- a/src/main/java/one/devos/nautical/teabridge/mixin/ServerPlayerMixin.java +++ b/src/main/java/one/devos/nautical/teabridge/mixin/ServerPlayerMixin.java @@ -1,6 +1,7 @@ package one.devos.nautical.teabridge.mixin; import com.mojang.authlib.minecraft.MinecraftProfileTexture; +import one.devos.nautical.teabridge.TeaBridge; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -8,7 +9,6 @@ import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; -import one.devos.nautical.teabridge.Config; import one.devos.nautical.teabridge.discord.Discord; import one.devos.nautical.teabridge.discord.ProtoWebHook; import one.devos.nautical.teabridge.duck.PlayerWebHook; @@ -22,22 +22,22 @@ public abstract class ServerPlayerMixin implements PlayerWebHook { () -> Objects.requireNonNull(((ServerPlayer) (Object) this).getDisplayName()).getString(), () -> { ServerPlayer self = (ServerPlayer) (Object) this; - if (Config.INSTANCE.avatars().useTextureId()) { + if (TeaBridge.config.avatars().useTextureId()) { MinecraftProfileTexture skin = Objects.requireNonNull(self.getServer()).getSessionService().getTextures(self.getGameProfile()).skin(); - if (skin != null) return Config.INSTANCE.avatars().avatarUrl().formatted(skin.getHash()); + if (skin != null) return TeaBridge.config.avatars().avatarUrl().apply(skin.getHash()); } - return Config.INSTANCE.avatars().avatarUrl().formatted(self.getStringUUID()); + return TeaBridge.config.avatars().avatarUrl().apply(self.getStringUUID()); } ); @Override - public ProtoWebHook getWebHook() { + public ProtoWebHook proto() { return webHook; } @ModifyArg(method = "die", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V"), index = 0) private Component mirrorDeathMessage(Component deathMessage) { - if (Config.INSTANCE.game().mirrorDeath()) Discord.send("**" + deathMessage.getString() + "**"); + if (TeaBridge.config.game().mirrorDeath()) Discord.send("**" + deathMessage.getString() + "**"); return deathMessage; } } diff --git a/src/main/java/one/devos/nautical/teabridge/util/CrashHandler.java b/src/main/java/one/devos/nautical/teabridge/util/CrashHandler.java index 9044b4e..ee09c13 100644 --- a/src/main/java/one/devos/nautical/teabridge/util/CrashHandler.java +++ b/src/main/java/one/devos/nautical/teabridge/util/CrashHandler.java @@ -6,10 +6,11 @@ import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; -import com.google.gson.annotations.Expose; +import com.google.gson.JsonParser; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; import net.minecraft.CrashReport; -import one.devos.nautical.teabridge.Config; import one.devos.nautical.teabridge.TeaBridge; import one.devos.nautical.teabridge.discord.Discord; @@ -26,32 +27,32 @@ public boolean get() { public void crash(Runnable onCrash) { if (!value) { value = true; - Discord.send(Config.INSTANCE.game().serverCrashMessage()); + Discord.send(TeaBridge.config.game().serverCrashMessage()); onCrash.run(); } } }; - public static void uploadAndSend(final CrashReport crash) { - String crashMessage; + private static final URI LOG_UPLOAD_URI = URI.create("https://api.mclo.gs/1/log"); + + private static final Codec LOG_UPLOAD_RESPONSE_CODEC = Codec.STRING.fieldOf("url").codec(); + public static void uploadAndSend(final CrashReport crash) { + String message; try { - var response = TeaBridge.CLIENT.send(HttpRequest.newBuilder() - .uri(URI.create("https://api.mclo.gs/1/log")) + HttpResponse response = TeaBridge.CLIENT.send(HttpRequest.newBuilder(LOG_UPLOAD_URI) .POST(HttpRequest.BodyPublishers.ofString("content=" + URLEncoder.encode(crash.getDetails(), StandardCharsets.UTF_8))) .header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") .build(), HttpResponse.BodyHandlers.ofString()); if (response.statusCode() / 100 != 2) throw new Exception("Non-success status code from request " + response); - crashMessage = JsonUtils.GSON.fromJson(response.body(), LogUploadResponse.class).url; + message = LOG_UPLOAD_RESPONSE_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseString(response.body())).getOrThrow(); } catch (Exception e) { - crashMessage = "Failed to upload crash report : " + e.getMessage(); + message = "Failed to upload crash report : " + e.getMessage(); } - if (crashMessage != null) Discord.send(crashMessage); + Discord.send(message); } - private record LogUploadResponse(@Expose String url) { } - public interface CrashValue { boolean get(); void crash(Runnable onCrash); diff --git a/src/main/java/one/devos/nautical/teabridge/util/LRUHashMap.java b/src/main/java/one/devos/nautical/teabridge/util/LRUHashMap.java deleted file mode 100644 index 79063d8..0000000 --- a/src/main/java/one/devos/nautical/teabridge/util/LRUHashMap.java +++ /dev/null @@ -1,17 +0,0 @@ -package one.devos.nautical.teabridge.util; - -import java.util.LinkedHashMap; -import java.util.Map; - -public final class LRUHashMap extends LinkedHashMap { - private final int maxSize; - - public LRUHashMap(int maxSize) { - this.maxSize = maxSize; - } - - @Override - protected boolean removeEldestEntry(Map.Entry entry) { - return size() > maxSize; - } -} diff --git a/src/main/java/one/devos/nautical/teabridge/util/MoreCodecs.java b/src/main/java/one/devos/nautical/teabridge/util/MoreCodecs.java new file mode 100644 index 0000000..0998f9c --- /dev/null +++ b/src/main/java/one/devos/nautical/teabridge/util/MoreCodecs.java @@ -0,0 +1,33 @@ +package one.devos.nautical.teabridge.util; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; + +import java.net.URI; +import java.util.function.Function; + +public class MoreCodecs { + public static final Codec URI = Codec.STRING.comapFlatMap(checkedMapper(java.net.URI::create), java.net.URI::toString); + + public static Function> checkedMapper(CheckedMappingFunction mapper) { + return mapper; + } + + @FunctionalInterface + public interface CheckedMappingFunction extends Function> { + B map(A a) throws Exception; + + @Override + default DataResult apply(A a) { + try { + return DataResult.success(map(a)); + } catch (Exception e) { + return DataResult.error(e::getMessage); + } + } + } + + public static Codec fromString(CheckedMappingFunction mapper) { + return Codec.STRING.comapFlatMap(checkedMapper(mapper), String::valueOf); + } +} diff --git a/src/main/java/one/devos/nautical/teabridge/util/StyledChatCompat.java b/src/main/java/one/devos/nautical/teabridge/util/StyledChatCompat.java index b4d3ec1..dbcbfec 100644 --- a/src/main/java/one/devos/nautical/teabridge/util/StyledChatCompat.java +++ b/src/main/java/one/devos/nautical/teabridge/util/StyledChatCompat.java @@ -25,8 +25,8 @@ public class StyledChatCompat { public static Pair modify(PlayerChatMessage message) { if (USE_COMPAT) { try { - var proxyContent = ((Component) METHOD.invoke(message, "proxy_content")).getString(); - var proxyDisplayName = ((Component) METHOD.invoke(message, "proxy_display_name")).getString(); + String proxyContent = ((Component) METHOD.invoke(message, "proxy_content")).getString(); + String proxyDisplayName = ((Component) METHOD.invoke(message, "proxy_display_name")).getString(); if (!proxyContent.isBlank() || !proxyDisplayName.isBlank()) return Pair.of(proxyContent, proxyDisplayName); } catch (Throwable e) {