aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0441-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
blob: 1a42ad9df6e97207e684803f3c1bb5cbed4f087d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 13 May 2020 23:01:26 -0400
Subject: [PATCH] Protect Bedrock and End Portal/Frames from being destroyed

This fixes exploits that let players destroy bedrock by Pistons, explosions
and Mushrooom/Tree generation.

These blocks are designed to not be broken except by creative players/commands.
So protect them from a multitude of methods of destroying them.

A config is provided if you rather let players use these exploits, and let
them destroy the worlds End Portals and get on top of the nether easy.

diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
index e1d91a95c306e71ac77b3658de77ec9d18c4f8e6..c9bf57298c7023b2d609d5271609a4070bb1c773 100644
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -441,4 +441,15 @@ public class PaperConfig {
     private static void loggerSettings() {
         deobfuscateStacktraces = getBoolean("settings.loggers.deobfuscate-stacktraces", deobfuscateStacktraces);
     }
+
+    public static boolean allowBlockPermanentBreakingExploits = false;
+    private static void allowBlockPermanentBreakingExploits() {
+        if (config.contains("allow-perm-block-break-exploits")) {
+            allowBlockPermanentBreakingExploits = config.getBoolean("allow-perm-block-break-exploits", false);
+            config.set("allow-perm-block-break-exploits", null);
+        }
+
+        config.set("settings.unsupported-settings.allow-permanent-block-break-exploits-readme", "This setting controls if players should be able to break bedrock, end portals and other intended to be permanent blocks.");
+        allowBlockPermanentBreakingExploits = getBoolean("settings.unsupported-settings.allow-permanent-block-break-exploits", allowBlockPermanentBreakingExploits);
+    }
 }
diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
index a861b4b55862b1c5583101fe7f28a3a43c547468..1575fb0bbad6e11f25fb9ce51fd1f15a1b11e0fe 100644
--- a/src/main/java/net/minecraft/world/level/Explosion.java
+++ b/src/main/java/net/minecraft/world/level/Explosion.java
@@ -174,6 +174,7 @@ public class Explosion {
                         for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
                             BlockPos blockposition = new BlockPos(d4, d5, d6);
                             BlockState iblockdata = this.level.getBlockState(blockposition);
+                            if (!iblockdata.isDestroyable()) continue; // Paper
                             FluidState fluid = iblockdata.getFluidState(); // Paper
 
                             if (!this.level.isInWorldBounds(blockposition)) {
@@ -332,7 +333,7 @@ public class Explosion {
                 BlockState iblockdata = this.level.getBlockState(blockposition);
                 Block block = iblockdata.getBlock();
 
-                if (!iblockdata.isAir()) {
+                if (!iblockdata.isAir() && iblockdata.isDestroyable()) { // Paper
                     BlockPos blockposition1 = blockposition.immutable();
 
                     this.level.getProfiler().push("explosion_blocks");
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index b2b433218d280b2abac40c90397633ef78bc9510..1dd4ee6cf037dcc6a8683d79b623165d1be62d57 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -427,6 +427,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
     public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
         // CraftBukkit start - tree generation
         if (this.captureTreeGeneration) {
+            // Paper start
+            BlockState type = getBlockState(pos);
+            if (!type.isDestroyable()) return false;
+            // Paper end
             CraftBlockState blockstate = this.capturedBlockStates.get(pos);
             if (blockstate == null) {
                 blockstate = CapturedBlockState.getTreeBlockState(this, pos, flags);
diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java
index 6a31e3a3466369ede28e28bc3b9fda8dcb77e136..d6a3f3a2edae806b0ebf5bf5ac445116c0d64535 100644
--- a/src/main/java/net/minecraft/world/level/block/Block.java
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
@@ -91,6 +91,19 @@ public class Block extends BlockBehaviour implements ItemLike {
     protected final StateDefinition<Block, BlockState> stateDefinition;
     private BlockState defaultBlockState;
     // Paper start
+    public final boolean isDestroyable() {
+        return com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits ||
+            this != Blocks.BEDROCK &&
+            this != Blocks.END_PORTAL_FRAME &&
+            this != Blocks.END_PORTAL &&
+            this != Blocks.END_GATEWAY &&
+            this != Blocks.COMMAND_BLOCK &&
+            this != Blocks.REPEATING_COMMAND_BLOCK &&
+            this != Blocks.CHAIN_COMMAND_BLOCK &&
+            this != Blocks.BARRIER &&
+            this != Blocks.STRUCTURE_BLOCK &&
+            this != Blocks.JIGSAW;
+    }
     public co.aikar.timings.Timing timing;
     public co.aikar.timings.Timing getTiming() {
         if (timing == null) {
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
index c345bd7542f3ffa09719864887e1516f1182e7e3..4eac07022a7d896ee8921afa6d35cba7f0c89941 100644
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
@@ -200,6 +200,12 @@ public class PistonBaseBlock extends DirectionalBlock {
     @Override
     public boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) {
         Direction enumdirection = (Direction) state.getValue(PistonBaseBlock.FACING);
+        // Paper start - prevent retracting when we're facing the wrong way (we were replaced before retraction could occur)
+        Direction directionQueuedAs = Direction.from3DDataValue(data & 7); // Paper - copied from below
+        if (!com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits && enumdirection != directionQueuedAs) {
+            return false;
+        }
+        // Paper end - prevent retracting when we're facing the wrong way
 
         if (!world.isClientSide) {
             boolean flag = this.getNeighborSignal(world, pos, enumdirection);
@@ -232,7 +238,7 @@ public class PistonBaseBlock extends DirectionalBlock {
             BlockState iblockdata1 = (BlockState) ((BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, enumdirection)).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT);
 
             world.setBlock(pos, iblockdata1, 20);
-            world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata1, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true));
+            world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata1, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - diff on change
             world.blockUpdated(pos, iblockdata1.getBlock());
             iblockdata1.updateNeighbourShapes(world, pos, 2);
             if (this.isSticky) {
@@ -261,7 +267,14 @@ public class PistonBaseBlock extends DirectionalBlock {
                     }
                 }
             } else {
-                world.removeBlock(pos.relative(enumdirection), false);
+                // Paper start - fix headless pistons breaking blocks
+                BlockPos headPos = pos.relative(enumdirection);
+                if (com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits || world.getBlockState(headPos) == Blocks.PISTON_HEAD.defaultBlockState().setValue(FACING, enumdirection)) { // double check to make sure we're not a headless piston.
+                    world.removeBlock(headPos, false);
+                } else {
+                    ((ServerLevel)world).getChunkSource().blockChanged(headPos); // ... fix client desync
+                }
+                // Paper end - fix headless pistons breaking blocks
             }
 
             world.playSound((Player) null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
index a087eec747bb355afd627c6042d20440c3e43e1b..bea9c7ac0fe08c3ca3309e8311f192cc557ca67d 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -206,7 +206,7 @@ public abstract class BlockBehaviour {
 
     @Deprecated
     public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
-        return this.material.isReplaceable() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem()));
+        return this.material.isReplaceable() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem())) && (state.isDestroyable() || (context.getPlayer() != null && context.getPlayer().getAbilities().instabuild)); // Paper
     }
 
     @Deprecated
@@ -664,7 +664,11 @@ public abstract class BlockBehaviour {
         public Block getBlock() {
             return (Block) this.owner;
         }
-
+        // Paper start
+        public final boolean isDestroyable() {
+            return getBlock().isDestroyable();
+        }
+        // Paper end
         public Material getMaterial() {
             return this.material;
         }
@@ -762,7 +766,7 @@ public abstract class BlockBehaviour {
         }
 
         public PushReaction getPistonPushReaction() {
-            return this.getBlock().getPistonPushReaction(this.asState());
+            return !isDestroyable() ? PushReaction.BLOCK : this.getBlock().getPistonPushReaction(this.asState()); // Paper
         }
 
         public boolean isSolidRender(BlockGetter world, BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
index d5ba2e679ed1858ea18e18feffce50544ae036c2..ca3e143e641933fa6b9499bbaaa1836877d90c52 100644
--- a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
+++ b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
@@ -209,6 +209,13 @@ public class PortalForcer {
         for (int j = -1; j < 3; ++j) {
             for (int k = -1; k < 4; ++k) {
                 temp.setWithOffset((Vec3i) pos, portalDirection.getStepX() * j + enumdirection1.getStepX() * distanceOrthogonalToPortal, k, portalDirection.getStepZ() * j + enumdirection1.getStepZ() * distanceOrthogonalToPortal);
+                // Paper start - prevent destroying unbreakable blocks
+                if (!com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits) {
+                    if (!this.level.getBlockState(temp).isDestroyable()) {
+                        return false;
+                    }
+                }
+                // Paper end - prevent destroying unbreakable blocks
                 if (k < 0 && !this.level.getBlockState(temp).getMaterial().isSolid()) {
                     return false;
                 }