ViaLegacy/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_6_1to1_5_2/Protocol1_6_1to1_5_2.java

417 lines
20 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_6_1to1_5_2;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_10;
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.libs.mcstructs.text.components.StringComponent;
import com.viaversion.viaversion.libs.mcstructs.text.serializer.TextComponentSerializer;
import net.raphimc.vialegacy.ViaLegacy;
import net.raphimc.vialegacy.api.protocol.StatelessProtocol;
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_6_1to1_5_2.metadata.MetadataRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.rewriter.ItemRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.rewriter.SoundRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.storage.AttachTracker;
import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.storage.EntityTracker;
import net.raphimc.vialegacy.protocols.release.protocol1_6_2to1_6_1.ClientboundPackets1_6_1;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.ServerboundPackets1_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_8to1_7_6_10.metadata.MetaIndex1_8to1_7_6;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.types.Types1_7_6;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.logging.Level;
public class Protocol1_6_1to1_5_2 extends StatelessProtocol<ClientboundPackets1_5_2, ClientboundPackets1_6_1, ServerboundPackets1_5_2, ServerboundPackets1_6_4> {
private final LegacyItemRewriter<Protocol1_6_1to1_5_2> itemRewriter = new ItemRewriter(this);
public Protocol1_6_1to1_5_2() {
super(ClientboundPackets1_5_2.class, ClientboundPackets1_6_1.class, ServerboundPackets1_5_2.class, ServerboundPackets1_6_4.class);
}
@Override
protected void registerPackets() {
this.itemRewriter.register();
this.registerClientbound(ClientboundPackets1_5_2.JOIN_GAME, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // entity id
map(Types1_6_4.STRING); // level type
map(Type.BYTE); // game mode
map(Type.BYTE); // dimension id
map(Type.BYTE); // difficulty
map(Type.BYTE); // world height
map(Type.BYTE); // max players
handler(wrapper -> {
final int entityId = wrapper.get(Type.INT, 0);
final EntityTracker tracker = wrapper.user().get(EntityTracker.class);
tracker.getTrackedEntities().put(entityId, EntityTypes1_10.EntityType.PLAYER);
tracker.setPlayerID(entityId);
});
}
});
this.registerClientbound(ClientboundPackets1_5_2.CHAT_MESSAGE, new PacketHandlers() {
@Override
public void register() {
map(Types1_6_4.STRING, Types1_6_4.STRING, msg -> TextComponentSerializer.V1_6.serialize(new StringComponent(msg))); // message
}
});
this.registerClientbound(ClientboundPackets1_5_2.UPDATE_HEALTH, new PacketHandlers() {
@Override
public void register() {
map(Type.SHORT, Type.FLOAT); // health
map(Type.SHORT); // food
map(Type.FLOAT); // saturation
}
});
this.registerClientbound(ClientboundPackets1_5_2.SPAWN_PLAYER, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // entity id
map(Types1_6_4.STRING); // name
map(Type.INT); // x
map(Type.INT); // y
map(Type.INT); // z
map(Type.BYTE); // yaw
map(Type.BYTE); // pitch
map(Type.UNSIGNED_SHORT); // item
map(Types1_6_4.METADATA_LIST); // metadata
handler(wrapper -> MetadataRewriter.transform(EntityTypes1_10.EntityType.PLAYER, wrapper.get(Types1_6_4.METADATA_LIST, 0)));
handler(wrapper -> {
final int entityId = wrapper.get(Type.INT, 0);
wrapper.user().get(EntityTracker.class).getTrackedEntities().put(entityId, EntityTypes1_10.EntityType.PLAYER);
});
}
});
this.registerClientbound(ClientboundPackets1_5_2.COLLECT_ITEM, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // collected entity id
map(Type.INT); // collector entity id
handler(wrapper -> wrapper.user().get(EntityTracker.class).getTrackedEntities().remove(wrapper.get(Type.INT, 0)));
}
});
this.registerClientbound(ClientboundPackets1_5_2.SPAWN_ENTITY, new PacketHandlers() {
@Override
public void register() {
map(Type.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
// more conditional data
handler(wrapper -> {
final int entityID = wrapper.get(Type.INT, 0);
final int typeID = wrapper.get(Type.BYTE, 0);
wrapper.user().get(EntityTracker.class).getTrackedEntities().put(entityID, EntityTypes1_10.getTypeFromId(typeID, true));
});
}
});
this.registerClientbound(ClientboundPackets1_5_2.SPAWN_MOB, new PacketHandlers() {
@Override
public void register() {
map(Type.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); // metadata
handler(wrapper -> {
final int entityID = wrapper.get(Type.INT, 0);
final int typeID = wrapper.get(Type.UNSIGNED_BYTE, 0);
final EntityTypes1_10.EntityType entityType = EntityTypes1_10.getTypeFromId(typeID, false);
final List<Metadata> metadataList = wrapper.get(Types1_6_4.METADATA_LIST, 0);
wrapper.user().get(EntityTracker.class).getTrackedEntities().put(entityID, entityType);
MetadataRewriter.transform(entityType, metadataList);
if (entityType.isOrHasParent(EntityTypes1_10.EntityType.WOLF)) {
handleWolfMetadata(entityID, metadataList, wrapper);
}
});
}
});
this.registerClientbound(ClientboundPackets1_5_2.SPAWN_PAINTING, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // entity id
map(Types1_6_4.STRING); // motive
map(Types1_7_6.POSITION_INT); // position
map(Type.INT); // rotation
handler(wrapper -> {
final int entityID = wrapper.get(Type.INT, 0);
wrapper.user().get(EntityTracker.class).getTrackedEntities().put(entityID, EntityTypes1_10.EntityType.PAINTING);
});
}
});
this.registerClientbound(ClientboundPackets1_5_2.SPAWN_EXPERIENCE_ORB, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // entity id
map(Type.INT); // x
map(Type.INT); // y
map(Type.INT); // z
map(Type.SHORT); // count
handler(wrapper -> {
final int entityID = wrapper.get(Type.INT, 0);
wrapper.user().get(EntityTracker.class).getTrackedEntities().put(entityID, EntityTypes1_10.EntityType.EXPERIENCE_ORB);
});
}
});
this.registerClientbound(ClientboundPackets1_5_2.DESTROY_ENTITIES, new PacketHandlers() {
@Override
public void register() {
map(Types1_7_6.INT_ARRAY); // entity ids
handler(wrapper -> {
final EntityTracker tracker = wrapper.user().get(EntityTracker.class);
for (int entityId : wrapper.get(Types1_7_6.INT_ARRAY, 0)) {
tracker.getTrackedEntities().remove(entityId);
}
});
}
});
this.registerClientbound(ClientboundPackets1_5_2.ATTACH_ENTITY, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // riding entity id
map(Type.INT); // vehicle entity id
handler(wrapper -> {
final AttachTracker attachTracker = wrapper.user().get(AttachTracker.class);
final EntityTracker entityTracker = wrapper.user().get(EntityTracker.class);
final int ridingId = wrapper.get(Type.INT, 0);
final int vehicleId = wrapper.get(Type.INT, 1);
if (entityTracker.getPlayerID() == ridingId) {
attachTracker.vehicleEntityId = vehicleId;
}
});
create(Type.UNSIGNED_BYTE, (short) 0); // leash state
}
});
this.registerClientbound(ClientboundPackets1_5_2.ENTITY_METADATA, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // entity id
map(Types1_6_4.METADATA_LIST); // metadata
handler(wrapper -> {
final EntityTracker tracker = wrapper.user().get(EntityTracker.class);
final List<Metadata> metadataList = wrapper.get(Types1_6_4.METADATA_LIST, 0);
final int entityID = wrapper.get(Type.INT, 0);
final EntityTypes1_10.EntityType entityType = tracker.getTrackedEntities().get(entityID);
if (tracker.getTrackedEntities().containsKey(entityID)) {
MetadataRewriter.transform(entityType, metadataList);
if (metadataList.isEmpty()) wrapper.cancel();
if (entityType.isOrHasParent(EntityTypes1_10.EntityType.WOLF)) {
handleWolfMetadata(entityID, metadataList, wrapper);
}
} else {
wrapper.cancel();
}
});
}
});
this.registerClientbound(ClientboundPackets1_5_2.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.5.2 sound '" + oldSound + "'");
}
newSound = "";
}
if (newSound.isEmpty()) {
wrapper.cancel();
return;
}
wrapper.write(Types1_6_4.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_5_2.STATISTICS, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // statistic id
map(Type.BYTE, Type.INT); // increment
}
});
this.registerClientbound(ClientboundPackets1_5_2.PLAYER_ABILITIES, new PacketHandlers() {
@Override
public void register() {
map(Type.BYTE); // flags
handler(wrapper -> {
final float flySpeed = wrapper.read(Type.BYTE) / 255F; // fly speed
final float walkSpeed = wrapper.read(Type.BYTE) / 255F; // walk speed
wrapper.write(Type.FLOAT, flySpeed);
wrapper.write(Type.FLOAT, walkSpeed);
final PacketWrapper entityProperties = PacketWrapper.create(ClientboundPackets1_6_1.ENTITY_PROPERTIES, wrapper.user());
entityProperties.write(Type.INT, wrapper.user().get(EntityTracker.class).getPlayerID()); // entity id
entityProperties.write(Type.INT, 1); // count
entityProperties.write(Types1_6_4.STRING, "generic.movementSpeed"); // id
entityProperties.write(Type.DOUBLE, (double) walkSpeed); // value
wrapper.send(Protocol1_6_1to1_5_2.class);
entityProperties.send(Protocol1_6_1to1_5_2.class);
wrapper.cancel();
});
}
});
this.registerClientbound(ClientboundPackets1_5_2.PLUGIN_MESSAGE, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
String channel = wrapper.read(Types1_6_4.STRING); // channel
short length = wrapper.read(Type.SHORT); // length
try {
if (channel.equals("MC|TPack")) {
channel = "MC|RPack";
final String[] data = new String(wrapper.read(Type.REMAINING_BYTES), StandardCharsets.UTF_8).split("\0"); // data
final String url = data[0];
final String resolution = data[1];
if (!resolution.equals("16")) {
wrapper.cancel();
return;
}
wrapper.write(Type.REMAINING_BYTES, url.getBytes(StandardCharsets.UTF_8));
length = (short) 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(Types1_6_4.STRING, channel); // channel
wrapper.write(Type.SHORT, length); // length
});
}
});
this.registerServerbound(ServerboundPackets1_6_4.SERVER_PING, wrapper -> {
wrapper.clearPacket();
wrapper.write(Type.BYTE, (byte) 1); // readSuccessfully
});
this.registerServerbound(ServerboundPackets1_6_4.ENTITY_ACTION, new PacketHandlers() {
@Override
public void register() {
map(Type.INT); // entity id
map(Type.BYTE); // action id
read(Type.INT); // action parameter
handler(wrapper -> {
if (wrapper.get(Type.BYTE, 0) > 5) wrapper.cancel();
});
}
});
this.registerServerbound(ServerboundPackets1_6_4.STEER_VEHICLE, ServerboundPackets1_5_2.INTERACT_ENTITY, wrapper -> {
final AttachTracker attachTracker = wrapper.user().get(AttachTracker.class);
final EntityTracker entityTracker = wrapper.user().get(EntityTracker.class);
wrapper.read(Type.FLOAT); // sideways
wrapper.read(Type.FLOAT); // forwards
wrapper.read(Type.BOOLEAN); // jumping
final boolean sneaking = wrapper.read(Type.BOOLEAN); // sneaking
if (attachTracker.lastSneakState != sneaking) {
attachTracker.lastSneakState = sneaking;
if (sneaking) {
wrapper.write(Type.INT, entityTracker.getPlayerID()); // player id
wrapper.write(Type.INT, attachTracker.vehicleEntityId); // entity id
wrapper.write(Type.BYTE, (byte) 0); // mode
return;
}
}
wrapper.cancel();
});
this.registerServerbound(ServerboundPackets1_6_4.PLAYER_ABILITIES, new PacketHandlers() {
@Override
public void register() {
map(Type.BYTE); // flags
map(Type.FLOAT, Type.BYTE, f -> (byte) (f * 255F)); // fly speed
map(Type.FLOAT, Type.BYTE, f -> (byte) (f * 255F)); // walk speed
}
});
}
private void handleWolfMetadata(final int entityId, final List<Metadata> metadataList, final PacketWrapper wrapper) throws Exception {
for (Metadata metadata : metadataList) {
final MetaIndex1_8to1_7_6 index = MetaIndex1_8to1_7_6.searchIndex(EntityTypes1_10.EntityType.WOLF, metadata.id());
if (index == MetaIndex1_8to1_7_6.TAMEABLE_FLAGS) {
if ((metadata.<Byte>value() & 4) != 0) { // is tamed
final PacketWrapper attributes = PacketWrapper.create(ClientboundPackets1_6_1.ENTITY_PROPERTIES, wrapper.user());
attributes.write(Type.INT, entityId); // entity id
attributes.write(Type.INT, 1); // count
attributes.write(Types1_6_4.STRING, "generic.maxHealth"); // id
attributes.write(Type.DOUBLE, 20.0D); // value
wrapper.send(Protocol1_6_1to1_5_2.class);
attributes.send(Protocol1_6_1to1_5_2.class);
wrapper.cancel();
}
break;
}
}
}
@Override
public void init(UserConnection userConnection) {
userConnection.put(new PreNettySplitter(Protocol1_6_1to1_5_2.class, ClientboundPackets1_5_2::getPacket));
userConnection.put(new EntityTracker());
userConnection.put(new AttachTracker());
}
@Override
public LegacyItemRewriter<Protocol1_6_1to1_5_2> getItemRewriter() {
return this.itemRewriter;
}
}