Refactored GrinderRegistry. (#2644)

* Refactored GrinderRegistry.

Changed IGrinderRegistry#getRecipes to return an unmodifiable collection.
Added a way to remove recipes explicitly instead the internal list.
Added a cache to lookup recipes instead of iterating a list.

Renamed IGrinderEntry to IGrinderRecipe
Made IGrindRecipe immutable for easy caching.

Improved GrinderLogging and Exception Handling
JEI Workaround as it expects a List instead Collection.

* Added blacklist of explicit oredict names for the grindstone.

This can be used should the automatic recipe generation create unintended
loopholes.
This commit is contained in:
yueh
2016-12-02 23:47:50 +01:00
committed by GitHub
parent c405e725b2
commit eb1e86cacb
10 changed files with 361 additions and 265 deletions
@@ -24,13 +24,17 @@
package appeng.api.features; package appeng.api.features;
import java.util.Optional;
import javax.annotation.Nonnull;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
/** /**
* Registration Records for {@link IGrinderRegistry} * Registration Records for {@link IGrinderRegistry}
*/ */
public interface IGrinderEntry public interface IGrinderRecipe
{ {
/** /**
@@ -38,66 +42,40 @@ public interface IGrinderEntry
* *
* @return input that the grinder will accept. * @return input that the grinder will accept.
*/ */
@Nonnull
ItemStack getInput(); ItemStack getInput();
/**
* lets you change the grinder recipe by changing its input.
*
* @param input input item
*/
void setInput( ItemStack input );
/** /**
* gets the current output * gets the current output
* *
* @return output that the grinder will produce * @return output that the grinder will produce
*/ */
@Nonnull
ItemStack getOutput(); ItemStack getOutput();
/** /**
* allows you to change the output. * gets the current output
* *
* @param output output item * @return output that the grinder will produce
*/ */
void setOutput( ItemStack output ); @Nonnull
Optional<ItemStack> getOptionalOutput();
/** /**
* gets the current output * gets the current output
* *
* @return output that the grinder will produce * @return output that the grinder will produce
*/ */
ItemStack getOptionalOutput(); Optional<ItemStack> getSecondOptionalOutput();
/**
* gets the current output
*
* @return output that the grinder will produce
*/
ItemStack getSecondOptionalOutput();
/**
* stack, and 0.0-1.0 chance that it will be generated.
*
* @param output output item
* @param chance generation chance
*/
void setOptionalOutput( ItemStack output, float chance );
/** /**
* 0.0 - 1.0 the chance that the optional output will be generated. * 0.0 - 1.0 the chance that the optional output will be generated.
* *
* @return chance of optional output * @return chance of optional output
*/ */
@Nonnull
float getOptionalChance(); float getOptionalChance();
/**
* stack, and 0.0-1.0 chance that it will be generated.
*
* @param output second optional output item
* @param chance second optional output chance
*/
void setSecondOptionalOutput( ItemStack output, float chance );
/** /**
* 0.0 - 1.0 the chance that the optional output will be generated. * 0.0 - 1.0 the chance that the optional output will be generated.
* *
@@ -106,16 +84,10 @@ public interface IGrinderEntry
float getSecondOptionalChance(); float getSecondOptionalChance();
/** /**
* Energy cost, in turns. * Amount of turns required to process the item.
* *
* @return number of turns it takes to produce the output from the input. * @return number of turns it takes to produce the output from the input.
*/ */
int getEnergyCost(); int getRequiredTurns();
/**
* Allows you to adjust the number of turns
*
* @param c number of turns to produce output.
*/
void setEnergyCost( int c );
} }
@@ -24,7 +24,10 @@
package appeng.api.features; package appeng.api.features;
import java.util.List; import java.util.Collection;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@@ -36,51 +39,90 @@ public interface IGrinderRegistry
{ {
/** /**
* Current list of registered recipes, you can modify this if you want too. * An immutable list of the currently registered recipes.
* *
* @return currentlyRegisteredRecipes * @return currentlyRegisteredRecipes
*/ */
List<IGrinderEntry> getRecipes(); @Nonnull
Collection<IGrinderRecipe> getRecipes();
/** /**
* add a new recipe the easy way, in &#8594; out, how many turns., duplicates will not be added. * Add a new recipe with a single input and output and how many turns it requires.
*
* Will ignore duplicate recipes with the same input item.
* *
* @param in input * @param in The {@link ItemStack} to grind.
* @param out output * @param out The {@link ItemStack} to output.
* @param turns amount of turns to turn the input into the output * @param turns Amount of turns to turn the input into the output, with turns > 0.
*/ */
void addRecipe( ItemStack in, ItemStack out, int turns ); void addRecipe( @Nonnull ItemStack in, @Nonnull ItemStack out, int turns );
/**
* Add a new recipe with an input, output and a single optional output.
*
* Will ignore duplicate recipes with the same input item.
*
* @param in The {@link ItemStack} to grind.
* @param out The {@link ItemStack} to output.
* @param optional The optional {@link ItemStack} to output of a certain chance.
* @param chance Chance to get the optional output within 0.0 - 1.0
* @param turns Amount of turns to turn the input into the output, with turns > 0.
*/
void addRecipe( @Nonnull ItemStack in, @Nonnull ItemStack out, @Nonnull ItemStack optional, float chance, int turns );
/** /**
* add a new recipe with optional outputs, duplicates will not be added. * add a new recipe with optional outputs, duplicates will not be added.
*
* Will ignore duplicate recipes with the same input item.
* *
* @param in input * @param in The {@link ItemStack} to grind.
* @param out output * @param out The {@link ItemStack} to output.
* @param optional optional output * @param optional The first optional {@link ItemStack} to output of a certain chance.
* @param chance chance to get the optional output within 0.0 - 1.0 * @param chance Chance to get the first optional output within 0.0 - 1.0
* @param turns amount of turns to turn the input into the outputs * @param optional2 The second optional {@link ItemStack} to output of a certain chance.
*/
void addRecipe( ItemStack in, ItemStack out, ItemStack optional, float chance, int turns );
/**
* add a new recipe with optional outputs, duplicates will not be added.
*
* @param in input
* @param out output
* @param optional optional output
* @param chance chance to get the optional output within 0.0 - 1.0
* @param optional2 second optional output
* @param chance2 chance to get the second optional output within 0.0 - 1.0 * @param chance2 chance to get the second optional output within 0.0 - 1.0
* @param turns amount of turns to turn the input into the outputs * @param turns Amount of turns to turn the input into the output, with turns > 0.
*
*/ */
void addRecipe( ItemStack in, ItemStack out, ItemStack optional, float chance, ItemStack optional2, float chance2, int turns ); void addRecipe( @Nonnull ItemStack in, @Nonnull ItemStack out, @Nonnull ItemStack optional, float chance, @Nonnull ItemStack optional2, float chance2, int turns );
/**
* Remove the specific from the recipe list.
*
* @param recipe The recipe to be removed.
* @return true, if it was removed
*/
boolean removeRecipe( @Nonnull IGrinderRecipe recipe );
/** /**
* Searches for a recipe for a given input, and returns it. * Searches for a recipe for a given input, and returns it.
* *
* @param input input * @param input The {@link ItemStack} to be grinded.
* *
* @return identified recipe or null * @return identified recipe or null
*/ */
IGrinderEntry getRecipeForInput( ItemStack input ); @Nullable
IGrinderRecipe getRecipeForInput( @Nonnull ItemStack input );
/**
* Allows do add a custom ratio from an ore to dust when being grinded.
*
* The default ratio is 1 ore to 2 dusts.
*
* These have to be added before any recipe is registered. Otherwise it will use the default value.
*
* @param oredictName The name of the ore;
* @param ratio The amount, must be > 0;
*/
void addDustRatio( @Nonnull String oredictName, int ratio );
/**
* Remove a custom ratio for a specific ore name.
*
* Will use the default of 2 value afterwards.
*
* @param oredictName The name of the ore;
*/
boolean removeDustRatio( @Nonnull String oredictName );
} }
+19 -2
View File
@@ -24,8 +24,11 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.common.collect.Sets;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property; import net.minecraftforge.common.config.Property;
@@ -99,6 +102,7 @@ public final class AEConfig extends Configuration implements IConfigurableObject
// Grindstone // Grindstone
private String[] grinderOres = Stream.of( ORES_VANILLA, ORES_AE, ORES_COMMON, ORES_MISC ).flatMap( Stream::of ).toArray( String[]::new ); private String[] grinderOres = Stream.of( ORES_VANILLA, ORES_AE, ORES_COMMON, ORES_MISC ).flatMap( Stream::of ).toArray( String[]::new );
private Set<String> grinderBlackList;
private double oreDoublePercentage = 90.0; private double oreDoublePercentage = 90.0;
// Batteries // Batteries
@@ -153,8 +157,16 @@ public final class AEConfig extends Configuration implements IConfigurableObject
this.removeCrashingItemsOnLoad = this.get( "general", "removeCrashingItemsOnLoad", false, this.removeCrashingItemsOnLoad = this.get( "general", "removeCrashingItemsOnLoad", false,
"Will auto-remove items that crash when being loaded from storage. This will destroy those items instead of crashing the game!" ).getBoolean(); "Will auto-remove items that crash when being loaded from storage. This will destroy those items instead of crashing the game!" ).getBoolean();
this.grinderOres = this.get( "GrindStone", "grinderOres", this.grinderOres ).getStringList(); this.setCategoryComment( "GrindStone",
this.oreDoublePercentage = this.get( "GrindStone", "oreDoublePercentage", this.oreDoublePercentage ).getDouble( this.oreDoublePercentage ); "Creates recipe of the following pattern automatically: '1 oreTYPE => 2 dustTYPE' and '(1 ingotTYPE or 1 crystalTYPE or 1 gemTYPE) => 1 dustTYPE'" );
this.grinderOres = this.get( "GrindStone", "grinderOres", this.grinderOres, "The list of types to handle. Specify without a prefix like ore or dust." )
.getStringList();
this.grinderBlackList = Sets.newHashSet(
this.get( "GrindStone", "blacklist", new String[] {}, "Blacklists the exact oredict name from being handled by any recipe." )
.getStringList() );
this.oreDoublePercentage = this
.get( "GrindStone", "oreDoublePercentage", this.oreDoublePercentage, "Chance to actually get an output with stacksize > 1." )
.getDouble( this.oreDoublePercentage );
this.settings.registerSetting( Settings.SEARCH_TOOLTIPS, YesNo.YES ); this.settings.registerSetting( Settings.SEARCH_TOOLTIPS, YesNo.YES );
this.settings.registerSetting( Settings.TERMINAL_STYLE, TerminalStyle.TALL ); this.settings.registerSetting( Settings.TERMINAL_STYLE, TerminalStyle.TALL );
@@ -632,6 +644,11 @@ public final class AEConfig extends Configuration implements IConfigurableObject
return grinderOres; return grinderOres;
} }
public Set<String> getGrinderBlackList()
{
return this.grinderBlackList;
}
public double getOreDoublePercentage() public double getOreDoublePercentage()
{ {
return oreDoublePercentage; return oreDoublePercentage;
+2 -2
View File
@@ -290,11 +290,11 @@ public final class AELog
* *
* @param message String to be logged * @param message String to be logged
*/ */
public static void grinder( @Nonnull final String message ) public static void grinder( @Nonnull final String message, final Object... params )
{ {
if( AEConfig.instance().isFeatureEnabled( AEFeature.GRINDER_LOGGING ) ) if( AEConfig.instance().isFeatureEnabled( AEFeature.GRINDER_LOGGING ) )
{ {
log( Level.DEBUG, "grinder: " + message ); log( Level.DEBUG, "grinder: " + message, params );
} }
} }
@@ -19,17 +19,20 @@
package appeng.core.features.registries; package appeng.core.features.registries;
import java.util.ArrayList; import java.util.Collection;
import java.util.HashMap; import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.init.Items; import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import appeng.api.features.IGrinderEntry; import appeng.api.features.IGrinderRecipe;
import appeng.api.features.IGrinderRegistry; import appeng.api.features.IGrinderRegistry;
import appeng.core.AEConfig; import appeng.core.AEConfig;
import appeng.core.AELog; import appeng.core.AELog;
@@ -41,17 +44,23 @@ import appeng.util.Platform;
public final class GrinderRecipeManager implements IGrinderRegistry, IOreListener public final class GrinderRecipeManager implements IGrinderRegistry, IOreListener
{ {
private final List<IGrinderEntry> recipes; private final Map<CacheKey, IGrinderRecipe> recipes;
private final Map<ItemStack, String> ores; private final Map<ItemStack, String> ores;
private final Map<ItemStack, String> ingots; private final Map<ItemStack, String> ingots;
private final Map<String, ItemStack> dusts; private final Map<String, ItemStack> dusts;
private final Map<String, Integer> dustToOreRatio;
public GrinderRecipeManager() public GrinderRecipeManager()
{ {
this.recipes = new ArrayList<IGrinderEntry>(); this.recipes = Maps.newHashMap();
this.ores = new HashMap<ItemStack, String>(); this.ores = Maps.newHashMap();
this.ingots = new HashMap<ItemStack, String>(); this.ingots = Maps.newHashMap();
this.dusts = new HashMap<String, ItemStack>(); this.dusts = Maps.newHashMap();
this.dustToOreRatio = Maps.newHashMap();
this.addDustRatio( "Obsidian", 1 );
this.addDustRatio( "Charcoal", 1 );
this.addDustRatio( "Coal", 1 );
this.addOre( "Coal", new ItemStack( Items.COAL ) ); this.addOre( "Coal", new ItemStack( Items.COAL ) );
this.addOre( "Charcoal", new ItemStack( Items.COAL, 1, 1 ) ); this.addOre( "Charcoal", new ItemStack( Items.COAL, 1, 1 ) );
@@ -76,62 +85,147 @@ public final class GrinderRecipeManager implements IGrinderRegistry, IOreListene
} }
@Override @Override
public List<IGrinderEntry> getRecipes() public Collection<IGrinderRecipe> getRecipes()
{ {
this.log( "API - getRecipes" ); return Collections.unmodifiableCollection( this.recipes.values() );
return this.recipes;
} }
@Override @Override
public void addRecipe( final ItemStack in, final ItemStack out, final int cost ) public void addRecipe( final ItemStack in, final ItemStack out, final int cost )
{ {
if( in == null || out == null ) Preconditions.checkNotNull( in, "Null is not accepted as input itemstack." );
{ Preconditions.checkNotNull( out, "Null is not accepted as output itemstack." );
this.log( "Invalid Grinder Recipe Specified." ); Preconditions.checkArgument( cost > 0, "Turns must be > 0" );
return;
} this.log( "Allow Grinding of '%1$s' to '%2$s' for %3$d turn", Platform.getItemDisplayName( in ), Platform.getItemDisplayName( out ), cost );
this.log( "Allow Grinding of " + Platform.getItemDisplayName( in ) + " to " + Platform.getItemDisplayName( out ) + " for " + cost );
this.injectRecipe( new AppEngGrinderRecipe( this.copy( in ), this.copy( out ), cost ) ); this.injectRecipe( new AppEngGrinderRecipe( this.copy( in ), this.copy( out ), cost ) );
} }
@Override @Override
public void addRecipe( final ItemStack in, final ItemStack out, final ItemStack optional, final float chance, final int cost ) public void addRecipe( final ItemStack in, final ItemStack out, final ItemStack optional, final float chance, final int cost )
{ {
if( in == null || ( optional == null && out == null ) ) Preconditions.checkNotNull( in, "Null is not accepted as input itemstack." );
{ Preconditions.checkNotNull( out, "Null is not accepted as output itemstack." );
this.log( "Invalid Grinder Recipe Specified." ); Preconditions.checkNotNull( optional, "Null is not accepted as optional itemstack." );
return; Preconditions.checkArgument( chance >= 0.0 && chance <= 1.0, "chance must be within 0.0 - 1.0." );
} Preconditions.checkArgument( cost > 0, "Turns must be > 0" );
this.log( "Allow Grinding of '%1$s' to '%2$s' with optional '%3$s' @ %4$.2f for %5$d", Platform.getItemDisplayName( in ),
Platform.getItemDisplayName( out ), Platform.getItemDisplayName( optional ), chance, cost );
this.log( "Allow Grinding of " + Platform.getItemDisplayName( in ) + " to " + Platform.getItemDisplayName( out ) + " with optional " + Platform.getItemDisplayName( optional ) + " for " + cost );
this.injectRecipe( new AppEngGrinderRecipe( this.copy( in ), this.copy( out ), this.copy( optional ), chance, cost ) ); this.injectRecipe( new AppEngGrinderRecipe( this.copy( in ), this.copy( out ), this.copy( optional ), chance, cost ) );
} }
@Override @Override
public void addRecipe( final ItemStack in, final ItemStack out, final ItemStack optional, final float chance, final ItemStack optional2, final float chance2, final int cost ) public void addRecipe( final ItemStack in, final ItemStack out, final ItemStack optional1, final float chance1, final ItemStack optional2, final float chance2, final int cost )
{ {
if( in == null || ( optional == null && out == null && optional2 == null ) ) Preconditions.checkNotNull( in, "Null is not accepted as input itemstack." );
Preconditions.checkNotNull( out, "Null is not accepted as output itemstack." );
Preconditions.checkNotNull( optional1, "Null is not accepted as optional itemstack." );
Preconditions.checkArgument( chance1 >= 0.0 && chance1 <= 1.0, "chance must be within 0.0 - 1.0." );
Preconditions.checkNotNull( optional2, "Null is not accepted as optional2 itemstack." );
Preconditions.checkArgument( chance2 >= 0.0 && chance2 <= 1.0, "chance2 must be within 0.0 - 1.0." );
Preconditions.checkArgument( cost > 0, "Turns must be > 0" );
this.log( "Allow Grinding of '%1$s' to '%2$s' with optional '%3$s' @ %4$.2f and optional2 '%5$s' @ %6$.2f for %7$d", Platform.getItemDisplayName( in ),
Platform.getItemDisplayName( out ), Platform.getItemDisplayName( optional1 ), chance1, Platform.getItemDisplayName( optional2 ), chance2,
cost );
this.injectRecipe(
new AppEngGrinderRecipe( this.copy( in ), this.copy( out ), this.copy( optional1 ), this.copy( optional2 ), chance1, chance2, cost ) );
}
@Override
public boolean removeRecipe( IGrinderRecipe recipe )
{
Preconditions.checkNotNull( recipe, "Cannot remove null as recipe." );
final CacheKey key = new CacheKey( recipe.getInput() );
final IGrinderRecipe removedRecipe = this.recipes.remove( key );
this.log( "Removed Grinding of '%1%s'", Platform.getItemDisplayName( recipe.getInput() ) );
return removedRecipe != null;
}
@Override
public IGrinderRecipe getRecipeForInput( final ItemStack input )
{
this.log( "Looking up recipe for '%1$s'", Platform.getItemDisplayName( input ) );
if( input == null )
{ {
this.log( "Invalid Grinder Recipe Specified." ); return null;
return;
} }
this.log( "Allow Grinding of " + Platform.getItemDisplayName( in ) + " to " + Platform.getItemDisplayName( out ) + " with optional " + Platform.getItemDisplayName( optional ) + " for " + cost ); final IGrinderRecipe recipe = this.recipes.get( new CacheKey( input ) );
this.injectRecipe( new AppEngGrinderRecipe( this.copy( in ), this.copy( out ), this.copy( optional ), this.copy( optional2 ), chance, chance2, cost ) );
if( recipe == null )
{
return null;
}
this.log( "Recipe for '%1$s' found '%2$s'", input.getUnlocalizedName(), Platform.getItemDisplayName( recipe.getOutput() ) );
return recipe;
}
@Override
public void addDustRatio( String oredictName, int ratio )
{
Preconditions.checkNotNull( oredictName );
Preconditions.checkArgument( ratio > 0 );
this.log( "Added ratio for '%1$s' of %2$d", oredictName, ratio );
this.dustToOreRatio.put( oredictName, ratio );
}
@Override
public boolean removeDustRatio( String oredictName )
{
Preconditions.checkNotNull( oredictName );
this.log( "Removed ratio for '%1$s'", oredictName );
return this.dustToOreRatio.remove( oredictName ) != null;
}
@Override
public void oreRegistered( final String name, final ItemStack item )
{
if( !AEConfig.instance().getGrinderBlackList().contains( name ) && ( name.startsWith( "ore" ) || name.startsWith( "crystal" ) || name
.startsWith( "gem" ) || name.startsWith( "ingot" ) || name.startsWith( "dust" ) ) )
{
for( final String ore : AEConfig.instance().getGrinderOres() )
{
if( name.equals( "ore" + ore ) )
{
this.addOre( ore, item );
}
else if( name.equals( "crystal" + ore ) || name.equals( "ingot" + ore ) || name.equals( "gem" + ore ) )
{
this.addIngot( ore, item );
}
else if( name.equals( "dust" + ore ) )
{
this.addDust( ore, item );
}
}
}
} }
private void injectRecipe( final AppEngGrinderRecipe appEngGrinderRecipe ) private void injectRecipe( final AppEngGrinderRecipe appEngGrinderRecipe )
{ {
for( final IGrinderEntry gr : this.recipes ) final CacheKey cacheKey = new CacheKey( appEngGrinderRecipe.getInput() );
if( this.recipes.containsKey( cacheKey ) )
{ {
if( Platform.itemComparisons().isSameItem( gr.getInput(), appEngGrinderRecipe.getInput() ) ) this.log( "Tried to add duplicate recipe for '%1$s'", Platform.getItemDisplayName( appEngGrinderRecipe.getInput() ) );
{ return;
return;
}
} }
this.recipes.add( appEngGrinderRecipe ); this.recipes.put( cacheKey, appEngGrinderRecipe );
} }
private ItemStack copy( final ItemStack is ) private ItemStack copy( final ItemStack is )
@@ -143,47 +237,9 @@ public final class GrinderRecipeManager implements IGrinderRegistry, IOreListene
return null; return null;
} }
@Override
public IGrinderEntry getRecipeForInput( final ItemStack input )
{
this.log( "Looking up recipe for " + Platform.getItemDisplayName( input ) );
if( input != null )
{
for( final IGrinderEntry r : this.recipes )
{
if( Platform.itemComparisons().isEqualItem( input, r.getInput() ) )
{
this.log( "Recipe for " + input.getUnlocalizedName() + " found " + Platform.getItemDisplayName( r.getOutput() ) );
return r;
}
}
this.log( "Could not find recipe for " + Platform.getItemDisplayName( input ) );
}
return null;
}
private void log( final String o )
{
AELog.grinder( o );
}
private int getDustToOreRatio( final String name ) private int getDustToOreRatio( final String name )
{ {
if( name.equals( "Obsidian" ) ) return this.dustToOreRatio.getOrDefault( name, 2 );
{
return 1;
}
if( name.equals( "Charcoal" ) )
{
return 1;
}
if( name.equals( "Coal" ) )
{
return 1;
}
return 2;
} }
private void addOre( final String name, final ItemStack item ) private void addOre( final String name, final ItemStack item )
@@ -192,7 +248,7 @@ public final class GrinderRecipeManager implements IGrinderRegistry, IOreListene
{ {
return; return;
} }
this.log( "Adding Ore - " + name + " : " + Platform.getItemDisplayName( item ) ); this.log( "Adding Ore: '%1$s'", Platform.getItemDisplayName( item ) );
this.ores.put( item, name ); this.ores.put( item, name );
@@ -219,7 +275,7 @@ public final class GrinderRecipeManager implements IGrinderRegistry, IOreListene
{ {
return; return;
} }
this.log( "Adding Ingot - " + name + " : " + Platform.getItemDisplayName( item ) ); this.log( "Adding Ingot: '%1$s'", Platform.getItemDisplayName( item ) );
this.ingots.put( item, name ); this.ingots.put( item, name );
@@ -237,11 +293,11 @@ public final class GrinderRecipeManager implements IGrinderRegistry, IOreListene
} }
if( this.dusts.containsKey( name ) ) if( this.dusts.containsKey( name ) )
{ {
this.log( "Rejecting Dust - " + name + " : " + Platform.getItemDisplayName( item ) ); this.log( "Rejecting Dust: '%1$s'", Platform.getItemDisplayName( item ) );
return; return;
} }
this.log( "Adding Dust - " + name + " : " + Platform.getItemDisplayName( item ) ); this.log( "Adding Dust: '%1$s'", Platform.getItemDisplayName( item ) );
this.dusts.put( name, item ); this.dusts.put( name, item );
@@ -274,26 +330,68 @@ public final class GrinderRecipeManager implements IGrinderRegistry, IOreListene
} }
} }
@Override private void log( final String o, Object... params )
public void oreRegistered( final String name, final ItemStack item )
{ {
if( name.startsWith( "ore" ) || name.startsWith( "crystal" ) || name.startsWith( "gem" ) || name.startsWith( "ingot" ) || name.startsWith( "dust" ) ) AELog.grinder( o, params );
}
private static class CacheKey
{
private final Item item;
private final int damage;
CacheKey( ItemStack input )
{ {
for( final String ore : AEConfig.instance().getGrinderOres() ) Preconditions.checkNotNull( input );
Preconditions.checkNotNull( input.getItem() );
this.item = input.getItem();
this.damage = input.getItemDamage();
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + damage;
result = prime * result + ( ( item == null ) ? 0 : item.hashCode() );
return result;
}
@Override
public boolean equals( Object obj )
{
if( this == obj )
{ {
if( name.equals( "ore" + ore ) ) return true;
}
if( obj == null || getClass() != obj.getClass() )
{
return false;
}
CacheKey other = (CacheKey) obj;
if( damage != other.damage )
{
return false;
}
if( item == null )
{
if( other.item != null )
{ {
this.addOre( ore, item ); return false;
}
else if( name.equals( "crystal" + ore ) || name.equals( "ingot" + ore ) || name.equals( "gem" + ore ) )
{
this.addIngot( ore, item );
}
else if( name.equals( "dust" + ore ) )
{
this.addDust( ore, item );
} }
} }
else if( item != other.item )
{
return false;
}
return true;
} }
} }
} }
@@ -19,55 +19,49 @@
package appeng.core.features.registries.entries; package appeng.core.features.registries.entries;
import java.util.Optional;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import appeng.api.features.IGrinderEntry; import appeng.api.features.IGrinderRecipe;
public class AppEngGrinderRecipe implements IGrinderEntry public class AppEngGrinderRecipe implements IGrinderRecipe
{ {
private ItemStack in; private final ItemStack in;
private ItemStack out; private final ItemStack out;
private float optionalChance; private final float optionalChance;
private ItemStack optionalOutput; private final Optional<ItemStack> optionalOutput;
private float optionalChance2; private final float optionalChance2;
private ItemStack optionalOutput2; private final Optional<ItemStack> optionalOutput2;
private int energy; private final int turns;
public AppEngGrinderRecipe( final ItemStack a, final ItemStack b, final int cost ) public AppEngGrinderRecipe( final ItemStack input, final ItemStack output, final int cost )
{ {
this.in = a; this( input, output, null, null, 0, 0, cost );
this.out = b;
this.energy = cost;
} }
public AppEngGrinderRecipe( final ItemStack a, final ItemStack b, final ItemStack c, final float chance, final int cost ) public AppEngGrinderRecipe( final ItemStack input, final ItemStack output, final ItemStack optional, final float chance, final int cost )
{ {
this.in = a; this( input, output, optional, null, chance, 0, cost );
this.out = b;
this.optionalOutput = c;
this.optionalChance = chance;
this.energy = cost;
} }
public AppEngGrinderRecipe( final ItemStack a, final ItemStack b, final ItemStack c, final ItemStack d, final float chance, final float chance2, final int cost ) public AppEngGrinderRecipe( final ItemStack input, final ItemStack output, final ItemStack optional1, final ItemStack optional2, final float chance1, final float chance2, final int cost )
{ {
this.in = a; this.in = input;
this.out = b; this.out = output;
this.optionalOutput = c; this.optionalOutput = Optional.ofNullable( optional1 );
this.optionalChance = chance; this.optionalChance = chance1;
this.optionalOutput2 = d; this.optionalOutput2 = Optional.ofNullable( optional2 );
this.optionalChance2 = chance2; this.optionalChance2 = chance2;
this.energy = cost; this.turns = cost;
} }
@Override @Override
@@ -76,12 +70,6 @@ public class AppEngGrinderRecipe implements IGrinderEntry
return this.in; return this.in;
} }
@Override
public void setInput( final ItemStack i )
{
this.in = i.copy();
}
@Override @Override
public ItemStack getOutput() public ItemStack getOutput()
{ {
@@ -89,43 +77,23 @@ public class AppEngGrinderRecipe implements IGrinderEntry
} }
@Override @Override
public void setOutput( final ItemStack o ) public Optional<ItemStack> getOptionalOutput()
{
this.out = o.copy();
}
@Override
public ItemStack getOptionalOutput()
{ {
return this.optionalOutput; return this.optionalOutput;
} }
@Override @Override
public ItemStack getSecondOptionalOutput() public Optional<ItemStack> getSecondOptionalOutput()
{ {
return this.optionalOutput2; return this.optionalOutput2;
} }
@Override
public void setOptionalOutput( final ItemStack output, final float chance )
{
this.optionalOutput = output.copy();
this.optionalChance = chance;
}
@Override @Override
public float getOptionalChance() public float getOptionalChance()
{ {
return this.optionalChance; return this.optionalChance;
} }
@Override
public void setSecondOptionalOutput( final ItemStack output, final float chance )
{
this.optionalChance2 = chance;
this.optionalOutput2 = output.copy();
}
@Override @Override
public float getSecondOptionalChance() public float getSecondOptionalChance()
{ {
@@ -133,14 +101,8 @@ public class AppEngGrinderRecipe implements IGrinderEntry
} }
@Override @Override
public int getEnergyCost() public int getRequiredTurns()
{ {
return this.energy; return this.turns;
}
@Override
public void setEnergyCost( final int c )
{
this.energy = c;
} }
} }
@@ -22,16 +22,16 @@ package appeng.integration.modules.jei;
import mezz.jei.api.recipe.IRecipeHandler; import mezz.jei.api.recipe.IRecipeHandler;
import mezz.jei.api.recipe.IRecipeWrapper; import mezz.jei.api.recipe.IRecipeWrapper;
import appeng.api.features.IGrinderEntry; import appeng.api.features.IGrinderRecipe;
class GrinderRecipeHandler implements IRecipeHandler<IGrinderEntry> class GrinderRecipeHandler implements IRecipeHandler<IGrinderRecipe>
{ {
@Override @Override
public Class<IGrinderEntry> getRecipeClass() public Class<IGrinderRecipe> getRecipeClass()
{ {
return IGrinderEntry.class; return IGrinderRecipe.class;
} }
@Override @Override
@@ -41,19 +41,19 @@ class GrinderRecipeHandler implements IRecipeHandler<IGrinderEntry>
} }
@Override @Override
public String getRecipeCategoryUid( IGrinderEntry recipe ) public String getRecipeCategoryUid( IGrinderRecipe recipe )
{ {
return GrinderRecipeCategory.UID; return GrinderRecipeCategory.UID;
} }
@Override @Override
public IRecipeWrapper getRecipeWrapper( IGrinderEntry recipe ) public IRecipeWrapper getRecipeWrapper( IGrinderRecipe recipe )
{ {
return new GrinderRecipeWrapper( recipe ); return new GrinderRecipeWrapper( recipe );
} }
@Override @Override
public boolean isRecipeValid( IGrinderEntry recipe ) public boolean isRecipeValid( IGrinderRecipe recipe )
{ {
return true; return true;
} }
@@ -31,15 +31,15 @@ import net.minecraft.item.ItemStack;
import mezz.jei.api.ingredients.IIngredients; import mezz.jei.api.ingredients.IIngredients;
import mezz.jei.api.recipe.BlankRecipeWrapper; import mezz.jei.api.recipe.BlankRecipeWrapper;
import appeng.api.features.IGrinderEntry; import appeng.api.features.IGrinderRecipe;
class GrinderRecipeWrapper extends BlankRecipeWrapper class GrinderRecipeWrapper extends BlankRecipeWrapper
{ {
private final IGrinderEntry recipe; private final IGrinderRecipe recipe;
GrinderRecipeWrapper( IGrinderEntry recipe ) GrinderRecipeWrapper( IGrinderRecipe recipe )
{ {
this.recipe = recipe; this.recipe = recipe;
} }
@@ -50,14 +50,8 @@ class GrinderRecipeWrapper extends BlankRecipeWrapper
ingredients.setInput( ItemStack.class, recipe.getInput() ); ingredients.setInput( ItemStack.class, recipe.getInput() );
List<ItemStack> outputs = new ArrayList<>( 3 ); List<ItemStack> outputs = new ArrayList<>( 3 );
outputs.add( recipe.getOutput() ); outputs.add( recipe.getOutput() );
if( recipe.getOptionalOutput() != null ) recipe.getOptionalOutput().ifPresent( outputs::add );
{ recipe.getSecondOptionalOutput().ifPresent( outputs::add );
outputs.add( recipe.getOptionalOutput() );
}
if( recipe.getSecondOptionalOutput() != null )
{
outputs.add( recipe.getSecondOptionalOutput() );
}
ingredients.setOutputs( ItemStack.class, outputs ); ingredients.setOutputs( ItemStack.class, outputs );
} }
@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@@ -70,8 +71,10 @@ public class JEIPlugin extends BlankModPlugin
registerDescriptions( definitions, registry ); registerDescriptions( definitions, registry );
// Allow recipe transfer from JEI to crafting and pattern terminal // Allow recipe transfer from JEI to crafting and pattern terminal
registry.getRecipeTransferRegistry().addRecipeTransferHandler( new RecipeTransferHandler<>( ContainerCraftingTerm.class ), VanillaRecipeCategoryUid.CRAFTING ); registry.getRecipeTransferRegistry().addRecipeTransferHandler( new RecipeTransferHandler<>( ContainerCraftingTerm.class ),
registry.getRecipeTransferRegistry().addRecipeTransferHandler( new RecipeTransferHandler<>( ContainerPatternTerm.class ), VanillaRecipeCategoryUid.CRAFTING ); VanillaRecipeCategoryUid.CRAFTING );
registry.getRecipeTransferRegistry().addRecipeTransferHandler( new RecipeTransferHandler<>( ContainerPatternTerm.class ),
VanillaRecipeCategoryUid.CRAFTING );
} }
private void registerDescriptions( IDefinitions definitions, IModRegistry registry ) private void registerDescriptions( IDefinitions definitions, IModRegistry registry )
@@ -130,7 +133,7 @@ public class JEIPlugin extends BlankModPlugin
return; return;
} }
registry.addRecipes( AEApi.instance().registries().grinder().getRecipes() ); registry.addRecipes( Lists.newArrayList( AEApi.instance().registries().grinder().getRecipes() ) );
registry.addRecipeHandlers( new GrinderRecipeHandler() ); registry.addRecipeHandlers( new GrinderRecipeHandler() );
registry.addRecipeCategories( new GrinderRecipeCategory( registry.getJeiHelpers().getGuiHelper() ) ); registry.addRecipeCategories( new GrinderRecipeCategory( registry.getJeiHelpers().getGuiHelper() ) );
registry.addRecipeCategoryCraftingItem( grindstone, GrinderRecipeCategory.UID ); registry.addRecipeCategoryCraftingItem( grindstone, GrinderRecipeCategory.UID );
@@ -28,7 +28,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import appeng.api.AEApi; import appeng.api.AEApi;
import appeng.api.features.IGrinderEntry; import appeng.api.features.IGrinderRecipe;
import appeng.api.implementations.tiles.ICrankable; import appeng.api.implementations.tiles.ICrankable;
import appeng.tile.AEBaseInvTile; import appeng.tile.AEBaseInvTile;
import appeng.tile.inventory.AppEngInternalInventory; import appeng.tile.inventory.AppEngInternalInventory;
@@ -108,7 +108,7 @@ public class TileGrinder extends AEBaseInvTile implements ICrankable
continue; continue;
} }
final IGrinderEntry r = AEApi.instance().registries().grinder().getRecipeForInput( item ); final IGrinderRecipe r = AEApi.instance().registries().grinder().getRecipeForInput( item );
if( r != null ) if( r != null )
{ {
if( item.stackSize >= r.getInput().stackSize ) if( item.stackSize >= r.getInput().stackSize )
@@ -144,10 +144,10 @@ public class TileGrinder extends AEBaseInvTile implements ICrankable
this.points++; this.points++;
final ItemStack processing = this.getStackInSlot( 6 ); final ItemStack processing = this.getStackInSlot( 6 );
final IGrinderEntry r = AEApi.instance().registries().grinder().getRecipeForInput( processing ); final IGrinderRecipe r = AEApi.instance().registries().grinder().getRecipeForInput( processing );
if( r != null ) if( r != null )
{ {
if( r.getEnergyCost() > this.points ) if( r.getRequiredTurns() > this.points )
{ {
return; return;
} }
@@ -157,17 +157,25 @@ public class TileGrinder extends AEBaseInvTile implements ICrankable
this.addItem( sia, r.getOutput() ); this.addItem( sia, r.getOutput() );
float chance = ( Platform.getRandomInt() % 2000 ) / 2000.0f; r.getOptionalOutput().ifPresent( itemStack ->
if( chance <= r.getOptionalChance() )
{ {
this.addItem( sia, r.getOptionalOutput() ); final float chance = ( Platform.getRandomInt() % 2000 ) / 2000.0f;
}
chance = ( Platform.getRandomInt() % 2000 ) / 2000.0f; if( chance <= r.getOptionalChance() )
if( chance <= r.getSecondOptionalChance() ) {
this.addItem( sia, itemStack );
}
} );
r.getSecondOptionalOutput().ifPresent( itemStack ->
{ {
this.addItem( sia, r.getSecondOptionalOutput() ); final float chance = ( Platform.getRandomInt() % 2000 ) / 2000.0f;
}
if( chance <= r.getSecondOptionalChance() )
{
this.addItem( sia, itemStack );
}
} );
this.setInventorySlotContents( 6, null ); this.setInventorySlotContents( 6, null );
} }