Make engine-mode 2 truly random

Up to now a simple counter variable was used to iterate through the
hidden-blocks in engine-mode 2 while obfuscating. This results in low
quality obfuscation.

One could for example easily write a hack, which bypasses Anti-Xray
by not showing ores, which have a certain pattern.

Furthermore, engine-mode 1 is slightly optimized by this commit.
However, engine-mode 2 is probably somewhat slower. I did some tests but
I wasn't able to get stable results for some reason. Therefore this
needs further testing.

An optimized random algorithm is utilized to pick random blocks from the
hidden-blocks list. This implementation uses xorshift and integer
multiplication for bounding. The resulting distribution is negligibly
biased because xorshift doesn't generate 0 and integer multiplication
also implies biased results.
This commit is contained in:
stonar96 2020-08-07 04:26:01 +02:00 committed by MiniDigger
parent cd06ca5eb8
commit 0bad695802

View File

@ -98,10 +98,10 @@ index 0000000000000000000000000000000000000000..df7e4183d8842f5be8ae9d0698f8fa90
+}
diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
new file mode 100644
index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c1456ca57be1
index 0000000000000000000000000000000000000000..70b854c2619551df1f1984204010fa15b743234a
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java
@@ -0,0 +1,622 @@
@@ -0,0 +1,604 @@
+package com.destroystokyo.paper.antixray;
+
+import java.util.ArrayList;
@ -109,6 +109,8 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.IntSupplier;
+
+import net.minecraft.server.*;
+import org.bukkit.Bukkit;
@ -290,7 +292,25 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ boolean[] obfuscateTemp = null;
+ dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData());
+ dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData());
+ int counter = 0;
+ int numberOfBlocks = predefinedBlockDataBits.length;
+ // Keep the lambda expressions as simple as possible. They are used very frequently.
+ IntSupplier random = numberOfBlocks == 1 ? (() -> 0) : new IntSupplier() {
+ private int state;
+
+ {
+ while ((state = ThreadLocalRandom.current().nextInt()) == 0);
+ }
+
+ @Override
+ public int getAsInt() {
+ // https://en.wikipedia.org/wiki/Xorshift
+ state ^= state << 13;
+ state ^= state >>> 17;
+ state ^= state << 5;
+ // https://www.pcg-random.org/posts/bounded-rands.html
+ return (int) ((Integer.toUnsignedLong(state) * numberOfBlocks) >>> 32);
+ }
+ };
+
+ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) {
+ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) {
@ -328,7 +348,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+
+ // Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section
+ dataBitsWriter.setBitsPerObject(0);
+ obfuscateLayer(-1, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, emptyNearbyChunkSections, counter);
+ obfuscateLayer(-1, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, emptyNearbyChunkSections, random);
+ }
+
+ dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex));
@ -343,7 +363,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ current = next;
+ next = nextNext;
+ nextNext = temp;
+ counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
+ obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, random);
+ }
+
+ // Check if the chunk section above doesn't need obfuscation
@ -368,7 +388,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ // There is nothing to read anymore
+ dataBitsReader.setBitsPerObject(0);
+ solid[0] = true;
+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
+ obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, random);
+ }
+ } else {
+ // If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section
@ -380,7 +400,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ current = next;
+ next = nextNext;
+ nextNext = temp;
+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
+ obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, random);
+ }
+
+ dataBitsWriter.finish();
@ -390,7 +410,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true);
+ }
+
+ private int obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, int counter) {
+ private void obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, IntSupplier random) {
+ // First block of first line
+ int dataBits = dataBitsReader.read();
+
@ -402,11 +422,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(0, y, 15))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 0))] || current[0][0]) {
+ dataBitsWriter.skip();
+ } else {
+ if (counter >= predefinedBlockDataBits.length) {
+ counter = 0;
+ }
+
+ dataBitsWriter.write(predefinedBlockDataBits[counter++]);
+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]);
+ }
+ }
+
@ -427,11 +443,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(x, y, 15))] || current[0][x]) {
+ dataBitsWriter.skip();
+ } else {
+ if (counter >= predefinedBlockDataBits.length) {
+ counter = 0;
+ }
+
+ dataBitsWriter.write(predefinedBlockDataBits[counter++]);
+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]);
+ }
+ }
+
@ -451,11 +463,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(15, y, 15))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 0))] || current[0][15]) {
+ dataBitsWriter.skip();
+ } else {
+ if (counter >= predefinedBlockDataBits.length) {
+ counter = 0;
+ }
+
+ dataBitsWriter.write(predefinedBlockDataBits[counter++]);
+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]);
+ }
+ }
+
@ -477,11 +485,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ if (nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, z))] || current[z][0]) {
+ dataBitsWriter.skip();
+ } else {
+ if (counter >= predefinedBlockDataBits.length) {
+ counter = 0;
+ }
+
+ dataBitsWriter.write(predefinedBlockDataBits[counter++]);
+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]);
+ }
+ }
+
@ -503,11 +507,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ if (current[z][x]) {
+ dataBitsWriter.skip();
+ } else {
+ if (counter >= predefinedBlockDataBits.length) {
+ counter = 0;
+ }
+
+ dataBitsWriter.write(predefinedBlockDataBits[counter++]);
+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]);
+ }
+ }
+
@ -528,11 +528,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ if (nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, z))] || current[z][15]) {
+ dataBitsWriter.skip();
+ } else {
+ if (counter >= predefinedBlockDataBits.length) {
+ counter = 0;
+ }
+
+ dataBitsWriter.write(predefinedBlockDataBits[counter++]);
+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]);
+ }
+ }
+
@ -552,11 +548,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(0, y, 0))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 15))] || current[15][0]) {
+ dataBitsWriter.skip();
+ } else {
+ if (counter >= predefinedBlockDataBits.length) {
+ counter = 0;
+ }
+
+ dataBitsWriter.write(predefinedBlockDataBits[counter++]);
+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]);
+ }
+ }
+
@ -577,11 +569,7 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(x, y, 0))] || current[15][x]) {
+ dataBitsWriter.skip();
+ } else {
+ if (counter >= predefinedBlockDataBits.length) {
+ counter = 0;
+ }
+
+ dataBitsWriter.write(predefinedBlockDataBits[counter++]);
+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]);
+ }
+ }
+
@ -601,19 +589,13 @@ index 0000000000000000000000000000000000000000..f475427af03a0bcd69a215daf3d0c145
+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(15, y, 0))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 15))] || current[15][15]) {
+ dataBitsWriter.skip();
+ } else {
+ if (counter >= predefinedBlockDataBits.length) {
+ counter = 0;
+ }
+
+ dataBitsWriter.write(predefinedBlockDataBits[counter++]);
+ dataBitsWriter.write(predefinedBlockDataBits[random.getAsInt()]);
+ }
+ }
+
+ if (!obfuscate[dataBits]) {
+ next[15][15] = true;
+ }
+
+ return counter;
+ }
+
+ private boolean[] readDataPalette(DataPalette<IBlockData> dataPalette, boolean[] temp, boolean[] global) {