Closes #1899, Fixed #1898: Adds an easy way to export interesting information into CSV format

Mostly used for the recipe system, but can also be used for debugging purposes. Debug options needs to be ticked to use the full information gain. Recipes only require the normal localization and the specific name plus metadata.

Shifted the recipes into a recipes folder where the CSV will also reside. This will also elevate the copying of the readme to the user directory since it can reside in the recipes folder.

Fixed a bug where the copier would copy the would also copy empty folders
This commit is contained in:
thatsIch 2015-08-20 19:15:52 +02:00 committed by yueh
parent 7dee5699cf
commit 888b3e5600
36 changed files with 1483 additions and 274 deletions

View File

@ -31,7 +31,7 @@ invtweaks_version=1.59
#########################################################
fmp_version=1.2.0.345
code_chicken_lib_version=1.1.3.138
code_chicken_core_version=1.0.7.46
code_chicken_core_version=1.0.7.47
nei_version=1.0.5.111
bc_version=7.0.9
opencomputers_version=1.5.18.39

View File

@ -135,5 +135,5 @@ dependencies {
// compile(group: 'api', name: 'railcraft', version: "${api_railcraft_version}")
// compile(group: 'api', name: 'rf', version: "${api_rf_version}")
testCompile "junit:junit:4.11"
testCompile "junit:junit:4.12"
}

View File

@ -49,6 +49,6 @@ public class ItemCraftingStorage extends AEBaseItemBlock
@Override
public boolean hasContainerItem( final ItemStack stack )
{
return AEConfig.instance.isFeatureEnabled( AEFeature.enableDisassemblyCrafting );
return AEConfig.instance.isFeatureEnabled( AEFeature.EnableDisassemblyCrafting );
}
}

View File

@ -169,7 +169,7 @@ public final class AEConfig extends Configuration implements IConfigurableObject
for( final AEFeature feature : AEFeature.values() )
{
if( feature.isVisible )
if( feature.isVisible() )
{
if( this.get( "Features." + feature.category, feature.name(), feature.defaultValue ).getBoolean( feature.defaultValue ) )
{

View File

@ -109,4 +109,12 @@ public final class AELog
log( Level.INFO, format, data );
}
}
public static void debug( String format, Object... data )
{
if( AEConfig.instance.isFeatureEnabled( AEFeature.DebugLogging ) )
{
log( Level.DEBUG, format, data );
}
}
}

View File

@ -26,6 +26,7 @@ import javax.annotation.Nonnull;
import com.google.common.base.Stopwatch;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod;
@ -48,8 +49,13 @@ import appeng.core.sync.network.NetworkHandler;
import appeng.core.worlddata.WorldData;
import appeng.hooks.TickHandler;
import appeng.integration.IntegrationRegistry;
import appeng.recipes.CustomRecipeConfig;
import appeng.recipes.CustomRecipeForgeConfiguration;
import appeng.server.AECommand;
import appeng.services.VersionChecker;
import appeng.services.export.ExportConfig;
import appeng.services.export.ExportProcess;
import appeng.services.export.ForgeExportConfig;
import appeng.services.version.VersionCheckerConfig;
import appeng.util.Platform;
@ -77,15 +83,28 @@ public final class AppEng
@Nonnull
private static final AppEng INSTANCE = new AppEng();
private final IMCHandler imcHandler;
private final Registration registration;
private File configDirectory;
private CustomRecipeConfig customRecipeConfig;
/**
* Folder for recipes
*
* used for CSV item names and the recipes
*/
private File recipeDirectory;
/**
* determined in pre-init but used in init
*/
private ExportConfig exportConfig;
private AppEng()
{
this.imcHandler = new IMCHandler();
FMLCommonHandler.instance().registerCrashCallable( new ModCrashEnhancement( CrashInfo.MOD_VERSION ) );
this.registration = new Registration();
}
@Nonnull
@ -95,9 +114,10 @@ public final class AppEng
return INSTANCE;
}
public final File getConfigDirectory()
@Nonnull
public final Registration getRegistration()
{
return this.configDirectory;
return this.registration;
}
@EventHandler
@ -110,14 +130,19 @@ public final class AppEng
final Stopwatch watch = Stopwatch.createStarted();
this.configDirectory = new File( event.getModConfigurationDirectory().getPath(), "AppliedEnergistics2" );
this.recipeDirectory = new File( this.configDirectory, "recipes" );
final File configFile = new File( this.configDirectory, "AppliedEnergistics2.cfg" );
final File facadeFile = new File( this.configDirectory, "Facades.cfg" );
final File versionFile = new File( this.configDirectory, "VersionChecker.cfg" );
final File recipeFile = new File( this.configDirectory, "CustomRecipes.cfg" );
final Configuration recipeConfiguration = new Configuration( recipeFile );
AEConfig.instance = new AEConfig( configFile );
FacadeConfig.instance = new FacadeConfig( facadeFile );
final VersionCheckerConfig versionCheckerConfig = new VersionCheckerConfig( versionFile );
this.customRecipeConfig = new CustomRecipeForgeConfiguration( recipeConfiguration );
this.exportConfig = new ForgeExportConfig( recipeConfiguration );
AELog.info( "Pre Initialization ( started )" );
@ -132,9 +157,9 @@ public final class AppEng
CommonHelper.proxy.preinit();
}
Registration.INSTANCE.preInitialize( event );
this.registration.preInitialize( event );
if( versionCheckerConfig.isEnabled() )
if( versionCheckerConfig.isVersionCheckingEnabled() )
{
final VersionChecker versionChecker = new VersionChecker( versionCheckerConfig );
final Thread versionCheckerThread = new Thread( versionChecker );
@ -157,23 +182,31 @@ public final class AppEng
@EventHandler
private void init( final FMLInitializationEvent event )
{
final Stopwatch star = Stopwatch.createStarted();
final Stopwatch start = Stopwatch.createStarted();
AELog.info( "Initialization ( started )" );
Registration.INSTANCE.initialize( event );
if( exportConfig.isExportingItemNamesEnabled() )
{
final ExportProcess process = new ExportProcess( this.recipeDirectory, exportConfig );
final Thread exportProcessThread = new Thread( process );
this.startService( "AE2 CSV Export", exportProcessThread );
}
this.registration.initialize( event, this.recipeDirectory, this.customRecipeConfig );
IntegrationRegistry.INSTANCE.init();
CommonHelper.proxy.init();
AELog.info( "Initialization ( ended after " + star.elapsed( TimeUnit.MILLISECONDS ) + "ms )" );
AELog.info( "Initialization ( ended after " + start.elapsed( TimeUnit.MILLISECONDS ) + "ms )" );
}
@EventHandler
private void postInit( final FMLPostInitializationEvent event )
{
final Stopwatch star = Stopwatch.createStarted();
final Stopwatch start = Stopwatch.createStarted();
AELog.info( "Post Initialization ( started )" );
Registration.INSTANCE.postInit( event );
this.registration.postInit( event );
IntegrationRegistry.INSTANCE.postInit();
FMLCommonHandler.instance().registerCrashCallable( new IntegrationCrashEnhancement() );
@ -183,19 +216,15 @@ public final class AppEng
NetworkRegistry.INSTANCE.registerGuiHandler( this, GuiBridge.GUI_Handler );
NetworkHandler.instance = new NetworkHandler( "AE2" );
AELog.info( "Post Initialization ( ended after " + star.elapsed( TimeUnit.MILLISECONDS ) + "ms )" );
// System.out.println( "REGISTRY:" );
// for( Block block : GameData.getBlockRegistry().typeSafeIterable() )
// {
// System.out.println( "block = " + block + ", " + block.getUnlocalizedName() + ", " + block.getLocalizedName() );
// }
AELog.info( "Post Initialization ( ended after " + start.elapsed( TimeUnit.MILLISECONDS ) + "ms )" );
}
@EventHandler
private void handleIMCEvent( final FMLInterModComms.IMCEvent event )
{
this.imcHandler.handleIMCEvent( event );
final IMCHandler imcHandler = new IMCHandler();
imcHandler.handleIMCEvent( event );
}
@EventHandler

View File

@ -22,12 +22,12 @@ package appeng.core;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import javax.annotation.Nonnull;
import org.apache.commons.io.FileUtils;
import appeng.api.recipes.IRecipeHandler;
import appeng.recipes.CustomRecipeConfig;
import appeng.recipes.loader.ConfigLoader;
import appeng.recipes.loader.JarLoader;
import appeng.recipes.loader.RecipeResourceCopier;
@ -44,57 +44,74 @@ import com.google.common.base.Preconditions;
*/
public class RecipeLoader implements Runnable
{
/**
* recipe path in the jar
*/
private static final String ASSETS_RECIPE_PATH = "/assets/appliedenergistics2/recipes/";
@Nonnull
private final IRecipeHandler handler;
@Nonnull
private final CustomRecipeConfig config;
@Nonnull
private final File recipeDirectory;
/**
* @param config configuration for the knowledge how to handle the loading process
* @param handler handler to load the recipes
*
* @throws NullPointerException if handler is <tt>null</tt>
*/
public RecipeLoader( @Nonnull final IRecipeHandler handler )
public RecipeLoader( @Nonnull final File recipeDirectory, @Nonnull final CustomRecipeConfig config, @Nonnull final IRecipeHandler handler )
{
Preconditions.checkNotNull( handler );
this.handler = handler;
this.recipeDirectory = Preconditions.checkNotNull( recipeDirectory );
Preconditions.checkArgument( !recipeDirectory.isFile() );
this.config = Preconditions.checkNotNull( config );
this.handler = Preconditions.checkNotNull( handler );
}
@Override
public void run()
public final void run()
{
// setup copying
final RecipeResourceCopier copier = new RecipeResourceCopier( "assets/appliedenergistics2/recipes/" );
final File configDirectory = AppEng.instance().getConfigDirectory();
final File generatedRecipesDir = new File( configDirectory, "generated-recipes" );
final File userRecipesDir = new File( configDirectory, "user-recipes" );
final File readmeGenDest = new File( generatedRecipesDir, "README.html" );
final File readmeUserDest = new File( userRecipesDir, "README.html" );
// generates generated and user recipes dir
// will clean the generated every time to keep it up to date
// copies over the recipes in the jar over to the generated folder
// copies over the readmes
try
if( this.config.isEnabled() )
{
FileUtils.forceMkdir( generatedRecipesDir );
FileUtils.forceMkdir( userRecipesDir );
FileUtils.cleanDirectory( generatedRecipesDir );
// setup copying
final RecipeResourceCopier copier = new RecipeResourceCopier( "assets/appliedenergistics2/recipes/" );
copier.copyTo( generatedRecipesDir );
FileUtils.copyFile( readmeGenDest, readmeUserDest );
final File generatedRecipesDir = new File( this.recipeDirectory, "generated" );
final File userRecipesDir = new File( this.recipeDirectory, "user" );
// parse recipes prioritising the user scripts by using the generated as template
this.handler.parseRecipes( new ConfigLoader( generatedRecipesDir, userRecipesDir ), "index.recipe" );
// generates generated and user recipes dir
// will clean the generated every time to keep it up to date
// copies over the recipes in the jar over to the generated folder
// copies over the readmes
try
{
FileUtils.forceMkdir( generatedRecipesDir );
FileUtils.forceMkdir( userRecipesDir );
FileUtils.cleanDirectory( generatedRecipesDir );
copier.copyTo( ".recipe", generatedRecipesDir );
copier.copyTo( ".html", recipeDirectory );
// parse recipes prioritising the user scripts by using the generated as template
this.handler.parseRecipes( new ConfigLoader( generatedRecipesDir, userRecipesDir ), "index.recipe" );
}
// on failure use jar parsing
catch( final IOException e )
{
AELog.error( e );
this.handler.parseRecipes( new JarLoader( ASSETS_RECIPE_PATH ), "index.recipe" );
}
catch( final URISyntaxException e )
{
AELog.error( e );
this.handler.parseRecipes( new JarLoader( ASSETS_RECIPE_PATH ), "index.recipe" );
}
}
// on failure use jar parsing
catch( final IOException e )
else
{
AELog.error( e );
this.handler.parseRecipes( new JarLoader( "/assets/appliedenergistics2/recipes/" ), "index.recipe" );
}
catch( final URISyntaxException e )
{
AELog.error( e );
this.handler.parseRecipes( new JarLoader( "/assets/appliedenergistics2/recipes/" ), "index.recipe" );
this.handler.parseRecipes( new JarLoader( ASSETS_RECIPE_PATH ), "index.recipe" );
}
}
}

View File

@ -19,6 +19,12 @@
package appeng.core;
import java.io.File;
import javax.annotation.Nonnull;
import com.google.common.base.Preconditions;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@ -80,6 +86,7 @@ import appeng.me.cache.TickManagerCache;
import appeng.me.storage.AEExternalHandler;
import appeng.parts.PartPlacement;
import appeng.recipes.AEItemResolver;
import appeng.recipes.CustomRecipeConfig;
import appeng.recipes.RecipeHandler;
import appeng.recipes.game.DisassembleRecipe;
import appeng.recipes.game.FacadeRecipe;
@ -109,16 +116,19 @@ import appeng.worldgen.QuartzWorldGen;
public final class Registration
{
public static final Registration INSTANCE = new Registration();
private final RecipeHandler recipeHandler;
public BiomeGenBase storageBiome;
private BiomeGenBase storageBiome;
private Registration()
Registration()
{
this.recipeHandler = new RecipeHandler();
}
public BiomeGenBase getStorageBiome()
{
return this.storageBiome;
}
public void preInitialize( final FMLPreInitializationEvent event )
{
this.registerSpatial( false );
@ -138,7 +148,7 @@ public final class Registration
// Register all detected handlers and features (items, blocks) in pre-init
for( final IFeatureHandler handler : definitions.getFeatureHandlerRegistry().getRegisteredFeatureHandlers() )
{
handler.register(event.getSide() );
handler.register( event.getSide() );
}
for( final IAEFeature feature : definitions.getFeatureRegistry().getRegisteredFeatures() )
@ -215,8 +225,13 @@ public final class Registration
registry.addNewCraftHandler( "shapeless", Shapeless.class );
}
public void initialize( final FMLInitializationEvent event )
public void initialize( @Nonnull final FMLInitializationEvent event, @Nonnull final File recipeDirectory, @Nonnull final CustomRecipeConfig customRecipeConfig )
{
Preconditions.checkNotNull( event );
Preconditions.checkNotNull( recipeDirectory );
Preconditions.checkArgument( !recipeDirectory.isFile() );
Preconditions.checkNotNull( customRecipeConfig );
final IAppEngApi api = AEApi.instance();
final IPartHelper partHelper = api.partHelper();
final IRegistryContainer registries = api.registries();
@ -224,29 +239,33 @@ public final class Registration
// Perform ore camouflage!
MultiItem.instance.makeUnique();
final Runnable recipeLoader = new RecipeLoader( this.recipeHandler );
final Runnable recipeLoader = new RecipeLoader( recipeDirectory, customRecipeConfig, this.recipeHandler );
recipeLoader.run();
// TODO readd layers
// partHelper.registerNewLayer( "appeng.parts.layers.LayerISidedInventory", "net.minecraft.inventory.ISidedInventory" );
// partHelper.registerNewLayer( "appeng.parts.layers.LayerIFluidHandler", "net.minecraftforge.fluids.IFluidHandler" );
// partHelper.registerNewLayer( "appeng.parts.layers.LayerITileStorageMonitorable", "appeng.api.implementations.tiles.ITileStorageMonitorable" );
// partHelper.registerNewLayer( "appeng.parts.layers.LayerISidedInventory",
// "net.minecraft.inventory.ISidedInventory" );
// partHelper.registerNewLayer( "appeng.parts.layers.LayerIFluidHandler",
// "net.minecraftforge.fluids.IFluidHandler" );
// partHelper.registerNewLayer( "appeng.parts.layers.LayerITileStorageMonitorable",
// "appeng.api.implementations.tiles.ITileStorageMonitorable" );
// if( IntegrationRegistry.INSTANCE.isEnabled( IntegrationType.IC2 ) )
// {
// partHelper.registerNewLayer( "appeng.parts.layers.LayerIEnergySink", "ic2.api.energy.tile.IEnergySink" );
// partHelper.registerNewLayer( "appeng.parts.layers.LayerIEnergySource", "ic2.api.energy.tile.IEnergySource" );
// }
//
// if( IntegrationRegistry.INSTANCE.isEnabled( IntegrationType.RF ) )
// {
// partHelper.registerNewLayer( "appeng.parts.layers.LayerIEnergyHandler", "cofh.api.energy.IEnergyReceiver" );
// }
//
// if( IntegrationRegistry.INSTANCE.isEnabled( IntegrationType.OpenComputers ) )
// {
// partHelper.registerNewLayer( "appeng.parts.layers.LayerSidedEnvironment", "li.cil.oc.api.network.SidedEnvironment" );
// }
// if( IntegrationRegistry.INSTANCE.isEnabled( IntegrationType.IC2 ) )
// {
// partHelper.registerNewLayer( "appeng.parts.layers.LayerIEnergySink", "ic2.api.energy.tile.IEnergySink" );
// partHelper.registerNewLayer( "appeng.parts.layers.LayerIEnergySource", "ic2.api.energy.tile.IEnergySource" );
// }
//
// if( IntegrationRegistry.INSTANCE.isEnabled( IntegrationType.RF ) )
// {
// partHelper.registerNewLayer( "appeng.parts.layers.LayerIEnergyHandler", "cofh.api.energy.IEnergyReceiver" );
// }
//
// if( IntegrationRegistry.INSTANCE.isEnabled( IntegrationType.OpenComputers ) )
// {
// partHelper.registerNewLayer( "appeng.parts.layers.LayerSidedEnvironment",
// "li.cil.oc.api.network.SidedEnvironment" );
// }
FMLCommonHandler.instance().bus().register( TickHandler.INSTANCE );
MinecraftForge.EVENT_BUS.register( TickHandler.INSTANCE );
@ -283,13 +302,13 @@ public final class Registration
registration.registerAchievementHandlers();
registration.registerAchievements();
if( AEConfig.instance.isFeatureEnabled( AEFeature.enableDisassemblyCrafting ) )
if( AEConfig.instance.isFeatureEnabled( AEFeature.EnableDisassemblyCrafting ) )
{
GameRegistry.addRecipe( new DisassembleRecipe() );
RecipeSorter.register( "appliedenergistics2:disassemble", DisassembleRecipe.class, Category.SHAPELESS, "after:minecraft:shapeless" );
}
if( AEConfig.instance.isFeatureEnabled( AEFeature.enableFacadeCrafting ) )
if( AEConfig.instance.isFeatureEnabled( AEFeature.EnableFacadeCrafting ) )
{
GameRegistry.addRecipe( new FacadeRecipe() );
RecipeSorter.register( "appliedenergistics2:facade", FacadeRecipe.class, Category.SHAPED, "after:minecraft:shaped" );

View File

@ -21,119 +21,124 @@ package appeng.core.features;
public enum AEFeature
{
Core( null ), // stuff that has no reason for ever being turned off, or that
// stuff that has no reason for ever being turned off, or that
// is just flat out required by tons of
// important stuff.
Core( null )
{
@Override
public boolean isVisible()
{
return false;
}
},
CertusQuartzWorldGen( "World" ),
MeteoriteWorldGen( "World" ),
DecorativeLights( "World" ),
DecorativeQuartzBlocks( "World" ),
SkyStoneChests( "World" ),
SpawnPressesInMeteorites( "World" ),
GrindStone( "World" ),
Flour( "World" ),
Inscriber( "World" ),
ChestLoot( "World" ),
VillagerTrading( "World" ),
TinyTNT( "World" ),
CertusQuartzWorldGen( Constants.CATEGORY_WORLD ),
MeteoriteWorldGen( Constants.CATEGORY_WORLD ),
DecorativeLights( Constants.CATEGORY_WORLD ),
DecorativeQuartzBlocks( Constants.CATEGORY_WORLD ),
SkyStoneChests( Constants.CATEGORY_WORLD ),
SpawnPressesInMeteorites( Constants.CATEGORY_WORLD ),
GrindStone( Constants.CATEGORY_WORLD ),
Flour( Constants.CATEGORY_WORLD ),
Inscriber( Constants.CATEGORY_WORLD ),
ChestLoot( Constants.CATEGORY_WORLD ),
VillagerTrading( Constants.CATEGORY_WORLD ),
TinyTNT( Constants.CATEGORY_WORLD ),
PoweredTools( "ToolsClassifications" ),
CertusQuartzTools( "ToolsClassifications" ),
NetherQuartzTools( "ToolsClassifications" ),
PoweredTools( Constants.CATEGORY_TOOLS_CLASSIFICATIONS ),
CertusQuartzTools( Constants.CATEGORY_TOOLS_CLASSIFICATIONS ),
NetherQuartzTools( Constants.CATEGORY_TOOLS_CLASSIFICATIONS ),
QuartzHoe( "Tools" ),
QuartzSpade( "Tools" ),
QuartzSword( "Tools" ),
QuartzPickaxe( "Tools" ),
QuartzAxe( "Tools" ),
QuartzKnife( "Tools" ),
QuartzWrench( "Tools" ),
ChargedStaff( "Tools" ),
EntropyManipulator( "Tools" ),
MatterCannon( "Tools" ),
WirelessAccessTerminal( "Tools" ),
ColorApplicator( "Tools" ),
PaintBalls( "Tools" ),
MeteoriteCompass( "Tools" ),
QuartzHoe( Constants.CATEGORY_TOOLS ),
QuartzSpade( Constants.CATEGORY_TOOLS ),
QuartzSword( Constants.CATEGORY_TOOLS ),
QuartzPickaxe( Constants.CATEGORY_TOOLS ),
QuartzAxe( Constants.CATEGORY_TOOLS ),
QuartzKnife( Constants.CATEGORY_TOOLS ),
QuartzWrench( Constants.CATEGORY_TOOLS ),
ChargedStaff( Constants.CATEGORY_TOOLS ),
EntropyManipulator( Constants.CATEGORY_TOOLS ),
MatterCannon( Constants.CATEGORY_TOOLS ),
WirelessAccessTerminal( Constants.CATEGORY_TOOLS ),
ColorApplicator( Constants.CATEGORY_TOOLS ),
MeteoriteCompass( Constants.CATEGORY_TOOLS ),
CraftingCPU( "CraftingFeatures" ),
PowerGen( Constants.CATEGORY_NETWORK_FEATURES ),
Security( Constants.CATEGORY_NETWORK_FEATURES ),
SpatialIO( Constants.CATEGORY_NETWORK_FEATURES ),
QuantumNetworkBridge( Constants.CATEGORY_NETWORK_FEATURES ),
Channels( Constants.CATEGORY_NETWORK_FEATURES ),
PowerGen( "NetworkFeatures" ),
Security( "NetworkFeatures" ),
SpatialIO( "NetworkFeatures" ),
QuantumNetworkBridge( "NetworkFeatures" ),
Channels( "NetworkFeatures" ),
LevelEmitter( Constants.CATEGORY_NETWORK_BUSES ),
CraftingTerminal( Constants.CATEGORY_NETWORK_BUSES ),
StorageMonitor( Constants.CATEGORY_NETWORK_BUSES ),
P2PTunnel( Constants.CATEGORY_NETWORK_BUSES ),
FormationPlane( Constants.CATEGORY_NETWORK_BUSES ),
AnnihilationPlane( Constants.CATEGORY_NETWORK_BUSES ),
IdentityAnnihilationPlane( Constants.CATEGORY_NETWORK_BUSES ),
ImportBus( Constants.CATEGORY_NETWORK_BUSES ),
ExportBus( Constants.CATEGORY_NETWORK_BUSES ),
StorageBus( Constants.CATEGORY_NETWORK_BUSES ),
PartConversionMonitor( Constants.CATEGORY_NETWORK_BUSES ),
LevelEmitter( "NetworkBuses" ),
CraftingTerminal( "NetworkBuses" ),
StorageMonitor( "NetworkBuses" ),
P2PTunnel( "NetworkBuses" ),
FormationPlane( "NetworkBuses" ),
AnnihilationPlane( "NetworkBuses" ),
IdentityAnnihilationPlane( "NetworkBuses" ),
ImportBus( "NetworkBuses" ),
ExportBus( "NetworkBuses" ),
StorageBus( "NetworkBuses" ),
PartConversionMonitor( "NetworkBuses" ),
PortableCell( Constants.CATEGORY_PORTABLE_CELL ),
StorageCells( "Storage" ),
MEChest( "Storage" ),
MEDrive( "Storage" ),
IOPort( "Storage" ),
StorageCells( Constants.CATEGORY_STORAGE ),
MEChest( Constants.CATEGORY_STORAGE ),
MEDrive( Constants.CATEGORY_STORAGE ),
IOPort( Constants.CATEGORY_STORAGE ),
PortableCell( "PortableCell" ),
NetworkTool( Constants.CATEGORY_NETWORK_TOOL ),
NetworkTool( "NetworkTool" ),
DenseEnergyCells( Constants.CATEGORY_HIGHER_CAPACITY ),
DenseCables( Constants.CATEGORY_HIGHER_CAPACITY ),
DenseEnergyCells( "HigherCapacity" ),
DenseCables( "HigherCapacity" ),
P2PTunnelRF( Constants.CATEGORY_P2P_TUNNELS ),
P2PTunnelME( Constants.CATEGORY_P2P_TUNNELS ),
P2PTunnelItems( Constants.CATEGORY_P2P_TUNNELS ),
P2PTunnelRedstone( Constants.CATEGORY_P2P_TUNNELS ),
P2PTunnelEU( Constants.CATEGORY_P2P_TUNNELS ),
P2PTunnelLiquids( Constants.CATEGORY_P2P_TUNNELS ),
P2PTunnelLight( Constants.CATEGORY_P2P_TUNNELS ),
P2PTunnelOpenComputers( Constants.CATEGORY_P2P_TUNNELS ),
P2PTunnelPressure( Constants.CATEGORY_P2P_TUNNELS ),
P2PTunnelRF( "P2PTunnels" ),
P2PTunnelME( "P2PTunnels" ),
P2PTunnelItems( "P2PTunnels" ),
P2PTunnelRedstone( "P2PTunnels" ),
P2PTunnelEU( "P2PTunnels" ),
P2PTunnelLiquids( "P2PTunnels" ),
P2PTunnelLight( "P2PTunnels" ),
P2PTunnelOpenComputers( "P2PTunnels" ),
P2PTunnelPressure( "P2PTunnels" ),
MassCannonBlockDamage( Constants.CATEGORY_BLOCK_FEATURES ),
TinyTNTBlockDamage( Constants.CATEGORY_BLOCK_FEATURES ),
MassCannonBlockDamage( "BlockFeatures" ),
TinyTNTBlockDamage( "BlockFeatures" ),
Facades( Constants.CATEGORY_FACADES ),
Facades( "Facades" ),
UnsupportedDeveloperTools( Constants.CATEGORY_MISC, false ),
Creative( Constants.CATEGORY_MISC ),
GrinderLogging( Constants.CATEGORY_MISC, false ),
Logging( Constants.CATEGORY_MISC ),
IntegrationLogging( Constants.CATEGORY_MISC, false ),
WebsiteRecipes( Constants.CATEGORY_MISC, false ),
LogSecurityAudits( Constants.CATEGORY_MISC, false ),
Achievements( Constants.CATEGORY_MISC ),
UpdateLogging( Constants.CATEGORY_MISC, false ),
PacketLogging( Constants.CATEGORY_MISC, false ),
CraftingLog( Constants.CATEGORY_MISC, false ),
LightDetector( Constants.CATEGORY_MISC ),
DebugLogging( Constants.CATEGORY_MISC, false ),
UnsupportedDeveloperTools( "Misc", false ),
Creative( "Misc" ),
GrinderLogging( "Misc", false ),
Logging( "Misc" ),
IntegrationLogging( "Misc", false ),
WebsiteRecipes( "Misc", false ),
UpdateLogging( "Misc", false ),
PacketLogging( "Misc", false ),
CraftingLog( "Misc", false ),
LightDetector( "Misc" ),
LogSecurityAudits( "Misc", false ),
Achievements( "Misc" ),
EnableFacadeCrafting( Constants.CATEGORY_CRAFTING ),
InWorldSingularity( Constants.CATEGORY_CRAFTING ),
InWorldFluix( Constants.CATEGORY_CRAFTING ),
InWorldPurification( Constants.CATEGORY_CRAFTING ),
InterfaceTerminal( Constants.CATEGORY_CRAFTING ),
EnableDisassemblyCrafting( Constants.CATEGORY_CRAFTING ),
enableFacadeCrafting( "Crafting" ),
inWorldSingularity( "Crafting" ),
inWorldFluix( "Crafting" ),
inWorldPurification( "Crafting" ),
InterfaceTerminal( "Crafting" ),
AlphaPass( Constants.CATEGORY_RENDERING ), PaintBalls( Constants.CATEGORY_TOOLS ),
AlphaPass( "Rendering" ),
MolecularAssembler( Constants.CATEGORY_CRAFTING_FEATURES ),
Patterns( Constants.CATEGORY_CRAFTING_FEATURES ),
CraftingCPU( Constants.CATEGORY_CRAFTING_FEATURES ),
MolecularAssembler( "CraftingFeatures" ),
enableDisassemblyCrafting( "Crafting" ),
Patterns( "CraftingFeatures" ),
ChunkLoggerTrace( "Commands", false );
ChunkLoggerTrace( Constants.CATEGORY_COMMANDS, false );
public final String category;
public final boolean isVisible;
public final boolean defaultValue;
AEFeature( final String cat )
@ -144,7 +149,39 @@ public enum AEFeature
AEFeature( final String cat, final boolean defaultValue )
{
this.category = cat;
this.isVisible = !this.name().equals( "Core" );
this.defaultValue = defaultValue;
}
/**
* override to set visibility
*
* @return default true
*/
public boolean isVisible()
{
return true;
}
private enum Constants
{
;
private static final String CATEGORY_MISC = "Misc";
private static final String CATEGORY_CRAFTING = "Crafting";
private static final String CATEGORY_WORLD = "World";
private static final String CATEGORY_TOOLS = "Tools";
private static final String CATEGORY_TOOLS_CLASSIFICATIONS = "ToolsClassifications";
private static final String CATEGORY_NETWORK_BUSES = "NetworkBuses";
private static final String CATEGORY_P2P_TUNNELS = "P2PTunnels";
private static final String CATEGORY_BLOCK_FEATURES = "BlockFeatures";
private static final String CATEGORY_CRAFTING_FEATURES = "CraftingFeatures";
private static final String CATEGORY_STORAGE = "Storage";
private static final String CATEGORY_HIGHER_CAPACITY = "HigherCapacity";
private static final String CATEGORY_NETWORK_FEATURES = "NetworkFeatures";
private static final String CATEGORY_COMMANDS = "Commands";
private static final String CATEGORY_RENDERING = "Rendering";
private static final String CATEGORY_FACADES = "Facades";
private static final String CATEGORY_NETWORK_TOOL = "NetworkTool";
private static final String CATEGORY_PORTABLE_CELL = "PortableCell";
}
}

View File

@ -62,7 +62,7 @@ public final class EntityChargedQuartz extends AEBaseEntityItem
{
super.onUpdate();
if( !AEConfig.instance.isFeatureEnabled( AEFeature.inWorldFluix ) )
if( !AEConfig.instance.isFeatureEnabled( AEFeature.InWorldFluix ) )
{
return;
}

View File

@ -58,7 +58,7 @@ public final class EntityGrowingCrystal extends EntityItem
{
super.onUpdate();
if( !AEConfig.instance.isFeatureEnabled( AEFeature.inWorldPurification ) )
if( !AEConfig.instance.isFeatureEnabled( AEFeature.InWorldPurification ) )
{
return;
}

View File

@ -73,7 +73,7 @@ public final class EntitySingularity extends AEBaseEntityItem
return;
}
if( !AEConfig.instance.isFeatureEnabled( AEFeature.inWorldSingularity ) )
if( !AEConfig.instance.isFeatureEnabled( AEFeature.InWorldSingularity ) )
{
return;
}

View File

@ -90,7 +90,7 @@ public class NEI implements INEI, IContainerTooltipHandler, IIntegrationModule
this.registerRecipeHandler( new NEIWorldCraftingHandler() );
this.registerRecipeHandler( new NEIGrinderRecipeHandler() );
if( AEConfig.instance.isFeatureEnabled( AEFeature.Facades ) && AEConfig.instance.isFeatureEnabled( AEFeature.enableFacadeCrafting ) )
if( AEConfig.instance.isFeatureEnabled( AEFeature.Facades ) && AEConfig.instance.isFeatureEnabled( AEFeature.EnableFacadeCrafting ) )
{
this.registerRecipeHandler( new NEIFacadeRecipeHandler() );
}

View File

@ -221,17 +221,17 @@ public class NEIWorldCraftingHandler implements ICraftingHandler, IUsageHandler
this.addRecipe( materials.engProcessorPress(), GuiText.inWorldCraftingPresses.getLocal() );
}
if( AEConfig.instance.isFeatureEnabled( AEFeature.inWorldFluix ) )
if( AEConfig.instance.isFeatureEnabled( AEFeature.InWorldFluix ) )
{
this.addRecipe( materials.fluixCrystal(), GuiText.inWorldFluix.getLocal() );
}
if( AEConfig.instance.isFeatureEnabled( AEFeature.inWorldSingularity ) )
if( AEConfig.instance.isFeatureEnabled( AEFeature.InWorldSingularity ) )
{
this.addRecipe( materials.qESingularity(), GuiText.inWorldSingularity.getLocal() );
}
if( AEConfig.instance.isFeatureEnabled( AEFeature.inWorldPurification ) )
if( AEConfig.instance.isFeatureEnabled( AEFeature.InWorldPurification ) )
{
this.addRecipe( materials.purifiedCertusQuartzCrystal(), GuiText.inWorldPurificationCertus.getLocal() );
this.addRecipe( materials.purifiedNetherQuartzCrystal(), GuiText.inWorldPurificationNether.getLocal() );

View File

@ -40,47 +40,80 @@ public enum MaterialType
{
InvalidType( -1, AEFeature.Core ),
CertusQuartzCrystal( 0, AEFeature.Core, "crystalCertusQuartz" ), CertusQuartzCrystalCharged( 1, AEFeature.Core, EntityChargedQuartz.class ),
CertusQuartzCrystal( 0, AEFeature.Core, "crystalCertusQuartz" ),
CertusQuartzCrystalCharged( 1, AEFeature.Core, EntityChargedQuartz.class ),
CertusQuartzDust( 2, AEFeature.Core, "dustCertusQuartz" ), NetherQuartzDust( 3, AEFeature.Core, "dustNetherQuartz" ), Flour( 4, AEFeature.Flour, "dustWheat" ), GoldDust( 51, AEFeature.Core, "dustGold" ), IronDust( 49, AEFeature.Core, "dustIron" ), IronNugget( 50, AEFeature.Core, "nuggetIron" ),
CertusQuartzDust( 2, AEFeature.Core, "dustCertusQuartz" ),
NetherQuartzDust( 3, AEFeature.Core, "dustNetherQuartz" ),
Flour( 4, AEFeature.Flour, "dustWheat" ),
GoldDust( 51, AEFeature.Core, "dustGold" ),
IronDust( 49, AEFeature.Core, "dustIron" ),
IronNugget( 50, AEFeature.Core, "nuggetIron" ),
Silicon( 5, AEFeature.Core, "itemSilicon" ), MatterBall( 6 ),
Silicon( 5, AEFeature.Core, "itemSilicon" ),
MatterBall( 6 ),
FluixCrystal( 7, AEFeature.Core, "crystalFluix" ), FluixDust( 8, AEFeature.Core, "dustFluix" ), FluixPearl( 9, AEFeature.Core, "pearlFluix" ),
FluixCrystal( 7, AEFeature.Core, "crystalFluix" ),
FluixDust( 8, AEFeature.Core, "dustFluix" ),
FluixPearl( 9, AEFeature.Core, "pearlFluix" ),
PurifiedCertusQuartzCrystal( 10 ), PurifiedNetherQuartzCrystal( 11 ), PurifiedFluixCrystal( 12 ),
PurifiedCertusQuartzCrystal( 10 ),
PurifiedNetherQuartzCrystal( 11 ),
PurifiedFluixCrystal( 12 ),
CalcProcessorPress( 13 ), EngProcessorPress( 14 ), LogicProcessorPress( 15 ),
CalcProcessorPress( 13 ),
EngProcessorPress( 14 ),
LogicProcessorPress( 15 ),
CalcProcessorPrint( 16 ), EngProcessorPrint( 17 ), LogicProcessorPrint( 18 ),
CalcProcessorPrint( 16 ),
EngProcessorPrint( 17 ),
LogicProcessorPrint( 18 ),
SiliconPress( 19 ), SiliconPrint( 20 ),
SiliconPress( 19 ),
SiliconPrint( 20 ),
NamePress( 21 ),
LogicProcessor( 22 ), CalcProcessor( 23 ), EngProcessor( 24 ),
LogicProcessor( 22 ),
CalcProcessor( 23 ),
EngProcessor( 24 ),
// Basic Cards
BasicCard( 25 ), CardRedstone( 26 ), CardCapacity( 27 ),
BasicCard( 25 ),
CardRedstone( 26 ),
CardCapacity( 27 ),
// Adv Cards
AdvCard( 28 ), CardFuzzy( 29 ), CardSpeed( 30 ), CardInverter( 31 ),
AdvCard( 28 ),
CardFuzzy( 29 ), CardSpeed( 30 ),
CardInverter( 31 ),
Cell2SpatialPart( 32, AEFeature.SpatialIO ), Cell16SpatialPart( 33, AEFeature.SpatialIO ), Cell128SpatialPart( 34, AEFeature.SpatialIO ),
Cell2SpatialPart( 32, AEFeature.SpatialIO ),
Cell16SpatialPart( 33, AEFeature.SpatialIO ),
Cell128SpatialPart( 34, AEFeature.SpatialIO ),
Cell1kPart( 35, AEFeature.StorageCells ), Cell4kPart( 36, AEFeature.StorageCells ), Cell16kPart( 37, AEFeature.StorageCells ), Cell64kPart( 38, AEFeature.StorageCells ), EmptyStorageCell( 39, AEFeature.StorageCells ),
Cell1kPart( 35, AEFeature.StorageCells ),
Cell4kPart( 36, AEFeature.StorageCells ),
Cell16kPart( 37, AEFeature.StorageCells ),
Cell64kPart( 38, AEFeature.StorageCells ),
EmptyStorageCell( 39, AEFeature.StorageCells ),
WoodenGear( 40, AEFeature.GrindStone, "gearWood" ),
Wireless( 41, AEFeature.WirelessAccessTerminal ), WirelessBooster( 42, AEFeature.WirelessAccessTerminal ),
Wireless( 41, AEFeature.WirelessAccessTerminal ),
WirelessBooster( 42, AEFeature.WirelessAccessTerminal ),
FormationCore( 43 ), AnnihilationCore( 44 ),
FormationCore( 43 ),
AnnihilationCore( 44 ),
SkyDust( 45, AEFeature.Core ),
EnderDust( 46, AEFeature.QuantumNetworkBridge, "dustEnder,dustEnderPearl", EntitySingularity.class ), Singularity( 47, AEFeature.QuantumNetworkBridge, EntitySingularity.class ), QESingularity( 48, AEFeature.QuantumNetworkBridge, EntitySingularity.class ),
EnderDust( 46, AEFeature.QuantumNetworkBridge, "dustEnder,dustEnderPearl", EntitySingularity.class ),
Singularity( 47, AEFeature.QuantumNetworkBridge, EntitySingularity.class ),
QESingularity( 48, AEFeature.QuantumNetworkBridge, EntitySingularity.class ),
BlankPattern( 52 ), CardCrafting( 53 );
BlankPattern( 52 ),
CardCrafting( 53 );
private final EnumSet<AEFeature> features;
// TextureAtlasSprite for the material.

View File

@ -311,6 +311,6 @@ public final class ItemBasicStorageCell extends AEBaseItem implements IStorageCe
@Override
public boolean hasContainerItem( final ItemStack stack )
{
return AEConfig.instance.isFeatureEnabled( AEFeature.enableDisassemblyCrafting );
return AEConfig.instance.isFeatureEnabled( AEFeature.EnableDisassemblyCrafting );
}
}

View File

@ -0,0 +1,30 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes;
/**
* @author thatsIch
* @version rv3 - 22.08.2015
* @since rv3 22.08.2015
*/
public interface CustomRecipeConfig
{
boolean isEnabled();
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes;
import javax.annotation.Nonnull;
import com.google.common.base.Preconditions;
import net.minecraftforge.common.config.Configuration;
/**
* @author thatsIch
* @version rv3 - 23.08.2015
* @since rv3 23.08.2015
*/
public class CustomRecipeForgeConfiguration implements CustomRecipeConfig
{
private final boolean isEnabled;
public CustomRecipeForgeConfiguration( @Nonnull final Configuration config )
{
Preconditions.checkNotNull( config );
this.isEnabled = config.getBoolean( "enabled", "general", true, "If true, the custom recipes are enabled. Acts as a master switch." );
}
@Override
public final boolean isEnabled()
{
return this.isEnabled;
}
}

View File

@ -23,7 +23,6 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import javax.annotation.Nonnull;
import appeng.api.recipes.IRecipeLoader;
@ -31,6 +30,9 @@ import appeng.api.recipes.IRecipeLoader;
import com.google.common.base.Preconditions;
/**
* Loads the recipes from the config folder
*/
public final class ConfigLoader implements IRecipeLoader
{
private final File generatedRecipesDir;

View File

@ -22,6 +22,7 @@ package appeng.recipes.loader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
@ -30,8 +31,8 @@ import java.util.Enumeration;
import java.util.HashSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import org.apache.commons.io.FileUtils;
@ -40,9 +41,7 @@ import com.google.common.base.Preconditions;
/**
* copies recipes in jars onto file system
* includes the readme,
* needs to be modified if other files needs to be handled
* copies recipes in jars onto file system includes the readme, needs to be modified if other files needs to be handled
*
* @author thatsIch
* @version rv3 - 11.05.2015
@ -55,6 +54,10 @@ public class RecipeResourceCopier
*/
private static final int INITIAL_RESOURCE_CAPACITY = 20;
private static final Pattern DOT_COMPILE_PATTERN = Pattern.compile( ".", Pattern.LITERAL );
private static final String FILE_PROTOCOL = "file";
private static final String CLASS_EXTENSION = ".class";
private static final String JAR_PROTOCOL = "jar";
private static final String UTF_8_ENCODING = "UTF-8";
/**
* copy source in the jar
@ -76,47 +79,52 @@ public class RecipeResourceCopier
/**
* copies recipes found in the root to destination.
*
* @param identifier only copy files which end with the identifier
* @param destination destination folder to which the recipes are copied to
*
* @throws URISyntaxException {@see #getResourceListing}
* @throws IOException {@see #getResourceListing} and if copying the detected resource to file is not possible
* @throws NullPointerException if either parameter is <tt>null</tt>
* @throws URISyntaxException {@see #getResourceListing}
* @throws IOException {@see #getResourceListing} and if copying the detected resource to file is not possible
* @throws NullPointerException if either parameter is <tt>null</tt>
* @throws IllegalArgumentException if destination is not a directory
*/
public void copyTo( @Nonnull final File destination ) throws URISyntaxException, IOException
public void copyTo( @Nonnull final String identifier, @Nonnull final File destination ) throws URISyntaxException, IOException
{
Preconditions.checkNotNull( destination );
Preconditions.checkArgument( destination.isDirectory() );
this.copyTo( destination, this.root );
this.copyTo( identifier, destination, this.root );
}
/**
* @see {RecipeResourceCopier#copyTo(File)}
*
* @param destination destination folder to which the recipes are copied to
* @param directory the folder to copy.
*
* @throws URISyntaxException {@see #getResourceListing}
* @throws IOException {@see #getResourceListing} and if copying the detected resource to file is not possible
* @see {RecipeResourceCopier#copyTo(File)}
*/
private void copyTo( final File destination, final String directory ) throws URISyntaxException, IOException
private void copyTo( @Nonnull final String identifier, @Nonnull final File destination, @Nonnull final String directory ) throws URISyntaxException, IOException
{
assert identifier != null;
assert destination != null;
assert directory != null;
final String[] listing = this.getResourceListing( this.getClass(), directory );
final Class<? extends RecipeResourceCopier> copierClass = this.getClass();
final String[] listing = this.getResourceListing( copierClass, directory );
for( final String list : listing )
{
if( list.endsWith( ".recipe" ) || list.endsWith( ".html" ) )
if( list.endsWith( identifier ) )
{
// generate folder before the file is copied so no empty folders will be generated
FileUtils.forceMkdir( destination );
this.copyFile( destination, directory, list );
}
else if( !list.contains( "." ) )
{
final File subDirectory = new File( destination, list );
FileUtils.forceMkdir( subDirectory );
this.copyTo( subDirectory, directory + list + "/" );
this.copyTo( identifier, subDirectory, directory + list + "/" );
}
}
}
@ -126,16 +134,18 @@ public class RecipeResourceCopier
*
* @param destination folder to which the file is copied to
* @param directory the directory containing the file
* @param fileName the fily to copy
* @param fileName the file to copy
*
* @throws IOException if copying the file is not possible
*/
private void copyFile( final File destination, final String directory, final String fileName ) throws IOException
private void copyFile( @Nonnull final File destination, @Nonnull final String directory, @Nonnull final String fileName ) throws IOException
{
assert destination != null;
assert directory != null;
assert fileName != null;
final InputStream inStream = this.getClass().getResourceAsStream( '/' + directory + fileName );
final Class<? extends RecipeResourceCopier> copierClass = this.getClass();
final InputStream inStream = copierClass.getResourceAsStream( '/' + directory + fileName );
final File outFile = new File( destination, fileName );
if( !outFile.exists() && inStream != null )
@ -146,72 +156,107 @@ public class RecipeResourceCopier
}
/**
* List directory contents for a resource folder. Not recursive.
* This is basically a brute-force implementation.
* List directory contents for a resource folder. Not recursive. This is basically a brute-force implementation.
* Works for regular files and also JARs.
*
* @param clazz Any java class that lives in the same place as the resources you want.
* @param path Should end with "/", but not start with one.
* @param path Should end with "/", but not start with one.
*
* @return Just the name of each member item, not the full paths.
*
* @throws URISyntaxException if it is a file path and the URL can not be converted to URI
* @throws IOException if jar path can not be decoded
* @throws URISyntaxException if it is a file path and the URL can not be converted to URI
* @throws IOException if jar path can not be decoded
* @throws UnsupportedOperationException if it is neither in jar nor in file path
*/
@Nonnull
private String[] getResourceListing( final Class<?> clazz, final String path ) throws URISyntaxException, IOException
private String[] getResourceListing( @Nonnull final Class<?> clazz, @Nonnull final String path ) throws URISyntaxException, IOException
{
assert clazz != null;
assert path != null;
URL dirURL = clazz.getClassLoader().getResource( path );
if( dirURL != null && dirURL.getProtocol().equals( "file" ) )
final ClassLoader classLoader = clazz.getClassLoader();
if( classLoader == null )
{
// A file path: easy enough
return new File( dirURL.toURI() ).list();
throw new IllegalStateException( "ClassLoader was not found. It was probably loaded at a inappropriate time" );
}
URL dirURL = classLoader.getResource( path );
if( dirURL != null )
{
final String protocol = dirURL.getProtocol();
if( protocol.equals( FILE_PROTOCOL ) )
{
// A file path: easy enough
final URI uriOfURL = dirURL.toURI();
final File fileOfURI = new File( uriOfURL );
final String[] filesAndDirectoriesOfURI = fileOfURI.list();
if( filesAndDirectoriesOfURI == null )
{
throw new IllegalStateException( "Files and Directories were illegal. Either an abstract pathname does not denote a directory, or an I/O error occured." );
}
else
{
return filesAndDirectoriesOfURI;
}
}
}
if( dirURL == null )
{
/*
* In case of a jar file, we can't actually find a directory.
* Have to assume the same jar as clazz.
*/
final String me = DOT_COMPILE_PATTERN.matcher( clazz.getName() ).replaceAll( "/" ) + ".class";
dirURL = clazz.getClassLoader().getResource( me );
/*
* In case of a jar file, we can't actually find a directory.
* Have to assume the same jar as clazz.
*/
final String className = clazz.getName();
final Matcher matcher = DOT_COMPILE_PATTERN.matcher( className );
final String me = matcher.replaceAll( "/" ) + CLASS_EXTENSION;
dirURL = classLoader.getResource( me );
}
if( dirURL != null && dirURL.getProtocol().equals( "jar" ) )
if( dirURL != null )
{
/* A JAR path */
final String jarPath = dirURL.getPath().substring( 5, dirURL.getPath().indexOf( '!' ) ); //strip out only the JAR file
final JarFile jar = new JarFile( URLDecoder.decode( jarPath, "UTF-8" ) );
try
{
final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
final Collection<String> result = new HashSet<String>( INITIAL_RESOURCE_CAPACITY ); //avoid duplicates in case it is a subdirectory
while( entries.hasMoreElements() )
{
final String name = entries.nextElement().getName();
if( name.startsWith( path ) )
{ //filter according to the path
String entry = name.substring( path.length() );
final int checkSubDir = entry.indexOf( '/' );
if( checkSubDir >= 0 )
{
// if it is a subdirectory, we just return the directory name
entry = entry.substring( 0, checkSubDir );
}
result.add( entry );
}
}
return result.toArray( new String[result.size()] );
}
finally
final String protocol = dirURL.getProtocol();
if( protocol.equals( JAR_PROTOCOL ) )
{
jar.close();
/* A JAR path */
final String dirPath = dirURL.getPath();
final String jarPath = dirPath.substring( 5, dirPath.indexOf( '!' ) ); // strip out only
// the JAR file
final JarFile jar = new JarFile( URLDecoder.decode( jarPath, UTF_8_ENCODING ) );
try
{
final Enumeration<JarEntry> entries = jar.entries(); // gives ALL entries in jar
final Collection<String> result = new HashSet<String>( INITIAL_RESOURCE_CAPACITY ); // avoid duplicates
// in case it is a
// subdirectory
while( entries.hasMoreElements() )
{
final JarEntry entry = entries.nextElement();
final String entryFullName = entry.getName();
if( entryFullName.startsWith( path ) )
{ // filter according to the path
String entryName = entryFullName.substring( path.length() );
final int checkSubDir = entryName.indexOf( '/' );
if( checkSubDir >= 0 )
{
// if it is a subdirectory, we just return the directory name
entryName = entryName.substring( 0, checkSubDir );
}
result.add( entryName );
}
}
return result.toArray( new String[result.size()] );
}
finally
{
jar.close();
}
}
}

View File

@ -25,7 +25,8 @@ import appeng.server.subcommands.Supporters;
public enum Commands
{
Chunklogger( 4, new ChunkLogger() ), supporters( 0, new Supporters() );
Chunklogger( 4, new ChunkLogger() ),
Supporters( 0, new Supporters() );
public final int level;
public final ISubCommand command;

View File

@ -74,7 +74,13 @@ public final class VersionChecker implements Runnable
{
Thread.yield();
// persist the config
this.config.save();
// retrieve data
final String rawLastCheck = this.config.lastCheck();
// process data
final long lastCheck = Long.parseLong( rawLastCheck );
final Date now = new Date();
final long nowInMs = now.getTime();

View File

@ -0,0 +1,41 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
/**
* Defines a concrete result type when using the {@link Checker#isEqual(Object)} from the {@link Checker} class.
*
* @author thatsIch
* @version rv3 - 25.09.2015
* @see Checker
* @since rv3 - 25.09.2015
*/
enum CheckType
{
/**
* If checking resulted in both objects being <b>equal</b>
*/
EQUAL,
/**
* If checking resulted in both objects being <b>unequal</b>
*/
UNEQUAL
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
import javax.annotation.Nonnull;
/**
* Checks against a specific type with its own check type for clear outcome.
*
* The constructor will generally have a value which the checker will check against
*
* @author thatsIch
* @version rv3 - 01.09.2015
* @since rv3 - 01.09.2015
*/
interface Checker<T>
{
/**
* @param checkedAgainst the object it is checked against
*
* @return non null being either equal or unequal
*
* @since rv3 - 01.09.2015
*/
@Nonnull
CheckType isEqual( @Nonnull final T checkedAgainst );
}

View File

@ -0,0 +1,82 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
import javax.annotation.Nonnull;
/**
* @author thatsIch
* @version rv3 - 14.08.2015
* @since rv3 14.08.2015
*/
public interface ExportConfig
{
/**
* config switch to disable the exporting.
* if the recipes system is not used
* there is no reason to export them.
* Still can be useful for debugging purpose,
* thus not tying it to the recipe system directly.
*
* @return true if exporting is enabled
*/
boolean isExportingItemNamesEnabled();
/**
* config switch for using the digest cache.
*
* @return true if cache is enabled
*/
boolean isCacheEnabled();
/**
* config switch to always refresh the CSV. Might be useful to activate on debugging.
*
* @return true if force refresh is enabled
*/
boolean isForceRefreshEnabled();
/**
* config switch to export more information mostly used for debugging
*
* @return true if additional information are enabled
*/
boolean isAdditionalInformationEnabled();
/**
* Will get the cache from last session. Can be used to reduce I/O operations though containing itself calculation.
*
* @return a digest from the last calculation
*/
String getCache();
/**
* sets the cache for the next session to reduce calculation overhead
*
* @param digest new digest for the cache
*/
void setCache( @Nonnull String digest );
/**
* Will delegate the saving
*/
void save();
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
/**
* Defines the different modes which need to be distinguished upon exporting.
*
* using a different mode will result in a different export outcome.
*
* @author thatsIch
* @version rv3 - 23.09.2015
* @since rv3 - 23.09.2015
*/
enum ExportMode
{
/**
* Will provide general users with information required for recipe making
*/
MINIMAL,
/**
* Will provide advanced users with information with debugging functionality
*/
VERBOSE
}

View File

@ -0,0 +1,130 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
import java.io.File;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import net.minecraft.item.Item;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry;
import cpw.mods.fml.common.registry.GameData;
import appeng.core.AELog;
/**
* Main entry point for exporting the CSV file
*
* makes everything threadable
*
* @author thatsIch
* @version rv3 - 14.08.2015
* @since rv3 14.08.2015
*/
public class ExportProcess implements Runnable
{
private static final String FORCE_REFRESH_MESSAGE = "Force Refresh enabled. Will ignore cache and export CSV content.";
private static final String CACHE_ENABLED_MESSAGE = "Cache is enabled. Checking for new mod configurations.";
private static final String EQUAL_CONTENT_MESSAGE = "Same mod configuration was found. Not updating CSV content.";
private static final String UNEQUAL_CONTENT_MESSAGE = "New mod configuration was found. Commencing exporting.";
private static final String CACHE_DISABLED_MESSAGE = "Cache is disabled. Commencing exporting.";
private static final String EXPORT_START_MESSAGE = "Item Exporting ( started )";
private static final String EXPORT_END_MESSAGE = "Item Exporting ( ended after %s ms)";
@Nonnull
private final File exportDirectory;
@Nonnull
private final Checker<List<ModContainer>> modChecker;
@Nonnull
private final ExportConfig config;
/**
* @param exportDirectory directory where the final CSV file will be exported to
* @param config configuration to manipulate the export process
*/
public ExportProcess( @Nonnull final File exportDirectory, @Nonnull final ExportConfig config )
{
this.exportDirectory = Preconditions.checkNotNull( exportDirectory );
this.config = Preconditions.checkNotNull( config );
this.modChecker = new ModListChecker( config );
}
/**
* Will check and export if various config settings will lead to exporting the CSV file.
*/
@Override
public void run()
{
// no priority to this thread
Thread.yield();
// logic when to cancel the export process
if( this.config.isForceRefreshEnabled() )
{
AELog.info( FORCE_REFRESH_MESSAGE );
}
else
{
if( this.config.isCacheEnabled() )
{
AELog.info( CACHE_ENABLED_MESSAGE );
final Loader loader = Loader.instance();
final List<ModContainer> mods = loader.getActiveModList();
if( this.modChecker.isEqual( mods ) == CheckType.EQUAL )
{
AELog.info( EQUAL_CONTENT_MESSAGE );
return;
}
else
{
AELog.info( UNEQUAL_CONTENT_MESSAGE );
}
}
else
{
AELog.info( CACHE_DISABLED_MESSAGE );
}
}
AELog.info( EXPORT_START_MESSAGE );
final Stopwatch watch = Stopwatch.createStarted();
final FMLControlledNamespacedRegistry<Item> itemRegistry = GameData.getItemRegistry();
final ExportMode mode = this.config.isAdditionalInformationEnabled() ? ExportMode.VERBOSE : ExportMode.MINIMAL;
final Exporter exporter = new MinecraftItemCSVExporter( this.exportDirectory, itemRegistry, mode );
exporter.export();
AELog.info( EXPORT_END_MESSAGE, watch.elapsed( TimeUnit.MILLISECONDS ) );
}
}

View File

@ -0,0 +1,35 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
/**
* General purpose interface to define an export operation with side effects
*
* @author thatsIch
* @version rv3 - 19.08.2015
* @since rv3 19.08.2015
*/
interface Exporter
{
/**
* Will export something defined by the Exporter with side effects
*/
void export();
}

View File

@ -0,0 +1,129 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
import javax.annotation.Nonnull;
import com.google.common.base.Preconditions;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
/**
* Offers configuration switches for the user to change the export process
*
* @author thatsIch
* @version rv3 - 14.08.2015
* @since rv3 14.08.2015
*/
public final class ForgeExportConfig implements ExportConfig
{
private static final String GENERAL_CATEGORY = "general";
private static final String CACHE_CATEGORY = "cache";
private static final String EXPORT_ITEM_NAMES_KEY = "exportItemNames";
private static final boolean EXPORT_ITEM_NAMES_DEFAULT = true;
private static final String EXPORT_ITEM_NAMES_DESCRIPTION = "If true, all registered items will be exported containing the internal minecraft name and the localized name to actually find the item you are using. This also contains the item representation of the blocks, but are missing items, which are too much to display e.g. FMP.";
private static final String ENABLE_FORCE_REFRESH_KEY = "enableForceRefresh";
private static final boolean ENABLE_FORCE_REFRESH_DEFAULT = false;
private static final String ENABLE_FORCE_REFRESH_DESCRIPTION = "If true, the CSV exporting will always happen. This will not use the cache to reduce the computation.";
private static final String ENABLE_CACHE_KEY = "enableCache";
private static final boolean ENABLE_CACHE_DEFAULT = true;
private static final String ENABLE_CACHE_DESCRIPTION = "Caching can save processing time, if there are a lot of items.";
private static final String ENABLE_ADDITIONAL_INFO_KEY = "enableAdditionalInfo";
private static final boolean ENABLE_ADDITIONAL_INFO_DEFAULT = false;
private static final String ENABLE_ADDITIONAL_INFO_DESCRIPTION = "Will output more detailed information into the CSV like corresponding items";
private static final String DIGEST_KEY = "digest";
private static final String DIGEST_DEFAULT = "";
private static final String DIGEST_DESCRIPTION = "Digest of all the mods and versions to check if a re-export of the item names is required.";
private final boolean exportItemNamesEnabled;
private final boolean cacheEnabled;
private final boolean forceRefreshEnabled;
private final boolean additionalInformationEnabled;
private final String cache;
private final Configuration config;
/**
* Constructor using the configuration. Apparently there are some race conditions if constructing configurations on multiple file accesses
*
* @param config to be wrapped configuration.
*/
public ForgeExportConfig( @Nonnull final Configuration config )
{
this.config = Preconditions.checkNotNull( config );
this.exportItemNamesEnabled = this.config.getBoolean( EXPORT_ITEM_NAMES_KEY, GENERAL_CATEGORY, EXPORT_ITEM_NAMES_DEFAULT, EXPORT_ITEM_NAMES_DESCRIPTION );
this.cacheEnabled = this.config.getBoolean( ENABLE_CACHE_KEY, CACHE_CATEGORY, ENABLE_CACHE_DEFAULT, ENABLE_CACHE_DESCRIPTION );
this.additionalInformationEnabled = this.config.getBoolean( ENABLE_ADDITIONAL_INFO_KEY, GENERAL_CATEGORY, ENABLE_ADDITIONAL_INFO_DEFAULT, ENABLE_ADDITIONAL_INFO_DESCRIPTION );
this.cache = this.config.getString( DIGEST_KEY, CACHE_CATEGORY, DIGEST_DEFAULT, DIGEST_DESCRIPTION );
this.forceRefreshEnabled = this.config.getBoolean( ENABLE_FORCE_REFRESH_KEY, GENERAL_CATEGORY, ENABLE_FORCE_REFRESH_DEFAULT, ENABLE_FORCE_REFRESH_DESCRIPTION );
}
@Override
public boolean isExportingItemNamesEnabled()
{
return this.exportItemNamesEnabled;
}
@Override
public boolean isCacheEnabled()
{
return this.cacheEnabled;
}
@Override
public boolean isForceRefreshEnabled()
{
return this.forceRefreshEnabled;
}
@Override
public boolean isAdditionalInformationEnabled()
{
return this.additionalInformationEnabled;
}
@Override
public String getCache()
{
return this.cache;
}
@Override
public void setCache( @Nonnull final String digest )
{
final Property digestProperty = this.config.get( CACHE_CATEGORY, DIGEST_KEY, DIGEST_DEFAULT );
digestProperty.set( digest );
this.config.save();
}
@Override
public void save()
{
this.config.save();
}
}

View File

@ -0,0 +1,290 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.StatCollector;
import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry;
import appeng.core.AELog;
/**
* handles the exporting including processing, transformation and persisting the information
*
* @author thatsIch
* @version rv3 - 14.08.2015
* @since rv3 14.08.2015
*/
final class MinecraftItemCSVExporter implements Exporter
{
private static final String ITEM_CSV_FILE_NAME = "items.csv";
private static final String MINIMAL_HEADER = "Mod:Item:MetaData, Localized Name";
private static final String VERBOSE_HEADER = MINIMAL_HEADER + ", Unlocalized Name, Is Air?, Class Name";
private static final String EXPORT_SUCCESSFUL_MESSAGE = "Exported successfully %d items into %s";
private static final String EXPORT_UNSUCCESSFUL_MESSAGE = "Exporting was unsuccessful.";
@Nonnull
private final File exportDirectory;
@Nonnull
private final FMLControlledNamespacedRegistry<Item> itemRegistry;
@Nonnull
private final ExportMode mode;
/**
* @param exportDirectory directory of the resulting export file. Non-null required.
* @param itemRegistry the registry with minecraft items. Needs to be populated at that time, thus the exporting can only happen in init (pre-init is the
* phase when all items are determined)
* @param mode mode in which the export should be operated. Resulting CSV will change depending on this.
*/
MinecraftItemCSVExporter( @Nonnull final File exportDirectory, @Nonnull final FMLControlledNamespacedRegistry<Item> itemRegistry, @Nonnull final ExportMode mode )
{
this.exportDirectory = Preconditions.checkNotNull( exportDirectory );
Preconditions.checkArgument( !exportDirectory.isFile() );
this.itemRegistry = Preconditions.checkNotNull( itemRegistry );
this.mode = Preconditions.checkNotNull( mode );
}
@Override
public void export()
{
final Iterable<Item> items = this.itemRegistry.typeSafeIterable();
final List<Item> itemList = Lists.newArrayList( items );
final List<String> lines = Lists.transform( itemList, new ItemRowExtractFunction( this.itemRegistry, this.mode ) );
final Joiner newLineJoiner = Joiner.on( '\n' );
final Joiner newLineJoinerIgnoringNull = newLineJoiner.skipNulls();
final String joined = newLineJoinerIgnoringNull.join( lines );
final File file = new File( this.exportDirectory, ITEM_CSV_FILE_NAME );
try
{
FileUtils.forceMkdir( this.exportDirectory );
final Writer writer = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( file ), Charset.forName("UTF-8") ) );
final String header = this.mode == ExportMode.MINIMAL ? MINIMAL_HEADER : VERBOSE_HEADER;
writer.write( header );
writer.write( "\n" );
writer.write( joined );
writer.flush();
writer.close();
AELog.info( EXPORT_SUCCESSFUL_MESSAGE, lines.size(), ITEM_CSV_FILE_NAME );
}
catch( final IOException e )
{
AELog.warning( EXPORT_UNSUCCESSFUL_MESSAGE );
AELog.error( e );
}
}
/**
* Extracts item name with meta and the display name
*/
private static final class TypeExtractFunction implements Function<ItemStack, String>
{
private static final String EXTRACTING_NULL_MESSAGE = "extracting type null";
private static final String EXTRACTING_ITEM_MESSAGE = "extracting type %s:%d";
@Nonnull
private final String itemName;
@Nonnull
private final ExportMode mode;
private TypeExtractFunction( @Nonnull final String itemName, @Nonnull final ExportMode mode )
{
this.itemName = Preconditions.checkNotNull( itemName );
Preconditions.checkArgument( !itemName.isEmpty() );
this.mode = Preconditions.checkNotNull( mode );
}
@Nullable
@Override
public String apply( @Nullable final ItemStack input )
{
if( input == null )
{
AELog.debug( EXTRACTING_NULL_MESSAGE );
return null;
}
else
{
AELog.debug( EXTRACTING_ITEM_MESSAGE, input.getDisplayName(), input.getItemDamage() );
}
final List<String> joinedBlockAttributes = Lists.newArrayListWithCapacity( 5 );
final int meta = input.getItemDamage();
final String metaName = this.itemName + ':' + meta;
final String localization = input.getDisplayName();
joinedBlockAttributes.add( metaName );
joinedBlockAttributes.add( localization );
if( this.mode == ExportMode.VERBOSE )
{
final Item item = input.getItem();
final String unlocalizedItem = input.getUnlocalizedName();
final Block block = Block.getBlockFromItem( item );
final boolean isBlock = !block.equals( Blocks.air );
final Class<? extends ItemStack> stackClass = input.getClass();
final String stackClassName = stackClass.getName();
joinedBlockAttributes.add( unlocalizedItem );
joinedBlockAttributes.add( Boolean.toString( isBlock ) );
joinedBlockAttributes.add( stackClassName );
}
final Joiner csvJoiner = Joiner.on( ", " );
final Joiner csvJoinerIgnoringNulls = csvJoiner.skipNulls();
return csvJoinerIgnoringNulls.join( joinedBlockAttributes );
}
}
/**
* transforms an item into a row representation of the CSV file
*/
private static final class ItemRowExtractFunction implements Function<Item, String>
{
/**
* this extension is required to apply the {@link StatCollector}
*/
private static final String LOCALIZATION_NAME_EXTENSION = ".name";
private static final String EXPORTING_NOTHING_MESSAGE = "Exporting nothing";
private static final String EXPORTING_SUBTYPES_MESSAGE = "Exporting input %s with subtypes: %b";
@Nonnull
private final FMLControlledNamespacedRegistry<Item> itemRegistry;
@Nonnull
private final ExportMode mode;
/**
* @param itemRegistry used to retrieve the name of the item
* @param mode extracts more or less information from item depending on mode
*/
ItemRowExtractFunction( @Nonnull final FMLControlledNamespacedRegistry<Item> itemRegistry, @Nonnull final ExportMode mode )
{
this.itemRegistry = Preconditions.checkNotNull( itemRegistry );
this.mode = Preconditions.checkNotNull( mode );
}
@Nullable
@Override
public String apply( @Nullable final Item input )
{
if( input == null )
{
AELog.debug( EXPORTING_NOTHING_MESSAGE );
return null;
}
else
{
AELog.debug( EXPORTING_SUBTYPES_MESSAGE, input.getUnlocalizedName(), input.getHasSubtypes() );
}
final String itemName = this.itemRegistry.getNameForObject( input );
final boolean hasSubtypes = input.getHasSubtypes();
if( hasSubtypes )
{
final CreativeTabs creativeTab = input.getCreativeTab();
final List<ItemStack> stacks = Lists.newArrayList();
// modifies the stacks list and adds the different sub types to it
try
{
input.getSubItems( input, creativeTab, stacks );
}
catch( final Exception ignored )
{
AELog.error( ignored );
// ignore if mods do bullshit in their code
return null;
}
// list can be empty, no clue why
if( stacks.isEmpty() )
{
return null;
}
final Joiner newLineJoiner = Joiner.on( '\n' );
final Joiner typeJoiner = newLineJoiner.skipNulls();
final List<String> transformedTypes = Lists.transform( stacks, new TypeExtractFunction( itemName, this.mode ) );
return typeJoiner.join( transformedTypes );
}
final List<String> joinedBlockAttributes = Lists.newArrayListWithCapacity( 5 );
final String unlocalizedItem = input.getUnlocalizedName();
final String localization = StatCollector.translateToLocal( unlocalizedItem + LOCALIZATION_NAME_EXTENSION );
joinedBlockAttributes.add( itemName );
joinedBlockAttributes.add( localization );
if( this.mode == ExportMode.VERBOSE )
{
final Block block = Block.getBlockFromItem( input );
final boolean isBlock = !block.equals( Blocks.air );
final Class<? extends Item> itemClass = input.getClass();
final String itemClassName = itemClass.getName();
joinedBlockAttributes.add( unlocalizedItem );
joinedBlockAttributes.add( Boolean.toString( isBlock ) );
joinedBlockAttributes.add( itemClassName );
}
final Joiner csvJoiner = Joiner.on( ", " );
final Joiner csvJoinerIgnoringNulls = csvJoiner.skipNulls();
return csvJoinerIgnoringNulls.join( joinedBlockAttributes );
}
}
}

View File

@ -0,0 +1,92 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
import java.util.List;
import javax.annotation.Nonnull;
import com.google.common.base.Preconditions;
import org.apache.commons.codec.digest.DigestUtils;
import cpw.mods.fml.common.ModContainer;
/**
* Checks the cached digest against the current mods including their versions.
* Use the config to manipulate the process
*
* @author thatsIch
* @version rv3 - 01.09.2015
* @since rv3 - 01.09.2015
*/
final class ModListChecker implements Checker<List<ModContainer>>
{
private final String configHashValue;
@Nonnull
private final ExportConfig config;
/**
* @param config uses the config to retrieve the old hash of the mod list
*/
ModListChecker( @Nonnull final ExportConfig config )
{
this.config = Preconditions.checkNotNull( config );
this.configHashValue = Preconditions.checkNotNull( config.getCache() );
}
/**
* Compiles a list of all mods and their versions to a digest which is updated, if it differs from the config. This is used to elevate the need to export
* the csv once again, if no change was detected.
*
* @param modContainers all mods and their versions to check if a difference exists between the current instance and the previous instance
*
* @return CheckType.EQUAL if no change was detected
*/
@Nonnull
@Override
public CheckType isEqual( @Nonnull final List<ModContainer> modContainers )
{
Preconditions.checkNotNull( modContainers );
final StringBuilder builder = new StringBuilder();
for( final ModContainer container : modContainers )
{
builder.append( container.getModId() );
builder.append( container.getVersion() );
}
final String allModsAndVersions = builder.toString();
final String hex = DigestUtils.md5Hex( allModsAndVersions );
if( hex.equals( this.configHashValue ) )
{
return CheckType.EQUAL;
}
else
{
this.config.setCache( hex );
return CheckType.UNEQUAL;
}
}
}

View File

@ -0,0 +1,33 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
/**
* the export package is to export all the required information for recipes into a convenient CSV file
* often names are difficult to acquire without access to the internal names.
*
* To save from rescanning every start-up it can save a list of mods and their version
* and if only something changed, it requires to update the CSV.
*
* There is no explicit check if it was manually tempered
*
* @author thatsIch
* @version rv3 - 14.08.2015
* @since rv3 14.08.2015
*/
package appeng.services.export;

View File

@ -65,7 +65,7 @@ public final class VersionCheckerConfig
this.shouldPostChangelog = this.config.getBoolean( "changelog", "client", true, "If true, the player is getting a notification including changelog. Only happens if notification are enabled." );
}
public boolean isEnabled()
public boolean isVersionCheckingEnabled()
{
return this.isEnabled;
}
@ -76,8 +76,7 @@ public final class VersionCheckerConfig
}
/**
* Stores the current date in milli seconds into the "lastCheck" field of the config
* and makes it persistent.
* Stores the current date in milli seconds into the "lastCheck" field of the config and makes it persistent.
*/
public void updateLastCheck()
{
@ -109,4 +108,12 @@ public final class VersionCheckerConfig
{
return this.shouldPostChangelog;
}
public void save()
{
if( this.config.hasChanged() )
{
this.config.save();
}
}
}

View File

@ -1,6 +1,6 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@ -23,6 +23,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.util.BlockPos;
import net.minecraft.util.Vec3;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.biome.WorldChunkManagerHell;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider;
@ -30,6 +31,7 @@ import net.minecraftforge.client.IRenderHandler;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import appeng.client.render.SpatialSkyRender;
import appeng.core.AppEng;
import appeng.core.Registration;
@ -44,7 +46,11 @@ public class StorageWorldProvider extends WorldProvider
@Override
protected void registerWorldChunkManager()
{
super.worldChunkMgr = new WorldChunkManagerHell( Registration.INSTANCE.storageBiome, 0.0F );
final AppEng ae2internal = AppEng.instance();
final Registration ae2registration = ae2internal.getRegistration();
final BiomeGenBase storageBiome = ae2registration.getStorageBiome();
super.worldChunkMgr = new WorldChunkManagerHell( storageBiome, 0.0F );
}
@Override

View File

@ -1,6 +1,6 @@
import=processing/factorization.recipe
import=processing/grind.recipe
import=processing/hydralicraft.recipe
import=processing/hydraulicraft.recipe
import=processing/ic2.recipe
import=processing/mekanism.recipe
import=processing/rotarycraft.recipe