diff --git a/fabric-networking-api-v1/src/client/java/net/fabricmc/fabric/mixin/networking/client/DisconnectedScreenMixin.java b/fabric-networking-api-v1/src/client/java/net/fabricmc/fabric/mixin/networking/client/DisconnectedScreenMixin.java new file mode 100644 index 000000000..cc770b064 --- /dev/null +++ b/fabric-networking-api-v1/src/client/java/net/fabricmc/fabric/mixin/networking/client/DisconnectedScreenMixin.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.networking.client; + +import com.mojang.blaze3d.systems.RenderSystem; +import org.objectweb.asm.Opcodes; +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; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.font.MultilineText; +import net.minecraft.client.gui.screen.DisconnectedScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; + +/** + * This mixin makes disconnect reason text scrollable. + */ +@Mixin(DisconnectedScreen.class) +public abstract class DisconnectedScreenMixin extends Screen { + @Shadow + private int reasonHeight; + + @Unique + private int actualReasonHeight; + + @Unique + private int scroll; + + @Unique + private int maxScroll; + + private DisconnectedScreenMixin() { + super(null); + } + + // Inject to right after reasonHeight is stored, to make sure the back button have correct position. + @Inject(method = "init", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/screen/DisconnectedScreen;reasonHeight:I", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER)) + private void init(CallbackInfo ci) { + actualReasonHeight = reasonHeight; + reasonHeight = Math.min(reasonHeight, height - 100); + scroll = 0; + maxScroll = actualReasonHeight - reasonHeight; + } + + @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/font/MultilineText;drawCenterWithShadow(Lnet/minecraft/client/util/math/MatrixStack;II)I")) + private int render(MultilineText instance, MatrixStack matrixStack, int x, int y) { + double scale = client.getWindow().getScaleFactor(); + RenderSystem.enableScissor(0, (int) (y * scale), (int) (width * scale), (int) (reasonHeight * scale)); + instance.drawCenterWithShadow(matrixStack, x, y - scroll); + RenderSystem.disableScissor(); + + // Draw gradient at the top/bottom to indicate that the text is scrollable. + if (actualReasonHeight > reasonHeight) { + int startX = (width - instance.getMaxWidth()) / 2; + int endX = (width + instance.getMaxWidth()) / 2; + + if (scroll > 0) { + fillGradient(matrixStack, startX, y, endX, y + 10, 0xFF000000, 0); + } + + if (scroll < maxScroll) { + fillGradient(matrixStack, startX, y + reasonHeight - 10, endX, y + reasonHeight, 0, 0xFF000000); + } + } + + return y + reasonHeight; + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + scroll = MathHelper.clamp(scroll - (MathHelper.sign(amount) * client.textRenderer.fontHeight * 10), 0, maxScroll); + return super.mouseScrolled(mouseX, mouseY, amount); + } +} diff --git a/fabric-networking-api-v1/src/client/resources/fabric-networking-api-v1.client.mixins.json b/fabric-networking-api-v1/src/client/resources/fabric-networking-api-v1.client.mixins.json index 356826f22..539b9bef7 100644 --- a/fabric-networking-api-v1/src/client/resources/fabric-networking-api-v1.client.mixins.json +++ b/fabric-networking-api-v1/src/client/resources/fabric-networking-api-v1.client.mixins.json @@ -6,7 +6,8 @@ "accessor.ConnectScreenAccessor", "accessor.MinecraftClientAccessor", "client.ClientLoginNetworkHandlerMixin", - "client.ClientPlayNetworkHandlerMixin" + "client.ClientPlayNetworkHandlerMixin", + "client.DisconnectedScreenMixin" ], "injectors": { "defaultRequire": 1 diff --git a/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/disconnectscreen/DisconnectScreenTest.java b/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/disconnectscreen/DisconnectScreenTest.java new file mode 100644 index 000000000..1892061cb --- /dev/null +++ b/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/disconnectscreen/DisconnectScreenTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.test.networking.disconnectscreen; + +import net.minecraft.text.Text; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; + +public class DisconnectScreenTest implements ClientModInitializer { + @Override + public void onInitializeClient() { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> + dispatcher.register(ClientCommandManager.literal("disconnect_screen_test").executes(context -> { + StringBuilder builder = new StringBuilder("A very long disconnect reason:"); + + for (int i = 0; i < 100; i++) { + builder.append("\nLine ").append(i + 1); + } + + context.getSource().getPlayer().networkHandler.getConnection().disconnect(Text.of(builder.toString())); + return 1; + }))); + } +} diff --git a/fabric-networking-api-v1/src/testmod/resources/fabric.mod.json b/fabric-networking-api-v1/src/testmod/resources/fabric.mod.json index 664aee76b..3a7472728 100644 --- a/fabric-networking-api-v1/src/testmod/resources/fabric.mod.json +++ b/fabric-networking-api-v1/src/testmod/resources/fabric.mod.json @@ -17,6 +17,7 @@ ], "client": [ "net.fabricmc.fabric.test.networking.channeltest.NetworkingChannelClientTest", + "net.fabricmc.fabric.test.networking.disconnectscreen.DisconnectScreenTest", "net.fabricmc.fabric.test.networking.keybindreciever.NetworkingKeybindClientPacketTest", "net.fabricmc.fabric.test.networking.login.NetworkingLoginQueryClientTest", "net.fabricmc.fabric.test.networking.play.NetworkingPlayPacketClientTest"