Applied-Energistics-2-tiler.../recipes/RecipeHandler.java
AlgorithmX2 49fcc7f510 Renamed Inscriber Recipe Type to Inscribe.
Missing Icon now supports Items.
Fixed Missing Icons for debug Items.
SkyStone Chests can now be disabled.
More work on Website Recipes.
Removed Unused method in Network Handler.
2014-03-08 21:35:53 -06:00

548 lines
12 KiB
Java

package appeng.recipes;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import net.minecraft.item.ItemStack;
import appeng.api.AEApi;
import appeng.api.exceptions.MissingIngredientError;
import appeng.api.exceptions.RecipeError;
import appeng.api.exceptions.RegistrationError;
import appeng.api.features.IRecipeHandlerRegistry;
import appeng.api.recipes.ICraftHandler;
import appeng.api.recipes.IIngredient;
import appeng.api.recipes.IRecipeHandler;
import appeng.api.recipes.IRecipeLoader;
import appeng.core.AEConfig;
import appeng.core.AELog;
import appeng.core.features.AEFeature;
import appeng.items.materials.ItemMaterial;
import appeng.items.parts.ItemPart;
import appeng.recipes.handlers.IWebsiteSeralizer;
import appeng.recipes.handlers.OreRegistration;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.GameRegistry.UniqueIdentifier;
public class RecipeHandler implements IRecipeHandler
{
final public List<String> tokens = new LinkedList<String>();
final RecipeData data;
public RecipeHandler() {
data = new RecipeData();
}
RecipeHandler(RecipeHandler parent) {
data = parent.data;
}
private void addCrafting(ICraftHandler ch)
{
data.Handlers.add( ch );
}
public List<IWebsiteSeralizer> findRecipe(ItemStack output)
{
List<IWebsiteSeralizer> out = new LinkedList<IWebsiteSeralizer>();
for (ICraftHandler ch : data.Handlers)
{
try
{
if ( ch instanceof IWebsiteSeralizer && ((IWebsiteSeralizer) ch).canCraft( output ) )
{
out.add( (IWebsiteSeralizer) ch );
}
}
catch (Throwable t)
{
AELog.error( t );
}
}
return out;
}
@Override
public void registerHandlers()
{
HashMap<Class, Integer> processed = new HashMap<Class, Integer>();
try
{
for (ICraftHandler ch : data.Handlers)
{
try
{
ch.register();
Class clz = ch.getClass();
Integer i = processed.get( clz );
if ( i == null )
processed.put( clz, 1 );
else
processed.put( clz, i + 1 );
}
catch (RegistrationError e)
{
AELog.warning( "Unable to regsiter a recipe: " + e.getMessage() );
if ( data.exceptions )
AELog.error( e );
if ( data.crash )
throw e;
}
catch (MissingIngredientError e)
{
if ( data.erroronmissing )
{
AELog.warning( "Unable to regsiter a recipe:" + e.getMessage() );
if ( data.exceptions )
AELog.error( e );
if ( data.crash )
throw e;
}
}
}
}
catch (Throwable e)
{
if ( data.exceptions )
AELog.error( e );
if ( data.crash )
throw new RuntimeException( e );
}
for (Entry<Class, Integer> e : processed.entrySet())
{
AELog.info( "Recipes Loading: " + e.getKey().getSimpleName() + ": " + e.getValue() + " loaded." );
}
if ( AEConfig.instance.isFeatureEnabled( AEFeature.WebsiteRecipes ) )
{
try
{
ZipOutputStream out = new ZipOutputStream( new FileOutputStream( "recipes.zip" ) );
for (String s : data.knownItem)
{
try
{
Ingredient i = new Ingredient( this, s, 1 );
for (ItemStack is : i.getItemStackSet())
{
List<IWebsiteSeralizer> recipes = findRecipe( is );
if ( !recipes.isEmpty() )
{
int offset = 0;
String realName = getName( is );
for (IWebsiteSeralizer ws : recipes)
{
out.putNextEntry( new ZipEntry( realName + "_" + offset + ".txt" ) );
offset++;
out.write( ws.getPattern( this ).getBytes() );
}
}
}
}
catch (Throwable t)
{
// :P
}
}
out.close();
}
catch (FileNotFoundException e1)
{
AELog.error( e1 );
}
catch (IOException e1)
{
AELog.error( e1 );
}
}
}
public String getName(IIngredient i)
{
try
{
for (ItemStack is : i.getItemStackSet())
return getName( is );
}
catch (Throwable t)
{
t.printStackTrace();
// :P
}
return i.getNameSpace() + ":" + i.getItemName();
}
public String getName(ItemStack is)
{
UniqueIdentifier id = GameRegistry.findUniqueIdentifierFor( is.getItem() );
String realName = id.modId + ":" + id.name;
if ( is.getItem() == AEApi.instance().blocks().blockSkyStone.item() )
{
switch (is.getItemDamage())
{
case 1:
realName += ".Block";
break;
case 2:
realName += ".Brick";
break;
case 3:
realName += ".SmallBrick";
break;
default:
}
}
else if ( is.getItem() == AEApi.instance().blocks().blockSkyChest.item() )
{
switch (is.getItemDamage())
{
case 1:
realName += ".Block";
break;
default:
}
}
else if ( is.getItem() instanceof ItemMaterial )
realName += "." + ((ItemMaterial) is.getItem()).getTypeByStack( is ).name();
else if ( is.getItem() instanceof ItemPart )
realName += "." + ((ItemPart) is.getItem()).getTypeByStack( is ).name();
else if ( is.getItemDamage() > 0 )
realName += "." + is.getItemDamage();
return realName;
}
public String alias(String in)
{
String out = data.aliases.get( in );
if ( out != null )
return out;
return in;
}
@Override
public void parseRecipes(IRecipeLoader loader, String path)
{
try
{
BufferedReader reader = null;
try
{
reader = loader.getFile( path );
}
catch (Exception err)
{
AELog.warning( "Error Loading Recipe File:" + path );
if ( data.exceptions )
AELog.error( err );
return;
}
boolean inQuote = false;
boolean inComment = false;
String token = "";
int line = 0;
int val = -1;
while ((val = reader.read()) != -1)
{
char c = (char) val;
if ( c == '\n' )
line++;
if ( inComment )
{
if ( c == '\n' || c == '\r' )
inComment = false;
}
else if ( inQuote )
{
switch (c)
{
case '"':
inQuote = !inQuote;
break;
default:
token = token + c;
}
}
else
{
switch (c)
{
case '"':
inQuote = !inQuote;
break;
case ',':
if ( token.length() > 0 )
{
tokens.add( token );
tokens.add( "," );
}
token = "";
break;
case '=':
processTokens( loader, path, line );
if ( token.length() > 0 )
tokens.add( token );
token = "";
break;
case '#':
inComment = true;
// then add a token if you can...
case '\n':
case '\t':
case '\r':
case ' ':
if ( token.length() > 0 )
tokens.add( token );
token = "";
break;
default:
token = token + c;
}
}
}
reader.close();
processTokens( loader, path, line );
}
catch (Throwable e)
{
AELog.error( e );
if ( data.crash )
throw new RuntimeException( e );
}
}
private void processTokens(IRecipeLoader loader, String file, int line) throws RecipeError
{
try
{
IRecipeHandlerRegistry cr = AEApi.instance().registries().recipes();
if ( tokens.isEmpty() )
return;
int split = tokens.indexOf( "->" );
if ( split != -1 )
{
String operation = tokens.remove( 0 ).toLowerCase();
if ( operation.equals( "alias" ) )
{
if ( tokens.size() == 3 && tokens.indexOf( "->" ) == 1 )
data.aliases.put( tokens.get( 0 ), tokens.get( 2 ) );
else
throw new RecipeError( "Alias must have exactly 1 input and 1 output." );
}
else if ( operation.equals( "group" ) )
{
List<String> pre = tokens.subList( 0, split - 1 );
List<String> post = tokens.subList( split, tokens.size() );
List<List<IIngredient>> inputs = parseLines( pre );
if ( inputs.size() == 1 && inputs.get( 0 ).size() > 0 && post.size() == 1 )
{
data.groups.put( post.get( 0 ), new GroupIngredient( post.get( 0 ), inputs.get( 0 ) ) );
}
else
throw new RecipeError( "Group must have exactly 1 output, and 1 or more inputs." );
}
else if ( operation.equals( "ore" ) )
{
List<String> pre = tokens.subList( 0, split - 1 );
List<String> post = tokens.subList( split, tokens.size() );
List<List<IIngredient>> inputs = parseLines( pre );
if ( inputs.size() == 1 && inputs.get( 0 ).size() > 0 && post.size() == 1 )
{
ICraftHandler ch = new OreRegistration( inputs.get( 0 ), post.get( 0 ) );
addCrafting( ch );
}
else
throw new RecipeError( "Group must have exactly 1 output, and 1 or more inputs in a single row." );
}
else
{
List<String> pre = tokens.subList( 0, split - 1 );
List<String> post = tokens.subList( split, tokens.size() );
List<List<IIngredient>> inputs = parseLines( pre );
List<List<IIngredient>> outputs = parseLines( post );
ICraftHandler ch = cr.getCraftHandlerFor( operation );
if ( ch != null )
{
ch.setup( inputs, outputs );
addCrafting( ch );
}
else
throw new RecipeError( "Invalid crafting type: " + operation );
}
}
else
{
String operation = tokens.remove( 0 ).toLowerCase();
if ( operation.equals( "exceptions" ) && (tokens.get( 0 ).equals( "true" ) || tokens.get( 0 ).equals( "false" )) )
{
if ( tokens.size() == 1 )
{
data.exceptions = tokens.get( 0 ).equals( "true" );
}
else
throw new RecipeError( "exceptions must be true or false explicitly." );
}
else if ( operation.equals( "crash" ) && (tokens.get( 0 ).equals( "true" ) || tokens.get( 0 ).equals( "false" )) )
{
if ( tokens.size() == 1 )
{
data.crash = tokens.get( 0 ).equals( "true" );
}
else
throw new RecipeError( "crash must be true or false explicitly." );
}
else if ( operation.equals( "erroronmissing" ) )
{
if ( tokens.size() == 1 && (tokens.get( 0 ).equals( "true" ) || tokens.get( 0 ).equals( "false" )) )
{
data.erroronmissing = tokens.get( 0 ).equals( "true" );
}
else
throw new RecipeError( "erroronmissing must be true or false explicitly." );
}
else if ( operation.equals( "import" ) )
{
if ( tokens.size() == 1 )
(new RecipeHandler( this )).parseRecipes( loader, tokens.get( 0 ) );
else
throw new RecipeError( "Import must have exactly 1 input." );
}
else
throw new RecipeError( operation + ": " + tokens.toString() + "; recipe without an output." );
}
}
catch (RecipeError e)
{
AELog.warning( "Recipe Error '" + e.getMessage() + "' near line:" + line + " in " + file + " with: " + tokens.toString() );
if ( data.exceptions )
AELog.error( e );
if ( data.crash )
throw e;
}
tokens.clear();
}
private List<List<IIngredient>> parseLines(List<String> subList) throws RecipeError
{
List<List<IIngredient>> out = new LinkedList<List<IIngredient>>();
List<IIngredient> cList = new LinkedList<IIngredient>();
boolean hasQty = false;
int qty = 1;
for (String v : subList)
{
if ( v.equals( "," ) )
{
if ( hasQty )
throw new RecipeError( "Qty found with no item." );
if ( !cList.isEmpty() )
out.add( cList );
cList = new LinkedList<IIngredient>();
}
else
{
if ( isNumber( v ) )
{
if ( hasQty )
throw new RecipeError( "Qty found with no item." );
hasQty = true;
qty = Integer.parseInt( v );
}
else
{
if ( hasQty )
{
cList.add( findIngrident( v, qty ) );
hasQty = false;
}
else
cList.add( findIngrident( v, 1 ) );
}
}
}
if ( !cList.isEmpty() )
out.add( cList );
return out;
}
private IIngredient findIngrident(String v, int qty) throws RecipeError
{
GroupIngredient gi = data.groups.get( v );
if ( gi != null )
return gi.copy( qty );
data.knownItem.add( v );
return new Ingredient( this, v, qty );
}
private boolean isNumber(String v)
{
if ( v.length() <= 0 )
return false;
int l = v.length();
for (int x = 0; x < l; x++)
{
if ( !Character.isDigit( v.charAt( x ) ) )
return false;
}
return true;
}
}