From 3ec591509c6998221a23546d2db396fac28ab747 Mon Sep 17 00:00:00 2001 From: chris Date: Thu, 7 Sep 2023 00:13:19 +0200 Subject: [PATCH] Fix forced zoom issue when switching into spectator mode (#4093) * init: spectator mode support * properly set second abilitylayer for spectator mode * Fixes https://github.com/GeyserMC/Geyser/issues/3318 by not sending changed flags in spectator mode --- .../entity/type/living/ArmorStandEntity.java | 2 +- .../type/player/SessionPlayerEntity.java | 12 ++++- .../geyser/session/GeyserSession.java | 44 ++++++++++++------- .../protocol/java/JavaLoginTranslator.java | 3 +- .../protocol/java/JavaRespawnTranslator.java | 3 +- .../java/level/JavaGameEventTranslator.java | 3 +- .../org/geysermc/geyser/util/EntityUtils.java | 15 +++++++ 7 files changed, 60 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java index 903f08b64..c9018fe76 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/ArmorStandEntity.java @@ -249,7 +249,7 @@ public class ArmorStandEntity extends LivingEntity { @Override public InteractionResult interactAt(Hand hand) { if (!isMarker && session.getPlayerInventory().getItemInHand(hand).asItem() != Items.NAME_TAG) { - // Java Edition returns SUCCESS if in spectator mode, but this is overrided with an earlier check on the client + // Java Edition returns SUCCESS if in spectator mode, but this is overridden with an earlier check on the client return InteractionResult.CONSUME; } else { return InteractionResult.PASS; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index 540aa989f..bcd32253f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeTyp import com.github.steveice10.mc.protocol.data.game.entity.metadata.GlobalPos; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; import org.cloudburstmc.math.vector.Vector3f; @@ -109,10 +110,17 @@ public class SessionPlayerEntity extends PlayerEntity { this.position = position; } + /** + * Sending any updated flags (sprinting, onFire, etc.) to the client while in spectator is not needed + * Also "fixes" https://github.com/GeyserMC/Geyser/issues/3318 + */ @Override public void setFlags(ByteEntityMetadata entityMetadata) { - super.setFlags(entityMetadata); - session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING)); + // TODO: proper fix, BDS somehow does it? https://paste.gg/p/anonymous/3adfb7612f1540be80fa03a2281f93dc (BDS 1.20.13) + if (!this.session.getGameMode().equals(GameMode.SPECTATOR)) { + super.setFlags(entityMetadata); + session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING)); + } refreshSpeed = true; } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 9cfc29889..f7d362e0d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -98,20 +98,20 @@ import org.cloudburstmc.protocol.common.util.OptionalBoolean; import org.geysermc.api.util.BedrockPlatform; import org.geysermc.api.util.InputMode; import org.geysermc.api.util.UiProfile; -import org.geysermc.geyser.api.bedrock.camera.CameraShake; -import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormBuilder; import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.util.BedrockData; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.bedrock.camera.CameraShake; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.api.event.bedrock.SessionLoginEvent; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.RemoteServer; +import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.configuration.GeyserConfiguration; @@ -131,7 +131,6 @@ import org.geysermc.geyser.item.Items; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.physics.CollisionManager; -import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.netty.LocalSession; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.BlockMappings; @@ -147,6 +146,7 @@ import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.DimensionUtils; +import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.jetbrains.annotations.NotNull; @@ -1516,11 +1516,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { StartGamePacket startGamePacket = new StartGamePacket(); startGamePacket.setUniqueEntityId(playerEntity.getGeyserId()); startGamePacket.setRuntimeEntityId(playerEntity.getGeyserId()); - startGamePacket.setPlayerGameType(switch (gameMode) { - case CREATIVE -> GameType.CREATIVE; - case ADVENTURE -> GameType.ADVENTURE; - default -> GameType.SURVIVAL; - }); + startGamePacket.setPlayerGameType(EntityUtils.toBedrockGamemode(gameMode)); startGamePacket.setPlayerPosition(Vector3f.from(0, 69, 0)); startGamePacket.setRotation(Vector2f.from(1, 1)); @@ -1756,7 +1752,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { AbilityLayer abilityLayer = new AbilityLayer(); Set abilities = abilityLayer.getAbilityValues(); - if (canFly || spectator) { + if (canFly) { abilities.add(Ability.MAY_FLY); } @@ -1790,15 +1786,31 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } if (spectator) { - abilities.add(Ability.NO_CLIP); + AbilityLayer spectatorLayer = new AbilityLayer(); + spectatorLayer.setLayerType(AbilityLayer.Type.SPECTATOR); + // Setting all abilitySet causes the zoom issue... BDS only sends these, so ig we will too + Set abilitySet = spectatorLayer.getAbilitiesSet(); + abilitySet.add(Ability.BUILD); + abilitySet.add(Ability.MINE); + abilitySet.add(Ability.DOORS_AND_SWITCHES); + abilitySet.add(Ability.OPEN_CONTAINERS); + abilitySet.add(Ability.ATTACK_PLAYERS); + abilitySet.add(Ability.ATTACK_MOBS); + abilitySet.add(Ability.INVULNERABLE); + abilitySet.add(Ability.FLYING); + abilitySet.add(Ability.MAY_FLY); + abilitySet.add(Ability.INSTABUILD); + abilitySet.add(Ability.NO_CLIP); + + Set abilityValues = spectatorLayer.getAbilityValues(); + abilityValues.add(Ability.INVULNERABLE); + abilityValues.add(Ability.FLYING); + abilityValues.add(Ability.NO_CLIP); + + updateAbilitiesPacket.getAbilityLayers().add(spectatorLayer); } - // https://github.com/GeyserMC/Geyser/issues/3769 Setting Spectator mode ability layer - if (spectator) { - abilityLayer.setLayerType(AbilityLayer.Type.SPECTATOR); - } else { - abilityLayer.setLayerType(AbilityLayer.Type.BASE); - } + abilityLayer.setLayerType(AbilityLayer.Type.BASE); abilityLayer.setFlySpeed(flySpeed); // https://github.com/GeyserMC/Geyser/issues/3139 as of 1.19.10 abilityLayer.setWalkSpeed(walkSpeed == 0f ? 0.01f : walkSpeed); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java index 9f7fd4f40..135e9ed01 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java @@ -45,6 +45,7 @@ import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.DimensionUtils; +import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.JavaCodecUtil; import org.geysermc.geyser.util.PluginMessageUtils; @@ -113,7 +114,7 @@ public class JavaLoginTranslator extends PacketTranslator { @@ -59,7 +60,7 @@ public class JavaRespawnTranslator extends PacketTranslator { @@ -105,7 +106,7 @@ public class JavaGameEventTranslator extends PacketTranslator GameType.CREATIVE; + case ADVENTURE -> GameType.ADVENTURE; + case SPECTATOR -> GameType.SPECTATOR; + default -> GameType.SURVIVAL; + }; + } + private EntityUtils() { } }