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.
This commit is contained in:
Aikar 2020-05-13 23:01:26 -04:00
parent 27979040fd
commit eb626e1176
6 changed files with 156 additions and 52 deletions

View file

@ -344,7 +344,7 @@
+ return chunk == null ? null : chunk.getFluidState(blockposition);
+ }
+
@Override
+ @Override
+ public final boolean hasChunkAt(BlockPos pos) {
+ return getChunkIfLoaded(pos.getX() >> 4, pos.getZ() >> 4) != null; // Paper - Perf: Optimize Level.hasChunkAt(BlockPosition)Z
+ }
@ -365,18 +365,22 @@
+ return getWorldBorder().isWithinBounds(blockposition) ? getBlockStateIfLoaded(blockposition) : null;
+ }
+
+ @Override
@Override
public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) {
+ // Paper end
ChunkAccess ichunkaccess = this.getChunkSource().getChunk(chunkX, chunkZ, leastStatus, create);
if (ichunkaccess == null && create) {
@@ -207,6 +444,18 @@
@@ -207,6 +444,22 @@
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
+ // CraftBukkit start - tree generation
+ if (this.captureTreeGeneration) {
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
+ BlockState type = getBlockState(pos);
+ if (!type.isDestroyable()) return false;
+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
+ CraftBlockState blockstate = this.capturedBlockStates.get(pos);
+ if (blockstate == null) {
+ blockstate = CapturedBlockState.getTreeBlockState(this, pos, flags);
@ -390,7 +394,7 @@
if (this.isOutsideBuildHeight(pos)) {
return false;
} else if (!this.isClientSide && this.isDebug()) {
@@ -214,45 +463,126 @@
@@ -214,45 +467,126 @@
} else {
LevelChunk chunk = this.getChunkAt(pos);
Block block = state.getBlock();
@ -532,7 +536,7 @@
public void onBlockStateChange(BlockPos pos, BlockState oldBlock, BlockState newBlock) {}
@Override
@@ -270,15 +600,33 @@
@@ -270,15 +604,33 @@
return false;
} else {
FluidState fluid = this.getFluidState(pos);
@ -569,7 +573,7 @@
}
boolean flag1 = this.setBlock(pos, fluid.createLegacyBlock(), 3, maxUpdateDepth);
@@ -340,10 +688,18 @@
@@ -340,10 +692,18 @@
@Override
public BlockState getBlockState(BlockPos pos) {
@ -589,7 +593,7 @@
return chunk.getBlockState(pos);
}
@@ -446,34 +802,53 @@
@@ -446,34 +806,53 @@
this.pendingBlockEntityTickers.clear();
}
@ -641,18 +645,18 @@
+ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
+ // Paper end - Prevent block entity and entity crashes
}
}
+ }
+ // Paper start - Option to prevent armor stands from doing entity lookups
+ @Override
+ public boolean noCollision(@Nullable Entity entity, AABB box) {
+ if (entity instanceof net.minecraft.world.entity.decoration.ArmorStand && !entity.level().paperConfig().entities.armorStands.doCollisionEntityLookups) return false;
+ return LevelAccessor.super.noCollision(entity, box);
+ }
}
+ // Paper end - Option to prevent armor stands from doing entity lookups
public boolean shouldTickDeath(Entity entity) {
return true;
@@ -510,13 +885,32 @@
@@ -510,13 +889,32 @@
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
@ -686,7 +690,7 @@
this.getChunkAt(blockposition).addAndRegisterBlockEntity(blockEntity);
}
}
@@ -643,7 +1037,7 @@
@@ -643,7 +1041,7 @@
for (int k = 0; k < j; ++k) {
EnderDragonPart entitycomplexpart = aentitycomplexpart[k];
@ -695,7 +699,7 @@
if (t0 != null && predicate.test(t0)) {
result.add(t0);
@@ -912,7 +1306,7 @@
@@ -912,7 +1310,7 @@
public static enum ExplosionInteraction implements StringRepresentable {

View file

@ -53,16 +53,17 @@
}
private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) {
@@ -135,7 +150,7 @@
@@ -135,7 +150,8 @@
for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
BlockPos blockposition = BlockPos.containing(d4, d5, d6);
BlockState iblockdata = this.level.getBlockState(blockposition);
- FluidState fluid = this.level.getFluidState(blockposition);
+ if (!iblockdata.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
+ FluidState fluid = iblockdata.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions
if (!this.level.isInWorldBounds(blockposition)) {
break;
@@ -149,6 +164,15 @@
@@ -149,6 +165,15 @@
if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockposition, iblockdata, f)) {
set.add(blockposition);
@ -78,7 +79,7 @@
}
d4 += d0 * 0.30000001192092896D;
@@ -171,7 +195,7 @@
@@ -171,7 +196,7 @@
int l = Mth.floor(this.center.y + (double) f + 1.0D);
int i1 = Mth.floor(this.center.z - (double) f - 1.0D);
int j1 = Mth.floor(this.center.z + (double) f + 1.0D);
@ -87,7 +88,7 @@
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
@@ -192,10 +216,38 @@
@@ -192,10 +217,38 @@
d3 /= d4;
boolean flag = this.damageCalculator.shouldDamageEntity(this, entity);
float f1 = this.damageCalculator.getKnockbackMultiplier(entity);
@ -128,7 +129,7 @@
}
double d5 = (1.0D - d0) * (double) f2 * (double) f1;
@@ -204,7 +256,7 @@
@@ -204,7 +257,7 @@
if (entity instanceof LivingEntity) {
LivingEntity entityliving = (LivingEntity) entity;
@ -137,7 +138,7 @@
} else {
d6 = d5;
}
@@ -214,11 +266,19 @@
@@ -214,11 +267,19 @@
d3 *= d6;
Vec3 vec3d = new Vec3(d1, d2, d3);
@ -158,7 +159,7 @@
this.hitPlayers.put(entityhuman, vec3d);
}
}
@@ -235,10 +295,62 @@
@@ -235,10 +296,62 @@
List<ServerExplosion.StackCollector> list1 = new ArrayList();
Util.shuffle(positions, this.level.random);
@ -221,7 +222,7 @@
this.level.getBlockState(blockposition).onExplosionHit(this.level, blockposition, this, (itemstack, blockposition1) -> {
ServerExplosion.addOrAppendStack(list1, itemstack, blockposition1);
@@ -262,13 +374,22 @@
@@ -262,13 +375,22 @@
BlockPos blockposition = (BlockPos) iterator.next();
if (this.level.random.nextInt(3) == 0 && this.level.getBlockState(blockposition).isAir() && this.level.getBlockState(blockposition.below()).isSolidRender()) {
@ -245,7 +246,7 @@
this.level.gameEvent(this.source, (Holder) GameEvent.EXPLODE, this.center);
List<BlockPos> list = this.calculateExplodedPositions();
@@ -288,6 +409,7 @@
@@ -288,6 +410,7 @@
}
private static void addOrAppendStack(List<ServerExplosion.StackCollector> droppedItemsOut, ItemStack item, BlockPos pos) {
@ -253,7 +254,7 @@
Iterator iterator = droppedItemsOut.iterator();
do {
@@ -372,4 +494,85 @@
@@ -372,4 +495,85 @@
}
}

View file

@ -1,11 +1,30 @@
--- a/net/minecraft/world/level/block/Block.java
+++ b/net/minecraft/world/level/block/Block.java
@@ -292,15 +292,41 @@
});
state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true);
}
+
@@ -88,6 +88,21 @@
public static final int UPDATE_LIMIT = 512;
protected final StateDefinition<Block, BlockState> stateDefinition;
private BlockState defaultBlockState;
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
+ public final boolean isDestroyable() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits ||
+ this != Blocks.BARRIER &&
+ 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.STRUCTURE_BLOCK &&
+ this != Blocks.JIGSAW;
+ }
+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
@Nullable
private Item item;
private static final int CACHE_SIZE = 256;
@@ -295,12 +310,38 @@
}
+ // Paper start - Add BlockBreakBlockEvent
+ public static boolean dropResources(BlockState state, LevelAccessor levelAccessor, BlockPos pos, @Nullable BlockEntity blockEntity, BlockPos source) {
@ -25,9 +44,9 @@
+ block.popExperience(serverLevel, pos, event.getExpToDrop()); // Paper - Properly handle xp dropping
+ }
+ return true;
}
+ }
+ // Paper end - Add BlockBreakBlockEvent
+
public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool) {
+ // Paper start - Properly handle xp dropping
+ dropResources(state, world, pos, blockEntity, entity, tool, true);
@ -43,7 +62,7 @@
}
}
@@ -340,7 +366,13 @@
@@ -340,7 +381,13 @@
ItemEntity entityitem = (ItemEntity) itemEntitySupplier.get();
entityitem.setDefaultPickUpDelay();
@ -58,7 +77,7 @@
return;
}
}
@@ -348,8 +380,13 @@
@@ -348,8 +395,13 @@
}
public void popExperience(ServerLevel world, BlockPos pos, int size) {
@ -73,7 +92,7 @@
}
}
@@ -367,10 +404,18 @@
@@ -367,10 +419,18 @@
return this.defaultBlockState();
}
@ -94,7 +113,7 @@
}
public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {}
@@ -490,15 +535,35 @@
@@ -490,15 +550,35 @@
return this.builtInRegistryHolder;
}

View file

@ -33,7 +33,20 @@
world.blockEvent(pos, this, b0, enumdirection.get3DDataValue());
}
@@ -229,6 +248,13 @@
@@ -197,6 +216,12 @@
@Override
protected boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) {
Direction enumdirection = (Direction) state.getValue(PistonBaseBlock.FACING);
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed; 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 (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits && enumdirection != directionQueuedAs) {
+ return false;
+ }
+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
BlockState iblockdata1 = (BlockState) state.setValue(PistonBaseBlock.EXTENDED, true);
if (!world.isClientSide) {
@@ -229,8 +254,15 @@
BlockState iblockdata2 = (BlockState) ((BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, enumdirection)).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT);
@ -45,9 +58,12 @@
+ }
+ // Paper end - Fix sticky pistons and BlockPistonRetractEvent
world.setBlock(pos, iblockdata2, 20);
world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true));
- world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true));
+ world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - Protect Bedrock and End Portal/Frames from being destroyed; diff on change
world.blockUpdated(pos, iblockdata2.getBlock());
@@ -255,6 +281,13 @@
iblockdata2.updateNeighbourShapes(world, pos, 2);
if (this.isSticky) {
@@ -255,11 +287,25 @@
if (type == 1 && !iblockdata3.isAir() && PistonBaseBlock.isPushable(iblockdata3, world, blockposition1, enumdirection.getOpposite(), false, enumdirection) && (iblockdata3.getPistonPushReaction() == PushReaction.NORMAL || iblockdata3.is(Blocks.PISTON) || iblockdata3.is(Blocks.STICKY_PISTON))) {
this.moveBlocks(world, pos, enumdirection, false);
} else {
@ -61,18 +77,31 @@
world.removeBlock(pos.relative(enumdirection), false);
}
}
@@ -335,7 +368,49 @@
} else {
- world.removeBlock(pos.relative(enumdirection), false);
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed; fix headless pistons breaking blocks
+ BlockPos headPos = pos.relative(enumdirection);
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits || 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 - Protect Bedrock and End Portal/Frames from being destroyed
}
world.playSound((Player) null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
@@ -335,7 +381,49 @@
BlockState[] aiblockdata = new BlockState[list.size() + list2.size()];
Direction enumdirection1 = extend ? dir : dir.getOpposite();
int i = 0;
+ // CraftBukkit start
+ final org.bukkit.block.Block bblock = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+
+ final List<BlockPos> moved = pistonextendschecker.getToPush();
+ final List<BlockPos> broken = pistonextendschecker.getToDestroy();
+
+ List<org.bukkit.block.Block> blocks = new AbstractList<org.bukkit.block.Block>() {
+
+ @Override
+ public int size() {
+ return moved.size() + broken.size();
@ -111,7 +140,7 @@
BlockPos blockposition3;
int j;
BlockState iblockdata1;
@@ -345,7 +420,7 @@
@@ -345,7 +433,7 @@
iblockdata1 = world.getBlockState(blockposition3);
BlockEntity tileentity = iblockdata1.hasBlockEntity() ? world.getBlockEntity(blockposition3) : null;
@ -120,7 +149,7 @@
world.setBlock(blockposition3, Blocks.AIR.defaultBlockState(), 18);
world.gameEvent((Holder) GameEvent.BLOCK_DESTROY, blockposition3, GameEvent.Context.of(iblockdata1));
if (!iblockdata1.is(BlockTags.FIRE)) {
@@ -358,13 +433,25 @@
@@ -358,13 +446,25 @@
BlockState iblockdata2;
for (j = list.size() - 1; j >= 0; --j) {

View file

@ -37,6 +37,15 @@
if (state.hasBlockEntity() && !state.is(newState.getBlock())) {
world.removeBlockEntity(pos);
}
@@ -166,7 +178,7 @@
}
protected void onExplosionHit(BlockState state, ServerLevel world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stackMerger) {
- if (!state.isAir() && explosion.getBlockInteraction() != Explosion.BlockInteraction.TRIGGER_BLOCK) {
+ if (!state.isAir() && explosion.getBlockInteraction() != Explosion.BlockInteraction.TRIGGER_BLOCK && state.isDestroyable()) { // Paper - Protect Bedrock and End Portal/Frames from being destroyed
Block block = state.getBlock();
boolean flag = explosion.getIndirectSourceEntity() instanceof Player;
@@ -174,8 +186,10 @@
BlockEntity tileentity = state.hasBlockEntity() ? world.getBlockEntity(pos) : null;
LootParams.Builder lootparams_a = (new LootParams.Builder(world)).withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos)).withParameter(LootContextParams.TOOL, ItemStack.EMPTY).withOptionalParameter(LootContextParams.BLOCK_ENTITY, tileentity).withOptionalParameter(LootContextParams.THIS_ENTITY, explosion.getDirectSourceEntity());
@ -50,6 +59,15 @@
}
state.spawnAfterBreak(world, pos, ItemStack.EMPTY, flag);
@@ -243,7 +257,7 @@
}
protected boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
- return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem()));
+ return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem())) && (state.isDestroyable() || (context.getPlayer() != null && context.getPlayer().getAbilities().instabuild)); // Paper - Protect Bedrock and End Portal/Frames from being destroyed
}
protected boolean canBeReplaced(BlockState state, Fluid fluid) {
@@ -851,7 +865,15 @@
this.spawnTerrainParticles = blockbase_info.spawnTerrainParticles;
this.instrument = blockbase_info.instrument;
@ -81,7 +99,20 @@
this.legacySolid = this.calculateSolid();
this.occlusionShape = this.canOcclude ? ((Block) this.owner).getOcclusionShape(this.asState()) : Shapes.empty();
@@ -945,19 +969,19 @@
@@ -925,6 +949,12 @@
return this.legacySolid;
}
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
+ public final boolean isDestroyable() {
+ return getBlock().isDestroyable();
+ }
+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
+
public boolean isValidSpawn(BlockGetter world, BlockPos pos, EntityType<?> type) {
return this.getBlock().properties.isValidSpawn.test(this.asState(), world, pos, type);
}
@@ -945,19 +975,19 @@
return this.occlusionShape;
}
@ -106,7 +137,15 @@
return this.isAir;
}
@@ -1035,7 +1059,7 @@
@@ -1028,14 +1058,14 @@
}
public PushReaction getPistonPushReaction() {
- return this.pushReaction;
+ return !this.isDestroyable() ? PushReaction.BLOCK : this.pushReaction; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
}
public boolean isSolidRender() {
return this.solidRender;
}
@ -115,24 +154,22 @@
return this.canOcclude;
}
@@ -1125,9 +1149,15 @@
@@ -1125,7 +1155,13 @@
}
public void onPlace(Level world, BlockPos pos, BlockState state, boolean notify) {
- this.getBlock().onPlace(this.asState(), world, pos, state, notify);
+ // CraftBukkit start
+ this.onPlace(world, pos, state, notify, null);
}
+ }
+
+ public void onPlace(Level world, BlockPos blockposition, BlockState iblockdata, boolean flag, @Nullable UseOnContext context) {
+ this.getBlock().onPlace(this.asState(), world, blockposition, iblockdata, flag, context);
+ // CraftBukkit end
+ }
+
public void onRemove(Level world, BlockPos pos, BlockState state, boolean moved) {
this.getBlock().onRemove(this.asState(), world, pos, state, moved);
}
@@ -1154,6 +1184,7 @@
public void onRemove(Level world, BlockPos pos, BlockState state, boolean moved) {
@@ -1154,6 +1190,7 @@
public void spawnAfterBreak(ServerLevel world, BlockPos pos, ItemStack tool, boolean dropExperience) {
this.getBlock().spawnAfterBreak(this.asState(), world, pos, tool, dropExperience);
@ -140,7 +177,7 @@
}
public List<ItemStack> getDrops(LootParams.Builder builder) {
@@ -1250,11 +1281,11 @@
@@ -1250,11 +1287,11 @@
return this.getBlock().builtInRegistryHolder().is(key);
}

View file

@ -133,3 +133,17 @@
return Optional.of(new BlockUtil.FoundRectangle(blockposition1.immutable(), 2, 3));
}
@@ -178,6 +207,13 @@
for (int j = -1; j < 3; ++j) {
for (int k = -1; k < 4; ++k) {
temp.setWithOffset(pos, portalDirection.getStepX() * j + enumdirection1.getStepX() * distanceOrthogonalToPortal, k, portalDirection.getStepZ() * j + enumdirection1.getStepZ() * distanceOrthogonalToPortal);
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits) {
+ if (!this.level.getBlockState(temp).isDestroyable()) {
+ return false;
+ }
+ }
+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
if (k < 0 && !this.level.getBlockState(temp).isSolid()) {
return false;
}