1153 lines
60 KiB
Java
1153 lines
60 KiB
Java
/*
|
|
* This file is part of ViaLegacy - https://github.com/RaphiMC/ViaLegacy
|
|
* Copyright (C) 2020-2024 RK_01/RaphiMC and contributors
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4;
|
|
|
|
import com.google.common.base.Joiner;
|
|
import com.viaversion.viaversion.api.Via;
|
|
import com.viaversion.viaversion.api.connection.ProtocolInfo;
|
|
import com.viaversion.viaversion.api.connection.UserConnection;
|
|
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
|
|
import com.viaversion.viaversion.api.minecraft.Position;
|
|
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
|
|
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_10;
|
|
import com.viaversion.viaversion.api.minecraft.item.DataItem;
|
|
import com.viaversion.viaversion.api.minecraft.item.Item;
|
|
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
|
|
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
|
|
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
|
import com.viaversion.viaversion.api.protocol.packet.State;
|
|
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
|
|
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
|
|
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
|
import com.viaversion.viaversion.api.type.Type;
|
|
import com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;
|
|
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;
|
|
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;
|
|
import com.viaversion.viaversion.libs.gson.JsonObject;
|
|
import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;
|
|
import com.viaversion.viaversion.protocols.base.ClientboundStatusPackets;
|
|
import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;
|
|
import com.viaversion.viaversion.protocols.base.ServerboundStatusPackets;
|
|
import com.viaversion.viaversion.protocols.protocol1_8.ClientboundPackets1_8;
|
|
import com.viaversion.viaversion.util.IdAndData;
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
|
import io.netty.channel.ChannelPromise;
|
|
import net.raphimc.vialegacy.ViaLegacy;
|
|
import net.raphimc.vialegacy.api.protocol.StatelessTransitionProtocol;
|
|
import net.raphimc.vialegacy.api.remapper.LegacyItemRewriter;
|
|
import net.raphimc.vialegacy.api.splitter.PreNettySplitter;
|
|
import net.raphimc.vialegacy.api.util.PacketUtil;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.providers.EncryptionProvider;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter.ChatComponentRewriter;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter.ItemRewriter;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter.SoundRewriter;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter.StatisticRewriter;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.*;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.MetaType1_6_4;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_6_10to1_7_2_5.ClientboundPackets1_7_2;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_7_6_10to1_7_2_5.ServerboundPackets1_7_2;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.Protocol1_8to1_7_6_10;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.types.MetaType1_7_6;
|
|
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.types.Types1_7_6;
|
|
|
|
import java.util.List;
|
|
import java.util.logging.Level;
|
|
|
|
public class Protocol1_7_2_5to1_6_4 extends StatelessTransitionProtocol<ClientboundPackets1_6_4, ClientboundPackets1_7_2, ServerboundPackets1_6_4, ServerboundPackets1_7_2> {
|
|
|
|
private final LegacyItemRewriter<Protocol1_7_2_5to1_6_4> itemRewriter = new ItemRewriter(this);
|
|
|
|
public Protocol1_7_2_5to1_6_4() {
|
|
super(ClientboundPackets1_6_4.class, ClientboundPackets1_7_2.class, ServerboundPackets1_6_4.class, ServerboundPackets1_7_2.class);
|
|
}
|
|
|
|
@Override
|
|
protected void registerPackets() {
|
|
this.itemRewriter.register();
|
|
|
|
this.registerClientboundTransition(ClientboundPackets1_6_4.JOIN_GAME,
|
|
ClientboundPackets1_7_2.JOIN_GAME, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT); // entity id
|
|
handler(wrapper -> {
|
|
wrapper.user().get(PlayerInfoStorage.class).entityId = wrapper.get(Type.INT, 0);
|
|
final String terrainType = wrapper.read(Types1_6_4.STRING); // level type
|
|
final short gameType = wrapper.read(Type.BYTE); // game mode
|
|
final byte dimension = wrapper.read(Type.BYTE); // dimension id
|
|
final short difficulty = wrapper.read(Type.BYTE); // difficulty
|
|
wrapper.read(Type.BYTE); // world height
|
|
final short maxPlayers = wrapper.read(Type.BYTE); // max players
|
|
|
|
wrapper.write(Type.UNSIGNED_BYTE, gameType);
|
|
wrapper.write(Type.BYTE, dimension);
|
|
wrapper.write(Type.UNSIGNED_BYTE, difficulty);
|
|
wrapper.write(Type.UNSIGNED_BYTE, maxPlayers);
|
|
wrapper.write(Type.STRING, terrainType);
|
|
});
|
|
handler(wrapper -> {
|
|
final byte dimensionId = wrapper.get(Type.BYTE, 0);
|
|
wrapper.user().get(DimensionTracker.class).changeDimension(dimensionId);
|
|
|
|
wrapper.user().put(new ChunkTracker(wrapper.user()));
|
|
});
|
|
}
|
|
}, State.LOGIN, (PacketHandler) wrapper -> {
|
|
ViaLegacy.getPlatform().getLogger().warning("Server skipped LOGIN state");
|
|
final PacketWrapper sharedKey = PacketWrapper.create(ClientboundPackets1_6_4.SHARED_KEY, wrapper.user());
|
|
sharedKey.write(Type.SHORT_BYTE_ARRAY, new byte[0]);
|
|
sharedKey.write(Type.SHORT_BYTE_ARRAY, new byte[0]);
|
|
wrapper.user().get(ProtocolMetadataStorage.class).skipEncryption = true;
|
|
sharedKey.send(Protocol1_7_2_5to1_6_4.class, false); // switch to play state
|
|
wrapper.user().get(ProtocolMetadataStorage.class).skipEncryption = false;
|
|
|
|
wrapper.setPacketType(ClientboundPackets1_6_4.JOIN_GAME);
|
|
wrapper.send(Protocol1_7_2_5to1_6_4.class, false);
|
|
wrapper.cancel();
|
|
}
|
|
);
|
|
this.registerClientbound(ClientboundPackets1_6_4.CHAT_MESSAGE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_6_4.STRING, Type.STRING, ChatComponentRewriter::toClient); // message
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.ENTITY_EQUIPMENT, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT); // entity id
|
|
map(Type.SHORT); // slot
|
|
map(Types1_7_6.ITEM); // item
|
|
handler(wrapper -> itemRewriter.handleItemToClient(wrapper.user(), wrapper.get(Types1_7_6.ITEM, 0)));
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.RESPAWN, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT); // dimension id
|
|
map(Type.BYTE, Type.UNSIGNED_BYTE); // difficulty
|
|
map(Type.BYTE, Type.UNSIGNED_BYTE); // gamemode
|
|
read(Type.SHORT); // world height
|
|
map(Types1_6_4.STRING, Type.STRING); // worldType
|
|
handler(wrapper -> {
|
|
if (wrapper.user().get(DimensionTracker.class).changeDimension(wrapper.get(Type.INT, 0))) {
|
|
wrapper.user().get(ChunkTracker.class).clear();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.PLAYER_POSITION_ONLY_ONGROUND, ClientboundPackets1_7_2.PLAYER_POSITION, wrapper -> {
|
|
final PlayerInfoStorage playerInfoStorage = wrapper.user().get(PlayerInfoStorage.class);
|
|
final boolean supportsFlags = wrapper.user().getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_8);
|
|
|
|
wrapper.write(Type.DOUBLE, supportsFlags ? 0D : playerInfoStorage.posX); // x
|
|
wrapper.write(Type.DOUBLE, supportsFlags ? 0D : playerInfoStorage.posY + 1.62F); // y
|
|
wrapper.write(Type.DOUBLE, supportsFlags ? 0D : playerInfoStorage.posZ); // z
|
|
wrapper.write(Type.FLOAT, supportsFlags ? 0F : playerInfoStorage.yaw); // yaw
|
|
wrapper.write(Type.FLOAT, supportsFlags ? 0F : playerInfoStorage.pitch); // pitch
|
|
if (supportsFlags) {
|
|
wrapper.read(Type.BOOLEAN); // onGround
|
|
wrapper.write(Type.BYTE, (byte) 0b11111); // flags
|
|
|
|
wrapper.setPacketType(ClientboundPackets1_8.PLAYER_POSITION);
|
|
wrapper.send(Protocol1_8to1_7_6_10.class);
|
|
wrapper.cancel();
|
|
} else {
|
|
wrapper.passthrough(Type.BOOLEAN); // onGround
|
|
}
|
|
|
|
final PacketWrapper setVelocityToZero = PacketWrapper.create(ClientboundPackets1_7_2.ENTITY_VELOCITY, wrapper.user());
|
|
setVelocityToZero.write(Type.INT, playerInfoStorage.entityId); // entity id
|
|
setVelocityToZero.write(Type.SHORT, (short) 0); // velocity x
|
|
setVelocityToZero.write(Type.SHORT, (short) 0); // velocity y
|
|
setVelocityToZero.write(Type.SHORT, (short) 0); // velocity z
|
|
|
|
if (!wrapper.isCancelled()) wrapper.send(Protocol1_7_2_5to1_6_4.class);
|
|
setVelocityToZero.send(Protocol1_7_2_5to1_6_4.class);
|
|
wrapper.cancel();
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.PLAYER_POSITION_ONLY_POSITION, ClientboundPackets1_7_2.PLAYER_POSITION, wrapper -> {
|
|
final PlayerInfoStorage playerInfoStorage = wrapper.user().get(PlayerInfoStorage.class);
|
|
final boolean supportsFlags = wrapper.user().getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_8);
|
|
|
|
wrapper.passthrough(Type.DOUBLE); // x
|
|
wrapper.passthrough(Type.DOUBLE); // stance
|
|
wrapper.read(Type.DOUBLE); // y
|
|
wrapper.passthrough(Type.DOUBLE); // z
|
|
wrapper.write(Type.FLOAT, supportsFlags ? 0F : playerInfoStorage.yaw); // yaw
|
|
wrapper.write(Type.FLOAT, supportsFlags ? 0F : playerInfoStorage.pitch); // pitch
|
|
if (supportsFlags) {
|
|
wrapper.read(Type.BOOLEAN); // onGround
|
|
wrapper.write(Type.BYTE, (byte) 0b11000); // flags
|
|
|
|
wrapper.setPacketType(ClientboundPackets1_8.PLAYER_POSITION);
|
|
wrapper.send(Protocol1_8to1_7_6_10.class);
|
|
wrapper.cancel();
|
|
} else {
|
|
wrapper.passthrough(Type.BOOLEAN); // onGround
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.PLAYER_POSITION_ONLY_LOOK, ClientboundPackets1_7_2.PLAYER_POSITION, wrapper -> {
|
|
final PlayerInfoStorage playerInfoStorage = wrapper.user().get(PlayerInfoStorage.class);
|
|
final boolean supportsFlags = wrapper.user().getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_8);
|
|
|
|
wrapper.write(Type.DOUBLE, supportsFlags ? 0D : playerInfoStorage.posX); // x
|
|
wrapper.write(Type.DOUBLE, supportsFlags ? 0D : playerInfoStorage.posY + 1.62F); // y
|
|
wrapper.write(Type.DOUBLE, supportsFlags ? 0D : playerInfoStorage.posZ); // z
|
|
wrapper.passthrough(Type.FLOAT); // yaw
|
|
wrapper.passthrough(Type.FLOAT); // pitch
|
|
if (supportsFlags) {
|
|
wrapper.read(Type.BOOLEAN); // onGround
|
|
wrapper.write(Type.BYTE, (byte) 0b111); // flags
|
|
|
|
wrapper.setPacketType(ClientboundPackets1_8.PLAYER_POSITION);
|
|
wrapper.send(Protocol1_8to1_7_6_10.class);
|
|
wrapper.cancel();
|
|
} else {
|
|
wrapper.passthrough(Type.BOOLEAN); // onGround
|
|
}
|
|
|
|
final PacketWrapper setVelocityToZero = PacketWrapper.create(ClientboundPackets1_7_2.ENTITY_VELOCITY, wrapper.user());
|
|
setVelocityToZero.write(Type.INT, playerInfoStorage.entityId); // entity id
|
|
setVelocityToZero.write(Type.SHORT, (short) 0); // velocity x
|
|
setVelocityToZero.write(Type.SHORT, (short) 0); // velocity y
|
|
setVelocityToZero.write(Type.SHORT, (short) 0); // velocity z
|
|
|
|
if (!wrapper.isCancelled()) wrapper.send(Protocol1_7_2_5to1_6_4.class);
|
|
setVelocityToZero.send(Protocol1_7_2_5to1_6_4.class);
|
|
wrapper.cancel();
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.PLAYER_POSITION, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.DOUBLE); // x
|
|
map(Type.DOUBLE); // stance
|
|
read(Type.DOUBLE); // y
|
|
map(Type.DOUBLE); // z
|
|
map(Type.FLOAT); // yaw
|
|
map(Type.FLOAT); // pitch
|
|
map(Type.BOOLEAN); // onGround
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.HELD_ITEM_CHANGE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.SHORT, Type.BYTE); // slot
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.USE_BED, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT); // entity id
|
|
handler(wrapper -> {
|
|
if (wrapper.read(Type.BYTE) != 0) wrapper.cancel();
|
|
});
|
|
map(Types1_7_6.POSITION_BYTE); // position
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.ENTITY_ANIMATION, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT, Type.VAR_INT); // entity id
|
|
handler(wrapper -> {
|
|
short animate = wrapper.read(Type.BYTE); // animation
|
|
if (animate == 0 || animate == 4) wrapper.cancel();
|
|
if (animate >= 1 && animate <= 3) {
|
|
animate--;
|
|
} else {
|
|
animate -= 2;
|
|
}
|
|
wrapper.write(Type.UNSIGNED_BYTE, animate);
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.SPAWN_PLAYER, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT, Type.VAR_INT); // entity id
|
|
handler(wrapper -> {
|
|
final String name = wrapper.read(Types1_6_4.STRING); // name
|
|
wrapper.write(Type.STRING, (ViaLegacy.getConfig().isLegacySkinLoading() ? Via.getManager().getProviders().get(GameProfileFetcher.class).getMojangUUID(name) : new GameProfile(name).uuid).toString().replace("-", "")); // uuid
|
|
wrapper.write(Type.STRING, name);
|
|
});
|
|
map(Type.INT); // x
|
|
map(Type.INT); // y
|
|
map(Type.INT); // z
|
|
map(Type.BYTE); // yaw
|
|
map(Type.BYTE); // pitch
|
|
handler(wrapper -> {
|
|
final Item currentItem = new DataItem(wrapper.read(Type.UNSIGNED_SHORT), (byte) 1, (short) 0, null); // item
|
|
itemRewriter.handleItemToClient(wrapper.user(), currentItem);
|
|
wrapper.write(Type.SHORT, (short) currentItem.identifier());
|
|
});
|
|
map(Types1_6_4.METADATA_LIST, Types1_7_6.METADATA_LIST); // metadata
|
|
handler(wrapper -> rewriteMetadata(wrapper.user(), wrapper.get(Types1_7_6.METADATA_LIST, 0)));
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.SPAWN_ENTITY, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT, Type.VAR_INT); // entity id
|
|
map(Type.BYTE); // type id
|
|
map(Type.INT); // x
|
|
map(Type.INT); // y
|
|
map(Type.INT); // z
|
|
map(Type.BYTE); // pitch
|
|
map(Type.BYTE); // yaw
|
|
map(Type.INT); // data
|
|
handler(wrapper -> {
|
|
int data = wrapper.get(Type.INT, 3);
|
|
if (EntityTypes1_10.getTypeFromId(wrapper.get(Type.BYTE, 0), true) == EntityTypes1_10.ObjectType.FALLING_BLOCK.getType()) {
|
|
final int id = data & 0xFFFF;
|
|
final int metadata = data >> 16;
|
|
final IdAndData block = new IdAndData(id, metadata);
|
|
wrapper.user().get(ChunkTracker.class).remapBlockParticle(block);
|
|
data = (block.getId() & 0xFFFF) | block.getData() << 16;
|
|
}
|
|
wrapper.set(Type.INT, 3, data);
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.SPAWN_MOB, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT, Type.VAR_INT); // entity id
|
|
map(Type.UNSIGNED_BYTE); // type id
|
|
map(Type.INT); // x
|
|
map(Type.INT); // y
|
|
map(Type.INT); // z
|
|
map(Type.BYTE); // yaw
|
|
map(Type.BYTE); // pitch
|
|
map(Type.BYTE); // head yaw
|
|
map(Type.SHORT); // velocity x
|
|
map(Type.SHORT); // velocity y
|
|
map(Type.SHORT); // velocity z
|
|
map(Types1_6_4.METADATA_LIST, Types1_7_6.METADATA_LIST); // metadata
|
|
handler(wrapper -> rewriteMetadata(wrapper.user(), wrapper.get(Types1_7_6.METADATA_LIST, 0)));
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.SPAWN_PAINTING, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT, Type.VAR_INT); // entity id
|
|
map(Types1_6_4.STRING, Type.STRING); // motive
|
|
map(Types1_7_6.POSITION_INT); // position
|
|
map(Type.INT); // rotation
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.SPAWN_EXPERIENCE_ORB, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT, Type.VAR_INT); // entity id
|
|
map(Type.INT); // x
|
|
map(Type.INT); // y
|
|
map(Type.INT); // z
|
|
map(Type.SHORT); // count
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.ENTITY_METADATA, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT); // entity id
|
|
map(Types1_6_4.METADATA_LIST, Types1_7_6.METADATA_LIST); // metadata
|
|
handler(wrapper -> rewriteMetadata(wrapper.user(), wrapper.get(Types1_7_6.METADATA_LIST, 0)));
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.ENTITY_PROPERTIES, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT); // entity id
|
|
handler(wrapper -> {
|
|
final int amount = wrapper.passthrough(Type.INT); // count
|
|
for (int i = 0; i < amount; i++) {
|
|
wrapper.write(Type.STRING, wrapper.read(Types1_6_4.STRING)); // id
|
|
wrapper.passthrough(Type.DOUBLE); // baseValue
|
|
final int modifierCount = wrapper.passthrough(Type.SHORT); // modifier count
|
|
for (int x = 0; x < modifierCount; x++) {
|
|
wrapper.passthrough(Type.UUID); // modifier uuid
|
|
wrapper.passthrough(Type.DOUBLE); // modifier amount
|
|
wrapper.passthrough(Type.BYTE); // modifier operation
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.CHUNK_DATA, wrapper -> {
|
|
final Chunk chunk = wrapper.passthrough(Types1_7_6.getChunk(wrapper.user().get(DimensionTracker.class).getDimension()));
|
|
wrapper.user().get(ChunkTracker.class).trackAndRemap(chunk);
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.MULTI_BLOCK_CHANGE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT); // chunkX
|
|
map(Type.INT); // chunkZ
|
|
map(Types1_7_6.BLOCK_CHANGE_RECORD_ARRAY); // blockChangeRecords
|
|
handler(wrapper -> {
|
|
final int chunkX = wrapper.get(Type.INT, 0);
|
|
final int chunkZ = wrapper.get(Type.INT, 1);
|
|
final BlockChangeRecord[] blockChangeRecords = wrapper.get(Types1_7_6.BLOCK_CHANGE_RECORD_ARRAY, 0);
|
|
for (BlockChangeRecord record : blockChangeRecords) {
|
|
final int targetX = record.getSectionX() + (chunkX << 4);
|
|
final int targetY = record.getY(-1);
|
|
final int targetZ = record.getSectionZ() + (chunkZ << 4);
|
|
final IdAndData block = IdAndData.fromRawData(record.getBlockId());
|
|
final Position pos = new Position(targetX, targetY, targetZ);
|
|
wrapper.user().get(ChunkTracker.class).trackAndRemap(pos, block);
|
|
record.setBlockId(block.toRawData());
|
|
}
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.BLOCK_CHANGE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_7_6.POSITION_UBYTE); // position
|
|
map(Type.UNSIGNED_SHORT, Type.VAR_INT); // block id
|
|
map(Type.UNSIGNED_BYTE); // block data
|
|
handler(wrapper -> {
|
|
final Position pos = wrapper.get(Types1_7_6.POSITION_UBYTE, 0); // position
|
|
final int blockId = wrapper.get(Type.VAR_INT, 0); // block id
|
|
final int data = wrapper.get(Type.UNSIGNED_BYTE, 0); // block data
|
|
final IdAndData block = new IdAndData(blockId, data);
|
|
wrapper.user().get(ChunkTracker.class).trackAndRemap(pos, block);
|
|
wrapper.set(Type.VAR_INT, 0, block.getId()); // block id
|
|
wrapper.set(Type.UNSIGNED_BYTE, 0, (short) block.getData()); // block data
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.BLOCK_ACTION, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_7_6.POSITION_SHORT); // position
|
|
map(Type.BYTE, Type.UNSIGNED_BYTE); // type
|
|
map(Type.BYTE, Type.UNSIGNED_BYTE); // data
|
|
map(Type.SHORT, Type.VAR_INT); // block id
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.BLOCK_BREAK_ANIMATION, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT, Type.VAR_INT); // entity id
|
|
map(Types1_7_6.POSITION_INT); // position
|
|
map(Type.BYTE); // progress
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.MAP_BULK_CHUNK, wrapper -> {
|
|
final Chunk[] chunks = wrapper.passthrough(Types1_7_6.CHUNK_BULK);
|
|
for (Chunk chunk : chunks) {
|
|
wrapper.user().get(ChunkTracker.class).trackAndRemap(chunk);
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.EXPLOSION, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.DOUBLE, Type.FLOAT); // x
|
|
map(Type.DOUBLE, Type.FLOAT); // y
|
|
map(Type.DOUBLE, Type.FLOAT); // z
|
|
map(Type.FLOAT); // radius
|
|
map(Type.INT); // record count
|
|
handler(wrapper -> {
|
|
final int x = wrapper.get(Type.FLOAT, 0).intValue();
|
|
final int y = wrapper.get(Type.FLOAT, 1).intValue();
|
|
final int z = wrapper.get(Type.FLOAT, 2).intValue();
|
|
final int recordCount = wrapper.get(Type.INT, 0);
|
|
final ChunkTracker chunkTracker = wrapper.user().get(ChunkTracker.class);
|
|
for (int i = 0; i < recordCount; i++) {
|
|
final Position pos = new Position(x + wrapper.passthrough(Type.BYTE), y + wrapper.passthrough(Type.BYTE), z + wrapper.passthrough(Type.BYTE));
|
|
chunkTracker.trackAndRemap(pos, new IdAndData(0, 0));
|
|
}
|
|
});
|
|
map(Type.FLOAT); // velocity x
|
|
map(Type.FLOAT); // velocity y
|
|
map(Type.FLOAT); // velocity z
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.NAMED_SOUND, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
handler(wrapper -> {
|
|
final String oldSound = wrapper.read(Types1_6_4.STRING); // sound
|
|
String newSound = SoundRewriter.map(oldSound);
|
|
if (oldSound.isEmpty()) newSound = "";
|
|
if (newSound == null) {
|
|
if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) {
|
|
ViaLegacy.getPlatform().getLogger().warning("Unable to map 1.6.4 sound '" + oldSound + "'");
|
|
}
|
|
newSound = "";
|
|
}
|
|
if (newSound.isEmpty()) {
|
|
wrapper.cancel();
|
|
return;
|
|
}
|
|
wrapper.write(Type.STRING, newSound);
|
|
});
|
|
map(Type.INT); // x
|
|
map(Type.INT); // y
|
|
map(Type.INT); // z
|
|
map(Type.FLOAT); // volume
|
|
map(Type.UNSIGNED_BYTE); // pitch
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.EFFECT, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT); // effect id
|
|
map(Types1_7_6.POSITION_UBYTE); // position
|
|
map(Type.INT); // data
|
|
map(Type.BOOLEAN); // server wide
|
|
handler(wrapper -> {
|
|
final int effectId = wrapper.get(Type.INT, 0); // effect id
|
|
int data = wrapper.get(Type.INT, 1); // data
|
|
final boolean disableRelativeVolume = wrapper.get(Type.BOOLEAN, 0); // server wide
|
|
|
|
if (!disableRelativeVolume && effectId == 2001) { // block break effect
|
|
final ChunkTracker chunkTracker = wrapper.user().get(ChunkTracker.class);
|
|
final int blockID = data & 4095;
|
|
final int blockData = data >> 12 & 255;
|
|
final IdAndData block = new IdAndData(blockID, blockData);
|
|
chunkTracker.remapBlockParticle(block);
|
|
data = (block.getId() & 4095) | block.getData() << 12;
|
|
|
|
wrapper.set(Type.INT, 1, data);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.SPAWN_PARTICLE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_6_4.STRING, Type.STRING); // particle
|
|
map(Type.FLOAT); // x
|
|
map(Type.FLOAT); // y
|
|
map(Type.FLOAT); // z
|
|
map(Type.FLOAT); // offset x
|
|
map(Type.FLOAT); // offset y
|
|
map(Type.FLOAT); // offset z
|
|
map(Type.FLOAT); // speed
|
|
map(Type.INT); // amount
|
|
handler(wrapper -> {
|
|
final String[] parts = wrapper.get(Type.STRING, 0).split("_", 3);
|
|
if (parts[0].equals("tilecrack")) {
|
|
parts[0] = "blockcrack";
|
|
}
|
|
if (parts[0].equals("blockcrack") || parts[0].equals("blockdust")) {
|
|
final int id = Integer.parseInt(parts[1]);
|
|
final int metadata = Integer.parseInt(parts[2]);
|
|
final IdAndData block = new IdAndData(id, metadata);
|
|
wrapper.user().get(ChunkTracker.class).remapBlockParticle(block);
|
|
parts[1] = String.valueOf(block.getId());
|
|
parts[2] = String.valueOf(block.getData());
|
|
}
|
|
|
|
wrapper.set(Type.STRING, 0, String.join("_", parts));
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.GAME_EVENT, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.BYTE, Type.UNSIGNED_BYTE); // reason
|
|
map(Type.BYTE, Type.FLOAT); // value
|
|
handler(wrapper -> {
|
|
final short gameState = wrapper.get(Type.UNSIGNED_BYTE, 0);
|
|
if (gameState == 1) {
|
|
final PacketWrapper startRain = PacketWrapper.create(ClientboundPackets1_7_2.GAME_EVENT, wrapper.user());
|
|
startRain.write(Type.UNSIGNED_BYTE, (short) 7);
|
|
startRain.write(Type.FLOAT, 1.0f);
|
|
|
|
wrapper.send(Protocol1_7_2_5to1_6_4.class);
|
|
startRain.send(Protocol1_7_2_5to1_6_4.class);
|
|
wrapper.cancel();
|
|
} else if (gameState == 2) {
|
|
final PacketWrapper stopRain = PacketWrapper.create(ClientboundPackets1_7_2.GAME_EVENT, wrapper.user());
|
|
stopRain.write(Type.UNSIGNED_BYTE, (short) 7);
|
|
stopRain.write(Type.FLOAT, 0.0f);
|
|
|
|
wrapper.send(Protocol1_7_2_5to1_6_4.class);
|
|
stopRain.send(Protocol1_7_2_5to1_6_4.class);
|
|
wrapper.cancel();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.SPAWN_GLOBAL_ENTITY, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.INT, Type.VAR_INT); // entity id
|
|
map(Type.BYTE); // type id
|
|
map(Type.INT); // x
|
|
map(Type.INT); // y
|
|
map(Type.INT); // z
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.OPEN_WINDOW, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.UNSIGNED_BYTE); // window id
|
|
map(Type.UNSIGNED_BYTE); // window type
|
|
map(Types1_6_4.STRING, Type.STRING); // title
|
|
map(Type.UNSIGNED_BYTE); // slots
|
|
map(Type.BOOLEAN); // use provided title
|
|
// more conditional data
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.CLOSE_WINDOW, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.BYTE, Type.UNSIGNED_BYTE); // window id
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.SET_SLOT, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.BYTE); // window id
|
|
map(Type.SHORT); // slot
|
|
map(Types1_7_6.ITEM); // item
|
|
handler(wrapper -> itemRewriter.handleItemToClient(wrapper.user(), wrapper.get(Types1_7_6.ITEM, 0)));
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.WINDOW_ITEMS, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.BYTE, Type.UNSIGNED_BYTE); // window id
|
|
handler(wrapper -> {
|
|
final Item[] items = wrapper.passthrough(Types1_7_6.ITEM_ARRAY); // items
|
|
for (Item item : items) {
|
|
itemRewriter.handleItemToClient(wrapper.user(), item);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.UPDATE_SIGN, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_7_6.POSITION_SHORT); // position
|
|
map(Types1_6_4.STRING, Type.STRING); // line 1
|
|
map(Types1_6_4.STRING, Type.STRING); // line 2
|
|
map(Types1_6_4.STRING, Type.STRING); // line 3
|
|
map(Types1_6_4.STRING, Type.STRING); // line 4
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.MAP_DATA, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
read(Type.SHORT); // item id
|
|
map(Type.SHORT, Type.VAR_INT); // map id
|
|
map(Type.SHORT_BYTE_ARRAY); // data
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.BLOCK_ENTITY_DATA, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_7_6.POSITION_SHORT); // position
|
|
map(Type.BYTE, Type.UNSIGNED_BYTE); // type
|
|
map(Types1_7_6.NBT); // data
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.OPEN_SIGN_EDITOR, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
read(Type.BYTE); // always 0
|
|
map(Types1_7_6.POSITION_INT); // position
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.STATISTICS, wrapper -> {
|
|
wrapper.cancel();
|
|
final StatisticsStorage statisticsStorage = wrapper.user().get(StatisticsStorage.class);
|
|
final int statId = wrapper.read(Type.INT); // statistic id
|
|
final int increment = wrapper.read(Type.INT); // increment
|
|
statisticsStorage.values.put(statId, statisticsStorage.values.get(statId) + increment);
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.PLAYER_INFO, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_6_4.STRING, Type.STRING); // name
|
|
map(Type.BOOLEAN); // online
|
|
map(Type.SHORT); // ping
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.TAB_COMPLETE, wrapper -> {
|
|
final String completions = wrapper.read(Types1_6_4.STRING); // completions
|
|
final String[] completionsArray = completions.split("\0");
|
|
wrapper.write(Type.VAR_INT, completionsArray.length); // completions count
|
|
for (String s : completionsArray) {
|
|
wrapper.write(Type.STRING, s); // completion
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.SCOREBOARD_OBJECTIVE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_6_4.STRING, Type.STRING); // name
|
|
map(Types1_6_4.STRING, Type.STRING); // value
|
|
map(Type.BYTE); // mode
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.UPDATE_SCORE, wrapper -> {
|
|
wrapper.write(Type.STRING, wrapper.read(Types1_6_4.STRING)); // name
|
|
final byte mode = wrapper.passthrough(Type.BYTE); // mode
|
|
if (mode == 0) {
|
|
wrapper.write(Type.STRING, wrapper.read(Types1_6_4.STRING)); // objective
|
|
wrapper.passthrough(Type.INT); // score
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.DISPLAY_SCOREBOARD, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.BYTE); // position
|
|
map(Types1_6_4.STRING, Type.STRING); // name
|
|
}
|
|
});
|
|
this.registerClientbound(ClientboundPackets1_6_4.TEAMS, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_6_4.STRING, Type.STRING); // name
|
|
handler(wrapper -> {
|
|
final byte mode = wrapper.passthrough(Type.BYTE); // mode
|
|
if (mode == 0 || mode == 2) {
|
|
wrapper.write(Type.STRING, wrapper.read(Types1_6_4.STRING)); // display name
|
|
wrapper.write(Type.STRING, wrapper.read(Types1_6_4.STRING)); // prefix
|
|
wrapper.write(Type.STRING, wrapper.read(Types1_6_4.STRING)); // suffix
|
|
wrapper.passthrough(Type.BYTE); // flags
|
|
}
|
|
if (mode == 0 || mode == 3 || mode == 4) {
|
|
final int count = wrapper.passthrough(Type.SHORT); // count
|
|
for (int i = 0; i < count; i++) {
|
|
wrapper.write(Type.STRING, wrapper.read(Types1_6_4.STRING)); // player name
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
this.registerClientboundTransition(ClientboundPackets1_6_4.PLUGIN_MESSAGE,
|
|
ClientboundPackets1_7_2.PLUGIN_MESSAGE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
handler(wrapper -> {
|
|
final String channel = wrapper.read(Types1_6_4.STRING); // channel
|
|
int length = wrapper.read(Type.SHORT); // length
|
|
|
|
if (length < 0) {
|
|
wrapper.write(Type.STRING, channel); // channel
|
|
wrapper.write(Type.UNSIGNED_SHORT, 0); // length
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (channel.equals("MC|TrList")) {
|
|
wrapper.passthrough(Type.INT); // window id
|
|
final int count = wrapper.passthrough(Type.UNSIGNED_BYTE); // count
|
|
for (int i = 0; i < count; i++) {
|
|
itemRewriter.handleItemToClient(wrapper.user(), wrapper.passthrough(Types1_7_6.ITEM)); // item 1
|
|
itemRewriter.handleItemToClient(wrapper.user(), wrapper.passthrough(Types1_7_6.ITEM)); // item 3
|
|
if (wrapper.passthrough(Type.BOOLEAN)) { // has 3 items
|
|
itemRewriter.handleItemToClient(wrapper.user(), wrapper.passthrough(Types1_7_6.ITEM)); // item 2
|
|
}
|
|
wrapper.passthrough(Type.BOOLEAN); // unavailable
|
|
}
|
|
length = PacketUtil.calculateLength(wrapper);
|
|
}
|
|
} catch (Exception e) {
|
|
if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) {
|
|
Via.getPlatform().getLogger().log(Level.WARNING, "Failed to handle packet", e);
|
|
}
|
|
wrapper.cancel();
|
|
return;
|
|
}
|
|
|
|
wrapper.resetReader();
|
|
wrapper.write(Type.STRING, channel); // channel
|
|
wrapper.write(Type.UNSIGNED_SHORT, length); // length
|
|
});
|
|
}
|
|
}, State.LOGIN, (PacketHandler) PacketWrapper::cancel
|
|
);
|
|
this.registerClientboundTransition(ClientboundPackets1_6_4.SHARED_KEY, ClientboundLoginPackets.GAME_PROFILE, (PacketHandler) wrapper -> {
|
|
final ProtocolInfo info = wrapper.user().getProtocolInfo();
|
|
final ProtocolMetadataStorage protocolMetadata = wrapper.user().get(ProtocolMetadataStorage.class);
|
|
wrapper.read(Type.SHORT_BYTE_ARRAY); // shared secret
|
|
wrapper.read(Type.SHORT_BYTE_ARRAY); // verify token
|
|
wrapper.write(Type.STRING, info.getUuid().toString().replace("-", "")); // uuid
|
|
wrapper.write(Type.STRING, info.getUsername()); // user name
|
|
|
|
if (!protocolMetadata.skipEncryption) {
|
|
Via.getManager().getProviders().get(EncryptionProvider.class).enableDecryption(wrapper.user());
|
|
}
|
|
|
|
// Parts of BaseProtocol1_7 GAME_PROFILE handler
|
|
if (info.protocolVersion().olderThan(ProtocolVersion.v1_20_2)) {
|
|
info.setState(State.PLAY);
|
|
}
|
|
Via.getManager().getConnectionManager().onLoginSuccess(wrapper.user());
|
|
if (!info.getPipeline().hasNonBaseProtocols()) {
|
|
wrapper.user().setActive(false);
|
|
}
|
|
if (Via.getManager().isDebug()) {
|
|
ViaLegacy.getPlatform().getLogger().log(Level.INFO, "{0} logged in with protocol {1}, Route: {2}", new Object[]{info.getUsername(), info.protocolVersion().getName(), Joiner.on(", ").join(info.getPipeline().pipes(), ", ")});
|
|
}
|
|
|
|
final PacketWrapper respawn = PacketWrapper.create(ServerboundPackets1_6_4.CLIENT_STATUS, wrapper.user());
|
|
respawn.write(Type.BYTE, (byte) 0); // force respawn
|
|
respawn.sendToServer(Protocol1_7_2_5to1_6_4.class);
|
|
});
|
|
this.registerClientboundTransition(ClientboundPackets1_6_4.SERVER_AUTH_DATA, ClientboundLoginPackets.HELLO, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_6_4.STRING, Type.STRING); // server hash
|
|
map(Type.SHORT_BYTE_ARRAY); // public key
|
|
map(Type.SHORT_BYTE_ARRAY); // verify token
|
|
handler(wrapper -> {
|
|
final ProtocolMetadataStorage protocolMetadata = wrapper.user().get(ProtocolMetadataStorage.class);
|
|
final String serverHash = wrapper.get(Type.STRING, 0);
|
|
protocolMetadata.authenticate = !serverHash.equals("-");
|
|
});
|
|
}
|
|
});
|
|
this.registerClientboundTransition(ClientboundPackets1_6_4.DISCONNECT,
|
|
ClientboundStatusPackets.STATUS_RESPONSE, (PacketHandler) wrapper -> {
|
|
final String reason = wrapper.read(Types1_6_4.STRING); // reason
|
|
try {
|
|
final String[] motdParts = reason.split("\0");
|
|
final JsonObject rootObject = new JsonObject();
|
|
final JsonObject descriptionObject = new JsonObject();
|
|
final JsonObject playersObject = new JsonObject();
|
|
final JsonObject versionObject = new JsonObject();
|
|
|
|
descriptionObject.addProperty("text", motdParts[3]);
|
|
playersObject.addProperty("max", Integer.parseInt(motdParts[5]));
|
|
playersObject.addProperty("online", Integer.parseInt(motdParts[4]));
|
|
versionObject.addProperty("name", motdParts[2]);
|
|
versionObject.addProperty("protocol", Integer.parseInt(motdParts[1]));
|
|
rootObject.add("description", descriptionObject);
|
|
rootObject.add("players", playersObject);
|
|
rootObject.add("version", versionObject);
|
|
|
|
wrapper.write(Type.STRING, rootObject.toString());
|
|
} catch (Throwable e) {
|
|
ViaLegacy.getPlatform().getLogger().log(Level.WARNING, "Could not parse 1.6.4 ping: " + reason, e);
|
|
wrapper.cancel();
|
|
}
|
|
}, ClientboundLoginPackets.LOGIN_DISCONNECT, new PacketHandlers() {
|
|
@Override
|
|
protected void register() {
|
|
map(Types1_6_4.STRING, Type.STRING, ChatComponentRewriter::toClientDisconnect); // reason
|
|
}
|
|
}, ClientboundPackets1_7_2.DISCONNECT, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_6_4.STRING, Type.STRING, ChatComponentRewriter::toClientDisconnect); // reason
|
|
}
|
|
}
|
|
);
|
|
this.cancelClientbound(ClientboundPackets1_6_4.CREATIVE_INVENTORY_ACTION);
|
|
|
|
this.registerServerboundTransition(ServerboundStatusPackets.STATUS_REQUEST, ServerboundPackets1_6_4.SERVER_PING, wrapper -> {
|
|
final HandshakeStorage handshakeStorage = wrapper.user().get(HandshakeStorage.class);
|
|
final String ip = handshakeStorage.getHostname();
|
|
final int port = handshakeStorage.getPort();
|
|
wrapper.write(Type.UNSIGNED_BYTE, (short) 1); // always 1
|
|
wrapper.write(Type.UNSIGNED_BYTE, (short) ServerboundPackets1_6_4.PLUGIN_MESSAGE.getId()); // packet id
|
|
wrapper.write(Types1_6_4.STRING, "MC|PingHost"); // channel
|
|
wrapper.write(Type.SHORT, (short) (3 + 2 * ip.length() + 4)); // length
|
|
wrapper.write(Type.UNSIGNED_BYTE, (short) wrapper.user().getProtocolInfo().serverProtocolVersion().getVersion()); // protocol Id
|
|
wrapper.write(Types1_6_4.STRING, ip); // hostname
|
|
wrapper.write(Type.INT, port); // port
|
|
});
|
|
this.registerServerboundTransition(ServerboundStatusPackets.PING_REQUEST, null, wrapper -> {
|
|
wrapper.cancel();
|
|
final PacketWrapper pong = PacketWrapper.create(ClientboundStatusPackets.PONG_RESPONSE, wrapper.user());
|
|
pong.write(Type.LONG, wrapper.read(Type.LONG)); // start time
|
|
pong.send(Protocol1_7_2_5to1_6_4.class);
|
|
});
|
|
this.registerServerboundTransition(ServerboundLoginPackets.HELLO, ServerboundPackets1_6_4.CLIENT_PROTOCOL, wrapper -> {
|
|
final HandshakeStorage handshakeStorage = wrapper.user().get(HandshakeStorage.class);
|
|
|
|
final String name = wrapper.read(Type.STRING); // user name
|
|
|
|
wrapper.write(Type.UNSIGNED_BYTE, (short) wrapper.user().getProtocolInfo().serverProtocolVersion().getVersion()); // protocol id
|
|
wrapper.write(Types1_6_4.STRING, name); // user name
|
|
wrapper.write(Types1_6_4.STRING, handshakeStorage.getHostname()); // hostname
|
|
wrapper.write(Type.INT, handshakeStorage.getPort()); // port
|
|
|
|
final ProtocolInfo info = wrapper.user().getProtocolInfo();
|
|
// Set the information early
|
|
if (info.getUsername() == null) {
|
|
info.setUsername(name);
|
|
}
|
|
if (info.getUuid() == null) {
|
|
info.setUuid(ViaLegacy.getConfig().isLegacySkinLoading() ? Via.getManager().getProviders().get(GameProfileFetcher.class).getMojangUUID(name) : new GameProfile(name).uuid);
|
|
}
|
|
});
|
|
this.registerServerboundTransition(ServerboundLoginPackets.ENCRYPTION_KEY, ServerboundPackets1_6_4.SHARED_KEY, null);
|
|
this.registerServerbound(ServerboundPackets1_7_2.CHAT_MESSAGE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.STRING, Types1_6_4.STRING); // message
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.INTERACT_ENTITY, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
handler(wrapper -> wrapper.write(Type.INT, wrapper.user().get(PlayerInfoStorage.class).entityId)); // player id
|
|
map(Type.INT); // entity id
|
|
map(Type.BYTE); // mode
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.PLAYER_MOVEMENT, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.BOOLEAN); // onGround
|
|
handler(wrapper -> wrapper.user().get(PlayerInfoStorage.class).onGround = wrapper.get(Type.BOOLEAN, 0));
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.PLAYER_POSITION, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.DOUBLE); // x
|
|
map(Type.DOUBLE); // y
|
|
map(Type.DOUBLE); // stance
|
|
map(Type.DOUBLE); // z
|
|
map(Type.BOOLEAN); // onGround
|
|
handler(wrapper -> {
|
|
final PlayerInfoStorage playerInfoStorage = wrapper.user().get(PlayerInfoStorage.class);
|
|
playerInfoStorage.posX = wrapper.get(Type.DOUBLE, 0);
|
|
playerInfoStorage.posY = wrapper.get(Type.DOUBLE, 1);
|
|
playerInfoStorage.posZ = wrapper.get(Type.DOUBLE, 3);
|
|
playerInfoStorage.onGround = wrapper.get(Type.BOOLEAN, 0);
|
|
});
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.PLAYER_ROTATION, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.FLOAT); // yaw
|
|
map(Type.FLOAT); // pitch
|
|
map(Type.BOOLEAN); // onGround
|
|
handler(wrapper -> {
|
|
final PlayerInfoStorage playerInfoStorage = wrapper.user().get(PlayerInfoStorage.class);
|
|
playerInfoStorage.yaw = wrapper.get(Type.FLOAT, 0);
|
|
playerInfoStorage.pitch = wrapper.get(Type.FLOAT, 1);
|
|
playerInfoStorage.onGround = wrapper.get(Type.BOOLEAN, 0);
|
|
});
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.PLAYER_POSITION_AND_ROTATION, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.DOUBLE); // x
|
|
map(Type.DOUBLE); // y
|
|
map(Type.DOUBLE); // stance
|
|
map(Type.DOUBLE); // z
|
|
map(Type.FLOAT); // yaw
|
|
map(Type.FLOAT); // pitch
|
|
map(Type.BOOLEAN); // onGround
|
|
handler(wrapper -> {
|
|
final PlayerInfoStorage playerInfoStorage = wrapper.user().get(PlayerInfoStorage.class);
|
|
playerInfoStorage.posX = wrapper.get(Type.DOUBLE, 0);
|
|
playerInfoStorage.posY = wrapper.get(Type.DOUBLE, 1);
|
|
playerInfoStorage.posZ = wrapper.get(Type.DOUBLE, 3);
|
|
playerInfoStorage.yaw = wrapper.get(Type.FLOAT, 0);
|
|
playerInfoStorage.pitch = wrapper.get(Type.FLOAT, 1);
|
|
playerInfoStorage.onGround = wrapper.get(Type.BOOLEAN, 0);
|
|
});
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.PLAYER_BLOCK_PLACEMENT, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_7_6.POSITION_UBYTE); // position
|
|
map(Type.UNSIGNED_BYTE); // direction
|
|
map(Types1_7_6.ITEM); // item
|
|
handler(wrapper -> itemRewriter.handleItemToServer(wrapper.user(), wrapper.get(Types1_7_6.ITEM, 0)));
|
|
map(Type.UNSIGNED_BYTE); // offset x
|
|
map(Type.UNSIGNED_BYTE); // offset y
|
|
map(Type.UNSIGNED_BYTE); // offset z
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.CLICK_WINDOW, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.BYTE); // windowId
|
|
map(Type.SHORT); // slot
|
|
map(Type.BYTE); // button
|
|
map(Type.SHORT); // action
|
|
map(Type.BYTE); // mode
|
|
map(Types1_7_6.ITEM); // item
|
|
handler(wrapper -> itemRewriter.handleItemToServer(wrapper.user(), wrapper.get(Types1_7_6.ITEM, 0)));
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.UPDATE_SIGN, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Types1_7_6.POSITION_SHORT); // position
|
|
map(Type.STRING, Types1_6_4.STRING); // line 1
|
|
map(Type.STRING, Types1_6_4.STRING); // line 2
|
|
map(Type.STRING, Types1_6_4.STRING); // line 3
|
|
map(Type.STRING, Types1_6_4.STRING); // line 4
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.TAB_COMPLETE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.STRING, Types1_6_4.STRING); // text
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.CLIENT_SETTINGS, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
map(Type.STRING, Types1_6_4.STRING); // language
|
|
handler(wrapper -> {
|
|
byte renderDistance = wrapper.read(Type.BYTE); // render distance
|
|
if (renderDistance <= 2) {
|
|
renderDistance = 3; // TINY
|
|
} else if (renderDistance <= 4) {
|
|
renderDistance = 2; // SHORT
|
|
} else if (renderDistance <= 8) {
|
|
renderDistance = 1; // NORMAL
|
|
} else { // >= 16
|
|
renderDistance = 0; // FAR
|
|
}
|
|
wrapper.write(Type.BYTE, renderDistance);
|
|
|
|
final byte chatVisibility = wrapper.read(Type.BYTE); // chat visibility
|
|
final boolean enableColors = wrapper.read(Type.BOOLEAN); // enable colors
|
|
final byte mask = (byte) (chatVisibility | (enableColors ? 1 : 0) << 3);
|
|
wrapper.write(Type.BYTE, mask); // mask
|
|
});
|
|
map(Type.BYTE); // difficulty
|
|
map(Type.BOOLEAN); // show cape
|
|
}
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.CLIENT_STATUS, wrapper -> {
|
|
final int action = wrapper.read(Type.VAR_INT); // action
|
|
|
|
if (action == 1) { // Request Statistics
|
|
final Object2IntMap<String> loadedStatistics = new Object2IntOpenHashMap<>();
|
|
for (Int2IntMap.Entry entry : wrapper.user().get(StatisticsStorage.class).values.int2IntEntrySet()) {
|
|
final String key = StatisticRewriter.map(entry.getIntKey());
|
|
if (key == null) continue;
|
|
loadedStatistics.put(key, entry.getIntValue());
|
|
}
|
|
|
|
final PacketWrapper statistics = PacketWrapper.create(ClientboundPackets1_8.STATISTICS, wrapper.user());
|
|
statistics.write(Type.VAR_INT, loadedStatistics.size()); // count
|
|
for (Object2IntMap.Entry<String> entry : loadedStatistics.object2IntEntrySet()) {
|
|
statistics.write(Type.STRING, entry.getKey()); // statistic name
|
|
statistics.write(Type.VAR_INT, entry.getIntValue()); // statistic value
|
|
}
|
|
statistics.send(Protocol1_7_2_5to1_6_4.class);
|
|
}
|
|
if (action != 0) {
|
|
wrapper.cancel();
|
|
return;
|
|
}
|
|
wrapper.write(Type.BYTE, (byte) 1); // force respawn
|
|
});
|
|
this.registerServerbound(ServerboundPackets1_7_2.PLUGIN_MESSAGE, new PacketHandlers() {
|
|
@Override
|
|
public void register() {
|
|
handler(wrapper -> {
|
|
final String channel = wrapper.read(Type.STRING); // channel
|
|
short length = wrapper.read(Type.SHORT); // length
|
|
|
|
try {
|
|
switch (channel) {
|
|
case "MC|BEdit":
|
|
case "MC|BSign":
|
|
itemRewriter.handleItemToServer(wrapper.user(), wrapper.passthrough(Types1_7_6.ITEM));
|
|
length = (short) PacketUtil.calculateLength(wrapper);
|
|
break;
|
|
case "MC|AdvCdm":
|
|
final byte type = wrapper.read(Type.BYTE); // command block type
|
|
if (type == 0) {
|
|
wrapper.passthrough(Type.INT); // x
|
|
wrapper.passthrough(Type.INT); // y
|
|
wrapper.passthrough(Type.INT); // z
|
|
wrapper.passthrough(Type.STRING); // command
|
|
} else {
|
|
wrapper.cancel();
|
|
return;
|
|
}
|
|
length = (short) PacketUtil.calculateLength(wrapper);
|
|
break;
|
|
}
|
|
} catch (Exception e) {
|
|
if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) {
|
|
Via.getPlatform().getLogger().log(Level.WARNING, "Failed to handle packet", e);
|
|
}
|
|
wrapper.cancel();
|
|
return;
|
|
}
|
|
|
|
wrapper.resetReader();
|
|
wrapper.write(Types1_6_4.STRING, channel); // channel
|
|
wrapper.write(Type.SHORT, length); // length
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
private void rewriteMetadata(final UserConnection user, final List<Metadata> metadataList) {
|
|
for (Metadata metadata : metadataList) {
|
|
if (metadata.metaType().equals(MetaType1_6_4.Slot)) {
|
|
itemRewriter.handleItemToClient(user, metadata.value());
|
|
}
|
|
metadata.setMetaType(MetaType1_7_6.byId(metadata.metaType().typeId()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void register(ViaProviders providers) {
|
|
providers.require(EncryptionProvider.class);
|
|
}
|
|
|
|
@Override
|
|
public void init(UserConnection userConnection) {
|
|
userConnection.put(new PreNettySplitter(Protocol1_7_2_5to1_6_4.class, ClientboundPackets1_6_4::getPacket));
|
|
|
|
userConnection.put(new PlayerInfoStorage());
|
|
userConnection.put(new StatisticsStorage());
|
|
userConnection.put(new DimensionTracker());
|
|
userConnection.put(new ChunkTracker(userConnection)); // Set again in JOIN_GAME handler for version comparisons to work
|
|
|
|
if (userConnection.getChannel() != null) {
|
|
userConnection.getChannel().pipeline().addFirst(new ChannelOutboundHandlerAdapter() {
|
|
@Override
|
|
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
|
|
if (ctx.channel().isWritable() && userConnection.getProtocolInfo().getClientState().equals(State.PLAY) && userConnection.get(PlayerInfoStorage.class).entityId != -1) {
|
|
final PacketWrapper disconnect = PacketWrapper.create(ServerboundPackets1_6_4.DISCONNECT, userConnection);
|
|
disconnect.write(Types1_6_4.STRING, "Quitting"); // reason
|
|
disconnect.sendToServer(Protocol1_7_2_5to1_6_4.class);
|
|
}
|
|
|
|
super.close(ctx, promise);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public LegacyItemRewriter<Protocol1_7_2_5to1_6_4> getItemRewriter() {
|
|
return this.itemRewriter;
|
|
}
|
|
|
|
}
|