From 5529a1cc1c004b066c5a737f96e7de1749a2acf0 Mon Sep 17 00:00:00 2001
From: Camotoy <20743703+Camotoy@users.noreply.github.com>
Date: Wed, 28 Jul 2021 13:21:18 -0400
Subject: [PATCH] Fix spectator gamemode movement

- Don't allow the player to toggle flight status in spectator mode
- Fix weird flight movement when player was previously on the ground
- The player is always flying in spectator mode, no exceptions
---
 .../network/session/GeyserSession.java        | 29 ++++++++-----------
 .../BedrockAdventureSettingsTranslator.java   |  7 +++++
 .../world/JavaNotifyClientTranslator.java     | 14 +++++++--
 3 files changed, 31 insertions(+), 19 deletions(-)

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 8f71e6fa8..444edcab4 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
@@ -40,6 +40,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
 import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
 import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
 import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
+import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket;
 import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket;
 import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
 import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
@@ -381,18 +382,6 @@ public class GeyserSession implements CommandSender {
      */
     private boolean flying = false;
 
-    /**
-     * If the current player is in noclip
-     */
-    @Setter
-    private boolean noClip = false;
-
-    /**
-     * If the current player can not interact with the world
-     */
-    @Setter
-    private boolean worldImmutable = false;
-
     /**
      * Caches current rain status.
      */
@@ -1282,15 +1271,21 @@ public class GeyserSession implements CommandSender {
         adventureSettingsPacket.setPlayerPermission(opPermissionLevel >= 2 ? PlayerPermission.OPERATOR : PlayerPermission.MEMBER);
 
         // Update the noClip and worldImmutable values based on the current gamemode
-        noClip = gameMode == GameMode.SPECTATOR;
-        worldImmutable = gameMode == GameMode.ADVENTURE || gameMode == GameMode.SPECTATOR;
+        boolean spectator = gameMode == GameMode.SPECTATOR;
+        boolean worldImmutable = gameMode == GameMode.ADVENTURE || spectator;
 
         Set<AdventureSetting> flags = adventureSettingsPacket.getSettings();
-        if (canFly) {
+        if (canFly || spectator) {
             flags.add(AdventureSetting.MAY_FLY);
         }
 
-        if (flying) {
+        if (flying || spectator) {
+            if (spectator && !flying) {
+                // We're "flying locked" in this gamemode
+                flying = true;
+                ClientPlayerAbilitiesPacket abilitiesPacket = new ClientPlayerAbilitiesPacket(true);
+                sendDownstreamPacket(abilitiesPacket);
+            }
             flags.add(AdventureSetting.FLYING);
         }
 
@@ -1298,7 +1293,7 @@ public class GeyserSession implements CommandSender {
             flags.add(AdventureSetting.WORLD_IMMUTABLE);
         }
 
-        if (noClip) {
+        if (spectator) {
             flags.add(AdventureSetting.NO_CLIP);
         }
 
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java
index 61cc80910..e868eedeb 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java
@@ -25,6 +25,7 @@
 
 package org.geysermc.connector.network.translators.bedrock;
 
+import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
 import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket;
 import com.nukkitx.protocol.bedrock.data.AdventureSetting;
 import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
@@ -39,6 +40,12 @@ public class BedrockAdventureSettingsTranslator extends PacketTranslator<Adventu
     @Override
     public void translate(AdventureSettingsPacket packet, GeyserSession session) {
         boolean isFlying = packet.getSettings().contains(AdventureSetting.FLYING);
+        if (!isFlying && session.getGameMode() == GameMode.SPECTATOR) {
+            // We should always be flying in spectator mode
+            session.sendAdventureSettings();
+            return;
+        }
+
         session.setFlying(isFlying);
         ClientPlayerAbilitiesPacket abilitiesPacket = new ClientPlayerAbilitiesPacket(isFlying);
         session.sendDownstreamPacket(abilitiesPacket);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
index 8a0ddb3b5..f6eb375e6 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
@@ -51,8 +51,6 @@ public class JavaNotifyClientTranslator extends PacketTranslator<ServerNotifyCli
     @Override
     public void translate(ServerNotifyClientPacket packet, GeyserSession session) {
         PlayerEntity entity = session.getPlayerEntity();
-        if (entity == null)
-            return;
 
         switch (packet.getNotification()) {
             case START_RAIN:
@@ -110,6 +108,18 @@ public class JavaNotifyClientTranslator extends PacketTranslator<ServerNotifyCli
 
                 session.sendAdventureSettings();
 
+                if (session.getPlayerEntity().isOnGround() && gameMode == GameMode.SPECTATOR) {
+                    // Fix a bug where the player has glitched movement and thinks they are still on the ground
+                    MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
+                    movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
+                    movePlayerPacket.setPosition(entity.getPosition());
+                    movePlayerPacket.setRotation(entity.getBedrockRotation());
+                    movePlayerPacket.setOnGround(false);
+                    movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
+                    movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.UNKNOWN);
+                    session.sendUpstreamPacket(movePlayerPacket);
+                }
+
                 // Update the crafting grid to add/remove barriers for creative inventory
                 PlayerInventoryTranslator.updateCraftingGrid(session, session.getPlayerInventory());
                 break;