Skip to content

Commit b40784d

Browse files
committed
add anti x-ray
1 parent 88827ec commit b40784d

4 files changed

Lines changed: 155 additions & 2 deletions

File tree

src/main/java/me/mapacheee/extendedhorizons/shared/config/MainConfig.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,16 @@ public record FakeChunksConfig(
4747
@Setting("use-compression") boolean useCompression,
4848
@Setting("cache-cleanup-interval") int cacheCleanupInterval,
4949
@Setting("enable-memory-cache") boolean enableMemoryCache,
50-
@Setting("max-memory-cache-size") int maxMemoryCacheSize) {
50+
@Setting("max-memory-cache-size") int maxMemoryCacheSize,
51+
@Setting("anti-xray") AntiXrayConfig antiXray) {
52+
53+
@ConfigSerializable
54+
public record AntiXrayConfig(
55+
boolean enabled,
56+
@Setting("hide-ores") boolean hideOres,
57+
@Setting("add-fake-ores") boolean addFakeOres,
58+
@Setting("fake-ore-density") double fakeOreDensity) {
59+
}
5160
}
5261

5362
@ConfigSerializable
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package me.mapacheee.extendedhorizons.viewdistance.service;
2+
3+
import net.minecraft.world.level.block.Block;
4+
import net.minecraft.world.level.block.Blocks;
5+
import net.minecraft.world.level.block.state.BlockState;
6+
import net.minecraft.world.level.chunk.LevelChunk;
7+
import net.minecraft.world.level.chunk.LevelChunkSection;
8+
import net.minecraft.world.level.chunk.PalettedContainer;
9+
10+
import java.util.Random;
11+
12+
/**
13+
* Anti-Xray utility for obfuscating chunks
14+
* Replaces valuable ores with stone/deepslate and adds fake ores
15+
*
16+
*/
17+
public class ChunkAntiXray {
18+
19+
private static final BlockState STONE = Blocks.STONE.defaultBlockState();
20+
private static final BlockState DEEPSLATE = Blocks.DEEPSLATE.defaultBlockState();
21+
22+
private static final Block[] VALUABLE_ORES = {
23+
Blocks.DIAMOND_ORE, Blocks.DEEPSLATE_DIAMOND_ORE,
24+
Blocks.EMERALD_ORE, Blocks.DEEPSLATE_EMERALD_ORE,
25+
Blocks.GOLD_ORE, Blocks.DEEPSLATE_GOLD_ORE,
26+
Blocks.IRON_ORE, Blocks.DEEPSLATE_IRON_ORE,
27+
Blocks.LAPIS_ORE, Blocks.DEEPSLATE_LAPIS_ORE,
28+
Blocks.REDSTONE_ORE, Blocks.DEEPSLATE_REDSTONE_ORE,
29+
Blocks.ANCIENT_DEBRIS,
30+
Blocks.COPPER_ORE, Blocks.DEEPSLATE_COPPER_ORE
31+
};
32+
33+
private static final BlockState[] FAKE_ORES = {
34+
Blocks.DIAMOND_ORE.defaultBlockState(),
35+
Blocks.DEEPSLATE_DIAMOND_ORE.defaultBlockState(),
36+
Blocks.GOLD_ORE.defaultBlockState(),
37+
Blocks.DEEPSLATE_GOLD_ORE.defaultBlockState(),
38+
Blocks.IRON_ORE.defaultBlockState()
39+
};
40+
41+
/**
42+
* Obfuscates a chunk by hiding real ores and adding fake ones
43+
*/
44+
public static void obfuscateChunk(LevelChunk chunk, boolean hideOres, boolean addFakeOres, double fakeOreDensity) {
45+
Random random = new Random(chunk.getPos().toLong());
46+
LevelChunkSection[] sections = chunk.getSections();
47+
48+
for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) {
49+
LevelChunkSection section = sections[sectionIndex];
50+
if (section == null || section.hasOnlyAir()) {
51+
continue;
52+
}
53+
54+
int sectionY = chunk.getMinSectionY() + sectionIndex;
55+
int baseY = sectionY * 16;
56+
57+
PalettedContainer<BlockState> blocks = section.getStates();
58+
59+
for (int x = 0; x < 16; x++) {
60+
for (int y = 0; y < 16; y++) {
61+
for (int z = 0; z < 16; z++) {
62+
BlockState state = blocks.get(x, y, z);
63+
if (state == null)
64+
continue;
65+
66+
Block block = state.getBlock();
67+
int worldY = baseY + y;
68+
69+
if (hideOres && isValuableOre(block)) {
70+
BlockState replacement = worldY < 0 ? DEEPSLATE : STONE;
71+
blocks.set(x, y, z, replacement);
72+
} else if (addFakeOres && random.nextDouble() < fakeOreDensity) {
73+
if (block == Blocks.STONE || block == Blocks.DEEPSLATE) {
74+
BlockState fakeOre = getFakeOre(random, worldY);
75+
blocks.set(x, y, z, fakeOre);
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}
83+
84+
private static boolean isValuableOre(Block block) {
85+
for (Block ore : VALUABLE_ORES) {
86+
if (block == ore) {
87+
return true;
88+
}
89+
}
90+
return false;
91+
}
92+
93+
private static BlockState getFakeOre(Random random, int y) {
94+
if (y < 0) {
95+
return random.nextBoolean()
96+
? Blocks.DEEPSLATE_DIAMOND_ORE.defaultBlockState()
97+
: Blocks.DEEPSLATE_GOLD_ORE.defaultBlockState();
98+
} else {
99+
return FAKE_ORES[random.nextInt(FAKE_ORES.length)];
100+
}
101+
}
102+
}

src/main/java/me/mapacheee/extendedhorizons/viewdistance/service/FakeChunkService.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,8 @@ private boolean shouldClearQueue(Queue<Long> queue, int playerChunkX, int player
766766
* Attempts to get a chunk from the servers memory cache or our own cache
767767
*/
768768
private LevelChunk getChunkFromMemoryCache(World world, int chunkX, int chunkZ) {
769-
if (!configService.get().performance().fakeChunks().enableMemoryCache()) {
769+
boolean antiXrayEnabled = configService.get().performance().fakeChunks().antiXray().enabled();
770+
if (!configService.get().performance().fakeChunks().enableMemoryCache() || antiXrayEnabled) {
770771
return null;
771772
}
772773

@@ -865,6 +866,22 @@ private void generateChunkAndSend(Player player, World world, int chunkX, int ch
865866
* Packets are created here in async thread and queued for sending
866867
*/
867868
private void sendChunkPacket(Player player, LevelChunk nmsChunk, long key, Set<Long> sentTracker) {
869+
if (configService.get().performance().fakeChunks().antiXray().enabled()) {
870+
try {
871+
nmsChunk = cloneChunkForObfuscation(nmsChunk);
872+
873+
boolean hideOres = configService.get().performance().fakeChunks().antiXray().hideOres();
874+
boolean addFakeOres = configService.get().performance().fakeChunks().antiXray().addFakeOres();
875+
double density = configService.get().performance().fakeChunks().antiXray().fakeOreDensity();
876+
877+
ChunkAntiXray.obfuscateChunk(nmsChunk, hideOres, addFakeOres, density);
878+
} catch (Exception e) {
879+
if (DEBUG) {
880+
logger.warn("[EH] Failed to obfuscate chunk: {}", e.getMessage());
881+
}
882+
}
883+
}
884+
868885
net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket packet = null;
869886
try {
870887
net.minecraft.world.level.lighting.LevelLightEngine lightEngine = nmsChunk.getLevel().getLightEngine();
@@ -903,6 +920,17 @@ private void sendChunkPacket(Player player, LevelChunk nmsChunk, long key, Set<L
903920
}
904921
}
905922

923+
/**
924+
* Placeholder for chunk cloning (not needed - memory cache is disabled when
925+
* anti-xray is active)
926+
*/
927+
private LevelChunk cloneChunkForObfuscation(LevelChunk original) {
928+
// No cloning needed: memory cache is automatically disabled when anti-xray is
929+
// enabled
930+
// This prevents cached chunks from being permanently obfuscated
931+
return original;
932+
}
933+
906934
/**
907935
* Sends a Column directly to the player using PacketEvents
908936
* Returns true if successful, false if Column is null or invalid

src/main/resources/config.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ performance:
6464
# Maximum chunks to cache in memory (1000 = ~40-80MB depending on chunk complexity)
6565
# Increase for more players in same area, decrease for dispersed players or low RAM
6666
max-memory-cache-size: 1000
67+
68+
# Anti-X-Ray for fake chunks
69+
70+
# Enable this feature will affect the speed to load fake chunks
71+
# Enable this only if its necessary
72+
anti-xray:
73+
# Enable anti-xray obfuscation for fake chunks
74+
enabled: false
75+
# Replace valuable ores with stone/deepslate
76+
hide-ores: true
77+
# Add fake ores to confuse x-ray users
78+
add-fake-ores: true
79+
# Density of fake ores (0.0-1.0, higher = more fakes)
80+
fake-ore-density: 0.15
6781

6882
occlusion-culling:
6983
enabled: true

0 commit comments

Comments
 (0)