Skip to content

Commit

Permalink
Fixed issues regarding calculation of unloaded chunks that weren't sa…
Browse files Browse the repository at this point in the history
…ved to disk yet (#1857)
  • Loading branch information
OmerBenGera committed Sep 16, 2023
1 parent d5f1fdf commit 6239172
Show file tree
Hide file tree
Showing 15 changed files with 1,254 additions and 929 deletions.
@@ -1,5 +1,6 @@
package com.bgsoftware.superiorskyblock.nms.v117;

import com.bgsoftware.common.annotations.Nullable;
import com.bgsoftware.common.reflection.ReflectField;
import com.bgsoftware.common.reflection.ReflectMethod;
import com.bgsoftware.superiorskyblock.SuperiorSkyblockPlugin;
Expand Down Expand Up @@ -97,37 +98,48 @@ public void setBiome(List<ChunkPosition> chunkPositions, org.bukkit.block.Biome

Biome biome = CraftBlock.biomeToBiomeBase(biomesRegistry, bukkitBiome);

NMSUtils.runActionOnChunks(serverLevel, chunksCoords, true, null, levelChunk -> {
ChunkPos chunkPos = levelChunk.getPos();
Biome[] biomes = BIOME_BASE_ARRAY.get(levelChunk.getBiomes());

if (biomes == null)
throw new RuntimeException("Error while receiving biome bases of chunk (" + chunkPos.x + "," + chunkPos.z + ").");

Arrays.fill(biomes, biome);
levelChunk.setUnsaved(true);

ClientboundForgetLevelChunkPacket forgetLevelChunkPacket = new ClientboundForgetLevelChunkPacket(chunkPos.x, chunkPos.z);
//noinspection deprecation
ClientboundLevelChunkPacket levelChunkPacket = new ClientboundLevelChunkPacket(levelChunk);
ClientboundLightUpdatePacket lightUpdatePacket = new ClientboundLightUpdatePacket(chunkPos,
serverLevel.getLightEngine(), null, null, true);

playersToUpdate.forEach(player -> {
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
serverPlayer.connection.send(forgetLevelChunkPacket);
serverPlayer.connection.send(lightUpdatePacket);
serverPlayer.connection.send(levelChunkPacket);
});
}, (chunkCoords, unloadedChunk) -> {
int[] biomes = unloadedChunk.contains("Biomes", 11) ? unloadedChunk.getIntArray("Biomes") : new int[256];
Arrays.fill(biomes, biomesRegistry.getId(biome));
unloadedChunk.putIntArray("Biomes", biomes);
NMSUtils.runActionOnChunks(serverLevel, chunksCoords, true, new NMSUtils.ChunkCallback() {
@Override
public void onLoadedChunk(LevelChunk levelChunk) {
ChunkPos chunkPos = levelChunk.getPos();
Biome[] biomes = BIOME_BASE_ARRAY.get(levelChunk.getBiomes());

if (biomes == null)
throw new RuntimeException("Error while receiving biome bases of chunk (" + chunkPos.x + "," + chunkPos.z + ").");

Arrays.fill(biomes, biome);
levelChunk.setUnsaved(true);

ClientboundForgetLevelChunkPacket forgetLevelChunkPacket = new ClientboundForgetLevelChunkPacket(chunkPos.x, chunkPos.z);
//noinspection deprecation
ClientboundLevelChunkPacket levelChunkPacket = new ClientboundLevelChunkPacket(levelChunk);
ClientboundLightUpdatePacket lightUpdatePacket = new ClientboundLightUpdatePacket(chunkPos,
serverLevel.getLightEngine(), null, null, true);

playersToUpdate.forEach(player -> {
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
serverPlayer.connection.send(forgetLevelChunkPacket);
serverPlayer.connection.send(lightUpdatePacket);
serverPlayer.connection.send(levelChunkPacket);
});
}

@Override
public void onUnloadedChunk(ChunkPos chunkPos, CompoundTag unloadedChunk) {
int[] biomes = unloadedChunk.contains("Biomes", 11) ? unloadedChunk.getIntArray("Biomes") : new int[256];
Arrays.fill(biomes, biomesRegistry.getId(biome));
unloadedChunk.putIntArray("Biomes", biomes);
}

@Override
public void onFinish() {
// Do nothing.
}
});
}

@Override
public void deleteChunks(Island island, List<ChunkPosition> chunkPositions, Runnable onFinish) {
public void deleteChunks(Island island, List<ChunkPosition> chunkPositions, @Nullable Runnable onFinish) {
if (chunkPositions.isEmpty())
return;

Expand All @@ -139,57 +151,69 @@ public void deleteChunks(Island island, List<ChunkPosition> chunkPositions, Runn

ServerLevel serverLevel = ((CraftWorld) chunkPositions.get(0).getWorld()).getHandle();

NMSUtils.runActionOnChunks(serverLevel, chunksCoords, true, onFinish, levelChunk -> {
Arrays.fill(levelChunk.getSections(), LevelChunk.EMPTY_SECTION);
NMSUtils.runActionOnChunks(serverLevel, chunksCoords, true, new NMSUtils.ChunkCallback() {
@Override
public void onLoadedChunk(LevelChunk levelChunk) {
Arrays.fill(levelChunk.getSections(), LevelChunk.EMPTY_SECTION);

removeEntities(levelChunk);
removeEntities(levelChunk);

new HashSet<>(levelChunk.getBlockEntities().keySet()).forEach(levelChunk.getLevel()::removeBlockEntity);
levelChunk.getBlockEntities().clear();
new HashSet<>(levelChunk.getBlockEntities().keySet()).forEach(levelChunk.getLevel()::removeBlockEntity);
levelChunk.getBlockEntities().clear();

removeBlocks(levelChunk);
}, (chunkCoords, levelCompound) -> {
ListTag sectionsList = new ListTag();
ListTag tileEntities = new ListTag();
removeBlocks(levelChunk);
}

levelCompound.put("Sections", sectionsList);
levelCompound.put("TileEntities", tileEntities);
levelCompound.put("Entities", new ListTag());
@Override
public void onUnloadedChunk(ChunkPos chunkPos, CompoundTag unloadedChunk) {
ListTag sectionsList = new ListTag();
ListTag tileEntities = new ListTag();

if (!(serverLevel.generator instanceof IslandsGenerator)) {
ProtoChunk protoChunk = NMSUtils.createProtoChunk(chunkCoords, serverLevel);
unloadedChunk.put("Sections", sectionsList);
unloadedChunk.put("TileEntities", tileEntities);
unloadedChunk.put("Entities", new ListTag());

try {
CustomChunkGenerator customChunkGenerator = new CustomChunkGenerator(serverLevel,
serverLevel.getChunkSource().getGenerator(), serverLevel.generator);
if (!(serverLevel.generator instanceof IslandsGenerator)) {
ProtoChunk protoChunk = NMSUtils.createProtoChunk(chunkPos, serverLevel);

WorldGenRegion region = new WorldGenRegion(serverLevel, Collections.singletonList(protoChunk),
ChunkStatus.SURFACE, 0);
try {
CustomChunkGenerator customChunkGenerator = new CustomChunkGenerator(serverLevel,
serverLevel.getChunkSource().getGenerator(), serverLevel.generator);

customChunkGenerator.buildSurface(region, protoChunk);
} catch (Exception ignored) {
}
WorldGenRegion region = new WorldGenRegion(serverLevel, Collections.singletonList(protoChunk),
ChunkStatus.SURFACE, 0);

LevelLightEngine lightEngine = serverLevel.getLightEngine();
LevelChunkSection[] levelChunkSections = protoChunk.getSections();
customChunkGenerator.buildSurface(region, protoChunk);
} catch (Exception ignored) {
}

for (int i = lightEngine.getMinLightSection(); i < lightEngine.getMaxLightSection(); ++i) {
for (LevelChunkSection levelChunkSection : levelChunkSections) {
if (levelChunkSection != LevelChunk.EMPTY_SECTION && levelChunkSection.bottomBlockY() >> 4 == i) {
CompoundTag sectionCompound = new CompoundTag();
sectionCompound.putByte("Y", (byte) (i & 255));
levelChunkSection.getStates().write(sectionCompound, "Palette", "BlockStates");
sectionsList.add(sectionCompound);
LevelLightEngine lightEngine = serverLevel.getLightEngine();
LevelChunkSection[] levelChunkSections = protoChunk.getSections();

for (int i = lightEngine.getMinLightSection(); i < lightEngine.getMaxLightSection(); ++i) {
for (LevelChunkSection levelChunkSection : levelChunkSections) {
if (levelChunkSection != LevelChunk.EMPTY_SECTION && levelChunkSection.bottomBlockY() >> 4 == i) {
CompoundTag sectionCompound = new CompoundTag();
sectionCompound.putByte("Y", (byte) (i & 255));
levelChunkSection.getStates().write(sectionCompound, "Palette", "BlockStates");
sectionsList.add(sectionCompound);
}
}
}
}

for (BlockPos blockEntityPos : protoChunk.getBlockEntitiesPos()) {
CompoundTag blockEntityCompound = protoChunk.getBlockEntityNbt(blockEntityPos);
if (blockEntityCompound != null)
tileEntities.add(blockEntityCompound);
for (BlockPos blockEntityPos : protoChunk.getBlockEntitiesPos()) {
CompoundTag blockEntityCompound = protoChunk.getBlockEntityNbt(blockEntityPos);
if (blockEntityCompound != null)
tileEntities.add(blockEntityCompound);
}
}
}

@Override
public void onFinish() {
if (onFinish != null)
onFinish.run();
}
});
}

Expand Down Expand Up @@ -218,31 +242,40 @@ public CompletableFuture<List<CalculatedChunk>> calculateChunks(List<ChunkPositi

ServerLevel serverLevel = ((CraftWorld) chunkPositions.get(0).getWorld()).getHandle();

NMSUtils.runActionOnChunks(serverLevel, chunksCoords, false, () -> {
completableFuture.complete(allCalculatedChunks);
}, levelChunk -> {
ChunkPos chunkPos = levelChunk.getPos();
ChunkPosition chunkPosition = ChunkPosition.of(serverLevel.getWorld(), chunkPos.x, chunkPos.z);
allCalculatedChunks.add(calculateChunk(chunkPosition, levelChunk.getSections()));
}, (chunkPos, unloadedChunk) -> {
ListTag sectionsList = unloadedChunk.getList("Sections", 10);
LevelChunkSection[] levelChunkSections = new LevelChunkSection[sectionsList.size()];

for (int i = 0; i < sectionsList.size(); ++i) {
CompoundTag sectionCompound = sectionsList.getCompound(i);
byte yPosition = sectionCompound.getByte("Y");
if (sectionCompound.contains("Palette", 9) && sectionCompound.contains("BlockStates", 12)) {
//noinspection deprecation
levelChunkSections[i] = new LevelChunkSection(yPosition);
levelChunkSections[i].getStates().read(sectionCompound.getList("Palette", 10),
sectionCompound.getLongArray("BlockStates"));
NMSUtils.runActionOnChunks(serverLevel, chunksCoords, false, new NMSUtils.ChunkCallback() {
@Override
public void onLoadedChunk(LevelChunk levelChunk) {
ChunkPos chunkPos = levelChunk.getPos();
ChunkPosition chunkPosition = ChunkPosition.of(serverLevel.getWorld(), chunkPos.x, chunkPos.z);
allCalculatedChunks.add(calculateChunk(chunkPosition, levelChunk.getSections()));
}

@Override
public void onUnloadedChunk(ChunkPos chunkPos, CompoundTag unloadedChunk) {
ListTag sectionsList = unloadedChunk.getList("Sections", 10);
LevelChunkSection[] levelChunkSections = new LevelChunkSection[sectionsList.size()];

for (int i = 0; i < sectionsList.size(); ++i) {
CompoundTag sectionCompound = sectionsList.getCompound(i);
byte yPosition = sectionCompound.getByte("Y");
if (sectionCompound.contains("Palette", 9) && sectionCompound.contains("BlockStates", 12)) {
//noinspection deprecation
levelChunkSections[i] = new LevelChunkSection(yPosition);
levelChunkSections[i].getStates().read(sectionCompound.getList("Palette", 10),
sectionCompound.getLongArray("BlockStates"));
}
}

ChunkPosition chunkPosition = ChunkPosition.of(serverLevel.getWorld(), chunkPos.x, chunkPos.z);
CalculatedChunk calculatedChunk = calculateChunk(chunkPosition, levelChunkSections);
allCalculatedChunks.add(calculatedChunk);
unloadedChunksCache.put(chunkPosition, calculatedChunk);
}

ChunkPosition chunkPosition = ChunkPosition.of(serverLevel.getWorld(), chunkPos.x, chunkPos.z);
CalculatedChunk calculatedChunk = calculateChunk(chunkPosition, levelChunkSections);
allCalculatedChunks.add(calculatedChunk);
unloadedChunksCache.put(chunkPosition, calculatedChunk);
@Override
public void onFinish() {
completableFuture.complete(allCalculatedChunks);
}
});

return completableFuture;
Expand Down
Expand Up @@ -37,12 +37,11 @@
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

public class NMSUtils {

Expand All @@ -60,8 +59,7 @@ private NMSUtils() {
}

public static void runActionOnChunks(ServerLevel serverLevel, Collection<ChunkPos> chunksCoords,
boolean saveChunks, Runnable onFinish, Consumer<LevelChunk> chunkConsumer,
BiConsumer<ChunkPos, net.minecraft.nbt.CompoundTag> unloadedChunkConsumer) {
boolean saveChunks, ChunkCallback chunkCallback) {
List<ChunkPos> unloadedChunks = new LinkedList<>();
List<LevelChunk> loadedChunks = new LinkedList<>();

Expand All @@ -74,8 +72,8 @@ public static void runActionOnChunks(ServerLevel serverLevel, Collection<ChunkPo
chunkAccess = serverLevel.getChunkIfLoaded(chunkPos.x, chunkPos.z);
}

if (chunkAccess instanceof LevelChunk) {
loadedChunks.add((LevelChunk) chunkAccess);
if (chunkAccess instanceof LevelChunk levelChunk) {
loadedChunks.add(levelChunk);
} else {
unloadedChunks.add(chunkPos);
}
Expand All @@ -84,26 +82,38 @@ public static void runActionOnChunks(ServerLevel serverLevel, Collection<ChunkPo
boolean hasUnloadedChunks = !unloadedChunks.isEmpty();

if (!loadedChunks.isEmpty())
runActionOnLoadedChunks(loadedChunks, chunkConsumer);
runActionOnLoadedChunks(loadedChunks, chunkCallback);

if (hasUnloadedChunks) {
runActionOnUnloadedChunks(serverLevel, unloadedChunks, saveChunks, unloadedChunkConsumer, onFinish);
} else if (onFinish != null) {
onFinish.run();
runActionOnUnloadedChunks(serverLevel, unloadedChunks, saveChunks, chunkCallback);
} else {
chunkCallback.onFinish();
}
}

public static void runActionOnLoadedChunks(Collection<LevelChunk> chunks, Consumer<LevelChunk> chunkConsumer) {
chunks.forEach(chunkConsumer);
public static void runActionOnLoadedChunks(Collection<LevelChunk> chunks, ChunkCallback chunkCallback) {
chunks.forEach(chunkCallback::onLoadedChunk);
}

public static void runActionOnUnloadedChunks(ServerLevel serverLevel,
Collection<ChunkPos> chunks,
boolean saveChunks,
BiConsumer<ChunkPos, net.minecraft.nbt.CompoundTag> chunkConsumer,
Runnable onFinish) {
public static void runActionOnUnloadedChunks(ServerLevel serverLevel, Collection<ChunkPos> chunks,
boolean saveChunks, ChunkCallback chunkCallback) {
ChunkMap chunkMap = serverLevel.getChunkSource().chunkMap;

Iterator<ChunkPos> chunksIterator = chunks.iterator();
while (chunksIterator.hasNext()) {
ChunkPos chunkPos = chunksIterator.next();
LevelChunk cachedUnloadedChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkPos.x, chunkPos.z);
if (cachedUnloadedChunk != null) {
chunkCallback.onLoadedChunk(cachedUnloadedChunk);
chunksIterator.remove();
}
}

if (chunks.isEmpty()) {
chunkCallback.onFinish();
return;
}

CompletableFuture<Void> pendingTask = new CompletableFuture<>();
PENDING_CHUNK_ACTIONS.add(pendingTask);

Expand All @@ -119,7 +129,7 @@ public static void runActionOnUnloadedChunks(ServerLevel serverLevel,
Suppliers.ofInstance(serverLevel.getDataStorage()), chunkCompound, chunkCoords, serverLevel);

if (chunkDataCompound.contains("Level", 10)) {
chunkConsumer.accept(chunkCoords, chunkDataCompound.getCompound("Level"));
chunkCallback.onUnloadedChunk(chunkCoords, chunkDataCompound.getCompound("Level"));
if (saveChunks)
chunkMap.write(chunkCoords, chunkDataCompound);
}
Expand All @@ -128,8 +138,7 @@ public static void runActionOnUnloadedChunks(ServerLevel serverLevel,
}
});
}).runSync(v -> {
if (onFinish != null)
onFinish.run();
chunkCallback.onFinish();

pendingTask.complete(null);
PENDING_CHUNK_ACTIONS.remove(pendingTask);
Expand Down Expand Up @@ -270,4 +279,14 @@ private static boolean isValidPosition(ServerLevel serverLevel, BlockPos blockPo
blockPos.getY() >= serverLevel.getMinBuildHeight() && blockPos.getY() < serverLevel.getMaxBuildHeight();
}

public interface ChunkCallback {

void onLoadedChunk(LevelChunk levelChunk);

void onUnloadedChunk(ChunkPos chunkPos, net.minecraft.nbt.CompoundTag unloadedChunk);

void onFinish();

}

}

0 comments on commit 6239172

Please sign in to comment.