Implement World#regenerateChunk (#7425)
Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Dieser Commit ist enthalten in:
Ursprung
6df46412cf
Commit
0cc2503b88
21
patches/api/0365-Implement-regenerateChunk.patch
Normale Datei
21
patches/api/0365-Implement-regenerateChunk.patch
Normale Datei
@ -0,0 +1,21 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Nassim Jahnke <jahnke.nassim@gmail.com>
|
||||||
|
Date: Sat, 5 Feb 2022 20:25:28 +0100
|
||||||
|
Subject: [PATCH] Implement regenerateChunk
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
|
||||||
|
index c808cc8dc96e325c543391048414880ed18a3ed3..7d2873a924ee81411a2a00bace0f58403fec43ea 100644
|
||||||
|
--- a/src/main/java/org/bukkit/World.java
|
||||||
|
+++ b/src/main/java/org/bukkit/World.java
|
||||||
|
@@ -505,8 +505,8 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||||
|
* @return Whether the chunk was actually regenerated
|
||||||
|
*
|
||||||
|
* @deprecated regenerating a single chunk is not likely to produce the same
|
||||||
|
- * chunk as before as terrain decoration may be spread across chunks. Use of
|
||||||
|
- * this method should be avoided as it is known to produce buggy results.
|
||||||
|
+ * chunk as before as terrain decoration may be spread across chunks. It may
|
||||||
|
+ * or may not change blocks in the adjacent chunks as well.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public boolean regenerateChunk(int x, int z);
|
@ -7,15 +7,50 @@ Subject: [PATCH] Only write chunk data to disk if it serializes without
|
|||||||
This ensures at least a valid version of the chunk exists
|
This ensures at least a valid version of the chunk exists
|
||||||
on disk, even if outdated
|
on disk, even if outdated
|
||||||
|
|
||||||
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||||
|
index 950efcc80455f73ec8ca4e991fcf9a5b2b7fa22e..012909055f4def2330e2fa839a98ff8bb41ea604 100644
|
||||||
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||||
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||||
|
@@ -1020,6 +1020,24 @@ public class RegionFile implements AutoCloseable {
|
||||||
|
this.pos = chunkcoordintpair;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ // Paper start - don't write garbage data to disk if writing serialization fails
|
||||||
|
+ @Override
|
||||||
|
+ public void write(final int b) {
|
||||||
|
+ if (this.count > 500_000_000) {
|
||||||
|
+ throw new RegionFileStorage.RegionFileSizeException("Region file too large: " + this.count);
|
||||||
|
+ }
|
||||||
|
+ super.write(b);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ public void write(final byte[] b, final int off, final int len) {
|
||||||
|
+ if (this.count + len > 500_000_000) {
|
||||||
|
+ throw new RegionFileStorage.RegionFileSizeException("Region file too large: " + (this.count + len));
|
||||||
|
+ }
|
||||||
|
+ super.write(b, off, len);
|
||||||
|
+ }
|
||||||
|
+ // Paper end
|
||||||
|
+
|
||||||
|
public void close() throws IOException {
|
||||||
|
ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count);
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||||
index 2dde10324e515bd58fc6ba7e93156ae783492cc2..c7216cf3317cbd49b032c44feb370c50928dd21e 100644
|
index 9f6c1de59ca011bd1203499f325fdfa305e215ce..4502362ff3c43eac489125deee59c66d76204e98 100644
|
||||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||||
@@ -298,10 +298,11 @@ public class RegionFileStorage implements AutoCloseable {
|
@@ -298,10 +298,17 @@ public class RegionFileStorage implements AutoCloseable {
|
||||||
NbtIo.write(nbt, (DataOutput) dataoutputstream);
|
NbtIo.write(nbt, (DataOutput) dataoutputstream);
|
||||||
regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - cache status on disk
|
regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - cache status on disk
|
||||||
regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
||||||
+ dataoutputstream.close(); // Paper - only write if successful
|
+ dataoutputstream.close(); // Paper - only write if successful
|
||||||
|
+ // Paper start - don't write garbage data to disk if writing serialization fails
|
||||||
|
+ } catch (RegionFileSizeException e) {
|
||||||
|
+ attempts = 5; // Don't retry
|
||||||
|
+ regionfile.clear(pos);
|
||||||
|
+ throw e;
|
||||||
|
+ // Paper end - don't write garbage data to disk if writing serialization fails
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
if (dataoutputstream != null) {
|
if (dataoutputstream != null) {
|
||||||
try {
|
try {
|
||||||
@ -24,7 +59,7 @@ index 2dde10324e515bd58fc6ba7e93156ae783492cc2..c7216cf3317cbd49b032c44feb370c50
|
|||||||
} catch (Throwable throwable1) {
|
} catch (Throwable throwable1) {
|
||||||
throwable.addSuppressed(throwable1);
|
throwable.addSuppressed(throwable1);
|
||||||
}
|
}
|
||||||
@@ -309,10 +310,7 @@ public class RegionFileStorage implements AutoCloseable {
|
@@ -309,10 +316,7 @@ public class RegionFileStorage implements AutoCloseable {
|
||||||
|
|
||||||
throw throwable;
|
throw throwable;
|
||||||
}
|
}
|
||||||
@ -36,3 +71,17 @@ index 2dde10324e515bd58fc6ba7e93156ae783492cc2..c7216cf3317cbd49b032c44feb370c50
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Paper start
|
// Paper start
|
||||||
|
@@ -359,4 +363,13 @@ public class RegionFileStorage implements AutoCloseable {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // Paper start
|
||||||
|
+ public static final class RegionFileSizeException extends RuntimeException {
|
||||||
|
+
|
||||||
|
+ public RegionFileSizeException(String message) {
|
||||||
|
+ super(message);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ // Paper end
|
||||||
|
}
|
||||||
|
97
patches/server/0862-Implement-regenerateChunk.patch
Normale Datei
97
patches/server/0862-Implement-regenerateChunk.patch
Normale Datei
@ -0,0 +1,97 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Nassim Jahnke <jahnke.nassim@gmail.com>
|
||||||
|
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 5fb475b3ccaa98861e2c817b37cd1740e5bfed8d..f2deb875992ad2d5c9dbcfe9ee7071a773fed684 100644
|
||||||
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||||
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||||
|
@@ -121,6 +121,7 @@ import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
public class CraftWorld extends CraftRegionAccessor implements World {
|
||||||
|
public static final int CUSTOM_DIMENSION_OFFSET = 10;
|
||||||
|
+ private static final ChunkStatus[] REGEN_CHUNK_STATUSES = {ChunkStatus.BIOMES, ChunkStatus.NOISE, ChunkStatus.SURFACE, ChunkStatus.CARVERS, ChunkStatus.LIQUID_CARVERS, ChunkStatus.FEATURES}; // Paper - implement regenerate chunk method
|
||||||
|
|
||||||
|
private final ServerLevel world;
|
||||||
|
private WorldBorder worldBorder;
|
||||||
|
@@ -435,27 +436,61 @@ 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;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- final long chunkKey = ChunkCoordIntPair.pair(x, z);
|
||||||
|
- world.getChunkProvider().unloadQueue.remove(chunkKey);
|
||||||
|
+ // 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, 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);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- net.minecraft.server.Chunk chunk = world.getChunkProvider().generateChunk(x, z);
|
||||||
|
- PlayerChunk playerChunk = world.getPlayerChunkMap().getChunk(x, z);
|
||||||
|
- if (playerChunk != null) {
|
||||||
|
- playerChunk.chunk = chunk;
|
||||||
|
+ final com.mojang.datafixers.util.Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = chunkStatus.generate(
|
||||||
|
+ Runnable::run,
|
||||||
|
+ serverLevel,
|
||||||
|
+ serverChunkCache.getGenerator(),
|
||||||
|
+ serverLevel.getStructureManager(),
|
||||||
|
+ serverChunkCache.getLightEngine(),
|
||||||
|
+ chunk -> {
|
||||||
|
+ throw new UnsupportedOperationException("Not creating full chunks here");
|
||||||
|
+ },
|
||||||
|
+ list,
|
||||||
|
+ true
|
||||||
|
+ ).join();
|
||||||
|
+ if (chunkStatus == ChunkStatus.NOISE) {
|
||||||
|
+ either.left().ifPresent(chunk -> net.minecraft.world.level.levelgen.Heightmap.primeHeightmaps(chunk, ChunkStatus.POST_FEATURES));
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (chunk != null) {
|
||||||
|
- refreshChunk(x, z);
|
||||||
|
+ for (final BlockPos blockPos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), serverLevel.getMinBuildHeight(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), serverLevel.getMaxBuildHeight() - 1, chunkPos.getMaxBlockZ())) {
|
||||||
|
+ serverChunkCache.blockChanged(blockPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
- return chunk != null;
|
||||||
|
- */
|
||||||
|
+ 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));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ serverChunkCache.getLightEngine().relight(chunksToRelight, pos -> {}, relit -> {});
|
||||||
|
+ return true;
|
||||||
|
+ // Paper end
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
In neuem Issue referenzieren
Einen Benutzer sperren