diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch index b96362df1adc..addf74ac5d15 100644 --- a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -308,6 +308,33 @@ this.getCommands().performPrefixedCommand(input.source, input.msg); } } +@@ -509,6 +_,7 @@ + this.settings.update(p -> p.spawnProtection.update(this.registryAccess(), spawnProtectionRadius)); + } + ++ // Paper start - add a bypass permission node + @Override + public boolean isUnderSpawnProtection(final ServerLevel level, final BlockPos pos, final Player player) { + LevelData.RespawnData respawnData = level.getRespawnData(); +@@ -524,6 +_,10 @@ + return false; + } + ++ if (player.getBukkitEntity().hasPermission("minecraft.spawn-protection.bypass")) { ++ return false; ++ } ++ + if (this.spawnProtectionRadius() <= 0) { + return false; + } +@@ -534,6 +_,7 @@ + int dist = Math.max(xd, zd); + return dist <= this.spawnProtectionRadius(); + } ++ // Paper - end + + @Override + public boolean repliesToStatus() { @@ -592,12 +_,15 @@ @Override diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch index 2b9944e520cd..356de8fa9a5d 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -125,7 +125,7 @@ if (this.isSleeping()) { this.stopSleeping(); } -@@ -405,12 +_,26 @@ +@@ -405,12 +_,44 @@ this.respawnConfig = input.read("respawn", ServerPlayer.RespawnConfig.CODEC).orElse(null); this.spawnExtraParticlesOnFall = input.getBooleanOr("spawn_extra_particles_on_fall", false); this.raidOmenPosition = input.read("raid_omen_position", BlockPos.CODEC).orElse(null); @@ -138,16 +138,34 @@ + // Paper start - Expand PlayerGameModeChangeEvent + private void loadGameTypes(ValueInput input) { -+ if (this.server.getForcedGameType() != null && this.server.getForcedGameType() != readPlayerMode(input, "playerGameType")) { -+ if (new org.bukkit.event.player.PlayerGameModeChangeEvent(this.getBukkitEntity(), org.bukkit.GameMode.getByValue(this.server.getDefaultGameType().getId()), org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null).callEvent()) { ++ GameType playerGameType = readPlayerMode(input, "playerGameType"); ++ GameType previousPlayerGameType = readPlayerMode(input, "previousPlayerGameType"); ++ ++ if (this.server.getForcedGameType() != null && this.server.getForcedGameType() != playerGameType) { ++ if (this.getBukkitEntity().hasPermission("minecraft.force-gamemode.bypass")) { // Paper - add bypass permission ++ this.gameMode.setGameModeForPlayer( ++ playerGameType != null ? playerGameType : this.calculateGameModeForNewPlayer(null), ++ previousPlayerGameType ++ ); ++ return; ++ } ++ ++ org.bukkit.GameMode forcedBukkitGameMode = org.bukkit.GameMode.getByValue(this.server.getDefaultGameType().getId()); ++ if (forcedBukkitGameMode == null) { ++ forcedBukkitGameMode = org.bukkit.GameMode.SURVIVAL; ++ } ++ ++ if (new org.bukkit.event.player.PlayerGameModeChangeEvent(this.getBukkitEntity(), forcedBukkitGameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null).callEvent()) { + this.gameMode.setGameModeForPlayer(this.server.getForcedGameType(), GameType.DEFAULT_MODE); + } else { -+ this.gameMode.setGameModeForPlayer(readPlayerMode(input, "playerGameType"), readPlayerMode(input, "previousPlayerGameType")); ++ this.gameMode.setGameModeForPlayer( ++ playerGameType != null ? playerGameType : this.calculateGameModeForNewPlayer(null), ++ previousPlayerGameType ++ ); + } + return; + } -+ this.gameMode -+ .setGameModeForPlayer(this.calculateGameModeForNewPlayer(readPlayerMode(input, "playerGameType")), readPlayerMode(input, "previousPlayerGameType")); ++ this.gameMode.setGameModeForPlayer(this.calculateGameModeForNewPlayer(playerGameType), previousPlayerGameType); + } + // Paper end - Expand PlayerGameModeChangeEvent + diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 0ba63b7e5449..32ef9409b879 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -270,7 +270,7 @@ + return; + } + // Paper end - Prevent moving into unloaded chunks -+ if (movedDist - expectedDist > Math.max(100.0, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed)) && !this.isSingleplayerOwner()) { ++ if (movedDist - expectedDist > Math.max(100.0, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed)) && !this.isSingleplayerOwner() && !this.player.getBukkitEntity().hasPermission("minecraft.movement.bypass.vehicle.quickly")) { // Paper - add bypass permission + // CraftBukkit end LOGGER.warn( "{} (vehicle of {}) moved too quickly! {},{},{}", vehicle.getPlainTextName(), this.player.getPlainTextName(), xDist, yDist, zDist @@ -293,7 +293,7 @@ movedDist = xDist * xDist + yDist * yDist + zDist * zDist; boolean fail = false; - if (movedDist > 0.0625) { -+ if (movedDist > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot ++ if (movedDist > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.getBukkitEntity().hasPermission("minecraft.movement.bypass.vehicle.wrongly")) { // Spigot fail = true; LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", vehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(movedDist)); } @@ -355,6 +355,16 @@ this.player.level().getChunkSource().move(this.player); Vec3 clientDeltaMovement = new Vec3(vehicle.getX() - oldX, vehicle.getY() - oldY, vehicle.getZ() - oldZ); this.handlePlayerKnownMovement(clientDeltaMovement); +@@ -513,7 +_,8 @@ + && !this.server.allowFlight() + && !vehicle.isFlyingVehicle() + && !vehicle.isNoGravity() +- && this.noBlocksAround(vehicle); ++ && this.noBlocksAround(vehicle) ++ && !this.getCraftPlayer().hasPermission("minecraft.flight.bypass.vehicle"); // Paper - add bypass permission + this.vehicleLastGoodX = vehicle.getX(); + this.vehicleLastGoodY = vehicle.getY(); + this.vehicleLastGoodZ = vehicle.getZ(); @@ -532,7 +_,7 @@ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); if (packet.getId() == this.awaitingTeleport) { @@ -388,7 +398,7 @@ this.player.getRecipeBook().setBookSetting(packet.getBookType(), packet.isOpen(), packet.isFiltering()); } -@@ -591,25 +_,112 @@ +@@ -591,25 +_,113 @@ } } @@ -402,7 +412,7 @@ - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); // Paper - AsyncTabCompleteEvent; run this async + // CraftBukkit start -+ if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.nameAndId()) && !this.server.isSingleplayerOwner(this.player.nameAndId())) { // Paper - configurable tab spam limits ++ if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.player.getBukkitEntity().hasPermission("minecraft.spam.bypass.command-suggestions.frequency") && !this.server.getPlayerList().isOp(this.player.nameAndId()) && !this.server.isSingleplayerOwner(this.player.nameAndId())) { // Paper - configurable tab spam limits and a bypass permission node + this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause // Paper - add proper async disconnect + return; + } @@ -414,7 +424,7 @@ + // Paper end - Don't suggest if tab-complete is disabled + // Paper start + final int index; -+ if (packet.getCommand().length() > 64 && ((index = packet.getCommand().indexOf(' ')) == -1 || index >= 64)) { ++ if (!this.player.getBukkitEntity().hasPermission("minecraft.spam.bypass.command-suggestions.length") && packet.getCommand().length() > 64 && ((index = packet.getCommand().indexOf(' ')) == -1 || index >= 64)) { // Paper - add bypass permission node + this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - add proper async disconnect + return; + } @@ -473,7 +483,8 @@ + // Paper end - AsyncTabCompleteEvent ParseResults parse = this.server.getCommands().getDispatcher().parse(command, this.player.createCommandSourceStack()); + // Paper start - Handle non-recoverable exceptions -+ if (!parse.getExceptions().isEmpty() ++ if (!this.player.getBukkitEntity().hasPermission("minecraft.spam.bypass.server-suggestions") // Paper - add a bypass permission node ++ && !parse.getExceptions().isEmpty() + && parse.getExceptions().values().stream().anyMatch(e -> e instanceof io.papermc.paper.brigadier.TagParseCommandSyntaxException)) { + this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); + return; @@ -785,7 +796,8 @@ + } + // Paper end - Prevent moving into unloaded chunks - if (this.shouldCheckPlayerMovement(isFallFlying)) { +- if (this.shouldCheckPlayerMovement(isFallFlying)) { ++ if (this.shouldCheckPlayerMovement(isFallFlying) && !this.player.getBukkitEntity().hasPermission("minecraft.movement.bypass.player.quickly")) { // Paper - add bypass permission float metersPerTick = isFallFlying ? 300.0F : 100.0F; - if (movedDist - expectedDist > metersPerTick * deltaPackets) { + if (movedDist - expectedDist > Math.max(metersPerTick, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) deltaPackets * speed))) { @@ -849,7 +861,7 @@ double oyDist = yDist; xDist = targetX - this.player.getX(); yDist = targetY - this.player.getY(); -@@ -1143,21 +_,88 @@ +@@ -1143,27 +_,96 @@ zDist = targetZ - this.player.getZ(); movedDist = xDist * xDist + yDist * yDist + zDist * zDist; @@ -861,8 +873,10 @@ && !this.player.isSleeping() && !this.player.isCreative() && !this.player.isSpectator() - && !this.player.isInPostImpulseGraceTime()) { +- && !this.player.isInPostImpulseGraceTime()) { - fail = true; ++ && !this.player.isInPostImpulseGraceTime() ++ && !this.player.getBukkitEntity().hasPermission("minecraft.movement.bypass.player.wrongly")) { // Paper - add bypass permission + // Paper start - Add fail move event + io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_WRONGLY, + targetX, targetY, targetZ, targetYRot, targetXRot, true); @@ -947,6 +961,13 @@ this.player.absSnapTo(targetX, targetY, targetZ, targetYRot, targetXRot); boolean isAutoSpinAttack = this.player.isAutoSpinAttack(); this.clientIsFloating = oyDist >= -0.03125 + && !playerStandsOnSomething + && !this.player.isSpectator() + && !this.server.allowFlight() ++ && !this.player.getBukkitEntity().hasPermission("minecraft.flight.bypass.player") // Paper - add bypass permission + && !this.player.getAbilities().mayfly + && !this.player.hasEffect(MobEffects.LEVITATION) + && !isFallFlying @@ -1192,7 +_,7 @@ this.lastGoodY = this.player.getY(); this.lastGoodZ = this.player.getZ(); @@ -956,6 +977,17 @@ this.player .doCheckFallDamage( this.player.getX() - startX, this.player.getY() - startY, this.player.getZ() - startZ, packet.isOnGround() +@@ -1216,6 +_,10 @@ + return false; + } + ++ if (isFallFlying && this.player.getBukkitEntity().hasPermission("minecraft.movement.bypass.elytra")) { // Paper - add bypass permission ++ return false; ++ } ++ + GameRules gameRules = this.player.level().getGameRules(); + return gameRules.get(GameRules.PLAYER_MOVEMENT_CHECK) && (!isFallFlying || gameRules.get(GameRules.ELYTRA_MOVEMENT_CHECK)); + } @@ -1232,6 +_,7 @@ this.player.getXRot() ); @@ -1563,7 +1595,7 @@ return Optional.empty(); } -@@ -1669,31 +_,91 @@ +@@ -1669,31 +_,92 @@ return false; } @@ -1644,7 +1676,8 @@ + // CraftBukkit start - replaced with thread safe throttle + // throttler.increment(); + if (!throttler.isIncrementAndUnderThreshold() -+ // CraftBukkit end - replaced with thread safe throttle ++ // CraftBukkit end - replaced with thread safe throttle ++ && !this.player.getBukkitEntity().hasPermission("minecraft.spam.bypass.rate") // Paper - add bypass permission node && !this.server.getPlayerList().isOp(this.player.nameAndId()) && !this.server.isSingleplayerOwner(this.player.nameAndId())) { - this.disconnect(Component.translatable("disconnect.spam")); @@ -2296,7 +2329,7 @@ public void handlePlaceRecipe(final ServerboundPlaceRecipePacket packet) { + // Paper start - auto recipe limit + if (!org.bukkit.Bukkit.isPrimaryThread()) { -+ if (!this.recipeSpamPackets.isIncrementAndUnderThreshold()) { ++ if (!this.player.getBukkitEntity().hasPermission("minecraft.spam.bypass.place") && !this.recipeSpamPackets.isIncrementAndUnderThreshold()) { // Paper - add bypass permission node + this.disconnectAsync(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause // Paper - add proper async disconnect + return; + }