diff --git a/src/celery/java/dev/rdh/sarcio/celeritas/CeleritasConfigIntegration.java b/src/celery/java/dev/rdh/sarcio/celeritas/CeleritasConfigIntegration.java index c53ffc6..ad91ba2 100644 --- a/src/celery/java/dev/rdh/sarcio/celeritas/CeleritasConfigIntegration.java +++ b/src/celery/java/dev/rdh/sarcio/celeritas/CeleritasConfigIntegration.java @@ -51,7 +51,17 @@ private static OptionPage createPage() { .build()) .build(); - return new OptionPage(id("options"), TextComponent.translatable("sarcio.options.pages.sarcio"), List.of(memory)); + OptionGroup tweaks = OptionGroup.createBuilder() + .setId(id("tweaks")) + .add(OptionImpl.createBuilder(boolean.class, INSTANCE) + .setId(id("long_chat")) + .setControl(TickBoxControl::new) + .setBinding((config, value) -> config.longChat = value, + config -> config.longChat) + .build()) + .build(); + + return new OptionPage(id("options"), TextComponent.translatable("sarcio.options.pages.sarcio"), List.of(memory, tweaks)); } private static OptionIdentifier id(String path) { diff --git a/src/main/java/dev/rdh/sarcio/SarcioConfig.java b/src/main/java/dev/rdh/sarcio/SarcioConfig.java index a1d047e..d6da957 100644 --- a/src/main/java/dev/rdh/sarcio/SarcioConfig.java +++ b/src/main/java/dev/rdh/sarcio/SarcioConfig.java @@ -19,6 +19,7 @@ public final class SarcioConfig { public boolean releaseCrashReserve; public boolean disableRealms; + public boolean longChat = true; public static SarcioConfig load() { if (Files.isRegularFile(PATH)) { diff --git a/src/main/java/dev/rdh/sarcio/SarcioMod.java b/src/main/java/dev/rdh/sarcio/SarcioMod.java index 53eabfe..bdfaf88 100644 --- a/src/main/java/dev/rdh/sarcio/SarcioMod.java +++ b/src/main/java/dev/rdh/sarcio/SarcioMod.java @@ -1,5 +1,6 @@ package dev.rdh.sarcio; +import dev.rdh.sarcio.util.LongChatDetector; import it.unimi.dsi.fastutil.bytes.ByteArrays; import net.fabricmc.api.ModInitializer; @@ -35,4 +36,8 @@ public void onInitialize() { } } } + + public static int chatLimit() { + return CONFIG.longChat && (Minecraft.getInstance().isIntegratedServerRunning() || LongChatDetector.supported()) ? 256 : 100; + } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/SarcioMixinPlugin.java b/src/main/java/dev/rdh/sarcio/mixin/SarcioMixinPlugin.java index 33fa07c..8620fc5 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/SarcioMixinPlugin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/SarcioMixinPlugin.java @@ -46,7 +46,7 @@ public void preApply(String targetClassName, ClassNode targetClass, String mixin @Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - if (mixinClassName.endsWith("core.LazyLoadBaseMixin")) { + if (mixinClassName.endsWith("core.LazySupplierMixin")) { Asm.asmLazyLoadBase(targetClass); } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/allocation_rate/world/WorldMixin.java b/src/main/java/dev/rdh/sarcio/mixin/allocation_rate/world/WorldMixin.java index e90a0bc..9258125 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/allocation_rate/world/WorldMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/allocation_rate/world/WorldMixin.java @@ -1,5 +1,7 @@ package dev.rdh.sarcio.mixin.allocation_rate.world; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import java.util.List; import java.util.Set; import net.minecraft.client.Minecraft; @@ -102,4 +104,12 @@ private int activeChunkPlayerCount(List players) { private Object useLocalPlayer(List players, int index) { return this.isClient ? Minecraft.getInstance().player : players.get(index); } + + @WrapOperation( + method = "tickEntity(Lnet/minecraft/entity/Entity;Z)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;isAreaLoaded(IIIIIIZ)Z") + ) + private boolean skipClientAreaCheck(World world, int minX, int minY, int minZ, int maxX, int maxY, int maxZ, boolean allowEmpty, Operation original) { + return this.isClient || original.call(world, minX, minY, minZ, maxX, maxY, maxZ, allowEmpty); + } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/CommandRegistryMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/CommandRegistryMixin.java new file mode 100644 index 0000000..5bbec6c --- /dev/null +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/CommandRegistryMixin.java @@ -0,0 +1,21 @@ +package dev.rdh.sarcio.mixin.bugfix; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import java.util.Locale; +import java.util.Map; +import net.minecraft.server.command.handler.CommandRegistry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(CommandRegistry.class) +public class CommandRegistryMixin { + @WrapOperation( + method = {"run(Lnet/minecraft/server/command/source/CommandSource;Ljava/lang/String;)I", "getSuggestions"}, + at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;") + ) + private Object sarcio$ignoreCommandCase(Map commands, Object name, Operation original) { + Object command = original.call(commands, name); + return command != null ? command : original.call(commands, ((String) name).toLowerCase(Locale.ROOT)); + } +} diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/MinecraftMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/MinecraftMixin.java index 3fb2d3e..3ff9f1c 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/MinecraftMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/MinecraftMixin.java @@ -40,7 +40,7 @@ public class MinecraftMixin { if (mc.screen == null) { if (this.options.togglePerspectiveKey.consumeClick()) { this.options.perspective = (this.options.perspective + 1) % 3; - if (this.options.perspective < 2) { + if (this.options.perspective < 2 && mc.getCamera() != mc.player) { mc.gameRenderer.updateShader(this.options.perspective == 0 ? mc.getCamera() : null); } mc.worldRenderer.onViewChanged(); diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/ScoreboardMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/ScoreboardMixin.java index ab307ef..a0ac2ff 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/ScoreboardMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/ScoreboardMixin.java @@ -37,4 +37,11 @@ public abstract class ScoreboardMixin { private void sarcio$dontThrowOnStaleTeam(String player, Team team, CallbackInfo ci) { ci.cancel(); } + + @Inject(method = "removeMemberFromTeam(Ljava/lang/String;Lnet/minecraft/scoreboard/team/Team;)V", at = @At("HEAD"), cancellable = true) + private void sarcio$dontRemoveFromNullTeam(String player, Team team, CallbackInfo ci) { + if (team == null) { + ci.cancel(); + } + } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/gui/BookEditScreenMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/gui/BookEditScreenMixin.java index e7cdb1c..1bb9d43 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/gui/BookEditScreenMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/gui/BookEditScreenMixin.java @@ -1,5 +1,8 @@ package dev.rdh.sarcio.mixin.bugfix.gui; +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Unique; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import net.minecraft.client.gui.screen.Screen; @@ -27,4 +30,20 @@ public abstract class BookEditScreenMixin extends Screen { private void sarcio$drawBackground(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { this.renderBackground(); } + + @Unique + private Text sarcio$hoveredText; + + @WrapWithCondition(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/game/BookEditScreen;renderTextHoverEffect(Lnet/minecraft/text/Text;II)V")) + private boolean sarcio$deferHoverTooltip(BookEditScreen instance, Text text, int x, int y) { + this.sarcio$hoveredText = text; + return false; + } + + @Inject(method = "render", at = @At("TAIL")) + private void sarcio$drawHoverTooltipLast(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { + Text text = this.sarcio$hoveredText; + this.sarcio$hoveredText = null; + this.renderTextHoverEffect(text, mouseX, mouseY); + } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/network/ServerListMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/network/ServerListMixin.java index f802dc3..b13b752 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/network/ServerListMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/network/ServerListMixin.java @@ -30,4 +30,19 @@ public class ServerListMixin { ci.cancel(); } } + + @Inject(method = "swap", at = @At("HEAD"), cancellable = true) + private void sarcio$dontSwapOutOfBounds(int index1, int index2, CallbackInfo ci) { + int size = this.entries.size(); + if (index1 < 0 || index1 >= size || index2 < 0 || index2 >= size) { + ci.cancel(); + } + } + + @Inject(method = "set", at = @At("HEAD"), cancellable = true) + private void sarcio$dontSetOutOfBounds(int index, ServerListEntry entry, CallbackInfo ci) { + if (index < 0 || index >= this.entries.size()) { + ci.cancel(); + } + } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/memory_management/enchantment/EnchantmentHelperMixin.java b/src/main/java/dev/rdh/sarcio/mixin/memory_management/enchantment/EnchantmentHelperMixin.java index a6a0539..26e829c 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/memory_management/enchantment/EnchantmentHelperMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/memory_management/enchantment/EnchantmentHelperMixin.java @@ -1,5 +1,7 @@ package dev.rdh.sarcio.mixin.memory_management.enchantment; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.item.ItemStack; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import dev.rdh.sarcio.util.EnchantmentReferenceCleaner; @@ -27,4 +29,13 @@ private static void clearDamageIterator(LivingEntity user, Entity target, Operat EnchantmentReferenceCleaner.clearDamageIterator(); } } + + @WrapMethod(method = "modifyProtection") + private static int clearProtectionModifier(ItemStack[] armor, DamageSource source, Operation original) { + try { + return original.call(armor, source); + } finally { + EnchantmentReferenceCleaner.clearProtectionModifier(); + } + } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/memory_management/enchantment/ProtectionModifierMixin.java b/src/main/java/dev/rdh/sarcio/mixin/memory_management/enchantment/ProtectionModifierMixin.java new file mode 100644 index 0000000..3cef82f --- /dev/null +++ b/src/main/java/dev/rdh/sarcio/mixin/memory_management/enchantment/ProtectionModifierMixin.java @@ -0,0 +1,24 @@ +package dev.rdh.sarcio.mixin.memory_management.enchantment; + +import dev.rdh.sarcio.util.EnchantmentReferenceCleaner; +import net.minecraft.entity.damage.DamageSource; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(targets = "net.minecraft.enchantment.EnchantmentHelper$ProtectionModifier") +abstract class ProtectionModifierMixin implements EnchantmentReferenceCleaner.Clearable { + @Shadow public DamageSource source; + + @Inject(method = "", at = @At("RETURN")) + private void register(CallbackInfo ci) { + EnchantmentReferenceCleaner.registerProtectionModifier(this); + } + + @Override + public void clearReferences() { + this.source = null; + } +} diff --git a/src/main/java/dev/rdh/sarcio/mixin/resourcepacks/ResourcePacksMixin.java b/src/main/java/dev/rdh/sarcio/mixin/resourcepacks/ResourcePacksMixin.java new file mode 100644 index 0000000..b43f11c --- /dev/null +++ b/src/main/java/dev/rdh/sarcio/mixin/resourcepacks/ResourcePacksMixin.java @@ -0,0 +1,58 @@ +package dev.rdh.sarcio.mixin.resourcepacks; + +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import net.minecraft.client.resource.pack.ResourcePacks; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(ResourcePacks.class) +public class ResourcePacksMixin { + @Shadow + private List availablePacks; + + @Unique + private Map sarcio$packIndex; + + @WrapMethod(method = "reload") + private void sarcio$indexAvailablePacks(Operation original) { + Map index = new HashMap<>(); + for (int i = 0; i < this.availablePacks.size(); i++) { + index.putIfAbsent(this.availablePacks.get(i), i); + } + this.sarcio$packIndex = index; + try { + original.call(); + } finally { + this.sarcio$packIndex = null; + } + } + + @WrapOperation(method = "reload", at = @At(value = "INVOKE", target = "Ljava/util/List;contains(Ljava/lang/Object;)Z")) + private boolean sarcio$hashContains(List packs, Object pack, Operation original) { + return packs == this.availablePacks ? this.sarcio$packIndex.containsKey(pack) : original.call(packs, pack); + } + + @WrapOperation(method = "reload", at = @At(value = "INVOKE", target = "Ljava/util/List;indexOf(Ljava/lang/Object;)I")) + private int sarcio$hashIndexOf(List packs, Object pack, Operation original) { + if (packs != this.availablePacks) { + return original.call(packs, pack); + } + + Integer index = this.sarcio$packIndex.get(pack); + return index == null ? -1 : index; + } + + @WrapOperation(method = "reload", at = @At(value = "INVOKE", target = "Ljava/util/List;removeAll(Ljava/util/Collection;)Z")) + private boolean sarcio$hashRemoveAll(List packs, Collection kept, Operation original) { + return original.call(packs, new HashSet<>(kept)); + } +} diff --git a/src/main/java/dev/rdh/sarcio/mixin/tweaks/ChatMessageC2SPacketMixin.java b/src/main/java/dev/rdh/sarcio/mixin/tweaks/ChatMessageC2SPacketMixin.java new file mode 100644 index 0000000..080f0b6 --- /dev/null +++ b/src/main/java/dev/rdh/sarcio/mixin/tweaks/ChatMessageC2SPacketMixin.java @@ -0,0 +1,15 @@ +package dev.rdh.sarcio.mixin.tweaks; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import dev.rdh.sarcio.SarcioMod; +import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(ChatMessageC2SPacket.class) +public class ChatMessageC2SPacketMixin { + @ModifyExpressionValue(method = "(Ljava/lang/String;)V", at = @At(value = "CONSTANT", args = "intValue=100")) + private int sarcio$longChatPacket(int original) { + return SarcioMod.chatLimit(); + } +} diff --git a/src/main/java/dev/rdh/sarcio/mixin/tweaks/ChatScreenMixin.java b/src/main/java/dev/rdh/sarcio/mixin/tweaks/ChatScreenMixin.java new file mode 100644 index 0000000..ba87b5a --- /dev/null +++ b/src/main/java/dev/rdh/sarcio/mixin/tweaks/ChatScreenMixin.java @@ -0,0 +1,15 @@ +package dev.rdh.sarcio.mixin.tweaks; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import dev.rdh.sarcio.SarcioMod; +import net.minecraft.client.gui.screen.game.ChatScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(ChatScreen.class) +public class ChatScreenMixin { + @ModifyExpressionValue(method = "init", at = @At(value = "CONSTANT", args = "intValue=100")) + private int sarcio$longChatInput(int original) { + return SarcioMod.chatLimit(); + } +} diff --git a/src/main/java/dev/rdh/sarcio/mixin/tweaks/ClientPlayNetworkHandlerMixin.java b/src/main/java/dev/rdh/sarcio/mixin/tweaks/ClientPlayNetworkHandlerMixin.java index dbdf15c..f1d43d7 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/tweaks/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/tweaks/ClientPlayNetworkHandlerMixin.java @@ -1,11 +1,16 @@ package dev.rdh.sarcio.mixin.tweaks; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import dev.rdh.sarcio.SarcioMod; +import dev.rdh.sarcio.util.LongChatDetector; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.network.handler.ClientPlayNetworkHandler; +import net.minecraft.client.options.ServerListEntry; 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.CallbackInfo; @Mixin(ClientPlayNetworkHandler.class) public class ClientPlayNetworkHandlerMixin { @@ -13,4 +18,11 @@ public class ClientPlayNetworkHandlerMixin { private boolean sarcio$skipTerrainScreen(Minecraft instance, Screen screen) { return false; } + + @Inject(method = "handleLogin", at = @At("TAIL")) + private void sarcio$detectLongChat(CallbackInfo ci) { + Minecraft minecraft = Minecraft.getInstance(); + ServerListEntry server = minecraft.getCurrentServerEntry(); + LongChatDetector.detect(SarcioMod.CONFIG.longChat && !minecraft.isIntegratedServerRunning() && server != null ? server.ip : null); + } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/tweaks/MinecraftMixin.java b/src/main/java/dev/rdh/sarcio/mixin/tweaks/MinecraftMixin.java index d851c68..ba262f0 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/tweaks/MinecraftMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/tweaks/MinecraftMixin.java @@ -1,5 +1,7 @@ package dev.rdh.sarcio.mixin.tweaks; +import dev.rdh.sarcio.util.LongChatDetector; +import net.minecraft.entity.Entity; import net.minecraft.client.Minecraft; import net.minecraft.client.render.ProgressRenderer; import net.minecraft.client.world.ClientWorld; @@ -41,4 +43,17 @@ public class MinecraftMixin { private boolean sarcio$skipWorldSwapProgress(ProgressRenderer instance, String message, ClientWorld world) { return world == null; } + + @Inject(method = "setWorld(Lnet/minecraft/client/world/ClientWorld;Ljava/lang/String;)V", at = @At("HEAD")) + private void sarcio$forgetLongChatOnDisconnect(ClientWorld world, String message, CallbackInfo ci) { + if (world == null) { + LongChatDetector.detect(null); // ping again on the next join, the answer may have changed + } + } + + @WrapWithCondition(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;updateShader(Lnet/minecraft/entity/Entity;)V")) + private boolean sarcio$keepShaderOnPerspectiveChange(GameRenderer instance, Entity camera) { + Minecraft minecraft = (Minecraft) (Object) this; + return minecraft.getCamera() != minecraft.player; + } } diff --git a/src/main/java/dev/rdh/sarcio/util/EnchantmentReferenceCleaner.java b/src/main/java/dev/rdh/sarcio/util/EnchantmentReferenceCleaner.java index 3dece4b..f6d4f1e 100644 --- a/src/main/java/dev/rdh/sarcio/util/EnchantmentReferenceCleaner.java +++ b/src/main/java/dev/rdh/sarcio/util/EnchantmentReferenceCleaner.java @@ -3,6 +3,7 @@ public final class EnchantmentReferenceCleaner { private static Clearable hurtIterator; private static Clearable damageIterator; + private static Clearable protectionModifier; private EnchantmentReferenceCleaner() { } @@ -15,6 +16,14 @@ public static void registerDamageIterator(Clearable iterator) { damageIterator = iterator; } + public static void registerProtectionModifier(Clearable modifier) { + protectionModifier = modifier; + } + + public static void clearProtectionModifier() { + protectionModifier.clearReferences(); + } + public static void clearHurtIterator() { hurtIterator.clearReferences(); } diff --git a/src/main/java/dev/rdh/sarcio/util/LongChatDetector.java b/src/main/java/dev/rdh/sarcio/util/LongChatDetector.java new file mode 100644 index 0000000..2dc0ac7 --- /dev/null +++ b/src/main/java/dev/rdh/sarcio/util/LongChatDetector.java @@ -0,0 +1,91 @@ +package dev.rdh.sarcio.util; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import dev.rdh.sarcio.SarcioMod; +import io.netty.buffer.Unpooled; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import net.minecraft.client.network.ServerAddress; +import net.minecraft.network.PacketByteBuf; + +public final class LongChatDetector { + private static final int PROTOCOL = 315; // 1.11 + private static final int TIMEOUT_MILLIS = 4000; + + private static final State NONE = new State(null, false); + private static final AtomicReference STATE = new AtomicReference<>(NONE); + + private record State(String ip, boolean supported) { + } + + private LongChatDetector() { + } + + public static boolean supported() { + return STATE.get().supported; + } + + public static void detect(String serverIp) { + if (Objects.equals(serverIp, STATE.get().ip)) { + return; + } + State pending = new State(serverIp, false); + STATE.set(pending); + if (serverIp == null) { + return; + } + Thread.ofVirtual().name("Long Chat Detector").start(() -> { + try { + STATE.compareAndSet(pending, new State(serverIp, ping(serverIp) >= PROTOCOL)); + } catch (Exception e) { + SarcioMod.LOGGER.warn("Could not detect long chat support for {}", serverIp, e); + STATE.compareAndSet(pending, NONE); + } + }); + } + + private static int ping(String serverIp) throws IOException { + ServerAddress address = ServerAddress.parse(serverIp); + try (Socket socket = new Socket()) { + socket.setSoTimeout(TIMEOUT_MILLIS); + socket.connect(new InetSocketAddress(address.getAddress(), address.getPort()), TIMEOUT_MILLIS); + + PacketByteBuf handshake = new PacketByteBuf(Unpooled.buffer()); + handshake.writeVarInt(0); + handshake.writeVarInt(PROTOCOL); + handshake.writeString(address.getAddress()); + handshake.writeShort(address.getPort()); + handshake.writeVarInt(1); + + PacketByteBuf out = new PacketByteBuf(Unpooled.buffer()); + out.writeVarInt(handshake.readableBytes()); + out.writeBytes(handshake); + out.writeVarInt(1); + out.writeVarInt(0); + out.readBytes(socket.getOutputStream(), out.readableBytes()); + + InputStream in = socket.getInputStream(); + int length = 0; + for (int shift = 0; ; shift += 7) { + int b = shift > 14 ? -1 : in.read(); + if (b < 0) { + throw new IOException("Bad status response"); + } + length |= (b & 0x7F) << shift; + if ((b & 0x80) == 0) { + break; + } + } + + PacketByteBuf response = new PacketByteBuf(Unpooled.wrappedBuffer(in.readNBytes(length))); + response.readVarInt(); + JsonObject version = JsonParser.parseString(response.readString(32767)).getAsJsonObject().getAsJsonObject("version"); + return version != null && version.has("protocol") ? version.get("protocol").getAsInt() : 0; + } + } +} diff --git a/src/main/resources/assets/sarcio/lang/en_US.lang b/src/main/resources/assets/sarcio/lang/en_US.lang index a4553a8..87a2782 100644 --- a/src/main/resources/assets/sarcio/lang/en_US.lang +++ b/src/main/resources/assets/sarcio/lang/en_US.lang @@ -3,3 +3,5 @@ sarcio.options.release_crash_reserve.name=Release Crash Reserve sarcio.options.release_crash_reserve.tooltip=Releases Minecraft's 10 MiB emergency reserve after startup. sarcio.options.disable_realms.name=Hide Realms Button sarcio.options.disable_realms.tooltip=Hides the Minecraft Realms button from the title screen. +sarcio.options.long_chat.name=256-Character Chat +sarcio.options.long_chat.tooltip=Allows 256-character chat messages on servers whose server list ping reported 1.11 or newer. A proxy in front of an older server can report a newer version; long messages will disconnect you there. diff --git a/src/main/resources/sarcio.mixins.json b/src/main/resources/sarcio.mixins.json index 47f044b..6b6c271 100644 --- a/src/main/resources/sarcio.mixins.json +++ b/src/main/resources/sarcio.mixins.json @@ -27,6 +27,7 @@ "bugfix.entity.ExperienceOrbEntityMixin", "bugfix.gui.ItemStackMixin", "bugfix.ScoreboardMixin", + "bugfix.CommandRegistryMixin", "bugfix.gui.StatMixin", "bugfix.world.WorldMixin", "bugfix.network.EntityPacketsMixin", @@ -43,6 +44,7 @@ "memory_management.enchantment.DamageWildcardMixin", "memory_management.enchantment.EnchantmentHelperMixin", "memory_management.enchantment.ProtectionWildcardMixin", + "memory_management.enchantment.ProtectionModifierMixin", "memory_management.network.NetHandlerPlayMixin", "memory_management.world.ChunkNibbleStorageMixin", "memory_management.world.AnvilChunkStorageMixin", @@ -131,6 +133,7 @@ "resourcepacks.CustomResourcePackMixin", "resourcepacks.BuiltInResourcePackMixin", "resourcepacks.AssetIndexMixin", + "resourcepacks.ResourcePacksMixin", "textures.TextureManagerMixin", "textures.TextureUtilMixin", "textures.HttpTextureMixin", @@ -143,6 +146,8 @@ "tweaks.MultiplayerServerListPingerMixin", "tweaks.ServerAddressAccessor", "tweaks.ServerAddressMixin", + "tweaks.ChatScreenMixin", + "tweaks.ChatMessageC2SPacketMixin", "netty.LegacyPingHandlerMixin" ], "injectors": {