Skip to content

Commit 8a2911b

Browse files
committed
feat: move chunk package to core module
1 parent 053ffd8 commit 8a2911b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+812
-713
lines changed

orebfuscator-core/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
<dependencies>
1717
<!-- Netty -->
18-
<!--<dependency>
18+
<dependency>
1919
<groupId>io.netty</groupId>
2020
<artifactId>netty-buffer</artifactId>
2121
<version>${dependency.netty.version}</version>
@@ -26,7 +26,7 @@
2626
<artifactId>netty-transport</artifactId>
2727
<version>${dependency.netty.version}</version>
2828
<scope>provided</scope>
29-
</dependency>-->
29+
</dependency>
3030

3131
<!-- Third-Party -->
3232
<dependency>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package dev.imprex.orebfuscator.chunk;
2+
3+
import io.netty.buffer.ByteBuf;
4+
5+
public class ByteBufUtil {
6+
7+
public static int getVarIntSize(int value) {
8+
for (int bytes = 1; bytes < 5; bytes++) {
9+
if ((value & -1 << bytes * 7) == 0) {
10+
return bytes;
11+
}
12+
}
13+
return 5;
14+
}
15+
16+
public static void skipVarInt(ByteBuf buffer) {
17+
int bytes = 0;
18+
byte in;
19+
do {
20+
in = buffer.readByte();
21+
if (++bytes > 5) {
22+
throw new IndexOutOfBoundsException("varint32 too long");
23+
}
24+
} while ((in & 0x80) != 0);
25+
}
26+
27+
public static int readVarInt(ByteBuf buffer) {
28+
int out = 0;
29+
int bytes = 0;
30+
byte in;
31+
do {
32+
in = buffer.readByte();
33+
out |= (in & 0x7F) << bytes++ * 7;
34+
if (bytes > 5) {
35+
throw new IndexOutOfBoundsException("varint32 too long");
36+
}
37+
} while ((in & 0x80) != 0);
38+
return out;
39+
}
40+
41+
public static void writeVarInt(ByteBuf buffer, int value) {
42+
while ((value & -0x80) != 0) {
43+
buffer.writeByte(value & 0x7F | 0x80);
44+
value >>>= 7;
45+
}
46+
buffer.writeByte(value);
47+
}
48+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package dev.imprex.orebfuscator.chunk;
2+
3+
import java.util.Arrays;
4+
import dev.imprex.orebfuscator.interop.ChunkPacketAccessor;
5+
import dev.imprex.orebfuscator.interop.WorldAccessor;
6+
import io.netty.buffer.ByteBuf;
7+
import io.netty.buffer.PooledByteBufAllocator;
8+
import io.netty.buffer.Unpooled;
9+
10+
public class Chunk implements AutoCloseable {
11+
12+
private final ChunkFactory factory;
13+
14+
private final int chunkX;
15+
private final int chunkZ;
16+
17+
private final WorldAccessor worldAccessor;
18+
private final ChunkSectionHolder[] sections;
19+
20+
private final ByteBuf inputBuffer;
21+
private final ByteBuf outputBuffer;
22+
23+
Chunk(ChunkFactory factory, ChunkPacketAccessor packet) {
24+
this.factory = factory;
25+
26+
this.chunkX = packet.chunkX();
27+
this.chunkZ = packet.chunkZ();
28+
29+
this.worldAccessor = packet.world();
30+
this.sections = new ChunkSectionHolder[this.worldAccessor.getSectionCount()];
31+
32+
byte[] data = packet.data();
33+
this.inputBuffer = Unpooled.wrappedBuffer(data);
34+
this.outputBuffer = PooledByteBufAllocator.DEFAULT.heapBuffer(data.length);
35+
36+
for (int sectionIndex = 0; sectionIndex < this.sections.length; sectionIndex++) {
37+
if (packet.isSectionPresent(sectionIndex)) {
38+
this.sections[sectionIndex] = new ChunkSectionHolder();
39+
}
40+
}
41+
}
42+
43+
public WorldAccessor world() {
44+
return worldAccessor;
45+
}
46+
47+
public int getSectionCount() {
48+
return this.sections.length;
49+
}
50+
51+
public ChunkSection getSection(int index) {
52+
ChunkSectionHolder chunkSection = this.sections[index];
53+
if (chunkSection != null) {
54+
return chunkSection.chunkSection;
55+
}
56+
return null;
57+
}
58+
59+
public int getBlockState(int x, int y, int z) {
60+
if (x >> 4 == this.chunkX && z >> 4 == this.chunkZ) {
61+
ChunkSectionHolder chunkSection = this.sections[this.worldAccessor.getSectionIndex(y)];
62+
if (chunkSection != null) {
63+
return chunkSection.data[ChunkSection.positionToIndex(x & 0xF, y & 0xF, z & 0xF)];
64+
}
65+
return 0;
66+
}
67+
68+
return -1;
69+
}
70+
71+
public byte[] finalizeOutput() {
72+
for (ChunkSectionHolder chunkSection : this.sections) {
73+
if (chunkSection != null) {
74+
chunkSection.write();
75+
}
76+
}
77+
78+
this.outputBuffer.writeBytes(this.inputBuffer);
79+
80+
return Arrays.copyOfRange(
81+
this.outputBuffer.array(), this.outputBuffer.arrayOffset(),
82+
this.outputBuffer.arrayOffset() + this.outputBuffer.readableBytes());
83+
}
84+
85+
@Override
86+
public void close() throws Exception {
87+
this.inputBuffer.release();
88+
this.outputBuffer.release();
89+
}
90+
91+
private void skipBiomePalettedContainer() {
92+
int bitsPerValue = this.inputBuffer.readUnsignedByte();
93+
94+
if (bitsPerValue == 0) {
95+
ByteBufUtil.readVarInt(this.inputBuffer);
96+
} else if (bitsPerValue <= 3) {
97+
for (int i = ByteBufUtil.readVarInt(this.inputBuffer); i > 0; i--) {
98+
ByteBufUtil.readVarInt(this.inputBuffer);
99+
}
100+
}
101+
102+
int expectedDataLength = SimpleVarBitBuffer.calculateArraySize(bitsPerValue, 64);
103+
104+
if (factory.versionFlags().hasLongArrayLengthField()) {
105+
int dataLength = ByteBufUtil.readVarInt(this.inputBuffer);
106+
if (expectedDataLength != dataLength) {
107+
throw new IndexOutOfBoundsException(
108+
"data.length != VarBitBuffer::size " + dataLength + " " + expectedDataLength);
109+
}
110+
}
111+
112+
this.inputBuffer.skipBytes(Long.BYTES * expectedDataLength);
113+
}
114+
115+
private class ChunkSectionHolder {
116+
117+
public final ChunkSection chunkSection;
118+
119+
public final int[] data;
120+
public final int extraOffset;
121+
122+
private int extraBytes;
123+
124+
public ChunkSectionHolder() {
125+
this.chunkSection = new ChunkSection(factory);
126+
127+
this.data = this.chunkSection.read(inputBuffer);
128+
this.extraOffset = inputBuffer.readerIndex();
129+
130+
if (factory.versionFlags().hasBiomePalettedContainer()) {
131+
skipBiomePalettedContainer();
132+
this.extraBytes = inputBuffer.readerIndex() - this.extraOffset;
133+
}
134+
}
135+
136+
public void write() {
137+
this.chunkSection.write(outputBuffer);
138+
if (this.extraBytes > 0) {
139+
outputBuffer.writeBytes(inputBuffer, this.extraOffset, extraBytes);
140+
}
141+
}
142+
}
143+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dev.imprex.orebfuscator.chunk;
2+
3+
import dev.imprex.orebfuscator.interop.ChunkPacketAccessor;
4+
import dev.imprex.orebfuscator.interop.RegistryAccessor;
5+
import dev.imprex.orebfuscator.interop.ServerAccessor;
6+
7+
public class ChunkFactory {
8+
9+
private final RegistryAccessor registryAccessor;
10+
private final ChunkVersionFlags versionFlags;
11+
12+
public ChunkFactory(ServerAccessor serverAccessor) {
13+
this.registryAccessor = serverAccessor.getRegistry();
14+
this.versionFlags = new ChunkVersionFlags(serverAccessor);
15+
}
16+
17+
RegistryAccessor registryAccessor() {
18+
return registryAccessor;
19+
}
20+
21+
ChunkVersionFlags versionFlags() {
22+
return versionFlags;
23+
}
24+
25+
public Chunk fromPacket(ChunkPacketAccessor packet) {
26+
return new Chunk(this, packet);
27+
}
28+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package dev.imprex.orebfuscator.chunk;
2+
3+
import dev.imprex.orebfuscator.interop.RegistryAccessor;
4+
import io.netty.buffer.ByteBuf;
5+
6+
public class ChunkSection {
7+
8+
private final RegistryAccessor registryAccessor;
9+
private final ChunkVersionFlags versionFlags;
10+
11+
private int blockCount;
12+
private int bitsPerBlock = -1;
13+
14+
private Palette palette;
15+
private VarBitBuffer data;
16+
17+
public ChunkSection(ChunkFactory factory) {
18+
this.registryAccessor = factory.registryAccessor();
19+
this.versionFlags = factory.versionFlags();
20+
21+
this.setBitsPerBlock(0, true);
22+
}
23+
24+
public RegistryAccessor registryAccessor() {
25+
return registryAccessor;
26+
}
27+
28+
private void setBitsPerBlock(int bitsPerBlock, boolean grow) {
29+
if (this.bitsPerBlock != bitsPerBlock) {
30+
if (versionFlags.hasSingleValuePalette() && bitsPerBlock == 0) {
31+
this.bitsPerBlock = 0;
32+
this.palette = new SingleValuePalette(this, 0);
33+
} else if (!grow && bitsPerBlock == 1) {
34+
// fix: fawe chunk format incompatibility with bitsPerBlock == 1
35+
// https://github.com/Imprex-Development/Orebfuscator/issues/36
36+
this.bitsPerBlock = bitsPerBlock;
37+
this.palette = new IndirectPalette(this.bitsPerBlock, this);
38+
} else if (bitsPerBlock <= 8) {
39+
this.bitsPerBlock = Math.max(4, bitsPerBlock);
40+
this.palette = new IndirectPalette(this.bitsPerBlock, this);
41+
} else {
42+
this.bitsPerBlock = registryAccessor.getMaxBitsPerBlockState();
43+
this.palette = new DirectPalette();
44+
}
45+
46+
if (this.bitsPerBlock == 0) {
47+
this.data = new ZeroVarBitBuffer(4096);
48+
} else {
49+
this.data = new SimpleVarBitBuffer(this.bitsPerBlock, 4096);
50+
}
51+
}
52+
}
53+
54+
int grow(int bitsPerBlock, int blockId) {
55+
Palette palette = this.palette;
56+
VarBitBuffer data = this.data;
57+
58+
this.setBitsPerBlock(bitsPerBlock, true);
59+
60+
for (int i = 0; i < data.size(); i++) {
61+
int preBlockId = palette.valueFor(data.get(i));
62+
this.data.set(i, this.palette.idFor(preBlockId));
63+
}
64+
65+
return this.palette.idFor(blockId);
66+
}
67+
68+
static int positionToIndex(int x, int y, int z) {
69+
return y << 8 | z << 4 | x;
70+
}
71+
72+
public void setBlockState(int x, int y, int z, int blockId) {
73+
this.setBlockState(positionToIndex(x, y, z), blockId);
74+
}
75+
76+
public void setBlockState(int index, int blockId) {
77+
int prevBlockId = this.getBlockState(index);
78+
79+
if (!registryAccessor.isAir(prevBlockId)) {
80+
--this.blockCount;
81+
}
82+
83+
if (!registryAccessor.isAir(blockId)) {
84+
++this.blockCount;
85+
}
86+
87+
int paletteIndex = this.palette.idFor(blockId);
88+
this.data.set(index, paletteIndex);
89+
}
90+
91+
public int getBlock(int x, int y, int z) {
92+
return this.getBlockState(positionToIndex(x, y, z));
93+
}
94+
95+
public int getBlockState(int index) {
96+
return this.palette.valueFor(this.data.get(index));
97+
}
98+
99+
public boolean isEmpty() {
100+
return this.blockCount == 0;
101+
}
102+
103+
public void write(ByteBuf buffer) {
104+
buffer.writeShort(this.blockCount);
105+
106+
buffer.writeByte(this.bitsPerBlock);
107+
this.palette.write(buffer);
108+
109+
long[] data = this.data.toArray();
110+
111+
if (versionFlags.hasLongArrayLengthField()) {
112+
ByteBufUtil.writeVarInt(buffer, data.length);
113+
}
114+
115+
for (long entry : data) {
116+
buffer.writeLong(entry);
117+
}
118+
}
119+
120+
public int[] read(ByteBuf buffer) {
121+
this.blockCount = buffer.readShort();
122+
123+
this.setBitsPerBlock(buffer.readUnsignedByte(), false);
124+
125+
this.palette.read(buffer);
126+
127+
long[] data = this.data.toArray();
128+
129+
if (versionFlags.hasLongArrayLengthField()) {
130+
int length = ByteBufUtil.readVarInt(buffer);
131+
if (data.length != length) {
132+
throw new IndexOutOfBoundsException("data.length != VarBitBuffer::size " + length + " " + this.data);
133+
}
134+
}
135+
136+
for (int i = 0; i < data.length; i++) {
137+
data[i] = buffer.readLong();
138+
}
139+
140+
int[] directData = new int[4096];
141+
for (int i = 0; i < directData.length; i++) {
142+
directData[i] = this.getBlockState(i);
143+
}
144+
return directData;
145+
}
146+
}

0 commit comments

Comments
 (0)