fix: improve biome setting to avoid writing directly to chunk (#2757)

* fix: improve biome setting to avoid writing directly to chunk

 - Removes possibility of writing to the LevelChunkSection biomes PalettedContainer whilst it is being read for sending packets
 - I believe this occured mostly on clipboard operations where blocks are written before biomes, so chunks are being sent whilst writing biomes
 - This would explain why the error reported in the below issue (and others) is/was so rare
 - Of course I could be completely wrong about all of this, but given the line in LevelChunkSection#write that the error seems to consistently occur on is when writing biomes to the packet, and that the only place I can find in FAWE where we write to a "live" PalettedContainer is for biomes, I am reasonably confident that this is the cause
 - Should address #2729

* Remove self-refraction-check
This commit is contained in:
Jordan
2024-06-15 13:08:42 +02:00
committed by GitHub
parent 8aba1e6c06
commit af83b2f9c9
10 changed files with 252 additions and 164 deletions

View File

@ -511,7 +511,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}
}
} else {
setBiomesToPalettedContainer(biomes, setSectionIndex, existingSection.getBiomes());
PalettedContainer<Holder<Biome>> paletteBiomes = setBiomesToPalettedContainer(
biomes,
setSectionIndex,
existingSection.getBiomes()
);
if (paletteBiomes != null) {
PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes);
}
}
}
}
@ -553,11 +560,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
if (existingSection == null) {
PalettedContainer<Holder<Biome>> biomeData = biomes == null ? new PalettedContainer<>(
biomeHolderIdMap,
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.getInternalBiomeId(
BiomeTypes.PLAINS)),
biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
PalettedContainer.Strategy.SECTION_BIOMES,
null
) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap);
@ -625,15 +628,14 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
existingSection.getBiomes()
);
newSection =
PaperweightPlatformAdapter.newChunkSection(
layerNo,
this::loadPrivately,
setArr,
adapter,
biomeRegistry,
biomeData
);
newSection = PaperweightPlatformAdapter.newChunkSection(
layerNo,
this::loadPrivately,
setArr,
adapter,
biomeRegistry,
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
);
if (!PaperweightPlatformAdapter.setSectionAtomic(
levelChunkSections,
existingSection,
@ -845,7 +847,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}
if (callback == null) {
if (finalizer != null) {
finalizer.run();
queueHandler.async(finalizer, null);
}
return null;
} else {
@ -1103,9 +1105,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
final int sectionIndex,
final PalettedContainerRO<Holder<Biome>> data
) {
BiomeType[] sectionBiomes;
if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) {
return null;
}
PalettedContainer<Holder<Biome>> biomeData;
if (data instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
biomeData = palettedContainer;
biomeData = palettedContainer.copy();
} else {
LOGGER.warn(
"Cannot correctly set biomes to world, existing biomes may be lost. Expected class " +
@ -1115,10 +1121,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
);
biomeData = data.recreate();
}
BiomeType[] sectionBiomes;
if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) {
return biomeData;
}
for (int y = 0, index = 0; y < 4; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < 4; x++, index++) {
@ -1130,10 +1132,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
x,
y,
z,
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.getInternalBiomeId(biomeType))
biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))
);
}
}

View File

@ -98,7 +98,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final Field fieldTickingFluidCount;
private static final Field fieldTickingBlockCount;
private static final Field fieldNonEmptyBlockCount;
private static final Field fieldBiomes;
private static final MethodHandle methodGetVisibleChunk;
@ -139,8 +139,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
fieldTickingFluidCount.setAccessible(true);
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "g"));
fieldTickingBlockCount.setAccessible(true);
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "f"));
fieldNonEmptyBlockCount.setAccessible(true);
fieldBiomes = LevelChunkSection.class.getDeclaredField(Refraction.pickName("biomes", "j"));
fieldBiomes.setAccessible(true);
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
"getVisibleChunkIfPresent",
@ -502,6 +502,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
return new LevelChunkSection(layer, dataPaletteBlocks, biomes);
}
public static void setBiomesToChunkSection(LevelChunkSection section, PalettedContainer<Holder<Biome>> biomes) {
try {
fieldBiomes.set(section, biomes);
} catch (IllegalAccessException e) {
LOGGER.error("Could not set biomes to chunk section", e);
}
}
/**
* Create a new {@link PalettedContainer<Biome>}. Should only be used if no biome container existed beforehand.
*/