89d51d5f29
Because this exploit has been widely known for years and has not been fixed by Mojang, we decided that it was worth allowing people to toggle it on/off due to how easy it is to make it configurable. It should be noted that this decision does not promise all future exploits will be configurable.
103 Zeilen
5.8 KiB
Diff
103 Zeilen
5.8 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Nassim Jahnke <nassim@njahnke.dev>
|
|
Date: Mon, 31 Jan 2022 11:21:50 +0100
|
|
Subject: [PATCH] Implement regenerateChunk
|
|
|
|
Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
|
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index c787fbcef295f2108a6f843524c00cdea4660f27..7bb43181e4de4cf3a3944aea88c45e54e476433c 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -150,6 +150,7 @@ import org.jetbrains.annotations.Nullable;
|
|
public class CraftWorld extends CraftRegionAccessor implements World {
|
|
public static final int CUSTOM_DIMENSION_OFFSET = 10;
|
|
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
|
|
+ private static final ChunkStatus[] REGEN_CHUNK_STATUSES = {ChunkStatus.BIOMES, ChunkStatus.NOISE, ChunkStatus.SURFACE, ChunkStatus.CARVERS, ChunkStatus.FEATURES, ChunkStatus.INITIALIZE_LIGHT}; // Paper - implement regenerate chunk method
|
|
|
|
private final ServerLevel world;
|
|
private WorldBorder worldBorder;
|
|
@@ -420,27 +421,68 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
@Override
|
|
public boolean regenerateChunk(int x, int z) {
|
|
org.spigotmc.AsyncCatcher.catchOp("chunk regenerate"); // Spigot
|
|
- throw new UnsupportedOperationException("Not supported in this Minecraft version! Unless you can fix it, this is not a bug :)");
|
|
- /*
|
|
- if (!unloadChunk0(x, z, false)) {
|
|
- return false;
|
|
+ // Paper start - implement regenerateChunk method
|
|
+ final ServerLevel serverLevel = this.world;
|
|
+ final net.minecraft.server.level.ServerChunkCache serverChunkCache = serverLevel.getChunkSource();
|
|
+ final ChunkPos chunkPos = new ChunkPos(x, z);
|
|
+ final net.minecraft.world.level.chunk.LevelChunk levelChunk = serverChunkCache.getChunk(chunkPos.x, chunkPos.z, true);
|
|
+ for (final BlockPos blockPos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), serverLevel.getMinBuildHeight(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), serverLevel.getMaxBuildHeight() - 1, chunkPos.getMaxBlockZ())) {
|
|
+ levelChunk.removeBlockEntity(blockPos);
|
|
+ serverLevel.setBlock(blockPos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), 16);
|
|
+ }
|
|
+
|
|
+ for (final ChunkStatus chunkStatus : REGEN_CHUNK_STATUSES) {
|
|
+ final List<ChunkAccess> list = new ArrayList<>();
|
|
+ final int range = Math.max(1, chunkStatus.getRange());
|
|
+ for (int chunkX = chunkPos.z - range; chunkX <= chunkPos.z + range; chunkX++) {
|
|
+ for (int chunkZ = chunkPos.x - range; chunkZ <= chunkPos.x + range; chunkZ++) {
|
|
+ ChunkAccess chunkAccess = serverChunkCache.getChunk(chunkZ, chunkX, chunkStatus.getParent(), true);
|
|
+ if (chunkAccess instanceof ImposterProtoChunk accessProtoChunk) {
|
|
+ chunkAccess = new ImposterProtoChunk(accessProtoChunk.getWrapped(), true);
|
|
+ } else if (chunkAccess instanceof net.minecraft.world.level.chunk.LevelChunk accessLevelChunk) {
|
|
+ chunkAccess = new ImposterProtoChunk(accessLevelChunk, true);
|
|
+ }
|
|
+ list.add(chunkAccess);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final java.util.concurrent.CompletableFuture<com.mojang.datafixers.util.Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = chunkStatus.generate(
|
|
+ Runnable::run,
|
|
+ serverLevel,
|
|
+ serverChunkCache.getGenerator(),
|
|
+ serverLevel.getStructureManager(),
|
|
+ serverChunkCache.getLightEngine(),
|
|
+ chunk -> {
|
|
+ throw new UnsupportedOperationException("Not creating full chunks here");
|
|
+ },
|
|
+ list
|
|
+ );
|
|
+ serverChunkCache.mainThreadProcessor.managedBlock(future::isDone);
|
|
+ if (chunkStatus == ChunkStatus.NOISE) {
|
|
+ future.join().left().ifPresent(chunk -> net.minecraft.world.level.levelgen.Heightmap.primeHeightmaps(chunk, ChunkStatus.POST_FEATURES));
|
|
+ }
|
|
}
|
|
|
|
- final long chunkKey = ChunkCoordIntPair.pair(x, z);
|
|
- world.getChunkProvider().unloadQueue.remove(chunkKey);
|
|
+ for (final BlockPos blockPos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), serverLevel.getMinBuildHeight(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), serverLevel.getMaxBuildHeight() - 1, chunkPos.getMaxBlockZ())) {
|
|
+ serverChunkCache.blockChanged(blockPos);
|
|
+ }
|
|
|
|
- net.minecraft.server.Chunk chunk = world.getChunkProvider().generateChunk(x, z);
|
|
- PlayerChunk playerChunk = world.getPlayerChunkMap().getChunk(x, z);
|
|
- if (playerChunk != null) {
|
|
- playerChunk.chunk = chunk;
|
|
+ final Set<ChunkPos> chunksToRelight = new HashSet<>(9);
|
|
+ for (int chunkX = chunkPos.x - 1; chunkX <= chunkPos.x + 1 ; chunkX++) {
|
|
+ for (int chunkZ = chunkPos.z - 1; chunkZ <= chunkPos.z + 1 ; chunkZ++) {
|
|
+ chunksToRelight.add(new ChunkPos(chunkX, chunkZ));
|
|
+ }
|
|
}
|
|
|
|
- if (chunk != null) {
|
|
- refreshChunk(x, z);
|
|
+ for (final ChunkPos pos : chunksToRelight) {
|
|
+ final ChunkAccess chunk = serverChunkCache.getChunk(pos.x, pos.z, false);
|
|
+ if (chunk != null) {
|
|
+ serverChunkCache.getLightEngine().lightChunk(chunk, false);
|
|
+ }
|
|
}
|
|
|
|
- return chunk != null;
|
|
- */
|
|
+ return true;
|
|
+ // Paper end - implement regenerate chunk method
|
|
}
|
|
|
|
@Override
|