Make ChunkSection an interface, don't allocate light arrays if not needed

This commit is contained in:
KennyTV 2021-05-20 18:32:28 +02:00
parent 04fedd2892
commit cf2adab728
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
15 changed files with 479 additions and 267 deletions

View File

@ -22,46 +22,50 @@
*/ */
package com.viaversion.viaversion.api.minecraft.chunks; package com.viaversion.viaversion.api.minecraft.chunks;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class ChunkSection { public interface ChunkSection {
/** /**
* Size (dimensions) of blocks in a chunks section. * Size (dimensions) of blocks in a chunks section.
*/ */
public static final int SIZE = 16 * 16 * 16; // width * depth * height int SIZE = 16 * 16 * 16; // width * depth * height
/**
* Length of the sky and block light nibble arrays.
*/
public static final int LIGHT_LENGTH = 16 * 16 * 16 / 2; // size * size * size / 2 (nibble bit count)
private final IntList palette;
private final Int2IntMap inversePalette;
private final int[] blocks;
private NibbleArray blockLight;
private NibbleArray skyLight;
private int nonAirBlocksCount;
public ChunkSection() { /**
this.blocks = new int[SIZE]; * Returns the block index of the given coordinates within a section.
this.blockLight = new NibbleArray(SIZE); *
palette = new IntArrayList(); * @param x section x
inversePalette = new Int2IntOpenHashMap(); * @param y section y
inversePalette.defaultReturnValue(-1); * @param z section z
* @return section block index
*/
static int index(int x, int y, int z) {
return y << 8 | z << 4 | x;
} }
public ChunkSection(int expectedPaletteLength) { int getFlatBlock(int idx);
this.blocks = new int[SIZE];
this.blockLight = new NibbleArray(SIZE);
// Pre-size the palette array/map int getFlatBlock(int x, int y, int z);
palette = new IntArrayList(expectedPaletteLength);
inversePalette = new Int2IntOpenHashMap(expectedPaletteLength); /**
inversePalette.defaultReturnValue(-1); * Set a block state in the chunk
* This method will not update non-air blocks count
*
* @param idx Index
* @param id The raw or flat id of the block
*/
void setFlatBlock(int idx, int id);
default void setFlatBlock(int x, int y, int z, int type) {
setFlatBlock(index(x, y, z), type);
}
default int getBlockWithoutData(int x, int y, int z) {
return getFlatBlock(x, y, z) >> 4;
}
default int getBlockData(int x, int y, int z) {
return getFlatBlock(x, y, z) & 0xF;
} }
/** /**
@ -74,206 +78,39 @@ public class ChunkSection {
* @param type The type of the block * @param type The type of the block
* @param data The data value of the block * @param data The data value of the block
*/ */
public void setBlock(int x, int y, int z, int type, int data) { default void setBlockWithData(int x, int y, int z, int type, int data) {
setFlatBlock(index(x, y, z), type << 4 | (data & 0xF)); setFlatBlock(index(x, y, z), type << 4 | (data & 0xF));
} }
public void setFlatBlock(int x, int y, int z, int type) { default void setBlockWithData(int idx, int type, int data) {
setFlatBlock(index(x, y, z), type);
}
public int getBlockId(int x, int y, int z) {
return getFlatBlock(x, y, z) >> 4;
}
public int getBlockData(int x, int y, int z) {
return getFlatBlock(x, y, z) & 0xF;
}
public int getFlatBlock(int x, int y, int z) {
int index = blocks[index(x, y, z)];
return palette.getInt(index);
}
public int getFlatBlock(int idx) {
int index = blocks[idx];
return palette.getInt(index);
}
public void setBlock(int idx, int type, int data) {
setFlatBlock(idx, type << 4 | (data & 0xF)); setFlatBlock(idx, type << 4 | (data & 0xF));
} }
public void setPaletteIndex(int idx, int index) { void setPaletteIndex(int idx, int index);
blocks[idx] = index;
int getPaletteIndex(int idx);
int getPaletteSize();
int getPaletteEntry(int index);
void setPaletteEntry(int index, int id);
void replacePaletteEntry(int oldId, int newId);
void addPaletteEntry(int id);
void clearPalette();
int getNonAirBlocksCount();
void setNonAirBlocksCount(int nonAirBlocksCount);
default boolean hasLight() {
return getLight() != null;
} }
public int getPaletteIndex(int idx) { @Nullable ChunkSectionLight getLight();
return blocks[idx];
}
public int getPaletteSize() { void setLight(@Nullable ChunkSectionLight light);
return palette.size();
}
public int getPaletteEntry(int index) {
return palette.getInt(index);
}
public void setPaletteEntry(int index, int id) {
int oldId = palette.set(index, id);
if (oldId == id) return;
inversePalette.put(id, index);
if (inversePalette.get(oldId) == index) {
inversePalette.remove(oldId);
for (int i = 0; i < palette.size(); i++) {
if (palette.getInt(i) == oldId) {
inversePalette.put(oldId, i);
break;
}
}
}
}
public void replacePaletteEntry(int oldId, int newId) {
int index = inversePalette.remove(oldId);
if (index == -1) return;
inversePalette.put(newId, index);
for (int i = 0; i < palette.size(); i++) {
if (palette.getInt(i) == oldId) {
palette.set(i, newId);
}
}
}
public void addPaletteEntry(int id) {
inversePalette.put(id, palette.size());
palette.add(id);
}
public void clearPalette() {
palette.clear();
inversePalette.clear();
}
/**
* Set a block state in the chunk
* This method will not update non-air blocks count
*
* @param idx Index
* @param id The raw or flat id of the block
*/
public void setFlatBlock(int idx, int id) {
int index = inversePalette.get(id);
if (index == -1) {
index = palette.size();
palette.add(id);
inversePalette.put(id, index);
}
blocks[idx] = index;
}
/**
* Set the block light array
*
* @param data The value to set the block light to
*/
public void setBlockLight(byte @Nullable [] data) {
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
if (this.blockLight == null) {
this.blockLight = new NibbleArray(data);
} else {
this.blockLight.setHandle(data);
}
}
/**
* Set the sky light array
*
* @param data The value to set the sky light to
*/
public void setSkyLight(byte @Nullable [] data) {
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
if (this.skyLight == null) {
this.skyLight = new NibbleArray(data);
} else {
this.skyLight.setHandle(data);
}
}
public byte @Nullable [] getBlockLight() {
return blockLight == null ? null : blockLight.getHandle();
}
public @Nullable NibbleArray getBlockLightNibbleArray() {
return blockLight;
}
public byte @Nullable [] getSkyLight() {
return skyLight == null ? null : skyLight.getHandle();
}
public @Nullable NibbleArray getSkyLightNibbleArray() {
return skyLight;
}
public void readBlockLight(ByteBuf input) {
if (this.blockLight == null) {
this.blockLight = new NibbleArray(LIGHT_LENGTH * 2);
}
input.readBytes(this.blockLight.getHandle());
}
public void readSkyLight(ByteBuf input) {
if (this.skyLight == null) {
this.skyLight = new NibbleArray(LIGHT_LENGTH * 2);
}
input.readBytes(this.skyLight.getHandle());
}
public static int index(int x, int y, int z) {
return y << 8 | z << 4 | x;
}
/**
* Write the block light to a buffer
*
* @param output The buffer to write to
*/
public void writeBlockLight(ByteBuf output) {
output.writeBytes(blockLight.getHandle());
}
/**
* Write the sky light to a buffer
*
* @param output The buffer to write to
*/
public void writeSkyLight(ByteBuf output) {
output.writeBytes(skyLight.getHandle());
}
/**
* Check if sky light is present
*
* @return True if skylight is present
*/
public boolean hasSkyLight() {
return skyLight != null;
}
public boolean hasBlockLight() {
return blockLight != null;
}
public int getNonAirBlocksCount() {
return nonAirBlocksCount;
}
public void setNonAirBlocksCount(int nonAirBlocksCount) {
this.nonAirBlocksCount = nonAirBlocksCount;
}
} }

View File

@ -0,0 +1,166 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.chunks;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ChunkSectionImpl implements ChunkSection {
private final IntList palette;
private final Int2IntMap inversePalette;
private final int[] blocks;
private ChunkSectionLight light;
private int nonAirBlocksCount;
public ChunkSectionImpl(boolean holdsLight) {
this.blocks = new int[SIZE];
palette = new IntArrayList();
inversePalette = new Int2IntOpenHashMap();
inversePalette.defaultReturnValue(-1);
if (holdsLight) {
this.light = new ChunkSectionLightImpl();
}
}
public ChunkSectionImpl(boolean holdsLight, int expectedPaletteLength) {
this.blocks = new int[SIZE];
if (holdsLight) {
this.light = new ChunkSectionLightImpl();
}
// Pre-size the palette array/map
palette = new IntArrayList(expectedPaletteLength);
inversePalette = new Int2IntOpenHashMap(expectedPaletteLength);
inversePalette.defaultReturnValue(-1);
}
@Override
public int getFlatBlock(int idx) {
int index = blocks[idx];
return palette.getInt(index);
}
@Override
public int getFlatBlock(int x, int y, int z) {
int index = blocks[ChunkSection.index(x, y, z)];
return palette.getInt(index);
}
@Override
public void setFlatBlock(int idx, int id) {
int index = inversePalette.get(id);
if (index == -1) {
index = palette.size();
palette.add(id);
inversePalette.put(id, index);
}
blocks[idx] = index;
}
@Override
public int getPaletteIndex(int idx) {
return blocks[idx];
}
@Override
public void setPaletteIndex(int idx, int index) {
blocks[idx] = index;
}
@Override
public int getPaletteSize() {
return palette.size();
}
@Override
public int getPaletteEntry(int index) {
return palette.getInt(index);
}
@Override
public void setPaletteEntry(int index, int id) {
int oldId = palette.set(index, id);
if (oldId == id) return;
inversePalette.put(id, index);
if (inversePalette.get(oldId) == index) {
inversePalette.remove(oldId);
for (int i = 0; i < palette.size(); i++) {
if (palette.getInt(i) == oldId) {
inversePalette.put(oldId, i);
break;
}
}
}
}
@Override
public void replacePaletteEntry(int oldId, int newId) {
int index = inversePalette.remove(oldId);
if (index == -1) return;
inversePalette.put(newId, index);
for (int i = 0; i < palette.size(); i++) {
if (palette.getInt(i) == oldId) {
palette.set(i, newId);
}
}
}
@Override
public void addPaletteEntry(int id) {
inversePalette.put(id, palette.size());
palette.add(id);
}
@Override
public void clearPalette() {
palette.clear();
inversePalette.clear();
}
@Override
public int getNonAirBlocksCount() {
return nonAirBlocksCount;
}
@Override
public void setNonAirBlocksCount(int nonAirBlocksCount) {
this.nonAirBlocksCount = nonAirBlocksCount;
}
@Override
public @Nullable ChunkSectionLight getLight() {
return light;
}
@Override
public void setLight(@Nullable ChunkSectionLight light) {
this.light = light;
}
}

View File

@ -0,0 +1,83 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.chunks;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface ChunkSectionLight {
/**
* Length of the sky and block light nibble arrays.
*/
int LIGHT_LENGTH = 16 * 16 * 16 / 2; // Dimensions / 2 (nibble bit count)
/**
* Check if sky light is present
*
* @return True if skylight is present
*/
boolean hasSkyLight();
boolean hasBlockLight();
byte @Nullable [] getSkyLight();
byte @Nullable [] getBlockLight();
/**
* Set the sky light array
*
* @param data The value to set the sky light to
*/
void setSkyLight(byte[] data);
/**
* Set the block light array
*
* @param data The value to set the block light to
*/
void setBlockLight(byte[] data);
@Nullable NibbleArray getBlockLightNibbleArray();
@Nullable NibbleArray getSkyLightNibbleArray();
void readSkyLight(ByteBuf input);
void readBlockLight(ByteBuf input);
/**
* Write the sky light to a buffer.
*
* @param output The buffer to write to
*/
void writeSkyLight(ByteBuf output);
/**
* Write the block light to a buffer.
*
* @param output The buffer to write to
*/
void writeBlockLight(ByteBuf output);
}

View File

@ -0,0 +1,113 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.chunks;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ChunkSectionLightImpl implements ChunkSectionLight {
private NibbleArray blockLight;
private NibbleArray skyLight;
public ChunkSectionLightImpl() {
// Block light is always written
this.blockLight = new NibbleArray(ChunkSection.SIZE);
}
@Override
public void setBlockLight(byte[] data) {
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
if (this.blockLight == null) {
this.blockLight = new NibbleArray(data);
} else {
this.blockLight.setHandle(data);
}
}
@Override
public void setSkyLight(byte[] data) {
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
if (this.skyLight == null) {
this.skyLight = new NibbleArray(data);
} else {
this.skyLight.setHandle(data);
}
}
@Override
public byte @Nullable [] getBlockLight() {
return blockLight == null ? null : blockLight.getHandle();
}
@Override
public @Nullable NibbleArray getBlockLightNibbleArray() {
return blockLight;
}
@Override
public byte @Nullable [] getSkyLight() {
return skyLight == null ? null : skyLight.getHandle();
}
@Override
public @Nullable NibbleArray getSkyLightNibbleArray() {
return skyLight;
}
@Override
public void readBlockLight(ByteBuf input) {
if (this.blockLight == null) {
this.blockLight = new NibbleArray(LIGHT_LENGTH * 2);
}
input.readBytes(this.blockLight.getHandle());
}
@Override
public void readSkyLight(ByteBuf input) {
if (this.skyLight == null) {
this.skyLight = new NibbleArray(LIGHT_LENGTH * 2);
}
input.readBytes(this.skyLight.getHandle());
}
@Override
public void writeBlockLight(ByteBuf output) {
output.writeBytes(blockLight.getHandle());
}
@Override
public void writeSkyLight(ByteBuf output) {
output.writeBytes(skyLight.getHandle());
}
@Override
public boolean hasSkyLight() {
return skyLight != null;
}
@Override
public boolean hasBlockLight() {
return blockLight != null;
}
}

View File

@ -23,6 +23,7 @@
package com.viaversion.viaversion.api.type.types.version; package com.viaversion.viaversion.api.type.types.version;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection; import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionImpl;
import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.CompactArrayUtil; import com.viaversion.viaversion.util.CompactArrayUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -48,12 +49,12 @@ public class ChunkSectionType1_13 extends Type<ChunkSection> {
ChunkSection chunkSection; ChunkSection chunkSection;
if (bitsPerBlock != GLOBAL_PALETTE) { if (bitsPerBlock != GLOBAL_PALETTE) {
int paletteLength = Type.VAR_INT.readPrimitive(buffer); int paletteLength = Type.VAR_INT.readPrimitive(buffer);
chunkSection = new ChunkSection(paletteLength); chunkSection = new ChunkSectionImpl(true, paletteLength);
for (int i = 0; i < paletteLength; i++) { for (int i = 0; i < paletteLength; i++) {
chunkSection.addPaletteEntry(Type.VAR_INT.readPrimitive(buffer)); chunkSection.addPaletteEntry(Type.VAR_INT.readPrimitive(buffer));
} }
} else { } else {
chunkSection = new ChunkSection(); chunkSection = new ChunkSectionImpl(true);
} }
// Read blocks // Read blocks

View File

@ -23,6 +23,7 @@
package com.viaversion.viaversion.api.type.types.version; package com.viaversion.viaversion.api.type.types.version;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection; import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionImpl;
import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.CompactArrayUtil; import com.viaversion.viaversion.util.CompactArrayUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -48,12 +49,12 @@ public class ChunkSectionType1_16 extends Type<ChunkSection> {
ChunkSection chunkSection; ChunkSection chunkSection;
if (bitsPerBlock != GLOBAL_PALETTE) { if (bitsPerBlock != GLOBAL_PALETTE) {
int paletteLength = Type.VAR_INT.readPrimitive(buffer); int paletteLength = Type.VAR_INT.readPrimitive(buffer);
chunkSection = new ChunkSection(paletteLength); chunkSection = new ChunkSectionImpl(false, paletteLength);
for (int i = 0; i < paletteLength; i++) { for (int i = 0; i < paletteLength; i++) {
chunkSection.addPaletteEntry(Type.VAR_INT.readPrimitive(buffer)); chunkSection.addPaletteEntry(Type.VAR_INT.readPrimitive(buffer));
} }
} else { } else {
chunkSection = new ChunkSection(); chunkSection = new ChunkSectionImpl(false);
} }
// Read blocks // Read blocks

View File

@ -23,6 +23,7 @@
package com.viaversion.viaversion.api.type.types.version; package com.viaversion.viaversion.api.type.types.version;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection; import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionImpl;
import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -36,7 +37,7 @@ public class ChunkSectionType1_8 extends Type<ChunkSection> {
@Override @Override
public ChunkSection read(ByteBuf buffer) throws Exception { public ChunkSection read(ByteBuf buffer) throws Exception {
ChunkSection chunkSection = new ChunkSection(); ChunkSection chunkSection = new ChunkSectionImpl(true);
// 0 index needs to be air in 1.9 // 0 index needs to be air in 1.9
chunkSection.addPaletteEntry(0); chunkSection.addPaletteEntry(0);
@ -46,7 +47,7 @@ public class ChunkSectionType1_8 extends Type<ChunkSection> {
int mask = littleEndianView.readShort(); int mask = littleEndianView.readShort();
int type = mask >> 4; int type = mask >> 4;
int data = mask & 0xF; int data = mask & 0xF;
chunkSection.setBlock(i, type, data); chunkSection.setBlockWithData(i, type, data);
} }
return chunkSection; return chunkSection;

View File

@ -23,6 +23,7 @@
package com.viaversion.viaversion.api.type.types.version; package com.viaversion.viaversion.api.type.types.version;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection; import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionImpl;
import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.CompactArrayUtil; import com.viaversion.viaversion.util.CompactArrayUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -52,7 +53,7 @@ public class ChunkSectionType1_9 extends Type<ChunkSection> {
// Read palette // Read palette
int paletteLength = Type.VAR_INT.readPrimitive(buffer); int paletteLength = Type.VAR_INT.readPrimitive(buffer);
ChunkSection chunkSection = bitsPerBlock != GLOBAL_PALETTE ? new ChunkSection(paletteLength) : new ChunkSection(); ChunkSection chunkSection = bitsPerBlock != GLOBAL_PALETTE ? new ChunkSectionImpl(true, paletteLength) : new ChunkSectionImpl(true);
for (int i = 0; i < paletteLength; i++) { for (int i = 0; i < paletteLength; i++) {
if (bitsPerBlock != GLOBAL_PALETTE) { if (bitsPerBlock != GLOBAL_PALETTE) {
chunkSection.addPaletteEntry(Type.VAR_INT.readPrimitive(buffer)); chunkSection.addPaletteEntry(Type.VAR_INT.readPrimitive(buffer));

View File

@ -129,7 +129,7 @@ public class Protocol1_12To1_11_1 extends AbstractProtocol<ClientboundPackets1_9
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) { for (int x = 0; x < 16; x++) {
int block = section.getBlockId(x, y, z); int block = section.getBlockWithoutData(x, y, z);
// Is this a bed? // Is this a bed?
if (block == 26) { if (block == 26) {
// NBT -> { color:14, x:132, y:64, z:222, id:"minecraft:bed" } (Debug output) // NBT -> { color:14, x:132, y:64, z:222, id:"minecraft:bed" } (Debug output)

View File

@ -57,9 +57,9 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
ChunkSection section = Types1_13.CHUNK_SECTION.read(data); ChunkSection section = Types1_13.CHUNK_SECTION.read(data);
sections[i] = section; sections[i] = section;
section.readBlockLight(data); section.getLight().readBlockLight(data);
if (world.getEnvironment() == Environment.NORMAL) { if (world.getEnvironment() == Environment.NORMAL) {
section.readSkyLight(data); section.getLight().readSkyLight(data);
} }
} }
@ -101,10 +101,10 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
ChunkSection section = chunk.getSections()[i]; ChunkSection section = chunk.getSections()[i];
if (section == null) continue; // Section not set if (section == null) continue; // Section not set
Types1_13.CHUNK_SECTION.write(buf, section); Types1_13.CHUNK_SECTION.write(buf, section);
section.writeBlockLight(buf); section.getLight().writeBlockLight(buf);
if (!section.hasSkyLight()) continue; // No sky light, we're done here. if (!section.getLight().hasSkyLight()) continue; // No sky light, we're done here.
section.writeSkyLight(buf); section.getLight().writeSkyLight(buf);
} }
buf.readerIndex(0); buf.readerIndex(0);

View File

@ -32,6 +32,7 @@ import com.viaversion.viaversion.api.protocol.remapper.ValueCreator;
import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.ClientboundPackets1_13; import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.ClientboundPackets1_13;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.types.Chunk1_13Type; import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.types.Chunk1_13Type;
import com.viaversion.viaversion.protocols.protocol1_14to1_13_2.ClientboundPackets1_14;
import com.viaversion.viaversion.protocols.protocol1_14to1_13_2.Protocol1_14To1_13_2; import com.viaversion.viaversion.protocols.protocol1_14to1_13_2.Protocol1_14To1_13_2;
import com.viaversion.viaversion.protocols.protocol1_14to1_13_2.storage.EntityTracker1_14; import com.viaversion.viaversion.protocols.protocol1_14to1_13_2.storage.EntityTracker1_14;
import com.viaversion.viaversion.protocols.protocol1_14to1_13_2.types.Chunk1_14Type; import com.viaversion.viaversion.protocols.protocol1_14to1_13_2.types.Chunk1_14Type;
@ -198,7 +199,7 @@ public class WorldPackets {
heightMap.put("WORLD_SURFACE", new LongArrayTag(encodeHeightMap(worldSurface))); heightMap.put("WORLD_SURFACE", new LongArrayTag(encodeHeightMap(worldSurface)));
chunk.setHeightMap(heightMap); chunk.setHeightMap(heightMap);
PacketWrapper lightPacket = wrapper.create(0x24); PacketWrapper lightPacket = wrapper.create(ClientboundPackets1_14.UPDATE_LIGHT);
lightPacket.write(Type.VAR_INT, chunk.getX()); lightPacket.write(Type.VAR_INT, chunk.getX());
lightPacket.write(Type.VAR_INT, chunk.getZ()); lightPacket.write(Type.VAR_INT, chunk.getZ());
@ -207,7 +208,7 @@ public class WorldPackets {
for (int i = 0; i < chunk.getSections().length; i++) { for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection sec = chunk.getSections()[i]; ChunkSection sec = chunk.getSections()[i];
if (sec == null) continue; if (sec == null) continue;
if (!chunk.isFullChunk() && sec.hasSkyLight()) { if (!chunk.isFullChunk() && sec.getLight().hasSkyLight()) {
skyLightMask |= (1 << (i + 1)); skyLightMask |= (1 << (i + 1));
} }
blockLightMask |= (1 << (i + 1)); blockLightMask |= (1 << (i + 1));
@ -223,20 +224,20 @@ public class WorldPackets {
if (chunk.isFullChunk()) if (chunk.isFullChunk())
lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, FULL_LIGHT); // chunk below 0 lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, FULL_LIGHT); // chunk below 0
for (ChunkSection section : chunk.getSections()) { for (ChunkSection section : chunk.getSections()) {
if (section == null || !section.hasSkyLight()) { if (section == null || !section.getLight().hasSkyLight()) {
if (chunk.isFullChunk()) { if (chunk.isFullChunk()) {
lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, FULL_LIGHT); lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, FULL_LIGHT);
} }
continue; continue;
} }
lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, section.getSkyLight()); lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, section.getLight().getSkyLight());
} }
if (chunk.isFullChunk()) if (chunk.isFullChunk())
lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, FULL_LIGHT); // chunk above 255 lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, FULL_LIGHT); // chunk above 255
for (ChunkSection section : chunk.getSections()) { for (ChunkSection section : chunk.getSections()) {
if (section == null) continue; if (section == null) continue;
lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, section.getBlockLight()); lightPacket.write(Type.BYTE_ARRAY_PRIMITIVE, section.getLight().getBlockLight());
} }
EntityTracker1_14 entityTracker = wrapper.user().get(EntityTracker1_14.class); EntityTracker1_14 entityTracker = wrapper.user().get(EntityTracker1_14.class);
@ -254,6 +255,13 @@ public class WorldPackets {
} }
lightPacket.send(Protocol1_14To1_13_2.class, true, true); lightPacket.send(Protocol1_14To1_13_2.class, true, true);
// Remove light references from chunk sections
for (ChunkSection section : chunk.getSections()) {
if (section != null) {
section.setLight(null);
}
}
} }
private Byte[] fromPrimitiveArray(byte[] bytes) { private Byte[] fromPrimitiveArray(byte[] bytes) {
@ -390,8 +398,8 @@ public class WorldPackets {
int skyLight = 0; int skyLight = 0;
int blockLight = 0; int blockLight = 0;
for (BlockFace blockFace : BlockFace.values()) { for (BlockFace blockFace : BlockFace.values()) {
NibbleArray skyLightArray = section.getSkyLightNibbleArray(); NibbleArray skyLightArray = section.getLight().getSkyLightNibbleArray();
NibbleArray blockLightArray = section.getBlockLightNibbleArray(); NibbleArray blockLightArray = section.getLight().getBlockLightNibbleArray();
int neighbourX = x + blockFace.getModX(); int neighbourX = x + blockFace.getModX();
int neighbourY = y + blockFace.getModY(); int neighbourY = y + blockFace.getModY();
int neighbourZ = z + blockFace.getModZ(); int neighbourZ = z + blockFace.getModZ();
@ -414,8 +422,8 @@ public class WorldPackets {
ChunkSection newSection = chunk.getSections()[ySection]; ChunkSection newSection = chunk.getSections()[ySection];
if (newSection == null) continue; if (newSection == null) continue;
skyLightArray = newSection.getSkyLightNibbleArray(); skyLightArray = newSection.getLight().getSkyLightNibbleArray();
blockLightArray = newSection.getBlockLightNibbleArray(); blockLightArray = newSection.getLight().getBlockLightNibbleArray();
} }
} else if (blockFace.getModZ() != 0) { } else if (blockFace.getModZ() != 0) {
// Another chunk, nothing we can do without an unnecessary amount of caching // Another chunk, nothing we can do without an unnecessary amount of caching
@ -447,15 +455,15 @@ public class WorldPackets {
} }
if (skyLight != 0) { if (skyLight != 0) {
if (!section.hasSkyLight()) { if (!section.getLight().hasSkyLight()) {
byte[] newSkyLight = new byte[2028]; byte[] newSkyLight = new byte[2028];
section.setSkyLight(newSkyLight); section.getLight().setSkyLight(newSkyLight);
} }
section.getSkyLightNibbleArray().set(x, y, z, skyLight); section.getLight().getSkyLightNibbleArray().set(x, y, z, skyLight);
} }
if (blockLight != 0) { if (blockLight != 0) {
section.getBlockLightNibbleArray().set(x, y, z, blockLight); section.getLight().getBlockLightNibbleArray().set(x, y, z, blockLight);
} }
} }

View File

@ -56,9 +56,9 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
ChunkSection section = Types1_9.CHUNK_SECTION.read(input); ChunkSection section = Types1_9.CHUNK_SECTION.read(input);
sections[i] = section; sections[i] = section;
section.readBlockLight(input); section.getLight().readBlockLight(input);
if (world.getEnvironment() == Environment.NORMAL) { if (world.getEnvironment() == Environment.NORMAL) {
section.readSkyLight(input); section.getLight().readSkyLight(input);
} }
} }
@ -96,10 +96,10 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
ChunkSection section = chunk.getSections()[i]; ChunkSection section = chunk.getSections()[i];
if (section == null) continue; // Section not set if (section == null) continue; // Section not set
Types1_9.CHUNK_SECTION.write(buf, section); Types1_9.CHUNK_SECTION.write(buf, section);
section.writeBlockLight(buf); section.getLight().writeBlockLight(buf);
if (!section.hasSkyLight()) continue; // No sky light, we're done here. if (!section.getLight().hasSkyLight()) continue; // No sky light, we're done here.
section.writeSkyLight(buf); section.getLight().writeSkyLight(buf);
} }
buf.readerIndex(0); buf.readerIndex(0);
Type.VAR_INT.writePrimitive(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 : 0)); Type.VAR_INT.writePrimitive(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 : 0));

View File

@ -111,7 +111,7 @@ public class Protocol1_9_3To1_9_1_2 extends AbstractProtocol<ClientboundPackets1
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) { for (int x = 0; x < 16; x++) {
int block = section.getBlockId(x, y, z); int block = section.getBlockWithoutData(x, y, z);
if (FakeTileEntity.hasBlock(block)) { if (FakeTileEntity.hasBlock(block)) {
tags.add(FakeTileEntity.getFromBlock(x + (chunk.getX() << 4), y + (i << 4), z + (chunk.getZ() << 4), block)); tags.add(FakeTileEntity.getFromBlock(x + (chunk.getX() << 4), y + (i << 4), z + (chunk.getZ() << 4), block));
} }

View File

@ -67,9 +67,9 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
if (!usedSections.get(i)) continue; // Section not set if (!usedSections.get(i)) continue; // Section not set
ChunkSection section = Types1_9.CHUNK_SECTION.read(input); ChunkSection section = Types1_9.CHUNK_SECTION.read(input);
sections[i] = section; sections[i] = section;
section.readBlockLight(input); section.getLight().readBlockLight(input);
if (world.getEnvironment() == Environment.NORMAL) { if (world.getEnvironment() == Environment.NORMAL) {
section.readSkyLight(input); section.getLight().readSkyLight(input);
} }
if (replacePistons) { if (replacePistons) {
section.replacePaletteEntry(36, replacementId); section.replacePaletteEntry(36, replacementId);
@ -100,10 +100,10 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
ChunkSection section = chunk.getSections()[i]; ChunkSection section = chunk.getSections()[i];
if (section == null) continue; // Section not set if (section == null) continue; // Section not set
Types1_9.CHUNK_SECTION.write(buf, section); Types1_9.CHUNK_SECTION.write(buf, section);
section.writeBlockLight(buf); section.getLight().writeBlockLight(buf);
if (!section.hasSkyLight()) continue; // No sky light, we're done here. if (!section.getLight().hasSkyLight()) continue; // No sky light, we're done here.
section.writeSkyLight(buf); section.getLight().writeSkyLight(buf);
} }
buf.readerIndex(0); buf.readerIndex(0);

View File

@ -22,6 +22,7 @@ import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk; import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk1_8; import com.viaversion.viaversion.api.minecraft.chunks.Chunk1_8;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection; import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLight;
import com.viaversion.viaversion.api.type.PartialType; import com.viaversion.viaversion.api.type.PartialType;
import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.minecraft.BaseChunkType; import com.viaversion.viaversion.api.type.types.minecraft.BaseChunkType;
@ -113,16 +114,16 @@ public class Chunk1_9to1_8Type extends PartialType<Chunk, ClientChunks> {
// Read block light // Read block light
for (int i = 0; i < SECTION_COUNT; i++) { for (int i = 0; i < SECTION_COUNT; i++) {
if (!usedSections.get(i)) continue; // Section not set, has no light if (!usedSections.get(i)) continue; // Section not set, has no light
sections[i].readBlockLight(input); sections[i].getLight().readBlockLight(input);
} }
// Read sky light // Read sky light
int bytesLeft = dataLength - (input.readerIndex() - startIndex); int bytesLeft = dataLength - (input.readerIndex() - startIndex);
if (bytesLeft >= ChunkSection.LIGHT_LENGTH) { if (bytesLeft >= ChunkSectionLight.LIGHT_LENGTH) {
for (int i = 0; i < SECTION_COUNT; i++) { for (int i = 0; i < SECTION_COUNT; i++) {
if (!usedSections.get(i)) continue; // Section not set, has no light if (!usedSections.get(i)) continue; // Section not set, has no light
sections[i].readSkyLight(input); sections[i].getLight().readSkyLight(input);
bytesLeft -= ChunkSection.LIGHT_LENGTH; bytesLeft -= ChunkSectionLight.LIGHT_LENGTH;
} }
} }
@ -162,10 +163,10 @@ public class Chunk1_9to1_8Type extends PartialType<Chunk, ClientChunks> {
ChunkSection section = chunk.getSections()[i]; ChunkSection section = chunk.getSections()[i];
if (section == null) continue; // Section not set if (section == null) continue; // Section not set
Types1_9.CHUNK_SECTION.write(buf, section); Types1_9.CHUNK_SECTION.write(buf, section);
section.writeBlockLight(buf); section.getLight().writeBlockLight(buf);
if (!section.hasSkyLight()) continue; // No sky light, we're done here. if (!section.getLight().hasSkyLight()) continue; // No sky light, we're done here.
section.writeSkyLight(buf); section.getLight().writeSkyLight(buf);
} }
buf.readerIndex(0); buf.readerIndex(0);
Type.VAR_INT.writePrimitive(output, buf.readableBytes() + (chunk.hasBiomeData() ? 256 : 0)); Type.VAR_INT.writePrimitive(output, buf.readableBytes() + (chunk.hasBiomeData() ? 256 : 0));