diff --git a/build.gradle b/build.gradle index 767f48d9..3fdd36f7 100644 --- a/build.gradle +++ b/build.gradle @@ -45,10 +45,6 @@ buildscript { } } -configurations.all { - resolutionStrategy.cacheDynamicVersionsFor 7200, 'hours' -} - sourceCompatibility = JavaVersion.VERSION_1_6 targetCompatibility = JavaVersion.VERSION_1_6 diff --git a/gradle.properties b/gradle.properties index 747423ed..a709a63a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ invtweaks_version=1.58 ######################################################### 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.12.26 diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle index e92d1418..c459e18f 100644 --- a/gradle/scripts/dependencies.gradle +++ b/gradle/scripts/dependencies.gradle @@ -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" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 117efacb..de04b8d0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Mon Aug 31 16:41:19 CEST 2015 +#Tue Sep 01 22:00:39 CEST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/appeng/block/crafting/ItemCraftingStorage.java b/src/main/java/appeng/block/crafting/ItemCraftingStorage.java index 28a34d90..22bb483e 100644 --- a/src/main/java/appeng/block/crafting/ItemCraftingStorage.java +++ b/src/main/java/appeng/block/crafting/ItemCraftingStorage.java @@ -50,6 +50,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 ); } } diff --git a/src/main/java/appeng/core/AEConfig.java b/src/main/java/appeng/core/AEConfig.java index b5db31d5..e9b592a2 100644 --- a/src/main/java/appeng/core/AEConfig.java +++ b/src/main/java/appeng/core/AEConfig.java @@ -171,7 +171,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 ) ) { diff --git a/src/main/java/appeng/core/AELog.java b/src/main/java/appeng/core/AELog.java index 554f324a..7795c0fe 100644 --- a/src/main/java/appeng/core/AELog.java +++ b/src/main/java/appeng/core/AELog.java @@ -100,4 +100,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 ); + } + } } diff --git a/src/main/java/appeng/core/AppEng.java b/src/main/java/appeng/core/AppEng.java index 5725656a..1d87b159 100644 --- a/src/main/java/appeng/core/AppEng.java +++ b/src/main/java/appeng/core/AppEng.java @@ -21,11 +21,12 @@ package appeng.core; import java.io.File; import java.util.concurrent.TimeUnit; - import javax.annotation.Nonnull; import com.google.common.base.Stopwatch; +import net.minecraftforge.common.config.Configuration; + import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Loader; import cpw.mods.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; @@ -74,15 +80,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; AppEng() { - this.imcHandler = new IMCHandler(); - FMLCommonHandler.instance().registerCrashCallable( new ModCrashEnhancement( CrashInfo.MOD_VERSION ) ); + + this.registration = new Registration(); } @Nonnull @@ -92,9 +111,10 @@ public final class AppEng return INSTANCE; } - public final File getConfigDirectory() + @Nonnull + public final Registration getRegistration() { - return this.configDirectory; + return this.registration; } @EventHandler @@ -107,14 +127,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 )" ); @@ -129,9 +154,9 @@ public final class AppEng CommonHelper.proxy.init(); } - 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 ); @@ -154,22 +179,30 @@ 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(); - 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() ); @@ -179,13 +212,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 )" ); + 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 diff --git a/src/main/java/appeng/core/RecipeLoader.java b/src/main/java/appeng/core/RecipeLoader.java index ea5e021b..1b9a2464 100644 --- a/src/main/java/appeng/core/RecipeLoader.java +++ b/src/main/java/appeng/core/RecipeLoader.java @@ -22,7 +22,6 @@ package appeng.core; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; - import javax.annotation.Nonnull; import com.google.common.base.Preconditions; @@ -30,6 +29,7 @@ import com.google.common.base.Preconditions; 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 appeng.recipes.loader.RecipeResourceCopier; */ 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 null */ - 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" ); } } } diff --git a/src/main/java/appeng/core/Registration.java b/src/main/java/appeng/core/Registration.java index 259a09b1..845ea8a4 100644 --- a/src/main/java/appeng/core/Registration.java +++ b/src/main/java/appeng/core/Registration.java @@ -19,6 +19,11 @@ 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; @@ -88,6 +93,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; @@ -117,18 +123,21 @@ import appeng.worldgen.QuartzWorldGen; public final class Registration { - public static final Registration INSTANCE = new Registration(); - private final RecipeHandler recipeHandler; private final DefinitionConverter converter; - public BiomeGenBase storageBiome; + private BiomeGenBase storageBiome; - private Registration() + Registration() { this.converter = new DefinitionConverter(); this.recipeHandler = new RecipeHandler(); } + public BiomeGenBase getStorageBiome() + { + return this.storageBiome; + } + public void preInitialize( final FMLPreInitializationEvent event ) { this.registerSpatial( false ); @@ -503,8 +512,13 @@ public final class Registration target.itemLumenPaintBall = source.coloredLumenPaintBall(); } - 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(); @@ -512,7 +526,7 @@ public final class Registration // Perform ore camouflage! ItemMultiMaterial.instance.makeUnique(); - final Runnable recipeLoader = new RecipeLoader( this.recipeHandler ); + final Runnable recipeLoader = new RecipeLoader( recipeDirectory, customRecipeConfig, this.recipeHandler ); recipeLoader.run(); partHelper.registerNewLayer( "appeng.parts.layers.LayerISidedInventory", "net.minecraft.inventory.ISidedInventory" ); @@ -554,13 +568,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" ); diff --git a/src/main/java/appeng/core/features/AEFeature.java b/src/main/java/appeng/core/features/AEFeature.java index f0ed7fa1..36f44be8 100644 --- a/src/main/java/appeng/core/features/AEFeature.java +++ b/src/main/java/appeng/core/features/AEFeature.java @@ -21,60 +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" ), + 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 ), - DecorativeLights( "World" ), DecorativeQuartzBlocks( "World" ), SkyStoneChests( "World" ), SpawnPressesInMeteorites( "World" ), + PoweredTools( Constants.CATEGORY_TOOLS_CLASSIFICATIONS ), + CertusQuartzTools( Constants.CATEGORY_TOOLS_CLASSIFICATIONS ), + NetherQuartzTools( Constants.CATEGORY_TOOLS_CLASSIFICATIONS ), - GrindStone( "World" ), Flour( "World" ), Inscriber( "World" ), + 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 ), - ChestLoot( "World" ), VillagerTrading( "World" ), + 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 ), - TinyTNT( "World" ), + 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 ), - PoweredTools( "ToolsClassifications" ), + PortableCell( Constants.CATEGORY_PORTABLE_CELL ), - CertusQuartzTools( "ToolsClassifications" ), + StorageCells( Constants.CATEGORY_STORAGE ), + MEChest( Constants.CATEGORY_STORAGE ), + MEDrive( Constants.CATEGORY_STORAGE ), + IOPort( Constants.CATEGORY_STORAGE ), - NetherQuartzTools( "ToolsClassifications" ), + NetworkTool( Constants.CATEGORY_NETWORK_TOOL ), - QuartzHoe( "Tools" ), QuartzSpade( "Tools" ), QuartzSword( "Tools" ), QuartzPickaxe( "Tools" ), QuartzAxe( "Tools" ), QuartzKnife( "Tools" ), QuartzWrench( "Tools" ), + DenseEnergyCells( Constants.CATEGORY_HIGHER_CAPACITY ), + DenseCables( Constants.CATEGORY_HIGHER_CAPACITY ), - ChargedStaff( "Tools" ), EntropyManipulator( "Tools" ), MatterCannon( "Tools" ), WirelessAccessTerminal( "Tools" ), ColorApplicator( "Tools" ), + 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 ), - CraftingCPU( "CraftingFeatures" ), PowerGen( "NetworkFeatures" ), Security( "NetworkFeatures" ), + MassCannonBlockDamage( Constants.CATEGORY_BLOCK_FEATURES ), + TinyTNTBlockDamage( Constants.CATEGORY_BLOCK_FEATURES ), - SpatialIO( "NetworkFeatures" ), QuantumNetworkBridge( "NetworkFeatures" ), Channels( "NetworkFeatures" ), + Facades( Constants.CATEGORY_FACADES ), - LevelEmitter( "NetworkBuses" ), CraftingTerminal( "NetworkBuses" ), StorageMonitor( "NetworkBuses" ), P2PTunnel( "NetworkBuses" ), FormationPlane( "NetworkBuses" ), AnnihilationPlane( "NetworkBuses" ), IdentityAnnihilationPlane( "NetworkBuses" ), ImportBus( "NetworkBuses" ), ExportBus( "NetworkBuses" ), StorageBus( "NetworkBuses" ), PartConversionMonitor( "NetworkBuses" ), + 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 ), - StorageCells( "Storage" ), PortableCell( "PortableCell" ), MEChest( "Storage" ), MEDrive( "Storage" ), IOPort( "Storage" ), + EnableFacadeCrafting( Constants.CATEGORY_CRAFTING ), + InWorldSingularity( Constants.CATEGORY_CRAFTING ), + InWorldFluix( Constants.CATEGORY_CRAFTING ), + InWorldPurification( Constants.CATEGORY_CRAFTING ), + InterfaceTerminal( Constants.CATEGORY_CRAFTING ), + EnableDisassemblyCrafting( Constants.CATEGORY_CRAFTING ), - NetworkTool( "NetworkTool" ), + AlphaPass( Constants.CATEGORY_RENDERING ), PaintBalls( Constants.CATEGORY_TOOLS ), - DenseEnergyCells( "HigherCapacity" ), DenseCables( "HigherCapacity" ), + MolecularAssembler( Constants.CATEGORY_CRAFTING_FEATURES ), + Patterns( Constants.CATEGORY_CRAFTING_FEATURES ), + CraftingCPU( Constants.CATEGORY_CRAFTING_FEATURES ), - P2PTunnelRF( "P2PTunnels" ), P2PTunnelME( "P2PTunnels" ), P2PTunnelItems( "P2PTunnels" ), P2PTunnelRedstone( "P2PTunnels" ), P2PTunnelEU( "P2PTunnels" ), P2PTunnelLiquids( "P2PTunnels" ), P2PTunnelLight( "P2PTunnels" ), P2PTunnelOpenComputers( "P2PTunnels" ), P2PTunnelPressure( "P2PTunnels" ), - - MassCannonBlockDamage( "BlockFeatures" ), TinyTNTBlockDamage( "BlockFeatures" ), Facades( "Facades" ), - - UnsupportedDeveloperTools( "Misc", false ), Creative( "Misc" ), - - GrinderLogging( "Misc", false ), Logging( "Misc" ), IntegrationLogging( "Misc", false ), WebsiteRecipes( "Misc", false ), - - enableFacadeCrafting( "Crafting" ), inWorldSingularity( "Crafting" ), inWorldFluix( "Crafting" ), inWorldPurification( "Crafting" ), UpdateLogging( "Misc", false ), - - AlphaPass( "Rendering" ), PaintBalls( "Tools" ), PacketLogging( "Misc", false ), CraftingLog( "Misc", false ), InterfaceTerminal( "Crafting" ), LightDetector( "Misc" ), - - enableDisassemblyCrafting( "Crafting" ), MolecularAssembler( "CraftingFeatures" ), MeteoriteCompass( "Tools" ), Patterns( "CraftingFeatures" ), - - ChunkLoggerTrace( "Commands", false ), LogSecurityAudits( "Misc", false ), Achievements( "Misc" ); + ChunkLoggerTrace( Constants.CATEGORY_COMMANDS, false ); public final String category; - public final boolean isVisible; public final boolean defaultValue; AEFeature( final String cat ) @@ -85,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"; + } } diff --git a/src/main/java/appeng/entity/EntityChargedQuartz.java b/src/main/java/appeng/entity/EntityChargedQuartz.java index 4f48866c..0bdd1647 100644 --- a/src/main/java/appeng/entity/EntityChargedQuartz.java +++ b/src/main/java/appeng/entity/EntityChargedQuartz.java @@ -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; } diff --git a/src/main/java/appeng/entity/EntityGrowingCrystal.java b/src/main/java/appeng/entity/EntityGrowingCrystal.java index 15038e5b..19f19c02 100644 --- a/src/main/java/appeng/entity/EntityGrowingCrystal.java +++ b/src/main/java/appeng/entity/EntityGrowingCrystal.java @@ -57,7 +57,7 @@ public final class EntityGrowingCrystal extends EntityItem { super.onUpdate(); - if( !AEConfig.instance.isFeatureEnabled( AEFeature.inWorldPurification ) ) + if( !AEConfig.instance.isFeatureEnabled( AEFeature.InWorldPurification ) ) { return; } diff --git a/src/main/java/appeng/entity/EntitySingularity.java b/src/main/java/appeng/entity/EntitySingularity.java index 521fc893..230da9c0 100644 --- a/src/main/java/appeng/entity/EntitySingularity.java +++ b/src/main/java/appeng/entity/EntitySingularity.java @@ -74,7 +74,7 @@ public final class EntitySingularity extends AEBaseEntityItem return; } - if( !AEConfig.instance.isFeatureEnabled( AEFeature.inWorldSingularity ) ) + if( !AEConfig.instance.isFeatureEnabled( AEFeature.InWorldSingularity ) ) { return; } diff --git a/src/main/java/appeng/integration/modules/NEI.java b/src/main/java/appeng/integration/modules/NEI.java index 9a399e77..e9f45ba5 100644 --- a/src/main/java/appeng/integration/modules/NEI.java +++ b/src/main/java/appeng/integration/modules/NEI.java @@ -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() ); } diff --git a/src/main/java/appeng/integration/modules/NEIHelpers/NEIWorldCraftingHandler.java b/src/main/java/appeng/integration/modules/NEIHelpers/NEIWorldCraftingHandler.java index b5c8f4aa..3de7724b 100644 --- a/src/main/java/appeng/integration/modules/NEIHelpers/NEIWorldCraftingHandler.java +++ b/src/main/java/appeng/integration/modules/NEIHelpers/NEIWorldCraftingHandler.java @@ -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() ); diff --git a/src/main/java/appeng/items/materials/MaterialType.java b/src/main/java/appeng/items/materials/MaterialType.java index 1d1c1fb3..b01bb983 100644 --- a/src/main/java/appeng/items/materials/MaterialType.java +++ b/src/main/java/appeng/items/materials/MaterialType.java @@ -42,47 +42,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 features; // IIcon for the material. diff --git a/src/main/java/appeng/items/storage/ItemBasicStorageCell.java b/src/main/java/appeng/items/storage/ItemBasicStorageCell.java index 98aeb72e..495f3b4e 100644 --- a/src/main/java/appeng/items/storage/ItemBasicStorageCell.java +++ b/src/main/java/appeng/items/storage/ItemBasicStorageCell.java @@ -308,6 +308,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 ); } } diff --git a/src/main/java/appeng/recipes/CustomRecipeConfig.java b/src/main/java/appeng/recipes/CustomRecipeConfig.java new file mode 100644 index 00000000..08829ca9 --- /dev/null +++ b/src/main/java/appeng/recipes/CustomRecipeConfig.java @@ -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 . + */ + +package appeng.recipes; + + +/** + * @author thatsIch + * @version rv3 - 22.08.2015 + * @since rv3 22.08.2015 + */ +public interface CustomRecipeConfig +{ + boolean isEnabled(); +} diff --git a/src/main/java/appeng/recipes/CustomRecipeForgeConfiguration.java b/src/main/java/appeng/recipes/CustomRecipeForgeConfiguration.java new file mode 100644 index 00000000..db9980a0 --- /dev/null +++ b/src/main/java/appeng/recipes/CustomRecipeForgeConfiguration.java @@ -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 . + */ + +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; + } +} diff --git a/src/main/java/appeng/recipes/loader/ConfigLoader.java b/src/main/java/appeng/recipes/loader/ConfigLoader.java index ff753ad3..b0a94c94 100644 --- a/src/main/java/appeng/recipes/loader/ConfigLoader.java +++ b/src/main/java/appeng/recipes/loader/ConfigLoader.java @@ -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 com.google.common.base.Preconditions; @@ -31,6 +30,9 @@ import com.google.common.base.Preconditions; import appeng.api.recipes.IRecipeLoader; +/** + * Loads the recipes from the config folder + */ public final class ConfigLoader implements IRecipeLoader { private final File generatedRecipesDir; diff --git a/src/main/java/appeng/recipes/loader/RecipeResourceCopier.java b/src/main/java/appeng/recipes/loader/RecipeResourceCopier.java index 187d2b0b..38d67b6c 100644 --- a/src/main/java/appeng/recipes/loader/RecipeResourceCopier.java +++ b/src/main/java/appeng/recipes/loader/RecipeResourceCopier.java @@ -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 com.google.common.base.Preconditions; @@ -40,9 +41,7 @@ import org.apache.commons.io.FileUtils; /** - * 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 null + * @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 null * @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 + * @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 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 copierClass = this.getClass(); + final InputStream inStream = copierClass.getResourceAsStream( '/' + directory + fileName ); final File outFile = new File( destination, fileName ); if( !outFile.exists() && inStream != null ) @@ -146,30 +156,50 @@ public class RecipeResourceCopier } /** - * List directory contents for a resource folder. Not recursive. - * This is basically a brute-force implementation. - * Works for regular files and also JARs. + * 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. * * @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 ) @@ -178,43 +208,52 @@ public class RecipeResourceCopier * 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 ); + 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 String protocol = dirURL.getProtocol(); + if( protocol.equals( JAR_PROTOCOL ) ) { - final Enumeration entries = jar.entries(); // gives ALL entries in jar - final Collection result = new HashSet( INITIAL_RESOURCE_CAPACITY ); // avoid duplicates - // in case it is a - // subdirectory - while( entries.hasMoreElements() ) + /* 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 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 ); - } - } + final Enumeration entries = jar.entries(); // gives ALL entries in jar + final Collection result = new HashSet( INITIAL_RESOURCE_CAPACITY ); // avoid duplicates - return result.toArray( new String[result.size()] ); - } - finally - { - jar.close(); + // 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(); + } } } diff --git a/src/main/java/appeng/server/Commands.java b/src/main/java/appeng/server/Commands.java index c880ea9f..a07589c7 100644 --- a/src/main/java/appeng/server/Commands.java +++ b/src/main/java/appeng/server/Commands.java @@ -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; diff --git a/src/main/java/appeng/services/VersionChecker.java b/src/main/java/appeng/services/VersionChecker.java index f19103b6..8af91c1c 100644 --- a/src/main/java/appeng/services/VersionChecker.java +++ b/src/main/java/appeng/services/VersionChecker.java @@ -76,7 +76,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(); diff --git a/src/main/java/appeng/services/export/CheckType.java b/src/main/java/appeng/services/export/CheckType.java new file mode 100644 index 00000000..7b37e04b --- /dev/null +++ b/src/main/java/appeng/services/export/CheckType.java @@ -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 . + */ + +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 equal + */ + EQUAL, + + /** + * If checking resulted in both objects being unequal + */ + UNEQUAL +} diff --git a/src/main/java/appeng/services/export/Checker.java b/src/main/java/appeng/services/export/Checker.java new file mode 100644 index 00000000..1b896acf --- /dev/null +++ b/src/main/java/appeng/services/export/Checker.java @@ -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 . + */ + +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 +{ + /** + * @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 ); +} diff --git a/src/main/java/appeng/services/export/ExportConfig.java b/src/main/java/appeng/services/export/ExportConfig.java new file mode 100644 index 00000000..8d57e6cc --- /dev/null +++ b/src/main/java/appeng/services/export/ExportConfig.java @@ -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 . + */ + +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(); +} diff --git a/src/main/java/appeng/services/export/ExportMode.java b/src/main/java/appeng/services/export/ExportMode.java new file mode 100644 index 00000000..20f7ecfa --- /dev/null +++ b/src/main/java/appeng/services/export/ExportMode.java @@ -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 . + */ + +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 +} diff --git a/src/main/java/appeng/services/export/ExportProcess.java b/src/main/java/appeng/services/export/ExportProcess.java new file mode 100644 index 00000000..ab6a4cdc --- /dev/null +++ b/src/main/java/appeng/services/export/ExportProcess.java @@ -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 . + */ + +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> 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 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 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 ) ); + } +} diff --git a/src/main/java/appeng/services/export/Exporter.java b/src/main/java/appeng/services/export/Exporter.java new file mode 100644 index 00000000..4bd5aa64 --- /dev/null +++ b/src/main/java/appeng/services/export/Exporter.java @@ -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 . + */ + +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(); +} diff --git a/src/main/java/appeng/services/export/ForgeExportConfig.java b/src/main/java/appeng/services/export/ForgeExportConfig.java new file mode 100644 index 00000000..bcf448ba --- /dev/null +++ b/src/main/java/appeng/services/export/ForgeExportConfig.java @@ -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 . + */ + +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(); + } +} diff --git a/src/main/java/appeng/services/export/MinecraftItemCSVExporter.java b/src/main/java/appeng/services/export/MinecraftItemCSVExporter.java new file mode 100644 index 00000000..0ef8b79f --- /dev/null +++ b/src/main/java/appeng/services/export/MinecraftItemCSVExporter.java @@ -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 . + */ + +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 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 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 items = this.itemRegistry.typeSafeIterable(); + final List itemList = Lists.newArrayList( items ); + + final List 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 + { + 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 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 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 + { + /** + * 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 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 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 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 transformedTypes = Lists.transform( stacks, new TypeExtractFunction( itemName, this.mode ) ); + + return typeJoiner.join( transformedTypes ); + } + + final List 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 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 ); + } + } +} diff --git a/src/main/java/appeng/services/export/ModListChecker.java b/src/main/java/appeng/services/export/ModListChecker.java new file mode 100644 index 00000000..cf4e7f8b --- /dev/null +++ b/src/main/java/appeng/services/export/ModListChecker.java @@ -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 . + */ + +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> +{ + 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 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; + } + } +} diff --git a/src/main/java/appeng/services/export/package-info.java b/src/main/java/appeng/services/export/package-info.java new file mode 100644 index 00000000..de426424 --- /dev/null +++ b/src/main/java/appeng/services/export/package-info.java @@ -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 . + */ + +/** + * 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; \ No newline at end of file diff --git a/src/main/java/appeng/services/version/VersionCheckerConfig.java b/src/main/java/appeng/services/version/VersionCheckerConfig.java index 6c034bae..d69859ff 100644 --- a/src/main/java/appeng/services/version/VersionCheckerConfig.java +++ b/src/main/java/appeng/services/version/VersionCheckerConfig.java @@ -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(); + } + } } diff --git a/src/main/java/appeng/spatial/StorageWorldProvider.java b/src/main/java/appeng/spatial/StorageWorldProvider.java index 18198fef..7d74d3cb 100644 --- a/src/main/java/appeng/spatial/StorageWorldProvider.java +++ b/src/main/java/appeng/spatial/StorageWorldProvider.java @@ -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.ChunkCoordinates; 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; @@ -32,6 +33,7 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import appeng.client.render.SpatialSkyRender; +import appeng.core.AppEng; import appeng.core.Registration; @@ -46,7 +48,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 diff --git a/src/main/resources/assets/appliedenergistics2/recipes/network/cells/spatial-componets.recipe b/src/main/resources/assets/appliedenergistics2/recipes/network/cells/spatial-components.recipe similarity index 100% rename from src/main/resources/assets/appliedenergistics2/recipes/network/cells/spatial-componets.recipe rename to src/main/resources/assets/appliedenergistics2/recipes/network/cells/spatial-components.recipe diff --git a/src/main/resources/assets/appliedenergistics2/recipes/processing/index.recipe b/src/main/resources/assets/appliedenergistics2/recipes/processing/index.recipe index 9adbe987..fd14d0c7 100644 --- a/src/main/resources/assets/appliedenergistics2/recipes/processing/index.recipe +++ b/src/main/resources/assets/appliedenergistics2/recipes/processing/index.recipe @@ -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