diff --git a/build.gradle b/build.gradle index f52eeb4ef2..b5b30caf3e 100644 --- a/build.gradle +++ b/build.gradle @@ -11,8 +11,10 @@ plugins { repositories { mavenLocal() mavenCentral() + maven { url = "https://maven.minecraftforge.net/" } maven { url = "https://repo.spongepowered.org/repository/maven-public/" } maven { url = "https://jitpack.io/" } + maven { url = "https://repo.viaversion.com" } } version = "5.4.0" @@ -39,8 +41,21 @@ minecraft { configurations { include implementation.extendsFrom(include) + + external + compile.extendsFrom(external) + + runtimeOnly.canBeResolved = true } +def viaLibs = [ + "com.viaversion:viaversion:${project.viaversion_version}", + "com.viaversion:viabackwards:${project.viabackwards_version}", + "com.viaversion:viarewind-universal:${project.viarewind_version}", + "org.yaml:snakeyaml:${project.snake_yml_version}", + "org.slf4j:slf4j-api:${project.slf4j_version}" +] + dependencies { implementation 'org.projectlombok:lombok:1.18.26' include("org.spongepowered:mixin:0.7.11-SNAPSHOT") { @@ -53,6 +68,16 @@ dependencies { exclude module: "slf4j-api" } + for (final def via in viaLibs) { + include(via) + } + + include('com.github.half-cambodian-hacker-man:Koffee:d8cee73') { + exclude module: 'asm-commons' + exclude module: 'asm-tree' + exclude module: 'asm' + } + annotationProcessor("org.spongepowered:mixin:0.7.11-SNAPSHOT") include "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" diff --git a/gradle.properties b/gradle.properties index 0d85fbdc91..9d0765522f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,6 +4,15 @@ org.gradle.jvmargs=-Xmx8g kotlin_version = 1.6.10 detekt_version = 1.19.0 forgeVersion=1.8.9-11.15.1.2318-1.8.9 + +vialoader_version=2.2.11-SNAPSHOT +viaversion_version=4.9.0-23w45a-SNAPSHOT +viabackwards_version=4.9.0-23w45a-SNAPSHOT +viarewind_version=3.0.4-SNAPSHOT + forgegradle_version = f2c5bb338e mixingradle_version = ae2a80e jcef_version = 100.0.14.2 + +snake_yml_version=2.2 +slf4j_version=2.0.9 diff --git a/libs/ViaBackwards-4.4.1.jar b/libs/ViaBackwards-4.4.1.jar new file mode 100644 index 0000000000..a41706cbaf Binary files /dev/null and b/libs/ViaBackwards-4.4.1.jar differ diff --git a/libs/ViaRewind-2.0.3-AnimationFixed.jar b/libs/ViaRewind-2.0.3-AnimationFixed.jar new file mode 100644 index 0000000000..7fe98b5b41 Binary files /dev/null and b/libs/ViaRewind-2.0.3-AnimationFixed.jar differ diff --git a/libs/ViaSnakeYaml-1.30.jar b/libs/ViaSnakeYaml-1.30.jar new file mode 100644 index 0000000000..6c9b2bc65b Binary files /dev/null and b/libs/ViaSnakeYaml-1.30.jar differ diff --git a/libs/ViaVersion-4.4.1.jar b/libs/ViaVersion-4.4.1.jar new file mode 100644 index 0000000000..4223bd1225 Binary files /dev/null and b/libs/ViaVersion-4.4.1.jar differ diff --git a/src/main/java/cc/paimonmc/viamcp/ViaMCP.java b/src/main/java/cc/paimonmc/viamcp/ViaMCP.java new file mode 100644 index 0000000000..8262769ee9 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/ViaMCP.java @@ -0,0 +1,136 @@ +package cc.paimonmc.viamcp; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.viaversion.viaversion.ViaManagerImpl; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.data.MappingDataLoader; +import io.netty.channel.EventLoop; +import io.netty.channel.local.LocalEventLoopGroup; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.ScaledResolution; +import org.apache.logging.log4j.LogManager; +import cc.paimonmc.viamcp.gui.AsyncVersionSlider; +import cc.paimonmc.viamcp.loader.MCPBackwardsLoader; +import cc.paimonmc.viamcp.loader.MCPRewindLoader; +import cc.paimonmc.viamcp.loader.MCPViaLoader; +import cc.paimonmc.viamcp.platform.MCPViaInjector; +import cc.paimonmc.viamcp.platform.MCPViaPlatform; +import cc.paimonmc.viamcp.utils.JLoggerToLog4j; +import java.io.File; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.logging.Logger; +import net.ccbluex.liquidbounce.utils.MinecraftInstance; + +import static net.ccbluex.liquidbounce.utils.MinecraftInstance.mc; + +public class ViaMCP { + public final static int PROTOCOL_VERSION = 47; + private static final ViaMCP instance = new ViaMCP(); + + public static ViaMCP getInstance() { + return instance; + } + + private final Logger jLogger = new JLoggerToLog4j(LogManager.getLogger("ViaMCP")); + private final CompletableFuture INIT_FUTURE = new CompletableFuture<>(); + + private ExecutorService ASYNC_EXEC; + private EventLoop EVENT_LOOP; + + private File file; + private int version; + private String lastServer; + + public static boolean hidden; + + public static void staticInit() { + getInstance().start(); + } + + /** + * Version Slider that works Asynchronously with the Version GUI + * Please initialize this before usage with initAsyncSlider() or initAsyncSlider(x, y, width (min. 110), height) + */ + public AsyncVersionSlider asyncSlider; + + public void start() { + try { + final ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("ViaMCP-%d").build(); + ASYNC_EXEC = Executors.newFixedThreadPool(8, factory); + + EVENT_LOOP = new LocalEventLoopGroup(1, factory).next(); + EVENT_LOOP.submit(INIT_FUTURE::join); + + setVersion(PROTOCOL_VERSION); + this.file = new File("ViaMCP"); + if (this.file.mkdir()) { + this.getjLogger().info("Creating ViaMCP Folder"); + } + + Via.init(ViaManagerImpl.builder().injector(new MCPViaInjector()).loader(new MCPViaLoader()).platform(new MCPViaPlatform(file)).build()); + + MappingDataLoader.enableMappingsCache(); + ((ViaManagerImpl) Via.getManager()).init(); + + new MCPBackwardsLoader(file); + new MCPRewindLoader(file); + + INIT_FUTURE.complete(null); + + //ViaMCP.getInstance().initAsyncSlider(); + } catch (final Exception exception) { + exception.printStackTrace(); + } + } + + public Logger getjLogger() { + return jLogger; + } + + public CompletableFuture getInitFuture() { + return INIT_FUTURE; + } + + public ExecutorService getAsyncExecutor() { + return ASYNC_EXEC; + } + + public EventLoop getEventLoop() { + return EVENT_LOOP; + } + + public File getFile() { + return file; + } + + public String getLastServer() { + return lastServer; + } + + public int getVersion() { + return version; + } + + public void setVersion(final int version) { + this.version = version; + } + + public void setFile(final File file) { + this.file = file; + } + + public void setLastServer(final String lastServer) { + this.lastServer = lastServer; + } + + public static boolean isHidden() { + return hidden; + } + + public static void setHidden(boolean hidden) { + ViaMCP.hidden = hidden; + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/gui/AsyncVersionSlider.java b/src/main/java/cc/paimonmc/viamcp/gui/AsyncVersionSlider.java new file mode 100644 index 0000000000..659532cc19 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/gui/AsyncVersionSlider.java @@ -0,0 +1,90 @@ +package cc.paimonmc.viamcp.gui; + +import cc.paimonmc.viamcp.ViaMCP; +import cc.paimonmc.viamcp.protocols.ProtocolCollection; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.MathHelper; + +import java.util.Arrays; +import java.util.Collections; + +public class AsyncVersionSlider extends GuiButton { + private float dragValue = (float) (ProtocolCollection.values().length - Arrays.asList(ProtocolCollection.values()).indexOf(ProtocolCollection.getProtocolCollectionById(ViaMCP.PROTOCOL_VERSION))) / ProtocolCollection.values().length; + + private final ProtocolCollection[] values; + private float sliderValue; + public boolean dragging; + + public AsyncVersionSlider(final int buttonId, final int x, final int y, final int widthIn, final int heightIn) { + super(buttonId, x, y, Math.max(widthIn, 110), heightIn, ""); + this.values = ProtocolCollection.values(); + Collections.reverse(Arrays.asList(values)); + this.sliderValue = dragValue; + this.displayString = values[(int) (this.sliderValue * (values.length - 1))].getVersion().getName(); + } + + public void drawButton(final Minecraft mc, final int mouseX, final int mouseY) { + super.drawButton(mc, mouseX, mouseY); + } + + /** + * Returns 0 if the button is disabled, 1 if the mouse is NOT hovering over this button and 2 if it IS hovering over + * this button. + */ + public int getHoverState(final boolean mouseOver) { + return 0; + } + + /** + * Fired when the mouse button is dragged. Equivalent of MouseListener.mouseDragged(MouseEvent e). + */ + public void mouseDragged(final Minecraft mc, final int mouseX, final int mouseY) { + if (this.visible) { + if (this.dragging) { + this.sliderValue = (float) (mouseX - (this.xPosition + 4)) / (float) (this.width - 8); + this.sliderValue = MathHelper.clamp_float(this.sliderValue, 0.0F, 1.0F); + this.dragValue = sliderValue; + this.displayString = values[(int) (this.sliderValue * (values.length - 1))].getVersion().getName(); + ViaMCP.getInstance().setVersion(values[(int) (this.sliderValue * (values.length - 1))].getVersion().getVersion()); + } + + mc.getTextureManager().bindTexture(buttonTextures); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + this.drawTexturedModalRect(this.xPosition + (int) (this.sliderValue * (float) (this.width - 8)), this.yPosition, 0, 66, 4, 20); + this.drawTexturedModalRect(this.xPosition + (int) (this.sliderValue * (float) (this.width - 8)) + 4, this.yPosition, 196, 66, 4, 20); + } + } + + /** + * Returns true if the mouse has been pressed on this control. Equivalent of MouseListener.mousePressed(MouseEvent + * e). + */ + public boolean mousePressed(final Minecraft mc, final int mouseX, final int mouseY) { + if (super.mousePressed(mc, mouseX, mouseY)) { + this.sliderValue = (float) (mouseX - (this.xPosition + 4)) / (float) (this.width - 8); + this.sliderValue = MathHelper.clamp_float(this.sliderValue, 0.0F, 1.0F); + this.dragValue = sliderValue; + this.displayString = values[(int) (this.sliderValue * (values.length - 1))].getVersion().getName(); + ViaMCP.getInstance().setVersion(values[(int) (this.sliderValue * (values.length - 1))].getVersion().getVersion()); + this.dragging = true; + return true; + } else { + return false; + } + } + + /** + * Fired when the mouse button is released. Equivalent of MouseListener.mouseReleased(MouseEvent e). + */ + public void mouseReleased(final int mouseX, final int mouseY) { + this.dragging = false; + } + + public void setVersion(final int protocol) { + this.dragValue = (float) (ProtocolCollection.values().length - Arrays.asList(ProtocolCollection.values()).indexOf(ProtocolCollection.getProtocolCollectionById(protocol))) / ProtocolCollection.values().length; + this.sliderValue = this.dragValue; + this.displayString = values[(int) (this.sliderValue * (values.length - 1))].getVersion().getName(); + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/gui/GuiProtocolSelector.java b/src/main/java/cc/paimonmc/viamcp/gui/GuiProtocolSelector.java new file mode 100644 index 0000000000..4c6303d81c --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/gui/GuiProtocolSelector.java @@ -0,0 +1,105 @@ +package cc.paimonmc.viamcp.gui; + +import cc.paimonmc.viamcp.ViaMCP; +import cc.paimonmc.viamcp.protocols.ProtocolCollection; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiSlot; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.EnumChatFormatting; + +import java.io.IOException; + +public class GuiProtocolSelector extends GuiScreen { + private final GuiScreen parent; + public SlotList list; + + public GuiProtocolSelector(final GuiScreen parent) { + this.parent = parent; + } + + @Override + public void initGui() { + super.initGui(); + buttonList.add(new GuiButton(1, width / 2 - 100, height - 25, 200, 20, "Back")); + buttonList.add(new GuiButton(2, width / 2 - 180, height - 25, 75, 20, "Credits")); + list = new SlotList(mc, width, height, 32, height - 32, 10); + } + + @Override + public void actionPerformed(final GuiButton guiButton){ + list.actionPerformed(guiButton); + + if (guiButton.id == 1) { + mc.displayGuiScreen(parent); + } + } + + @Override + public void handleMouseInput() throws IOException { + list.handleMouseInput(); + super.handleMouseInput(); + } + + @Override + public void drawScreen(final int mouseX, final int mouseY, final float partialTicks) { + list.drawScreen(mouseX, mouseY, partialTicks); + GlStateManager.pushMatrix(); + GlStateManager.scale(2.0, 2.0, 2.0); + final String title = EnumChatFormatting.BOLD + "ViaMCP Reborn"; + drawString(this.fontRendererObj, title, (this.width - (this.fontRendererObj.getStringWidth(title) * 2)) / 4, 5, -1); + GlStateManager.popMatrix(); + + final String versionName = ProtocolCollection.getProtocolById(ViaMCP.getInstance().getVersion()).getName(); + final String versionCodeName = ProtocolCollection.getProtocolInfoById(ViaMCP.getInstance().getVersion()).getName(); + final String versionReleaseDate = ProtocolCollection.getProtocolInfoById(ViaMCP.getInstance().getVersion()).getReleaseDate(); + final String versionTitle = "Version: " + versionName + " - " + versionCodeName; + final String versionReleased = "Released: " + versionReleaseDate; + + final int fixedHeight = ((5 + this.fontRendererObj.FONT_HEIGHT) * 2) + 2; + + drawString(this.fontRendererObj, EnumChatFormatting.GRAY + (EnumChatFormatting.BOLD + "Version Information"), (width - this.fontRendererObj.getStringWidth("Version Information")) / 2, fixedHeight, -1); + drawString(this.fontRendererObj, versionTitle, (width - this.fontRendererObj.getStringWidth(versionTitle)) / 2, fixedHeight + this.fontRendererObj.FONT_HEIGHT, -1); + drawString(this.fontRendererObj, versionReleased, (width - this.fontRendererObj.getStringWidth(versionReleased)) / 2, fixedHeight + this.fontRendererObj.FONT_HEIGHT * 2, -1); + + super.drawScreen(mouseX, mouseY, partialTicks); + } + + class SlotList extends GuiSlot { + public SlotList(final Minecraft mc, final int width, final int height, final int top, final int bottom, final int slotHeight) { + super(mc, width, height, top + 30, bottom, 18); + } + + @Override + public int getSize() { + return ProtocolCollection.values().length; + } + + @Override + public void elementClicked(final int i, final boolean b, final int i1, final int i2) { + final int protocolVersion = ProtocolCollection.values()[i].getVersion().getVersion(); + ViaMCP.getInstance().setVersion(protocolVersion); + ViaMCP.getInstance().asyncSlider.setVersion(protocolVersion); + } + + @Override + public boolean isSelected(final int i) { + return false; + } + + @Override + public void drawBackground() { + drawDefaultBackground(); + } + + @Override + public void drawSlot(final int i, final int i1, final int i2, final int i3, final int i4, final int i5) { + drawCenteredString(mc.fontRendererObj, (ViaMCP.getInstance().getVersion() == ProtocolCollection.values()[i].getVersion().getVersion() ? EnumChatFormatting.GREEN.toString() + EnumChatFormatting.BOLD : EnumChatFormatting.GRAY.toString()) + ProtocolCollection.getProtocolById(ProtocolCollection.values()[i].getVersion().getVersion()).getName(), width / 2, i2 + 2, -1); + GlStateManager.pushMatrix(); + GlStateManager.scale(0.5, 0.5, 0.5); + drawCenteredString(mc.fontRendererObj, "PVN: " + ProtocolCollection.getProtocolById(ProtocolCollection.values()[i].getVersion().getVersion()).getVersion(), width, (i2 + 2) * 2 + 20, -1); + GlStateManager.popMatrix(); + } + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/handler/CommonTransformer.java b/src/main/java/cc/paimonmc/viamcp/handler/CommonTransformer.java new file mode 100644 index 0000000000..cc40d811ff --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/handler/CommonTransformer.java @@ -0,0 +1,38 @@ +package cc.paimonmc.viamcp.handler; + +import com.viaversion.viaversion.util.PipelineUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; +import io.netty.handler.codec.MessageToMessageDecoder; + +import java.lang.reflect.InvocationTargetException; + +public class CommonTransformer { + public static final String HANDLER_DECODER_NAME = "via-decoder"; + public static final String HANDLER_ENCODER_NAME = "via-encoder"; + + public static void decompress(final ChannelHandlerContext ctx, final ByteBuf buf) throws InvocationTargetException { + final ChannelHandler handler = ctx.pipeline().get("decompress"); + final ByteBuf decompressed = handler instanceof MessageToMessageDecoder ? (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder) handler, ctx, buf).get(0) : (ByteBuf) PipelineUtil.callDecode((ByteToMessageDecoder) handler, ctx, buf).get(0); + + try { + buf.clear().writeBytes(decompressed); + } finally { + decompressed.release(); + } + } + + public static void compress(final ChannelHandlerContext ctx, final ByteBuf buf) throws Exception { + final ByteBuf compressed = ctx.alloc().buffer(); + + try { + PipelineUtil.callEncode((MessageToByteEncoder) ctx.pipeline().get("compress"), ctx, buf, compressed); + buf.clear().writeBytes(compressed); + } finally { + compressed.release(); + } + } +} \ No newline at end of file diff --git a/src/main/java/cc/paimonmc/viamcp/handler/MCPDecodeHandler.java b/src/main/java/cc/paimonmc/viamcp/handler/MCPDecodeHandler.java new file mode 100644 index 0000000000..4184fa28f0 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/handler/MCPDecodeHandler.java @@ -0,0 +1,100 @@ +package cc.paimonmc.viamcp.handler; + +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.exception.CancelCodecException; +import com.viaversion.viaversion.exception.CancelDecoderException; +import com.viaversion.viaversion.util.PipelineUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +@ChannelHandler.Sharable +public class MCPDecodeHandler extends MessageToMessageDecoder { + private final UserConnection info; + private boolean handledCompression; + private boolean skipDoubleTransform; + + public MCPDecodeHandler(final UserConnection info) { + this.info = info; + } + + public UserConnection getInfo() { + return info; + } + + // https://github.com/ViaVersion/ViaVersion/blob/master/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityDecodeHandler.java + @Override + protected void decode(final ChannelHandlerContext ctx, final ByteBuf bytebuf, final List out) throws Exception { + if (skipDoubleTransform) { + skipDoubleTransform = false; + out.add(bytebuf.retain()); + return; + } + + if (!info.checkIncomingPacket()) { + throw CancelDecoderException.generate(null); + } + + if (!info.shouldTransformPacket()) { + out.add(bytebuf.retain()); + return; + } + + final ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf); + + try { + final boolean needsCompress = handleCompressionOrder(ctx, transformedBuf); + + info.transformIncoming(transformedBuf, CancelDecoderException::generate); + + if (needsCompress) { + CommonTransformer.compress(ctx, transformedBuf); + skipDoubleTransform = true; + } + + out.add(transformedBuf.retain()); + } finally { + transformedBuf.release(); + } + } + + private boolean handleCompressionOrder(final ChannelHandlerContext ctx, final ByteBuf buf) throws InvocationTargetException { + if (handledCompression) { + return false; + } + + final int decoderIndex = ctx.pipeline().names().indexOf("decompress"); + + if (decoderIndex == -1) { + return false; + } + + handledCompression = true; + + if (decoderIndex > ctx.pipeline().names().indexOf("via-decoder")) { + // Need to decompress this packet due to bad order + CommonTransformer.decompress(ctx, buf); + final ChannelHandler encoder = ctx.pipeline().get("via-encoder"); + final ChannelHandler decoder = ctx.pipeline().get("via-decoder"); + ctx.pipeline().remove(encoder); + ctx.pipeline().remove(decoder); + ctx.pipeline().addAfter("compress", "via-encoder", encoder); + ctx.pipeline().addAfter("decompress", "via-decoder", decoder); + return true; + } + + return false; + } + + @Override + public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception { + if (PipelineUtil.containsCause(cause, CancelCodecException.class)) { + return; + } + super.exceptionCaught(ctx, cause); + } +} \ No newline at end of file diff --git a/src/main/java/cc/paimonmc/viamcp/handler/MCPEncodeHandler.java b/src/main/java/cc/paimonmc/viamcp/handler/MCPEncodeHandler.java new file mode 100644 index 0000000000..732328fc0d --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/handler/MCPEncodeHandler.java @@ -0,0 +1,87 @@ +package cc.paimonmc.viamcp.handler; + +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.exception.CancelCodecException; +import com.viaversion.viaversion.exception.CancelEncoderException; +import com.viaversion.viaversion.util.PipelineUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +@ChannelHandler.Sharable +public class MCPEncodeHandler extends MessageToMessageEncoder { + private final UserConnection info; + private boolean handledCompression; + + public MCPEncodeHandler(final UserConnection info) { + this.info = info; + } + + @Override + protected void encode(final ChannelHandlerContext ctx, final ByteBuf bytebuf, final List out) throws Exception { + if (!info.checkOutgoingPacket()) { + throw CancelEncoderException.generate(null); + } + + if (!info.shouldTransformPacket()) { + out.add(bytebuf.retain()); + return; + } + + final ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf); + + try { + final boolean needsCompress = handleCompressionOrder(ctx, transformedBuf); + + info.transformOutgoing(transformedBuf, CancelEncoderException::generate); + + if (needsCompress) { + CommonTransformer.compress(ctx, transformedBuf); + } + + out.add(transformedBuf.retain()); + } finally { + transformedBuf.release(); + } + } + + private boolean handleCompressionOrder(final ChannelHandlerContext ctx, final ByteBuf buf) throws InvocationTargetException { + if (handledCompression) { + return false; + } + + final int encoderIndex = ctx.pipeline().names().indexOf("compress"); + + if (encoderIndex == -1) { + return false; + } + handledCompression = true; + + if (encoderIndex > ctx.pipeline().names().indexOf("via-encoder")) { + // Need to decompress this packet due to bad order + CommonTransformer.decompress(ctx, buf); + final ChannelHandler encoder = ctx.pipeline().get("via-encoder"); + final ChannelHandler decoder = ctx.pipeline().get("via-decoder"); + ctx.pipeline().remove(encoder); + ctx.pipeline().remove(decoder); + ctx.pipeline().addAfter("compress", "via-encoder", encoder); + ctx.pipeline().addAfter("decompress", "via-decoder", decoder); + return true; + } + + return false; + } + + @Override + public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception { + if (PipelineUtil.containsCause(cause, CancelCodecException.class)) { + return; + } + + super.exceptionCaught(ctx, cause); + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/loader/MCPBackwardsLoader.java b/src/main/java/cc/paimonmc/viamcp/loader/MCPBackwardsLoader.java new file mode 100644 index 0000000000..bdad94301d --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/loader/MCPBackwardsLoader.java @@ -0,0 +1,34 @@ +package cc.paimonmc.viamcp.loader; + +import cc.paimonmc.viamcp.ViaMCP; +import com.viaversion.viabackwards.api.ViaBackwardsPlatform; + +import java.io.File; +import java.util.logging.Logger; + +public class MCPBackwardsLoader implements ViaBackwardsPlatform { + private final File file; + + public MCPBackwardsLoader(final File file) { + this.init(this.file = new File(file, "ViaBackwards")); + } + + @Override + public Logger getLogger() { + return ViaMCP.getInstance().getjLogger(); + } + + @Override + public void disable() { + } + + @Override + public boolean isOutdated() { + return false; + } + + @Override + public File getDataFolder() { + return new File(this.file, "config.yml"); + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/loader/MCPRewindLoader.java b/src/main/java/cc/paimonmc/viamcp/loader/MCPRewindLoader.java new file mode 100644 index 0000000000..2a13a90689 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/loader/MCPRewindLoader.java @@ -0,0 +1,21 @@ +package cc.paimonmc.viamcp.loader; + +import com.viaversion.viaversion.api.Via; +import de.gerrygames.viarewind.api.ViaRewindConfigImpl; +import de.gerrygames.viarewind.api.ViaRewindPlatform; + +import java.io.File; +import java.util.logging.Logger; + +public class MCPRewindLoader implements ViaRewindPlatform { + public MCPRewindLoader(final File file) { + final ViaRewindConfigImpl conf = new ViaRewindConfigImpl(file.toPath().resolve("ViaRewind").resolve("config.yml").toFile()); + conf.reloadConfig(); + this.init(conf); + } + + @Override + public Logger getLogger() { + return Via.getPlatform().getLogger(); + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/loader/MCPViaLoader.java b/src/main/java/cc/paimonmc/viamcp/loader/MCPViaLoader.java new file mode 100644 index 0000000000..cd933f0526 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/loader/MCPViaLoader.java @@ -0,0 +1,31 @@ +package cc.paimonmc.viamcp.loader; + +import cc.paimonmc.viamcp.ViaMCP; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.api.platform.ViaPlatformLoader; +import com.viaversion.viaversion.api.protocol.version.VersionProvider; +import com.viaversion.viaversion.bungee.providers.BungeeMovementTransmitter; +import com.viaversion.viaversion.protocols.base.BaseVersionProvider; +import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider; + +public class MCPViaLoader implements ViaPlatformLoader { + @Override + public void load() { + Via.getManager().getProviders().use(MovementTransmitterProvider.class, new BungeeMovementTransmitter()); + Via.getManager().getProviders().use(VersionProvider.class, new BaseVersionProvider() { + @Override + public int getClosestServerProtocol(final UserConnection connection) throws Exception { + if (connection.isClientSide()) { + return ViaMCP.getInstance().getVersion(); + } + + return super.getClosestServerProtocol(connection); + } + }); + } + + @Override + public void unload() { + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/platform/MCPViaAPI.java b/src/main/java/cc/paimonmc/viamcp/platform/MCPViaAPI.java new file mode 100644 index 0000000000..c1fedad6e2 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/platform/MCPViaAPI.java @@ -0,0 +1,8 @@ +package cc.paimonmc.viamcp.platform; + +import com.viaversion.viaversion.ViaAPIBase; + +import java.util.UUID; + +public class MCPViaAPI extends ViaAPIBase { +} diff --git a/src/main/java/cc/paimonmc/viamcp/platform/MCPViaConfig.java b/src/main/java/cc/paimonmc/viamcp/platform/MCPViaConfig.java new file mode 100644 index 0000000000..738e80de9d --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/platform/MCPViaConfig.java @@ -0,0 +1,63 @@ +package cc.paimonmc.viamcp.platform; + +import com.viaversion.viaversion.configuration.AbstractViaConfig; + +import java.io.File; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class MCPViaConfig extends AbstractViaConfig { + private static final List UNSUPPORTED = Arrays.asList("anti-xray-patch", "bungee-ping-interval", "bungee-ping-save", "bungee-servers", "quick-move-action-fix", "nms-player-ticking", "velocity-ping-interval", "velocity-ping-save", "velocity-servers", "blockconnection-method", "change-1_9-hitbox", "change-1_14-hitbox"); + + public MCPViaConfig(final File configFile) { + super(configFile); + reloadConfig(); + } + + @Override + public URL getDefaultConfigURL() { + return getClass().getClassLoader().getResource("assets/viaversion/config.yml"); + } + + @Override + protected void handleConfig(final Map config) { + // Is not needed! + } + + @Override + public List getUnsupportedOptions() { + return UNSUPPORTED; + } + + @Override + public boolean isAntiXRay() { + return false; + } + + @Override + public boolean isNMSPlayerTicking() { + return false; + } + + @Override + public boolean is1_12QuickMoveActionFix() { + return false; + } + + @Override + public String getBlockConnectionMethod() { + return "packet"; + } + + @Override + public boolean is1_9HitboxFix() { + return false; + } + + @Override + public boolean is1_14HitboxFix() { + return false; + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/platform/MCPViaInjector.java b/src/main/java/cc/paimonmc/viamcp/platform/MCPViaInjector.java new file mode 100644 index 0000000000..edbf7049ba --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/platform/MCPViaInjector.java @@ -0,0 +1,39 @@ +package cc.paimonmc.viamcp.platform; + +import cc.paimonmc.viamcp.ViaMCP; +import cc.paimonmc.viamcp.handler.CommonTransformer; +import com.viaversion.viaversion.api.platform.ViaInjector; +import com.viaversion.viaversion.libs.gson.JsonObject; + +public class MCPViaInjector implements ViaInjector { + @Override + public void inject() { + // In a nutshell, this is not forge + } + + @Override + public void uninject() { + // Update! Still not forge! + } + + @Override + public int getServerProtocolVersion() { + return ViaMCP.PROTOCOL_VERSION; + } + + @Override + public String getEncoderName() { + return CommonTransformer.HANDLER_ENCODER_NAME; + } + + @Override + public String getDecoderName() { + return CommonTransformer.HANDLER_DECODER_NAME; + } + + @Override + public JsonObject getDump() { + final JsonObject obj = new JsonObject(); + return obj; + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/platform/MCPViaPlatform.java b/src/main/java/cc/paimonmc/viamcp/platform/MCPViaPlatform.java new file mode 100644 index 0000000000..3011991597 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/platform/MCPViaPlatform.java @@ -0,0 +1,165 @@ +package cc.paimonmc.viamcp.platform; + +import cc.paimonmc.viamcp.ViaMCP; +import cc.paimonmc.viamcp.utils.FutureTaskId; +import cc.paimonmc.viamcp.utils.JLoggerToLog4j; +import com.viaversion.viaversion.api.ViaAPI; +import com.viaversion.viaversion.api.command.ViaCommandSender; +import com.viaversion.viaversion.api.configuration.ConfigurationProvider; +import com.viaversion.viaversion.api.configuration.ViaVersionConfig; +import com.viaversion.viaversion.api.platform.PlatformTask; +import com.viaversion.viaversion.api.platform.ViaPlatform; +import com.viaversion.viaversion.libs.gson.JsonObject; +import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; +import org.apache.logging.log4j.LogManager; + +import java.io.File; +import java.nio.file.Path; +import java.util.UUID; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +public class MCPViaPlatform implements ViaPlatform { + private final Logger logger = new JLoggerToLog4j(LogManager.getLogger("ViaVersion")); + + private final MCPViaConfig config; + private final File dataFolder; + private final ViaAPI api; + + public MCPViaPlatform(final File dataFolder) { + final Path configDir = dataFolder.toPath().resolve("ViaVersion"); + config = new MCPViaConfig(configDir.resolve("viaversion.yml").toFile()); + this.dataFolder = configDir.toFile(); + api = new MCPViaAPI(); + } + + public static String legacyToJson(final String legacy) { + return GsonComponentSerializer.gson().serialize(LegacyComponentSerializer.legacySection().deserialize(legacy)); + } + + @Override + public Logger getLogger() { + return logger; + } + + @Override + public String getPlatformName() { + return "ViaMCP"; + } + + @Override + public String getPlatformVersion() { + return String.valueOf(ViaMCP.PROTOCOL_VERSION); + } + + @Override + public String getPluginVersion() { + return "4.1.1"; + } + + @Override + public FutureTaskId runAsync(final Runnable runnable) { + return new FutureTaskId(CompletableFuture.runAsync(runnable, ViaMCP.getInstance().getAsyncExecutor()).exceptionally(throwable -> + { + if (!(throwable instanceof CancellationException)) { + throwable.printStackTrace(); + } + + return null; + }) + ); + } + + @Override + public FutureTaskId runSync(final Runnable runnable) { + return new FutureTaskId(ViaMCP.getInstance().getEventLoop().submit(runnable).addListener(errorLogger())); + } + + @Override + public PlatformTask runSync(final Runnable runnable, final long ticks) { + return new FutureTaskId(ViaMCP.getInstance().getEventLoop().schedule(() -> runSync(runnable), ticks * 50, TimeUnit.MILLISECONDS).addListener(errorLogger())); + } + + @Override + public PlatformTask runRepeatingSync(final Runnable runnable, final long ticks) { + return new FutureTaskId(ViaMCP.getInstance().getEventLoop().scheduleAtFixedRate(() -> runSync(runnable), 0, ticks * 50, TimeUnit.MILLISECONDS).addListener(errorLogger())); + } + + private > GenericFutureListener errorLogger() { + return future -> + { + if (!future.isCancelled() && future.cause() != null) { + future.cause().printStackTrace(); + } + }; + } + + @Override + public ViaCommandSender[] getOnlinePlayers() { + return new ViaCommandSender[1337]; // What the fuck + } + + private ViaCommandSender[] getServerPlayers() { + return new ViaCommandSender[1337]; // What the fuck 2: Electric Boogaloo + } + + @Override + public void sendMessage(final UUID uuid, final String s) { + // Don't even know why this needs to be overridden + } + + @Override + public boolean kickPlayer(final UUID uuid, final String s) { + return false; + } + + @Override + public boolean isPluginEnabled() { + return true; + } + + @Override + public ViaAPI getApi() { + return api; + } + + @Override + public ViaVersionConfig getConf() { + return config; + } + + @Override + public ConfigurationProvider getConfigurationProvider() { + return config; + } + + @Override + public File getDataFolder() { + return dataFolder; + } + + @Override + public void onReload() { + logger.info("ViaVersion was reloaded? (How did that happen)"); + } + + @Override + public JsonObject getDump() { + return new JsonObject(); + } + + @Override + public boolean isOldClientsAllowed() { + return true; + } + + @Override + public boolean hasPlugin(final String s) { + return false; + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/protocols/ProtocolCollection.java b/src/main/java/cc/paimonmc/viamcp/protocols/ProtocolCollection.java new file mode 100644 index 0000000000..e276cd9d36 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/protocols/ProtocolCollection.java @@ -0,0 +1,113 @@ +package cc.paimonmc.viamcp.protocols; + + +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; + +public enum ProtocolCollection { + /* 1.19.x */ + R_1_19_1(ProtocolVersion.v1_19_1, ProtocolInfoCollection.R1_19_1), + R_1_19(ProtocolVersion.v1_19, ProtocolInfoCollection.R1_19), + + /* 1.18.x */ + R1_18_2(ProtocolVersion.v1_18_2, ProtocolInfoCollection.R1_18_2), + R1_18(ProtocolVersion.v1_18, ProtocolInfoCollection.R1_18), + + /* 1.17.x */ + R1_17_1(ProtocolVersion.v1_17_1, ProtocolInfoCollection.R1_17_1), + R1_17(ProtocolVersion.v1_17, ProtocolInfoCollection.R1_17), + + /* 1.16.x */ + R1_16_4(ProtocolVersion.v1_16_4, ProtocolInfoCollection.R1_16_4), + R1_16_3(ProtocolVersion.v1_16_3, ProtocolInfoCollection.R1_16_3), + R1_16_2(ProtocolVersion.v1_16_2, ProtocolInfoCollection.R1_16_2), + R1_16_1(ProtocolVersion.v1_16_1, ProtocolInfoCollection.R1_16_1), + R1_16(ProtocolVersion.v1_16, ProtocolInfoCollection.R1_16), + + /* 1.15.x */ + R1_15_2(ProtocolVersion.v1_15_2, ProtocolInfoCollection.R1_15_2), + R1_15_1(ProtocolVersion.v1_15_1, ProtocolInfoCollection.R1_15_1), + R1_15(ProtocolVersion.v1_15, ProtocolInfoCollection.R1_15), + + /* 1.14.x */ + R1_14_4(ProtocolVersion.v1_14_4, ProtocolInfoCollection.R1_14_4), + R1_14_3(ProtocolVersion.v1_14_3, ProtocolInfoCollection.R1_14_3), + R1_14_2(ProtocolVersion.v1_14_2, ProtocolInfoCollection.R1_14_2), + R1_14_1(ProtocolVersion.v1_14_1, ProtocolInfoCollection.R1_14_1), + R1_14(ProtocolVersion.v1_14, ProtocolInfoCollection.R1_14), + + /* 1.13.x */ + R1_13_2(ProtocolVersion.v1_13_2, ProtocolInfoCollection.R1_13_2), + R1_13_1(ProtocolVersion.v1_13_1, ProtocolInfoCollection.R1_13_1), + R1_13(ProtocolVersion.v1_13, ProtocolInfoCollection.R1_13), + + /* 1.12.x */ + R1_12_2(ProtocolVersion.v1_12_2, ProtocolInfoCollection.R1_12_2), + R1_12_1(ProtocolVersion.v1_12_1, ProtocolInfoCollection.R1_12_1), + R1_12(ProtocolVersion.v1_12, ProtocolInfoCollection.R1_12), + + /* 1.11.x */ + R1_11_1(ProtocolVersion.v1_11_1, ProtocolInfoCollection.R1_11_1), + R1_11(ProtocolVersion.v1_11, ProtocolInfoCollection.R1_11), + + /* 1.10.x */ + R1_10(ProtocolVersion.v1_10, ProtocolInfoCollection.R1_10), + + /* 1.9.x */ + R1_9_3(ProtocolVersion.v1_9_3, ProtocolInfoCollection.R1_9_3), + R1_9_2(ProtocolVersion.v1_9_2, ProtocolInfoCollection.R1_9_2), + R1_9_1(ProtocolVersion.v1_9_1, ProtocolInfoCollection.R1_9_1), + R1_9(ProtocolVersion.v1_9, ProtocolInfoCollection.R1_9), + + /* 1.8.x */ + R1_8(ProtocolVersion.v1_8, ProtocolInfoCollection.R1_8), + + /* 1.7.x */ + R1_7_6(ProtocolVersion.v1_7_6, ProtocolInfoCollection.R1_7_6), + R1_7(ProtocolVersion.v1_7_1, ProtocolInfoCollection.R1_7); + + private final ProtocolVersion version; + private final ProtocolInfo info; + + ProtocolCollection(final ProtocolVersion version, final ProtocolInfo info) { + this.version = version; + this.info = info; + } + + public ProtocolVersion getVersion() { + return version; + } + + public ProtocolInfo getInfo() { + return info; + } + + public static ProtocolCollection getProtocolCollectionById(final int id) { + for (final ProtocolCollection coll : values()) { + if (coll.getVersion().getVersion() == id) { + return coll; + } + } + + return null; + } + + public static ProtocolVersion getProtocolById(final int id) { + for (final ProtocolCollection coll : values()) { + if (coll.getVersion().getVersion() == id) { + return coll.getVersion(); + } + } + + return null; + } + + public static ProtocolInfo getProtocolInfoById(final int id) { + for (final ProtocolCollection coll : values()) { + if (coll.getVersion().getVersion() == id) { + return coll.getInfo(); + } + } + + return null; + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/protocols/ProtocolInfo.java b/src/main/java/cc/paimonmc/viamcp/protocols/ProtocolInfo.java new file mode 100644 index 0000000000..c12a5a31e0 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/protocols/ProtocolInfo.java @@ -0,0 +1,25 @@ +package cc.paimonmc.viamcp.protocols; + +public class ProtocolInfo { + private final String name; + private final String description; + private final String releaseDate; + + public ProtocolInfo(final String name, final String description, final String releaseDate) { + this.name = name; + this.description = description; + this.releaseDate = releaseDate; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getReleaseDate() { + return releaseDate; + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/protocols/ProtocolInfoCollection.java b/src/main/java/cc/paimonmc/viamcp/protocols/ProtocolInfoCollection.java new file mode 100644 index 0000000000..480409c8db --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/protocols/ProtocolInfoCollection.java @@ -0,0 +1,145 @@ +package cc.paimonmc.viamcp.protocols; + +public class ProtocolInfoCollection { + private static final String NO_DESC = "No Description Available"; + + public static ProtocolInfo R1_19_1 = new ProtocolInfo( + "The Wild Update", NO_DESC, "July 27, 2022" + ); + + public static ProtocolInfo R1_19 = new ProtocolInfo( + "The Wild Update", NO_DESC, "June 7, 2022" + ); + + public static ProtocolInfo R1_18_2 = new ProtocolInfo( + "Caves & Cliffs: Part II", NO_DESC, "February 28, 2022" + ); + + public static ProtocolInfo R1_18 = new ProtocolInfo( + "Caves & Cliffs: Part II", NO_DESC, "November 30, 2021 - December 10, 2021" + ); + + public static ProtocolInfo R1_17_1 = new ProtocolInfo( + "Caves & Cliffs: Part I", NO_DESC, "July 6, 2021" + ); + + public static ProtocolInfo R1_17 = new ProtocolInfo( + "Caves & Cliffs: Part I", NO_DESC, "June 8, 2021" + ); + + public static ProtocolInfo R1_16_4 = new ProtocolInfo( + "Nether Update", NO_DESC, "November 2, 2020 - January 13, 2021" + ); + + public static ProtocolInfo R1_16_3 = new ProtocolInfo( + "Nether Update", NO_DESC, "September 7, 2020" + ); + + public static ProtocolInfo R1_16_2 = new ProtocolInfo( + "Nether Update", NO_DESC, "August 11, 2020" + ); + + public static ProtocolInfo R1_16_1 = new ProtocolInfo( + "Nether Update", NO_DESC, "June 24, 2020" + ); + + public static ProtocolInfo R1_16 = new ProtocolInfo( + "Nether Update", NO_DESC, "June 23, 2020" + ); + + public static ProtocolInfo R1_15_2 = new ProtocolInfo( + "Buzzy Bees", NO_DESC, "January 21, 2020" + ); + + public static ProtocolInfo R1_15_1 = new ProtocolInfo( + "Buzzy Bees", NO_DESC, "December 17, 2019" + ); + + public static ProtocolInfo R1_15 = new ProtocolInfo( + "Buzzy Bees", NO_DESC, "December 10, 2019" + ); + + public static ProtocolInfo R1_14_4 = new ProtocolInfo( + "Village & Pillage", NO_DESC, "July 19, 2019" + ); + + public static ProtocolInfo R1_14_3 = new ProtocolInfo( + "Village & Pillage", NO_DESC, "June 24, 2019" + ); + + public static ProtocolInfo R1_14_2 = new ProtocolInfo( + "Village & Pillage", NO_DESC, "May 27, 2019" + ); + + public static ProtocolInfo R1_14_1 = new ProtocolInfo( + "Village & Pillage", NO_DESC, "May 13, 2019" + ); + + public static ProtocolInfo R1_14 = new ProtocolInfo( + "Village & Pillage", NO_DESC, "April 23, 2019" + ); + + public static ProtocolInfo R1_13_2 = new ProtocolInfo( + "Update Aquatic", NO_DESC, "October 22, 2018" + ); + + public static ProtocolInfo R1_13_1 = new ProtocolInfo( + "Update Aquatic", NO_DESC, "August 22, 2018" + ); + + public static ProtocolInfo R1_13 = new ProtocolInfo( + "Update Aquatic", NO_DESC, "July 18, 2018" + ); + + public static ProtocolInfo R1_12_2 = new ProtocolInfo( + "World of Color Update", NO_DESC, "September 18, 2017" + ); + + public static ProtocolInfo R1_12_1 = new ProtocolInfo( + "World of Color Update", NO_DESC, "August 3, 2017" + ); + + public static ProtocolInfo R1_12 = new ProtocolInfo( + "World of Color Update", NO_DESC, "June 7, 2017" + ); + + public static ProtocolInfo R1_11_1 = new ProtocolInfo( + "Exploration Update", NO_DESC, "December 20, 2016 - December 21, 2016" + ); + + public static ProtocolInfo R1_11 = new ProtocolInfo( + "Exploration Update", NO_DESC, "November 14, 2016" + ); + + public static ProtocolInfo R1_10 = new ProtocolInfo( + "Frostburn Update", NO_DESC, "June 8, 2016 - June 23, 2016" + ); + + public static ProtocolInfo R1_9_3 = new ProtocolInfo( + "Combat Update", NO_DESC, "May 10, 2016" + ); + + public static ProtocolInfo R1_9_2 = new ProtocolInfo( + "Combat Update", NO_DESC, "March 30, 2016" + ); + + public static ProtocolInfo R1_9_1 = new ProtocolInfo( + "Combat Update", NO_DESC, "March 30, 2016" + ); + + public static ProtocolInfo R1_9 = new ProtocolInfo( + "Combat Update", NO_DESC, "February 29, 2016" + ); + + public static ProtocolInfo R1_8 = new ProtocolInfo( + "Bountiful Update", NO_DESC, "September 2, 2014 - December 9, 2015" + ); + + public static ProtocolInfo R1_7_6 = new ProtocolInfo( + "The Update that Changed the World", NO_DESC, "April 9, 2014 - June 26, 2014" + ); + + public static ProtocolInfo R1_7 = new ProtocolInfo( + "The Update that Changed the World", NO_DESC, "October 22, 2013 - February 26, 2014" + ); +} \ No newline at end of file diff --git a/src/main/java/cc/paimonmc/viamcp/utils/AttackOrder.java b/src/main/java/cc/paimonmc/viamcp/utils/AttackOrder.java new file mode 100644 index 0000000000..37fed7c0a4 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/utils/AttackOrder.java @@ -0,0 +1,42 @@ +package cc.paimonmc.viamcp.utils; + +import cc.paimonmc.viamcp.ViaMCP; +import cc.paimonmc.viamcp.protocols.ProtocolCollection; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.MovingObjectPosition; + +public class AttackOrder { + private static final Minecraft mc = Minecraft.getMinecraft(); + + public static final int VER_1_8_ID = 47; + + public static void sendConditionalSwing(final MovingObjectPosition mop) { + if (mop != null && mop.typeOfHit != MovingObjectPosition.MovingObjectType.ENTITY) { + mc.thePlayer.swingItem(); + } + } + + public static void sendFixedAttack(final EntityPlayer entityIn, final Entity target) { + // Using this instead of ViaMCP.PROTOCOL_VERSION so does not need to be changed between 1.8.x and 1.12.2 base + // getVersion() can be null, but not in this case, as ID 47 exists, if not removed + if (ViaMCP.getInstance().getVersion() <= ProtocolCollection.getProtocolById(VER_1_8_ID).getVersion()) { + send1_8Attack(entityIn, target); + } else { + send1_9Attack(entityIn, target); + } + } + + private static void send1_8Attack(EntityPlayer entityIn, Entity target) + { + mc.thePlayer.swingItem(); + mc.playerController.attackEntity(entityIn, target); + } + + private static void send1_9Attack(EntityPlayer entityIn, Entity target) + { + mc.playerController.attackEntity(entityIn, target); + mc.thePlayer.swingItem(); + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/utils/FixedSoundEngine.java b/src/main/java/cc/paimonmc/viamcp/utils/FixedSoundEngine.java new file mode 100644 index 0000000000..0aba1785a3 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/utils/FixedSoundEngine.java @@ -0,0 +1,83 @@ +package cc.paimonmc.viamcp.utils; + +import cc.paimonmc.viamcp.ViaMCP; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +public class FixedSoundEngine { + private static final Minecraft mc = Minecraft.getMinecraft(); + + /** + * Fix for block breaking sounds on protocols above 1.8.x + */ + public static boolean destroyBlock(final World world, final BlockPos pos, final boolean dropBlock) { + final IBlockState iblockstate = world.getBlockState(pos); + final Block block = iblockstate.getBlock(); + + // Moving playAusSFX out of else-statement to play sound correctly (For some reason block.getMaterial() always returns Material.air on 1.9+ protocols) + // This should also function correctly on 1.8.x protocol, so no need for base version checks + world.playAuxSFX(2001, pos, Block.getStateId(iblockstate)); + + if (block.getMaterial() == Material.air) { + return false; + } else { + if (dropBlock) { + block.dropBlockAsItem(world, pos, iblockstate, 0); + } + + return world.setBlockState(pos, Blocks.air.getDefaultState(), 3); + } + } + + /** + * Fix for block placing sounds on protocols above 1.8.x + */ + public static boolean onItemUse(final ItemBlock iblock, final ItemStack stack, final EntityPlayer playerIn, final World worldIn, BlockPos pos, final EnumFacing side, final float hitX, final float hitY, final float hitZ) { + final IBlockState iblockstate = worldIn.getBlockState(pos); + final Block block = iblockstate.getBlock(); + + if (!block.isReplaceable(worldIn, pos)) { + pos = pos.offset(side); + } + + if (stack.stackSize == 0) { + return false; + } else if (!playerIn.canPlayerEdit(pos, side, stack)) { + return false; + } else if (worldIn.canBlockBePlaced(iblock.getBlock(), pos, false, side, null, stack)) { + final int i = iblock.getMetadata(stack.getMetadata()); + IBlockState iblockstate1 = iblock.getBlock().onBlockPlaced(worldIn, pos, side, hitX, hitY, hitZ, i, playerIn); + + if (worldIn.setBlockState(pos, iblockstate1, 3)) { + iblockstate1 = worldIn.getBlockState(pos); + + if (iblockstate1.getBlock() == iblock.getBlock()) { + ItemBlock.setTileEntityNBT(worldIn, playerIn, pos, stack); + iblock.getBlock().onBlockPlacedBy(worldIn, pos, iblockstate1, playerIn, stack); + } + + if (ViaMCP.getInstance().getVersion() != ViaMCP.PROTOCOL_VERSION) { + // Using playSoundAtPos instead of playSoundEffect (I have no understanding as to why playSoundEffect is not functioning properly on 1.9+ protocols) + mc.theWorld.playSoundAtPos(pos.add(0.5, 0.5, 0.5), iblock.getBlock().stepSound.getPlaceSound(), (iblock.getBlock().stepSound.getVolume() + 1.0F) / 2.0F, iblock.getBlock().stepSound.getFrequency() * 0.8F, false); + } else { + worldIn.playSoundEffect((float) pos.getX() + 0.5F, (float) pos.getY() + 0.5F, (float) pos.getZ() + 0.5F, iblock.getBlock().stepSound.getPlaceSound(), (iblock.getBlock().stepSound.getVolume() + 1.0F) / 2.0F, iblock.getBlock().stepSound.getFrequency() * 0.8F); + } + + --stack.stackSize; + } + + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/utils/FutureTaskId.java b/src/main/java/cc/paimonmc/viamcp/utils/FutureTaskId.java new file mode 100644 index 0000000000..b9f7ea41ca --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/utils/FutureTaskId.java @@ -0,0 +1,24 @@ +package cc.paimonmc.viamcp.utils; + + +import com.viaversion.viaversion.api.platform.PlatformTask; + +import java.util.concurrent.Future; + +public class FutureTaskId implements PlatformTask> { + private final Future object; + + public FutureTaskId(final Future object) { + this.object = object; + } + + @Override + public Future getObject() { + return object; + } + + @Override + public void cancel() { + object.cancel(false); + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/utils/JLoggerToLog4j.java b/src/main/java/cc/paimonmc/viamcp/utils/JLoggerToLog4j.java new file mode 100644 index 0000000000..83f6c24db3 --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/utils/JLoggerToLog4j.java @@ -0,0 +1,65 @@ +package cc.paimonmc.viamcp.utils; + +import java.text.MessageFormat; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +public class JLoggerToLog4j extends Logger { + private final org.apache.logging.log4j.Logger base; + + public JLoggerToLog4j(final org.apache.logging.log4j.Logger logger) { + super("logger", null); + this.base = logger; + } + + public void log(final LogRecord record) { + this.log(record.getLevel(), record.getMessage()); + } + + public void log(final Level level, final String msg) { + if (level == Level.FINE) { + this.base.debug(msg); + } else if (level == Level.WARNING) { + this.base.warn(msg); + } else if (level == Level.SEVERE) { + this.base.error(msg); + } else if (level == Level.INFO) { + this.base.info(msg); + } else { + this.base.trace(msg); + } + } + + public void log(final Level level, final String msg, final Object param1) { + if (level == Level.FINE) { + this.base.debug(msg, param1); + } else if (level == Level.WARNING) { + this.base.warn(msg, param1); + } else if (level == Level.SEVERE) { + this.base.error(msg, param1); + } else if (level == Level.INFO) { + this.base.info(msg, param1); + } else { + this.base.trace(msg, param1); + } + } + + public void log(final Level level, final String msg, final Object[] params) { + log(level, MessageFormat.format(msg, params)); + } + + public void log(final Level level, final String msg, final Throwable params) { + if (level == Level.FINE) { + this.base.debug(msg, params); + } else if (level == Level.WARNING) { + this.base.warn(msg, params); + } else if (level == Level.SEVERE) { + this.base.error(msg, params); + } else if (level == Level.INFO) { + this.base.info(msg, params); + } else { + this.base.trace(msg, params); + } + } +} diff --git a/src/main/java/cc/paimonmc/viamcp/utils/NettyUtil.java b/src/main/java/cc/paimonmc/viamcp/utils/NettyUtil.java new file mode 100644 index 0000000000..bce472ecec --- /dev/null +++ b/src/main/java/cc/paimonmc/viamcp/utils/NettyUtil.java @@ -0,0 +1,28 @@ +package cc.paimonmc.viamcp.utils; + +import cc.paimonmc.viamcp.handler.CommonTransformer; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelPipeline; + +public class NettyUtil { + public static ChannelPipeline decodeEncodePlacement(final ChannelPipeline instance, String base, final String newHandler, final ChannelHandler handler) { + switch (base) { + case "decoder": { + if (instance.get(CommonTransformer.HANDLER_DECODER_NAME) != null) { + base = CommonTransformer.HANDLER_DECODER_NAME; + } + + break; + } + case "encoder": { + if (instance.get(CommonTransformer.HANDLER_ENCODER_NAME) != null) { + base = CommonTransformer.HANDLER_ENCODER_NAME; + } + + break; + } + } + + return instance.addBefore(base, newHandler, handler); + } +} diff --git a/src/main/java/net/ccbluex/liquidbounce/file/configs/SpecialConfig.kt b/src/main/java/net/ccbluex/liquidbounce/file/configs/SpecialConfig.kt index b309b0514d..f19ab8e1ef 100644 --- a/src/main/java/net/ccbluex/liquidbounce/file/configs/SpecialConfig.kt +++ b/src/main/java/net/ccbluex/liquidbounce/file/configs/SpecialConfig.kt @@ -26,8 +26,6 @@ class SpecialConfig(file: File) : FileConfig(file) { ClientFixes.blockFML = true ClientFixes.blockProxyPacket = true ClientFixes.blockPayloadPackets = true - ServerSpoof.enable = false - ServerSpoof.address = "redesky.com" GuiBackground.enabled = true GuiBackground.particles = false GuiAltManager.randomAltField.text = "F%nD%nP%n_%s%s%s" @@ -70,16 +68,6 @@ class SpecialConfig(file: File) : FileConfig(file) { ClientFixes.clientBrand = jsonValue.get("ClientBrand").getAsString(); } } - if (json.has("serverspoof")) { - val jsonValue = json.getAsJsonObject("serverspoof") - - if (jsonValue.has("enable")) { - ServerSpoof.enable = jsonValue.get("enable").asBoolean - } - if (jsonValue.has("address")) { - ServerSpoof.address = jsonValue.get("address").asString - } - } if (json.has("proxy")) { val jsonValue = json.getAsJsonObject("proxy") @@ -120,11 +108,6 @@ class SpecialConfig(file: File) : FileConfig(file) { antiForgeJson.addProperty("block-payload", ClientFixes.blockPayloadPackets) json.add("anti-forge", antiForgeJson) - val serverSpoofJson = JsonObject() - serverSpoofJson.addProperty("enable", ServerSpoof.enable) - serverSpoofJson.addProperty("address", ServerSpoof.address) - json.add("serverspoof", serverSpoofJson) - val proxyJson = JsonObject() proxyJson.addProperty("enable", ProxyManager.isEnable) proxyJson.addProperty("address", ProxyManager.proxy) diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/client/MixinMinecraft.java b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/client/MixinMinecraft.java index b5d9dee43b..f45a50f3c0 100644 --- a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/client/MixinMinecraft.java +++ b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/client/MixinMinecraft.java @@ -4,6 +4,8 @@ * https://github.com/SkidderMC/FDPClient/ */ package net.ccbluex.liquidbounce.injection.forge.mixins.client; +import cc.paimonmc.viamcp.ViaMCP; +import cc.paimonmc.viamcp.utils.AttackOrder; import net.ccbluex.liquidbounce.FDPClient; import net.ccbluex.liquidbounce.event.*; import net.ccbluex.liquidbounce.features.module.modules.client.SoundModule; @@ -25,11 +27,13 @@ import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.main.GameConfiguration; import net.minecraft.client.multiplayer.PlayerControllerMP; import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.client.particle.EffectRenderer; import net.minecraft.client.settings.GameSettings; import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.BlockPos; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.Util; @@ -100,6 +104,10 @@ public abstract class MixinMinecraft { private float prevYaw = 0.0f; + @Inject(method = "", at = @At("RETURN")) + public void injectConstructor(GameConfiguration p_i45547_1_, CallbackInfo ci) { + ViaMCP.staticInit(); + } @Inject(method = "run", at = @At("HEAD")) private void init(CallbackInfo callbackInfo) { @@ -198,6 +206,14 @@ private void clickMouse(CallbackInfo callbackInfo) { leftClickCounter = 0; } + @Redirect( + method = "clickMouse", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/PlayerControllerMP;attackEntity(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/entity/Entity;)V") + ) + public void fixAttackOrder_VanillaAttack(PlayerControllerMP controller, EntityPlayer player, Entity e) { + AttackOrder.sendFixedAttack(this.thePlayer, this.objectMouseOver.entityHit); + } + @Inject(method = "middleClickMouse", at = @At("HEAD")) private void middleClickMouse(CallbackInfo ci) { CPSCounter.registerClick(CPSCounter.MouseButton.MIDDLE); diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/gui/MixinGuiMultiplayer.java b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/gui/MixinGuiMultiplayer.java index e413ae3632..49cc469b9d 100644 --- a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/gui/MixinGuiMultiplayer.java +++ b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/gui/MixinGuiMultiplayer.java @@ -5,8 +5,8 @@ */ package net.ccbluex.liquidbounce.injection.forge.mixins.gui; +import cc.paimonmc.viamcp.gui.AsyncVersionSlider; import net.ccbluex.liquidbounce.ui.client.GuiProxySelect; -import net.ccbluex.liquidbounce.ui.client.GuiServerSpoof; import net.ccbluex.liquidbounce.ui.elements.ToolDropdown; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; @@ -25,19 +25,21 @@ public abstract class MixinGuiMultiplayer extends MixinGuiScreen { @Inject(method = "initGui", at = @At("RETURN")) private void initGui(CallbackInfo callbackInfo) { buttonList.add(toolButton = new GuiButton(997, 5, 8, 138, 20, "Tools")); - buttonList.add(new GuiButton(998, width - 104, 8, 98, 20, "%ui.serverSpoof%")); + // buttonList.add(new GuiButton(998, width - 104, 8, 98, 20, "%ui.serverSpoof%")); buttonList.add(new GuiButton(999, width - 208, 8, 98, 20, "Proxy")); } + @Inject(method = "createButtons",at = @At("HEAD")) + public void createButtons(CallbackInfo ci){ + buttonList.add(new AsyncVersionSlider(-1, this.width - 104, 8, 98, 20)); + } + @Inject(method = "actionPerformed", at = @At("HEAD")) private void actionPerformed(GuiButton button, CallbackInfo callbackInfo) { if (button.id == 997) ToolDropdown.toggleState(); switch(button.id) { - case 998: - mc.displayGuiScreen(new GuiServerSpoof((GuiScreen) (Object) this)); - break; case 999: mc.displayGuiScreen(new GuiProxySelect((GuiScreen) (Object) this)); break; diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/network/MixinNetworkManagerChInit.java b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/network/MixinNetworkManagerChInit.java new file mode 100644 index 0000000000..5ee4a3ba15 --- /dev/null +++ b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/network/MixinNetworkManagerChInit.java @@ -0,0 +1,33 @@ +/* + * FDPClient Hacked Client + * A free open source mixin-based injection hacked client for Minecraft using Minecraft Forge by LiquidBounce. + * https://github.com/SkidderMC/FDPClient/ + */ +package net.ccbluex.liquidbounce.injection.forge.mixins.network; + +import cc.paimonmc.viamcp.ViaMCP; +import cc.paimonmc.viamcp.handler.CommonTransformer; +import cc.paimonmc.viamcp.handler.MCPDecodeHandler; +import cc.paimonmc.viamcp.handler.MCPEncodeHandler; +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.connection.UserConnectionImpl; +import com.viaversion.viaversion.protocol.ProtocolPipelineImpl; +import io.netty.channel.Channel; +import io.netty.channel.socket.SocketChannel; +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(targets = "net.minecraft.network.NetworkManager$5") +public abstract class MixinNetworkManagerChInit { + + @Inject(method={"initChannel"}, at={@At(value="TAIL")}, remap=false) + private void onInitChannel(Channel p_initChannel_1_, CallbackInfo callbackInfo) { + if (p_initChannel_1_ instanceof SocketChannel && ViaMCP.getInstance().getVersion() != ViaMCP.PROTOCOL_VERSION) { + UserConnection user = new UserConnectionImpl(p_initChannel_1_, true); + new ProtocolPipelineImpl(user); + p_initChannel_1_.pipeline().addBefore("encoder", CommonTransformer.HANDLER_ENCODER_NAME, new MCPEncodeHandler(user)).addBefore("decoder", CommonTransformer.HANDLER_DECODER_NAME, new MCPDecodeHandler(user)); + } + } +} diff --git a/src/main/java/net/ccbluex/liquidbounce/ui/client/GuiServerSpoof.kt b/src/main/java/net/ccbluex/liquidbounce/ui/client/GuiServerSpoof.kt deleted file mode 100644 index 7cf10f80ab..0000000000 --- a/src/main/java/net/ccbluex/liquidbounce/ui/client/GuiServerSpoof.kt +++ /dev/null @@ -1,80 +0,0 @@ -package net.ccbluex.liquidbounce.ui.client - -import net.ccbluex.liquidbounce.FDPClient -import net.ccbluex.liquidbounce.handler.other.ServerSpoof -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.gui.GuiScreen -import net.minecraft.client.gui.GuiTextField -import org.lwjgl.input.Keyboard - -class GuiServerSpoof(private val prevGui: GuiScreen) : GuiScreen() { - - private lateinit var textField: GuiTextField - private lateinit var stat: GuiButton - - override fun initGui() { - Keyboard.enableRepeatEvents(true) - textField = GuiTextField(2, mc.fontRendererObj, width / 2 - 100, 60, 200, 20) - textField.isFocused = true - textField.text = ServerSpoof.address - textField.maxStringLength = 114514 - buttonList.add(GuiButton(2, width / 2 - 100, height / 4 + 96, "STATUS").also { stat = it }) - buttonList.add(GuiButton(0, width / 2 - 100, height / 4 + 120, "%ui.back%")) - updateButtonStat() - } - - private fun updateButtonStat() { - stat.displayString = "%ui.status%: " + if (ServerSpoof.enable) "§a%ui.on%" else "§c%ui.off%" - } - - override fun drawScreen(mouseX: Int, mouseY: Int, partialTicks: Float) { - drawBackground(0) - drawCenteredString(mc.fontRendererObj, "%ui.serverSpoof%", width / 2, 34, 0xffffff) - textField.drawTextBox() - if (textField.text.isEmpty() && !textField.isFocused) { - drawString(mc.fontRendererObj, "§7%ui.serverSpoof.address%", width / 2 - 100, 66, 0xffffff) - } - super.drawScreen(mouseX, mouseY, partialTicks) - } - - override fun actionPerformed(button: GuiButton) { - when (button.id) { - 0 -> { - ServerSpoof.address = textField.text - mc.displayGuiScreen(prevGui) - } - 2 -> { - ServerSpoof.enable = !ServerSpoof.enable - } - } - updateButtonStat() - FDPClient.fileManager.saveConfig(FDPClient.fileManager.specialConfig) - } - - override fun onGuiClosed() { - ServerSpoof.address = textField.text - } - - override fun keyTyped(typedChar: Char, keyCode: Int) { - if (Keyboard.KEY_ESCAPE == keyCode) { - mc.displayGuiScreen(prevGui) - return - } - - if (textField.isFocused) { - textField.textboxKeyTyped(typedChar, keyCode) - } - - super.keyTyped(typedChar, keyCode) - } - - override fun mouseClicked(mouseX: Int, mouseY: Int, mouseButton: Int) { - textField.mouseClicked(mouseX, mouseY, mouseButton) - super.mouseClicked(mouseX, mouseY, mouseButton) - } - - override fun updateScreen() { - textField.updateCursorCounter() - super.updateScreen() - } -} \ No newline at end of file diff --git a/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/element/elements/Text.kt b/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/element/elements/Text.kt index f2ef432fcf..dfa48a66e4 100644 --- a/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/element/elements/Text.kt +++ b/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/element/elements/Text.kt @@ -5,6 +5,8 @@ */ package net.ccbluex.liquidbounce.ui.client.hud.element.elements +import cc.paimonmc.viamcp.ViaMCP +import cc.paimonmc.viamcp.protocols.ProtocolCollection import net.ccbluex.liquidbounce.FDPClient import net.ccbluex.liquidbounce.ui.client.hud.designer.GuiHudDesigner import net.ccbluex.liquidbounce.ui.client.hud.element.Border @@ -130,6 +132,7 @@ class Text( "cps", "lcps" -> return CPSCounter.getCPS(CPSCounter.MouseButton.LEFT).toString() "mcps" -> return CPSCounter.getCPS(CPSCounter.MouseButton.MIDDLE).toString() "rcps" -> return CPSCounter.getCPS(CPSCounter.MouseButton.RIGHT).toString() + "portalVersion" -> ProtocolCollection.getProtocolById(ViaMCP.getInstance().version).getName() else -> null // Null = don't replace } } diff --git a/src/main/resources/mixins.fdpclient.json b/src/main/resources/mixins.fdpclient.json index 70d5a54bfa..cde30c9985 100644 --- a/src/main/resources/mixins.fdpclient.json +++ b/src/main/resources/mixins.fdpclient.json @@ -64,6 +64,7 @@ "network.MixinC17PacketCustomPayload", "network.MixinS3FPacketCustomPayload", "network.MixinBlockedServers", + "network.MixinNetworkManagerChInit", "performance.MixinBlock", "performance.MixinChunk", "performance.MixinWorld",