2014-02-10 04:16:36 +01:00
|
|
|
package appeng.recipes;
|
|
|
|
|
2014-02-11 06:51:25 +01:00
|
|
|
import java.io.BufferedReader;
|
2014-02-10 04:16:36 +01:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
2014-02-11 06:51:25 +01:00
|
|
|
import java.util.Map.Entry;
|
2014-02-10 04:16:36 +01:00
|
|
|
|
2014-02-11 06:51:25 +01:00
|
|
|
import appeng.api.recipes.IRecipeHandler;
|
|
|
|
import appeng.api.recipes.IRecipeLoader;
|
2014-02-10 04:16:36 +01:00
|
|
|
import appeng.core.AELog;
|
|
|
|
import appeng.recipes.handlers.CraftHandler;
|
|
|
|
import appeng.recipes.handlers.Grind;
|
2014-02-11 06:51:25 +01:00
|
|
|
import appeng.recipes.handlers.OreRegistration;
|
|
|
|
import appeng.recipes.handlers.Pureify;
|
2014-02-10 04:16:36 +01:00
|
|
|
import appeng.recipes.handlers.Shaped;
|
2014-02-11 06:51:25 +01:00
|
|
|
import appeng.recipes.handlers.Shapeless;
|
2014-02-10 04:16:36 +01:00
|
|
|
import appeng.recipes.handlers.Smelt;
|
|
|
|
|
2014-02-11 06:51:25 +01:00
|
|
|
public class RecipeHandler implements IRecipeHandler
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
|
|
|
|
2014-02-11 06:51:25 +01:00
|
|
|
final public List<String> tokens = new LinkedList<String>();
|
|
|
|
final RecipeData data;
|
|
|
|
|
|
|
|
public RecipeHandler() {
|
|
|
|
data = new RecipeData();
|
|
|
|
}
|
|
|
|
|
|
|
|
RecipeHandler(RecipeHandler parent) {
|
|
|
|
data = parent.data;
|
|
|
|
}
|
2014-02-10 04:16:36 +01:00
|
|
|
|
|
|
|
private void addCrafting(CraftHandler ch)
|
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
data.Handlers.add( ch );
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|
|
|
|
|
2014-02-11 06:51:25 +01:00
|
|
|
@Override
|
2014-02-10 04:16:36 +01:00
|
|
|
public void registerHandlers()
|
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
HashMap<Class, Integer> processed = new HashMap<Class, Integer>();
|
2014-02-11 00:15:43 +01:00
|
|
|
try
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
for (CraftHandler ch : data.Handlers)
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
2014-02-11 00:15:43 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
ch.register();
|
2014-02-11 06:51:25 +01:00
|
|
|
|
|
|
|
Class clz = ch.getClass();
|
|
|
|
Integer i = processed.get( clz );
|
|
|
|
if ( i == null )
|
|
|
|
processed.put( clz, 1 );
|
|
|
|
else
|
|
|
|
processed.put( clz, i + 1 );
|
2014-02-11 00:15:43 +01:00
|
|
|
}
|
|
|
|
catch (RegistrationError e)
|
|
|
|
{
|
|
|
|
AELog.warning( "Unable to regsiter a recipe." );
|
|
|
|
AELog.error( e );
|
2014-02-11 06:51:25 +01:00
|
|
|
if ( data.crash )
|
|
|
|
throw e;
|
2014-02-11 00:15:43 +01:00
|
|
|
}
|
|
|
|
catch (MissingIngredientError e)
|
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
if ( data.erroronmissing )
|
2014-02-11 00:15:43 +01:00
|
|
|
{
|
|
|
|
AELog.warning( "Unable to regsiter a recipe." );
|
2014-02-11 06:51:25 +01:00
|
|
|
AELog.error( e );
|
|
|
|
if ( data.crash )
|
|
|
|
throw e;
|
2014-02-11 00:15:43 +01:00
|
|
|
}
|
|
|
|
}
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|
|
|
|
}
|
2014-02-11 06:51:25 +01:00
|
|
|
catch (Throwable e)
|
2014-02-11 00:15:43 +01:00
|
|
|
{
|
|
|
|
AELog.error( e );
|
2014-02-11 06:51:25 +01:00
|
|
|
if ( data.crash )
|
|
|
|
throw new RuntimeException( e );
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Entry<Class, Integer> e : processed.entrySet())
|
|
|
|
{
|
|
|
|
AELog.info( "Recipes Loading: " + e.getKey().getSimpleName() + ": " + e.getValue() + " loaded." );
|
2014-02-11 00:15:43 +01:00
|
|
|
}
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public String alias(String in)
|
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
String out = data.aliases.get( in );
|
2014-02-10 04:16:36 +01:00
|
|
|
|
|
|
|
if ( out != null )
|
|
|
|
return out;
|
|
|
|
|
|
|
|
return in;
|
|
|
|
}
|
2014-02-11 06:51:25 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void parseRecipes(IRecipeLoader loader, String path)
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
2014-02-11 00:15:43 +01:00
|
|
|
try
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
BufferedReader reader = null;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
reader = loader.getFile( path );
|
|
|
|
}
|
|
|
|
catch (Exception err)
|
|
|
|
{
|
|
|
|
AELog.warning( "Error Loading Recipe File:" + path );
|
|
|
|
AELog.error( err );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
boolean inQuote = false;
|
2014-02-11 06:51:25 +01:00
|
|
|
boolean inComment = false;
|
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
String token = "";
|
|
|
|
int line = 0;
|
2014-02-11 06:51:25 +01:00
|
|
|
|
|
|
|
int val = -1;
|
|
|
|
while ((val = reader.read()) != -1)
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
char c = (char) val;
|
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
if ( c == '\n' )
|
|
|
|
line++;
|
2014-02-11 06:51:25 +01:00
|
|
|
|
|
|
|
if ( inComment )
|
|
|
|
{
|
|
|
|
if ( c == '\n' || c == '\r' )
|
|
|
|
inComment = false;
|
|
|
|
}
|
|
|
|
else if ( inQuote )
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
2014-02-11 00:15:43 +01:00
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case '"':
|
|
|
|
inQuote = !inQuote;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
token = token + c;
|
|
|
|
}
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|
2014-02-11 00:15:43 +01:00
|
|
|
else
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
2014-02-11 00:15:43 +01:00
|
|
|
switch (c)
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
2014-02-11 00:15:43 +01:00
|
|
|
case '"':
|
|
|
|
inQuote = !inQuote;
|
|
|
|
break;
|
|
|
|
case ',':
|
2014-02-11 06:51:25 +01:00
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
if ( token.length() > 0 )
|
|
|
|
{
|
|
|
|
tokens.add( token );
|
|
|
|
tokens.add( "," );
|
|
|
|
}
|
|
|
|
token = "";
|
|
|
|
break;
|
2014-02-11 06:51:25 +01:00
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
case '=':
|
2014-02-11 06:51:25 +01:00
|
|
|
|
|
|
|
processTokens( loader, path, line );
|
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
if ( token.length() > 0 )
|
|
|
|
tokens.add( token );
|
|
|
|
token = "";
|
2014-02-11 06:51:25 +01:00
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
break;
|
2014-02-11 06:51:25 +01:00
|
|
|
|
|
|
|
case '#':
|
|
|
|
inComment = true;
|
|
|
|
// then add a token if you can...
|
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
case '\n':
|
|
|
|
case '\t':
|
|
|
|
case '\r':
|
|
|
|
case ' ':
|
2014-02-11 06:51:25 +01:00
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
if ( token.length() > 0 )
|
|
|
|
tokens.add( token );
|
|
|
|
token = "";
|
2014-02-11 06:51:25 +01:00
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
token = token + c;
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|
|
|
|
}
|
2014-02-11 06:51:25 +01:00
|
|
|
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|
2014-02-11 06:51:25 +01:00
|
|
|
reader.close();
|
|
|
|
processTokens( loader, path, line );
|
2014-02-11 00:15:43 +01:00
|
|
|
}
|
2014-02-11 06:51:25 +01:00
|
|
|
catch (Throwable e)
|
2014-02-11 00:15:43 +01:00
|
|
|
{
|
|
|
|
AELog.error( e );
|
2014-02-11 06:51:25 +01:00
|
|
|
if ( data.crash )
|
|
|
|
throw new RuntimeException( e );
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-11 06:51:25 +01:00
|
|
|
private void processTokens(IRecipeLoader loader, String file, int line) throws RecipeError
|
2014-02-10 04:16:36 +01:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if ( tokens.isEmpty() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
int split = tokens.indexOf( "->" );
|
|
|
|
if ( split != -1 )
|
|
|
|
{
|
2014-02-11 00:15:43 +01:00
|
|
|
String operation = tokens.remove( 0 ).toLowerCase();
|
2014-02-10 04:16:36 +01:00
|
|
|
|
|
|
|
if ( operation.equals( "alias" ) )
|
|
|
|
{
|
|
|
|
if ( tokens.size() == 3 && tokens.indexOf( "->" ) == 1 )
|
2014-02-11 06:51:25 +01:00
|
|
|
data.aliases.put( tokens.get( 0 ), tokens.get( 2 ) );
|
2014-02-10 04:16:36 +01:00
|
|
|
else
|
|
|
|
throw new RecipeError( "Alias must have exactly 1 input and 1 output." );
|
|
|
|
}
|
2014-02-11 06:51:25 +01:00
|
|
|
else if ( operation.equals( "group" ) )
|
2014-02-11 00:15:43 +01:00
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
List<String> pre = tokens.subList( 0, split - 1 );
|
|
|
|
List<String> post = tokens.subList( split, tokens.size() );
|
|
|
|
|
|
|
|
List<List<Ingredient>> inputs = parseLines( pre );
|
|
|
|
|
|
|
|
if ( inputs.size() >= 1 && post.size() == 1 )
|
2014-02-11 00:15:43 +01:00
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
|
2014-02-11 00:15:43 +01:00
|
|
|
}
|
|
|
|
else
|
2014-02-11 06:51:25 +01:00
|
|
|
throw new RecipeError( "Group must have exactly 1 output, and 1 or more inputs." );
|
2014-02-11 00:15:43 +01:00
|
|
|
}
|
2014-02-11 06:51:25 +01:00
|
|
|
else if ( operation.equals( "ore" ) )
|
2014-02-11 00:15:43 +01:00
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
List<String> pre = tokens.subList( 0, split - 1 );
|
|
|
|
List<String> post = tokens.subList( split, tokens.size() );
|
|
|
|
|
|
|
|
List<List<Ingredient>> inputs = parseLines( pre );
|
|
|
|
|
|
|
|
if ( inputs.size() == 1 && inputs.get( 0 ).size() > 0 && post.size() == 1 )
|
2014-02-11 00:15:43 +01:00
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
CraftHandler ch = new OreRegistration( inputs.get( 0 ), post.get( 0 ) );
|
|
|
|
addCrafting( ch );
|
2014-02-11 00:15:43 +01:00
|
|
|
}
|
|
|
|
else
|
2014-02-11 06:51:25 +01:00
|
|
|
throw new RecipeError( "Group must have exactly 1 output, and 1 or more inputs in a single row." );
|
2014-02-11 00:15:43 +01:00
|
|
|
}
|
2014-02-10 04:16:36 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
List<String> pre = tokens.subList( 0, split - 1 );
|
|
|
|
List<String> post = tokens.subList( split, tokens.size() );
|
|
|
|
|
|
|
|
List<List<Ingredient>> inputs = parseLines( pre );
|
|
|
|
List<List<Ingredient>> outputs = parseLines( post );
|
|
|
|
|
|
|
|
CraftHandler ch = null;
|
|
|
|
|
|
|
|
if ( operation.equals( "shaped" ) )
|
|
|
|
ch = new Shaped();
|
2014-02-11 06:51:25 +01:00
|
|
|
else if ( operation.equals( "shapeless" ) )
|
|
|
|
ch = new Shapeless();
|
2014-02-10 04:16:36 +01:00
|
|
|
else if ( operation.equals( "smelt" ) )
|
|
|
|
ch = new Smelt();
|
2014-02-11 06:51:25 +01:00
|
|
|
else if ( operation.equals( "pureify" ) )
|
|
|
|
ch = new Pureify();
|
2014-02-10 04:16:36 +01:00
|
|
|
else if ( operation.equals( "grind" ) )
|
|
|
|
ch = new Grind();
|
|
|
|
|
|
|
|
if ( ch != null )
|
|
|
|
{
|
|
|
|
ch.setup( inputs, outputs );
|
|
|
|
addCrafting( ch );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw new RecipeError( "Invalid crafting type: " + operation );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2014-02-11 06:51:25 +01:00
|
|
|
{
|
|
|
|
String operation = tokens.remove( 0 ).toLowerCase();
|
|
|
|
|
|
|
|
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." );
|
|
|
|
}
|
2014-02-10 04:16:36 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
catch (RecipeError e)
|
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
AELog.warning( "Recipe Error near line:" + line + " in " + file + " with: " + tokens.toString() );
|
2014-02-10 04:16:36 +01:00
|
|
|
AELog.error( e );
|
2014-02-11 06:51:25 +01:00
|
|
|
if ( data.crash )
|
|
|
|
throw e;
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
tokens.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
private List<List<Ingredient>> parseLines(List<String> subList) throws RecipeError
|
|
|
|
{
|
|
|
|
List<List<Ingredient>> out = new LinkedList<List<Ingredient>>();
|
|
|
|
List<Ingredient> cList = new LinkedList<Ingredient>();
|
|
|
|
|
2014-02-11 06:51:25 +01:00
|
|
|
boolean hasQty = false;
|
|
|
|
int qty = 1;
|
|
|
|
|
2014-02-10 04:16:36 +01:00
|
|
|
for (String v : subList)
|
|
|
|
{
|
|
|
|
if ( v.equals( "," ) )
|
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
if ( hasQty )
|
|
|
|
throw new RecipeError( "Qty found with no item." );
|
2014-02-10 04:16:36 +01:00
|
|
|
if ( !cList.isEmpty() )
|
|
|
|
out.add( cList );
|
|
|
|
cList = new LinkedList<Ingredient>();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-02-11 06:51:25 +01:00
|
|
|
if ( isNumber( v ) )
|
|
|
|
{
|
|
|
|
if ( hasQty )
|
|
|
|
throw new RecipeError( "Qty found with no item." );
|
|
|
|
hasQty = true;
|
|
|
|
qty = Integer.parseInt( v );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( hasQty )
|
|
|
|
{
|
|
|
|
cList.add( new Ingredient( this, v, qty ) );
|
|
|
|
hasQty = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cList.add( new Ingredient( this, v, 1 ) );
|
|
|
|
}
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !cList.isEmpty() )
|
|
|
|
out.add( cList );
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
2014-02-11 06:51:25 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2014-02-10 04:16:36 +01:00
|
|
|
}
|