001/* 002 * This file is part of Baritone. 003 * 004 * Baritone is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU Lesser General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * Baritone is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public License 015 * along with Baritone. If not, see <https://www.gnu.org/licenses/>. 016 */ 017 018package baritone.api.utils; 019 020import baritone.api.utils.accessor.IItemStack; 021import com.google.common.collect.ImmutableSet; 022import net.minecraft.block.*; 023import net.minecraft.block.properties.IProperty; 024import net.minecraft.block.state.IBlockState; 025import net.minecraft.item.ItemStack; 026import net.minecraft.util.EnumFacing; 027import net.minecraft.util.ResourceLocation; 028 029import javax.annotation.Nonnull; 030import javax.annotation.Nullable; 031import java.util.*; 032import java.util.function.Consumer; 033import java.util.regex.MatchResult; 034import java.util.regex.Matcher; 035import java.util.regex.Pattern; 036import java.util.stream.Collectors; 037 038public final class BlockOptionalMeta { 039 040 private final Block block; 041 private final int meta; 042 private final boolean noMeta; 043 private final Set<IBlockState> blockstates; 044 private final ImmutableSet<Integer> stateHashes; 045 private final ImmutableSet<Integer> stackHashes; 046 private static final Pattern pattern = Pattern.compile("^(.+?)(?::(\\d+))?$"); 047 private static final Map<Object, Object> normalizations; 048 049 public BlockOptionalMeta(@Nonnull Block block, @Nullable Integer meta) { 050 this.block = block; 051 this.noMeta = meta == null; 052 this.meta = noMeta ? 0 : meta; 053 this.blockstates = getStates(block, meta); 054 this.stateHashes = getStateHashes(blockstates); 055 this.stackHashes = getStackHashes(blockstates); 056 } 057 058 public BlockOptionalMeta(@Nonnull Block block) { 059 this(block, null); 060 } 061 062 public BlockOptionalMeta(@Nonnull String selector) { 063 Matcher matcher = pattern.matcher(selector); 064 065 if (!matcher.find()) { 066 throw new IllegalArgumentException("invalid block selector"); 067 } 068 069 MatchResult matchResult = matcher.toMatchResult(); 070 noMeta = matchResult.group(2) == null; 071 072 ResourceLocation id = new ResourceLocation(matchResult.group(1)); 073 074 if (!Block.REGISTRY.containsKey(id)) { 075 throw new IllegalArgumentException("Invalid block ID"); 076 } 077 078 block = Block.REGISTRY.getObject(id); 079 meta = noMeta ? 0 : Integer.parseInt(matchResult.group(2)); 080 blockstates = getStates(block, getMeta()); 081 stateHashes = getStateHashes(blockstates); 082 stackHashes = getStackHashes(blockstates); 083 } 084 085 static { 086 Map<Object, Object> _normalizations = new HashMap<>(); 087 Consumer<Enum> put = instance -> _normalizations.put(instance.getClass(), instance); 088 put.accept(EnumFacing.NORTH); 089 put.accept(EnumFacing.Axis.Y); 090 put.accept(BlockLog.EnumAxis.Y); 091 put.accept(BlockStairs.EnumHalf.BOTTOM); 092 put.accept(BlockStairs.EnumShape.STRAIGHT); 093 put.accept(BlockLever.EnumOrientation.DOWN_X); 094 put.accept(BlockDoublePlant.EnumBlockHalf.LOWER); 095 put.accept(BlockSlab.EnumBlockHalf.BOTTOM); 096 put.accept(BlockDoor.EnumDoorHalf.LOWER); 097 put.accept(BlockDoor.EnumHingePosition.LEFT); 098 put.accept(BlockBed.EnumPartType.HEAD); 099 put.accept(BlockRailBase.EnumRailDirection.NORTH_SOUTH); 100 put.accept(BlockTrapDoor.DoorHalf.BOTTOM); 101 _normalizations.put(BlockBanner.ROTATION, 0); 102 _normalizations.put(BlockBed.OCCUPIED, false); 103 _normalizations.put(BlockBrewingStand.HAS_BOTTLE[0], false); 104 _normalizations.put(BlockBrewingStand.HAS_BOTTLE[1], false); 105 _normalizations.put(BlockBrewingStand.HAS_BOTTLE[2], false); 106 _normalizations.put(BlockButton.POWERED, false); 107 // _normalizations.put(BlockCactus.AGE, 0); 108 // _normalizations.put(BlockCauldron.LEVEL, 0); 109 // _normalizations.put(BlockChorusFlower.AGE, 0); 110 _normalizations.put(BlockChorusPlant.NORTH, false); 111 _normalizations.put(BlockChorusPlant.EAST, false); 112 _normalizations.put(BlockChorusPlant.SOUTH, false); 113 _normalizations.put(BlockChorusPlant.WEST, false); 114 _normalizations.put(BlockChorusPlant.UP, false); 115 _normalizations.put(BlockChorusPlant.DOWN, false); 116 // _normalizations.put(BlockCocoa.AGE, 0); 117 // _normalizations.put(BlockCrops.AGE, 0); 118 _normalizations.put(BlockDirt.SNOWY, false); 119 _normalizations.put(BlockDoor.OPEN, false); 120 _normalizations.put(BlockDoor.POWERED, false); 121 // _normalizations.put(BlockFarmland.MOISTURE, 0); 122 _normalizations.put(BlockFence.NORTH, false); 123 _normalizations.put(BlockFence.EAST, false); 124 _normalizations.put(BlockFence.WEST, false); 125 _normalizations.put(BlockFence.SOUTH, false); 126 // _normalizations.put(BlockFenceGate.POWERED, false); 127 // _normalizations.put(BlockFenceGate.IN_WALL, false); 128 _normalizations.put(BlockFire.AGE, 0); 129 _normalizations.put(BlockFire.NORTH, false); 130 _normalizations.put(BlockFire.EAST, false); 131 _normalizations.put(BlockFire.SOUTH, false); 132 _normalizations.put(BlockFire.WEST, false); 133 _normalizations.put(BlockFire.UPPER, false); 134 // _normalizations.put(BlockFrostedIce.AGE, 0); 135 _normalizations.put(BlockGrass.SNOWY, false); 136 // _normalizations.put(BlockHopper.ENABLED, true); 137 // _normalizations.put(BlockLever.POWERED, false); 138 // _normalizations.put(BlockLiquid.LEVEL, 0); 139 // _normalizations.put(BlockMycelium.SNOWY, false); 140 // _normalizations.put(BlockNetherWart.AGE, false); 141 _normalizations.put(BlockLeaves.CHECK_DECAY, false); 142 // _normalizations.put(BlockLeaves.DECAYABLE, false); 143 // _normalizations.put(BlockObserver.POWERED, false); 144 _normalizations.put(BlockPane.NORTH, false); 145 _normalizations.put(BlockPane.EAST, false); 146 _normalizations.put(BlockPane.WEST, false); 147 _normalizations.put(BlockPane.SOUTH, false); 148 // _normalizations.put(BlockPistonBase.EXTENDED, false); 149 // _normalizations.put(BlockPressurePlate.POWERED, false); 150 // _normalizations.put(BlockPressurePlateWeighted.POWER, false); 151 _normalizations.put(BlockQuartz.EnumType.LINES_X, BlockQuartz.EnumType.LINES_Y); 152 _normalizations.put(BlockQuartz.EnumType.LINES_Z, BlockQuartz.EnumType.LINES_Y); 153 // _normalizations.put(BlockRailDetector.POWERED, false); 154 // _normalizations.put(BlockRailPowered.POWERED, false); 155 _normalizations.put(BlockRedstoneWire.NORTH, false); 156 _normalizations.put(BlockRedstoneWire.EAST, false); 157 _normalizations.put(BlockRedstoneWire.SOUTH, false); 158 _normalizations.put(BlockRedstoneWire.WEST, false); 159 // _normalizations.put(BlockReed.AGE, false); 160 _normalizations.put(BlockSapling.STAGE, 0); 161 _normalizations.put(BlockSkull.NODROP, false); 162 _normalizations.put(BlockStandingSign.ROTATION, 0); 163 _normalizations.put(BlockStem.AGE, 0); 164 _normalizations.put(BlockTripWire.NORTH, false); 165 _normalizations.put(BlockTripWire.EAST, false); 166 _normalizations.put(BlockTripWire.WEST, false); 167 _normalizations.put(BlockTripWire.SOUTH, false); 168 _normalizations.put(BlockVine.NORTH, false); 169 _normalizations.put(BlockVine.EAST, false); 170 _normalizations.put(BlockVine.SOUTH, false); 171 _normalizations.put(BlockVine.WEST, false); 172 _normalizations.put(BlockVine.UP, false); 173 _normalizations.put(BlockWall.UP, false); 174 _normalizations.put(BlockWall.NORTH, false); 175 _normalizations.put(BlockWall.EAST, false); 176 _normalizations.put(BlockWall.WEST, false); 177 _normalizations.put(BlockWall.SOUTH, false); 178 normalizations = Collections.unmodifiableMap(_normalizations); 179 } 180 181 public static <C extends Comparable<C>, P extends IProperty<C>> P castToIProperty(Object value) { 182 //noinspection unchecked 183 return (P) value; 184 } 185 186 public static <C extends Comparable<C>, P extends IProperty<C>> C castToIPropertyValue(P iproperty, Object value) { 187 //noinspection unchecked 188 return (C) value; 189 } 190 191 /** 192 * Normalizes the specified blockstate by setting meta-affecting properties which 193 * are not being targeted by the meta parameter to their default values. 194 * <p> 195 * For example, block variant/color is the primary target for the meta value, so properties 196 * such as rotation/facing direction will be set to default values in order to nullify 197 * the effect that they have on the state's meta value. 198 * 199 * @param state The state to normalize 200 * @return The normalized block state 201 */ 202 public static IBlockState normalize(IBlockState state) { 203 IBlockState newState = state; 204 205 for (IProperty<?> property : state.getProperties().keySet()) { 206 Class<?> valueClass = property.getValueClass(); 207 if (normalizations.containsKey(property)) { 208 try { 209 newState = newState.withProperty( 210 castToIProperty(property), 211 castToIPropertyValue(property, normalizations.get(property)) 212 ); 213 } catch (IllegalArgumentException ignored) {} 214 } else if (normalizations.containsKey(state.getValue(property))) { 215 try { 216 newState = newState.withProperty( 217 castToIProperty(property), 218 castToIPropertyValue(property, normalizations.get(state.getValue(property))) 219 ); 220 } catch (IllegalArgumentException ignored) {} 221 } else if (normalizations.containsKey(valueClass)) { 222 try { 223 newState = newState.withProperty( 224 castToIProperty(property), 225 castToIPropertyValue(property, normalizations.get(valueClass)) 226 ); 227 } catch (IllegalArgumentException ignored) {} 228 } 229 } 230 231 return newState; 232 } 233 234 /** 235 * Evaluate the target meta value for the specified state. The target meta value is 236 * most often that which is influenced by the variant/color property of the block state. 237 * 238 * @param state The state to check 239 * @return The target meta of the state 240 * @see #normalize(IBlockState) 241 */ 242 public static int stateMeta(IBlockState state) { 243 return state.getBlock().getMetaFromState(normalize(state)); 244 } 245 246 private static Set<IBlockState> getStates(@Nonnull Block block, @Nullable Integer meta) { 247 return block.getBlockState().getValidStates().stream() 248 .filter(blockstate -> meta == null || stateMeta(blockstate) == meta) 249 .collect(Collectors.toSet()); 250 } 251 252 private static ImmutableSet<Integer> getStateHashes(Set<IBlockState> blockstates) { 253 return ImmutableSet.copyOf( 254 blockstates.stream() 255 .map(IBlockState::hashCode) 256 .toArray(Integer[]::new) 257 ); 258 } 259 260 private static ImmutableSet<Integer> getStackHashes(Set<IBlockState> blockstates) { 261 //noinspection ConstantConditions 262 return ImmutableSet.copyOf( 263 blockstates.stream() 264 .map(state -> new ItemStack( 265 state.getBlock().getItemDropped(state, new Random(), 0), 266 state.getBlock().damageDropped(state) 267 )) 268 .map(stack -> ((IItemStack) (Object) stack).getBaritoneHash()) 269 .toArray(Integer[]::new) 270 ); 271 } 272 273 public Block getBlock() { 274 return block; 275 } 276 277 public Integer getMeta() { 278 return noMeta ? null : meta; 279 } 280 281 public boolean matches(@Nonnull Block block) { 282 return block == this.block; 283 } 284 285 public boolean matches(@Nonnull IBlockState blockstate) { 286 Block block = blockstate.getBlock(); 287 return block == this.block && stateHashes.contains(blockstate.hashCode()); 288 } 289 290 public boolean matches(ItemStack stack) { 291 //noinspection ConstantConditions 292 int hash = ((IItemStack) (Object) stack).getBaritoneHash(); 293 294 if (noMeta) { 295 hash -= stack.getItemDamage(); 296 } 297 298 return stackHashes.contains(hash); 299 } 300 301 @Override 302 public String toString() { 303 return String.format("BlockOptionalMeta{block=%s,meta=%s}", block, getMeta()); 304 } 305 306 public static IBlockState blockStateFromStack(ItemStack stack) { 307 //noinspection deprecation 308 return Block.getBlockFromItem(stack.getItem()).getStateFromMeta(stack.getMetadata()); 309 } 310 311 public IBlockState getAnyBlockState() { 312 if (blockstates.size() > 0) { 313 return blockstates.iterator().next(); 314 } 315 316 return null; 317 } 318}