ViaVersion VersionSwitcher for Minecraft Coder Pack (MCP)
If you are interested in the project, or you have issues / suggestions, feel free to join our discord!
- ViaMCP
ViaVersion 5.9.0 did some changes to the packet registrations and Protocol API, you need to make sure your codes are being loaded after Via* platforms initiated.
For example
Like you do as this do, after that you need delay the registration or replacement you have implemented.
try {
ViaMCP.create();
// In case you want a version slider like in the Minecraft options, you can use this code here, please choose one of those:ViaMCP.INSTANCE.initAsyncSlider(); // For top left aligned sliderViaMCP.INSTANCE.initAsyncSlider(x, y, width (min. 110), height (recommended20)); // For custom position and size slider// herenewThread(() -> {
try {
Thread.sleep(1000); // Just make sure the viaversions are fully loaded, adjust it depend on your device.
} catch (Throwableignored) {}
ViaMCP.INSTANCE.applyFix(); // Check the bugs you want to fix.
}).start();
} catch (Exceptione) {
e.printStackTrace();
}Moreover, you have to update your registration code if you ever overrided packets which are already registered in Via* libraries:
// OldfinalProtocol1_17To1_16_4protocol = Via.getManager().getProtocolManager().getProtocol(Protocol1_17To1_16_4.class);
protocol.registerClientbound(ClientboundPackets1_17.PING, ClientboundPackets1_16_2.CONTAINER_ACK, wrapper -> {}, true);
protocol.registerServerbound(ServerboundPackets1_16_2.CONTAINER_ACK, ServerboundPackets1_17.PONG, wrapper -> {}, true);
// NewfinalProtocol1_17To1_16_4protocol = Via.getManager().getProtocolManager().getProtocol(Protocol1_17To1_16_4.class);
protocol.replaceClientbound(ClientboundPackets1_17.PING, wrapper -> {});
protocol.replaceServerbound(ServerboundPackets1_16_2.CONTAINER_ACK, wrapper -> {});Otherwise, your registration code may be forcely overrided by ViaVersion itself or result in a failure (not working or crash).
ViaVersion 4.10.0 did some changes to the ProtocolVersion API, you have to update your own code if you ever used the ViaLoadingBase class:
// OldViaLoadingBase.getInstance().getTargetVersion().isOlderThan(ProtocolVersion.v1_8);
ViaLoadingBase.getInstance().getTargetVersion().isNewerThan(ProtocolVersion.v1_8);
ViaLoadingBase.getInstance().getTargetVersion().isNewerThanOrEqualTo(ProtocolVersion.v1_8);
ViaLoadingBase.getInstance().getTargetVersion().isOlderThanOrEqualTo(ProtocolVersion.v1_8);
ViaLoadingBase.getInstance().getTargetVersion().getIndex();
// NewViaLoadingBase.getInstance().getTargetVersion().olderThan(ProtocolVersion.v1_8);
ViaLoadingBase.getInstance().getTargetVersion().newerThan(ProtocolVersion.v1_8);
ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_8);
ViaLoadingBase.getInstance().getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_8);
ViaLoadingBase.PROTOCOLS.indexOf(ViaLoadingBase.getInstance().getTargetVersion());In addition to that, the ComparableProtocolVersion class has been removed and it's methods have been moved to the ProtocolVersion class.
Firstly, you will need to add the listed libraries into your dependencies in IntelliJ or Eclipse
Dependencies (Included inside libraries folder)
ViaVersion-[ver]-downgraded.jar > ViaVersion > https://github.com/ViaVersion/ViaVersion
ViaBackwards-[ver]-downgraded.jar > ViaBackwards > https://github.com/ViaVersion/ViaBackwards
ViaRewind-[ver]-downgraded.jar > ViaRewind > https://github.com/ViaVersion/ViaRewind
Secondly, you need to add code that allows you to actually use ViaMCP (Choose the version folder that corresponds with your client version)
For other versions than 1.8.x and 1.12.2, you will need to modify the code to fit your client version. You can see namings for other major versions here
NOTE: ViaVersion 5.0.0+ doesn't support Java 8 anymore, therefore when updating the libraries yourself, you need to download the -Java8 jar files from the ci server or generate them yourself using this tool. If you find the java source files lacking imports, fix it yourself.
Add this to the main class of your client (aka injection function)
try {
ViaMCP.create();
// In case you want a version slider like in the Minecraft options, you can use this code here, please choose one of those:ViaMCP.INSTANCE.initAsyncSlider(); // For top left aligned sliderViaMCP.INSTANCE.initAsyncSlider(x, y, width (min. 110), height (recommended20)); // For custom position and size slider
} catch (Exceptione) {
e.printStackTrace();
}You will need to modify 2 methods inside NetworkManager.java
1. Hook ViaVersion into the Netty Pipeline
Find the method, that is func_181124_a, createNetworkManagerAndConnect or contains (Bootstrap)((Bootstrap)((Bootstrap)(new Bootstrap()).group((EventLoopGroup)lazyloadbase.getValue())
Find the vanilla network pipeline call:
// 1.8.x clientp_initChannel_1_.pipeline().addLast((String)"timeout", (ChannelHandler)(newReadTimeoutHandler(30))).addLast((String)"splitter", (ChannelHandler)(newMessageDeserializer2())).addLast((String)"decoder", (ChannelHandler)(newMessageDeserializer(EnumPacketDirection.CLIENTBOUND))).addLast((String)"prepender", (ChannelHandler)(newMessageSerializer2())).addLast((String)"encoder", (ChannelHandler)(newMessageSerializer(EnumPacketDirection.SERVERBOUND))).addLast((String)"packet_handler", (ChannelHandler)networkmanager);
// 1.12.x clientp_initChannel_1_.pipeline().addLast("timeout", newReadTimeoutHandler(30)).addLast("splitter", newNettyVarint21FrameDecoder()).addLast("decoder", newNettyPacketDecoder(EnumPacketDirection.CLIENTBOUND)).addLast("prepender", newNettyVarint21FrameEncoder()).addLast("encoder", newNettyPacketEncoder(EnumPacketDirection.SERVERBOUND)).addLast("packet_handler", networkmanager);After the vanilla network pipeline call, add the ViaMCP protocol pipeline hook:
if (p_initChannel_1_instanceofSocketChannel && ViaLoadingBase.getInstance().getTargetVersion().getVersion() != ViaMCP.NATIVE_VERSION) {
finalUserConnectionuser = newUserConnectionImpl(p_initChannel_1_, true);
newProtocolPipelineImpl(user);
p_initChannel_1_.pipeline().addLast(newMCPVLBPipeline(user));
}Your code should look like this afterwards (1.8.x for example), the vanilla network pipeline call should not be commented out and the ViaMCP protocol pipeline hook should be after the vanilla network pipeline call:
p_initChannel_1_.pipeline().addLast((String)"timeout", (ChannelHandler)(newReadTimeoutHandler(30))).addLast((String)"splitter", (ChannelHandler)(newMessageDeserializer2())).addLast((String)"decoder", (ChannelHandler)(newMessageDeserializer(EnumPacketDirection.CLIENTBOUND))).addLast((String)"prepender", (ChannelHandler)(newMessageSerializer2())).addLast((String)"encoder", (ChannelHandler)(newMessageSerializer(EnumPacketDirection.SERVERBOUND))).addLast((String)"packet_handler", (ChannelHandler)networkmanager);
if (p_initChannel_1_instanceofSocketChannel && ViaLoadingBase.getInstance().getTargetVersion().getVersion() != ViaMCP.NATIVE_VERSION) {
finalUserConnectionuser = newUserConnectionImpl(p_initChannel_1_, true);
newProtocolPipelineImpl(user);
p_initChannel_1_.pipeline().addLast(newMCPVLBPipeline(user));
}Side note: If you want to send custom packets, you have to store the UserConnection instance in a variable for later, it's important that this variable is NOT STATIC since it's also used for pinging servers!
2. Fix the compression in the NetworkManager#setCompressionTreshold function
Simply call the following code at the end of the method in Minecraft:
this.channel.pipeline().fireUserEventTriggered(newCompressionReorderEvent());You will need to add a button to access the protocol switcher (or alternatively use the version slider under this section)
In addSingleplayerMultiplayerButtons() function add (if in GuiMainMenu):
this.buttonList.add(newGuiButton(69, 5, 5, 90, 20, "Version"));In actionPerformed() function add:
if (button.id == 69)
{
this.mc.displayGuiScreen(newGuiProtocolSelector(this));
}You can also use a version slider to control ViaMCP versions
this.buttonList.add(ViaMCP.INSTANCE.getAsyncVersionSlider());Class: Minecraft.java
Function: clickMouse()
1.8.x
Replace this.thePlayer.swingItem(); on the 1st line in the if-clause with:
AttackOrder.sendConditionalSwing(this.objectMouseOver);Replace this.playerController.attackEntity(this.thePlayer, this.objectMouseOver.entityHit); in the switch in case ENTITY with:
AttackOrder.sendFixedAttack(this.thePlayer, this.objectMouseOver.entityHit);Class: EntityPlayerSP.java
Function: swingItem()
Replace this.sendQueue.addToSendQueue(new C0APacketAnimation()); with:
if (ViaLoadingBase.getInstance().getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_8)) {
this.sendQueue.addToSendQueue(newC0APacketAnimation());
} else {
AttackOrder.send1_9Animation();
}1.12.2
Replace this.player.swingArm(EnumHand.MAIN_HAND); at the last line in the else if-clause with:
AttackOrder.sendConditionalSwing(this.objectMouseOver, EnumHand.MAIN_HAND);Replace this.playerController.attackEntity(this.player, this.objectMouseOver.entityHit); in the switch in case ENTITY with:
AttackOrder.sendFixedAttack(this.thePlayer, this.objectMouseOver.entityHit, EnumHand.MAIN_HAND);Block Placement
Replace all code in onItemUse function in the ItemBlock class with:
returnFixedSoundEngine.onItemUse(this, stack, playerIn, worldIn, pos, side, hitX, hitY, hitZ);Block Breaking
Replace all code in destroyBlock function in the World class with:
returnFixedSoundEngine.destroyBlock(this, pos, dropBlock);Call the fixTransactions(); in the ViaMCP class file so ViaVersion doesn't remap anything in transaction packets.
After that, you need to do some changes in the Game code:
Class: S32PacketConfirmTransaction.java
Function: readPacketData()
Replace the code with this method:
publicvoidreadPacketData(PacketBufferbuf) throwsIOException {
if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_17)) {
this.windowId = buf.readInt();
} else {
this.windowId = buf.readUnsignedByte();
this.actionNumber = buf.readShort();
this.accepted = buf.readBoolean();
}
}Class: C0FPacketConfirmTransaction.java
Function: writePacketData()
Replace the code with this method:
publicvoidwritePacketData(PacketBufferbuf) throwsIOException {
if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_17)) {
buf.writeInt(this.windowId);
} else {
buf.writeByte(this.windowId);
buf.writeShort(this.uid);
buf.writeByte(this.accepted ? 1 : 0);
}
}Note: this code can be different depending on your mappings and game version, you just need to make sure it only reads the window id and doesn't read the rest of the packet because we previously removed the ViaVersion handlers which would have handled the rest of the packet.
Class: NetHandlerPlayClient.java
Function: handleConfirmTransaction()
Add this code after the checkThreadAndEnqueue function call:
if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_17)) {
this.addToSendQueue(newC0FPacketConfirmTransaction(packetIn.getWindowId(), 0, false));
return;
}Add the code below to the class PlayerControllerMP / GameMode:
privateintsequenceId;
publicintgetSequenceId() {
return ++this.sequenceId;
}Then replace the packet code in the functions below:
Function: clickBlock(BlockPos loc, EnumFacing face)
Replace: C07/CPacketPlayerDigging packet on action START_DESTROY_BLOCK
if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_19)) {
PacketWrapperpacket = PacketWrapper.create(ServerboundPackets1_19.PLAYER_ACTION, Via.getManager().getConnectionManager().getConnections().iterator().next());
packet.write(Types.VAR_INT, C07PacketPlayerDigging.Action.START_DESTROY_BLOCK.ordinal());
packet.write(Types.BLOCK_POSITION1_14, newBlockPosition(loc.getX(), loc.getY(), loc.getZ()));
packet.write(Types.BYTE, (byte) face.getIndex());
packet.write(Types.VAR_INT, this.getSequenceId());
packet.sendToServer(Protocol1_19To1_18_2.class);
}Function: onPlayerDamageBlock(BlockPos posBlock, EnumFacing directionFacing)
Replace: C07/CPacketPlayerDigging packet on action START_DESTROY_BLOCK
if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_19)) {
PacketWrapperpacket = PacketWrapper.create(ServerboundPackets1_19.PLAYER_ACTION, Via.getManager().getConnectionManager().getConnections().iterator().next());
packet.write(Types.VAR_INT, C07PacketPlayerDigging.Action.START_DESTROY_BLOCK.ordinal());
packet.write(Types.BLOCK_POSITION1_14, newBlockPosition(posBlock.getX(), posBlock.getY(), posBlock.getZ()));
packet.write(Types.BYTE, (byte) directionFacing.getIndex());
packet.write(Types.VAR_INT, this.getSequenceId());
packet.sendToServer(Protocol1_19To1_18_2.class);
}Replace: C07/CPacketPlayerDigging packet on action STOP_DESTROY_BLOCK
if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_19)) {
PacketWrapperpacket = PacketWrapper.create(ServerboundPackets1_19.PLAYER_ACTION, Via.getManager().getConnectionManager().getConnections().iterator().next());
packet.write(Types.VAR_INT, C07PacketPlayerDigging.Action.STOP_DESTROY_BLOCK.ordinal());
packet.write(Types.BLOCK_POSITION1_14, newBlockPosition(posBlock.getX(), posBlock.getY(), posBlock.getZ()));
packet.write(Types.BYTE, (byte) directionFacing.getIndex());
packet.write(Types.VAR_INT, this.getSequenceId());
packet.sendToServer(Protocol1_19To1_18_2.class);
}Function: onPlayerRightClick(EntityPlayerSP player, WorldClient worldIn, ItemStack heldStack, BlockPos hitPos, EnumFacing side, Vec3 hitVec)
Replace: C08PacketPlayerBlockPlacement/CPacketPlayerBlockPlacement packet send code
if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_19)) {
PacketWrapperpacket = PacketWrapper.create(ServerboundPackets1_19.USE_ITEM_ON, Via.getManager().getConnectionManager().getConnections().iterator().next());
packet.write(Types.VAR_INT, 0);
packet.write(Types.BLOCK_POSITION1_14, newBlockPosition(hitPos.getX(), hitPos.getY(), hitPos.getZ()));
packet.write(Types.VAR_INT, side.ordinal());
packet.write(Types.FLOAT, Float.valueOf(f)); // Change the arg according to your client code.packet.write(Types.FLOAT, Float.valueOf(f1));
packet.write(Types.FLOAT, Float.valueOf(f2));
packet.write(Types.BOOLEAN, false);
packet.write(Types.VAR_INT, this.getSequenceId());
packet.sendToServer(Protocol1_19To1_18_2.class);
}Function: sendUseItem(EntityPlayer playerIn, World worldIn, ItemStack itemStackIn)
Replace: C08PacketPlayerBlockPlacement/CPacketPlayerBlockPlacement packet send code
if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_19)) {
PacketWrapperpacket = PacketWrapper.create(ServerboundPackets1_19.USE_ITEM, Via.getManager().getConnectionManager().getConnections().iterator().next());
packet.write(Types.VAR_INT, 0);
packet.write(Types.VAR_INT, this.getSequenceId());
packet.sendToServer(Protocol1_19To1_18_2.class);
}Insert the code below in the end of function runTick() in the class Minecraft:
if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_21_2)) {
UserConnectionconnection = Via.getManager().getConnectionManager().getConnections().iterator().next();
PacketWrapperpacket = PacketWrapper.create(ServerboundPackets1_21_2.CLIENT_TICK_END, null, connection);
packet.sendToServer(Protocol1_21_2To1_21.class);
}Since it's not allowed to share any minecraft source code, I have to make the attributes update independent from MCP.
Please follow the steps below to apply the fix if you want :)
AIR_DRAG_MODIFIER: The origin minecraft's air friction is 0.98, check your LocalPlayer.java(for modern version) / EntityPlayerSP.java(for legacy version), LivingEntity.java(modern) / EntityLivingBase.java(legacy), Entity.java,then replace all 0.98 to the new air friction.
BOUNCINESS: Find the code that makes player stop(delta movement is 0.0) when collided horizontally, it's most probably in LocalPlayer.java / EntityPlayerSP.java, then rewrite the Vec3 delta movement / motionXZ which is 0.0 to the new attributes you got.
FRICTION_MODIFIER: Find the code that affects block friction, the origin minecraft's block friction is 0.91, and it's most probably in LivingEntity.java / EntityLivingBase.java, then replace them to the new attributes you got.
Call the fixHypixelLogin(); in the ViaMCP class file
Then insert the code below in the head of runTick() function in the class Minecraft:
PacketWrapperImplpacket = null;
PacketWrapperImplpacketInfo = null;
intmodelParts = 0;
// Hypixel only allow 1.21.4+ client to login.if (ViaLoadingBase.getInstance().getTargetVersion().newerThanOrEqualTo(ProtocolVersion.v1_21_4)) {
packet = (PacketWrapperImpl) PacketWrapper.create(ServerboundConfigurationPackets1_20_2.CUSTOM_PAYLOAD, ViaMCP.INSTANCE.user);
packet.write(Types.STRING, "minecraft:brand");
packet.write(Types.STRING, "vanilla");
packet.sendToServer(Protocol1_20_3To1_20_2.class);
packetInfo = (PacketWrapperImpl) PacketWrapper.create(ServerboundConfigurationPackets1_20_2.CLIENT_INFORMATION, ViaMCP.INSTANCE.user);
packetInfo.write(Types.STRING, Minecraft.getMinecraft().gameSettings.language.toLowerCase());
packetInfo.write(Types.BYTE, (byte) Minecraft.getMinecraft().gameSettings.renderDistanceChunks);
packetInfo.write(Types.VAR_INT, Minecraft.getMinecraft().gameSettings.chatVisibility.ordinal());
packetInfo.write(Types.BOOLEAN, Minecraft.getMinecraft().gameSettings.chatColours);
for (EnumPlayerModelPartsparts : Minecraft.getMinecraft().gameSettings.getModelParts()) {
modelParts |= parts.getPartMask();
}
packetInfo.write(Types.UNSIGNED_BYTE, (short) modelParts);
packetInfo.write(Types.VAR_INT, 1);
packetInfo.write(Types.BOOLEAN, true);
packetInfo.write(Types.BOOLEAN, true);
packetInfo.sendToServer(Protocol1_20_3To1_20_2.class);
}You can send raw packets with ViaMCP, you can use the following code to send raw packets:
finalPacketWrapperblockPlace = PacketWrapper.create(ServerboundPackets1_9.PLAYER_BLOCK_PLACEMENT, null); // Replace null with your stored UserConnection, see NetworkManager tutorial aboveblockPlace.write(Type.POSITION1_8, newPosition(0, 0, 0)); // Replace with the block positionblockPlace.write(Type.VAR_INT, 0); // Replace with the block face, see https://wiki.vg/index.php?title=Protocol&oldid=7617#Player_DiggingblockPlace.write(Type.VAR_INT, 0); // Replace with the hand, 0 for main hand, 1 for off handblockPlace.write(Type.UNSIGNED_BYTE, (short) 0); // The x pos of the crosshair, from 0 to 15 increasing from west to eastblockPlace.write(Type.UNSIGNED_BYTE, (short) 0); // The y pos of the crosshair, from 0 to 15 increasing from bottom to topblockPlace.write(Type.UNSIGNED_BYTE, (short) 0); // The z pos of the crosshair, from 0 to 15 increasing from north to southtry {
blockPlace.sendToServer(Protocol1_9To1_8.class); // Protocol class names are: server -> client version
} catch (Exceptione) {
// Packet sending failedthrownewRuntimeException(e);
}This should fix most peoples issues with dependencies (usually NoClassDefFoundError or ClassNotFoundException)
- First export your client normally
- Open your client .jar file with an archive program (winrar or 7zip for example)
- Also open all libraries with the selected archive program (ViaVersion, ViaBackwards, ViaRewind and SnakeYaml)
- From ViaVersion, ViaBackwards and ViaRewind drag and drop
assets,comandusfolders to your client .jar - Then save and close, now your client should be working correctly ;)