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
|
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 3ee7e5671dd2519cec72b81211f1f39176a228ba..cf9b9de8688e3f655631451409096d7ec0471910 100644
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -431,4 +431,10 @@ public class PaperConfig {
private static void midTickChunkTasks() {
midTickChunkTasks = getInt("settings.chunk-tasks-per-tick", midTickChunkTasks);
}
+
+ public static boolean allowBlockPermanentBreakingExploits = false;
+ private static void allowBlockPermanentBreakingExploits() {
+ allowBlockPermanentBreakingExploits = getBoolean("allow-perm-block-break-exploits", allowBlockPermanentBreakingExploits);
+ }
+
}
diff --git a/src/main/java/net/minecraft/server/Block.java b/src/main/java/net/minecraft/server/Block.java
index e40375b67a4a321048c87002a07fde5c5d2395db..d2207a2c95690de586ab2d181b64955a6d2ea70d 100644
--- a/src/main/java/net/minecraft/server/Block.java
+++ b/src/main/java/net/minecraft/server/Block.java
@@ -32,6 +32,14 @@ public class Block implements IMaterial {
protected final SoundEffectType stepSound;
protected final Material material;
// 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.MOVING_PISTON; // try to prevent creation of headless pistons
+ }
public co.aikar.timings.Timing timing;
public co.aikar.timings.Timing getTiming() {
if (timing == null) {
@@ -276,7 +284,7 @@ public class Block implements IMaterial {
@Deprecated
public boolean a(IBlockData iblockdata, BlockActionContext blockactioncontext) {
- return this.material.isReplaceable() && (blockactioncontext.getItemStack().isEmpty() || blockactioncontext.getItemStack().getItem() != this.getItem());
+ return this.material.isReplaceable() && (blockactioncontext.getItemStack().isEmpty() || blockactioncontext.getItemStack().getItem() != this.getItem()) && (iblockdata.isDestroyable() || (blockactioncontext.getEntity() != null && blockactioncontext.getEntity().abilities.canInstantlyBuild)); // Paper
}
@Deprecated
@@ -596,7 +604,7 @@ public class Block implements IMaterial {
@Deprecated
public EnumPistonReaction getPushReaction(IBlockData iblockdata) {
- return this.material.getPushReaction();
+ return !blockData.isDestroyable() ? EnumPistonReaction.BLOCK : this.material.getPushReaction(); // Paper
}
public void fallOn(World world, BlockPosition blockposition, Entity entity, float f) {
diff --git a/src/main/java/net/minecraft/server/BlockPiston.java b/src/main/java/net/minecraft/server/BlockPiston.java
index b29525c40dc8e3ae747b8ddf5a3bd79b7cc0b792..39cd8ab5925ceb9494e0ac910c73338c24ecda2c 100644
--- a/src/main/java/net/minecraft/server/BlockPiston.java
+++ b/src/main/java/net/minecraft/server/BlockPiston.java
@@ -175,6 +175,12 @@ public class BlockPiston extends BlockDirectional {
@Override
public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, int i, int j) {
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockPiston.FACING);
+ // Paper start - prevent retracting when we're facing the wrong way (we were replaced before retraction could occur)
+ EnumDirection directionQueuedAs = EnumDirection.fromType1(j & 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.a(world, blockposition, enumdirection);
@@ -204,7 +210,7 @@ public class BlockPiston extends BlockDirectional {
}
world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPistonMoving.a, enumdirection)).set(BlockPistonMoving.b, this.sticky ? BlockPropertyPistonType.STICKY : BlockPropertyPistonType.DEFAULT), 3);
- world.setTileEntity(blockposition, BlockPistonMoving.a((IBlockData) this.getBlockData().set(BlockPiston.FACING, EnumDirection.fromType1(j & 7)), enumdirection, false, true));
+ world.setTileEntity(blockposition, BlockPistonMoving.a((IBlockData) this.getBlockData().set(BlockPiston.FACING, EnumDirection.fromType1(j & 7)), enumdirection, false, true)); // Paper - diff on change, j is facing direction
if (this.sticky) {
BlockPosition blockposition1 = blockposition.b(enumdirection.getAdjacentX() * 2, enumdirection.getAdjacentY() * 2, enumdirection.getAdjacentZ() * 2);
IBlockData iblockdata1 = world.getType(blockposition1);
@@ -232,7 +238,14 @@ public class BlockPiston extends BlockDirectional {
}
}
} else {
- world.a(blockposition.shift(enumdirection), false);
+ // Paper start - fix headless pistons breaking blocks
+ BlockPosition headPos = blockposition.shift(enumdirection);
+ if (com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits || world.getType(headPos) == Blocks.PISTON_HEAD.getBlockData().set(FACING, enumdirection)) { // double check to make sure we're not a headless piston.
+ world.setAir(headPos, false);
+ } else {
+ ((WorldServer)world).getChunkProvider().flagDirty(headPos); // ... fix client desync
+ }
+ // Paper end - fix headless pistons breaking blocks
}
world.playSound((EntityHuman) null, blockposition, SoundEffects.BLOCK_PISTON_CONTRACT, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
diff --git a/src/main/java/net/minecraft/server/Explosion.java b/src/main/java/net/minecraft/server/Explosion.java
index a353f3d5fa5a5f54335f73584589de3f5cb20d3e..2552f860ff7a25f74e9a0600e58cefe064fac484 100644
--- a/src/main/java/net/minecraft/server/Explosion.java
+++ b/src/main/java/net/minecraft/server/Explosion.java
@@ -117,6 +117,7 @@ public class Explosion {
for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
BlockPosition blockposition = new BlockPosition(d4, d5, d6);
IBlockData iblockdata = this.world.getType(blockposition);
+ if (!iblockdata.isDestroyable()) continue; // Paper
Fluid fluid = iblockdata.getFluid(); // Paper
if (!iblockdata.isAir() || !fluid.isEmpty()) {
@@ -282,7 +283,7 @@ public class Explosion {
IBlockData iblockdata = this.world.getType(blockposition);
Block block = iblockdata.getBlock();
- if (!iblockdata.isAir()) {
+ if (!iblockdata.isAir() && iblockdata.isDestroyable()) { // Paper
BlockPosition blockposition1 = blockposition.immutableCopy();
this.world.getMethodProfiler().enter("explosion_blocks");
diff --git a/src/main/java/net/minecraft/server/IBlockData.java b/src/main/java/net/minecraft/server/IBlockData.java
index b19bbbbc81376177751396a2de9452ce1f84c06b..296b41bf36ee1ace5bd9db2b810bf926b5f5278f 100644
--- a/src/main/java/net/minecraft/server/IBlockData.java
+++ b/src/main/java/net/minecraft/server/IBlockData.java
@@ -52,6 +52,11 @@ public class IBlockData extends BlockDataAbstract<Block, IBlockData> implements
return (CraftBlockData) cachedCraftBlockData.clone();
}
// Paper end
+ // Paper start
+ public final boolean isDestroyable() {
+ return getBlock().isDestroyable();
+ }
+ // Paper end
public Material getMaterial() {
return this.getBlock().k(this);
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index 7bd3f789b6055d73f3a6c0628652cca241147887..032b7acee8c8af70c0283f5551ce2875607f6c98 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -342,6 +342,10 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i) {
// CraftBukkit start - tree generation
if (this.captureTreeGeneration) {
+ // Paper start
+ IBlockData type = getType(blockposition);
+ if (!type.isDestroyable()) return false;
+ // Paper end
CraftBlockState blockstate = capturedBlockStates.get(blockposition);
if (blockstate == null) {
blockstate = CapturedBlockState.getTreeBlockState(this, blockposition, i);
|