From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Sat, 2 May 2020 20:55:44 -0500
Subject: [PATCH] Player invulnerabilities


diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index bd4583ff7d76b83003ca327e8301fb1d5ff18f66..59fa9a9bcfde0a0137fa322343052ed68e47edbe 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -259,6 +259,7 @@ public class ServerPlayer extends Player {
     // CraftBukkit end
     public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper
     public boolean purpurClient = false; // Purpur
+    public boolean acceptingResourcePack = false; // Purpur
 
     public double lastEntitySpawnRadiusSquared; // Paper - optimise isOutsideRange, this field is in blocks
     public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleHashSet; // Paper
@@ -335,6 +336,7 @@ public class ServerPlayer extends Player {
         this.bukkitPickUpLoot = true;
         this.maxHealthCache = this.getMaxHealth();
         this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
+        this.spawnInvulnerableTime = world.purpurConfig.playerSpawnInvulnerableTicks; // Purpur
     }
     // Paper start - Chunk priority
     public BlockPos getPointInFront(double inFront) {
@@ -978,6 +980,12 @@ public class ServerPlayer extends Player {
 
     }
 
+    // Purpur start
+    public boolean isSpawnInvulnerable() {
+        return spawnInvulnerableTime > 0 || frozen;
+    }
+    // Purpur end
+
     @Override
     public boolean hurt(DamageSource source, float amount) {
         if (this.isInvulnerableTo(source)) {
@@ -985,7 +993,7 @@ public class ServerPlayer extends Player {
         } else {
             boolean flag = this.server.isDedicatedServer() && this.isPvpAllowed() && "fall".equals(source.msgId);
 
-            if (!flag && this.spawnInvulnerableTime > 0 && source != DamageSource.OUT_OF_WORLD) {
+            if (!flag && isSpawnInvulnerable() && source != DamageSource.OUT_OF_WORLD) { // Purpur
                 return false;
             } else {
                 if (source instanceof EntityDamageSource) {
@@ -1160,6 +1168,7 @@ public class ServerPlayer extends Player {
             }
             // Paper end
 
+            this.spawnInvulnerableTime = worldserver.purpurConfig.playerSpawnInvulnerableTicks; // Purpur
             return this;
         }
     }
@@ -1945,6 +1954,7 @@ public class ServerPlayer extends Player {
     }
 
     public void sendTexturePack(String url, String hash, boolean required, @Nullable Component resourcePackPrompt) {
+        this.acceptingResourcePack = true; // Purpur
         this.connection.send(new ClientboundResourcePackPacket(url, hash, required, resourcePackPrompt));
     }
 
@@ -2449,9 +2459,17 @@ public class ServerPlayer extends Player {
 
     @Override
     public boolean isImmobile() {
-        return super.isImmobile() || (this.connection != null && this.connection.isDisconnected()); // Paper
+        return super.isImmobile() || frozen || (this.connection != null && this.connection.isDisconnected()); // Paper // Purpur
     }
 
+    // Purpur start
+    private boolean frozen = false;
+
+    public void setFrozen(boolean frozen) {
+        this.frozen = frozen;
+    }
+    // Purpur end
+
     @Override
     public Scoreboard getScoreboard() {
         return this.getBukkitEntity().getScoreboard().getHandle();
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index f6a091ab2f549761b8d19fb35e1db907954a9717..e4b9edb4226ce3213181b6deb2fca6e9fab3e569 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1912,12 +1912,21 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
     @Override
     public void handleResourcePackResponse(ServerboundResourcePackPacket packet) {
         PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
+        // Purpur start
+        if (player.level.purpurConfig.playerInvulnerableWhileAcceptingResourcePack && !this.player.acceptingResourcePack) {
+            ServerGamePacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack packet exploitation attempt", this.player.getName());
+            this.disconnect(new TranslatableComponent("multiplayer.texturePrompt.failure.line1")); // "Server resource pack couldn't be applied"
+            return;
+        }
+        // Purpur end
         if (packet.getAction() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) {
             ServerGamePacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack rejection", this.player.getName());
             this.disconnect(new TranslatableComponent("multiplayer.requiredTexturePrompt.disconnect"), org.bukkit.event.player.PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // Paper - add cause
         }
         // Paper start
         PlayerResourcePackStatusEvent.Status packStatus = PlayerResourcePackStatusEvent.Status.values()[packet.action.ordinal()];
+        if (player.level.purpurConfig.playerInvulnerableWhileAcceptingResourcePack) player.setFrozen(packStatus == PlayerResourcePackStatusEvent.Status.ACCEPTED); // Purpur
+        this.player.acceptingResourcePack = packStatus == PlayerResourcePackStatusEvent.Status.ACCEPTED; // Purpur
         player.getBukkitEntity().setResourcePackStatus(packStatus);
         this.cserver.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(this.getCraftPlayer(), packStatus)); // CraftBukkit
         // Paper end
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 1bbb3f0658628ec36cd9e177df3acc07ef107428..3f8dc9192287d6eb3f966e0d8d4fdee82c753724 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -998,6 +998,8 @@ public abstract class PlayerList {
         }
         // Paper end
 
+        entityplayer1.spawnInvulnerableTime = entityplayer1.level.purpurConfig.playerSpawnInvulnerableTicks; // Purpur
+
         // CraftBukkit end
         return entityplayer1;
     }
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index 58b2f795b7018ac92fd72369bbe58d2f2c266adc..613dd74b460c0bd15fc8fd4e78380a81aef5f800 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -98,12 +98,16 @@ public class PurpurWorldConfig {
     public boolean idleTimeoutCountAsSleeping = false;
     public boolean idleTimeoutUpdateTabList = false;
     public boolean idleTimeoutTargetPlayer = true;
+    public int playerSpawnInvulnerableTicks = 60;
+    public boolean playerInvulnerableWhileAcceptingResourcePack = false;
     private void playerSettings() {
         idleTimeoutKick = getBoolean("gameplay-mechanics.player.idle-timeout.kick-if-idle", idleTimeoutKick);
         idleTimeoutTickNearbyEntities = getBoolean("gameplay-mechanics.player.idle-timeout.tick-nearby-entities", idleTimeoutTickNearbyEntities);
         idleTimeoutCountAsSleeping = getBoolean("gameplay-mechanics.player.idle-timeout.count-as-sleeping", idleTimeoutCountAsSleeping);
         idleTimeoutUpdateTabList = getBoolean("gameplay-mechanics.player.idle-timeout.update-tab-list", idleTimeoutUpdateTabList);
         idleTimeoutTargetPlayer = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer);
+        playerSpawnInvulnerableTicks = getInt("gameplay-mechanics.player.spawn-invulnerable-ticks", playerSpawnInvulnerableTicks);
+        playerInvulnerableWhileAcceptingResourcePack = getBoolean("gameplay-mechanics.player.invulnerable-while-accepting-resource-pack", playerInvulnerableWhileAcceptingResourcePack);
     }
 
     public boolean babiesAreRidable = true;
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 4254b3168f34b5d6ab9ac8bcece37ab02478b8e9..47f3eccc14b98adf41c9e6fe10b5b683bb94dc85 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -2575,5 +2575,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
     public void resetIdleTimer() {
         getHandle().resetLastActionTime();
     }
+
+    @Override
+    public boolean isSpawnInvulnerable() {
+        return getHandle().isSpawnInvulnerable();
+    }
+
+    @Override
+    public int getSpawnInvulnerableTicks() {
+        return getHandle().spawnInvulnerableTime;
+    }
+
+    @Override
+    public void setSpawnInvulnerableTicks(int spawnInvulnerableTime) {
+        getHandle().spawnInvulnerableTime = spawnInvulnerableTime;
+    }
     // Purpur end
 }
