3
0
Files
KCauldronX/src/main/java/org/spigotmc/AntiXray.java
2015-03-22 20:38:04 +03:00

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 ) ) );
}
}