187 lines
8.3 KiB
Java
187 lines
8.3 KiB
Java
package org.spigotmc;
|
|
|
|
import gnu.trove.set.TByteSet;
|
|
import gnu.trove.set.hash.TByteHashSet;
|
|
|
|
public class AntiXray
|
|
{
|
|
|
|
private static final CustomTimingsHandler update = new CustomTimingsHandler( "xray - update" );
|
|
private static final CustomTimingsHandler obfuscate = new CustomTimingsHandler( "xray - obfuscate" );
|
|
/*========================================================================*/
|
|
// Used to keep track of which blocks to obfuscate
|
|
private final boolean[] obfuscateBlocks = new boolean[ Short.MAX_VALUE ];
|
|
// Used to select a random replacement ore
|
|
private final byte[] replacementOres;
|
|
|
|
public AntiXray(SpigotWorldConfig config)
|
|
{
|
|
// Set all listed blocks as true to be obfuscated
|
|
for ( int id : ( config.engineMode == 1 ) ? config.hiddenBlocks : config.replaceBlocks )
|
|
{
|
|
obfuscateBlocks[id] = true;
|
|
}
|
|
|
|
// For every block
|
|
TByteSet blocks = new TByteHashSet();
|
|
for ( Integer i : config.hiddenBlocks )
|
|
{
|
|
net.minecraft.block.Block block = net.minecraft.block.Block.getBlockById( i );
|
|
// Check it exists and is not a tile entity
|
|
if ( block != null && !block.hasTileEntity() )
|
|
{
|
|
// Add it to the set of replacement blocks
|
|
blocks.add( (byte) (int) i );
|
|
}
|
|
}
|
|
// Bake it to a flat array of replacements
|
|
replacementOres = blocks.toArray();
|
|
}
|
|
|
|
/**
|
|
* Starts the timings handler, then updates all blocks within the set radius
|
|
* of the given coordinate, revealing them if they are hidden ores.
|
|
*/
|
|
public void updateNearbyBlocks(net.minecraft.world.World world, int x, int y, int z)
|
|
{
|
|
if ( world.getSpigotConfig().antiXray ) // Cauldron
|
|
{
|
|
update.startTiming();
|
|
updateNearbyBlocks( world, x, y, z, 2, false ); // 2 is the radius, we shouldn't change it as that would make it exponentially slower
|
|
update.stopTiming();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes all non exposed ores from the chunk buffer.
|
|
*/
|
|
public void obfuscate(int chunkX, int chunkZ, int bitmask, byte[] buffer, net.minecraft.world.World world)
|
|
{
|
|
// If the world is marked as obfuscated
|
|
if ( world.getSpigotConfig().antiXray ) // Cauldron
|
|
{
|
|
obfuscate.startTiming();
|
|
// Initial radius to search around for air
|
|
int initialRadius = 1;
|
|
// Which block in the buffer we are looking at, anywhere from 0 to 16^4
|
|
int index = 0;
|
|
// The iterator marking which random ore we should use next
|
|
int randomOre = 0;
|
|
|
|
// Chunk corner X and Z blocks
|
|
int startX = chunkX << 4;
|
|
int startZ = chunkZ << 4;
|
|
|
|
// Chunks can have up to 16 sections
|
|
for ( int i = 0; i < 16; i++ )
|
|
{
|
|
// If the bitmask indicates this chunk is sent...
|
|
if ( ( bitmask & 1 << i ) != 0 )
|
|
{
|
|
// Work through all blocks in the chunk, y,z,x
|
|
for ( int y = 0; y < 16; y++ )
|
|
{
|
|
for ( int z = 0; z < 16; z++ )
|
|
{
|
|
for ( int x = 0; x < 16; x++ )
|
|
{
|
|
// For some reason we can get too far ahead of ourselves (concurrent modification on bulk chunks?) so if we do, just abort and move on
|
|
if ( index >= buffer.length )
|
|
{
|
|
continue;
|
|
}
|
|
// Grab the block ID in the buffer.
|
|
// TODO: extended IDs are not yet supported
|
|
int blockId = buffer[index] & 0xFF;
|
|
// Check if the block should be obfuscated
|
|
if ( obfuscateBlocks[blockId] )
|
|
{
|
|
// TODO: Don't really understand this, but if radius is not 0 and the world isn't loaded, bail out
|
|
if ( initialRadius != 0 && !isLoaded( world, startX + x, ( i << 4 ) + y, startZ + z, initialRadius ) )
|
|
{
|
|
continue;
|
|
}
|
|
// On the otherhand, if radius is 0, or the nearby blocks are all non air, we can obfuscate
|
|
if ( initialRadius == 0 || !hasTransparentBlockAdjacent( world, startX + x, ( i << 4 ) + y, startZ + z, initialRadius ) )
|
|
{
|
|
switch ( world.spigotConfig.engineMode )
|
|
{
|
|
case 1:
|
|
// Replace with stone
|
|
buffer[index] = 1;
|
|
break;
|
|
case 2:
|
|
// Replace with random ore.
|
|
if ( randomOre >= replacementOres.length )
|
|
{
|
|
randomOre = 0;
|
|
}
|
|
buffer[index] = replacementOres[randomOre++];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
obfuscate.stopTiming();
|
|
}
|
|
}
|
|
|
|
private void updateNearbyBlocks(net.minecraft.world.World world, int x, int y, int z, int radius, boolean updateSelf)
|
|
{
|
|
// If the block in question is loaded
|
|
if ( world.blockExists( x, y, z ) )
|
|
{
|
|
// Get block id
|
|
net.minecraft.block.Block block = world.getBlock( x, y, z );
|
|
|
|
// See if it needs update
|
|
if ( updateSelf && obfuscateBlocks[net.minecraft.block.Block.getIdFromBlock( block )] )
|
|
{
|
|
// Send the update
|
|
world.markBlockForUpdate( x, y, z );
|
|
}
|
|
|
|
// Check other blocks for updates
|
|
if ( radius > 0 )
|
|
{
|
|
updateNearbyBlocks( world, x + 1, y, z, radius - 1, true );
|
|
updateNearbyBlocks( world, x - 1, y, z, radius - 1, true );
|
|
updateNearbyBlocks( world, x, y + 1, z, radius - 1, true );
|
|
updateNearbyBlocks( world, x, y - 1, z, radius - 1, true );
|
|
updateNearbyBlocks( world, x, y, z + 1, radius - 1, true );
|
|
updateNearbyBlocks( world, x, y, z - 1, radius - 1, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
private static boolean isLoaded(net.minecraft.world.World world, int x, int y, int z, int radius)
|
|
{
|
|
return world.blockExists( x, y, z )
|
|
|| ( radius > 0
|
|
&& ( isLoaded( world, x + 1, y, z, radius - 1 )
|
|
|| isLoaded( world, x - 1, y, z, radius - 1 )
|
|
|| isLoaded( world, x, y + 1, z, radius - 1 )
|
|
|| isLoaded( world, x, y - 1, z, radius - 1 )
|
|
|| isLoaded( world, x, y, z + 1, radius - 1 )
|
|
|| isLoaded( world, x, y, z - 1, radius - 1 ) ) );
|
|
}
|
|
|
|
private static boolean hasTransparentBlockAdjacent(net.minecraft.world.World world, int x, int y, int z, int radius)
|
|
{
|
|
return !world.getBlock( x, y, z ).isNormalCube() /* isSolidBlock */
|
|
|| ( radius > 0
|
|
&& ( hasTransparentBlockAdjacent( world, x + 1, y, z, radius - 1 )
|
|
|| hasTransparentBlockAdjacent( world, x - 1, y, z, radius - 1 )
|
|
|| hasTransparentBlockAdjacent( world, x, y + 1, z, radius - 1 )
|
|
|| hasTransparentBlockAdjacent( world, x, y - 1, z, radius - 1 )
|
|
|| hasTransparentBlockAdjacent( world, x, y, z + 1, radius - 1 )
|
|
|| hasTransparentBlockAdjacent( world, x, y, z - 1, radius - 1 ) ) );
|
|
}
|
|
}
|