package speedytools.common.selections; import io.netty.buffer.ByteBuf; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.init.Blocks; import net.minecraft.util.BlockPos; import net.minecraft.util.EnumFacing; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import speedytools.common.blocks.BlockWithMetadata; import speedytools.common.utilities.ErrorLog; /** * FillMatcher is used by fill algorithm to decide the type of blocks that should be added to the selection * Typical usage: * 1) client side: create the desired FillMatcher eg AnyNonAir * 2) the fill algorithm calls fillMatcher.matches(chunk, wcx, wcy, wcz). * MATCH: add this block to the selection * NO_MATCH: don't add this block to the selection * NOT_LOADED: mark as not loaded / not available * OUT_OF_BOUNDS: repeat using a call to fillMatcher.matches(world, .... ) * To transfer to the server side: * 1) fillmatcher.writeToBuffer(ByteBuf) * 2) on server side: FillMatcher.createMatcherFromBuffer(ByteBuf) * Created by TheGreyGhost on 5/11/14. */ public abstract class FillMatcher { /** generates a matcher from a ByteBuf serialised version * @param buf * @return the matcher, or null for invalid */ public static FillMatcher createMatcherFromBuffer(ByteBuf buf) { FillMatcher retval = null; try { byte matcherTypeID = buf.readByte(); switch (matcherTypeID) { case ANY_NON_AIR: { retval = new AnyNonAir(); break; } case ANY_SOLID: { retval = new AnySolid(); break; } case ONLY_SPECIFIED_BLOCK: { BlockWithMetadata blockWithMetadata = new BlockWithMetadata(); int blockID = buf.readInt(); blockWithMetadata.block = Block.getBlockById(blockID); blockWithMetadata.metaData = buf.readInt(); retval = new OnlySpecifiedBlock(blockWithMetadata); break; } case CONTOUR_FOLLOWER: { boolean additiveMode = buf.readBoolean(); int contourDirectionIndex = buf.readInt(); EnumFacing contourDirection = EnumFacing.getFront(contourDirectionIndex); retval = new ContourFollower(additiveMode, contourDirection); break; } default: { ErrorLog.defaultLog().info("Invalid matcherTypeID received:" + matcherTypeID); } } } catch (IndexOutOfBoundsException ioe) { ErrorLog.defaultLog().info("Exception while createMatcherFromBuffer: " + ioe); return null; } return retval; } public enum MatchResult {MATCH, NO_MATCH, NOT_LOADED, OUT_OF_BOUNDS} /** * does this block meet the matcher criteria? (i.e. it should be added to the selection) * @param chunk * @param wcx the x coordinate within the chunk (i.e. 0 - 15) * @param wcy the y coordinate within the chunk * @param wcz the z coordinate within the chunk (i.e. 0 - 15) * @return MATCH if the block matches * @return NO_MATCH if the block doesn't match * @return NOT_LOADED if the matcher can't tell because one of the chunks isn't loaded * @return OUT_OF_BOUNDS if the matcher needs to access an adjacent chunk */ public abstract MatchResult matches(Chunk chunk, int wcx, int wcy, int wcz); /** * does this block meet the matcher criteria? (i.e. it should be added to the selection) * to be used if matches(Chunk,...) returns OUT_OF_BOUNDS * @param world * @param wx the world x coordinate * @param wy the world x coordinate * @param wz the world x coordinate * @return MATCH if the block matches * @return NO_MATCH if the block doesn't match * @return NOT_LOADED if the matcher can't tell because one of the chunks isn't loaded */ public MatchResult matches(World world, int wx, int wy, int wz) { // Block block = world.getBlock(wx, wy, wz); Chunk chunk = world.getChunkFromChunkCoords(wx >> 4, wz >> 4); if (chunk.isEmpty()) return MatchResult.NOT_LOADED; return matches(chunk, wx & 0x0f, wy, wz & 0x0f); } public void writeToBuffer(ByteBuf buf) { buf.writeByte(getUniqueID()); } protected abstract byte getUniqueID(); // ----------------------- public static class AnyNonAir extends FillMatcher { public MatchResult matches(Chunk chunk, int wcx, int wcy, int wcz) { if (chunk.isEmpty()) return MatchResult.NOT_LOADED; Block block = chunk.getBlock(wcx, wcy, wcz); return (block != Blocks.air) ? MatchResult.MATCH : MatchResult.NO_MATCH; } protected byte getUniqueID() {return ANY_NON_AIR;} } // ----------------------- public static class AnySolid extends FillMatcher { public MatchResult matches(Chunk chunk, int wcx, int wcy, int wcz) { if (chunk.isEmpty()) return MatchResult.NOT_LOADED; Block block = chunk.getBlock(wcx, wcy, wcz); if (block == Blocks.air) return MatchResult.NO_MATCH; final int NO_INTERACTION = 1; return (block.getMaterial() == Material.water || block.getMobilityFlag() != NO_INTERACTION) ? MatchResult.MATCH : MatchResult.NO_MATCH; } protected byte getUniqueID() {return ANY_SOLID;} } // ----------------------- public static class NullMatcher extends FillMatcher { public MatchResult matches(Chunk chunk, int wcx, int wcy, int wcz) { return null; } protected byte getUniqueID() {return NULL;} } // ----------------------- // matches the specified block only; metadata sensitive except for lava and water materials public static class OnlySpecifiedBlock extends FillMatcher { public OnlySpecifiedBlock(BlockWithMetadata i_blockToMatch) { blockToMatch = i_blockToMatch; } public MatchResult matches(Chunk chunk, int wcx, int wcy, int wcz) { if (chunk.isEmpty()) return MatchResult.NOT_LOADED; Block block = chunk.getBlock(wcx, wcy, wcz); if (block != blockToMatch.block) return MatchResult.NO_MATCH; int metadata = chunk.getBlockMetadata(new BlockPos(wcx, wcy, wcz)); if (metadata == blockToMatch.metaData) return MatchResult.MATCH; if (block.getMaterial() == Material.lava || block.getMaterial() == Material.water) return MatchResult.MATCH; return MatchResult.NO_MATCH; } @Override public void writeToBuffer(ByteBuf buf) { super.writeToBuffer(buf); if (blockToMatch == null) { buf.writeInt(Block.getIdFromBlock(Blocks.air)); buf.writeInt(0); } else { buf.writeInt(Block.getIdFromBlock(blockToMatch.block)); buf.writeInt(blockToMatch.metaData); } } protected byte getUniqueID() {return ONLY_SPECIFIED_BLOCK;} private BlockWithMetadata blockToMatch; } // ----------------------- // follows a contour // additive mode: if true, follows an adjacent contour of solid blocks, stops when the block itself is solid // if false, follows an adjacent "contour" of air blocks, stops when the block itself is air public static class ContourFollower extends FillMatcher { public ContourFollower(boolean i_additiveMode, EnumFacing i_directionToContour) { additiveMode = i_additiveMode; directionToContour = i_directionToContour; } @Override public MatchResult matches(Chunk chunk, int wcx, int wcy, int wcz) { if (chunk.isEmpty()) return MatchResult.NOT_LOADED; final int NO_INTERACTION = 1; // first check if the block itself is suitable based on the additive mode Block block = chunk.getBlock(wcx, wcy, wcz); boolean thisBlockIsSolid = (block != Blocks.air && (block.getMaterial() == Material.water || block.getMobilityFlag() != NO_INTERACTION)); if (thisBlockIsSolid == additiveMode) return MatchResult.NO_MATCH; // next check if the contour is suitable based on the additive mode // wcx += Facing.offsetsXForSide[directionToContour]; // wcy += Facing.offsetsYForSide[directionToContour]; // wcz += Facing.offsetsZForSide[directionToContour]; wcx += directionToContour.getFrontOffsetX(); wcy += directionToContour.getFrontOffsetY(); wcz += directionToContour.getFrontOffsetZ(); if (wcx < 0 || wcx > 15 || wcz < 0 || wcz > 15) return MatchResult.OUT_OF_BOUNDS; final int MINIMUM_Y = 0; final int MAXIMUM_Y = 255; boolean contourBlockIsSolid; if (wcy < MINIMUM_Y || wcy > MAXIMUM_Y) { contourBlockIsSolid = false; } else { block = chunk.getBlock(wcx, wcy, wcz); contourBlockIsSolid = (block != Blocks.air && (block.getMaterial() == Material.water || block.getMobilityFlag() != NO_INTERACTION)); } return (contourBlockIsSolid != additiveMode) ? MatchResult.NO_MATCH : MatchResult.MATCH; } @Override public MatchResult matches(World world, int wx, int wy, int wz) { Chunk chunk = world.getChunkFromBlockCoords(new BlockPos(wx, wy, wz)); if (chunk.isEmpty()) return MatchResult.NOT_LOADED; final int NO_INTERACTION = 1; // first check if the block itself is suitable based on the additive mode Block block = chunk.getBlock(wx & 0x0f, wy, wz & 0x0f); boolean thisBlockIsSolid = (block != Blocks.air && (block.getMaterial() == Material.water || block.getMobilityFlag() != NO_INTERACTION)); if (thisBlockIsSolid == additiveMode) return MatchResult.NO_MATCH; // next check if the contour is suitable based on the additive mode wx += directionToContour.getFrontOffsetX(); wy += directionToContour.getFrontOffsetY(); wz += directionToContour.getFrontOffsetZ(); final int MINIMUM_Y = 0; final int MAXIMUM_Y = 255; boolean contourBlockIsSolid; if (wy < MINIMUM_Y || wy > MAXIMUM_Y) { contourBlockIsSolid = false; } else { chunk = world.getChunkFromBlockCoords(new BlockPos(wx, wy, wz)); if (chunk.isEmpty()) return MatchResult.NOT_LOADED; block = chunk.getBlock(wx & 0x0f, wy, wz & 0x0f); contourBlockIsSolid = (block != Blocks.air && (block.getMaterial() == Material.water || block.getMobilityFlag() != NO_INTERACTION)); } return (contourBlockIsSolid != additiveMode) ? MatchResult.NO_MATCH : MatchResult.MATCH; } @Override public void writeToBuffer(ByteBuf buf) { super.writeToBuffer(buf); buf.writeBoolean(additiveMode); buf.writeInt(directionToContour.getIndex()); } protected byte getUniqueID() {return CONTOUR_FOLLOWER;} // used to be ONLY_SPECIFIED_BLOCK -?!!! private EnumFacing directionToContour; private boolean additiveMode; } static private final byte ANY_NON_AIR = 1; static private final byte ANY_SOLID = 3; static private final byte ONLY_SPECIFIED_BLOCK = 5; static private final byte NULL = 7; static private final byte CONTOUR_FOLLOWER = 9; }