diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 52ee38231..db13eff95 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -29,6 +29,7 @@ import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.auth.exception.request.RequestException; import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket; import com.github.steveice10.packetlib.Client; import com.github.steveice10.packetlib.event.session.ConnectedEvent; import com.github.steveice10.packetlib.event.session.DisconnectedEvent; @@ -56,7 +57,7 @@ import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.cache.*; import org.geysermc.connector.network.translators.Registry; -import org.geysermc.connector.network.translators.TranslatorsInit; +import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.Toolbox; import java.net.InetSocketAddress; @@ -97,6 +98,11 @@ public class GeyserSession implements Player { @Setter private GameMode gameMode = GameMode.SURVIVAL; + @Setter + private boolean switchingDimension = false; + private boolean manyDimPackets = false; + private ServerRespawnPacket lastDimPacket = null; + public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { this.connector = connector; this.upstream = new UpstreamSession(bedrockServerSession); @@ -121,25 +127,8 @@ public class GeyserSession implements Player { public void connect(RemoteServer remoteServer) { startGame(); this.remoteServer = remoteServer; - if (!(connector.getConfig().getRemote().getAuthType().hashCode() == "online".hashCode())) { - connector.getLogger().info("Attempting to login using offline mode... authentication is disabled."); - authenticate(authenticationData.getName()); - } - Vector3f pos = Vector3f.ZERO; - int chunkX = pos.getFloorX() >> 4; - int chunkZ = pos.getFloorZ() >> 4; - NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket(); - chunkPublisherUpdatePacket.setPosition(pos.toInt()); - chunkPublisherUpdatePacket.setRadius(renderDistance << 4); - upstream.sendPacket(chunkPublisherUpdatePacket); - - LevelChunkPacket data = new LevelChunkPacket(); - data.setChunkX(chunkX); - data.setChunkZ(chunkZ); - data.setSubChunksLength(0); - data.setData(TranslatorsInit.EMPTY_LEVEL_CHUNK_DATA); - upstream.sendPacket(data); + ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false); BiomeDefinitionListPacket biomePacket = new BiomeDefinitionListPacket(); biomePacket.setTag(CompoundTag.EMPTY); @@ -198,6 +187,16 @@ public class GeyserSession implements Player { @Override public void packetReceived(PacketReceivedEvent event) { if (!closed) { + //handle consecutive respawn packets + if (event.getPacket().getClass().equals(ServerRespawnPacket.class)) { + manyDimPackets = lastDimPacket != null; + lastDimPacket = event.getPacket(); + return; + } else if (lastDimPacket != null) { + Registry.JAVA.translate(lastDimPacket.getClass(), lastDimPacket, GeyserSession.this); + lastDimPacket = null; + } + Registry.JAVA.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java index fd0bd7c5a..5e9ae0f06 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java @@ -25,10 +25,7 @@ package org.geysermc.connector.network.session.cache; -import it.unimi.dsi.fastutil.longs.Long2LongMap; -import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.*; import lombok.Getter; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; @@ -45,9 +42,9 @@ public class EntityCache { private GeyserSession session; @Getter - private Long2ObjectMap entities = new Long2ObjectOpenHashMap<>(); - private Long2LongMap entityIdTranslations = new Long2LongOpenHashMap(); - private Map playerEntities = new HashMap<>(); + private Long2ObjectMap entities = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); + private Long2LongMap entityIdTranslations = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); + private Map playerEntities = Collections.synchronizedMap(new HashMap<>()); private Map bossbars = new HashMap<>(); @Getter @@ -76,6 +73,13 @@ public class EntityCache { return false; } + public void removeAllEntities() { + List entities = new ArrayList<>(session.getEntityCache().getEntities().values()); + for (Entity entity : entities) { + session.getEntityCache().removeEntity(entity, false); + } + } + public Entity getEntityByGeyserId(long geyserId) { return entities.get(geyserId); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java index a19de3f29..aa7692830 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java @@ -156,6 +156,7 @@ public class TranslatorsInit { Registry.registerBedrock(SetLocalPlayerAsInitializedPacket.class, new BedrockPlayerInitializedTranslator()); Registry.registerBedrock(InteractPacket.class, new BedrockInteractTranslator()); Registry.registerBedrock(TextPacket.class, new BedrockTextTranslator()); + Registry.registerBedrock(RespawnPacket.class, new BedrockRespawnTranslator()); itemTranslator = new ItemTranslator(); blockTranslator = new BlockTranslator(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java index 334679346..595ca8626 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java @@ -34,6 +34,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket; import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket; import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.network.session.GeyserSession; @@ -53,7 +54,7 @@ public class BedrockActionTranslator extends PacketTranslator { + + @Override + public void translate(RespawnPacket packet, GeyserSession session) { + if (packet.getSpawnState() == RespawnPacket.State.CLIENT_READY) { + RespawnPacket respawnPacket = new RespawnPacket(); + respawnPacket.setRuntimeEntityId(0); + respawnPacket.setPosition(Vector3f.ZERO); + respawnPacket.setSpawnState(RespawnPacket.State.SERVER_SEARCHING); + session.getUpstream().sendPacket(respawnPacket); + + ClientRequestPacket javaRespawnPacket = new ClientRequestPacket(ClientRequest.RESPAWN); + session.getDownstream().getSession().send(javaRespawnPacket); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java index 87bb62e03..3cdc149d0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java @@ -26,22 +26,20 @@ package org.geysermc.connector.network.translators.java; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePacket; -import com.nukkitx.math.vector.Vector3i; -import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket; -import com.nukkitx.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; -import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket; -import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; -import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket; +import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; -import org.geysermc.connector.world.chunk.ChunkPosition; +import org.geysermc.connector.utils.DimensionUtils; public class JavaJoinGameTranslator extends PacketTranslator { @Override public void translate(ServerJoinGamePacket packet, GeyserSession session) { + PlayerEntity entity = session.getPlayerEntity(); + entity.setEntityId(packet.getEntityId()); + AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket(); bedrockPacket.setUniqueEntityId(session.getPlayerEntity().getGeyserId()); bedrockPacket.setPlayerPermission(1); @@ -51,9 +49,6 @@ public class JavaJoinGameTranslator extends PacketTranslator { @@ -41,37 +41,25 @@ public class JavaRespawnTranslator extends PacketTranslator if (entity == null) return; - if (entity.getDimension() == getDimension(packet.getDimension())) - return; - - entity.setDimension(getDimension(packet.getDimension())); - - ChangeDimensionPacket changeDimensionPacket = new ChangeDimensionPacket(); - changeDimensionPacket.setDimension(getDimension(packet.getDimension())); - changeDimensionPacket.setRespawn(false); - changeDimensionPacket.setPosition(entity.getPosition()); - session.getUpstream().sendPacket(changeDimensionPacket); + float maxHealth = entity.getAttributes().containsKey(AttributeType.MAX_HEALTH) ? entity.getAttributes().get(AttributeType.MAX_HEALTH).getValue() : 20f; + // Max health must be divisible by two in bedrock + entity.getAttributes().put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(maxHealth, (maxHealth % 2 == 1 ? maxHealth + 1 : maxHealth))); SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket(); playerGameTypePacket.setGamemode(packet.getGamemode().ordinal()); session.getUpstream().sendPacket(playerGameTypePacket); session.setGameMode(packet.getGamemode()); - /* - PlayStatusPacket playStatusPacket = new PlayStatusPacket(); - playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); - session.getUpstream().sendPacket(playStatusPacket); - */ - } - - private int getDimension(int javaDimension) { - switch (javaDimension) { - case -1: - return 1; - case 1: - return 2; + if (entity.getDimension() != DimensionUtils.javaToBedrock(packet.getDimension())) { + DimensionUtils.switchDimension(session, packet.getDimension(), false); + } else { + // Handled in JavaPlayerPositionRotationTranslator + session.setSpawned(false); + if (session.isManyDimPackets()) { //reloading world + int fakeDim = entity.getDimension() == 0 ? -1 : 0; + DimensionUtils.switchDimension(session, fakeDim, true); + DimensionUtils.switchDimension(session, packet.getDimension(), false); + } } - - return javaDimension; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerHealthTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerHealthTranslator.java index 73376385a..bebd8f9d7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerHealthTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerHealthTranslator.java @@ -25,11 +25,7 @@ package org.geysermc.connector.network.translators.java.entity.player; -import com.github.steveice10.mc.protocol.data.game.ClientRequest; -import com.github.steveice10.mc.protocol.packet.ingame.client.ClientRequestPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerHealthPacket; -import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.packet.RespawnPacket; import com.nukkitx.protocol.bedrock.packet.SetHealthPacket; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.attribute.AttributeType; @@ -44,8 +40,9 @@ public class JavaPlayerHealthTranslator extends PacketTranslator> 4; + int chunkZ = position.getZ() >> 4; + NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket(); + chunkPublisherUpdatePacket.setPosition(position); + chunkPublisherUpdatePacket.setRadius(radius + 1 << 4); + session.getUpstream().sendPacket(chunkPublisherUpdatePacket); + session.setLastChunkPosition(null); + for (int x = -radius; x <= radius; x++) { + for (int z = -radius; z <= radius; z++) { + LevelChunkPacket data = new LevelChunkPacket(); + data.setChunkX(chunkX + x); + data.setChunkZ(chunkZ + z); + data.setSubChunksLength(0); + data.setData(TranslatorsInit.EMPTY_LEVEL_CHUNK_DATA); + data.setCachingEnabled(false); + session.getUpstream().sendPacket(data); + + if (forceUpdate) { + Vector3i pos = Vector3i.from(chunkX + x << 4, 80, chunkZ + z << 4); + UpdateBlockPacket blockPacket = new UpdateBlockPacket(); + blockPacket.setBlockPosition(pos); + blockPacket.setDataLayer(1); + blockPacket.setRuntimeId(1); + session.getUpstream().sendPacket(blockPacket); + } + } + } + } + public static final class ChunkData { public ChunkSection[] sections; diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java new file mode 100644 index 000000000..ace1c6829 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java @@ -0,0 +1,49 @@ +package org.geysermc.connector.utils; + +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.protocol.bedrock.packet.*; +import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.network.session.GeyserSession; + +public class DimensionUtils { + public static void switchDimension(GeyserSession session, int javaDimension, boolean fake) { + int bedrockDimension = javaToBedrock(javaDimension); + Entity player = session.getPlayerEntity(); + if (bedrockDimension == player.getDimension()) + return; + Vector3i pos = Vector3i.from(0, Short.MAX_VALUE, 0); + + session.getEntityCache().removeAllEntities(); + + ChangeDimensionPacket changeDimensionPacket = new ChangeDimensionPacket(); + changeDimensionPacket.setDimension(bedrockDimension); + changeDimensionPacket.setRespawn(true); + changeDimensionPacket.setPosition(pos.toFloat()); + session.getUpstream().sendPacket(changeDimensionPacket); + player.setDimension(bedrockDimension); + + //let java server handle portal travel sound + StopSoundPacket stopSoundPacket = new StopSoundPacket(); + stopSoundPacket.setStoppingAllSound(true); + stopSoundPacket.setSoundName(""); + session.getUpstream().sendPacket(stopSoundPacket); + + if (fake) { + ChunkUtils.sendEmptyChunks(session, pos, 2, true); + } + + session.setSpawned(false); + session.setSwitchingDimension(true); + } + + public static int javaToBedrock(int javaDimension) { + switch (javaDimension) { + case -1: + return 1; + case 1: + return 2; + default: + return javaDimension; + } + } +}