Fix relight and removelight (#475)

* Start work on lighting engine (does not build)

* Implement getLighting

* Setting, flushing light etc works. Getting light should be working..?

* Better queue/chunk handling

* Use correct location for lighting update

* Correct set location, remove debug

* cleanup a little

* Fix fixlight

* Apply to all versions for the numpties

* Remove lighting extent if not using

* Actually bitmask blocks when setting in chunks

* Initialise Maps and Dequeues with inital size

* format

* Documentation maybe
This commit is contained in:
dordsor21
2020-05-27 11:45:08 +01:00
committed by GitHub
parent 1ff5e7761b
commit bdc14c10c7
27 changed files with 1910 additions and 93 deletions

View File

@ -30,6 +30,7 @@ import net.minecraft.server.v1_14_R1.DataPaletteBlock;
import net.minecraft.server.v1_14_R1.DataPaletteLinear;
import net.minecraft.server.v1_14_R1.GameProfileSerializer;
import net.minecraft.server.v1_14_R1.IBlockData;
import net.minecraft.server.v1_14_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_14_R1.PlayerChunk;
import net.minecraft.server.v1_14_R1.PlayerChunkMap;
import net.minecraft.server.v1_14_R1.World;
@ -167,7 +168,7 @@ public final class BukkitAdapter_1_14 extends NMSAdapter {
}
}
public static void sendChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, int X, int Z, int mask) {
public static void sendChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) {
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
if (playerChunk == null) {
return;
@ -187,6 +188,15 @@ public final class BukkitAdapter_1_14 extends NMSAdapter {
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
if (lighting) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z);
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}

View File

@ -54,10 +54,13 @@ import net.minecraft.server.v1_14_R1.DataPaletteHash;
import net.minecraft.server.v1_14_R1.DataPaletteLinear;
import net.minecraft.server.v1_14_R1.Entity;
import net.minecraft.server.v1_14_R1.EntityTypes;
import net.minecraft.server.v1_14_R1.EnumSkyBlock;
import net.minecraft.server.v1_14_R1.IBlockData;
import net.minecraft.server.v1_14_R1.LightEngineThreaded;
import net.minecraft.server.v1_14_R1.NBTTagCompound;
import net.minecraft.server.v1_14_R1.NBTTagInt;
import net.minecraft.server.v1_14_R1.NibbleArray;
import net.minecraft.server.v1_14_R1.SectionPosition;
import net.minecraft.server.v1_14_R1.TileEntity;
import net.minecraft.server.v1_14_R1.WorldServer;
import org.bukkit.World;
@ -72,6 +75,8 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
public Chunk nmsChunk;
public WorldServer world;
public int X, Z;
public NibbleArray[] blockLight = new NibbleArray[16];
public NibbleArray[] skyLight = new NibbleArray[16];
public BukkitGetBlocks_1_14(World world, int X, int Z) {
this(((CraftWorld) world).getHandle(), X, Z);
@ -119,6 +124,26 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
}
@Override
public int getSkyLight(int x, int y, int z) {
int layer = y >> 4;
if (skyLight[layer] == null) {
skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public int getEmmittedLight(int x, int y, int z) {
int layer = y >> 4;
if (blockLight[layer] == null) {
blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public CompoundTag getEntity(UUID uuid) {
Entity entity = world.getEntity(uuid);
@ -322,6 +347,29 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
}
}
boolean lightUpdate = false;
// Lighting
char[][] light = set.getLight();
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
char[][] skyLight = set.getSkyLight();
if (skyLight != null) {
lightUpdate = true;
try {
fillLightNibble(skyLight, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
Runnable[] syncTasks = null;
int bx = X << 4;
@ -427,24 +475,19 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
};
}
{//Lighting
// TODO optimize, cause this is really slow
LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e();
engine.a(nmsChunk, false);
}
Runnable callback;
if (bitMask == 0 && biomes == null) {
if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
int finalMask = bitMask;
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> {
// Set Modified
nmsChunk.d(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_14.sendChunk(nmsWorld, X, Z, finalMask);
BukkitAdapter_1_14.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate);
if (finalizer != null) finalizer.run();
};
}
@ -617,6 +660,25 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
return tmp;
}
private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
for (int Y = 0; Y < 16; Y++) {
if (light[Y] == null) {
continue;
}
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y));
if (nibble == null) {
continue;
}
synchronized (nibble) {
for (int i = 0; i < 4096; i++) {
if (light[Y][i] < 16) {
nibble.a(i, light[Y][i]);
}
}
}
}
}
@Override
public boolean hasSection(int layer) {
return getSections()[layer] != null;

View File

@ -24,6 +24,7 @@ import net.minecraft.server.v1_15_R1.DataPaletteBlock;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.GameProfileSerializer;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_15_R1.PlayerChunk;
import net.minecraft.server.v1_15_R1.PlayerChunkMap;
import net.minecraft.server.v1_15_R1.World;
@ -85,6 +86,8 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
fieldDirtyBits = PlayerChunk.class.getDeclaredField("r");
fieldDirtyBits.setAccessible(true);
fieldTickingBlockCount.setAccessible(true);
Method declaredGetVisibleChunk = PlayerChunkMap.class.getDeclaredMethod("getVisibleChunk", long.class);
declaredGetVisibleChunk.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
@ -165,7 +168,7 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
}
}
public static void sendChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, int X, int Z, int mask) {
public static void sendChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) {
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
if (playerChunk == null) {
return;
@ -185,6 +188,15 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
if (lighting) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z);
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}

View File

@ -55,10 +55,13 @@ import net.minecraft.server.v1_15_R1.DataPaletteHash;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.Entity;
import net.minecraft.server.v1_15_R1.EntityTypes;
import net.minecraft.server.v1_15_R1.EnumSkyBlock;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.LightEngineThreaded;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.NBTTagInt;
import net.minecraft.server.v1_15_R1.NibbleArray;
import net.minecraft.server.v1_15_R1.SectionPosition;
import net.minecraft.server.v1_15_R1.TileEntity;
import net.minecraft.server.v1_15_R1.WorldServer;
import org.bukkit.World;
@ -75,6 +78,8 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
public Chunk nmsChunk;
public WorldServer world;
public int X, Z;
public NibbleArray[] blockLight = new NibbleArray[16];
public NibbleArray[] skyLight = new NibbleArray[16];
public BukkitGetBlocks_1_15(World world, int X, int Z) {
this(((CraftWorld) world).getHandle(), X, Z);
@ -127,6 +132,26 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
}
@Override
public int getSkyLight(int x, int y, int z) {
int layer = y >> 4;
if (skyLight[layer] == null) {
skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public int getEmmittedLight(int x, int y, int z) {
int layer = y >> 4;
if (blockLight[layer] == null) {
blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public CompoundTag getEntity(UUID uuid) {
Entity entity = world.getEntity(uuid);
@ -335,6 +360,29 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
}
}
boolean lightUpdate = false;
// Lighting
char[][] light = set.getLight();
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
char[][] skyLight = set.getSkyLight();
if (skyLight != null) {
lightUpdate = true;
try {
fillLightNibble(skyLight, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
Runnable[] syncTasks = null;
int bx = X << 4;
@ -440,25 +488,19 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
};
}
//Lighting
// TODO optimize, cause this is really slow
LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e();
//lightChunk()
engine.a(nmsChunk, false);
Runnable callback;
if (bitMask == 0 && biomes == null) {
if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
int finalMask = bitMask;
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> {
// Set Modified
//setLastSaveHadEntities
nmsChunk.d(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_15.sendChunk(nmsWorld, X, Z, finalMask);
BukkitAdapter_1_15.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate);
if (finalizer != null) finalizer.run();
};
}
@ -637,6 +679,25 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
return tmp;
}
private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
for (int Y = 0; Y < 16; Y++) {
if (light[Y] == null) {
continue;
}
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y));
if (nibble == null) {
continue;
}
synchronized (nibble) {
for (int i = 0; i < 4096; i++) {
if (light[Y][i] < 16) {
nibble.a(i, light[Y][i]);
}
}
}
}
}
@Override
public boolean hasSection(int layer) {
return getSections()[layer] != null;

View File

@ -13,12 +13,6 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import net.jpountz.util.UnsafeUtils;
import net.minecraft.server.v1_15_R1.Block;
import net.minecraft.server.v1_15_R1.Chunk;
@ -30,6 +24,11 @@ import net.minecraft.server.v1_15_R1.DataPaletteBlock;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.GameProfileSerializer;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.LightEngine;
import net.minecraft.server.v1_15_R1.LightEngineLayer;
import net.minecraft.server.v1_15_R1.LightEngineStorage;
import net.minecraft.server.v1_15_R1.NibbleArray;
import net.minecraft.server.v1_15_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_15_R1.PlayerChunk;
import net.minecraft.server.v1_15_R1.PlayerChunkMap;
import net.minecraft.server.v1_15_R1.World;
@ -38,9 +37,15 @@ import org.bukkit.craftbukkit.v1_15_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
import sun.misc.Unsafe;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
public final class BukkitAdapter_1_15_2 extends NMSAdapter {
@ -60,6 +65,8 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
private final static MethodHandle methodGetVisibleChunk;
public final static MethodHandle methodSetLightNibbleArray;
private static final int CHUNKSECTION_BASE;
private static final int CHUNKSECTION_SHIFT;
@ -90,6 +97,10 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
declaredGetVisibleChunk.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
Method declaredSetLightNibbleArray = LightEngineStorage.class.getDeclaredMethod("a", long.class, NibbleArray.class);
declaredSetLightNibbleArray.setAccessible(true);
methodSetLightNibbleArray = MethodHandles.lookup().unreflect(declaredSetLightNibbleArray);
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
ReflectionUtils.setAccessibleNonFinal(tmp);
fieldLock = tmp;
@ -167,7 +178,7 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
}
}
public static void sendChunk(WorldServer nmsWorld, int X, int Z, int mask) {
public static void sendChunk(WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) {
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
if (playerChunk == null) {
return;
@ -187,6 +198,15 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
if (lighting) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z);
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}

View File

@ -38,10 +38,12 @@ import net.minecraft.server.v1_15_R1.DataPaletteHash;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.Entity;
import net.minecraft.server.v1_15_R1.EntityTypes;
import net.minecraft.server.v1_15_R1.EnumSkyBlock;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.LightEngineThreaded;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.NBTTagInt;
import net.minecraft.server.v1_15_R1.NibbleArray;
import net.minecraft.server.v1_15_R1.SectionPosition;
import net.minecraft.server.v1_15_R1.TileEntity;
import net.minecraft.server.v1_15_R1.WorldServer;
import org.bukkit.World;
@ -80,6 +82,8 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
public Chunk nmsChunk;
public WorldServer world;
public int X, Z;
public NibbleArray[] blockLight = new NibbleArray[16];
public NibbleArray[] skyLight = new NibbleArray[16];
public BukkitGetBlocks_1_15_2(World world, int X, int Z) {
this(((CraftWorld) world).getHandle(), X, Z);
@ -132,6 +136,26 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
}
@Override
public int getSkyLight(int x, int y, int z) {
int layer = y >> 4;
if (skyLight[layer] == null) {
skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public int getEmmittedLight(int x, int y, int z) {
int layer = y >> 4;
if (blockLight[layer] == null) {
blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public CompoundTag getEntity(UUID uuid) {
Entity entity = world.getEntity(uuid);
@ -347,6 +371,29 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
}
}
boolean lightUpdate = false;
// Lighting
char[][] light = set.getLight();
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
char[][] skyLight = set.getSkyLight();
if (skyLight != null) {
lightUpdate = true;
try {
fillLightNibble(skyLight, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
Runnable[] syncTasks = null;
int bx = X << 4;
@ -452,23 +499,19 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
};
}
//Lighting
// TODO optimize, cause this is really slow
LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e();
engine.a(nmsChunk, false);
Runnable callback;
if (bitMask == 0 && biomes == null) {
if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
int finalMask = bitMask;
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> {
// Set Modified
nmsChunk.d(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_15_2.sendChunk(nmsWorld, X, Z, finalMask);
BukkitAdapter_1_15_2.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate);
if (finalizer != null) finalizer.run();
};
}
@ -641,6 +684,25 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
return tmp;
}
private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
for (int Y = 0; Y < 16; Y++) {
if (light[Y] == null) {
continue;
}
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y));
if (nibble == null) {
continue;
}
synchronized (nibble) {
for (int i = 0; i < 4096; i++) {
if (light[Y][i] < 16) {
nibble.a(i, light[Y][i]);
}
}
}
}
}
@Override
public boolean hasSection(int layer) {
return getSections()[layer] != null;
@ -648,6 +710,8 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
@Override
public boolean trim(boolean aggressive) {
skyLight = new NibbleArray[16];
blockLight = new NibbleArray[16];
if (aggressive) {
sections = null;
nmsChunk = null;