Skip to content

Commit

Permalink
Added a new facade config. (AppliedEnergistics#3706)
Browse files Browse the repository at this point in the history
The whitelist is now based on manually add explicit entries instead of
automatically adding every existing block. This alone should save a few
MB of RAM depending on the modpack size.

Additionally the FacadeConfig is no longer extending the normal
Configuration provided by Forge. It will only parse the file once per
start and store the settings in an optimised collection to further
reduce memory consumption.

Any full block without a TileEntity and having a model is whitelisted
automatically and does not need an explicit entry anymore.

By default vanilla and AE2 glass is added as they are no fullblocks
technically.

The whitelist technically allows many more blocks now. E.g. stairs,
slabs or even items like saplings. But with various rendering quality.

Further there is an option to allow facades based on TileEntities, this
is completely unsupported and can work or corrupt the whole save.
  • Loading branch information
yueh committed Sep 2, 2018
1 parent 7ba2893 commit b1ac0ad
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 55 deletions.
148 changes: 111 additions & 37 deletions src/main/java/appeng/core/FacadeConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,69 +20,143 @@


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<ResourceLocation> whiteList;

public FacadeConfig( final File facadeFile )
private FacadeConfig( boolean allowTileEntityFacades, Object2IntMap<ResourceLocation> 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 ) );

public static FacadeConfig instance()
{
return instance;
}
final boolean allowTileEntityFacades = configurartion
.get( CONFIG_COMMON_KEY, CONFIG_COMMON_ALLOW_TILEENTITIES_KEY, false, CONFIG_COMMON_ALLOW_TILEENTITIES_COMMENT )
.setRequiresMcRestart( true )
.setShowInGui( false )
.getBoolean();

public boolean checkEnabled( final Block id, final int metadata, final boolean automatic )
{
if( id == null )
final Object2IntMap<ResourceLocation> configWhiteList = new Object2IntArrayMap<>();

final Set<ConfigCategory> whitelist = configurartion.getCategory( CONFIG_FACADES_KEY ).getChildren();
for( ConfigCategory configCategory : whitelist )
{
final String domain = configCategory.getName();
final Map<String, Property> values = configCategory.getValues();

for( Entry<String, Property> entry : values.entrySet() )
{
configWhiteList.put( new ResourceLocation( domain, entry.getKey() ), entry.getValue().getInt() );
}
}

if( configurartion.hasChanged() )
{
return false;
configurartion.save();
}

final ResourceLocation blk = Item.REGISTRY.getNameForObject( Item.getItemFromBlock( id ) );
if( blk == null )
instance = new FacadeConfig( allowTileEntityFacades, configWhiteList );
}

private static Configuration migrate( Configuration configurartion )
{
// Clear pre rv6 configs.
if( configurartion.getLoadedConfigVersion() == null )
{
for( final Field f : Block.class.getFields() )
for( String category : configurartion.getCategoryNames() )
{
try
{
if( f.get( Block.class ) == id )
{
return this.get( "minecraft", f.getName() + ( metadata == 0 ? "" : "/" + metadata ), automatic ).getBoolean( automatic );
}
}
catch( final Throwable e )
{
// :P
}
final ConfigCategory c = configurartion.getCategory( category );
configurartion.removeCategory( c );
}
}
else

// 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()
{
return instance;
}

public boolean allowTileEntityFacades()
{
return this.allowTileEntityFacades;
}

public boolean isWhiteListed( final Block block, final int metadata )
{
final Integer entry = this.whiteList.get( block.getRegistryName() );

if( entry != null )
{
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;
Expand Down
40 changes: 22 additions & 18 deletions src/main/java/appeng/items/parts/ItemFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,6 @@ private void calculateSubTypes()
// just absorb..
}
}

if( FacadeConfig.instance().hasChanged() )
{
FacadeConfig.instance().save();
}
}
}

Expand All @@ -148,47 +143,56 @@ private static boolean hasSimpleModel( IBlockState blockState )
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;
}
Expand Down

0 comments on commit b1ac0ad

Please sign in to comment.