Skip to content

Commit

Permalink
fix: improve biome setting to avoid writing directly to chunk (#2757)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
dordsor21 authored Jun 15, 2024
1 parent 8aba1e6 commit af83b2f
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 164 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,14 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
}
}
} else {
setBiomesToPalettedContainer(biomes, setSectionIndex, existingSection.getBiomes());
PalettedContainer<Holder<Biome>> paletteBiomes = setBiomesToPalettedContainer(
biomes,
setSectionIndex,
existingSection.getBiomes()
);
if (paletteBiomes != null) {
PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes);
}
}
}
}
Expand Down Expand Up @@ -553,11 +560,7 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
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);
Expand Down Expand Up @@ -625,15 +628,14 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
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,
Expand Down Expand Up @@ -845,7 +847,7 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
}
if (callback == null) {
if (finalizer != null) {
finalizer.run();
queueHandler.async(finalizer, null);
}
return null;
} else {
Expand Down Expand Up @@ -1103,9 +1105,13 @@ private PalettedContainer<Holder<Biome>> setBiomesToPalettedContainer(
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 " +
Expand All @@ -1115,10 +1121,6 @@ private PalettedContainer<Holder<Biome>> setBiomesToPalettedContainer(
);
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++) {
Expand All @@ -1130,10 +1132,7 @@ private PalettedContainer<Holder<Biome>> setBiomesToPalettedContainer(
x,
y,
z,
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.getInternalBiomeId(biomeType))
biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -502,6 +502,14 @@ private static LevelChunkSection newChunkSection(
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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,14 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
}
}
} else {
setBiomesToPalettedContainer(biomes, setSectionIndex, existingSection.getBiomes());
PalettedContainer<Holder<Biome>> paletteBiomes = setBiomesToPalettedContainer(
biomes,
setSectionIndex,
existingSection.getBiomes()
);
if (paletteBiomes != null) {
PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes);
}
}
}
}
Expand Down Expand Up @@ -552,11 +559,7 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
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
) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap);
newSection = PaperweightPlatformAdapter.newChunkSection(
Expand Down Expand Up @@ -623,15 +626,14 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
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,
Expand Down Expand Up @@ -843,7 +845,7 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
}
if (callback == null) {
if (finalizer != null) {
finalizer.run();
queueHandler.async(finalizer, null);
}
return null;
} else {
Expand Down Expand Up @@ -1100,9 +1102,13 @@ private PalettedContainer<Holder<Biome>> setBiomesToPalettedContainer(
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 " +
Expand All @@ -1112,10 +1118,6 @@ private PalettedContainer<Holder<Biome>> setBiomesToPalettedContainer(
);
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++) {
Expand All @@ -1127,10 +1129,7 @@ private PalettedContainer<Holder<Biome>> setBiomesToPalettedContainer(
x,
y,
z,
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
.getInstance()
.getBukkitImplAdapter()
.getInternalBiomeId(biomeType))
biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(biomeType))
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
import sun.misc.Unsafe;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -102,7 +101,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {

private static final Field fieldTickingFluidCount;
private static final Field fieldTickingBlockCount;
private static final Field fieldNonEmptyBlockCount;

private static final MethodHandle methodGetVisibleChunk;

Expand All @@ -111,6 +109,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {

private static final MethodHandle methodRemoveGameEventListener;
private static final MethodHandle methodremoveTickingBlockEntity;
private static final Field fieldBiomes;

private static final Field fieldOffers;
private static final MerchantOffers OFFERS = new MerchantOffers();
Expand Down Expand Up @@ -149,8 +148,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
fieldTickingFluidCount.setAccessible(true);
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f"));
fieldTickingBlockCount.setAccessible(true);
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "e"));
fieldNonEmptyBlockCount.setAccessible(true);
fieldBiomes = LevelChunkSection.class.getDeclaredField(Refraction.pickName("biomes", "i"));
fieldBiomes.setAccessible(true);

Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
"getVisibleChunkIfPresent",
Expand Down Expand Up @@ -417,7 +416,7 @@ public static LevelChunkSection newChunkSection(
@Nullable PalettedContainer<Holder<Biome>> biomes
) {
if (set == null) {
return newChunkSection(layer, biomeRegistry, biomes);
return newChunkSection(biomeRegistry, biomes);
}
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
Expand Down Expand Up @@ -507,7 +506,6 @@ public static LevelChunkSection newChunkSection(

@SuppressWarnings("deprecation") // Only deprecated in paper
private static LevelChunkSection newChunkSection(
int layer,
Registry<Biome> biomeRegistry,
@Nullable PalettedContainer<Holder<Biome>> biomes
) {
Expand All @@ -522,6 +520,14 @@ private static LevelChunkSection newChunkSection(
return new LevelChunkSection(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.
*/
Expand Down
Loading

0 comments on commit af83b2f

Please sign in to comment.