diff --git a/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/Protocol1_20_2To1_20_3.java b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/Protocol1_20_2To1_20_3.java index 84cdf228e..ccc0f89dd 100644 --- a/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/Protocol1_20_2To1_20_3.java +++ b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/Protocol1_20_2To1_20_3.java @@ -24,7 +24,9 @@ import com.viaversion.viabackwards.protocol.protocol1_20_2to1_20_3.rewriter.BlockItemPacketRewriter1_20_3; import com.viaversion.viabackwards.protocol.protocol1_20_2to1_20_3.rewriter.EntityPacketRewriter1_20_3; import com.viaversion.viabackwards.protocol.protocol1_20_2to1_20_3.storage.ResourcepackIDStorage; +import com.viaversion.viabackwards.protocol.protocol1_20_2to1_20_3.storage.SpawnPositionStorage; import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_3; import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType; import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; @@ -34,6 +36,7 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers; import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.data.entity.EntityTrackerBase; +import com.viaversion.viaversion.libs.fastutil.Pair; import com.viaversion.viaversion.libs.opennbt.tag.builtin.Tag; import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.rewriter.CommandRewriter1_19_4; import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ClientboundConfigurationPackets1_20_2; @@ -47,6 +50,7 @@ import com.viaversion.viaversion.rewriter.ComponentRewriter.ReadType; import com.viaversion.viaversion.rewriter.StatisticsRewriter; import com.viaversion.viaversion.rewriter.TagRewriter; + import java.util.BitSet; import java.util.UUID; @@ -307,6 +311,27 @@ public void register() { } } }); + registerClientbound(ClientboundPackets1_20_3.SPAWN_POSITION, wrapper -> { + final Position position = wrapper.passthrough(Type.POSITION1_14); + final float angle = wrapper.passthrough(Type.FLOAT); + + wrapper.user().get(SpawnPositionStorage.class).setSpawnPosition(Pair.of(position, angle)); + }); + registerClientbound(ClientboundPackets1_20_3.GAME_EVENT, wrapper -> { + final short reason = wrapper.passthrough(Type.UNSIGNED_BYTE); + + if (reason == 13) { // Level chunks load start + wrapper.cancel(); + final Pair spawnPositionAndAngle = wrapper.user().get(SpawnPositionStorage.class).getSpawnPosition(); + + // To emulate the old behavior, we send a fake spawn pos packet containing the actual spawn pos which forces + // the 1.20.2 client to close the downloading terrain screen like the new game state does + final PacketWrapper spawnPosition = wrapper.create(ClientboundPackets1_20_2.SPAWN_POSITION); + spawnPosition.write(Type.POSITION1_14, spawnPositionAndAngle.first()); // position + spawnPosition.write(Type.FLOAT, spawnPositionAndAngle.second()); // angle + spawnPosition.send(Protocol1_20_2To1_20_3.class, true); + } + }); cancelClientbound(ClientboundPackets1_20_3.RESOURCE_PACK_POP); registerServerbound(ServerboundPackets1_20_2.RESOURCE_PACK_STATUS, resourcePackStatusHandler()); @@ -326,7 +351,6 @@ private PacketHandler resourcePackStatusHandler() { }; } - private PacketHandler resourcePackHandler() { return wrapper -> { final UUID uuid = wrapper.read(Type.UUID); @@ -353,6 +377,7 @@ private void convertOptionalComponent(final PacketWrapper wrapper) throws Except @Override public void init(final UserConnection connection) { + connection.put(new SpawnPositionStorage()); addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_20_3.PLAYER)); } diff --git a/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/rewriter/EntityPacketRewriter1_20_3.java b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/rewriter/EntityPacketRewriter1_20_3.java index ccea483a5..42a6f4f0f 100644 --- a/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/rewriter/EntityPacketRewriter1_20_3.java +++ b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/rewriter/EntityPacketRewriter1_20_3.java @@ -19,12 +19,14 @@ import com.viaversion.viabackwards.api.rewriters.EntityRewriter; import com.viaversion.viabackwards.protocol.protocol1_20_2to1_20_3.Protocol1_20_2To1_20_3; +import com.viaversion.viabackwards.protocol.protocol1_20_2to1_20_3.storage.SpawnPositionStorage; import com.viaversion.viaversion.api.data.ParticleMappings; import com.viaversion.viaversion.api.minecraft.Particle; import com.viaversion.viaversion.api.minecraft.entities.EntityType; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_3; import com.viaversion.viaversion.api.minecraft.metadata.MetaType; 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.type.Type; import com.viaversion.viaversion.api.type.types.version.Types1_20_2; @@ -68,20 +70,30 @@ public void register() { map(Type.BOOLEAN); // Limited crafting map(Type.STRING); // Dimension key map(Type.STRING); // World + + handler(spawnPositionHandler()); handler(worldDataTrackerHandlerByKey()); } }); - protocol.registerClientbound(ClientboundPackets1_20_3.RESPAWN, new PacketHandlers() { @Override public void register() { map(Type.STRING); // Dimension map(Type.STRING); // World + + handler(spawnPositionHandler()); handler(worldDataTrackerHandlerByKey()); } }); } + private PacketHandler spawnPositionHandler() { + return wrapper -> { + final String world = wrapper.get(Type.STRING, 1); + wrapper.user().get(SpawnPositionStorage.class).setDimension(world); + }; + } + @Override protected void registerRewrites() { filter().handler((event, meta) -> { diff --git a/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/storage/SpawnPositionStorage.java b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/storage/SpawnPositionStorage.java new file mode 100644 index 000000000..83a928c7a --- /dev/null +++ b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_20_2to1_20_3/storage/SpawnPositionStorage.java @@ -0,0 +1,53 @@ +/* + * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards + * Copyright (C) 2023 ViaVersion 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 . + */ +package com.viaversion.viabackwards.protocol.protocol1_20_2to1_20_3.storage; + +import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viaversion.api.minecraft.Position; +import com.viaversion.viaversion.libs.fastutil.Pair; + +import java.util.Objects; + +public class SpawnPositionStorage implements StorableObject { + public static final Pair DEFAULT_SPAWN_POSITION = Pair.of(new Position(8, 64, 8), 0.0F); // Default values copied from the original client + + private Pair spawnPosition; + private String dimension; + + public Pair getSpawnPosition() { + return spawnPosition; + } + + public void setSpawnPosition(final Pair spawnPosition) { + this.spawnPosition = spawnPosition; + } + + /** + * Sets the dimension and resets the spawn position to the default value if the dimension changed. + * + * @param dimension The new dimension + */ + public void setDimension(final String dimension) { + final boolean changed = !Objects.equals(this.dimension, dimension); + this.dimension = dimension; + + if (changed) { + this.spawnPosition = DEFAULT_SPAWN_POSITION; + } + } +}