package org.ultramine.mods.anticheat; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import net.minecraft.block.Block; import net.minecraft.command.CommandException; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.oredict.OreDictionary; import net.openhft.koloboke.collect.map.IntIntMap; import net.openhft.koloboke.collect.map.hash.HashIntIntMaps; import net.openhft.koloboke.collect.set.ShortSet; import net.openhft.koloboke.collect.set.hash.HashShortSets; import org.ultramine.core.service.EventBusRegisteredService; import org.ultramine.server.chunk.AntiXRayService; import org.ultramine.server.chunk.ChunkSnapshot; import org.ultramine.server.event.SetBlockEvent; import org.ultramine.server.util.BasicTypeParser; import java.util.List; import java.util.Map; public class AntiXRayServiceImpl extends EventBusRegisteredService implements AntiXRayService<Integer> { private final CanReplaceStrategy strategy; private final IntIntMap worldProviderToStoneType; public AntiXRayServiceImpl(UMACConfig.AntiXRay config) { if(config.strategy.equals("replace-ore-to-stone")) this.strategy = new ReplaceIfContainsStrategy(createBlockSet(config.oreBlocks)); else if(config.strategy.equals("replace-all-to-stone")) this.strategy = new ReplaceAllStrategy(); else throw new IllegalArgumentException("Unknown antiXRay strategy in settings/anticheat.yml: " + config.strategy); this.worldProviderToStoneType = createWorldToBlockMap(config.worldProviderToStoneBlock); } private ShortSet createBlockSet(List<String> oreBlocks) { ShortSet tempSet = HashShortSets.newUpdatableSet(); for(String blockStr : oreBlocks) { ItemStack is; try { is = BasicTypeParser.parseItemStack(blockStr, true, 1, false); } catch (CommandException e) { continue; } int id = Item.itemRegistry.getIDForObject(is.getItem()); if(is.getItemDamage() == OreDictionary.WILDCARD_VALUE) { for(int i = 0; i < 16; i++) addIdMeta(tempSet, id, i); } else { addIdMeta(tempSet, id, is.getItemDamage()); } } return HashShortSets.newImmutableSet(tempSet); } private void addIdMeta(ShortSet set, int id, int meta) { set.add((short)(id | ((meta & 15) << 12))); } private IntIntMap createWorldToBlockMap(Map<Integer, String> worldProviderToStoneBlock) { IntIntMap tempMap = HashIntIntMaps.newUpdatableMap(); for(Map.Entry<Integer, String> ent : worldProviderToStoneBlock.entrySet()) { ItemStack is; try { is = BasicTypeParser.parseItemStack(ent.getValue(), false, 1, false); } catch (CommandException e) { continue; } int id = Item.itemRegistry.getIDForObject(is.getItem()); tempMap.put(ent.getKey().intValue(), (id | ((is.getItemDamage() & 15) << 12))); } return HashIntIntMaps.newImmutableMap(tempMap); } private boolean canReplaceType(int idMeta) { return strategy.canReplaceType(idMeta); } private boolean canReplaceType(int id, int meta) { return canReplaceType(id | ((meta & 15) << 12)); } private boolean canReplaceType(Block block, int meta) { return canReplaceType(Block.blockRegistry.getIDForObject(block), meta); } private boolean canReplaceBlock(ChunkSnapshot chunkSnapshot, int x, int y, int z) { int idMeta = chunkSnapshot.getBlockIdAndMeta(x, y, z); return canReplaceType(idMeta) && chunkSnapshot.getBlock(x-1, y, z) .isOpaqueCube() && chunkSnapshot.getBlock(x+1, y, z) .isOpaqueCube() && chunkSnapshot.getBlock(x, y, z-1).isOpaqueCube() && chunkSnapshot.getBlock(x, y, z+1).isOpaqueCube() && chunkSnapshot.getBlock(x, y-1, z) .isOpaqueCube() && chunkSnapshot.getBlock(x, y+1, z) .isOpaqueCube(); } private boolean canReplaceBlock(World world, int x, int y, int z) { Block block = world.getBlock(x, y, z); int meta = world.getBlockMetadata(x, y, z); return canReplaceType(block, meta) && world.getBlock(x-1, y, z) .isOpaqueCube() && world.getBlock(x+1, y, z) .isOpaqueCube() && world.getBlock(x, y, z-1).isOpaqueCube() && world.getBlock(x, y, z+1).isOpaqueCube() && world.getBlock(x, y-1, z) .isOpaqueCube() && world.getBlock(x, y+1, z) .isOpaqueCube(); } private boolean canReplaceBlock(World world, ChunkSnapshot chunkSnapshot, int x, int y, int z) { return canReplaceBlock(world, (chunkSnapshot.getX() << 4) | x, y, (chunkSnapshot.getZ() << 4) | z); } @Override public Integer prepareChunkSync(ChunkSnapshot chunkSnapshot, Chunk chunk) { World world = chunk.worldObj; int stone = worldProviderToStoneType.getOrDefault(DimensionManager.getProviderType(world.provider.dimensionId), 1); int stoneId = stone & 0xFFF; int stoneMeta = stone >> 12; int yMax = Math.min(chunkSnapshot.getTopFilledSegment() + 16, 254); for(int y = 1; y < yMax; y++) for(int x = 0; x < 16; x++) if(canReplaceBlock(world, chunkSnapshot, x, y, 0)) chunkSnapshot.setBlock(x, y, 0, stoneId, stoneMeta); for(int y = 1; y < yMax; y++) for(int x = 0; x < 16; x++) if(canReplaceBlock(world, chunkSnapshot, x, y, 15)) chunkSnapshot.setBlock(x, y, 15, stoneId, stoneMeta); for(int y = 1; y < yMax; y++) for(int z = 1; z < 15; z++) if(canReplaceBlock(world, chunkSnapshot, 0, y, z)) chunkSnapshot.setBlock(0, y, z, stoneId, stoneMeta); for(int y = 1; y < yMax; y++) for(int z = 1; z < 15; z++) if(canReplaceBlock(world, chunkSnapshot, 15, y, z)) chunkSnapshot.setBlock(15, y, z, stoneId, stoneMeta); return stone; } @Override public void prepareChunkAsync(ChunkSnapshot chunkSnapshot, Integer stoneType) { int stone = stoneType; int stoneId = stone & 0xFFF; int stoneMeta = stone >> 12; int yMax = Math.min(chunkSnapshot.getTopFilledSegment() + 16, 254); for(int y = 1; y < yMax; y++) for(int z = 1; z < 15; z++) for(int x = 1; x < 15; x++) if(canReplaceBlock(chunkSnapshot, x, y, z)) chunkSnapshot.setBlock(x, y, z, stoneId, stoneMeta); } private void checkAndUpdateLocation(World world, int x, int y, int z) { if(canReplaceBlock(world, x, y, z)) world.markBlockForUpdate(x, y, z); } @SubscribeEvent public void onBlockChange(SetBlockEvent e) { if(e.newBlock.isOpaqueCube() || !e.world.getBlock(e.x, e.y, e.z).isOpaqueCube()) return; checkAndUpdateLocation(e.world, e.x-1, e.y, e.z); checkAndUpdateLocation(e.world, e.x+1, e.y, e.z); checkAndUpdateLocation(e.world, e.x, e.y, e.z-1); checkAndUpdateLocation(e.world, e.x, e.y, e.z+1); checkAndUpdateLocation(e.world, e.x, e.y-1, e.z); checkAndUpdateLocation(e.world, e.x, e.y+1, e.z); } interface CanReplaceStrategy { boolean canReplaceType(int idMeta); } private static class ReplaceAllStrategy implements CanReplaceStrategy { @Override public boolean canReplaceType(int idMeta) { return true; } } private static class ReplaceIfContainsStrategy implements CanReplaceStrategy { private final ShortSet oreBlocks; private ReplaceIfContainsStrategy(ShortSet oreBlocks) { this.oreBlocks = oreBlocks; } @Override public boolean canReplaceType(int idMeta) { return oreBlocks.contains((short)idMeta); } } }