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
This commit is contained in:
chris 2023-09-07 00:13:19 +02:00 committed by GitHub
parent 54bb1f3d13
commit 3ec591509c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 60 additions and 22 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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<Ability> 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<Ability> 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<Ability> 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);

View File

@ -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<ClientboundLoginPacket
if (!needsSpawnPacket) {
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(packet.getGameMode().ordinal());
playerGameTypePacket.setGamemode(EntityUtils.toBedrockGamemode(packet.getGameMode()).ordinal());
session.sendUpstreamPacket(playerGameTypePacket);
}

View File

@ -38,6 +38,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;
@Translator(packet = ClientboundRespawnPacket.class)
public class JavaRespawnTranslator extends PacketTranslator<ClientboundRespawnPacket> {
@ -59,7 +60,7 @@ public class JavaRespawnTranslator extends PacketTranslator<ClientboundRespawnPa
entity.updateBedrockMetadata();
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
playerGameTypePacket.setGamemode(EntityUtils.toBedrockGamemode(packet.getGamemode()).ordinal());
session.sendUpstreamPacket(playerGameTypePacket);
session.setGameMode(packet.getGamemode());

View File

@ -44,6 +44,7 @@ import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.inventory.PlayerInventoryTranslator;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.EntityUtils;
@Translator(packet = ClientboundGameEventPacket.class)
public class JavaGameEventTranslator extends PacketTranslator<ClientboundGameEventPacket> {
@ -105,7 +106,7 @@ public class JavaGameEventTranslator extends PacketTranslator<ClientboundGameEve
GameMode gameMode = (GameMode) packet.getValue();
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(gameMode.ordinal());
playerGameTypePacket.setGamemode(EntityUtils.toBedrockGamemode(gameMode).ordinal());
session.sendUpstreamPacket(playerGameTypePacket);
session.setGameMode(gameMode);

View File

@ -26,9 +26,11 @@
package org.geysermc.geyser.util;
import com.github.steveice10.mc.protocol.data.game.entity.Effect;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.GameType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinitions;
@ -260,6 +262,19 @@ public final class EntityUtils {
return InteractionResult.PASS;
}
/**
* Convert Java GameMode to Bedrock GameType
* Needed to account for ordinal differences (spectator is 3 in Java, 6 in Bedrock)
*/
public static GameType toBedrockGamemode(GameMode gamemode) {
return switch (gamemode) {
case CREATIVE -> GameType.CREATIVE;
case ADVENTURE -> GameType.ADVENTURE;
case SPECTATOR -> GameType.SPECTATOR;
default -> GameType.SURVIVAL;
};
}
private EntityUtils() {
}
}