diff --git a/src/main/java/appeng/core/FacadeConfig.java b/src/main/java/appeng/core/FacadeConfig.java index b45d0bd31..fe1212e83 100644 --- a/src/main/java/appeng/core/FacadeConfig.java +++ b/src/main/java/appeng/core/FacadeConfig.java @@ -20,32 +20,124 @@ package appeng.core; import java.io.File; -import java.lang.reflect.Field; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import net.minecraft.block.Block; -import net.minecraft.item.Item; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; +import net.minecraftforge.common.config.Property.Type; + +import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; -public class FacadeConfig extends Configuration +public class FacadeConfig { + private static final String CONFIG_VERSION = "1"; + private static final String CONFIG_COMMON_KEY = "common"; + private static final String CONFIG_COMMON_COMMENT = "Settings applied to all facades.\n\n" // + + "By default full blocks with no tile entity and a model do not need whitelisting.\n"// + + "This will only be read once during client startup."; + private static final String CONFIG_COMMON_ALLOW_TILEENTITIES_KEY = "allowTileEntityFacades"; + private static final String CONFIG_COMMON_ALLOW_TILEENTITIES_COMMENT = "Unsupported: Allows whitelisting TileEntity as facades. Could work, have render issues, or corrupt your world. USE AT YOUR OWN RISK."; + private static final String CONFIG_FACADES_KEY = "facades"; + private static final String CONFIG_FACADES_COMMENT = "A way to explicitly handle certain blocks as facades.\n\n" // + + "Blocks can be added by their resource location under the following rules.\n" // + + " - One category per domain like minecraft or appliedenergistics2\n" // + + " - One key per id. E.g. glass in case of minecraft:glass\n" // + + " - An integer value ranging from 0 to 16 representing the metadata 0-15 and 16 as wildcard for all" // + + " - Multiple entries for the same id but different metadata are possible when needed"; + private static FacadeConfig instance; - private final Pattern replacementPattern; + private final boolean allowTileEntityFacades; + private final Object2IntMap whiteList; - public FacadeConfig( final File facadeFile ) + private FacadeConfig( boolean allowTileEntityFacades, Object2IntMap whiteList ) { - super( facadeFile ); - this.replacementPattern = Pattern.compile( "[^a-zA-Z0-9]" ); + this.allowTileEntityFacades = allowTileEntityFacades; + this.whiteList = whiteList; } + /** + * Creates a custom confuration based on a {@link Configuration}, but ultimately throws it away after reading it + * once to save a couple MB of memory. + * + * @param configFile + */ public static void init( final File configFile ) { - instance = new FacadeConfig( configFile ); + final Configuration configurartion = migrate( new Configuration( configFile, CONFIG_VERSION ) ); + + final boolean allowTileEntityFacades = configurartion + .get( CONFIG_COMMON_KEY, CONFIG_COMMON_ALLOW_TILEENTITIES_KEY, false, CONFIG_COMMON_ALLOW_TILEENTITIES_COMMENT ) + .setRequiresMcRestart( true ) + .setShowInGui( false ) + .getBoolean(); + + final Object2IntMap configWhiteList = new Object2IntArrayMap<>(); + + final Set whitelist = configurartion.getCategory( CONFIG_FACADES_KEY ).getChildren(); + for( ConfigCategory configCategory : whitelist ) + { + final String domain = configCategory.getName(); + final Map values = configCategory.getValues(); + + for( Entry entry : values.entrySet() ) + { + configWhiteList.put( new ResourceLocation( domain, entry.getKey() ), entry.getValue().getInt() ); + } + } + + if( configurartion.hasChanged() ) + { + configurartion.save(); + } + + instance = new FacadeConfig( allowTileEntityFacades, configWhiteList ); + } + + private static Configuration migrate( Configuration configurartion ) + { + // Clear pre rv6 configs. + if( configurartion.getLoadedConfigVersion() == null ) + { + for( String category : configurartion.getCategoryNames() ) + { + final ConfigCategory c = configurartion.getCategory( category ); + configurartion.removeCategory( c ); + } + } + + // Create general category, if missing + if( !configurartion.hasCategory( CONFIG_COMMON_KEY ) ) + { + configurartion.getCategory( CONFIG_COMMON_KEY ).setComment( CONFIG_COMMON_COMMENT ); + } + + // Create whitelist, if missing + if( !configurartion.hasCategory( CONFIG_FACADES_KEY ) ) + { + final ConfigCategory category = configurartion.getCategory( CONFIG_FACADES_KEY ); + category.setComment( CONFIG_FACADES_COMMENT ); + + // Whitelist some vanilla blocks like glass + final ConfigCategory minecraft = new ConfigCategory( "minecraft", category ); + minecraft.put( "glass", new Property( "glass", "16", Type.INTEGER ) ); + minecraft.put( "stained_glass", new Property( "stained_glass", "16", Type.INTEGER ) ); + + // Whitelist some AE2 blocks like quartz glass + final ConfigCategory appliedenergistics = new ConfigCategory( "appliedenergistics2", category ); + appliedenergistics.put( "quartz_glass", new Property( "quartz_glass", "16", Type.INTEGER ) ); + appliedenergistics.put( "quartz_vibrant_glass", new Property( "quartz_vibrant_glass", "16", Type.INTEGER ) ); + } + + return configurartion; } public static FacadeConfig instance() @@ -53,36 +145,18 @@ public class FacadeConfig extends Configuration return instance; } - public boolean checkEnabled( final Block id, final int metadata, final boolean automatic ) + public boolean allowTileEntityFacades() { - if( id == null ) - { - return false; - } + return this.allowTileEntityFacades; + } - final ResourceLocation blk = Item.REGISTRY.getNameForObject( Item.getItemFromBlock( id ) ); - if( blk == null ) + public boolean isWhiteListed( final Block block, final int metadata ) + { + final Integer entry = this.whiteList.get( block.getRegistryName() ); + + if( entry != null ) { - for( final Field f : Block.class.getFields() ) - { - try - { - if( f.get( Block.class ) == id ) - { - return this.get( "minecraft", f.getName() + ( metadata == 0 ? "" : "/" + metadata ), automatic ).getBoolean( automatic ); - } - } - catch( final Throwable e ) - { - // :P - } - } - } - else - { - final Matcher mod = this.replacementPattern.matcher( blk.getResourceDomain() ); - final Matcher name = this.replacementPattern.matcher( blk.getResourcePath() ); - return this.get( mod.replaceAll( "" ), name.replaceAll( "" ) + ( metadata == 0 ? "" : "." + metadata ), automatic ).getBoolean( automatic ); + return entry == metadata || entry == 16; } return false; diff --git a/src/main/java/appeng/items/parts/ItemFacade.java b/src/main/java/appeng/items/parts/ItemFacade.java index 2da863214..70f29cc4e 100644 --- a/src/main/java/appeng/items/parts/ItemFacade.java +++ b/src/main/java/appeng/items/parts/ItemFacade.java @@ -130,11 +130,6 @@ public class ItemFacade extends AEBaseItem implements IFacadeItem, IAlphaPassIte // just absorb.. } } - - if( FacadeConfig.instance().hasChanged() ) - { - FacadeConfig.instance().save(); - } } } @@ -148,47 +143,56 @@ public class ItemFacade extends AEBaseItem implements IFacadeItem, IAlphaPassIte return blockState.isFullCube(); } - public ItemStack createFacadeForItem( final ItemStack l, final boolean returnItem ) + public ItemStack createFacadeForItem( final ItemStack itemStack, final boolean returnItem ) { - if( l.isEmpty() ) + if( itemStack.isEmpty() ) { return ItemStack.EMPTY; } - final Block b = Block.getBlockFromItem( l.getItem() ); - if( b == Blocks.AIR || l.hasTagCompound() ) + final Block block = Block.getBlockFromItem( itemStack.getItem() ); + if( block == Blocks.AIR || itemStack.hasTagCompound() ) { return ItemStack.EMPTY; } - final int metadata = l.getItem().getMetadata( l.getItemDamage() ); - - final boolean hasTile = b.hasTileEntity( b.getDefaultState() ); + final int metadata = itemStack.getItem().getMetadata( itemStack.getItemDamage() ); // Try to get the block state based on the item stack's meta. If this fails, don't consider it for a facade // This for example fails for Pistons because they hardcoded an invalid meta value in vanilla IBlockState blockState; try { - blockState = b.getStateFromMeta( metadata ); + blockState = block.getStateFromMeta( metadata ); } catch( Exception e ) { - AELog.debug( e, "Cannot create a facade for " + b.getRegistryName() ); + AELog.debug( e, "Cannot create a facade for " + block.getRegistryName() ); return ItemStack.EMPTY; } - if( blockState.getRenderType() == EnumBlockRenderType.MODEL && !hasTile ) + final boolean areTileEntitiesEnabled = FacadeConfig.instance().allowTileEntityFacades(); + final boolean isWhiteListed = FacadeConfig.instance().isWhiteListed( block, metadata ); + final boolean isModel = blockState.getRenderType() == EnumBlockRenderType.MODEL; + + final IBlockState defaultState = block.getDefaultState(); + final boolean isTileEntity = block.hasTileEntity( defaultState ); + final boolean isFullCube = block.isFullCube( defaultState ); + + final boolean isTileEntityAllowed = !isTileEntity || ( areTileEntitiesEnabled && isWhiteListed ); + final boolean isBlockAllowed = isFullCube || isWhiteListed; + + if( isModel && isTileEntityAllowed && isBlockAllowed ) { if( returnItem ) { - return l; + return itemStack; } final ItemStack is = new ItemStack( this ); final NBTTagCompound data = new NBTTagCompound(); - data.setString( TAG_ITEM_ID, l.getItem().getRegistryName().toString() ); - data.setInteger( TAG_DAMAGE, l.getItemDamage() ); + data.setString( TAG_ITEM_ID, itemStack.getItem().getRegistryName().toString() ); + data.setInteger( TAG_DAMAGE, itemStack.getItemDamage() ); is.setTagCompound( data ); return is; }