Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-12-25 10:30:04 +01:00
Simplify heightmap calculation and avoid loading empty sections (#3045)
* Simplify heightmap calculation and avoid loading empty sections * rename label
Dieser Commit ist enthalten in:
Ursprung
c8f19849cb
Commit
b95bcde152
@ -1154,6 +1154,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return getSections(false)[layer] != null;
|
return getSections(false)[layer] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNonEmptySection(int layer) {
|
||||||
|
layer -= getMinSectionPosition();
|
||||||
|
LevelChunkSection section = getSections(false)[layer];
|
||||||
|
return section != null && !section.hasOnlyAir();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public synchronized boolean trim(boolean aggressive) {
|
public synchronized boolean trim(boolean aggressive) {
|
||||||
|
@ -1152,6 +1152,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return getSections(false)[layer] != null;
|
return getSections(false)[layer] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNonEmptySection(int layer) {
|
||||||
|
layer -= getMinSectionPosition();
|
||||||
|
LevelChunkSection section = getSections(false)[layer];
|
||||||
|
return section != null && !section.hasOnlyAir();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public synchronized boolean trim(boolean aggressive) {
|
public synchronized boolean trim(boolean aggressive) {
|
||||||
|
@ -1155,6 +1155,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return getSections(false)[layer] != null;
|
return getSections(false)[layer] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNonEmptySection(int layer) {
|
||||||
|
layer -= getMinSectionPosition();
|
||||||
|
LevelChunkSection section = getSections(false)[layer];
|
||||||
|
return section != null && !section.hasOnlyAir();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public synchronized boolean trim(boolean aggressive) {
|
public synchronized boolean trim(boolean aggressive) {
|
||||||
|
@ -1150,6 +1150,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return getSections(false)[layer] != null;
|
return getSections(false)[layer] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNonEmptySection(int layer) {
|
||||||
|
layer -= getMinSectionPosition();
|
||||||
|
LevelChunkSection section = getSections(false)[layer];
|
||||||
|
return section != null && !section.hasOnlyAir();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public synchronized boolean trim(boolean aggressive) {
|
public synchronized boolean trim(boolean aggressive) {
|
||||||
|
@ -1151,6 +1151,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return getSections(false)[layer] != null;
|
return getSections(false)[layer] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNonEmptySection(int layer) {
|
||||||
|
layer -= getMinSectionPosition();
|
||||||
|
LevelChunkSection section = getSections(false)[layer];
|
||||||
|
return section != null && !section.hasOnlyAir();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public synchronized boolean trim(boolean aggressive) {
|
public synchronized boolean trim(boolean aggressive) {
|
||||||
|
@ -1149,6 +1149,13 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return getSections(false)[layer] != null;
|
return getSections(false)[layer] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNonEmptySection(int layer) {
|
||||||
|
layer -= getMinSectionPosition();
|
||||||
|
LevelChunkSection section = getSections(false)[layer];
|
||||||
|
return section != null && !section.hasOnlyAir();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public synchronized boolean trim(boolean aggressive) {
|
public synchronized boolean trim(boolean aggressive) {
|
||||||
|
@ -18,11 +18,10 @@ public class HeightmapProcessor implements IBatchProcessor {
|
|||||||
private static final HeightMapType[] TYPES = HeightMapType.values();
|
private static final HeightMapType[] TYPES = HeightMapType.values();
|
||||||
private static final int BLOCKS_PER_Y_SHIFT = 8; // log2(256)
|
private static final int BLOCKS_PER_Y_SHIFT = 8; // log2(256)
|
||||||
private static final int BLOCKS_PER_Y = 256; // 16 x 16
|
private static final int BLOCKS_PER_Y = 256; // 16 x 16
|
||||||
private static final boolean[] COMPLETE = new boolean[BLOCKS_PER_Y];
|
|
||||||
private static final char[] AIR_LAYER = new char[4096];
|
private static final char[] AIR_LAYER = new char[4096];
|
||||||
|
private static final int NEEDED_UPDATES = TYPES.length * BLOCKS_PER_Y;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Arrays.fill(COMPLETE, true);
|
|
||||||
Arrays.fill(AIR_LAYER, (char) BlockTypesCache.ReservedIDs.AIR);
|
Arrays.fill(AIR_LAYER, (char) BlockTypesCache.ReservedIDs.AIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,13 +48,12 @@ public class HeightmapProcessor implements IBatchProcessor {
|
|||||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||||
// each heightmap gets one 16*16 array
|
// each heightmap gets one 16*16 array
|
||||||
int[][] heightmaps = new int[TYPES.length][BLOCKS_PER_Y];
|
int[][] heightmaps = new int[TYPES.length][BLOCKS_PER_Y];
|
||||||
boolean[][] updated = new boolean[TYPES.length][BLOCKS_PER_Y];
|
byte[] updated = new byte[BLOCKS_PER_Y];
|
||||||
int skip = 0;
|
int updateCount = 0; // count updates, this way we know when we're finished
|
||||||
int allSkipped = (1 << TYPES.length) - 1; // lowest types.length bits are set
|
layerIter:
|
||||||
layer:
|
|
||||||
for (int layer = maxY >> 4; layer >= minY >> 4; layer--) {
|
for (int layer = maxY >> 4; layer >= minY >> 4; layer--) {
|
||||||
boolean hasSectionSet = set.hasSection(layer);
|
boolean hasSectionSet = set.hasNonEmptySection(layer);
|
||||||
boolean hasSectionGet = get.hasSection(layer);
|
boolean hasSectionGet = get.hasNonEmptySection(layer);
|
||||||
if (!(hasSectionSet || hasSectionGet)) {
|
if (!(hasSectionSet || hasSectionGet)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -78,7 +76,7 @@ public class HeightmapProcessor implements IBatchProcessor {
|
|||||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
if (!hasSectionGet) {
|
if (!hasSectionGet) {
|
||||||
if (!hasSectionSet) {
|
if (!hasSectionSet) {
|
||||||
continue layer;
|
continue layerIter;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
} else if (getSection == null) {
|
} else if (getSection == null) {
|
||||||
@ -88,7 +86,7 @@ public class HeightmapProcessor implements IBatchProcessor {
|
|||||||
|| Arrays.equals(getSection, AIR_LAYER)) {
|
|| Arrays.equals(getSection, AIR_LAYER)) {
|
||||||
hasSectionGet = false;
|
hasSectionGet = false;
|
||||||
if (!hasSectionSet) {
|
if (!hasSectionSet) {
|
||||||
continue layer;
|
continue layerIter;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -103,30 +101,26 @@ public class HeightmapProcessor implements IBatchProcessor {
|
|||||||
if (block == null) {
|
if (block == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
byte updateStateAtJ = updated[j];
|
||||||
for (int i = 0; i < TYPES.length; i++) {
|
for (int i = 0; i < TYPES.length; i++) {
|
||||||
if ((skip & (1 << i)) != 0) {
|
int bitFlag = 1 << i;
|
||||||
continue; // skip finished height map
|
if ((updateStateAtJ & bitFlag) != 0) {
|
||||||
|
continue; // skip finished height map at this column
|
||||||
}
|
}
|
||||||
HeightMapType type = TYPES[i];
|
HeightMapType type = TYPES[i];
|
||||||
// ignore if that position was already set
|
// ignore if that position was already set
|
||||||
if (!updated[i][j] && type.includes(block)) {
|
if (type.includes(block)) {
|
||||||
// mc requires + 1, heightmaps are normalized internally, thus we need to "zero" them.
|
// mc requires + 1, heightmaps are normalized internally, thus we need to "zero" them.
|
||||||
heightmaps[i][j] = ((layer - get.getMinSectionPosition()) << 4) + y + 1;
|
heightmaps[i][j] = ((layer - get.getMinSectionPosition()) << 4) + y + 1;
|
||||||
updated[i][j] = true; // mark as updated
|
updated[j] |= (byte) bitFlag; // mark as updated
|
||||||
|
if (++updateCount == NEEDED_UPDATES) {
|
||||||
|
break layerIter; // all heightmaps in all columns updated
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < updated.length; i++) {
|
|
||||||
if ((skip & (1 << i)) == 0 // if already true, skip array equality check
|
|
||||||
&& Arrays.equals(updated[i], COMPLETE)) {
|
|
||||||
skip |= 1 << i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (skip != allSkipped) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break; // all maps are processed
|
|
||||||
}
|
}
|
||||||
for (int i = 0; i < TYPES.length; i++) {
|
for (int i = 0; i < TYPES.length; i++) {
|
||||||
set.setHeightMap(TYPES[i], heightmaps[i]);
|
set.setHeightMap(TYPES[i], heightmaps[i]);
|
||||||
|
@ -37,6 +37,17 @@ public interface IBlocks extends Trimable {
|
|||||||
*/
|
*/
|
||||||
boolean hasSection(int layer);
|
boolean hasSection(int layer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return whether the chunk has a section that has any non-air/reserved blocks}
|
||||||
|
* This method might be conservative and return {@code true} even if the section is empty.
|
||||||
|
*
|
||||||
|
* @param layer the section's layer
|
||||||
|
* @since TODO
|
||||||
|
*/
|
||||||
|
default boolean hasNonEmptySection(int layer) {
|
||||||
|
return hasSection(layer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the specified chunk section stored as an array of ordinals. Uses normal minecraft chunk-section position indices
|
* Obtain the specified chunk section stored as an array of ordinals. Uses normal minecraft chunk-section position indices
|
||||||
* (length 4096). Operations synchronises on the section and will load the section into memory if not present. For chunk
|
* (length 4096). Operations synchronises on the section and will load the section into memory if not present. For chunk
|
||||||
|
@ -882,6 +882,11 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
return chunkExisting != null && chunkExisting.hasSection(layer);
|
return chunkExisting != null && chunkExisting.hasSection(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNonEmptySection(final int layer) {
|
||||||
|
return chunkExisting != null && chunkExisting.hasNonEmptySection(layer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) {
|
public synchronized void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) {
|
||||||
final IChunkGet get = getOrCreateGet();
|
final IChunkGet get = getOrCreateGet();
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren