Add data generation. (#4399)

Recipe and other Data Generators
This commit is contained in:
Eutro
2020-06-02 22:13:49 +01:00
committed by GitHub
parent 1056629545
commit 58afa6539f
139 changed files with 2702 additions and 962 deletions
@@ -19,6 +19,7 @@
package appeng.recipes;
import appeng.core.Api;
import net.minecraft.item.ItemStack;
import appeng.api.AEApi;
@@ -1,131 +0,0 @@
package appeng.recipes;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import net.minecraft.util.JSONUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.common.crafting.JsonContext;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import appeng.core.AppEng;
import appeng.recipes.handlers.GrinderHandler;
import appeng.recipes.handlers.InscriberHandler;
import appeng.recipes.handlers.SmeltingHandler;
public class AERecipeLoader
{
private static final String AERECIPE_BASE = "/aerecipes";
private static Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
private final Map<ResourceLocation, IAERecipeFactory> factories = new HashMap<>();
private final ModContainer mod;
private final JsonContext ctx;
public AERecipeLoader()
{
this.mod = Loader.instance().getIndexedModList().get( AppEng.MOD_ID );
this.ctx = new JsonContext( AppEng.MOD_ID );
this.initFactories();
}
public boolean loadProcessingRecipes()
{
return CraftingHelper.findFiles( this.mod, "assets/" + AppEng.MOD_ID + AERECIPE_BASE, this::preprocess, this::process, true, true );
}
private boolean preprocess( final Path root )
{
return true;
}
private boolean process( final Path root, final Path file )
{
String relative = root.relativize( file ).toString();
if( !"json".equals( FilenameUtils.getExtension( file.toString() ) ) || relative.startsWith( "_" ) )
{
return true;
}
String name = FilenameUtils.removeExtension( relative ).replaceAll( "\\\\", "/" );
ResourceLocation key = new ResourceLocation( this.ctx.getModId(), name );
BufferedReader reader = null;
try
{
reader = Files.newBufferedReader( file );
JsonObject json = JSONUtils.fromJson( GSON, reader, JsonObject.class );
if( json.has( "conditions" ) && !CraftingHelper.processConditions( JSONUtils.getJsonArray( json, "conditions" ), this.ctx ) )
{
return true;
}
this.register( json );
}
catch( JsonParseException e )
{
FMLLog.log.error( "Parsing error loading recipe {}", key, e );
return false;
}
catch( IOException e )
{
FMLLog.log.error( "Couldn't read recipe {} from {}", key, file, e );
return false;
}
finally
{
IOUtils.closeQuietly( reader );
}
return true;
}
private void register( JsonObject json )
{
if( json == null || json.isJsonNull() )
{
throw new JsonSyntaxException( "Json cannot be null" );
}
String type = this.ctx.appendModId( JSONUtils.getString( json, "type" ) );
if( type.isEmpty() )
{
throw new JsonSyntaxException( "Recipe type can not be an empty string" );
}
IAERecipeFactory factory = this.factories.get( new ResourceLocation( type ) );
if( factory == null )
{
throw new JsonSyntaxException( "Unknown recipe type: " + type );
}
factory.register( json, this.ctx );
}
private void initFactories()
{
this.factories.put( new ResourceLocation( AppEng.MOD_ID, "inscriber" ), new InscriberHandler() );
this.factories.put( new ResourceLocation( AppEng.MOD_ID, "smelt" ), new SmeltingHandler() );
this.factories.put( new ResourceLocation( AppEng.MOD_ID, "grinder" ), new GrinderHandler() );
}
}
@@ -1,13 +0,0 @@
package appeng.recipes;
import com.google.gson.JsonObject;
import net.minecraftforge.common.crafting.JsonContext;
public interface IAERecipeFactory
{
void register( JsonObject json, JsonContext ctx );
}
@@ -1,45 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes;
import appeng.api.recipes.IRecipeHandler;
import appeng.api.recipes.IRecipeLoader;
/**
* @author AlgorithmX2
* @author thatsIch
* @version rv3 - 10.08.2015
* @since rv0
*/
public class RecipeHandler implements IRecipeHandler
{
@Override
public void parseRecipes( final IRecipeLoader loader, final String path )
{
// dummy
}
@Override
public void injectRecipes()
{
// dummy
}
}
@@ -0,0 +1,131 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes.conditions;
import appeng.core.AEConfig;
import appeng.core.AppEng;
import appeng.api.features.AEFeature;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.crafting.conditions.ICondition;
import net.minecraftforge.common.crafting.conditions.IConditionSerializer;
import java.util.Arrays;
import java.util.Locale;
import java.util.Set;
import java.util.stream.StreamSupport;
public class FeaturesEnabled implements ICondition
{
private static final ResourceLocation NAME = new ResourceLocation( AppEng.MOD_ID, "feature" );
private final AEFeature[] features;
public FeaturesEnabled( AEFeature... features )
{
this.features = features;
}
public FeaturesEnabled( Set<AEFeature> features ) {
this(features.toArray(new AEFeature[0]));
}
@Override
public ResourceLocation getID()
{
return NAME;
}
@Override
public boolean test()
{
for( AEFeature feature : features )
{
if( !AEConfig.instance().isFeatureEnabled( feature ) )
{
return false;
}
}
return true;
}
public static class Serializer implements IConditionSerializer<FeaturesEnabled>
{
private static final String JSON_FEATURES_KEY = "features";
public static final Serializer INSTANCE = new Serializer();
private Serializer()
{
}
@Override
public void write( JsonObject json, FeaturesEnabled value )
{
json.add( JSON_FEATURES_KEY, Arrays.stream( value.features )
.map( AEFeature::toString )
.reduce( new JsonArray(), ( JsonArray array, String string ) -> {
array.add( string );
return array;
}, ( a, b ) -> b ) );
}
@Override
public FeaturesEnabled read( JsonObject jsonObject )
{
AEFeature[] features;
if( JSONUtils.isJsonArray( jsonObject, JSON_FEATURES_KEY ) )
{
final JsonArray featuresArray = JSONUtils.getJsonArray( jsonObject, JSON_FEATURES_KEY );
features = StreamSupport.stream( featuresArray.spliterator(), false )
.filter( JsonElement::isJsonPrimitive )
.map( JsonElement::getAsString )
.map( s -> s.toUpperCase( Locale.ENGLISH ) )
.map( AEFeature::valueOf )
.toArray( AEFeature[]::new );
}
else if( JSONUtils.isString( jsonObject, JSON_FEATURES_KEY ) )
{
final String featureName = JSONUtils.getString( jsonObject, JSON_FEATURES_KEY ).toUpperCase( Locale.ENGLISH );
features = new AEFeature[] { AEFeature.valueOf( featureName ) };
}
else
{
features = new AEFeature[] {};
}
return new FeaturesEnabled( features );
}
@Override
public ResourceLocation getID()
{
return NAME;
}
}
}
@@ -1,67 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes.factories.conditions;
import java.util.Locale;
import java.util.function.BooleanSupplier;
import java.util.stream.Stream;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.minecraft.util.JSONUtils;
import net.minecraftforge.common.crafting.IConditionFactory;
import net.minecraftforge.common.crafting.JsonContext;
import appeng.core.AEConfig;
import appeng.core.features.AEFeature;
public class Features implements IConditionFactory
{
private static final String JSON_FEATURES_KEY = "features";
@Override
public BooleanSupplier parse( JsonContext jsonContext, JsonObject jsonObject )
{
final boolean result;
if( JSONUtils.isJsonArray( jsonObject, JSON_FEATURES_KEY ) )
{
final JsonArray features = JSONUtils.getJsonArray( jsonObject, JSON_FEATURES_KEY );
result = Stream.of( features )
.allMatch( p -> AEConfig.instance().isFeatureEnabled( AEFeature.valueOf( p.getAsString().toUpperCase( Locale.ENGLISH ) ) ) );
}
else if( JSONUtils.isString( jsonObject, JSON_FEATURES_KEY ) )
{
final String featureName = JSONUtils.getString( jsonObject, JSON_FEATURES_KEY ).toUpperCase( Locale.ENGLISH );
final AEFeature feature = AEFeature.valueOf( featureName );
result = AEConfig.instance().isFeatureEnabled( feature );
}
else
{
result = false;
}
return () -> result;
}
}
@@ -1,58 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes.factories.conditions;
import java.util.function.BooleanSupplier;
import com.google.gson.JsonObject;
import net.minecraft.util.JSONUtils;
import net.minecraftforge.common.crafting.IConditionFactory;
import net.minecraftforge.common.crafting.JsonContext;
import appeng.core.Api;
import appeng.core.AppEng;
public class MaterialExists implements IConditionFactory
{
private static final String JSON_MATERIAL_KEY = "material";
@Override
public BooleanSupplier parse( JsonContext jsonContext, JsonObject jsonObject )
{
final boolean result;
if( JSONUtils.isString( jsonObject, JSON_MATERIAL_KEY ) )
{
final String material = JSONUtils.getString( jsonObject, JSON_MATERIAL_KEY );
final Object item = Api.INSTANCE.registries().recipes().resolveItem( AppEng.MOD_ID, material );
result = item != null;
}
else
{
result = false;
}
return () -> result;
}
}
@@ -1,212 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2017, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes.factories.recipes;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.common.crafting.IRecipeFactory;
import net.minecraftforge.common.crafting.JsonContext;
import net.minecraftforge.oredict.ShapedOreRecipe;
import net.minecraftforge.oredict.ShapelessOreRecipe;
import appeng.api.AEApi;
import appeng.api.recipes.ResolverResult;
import appeng.core.AELog;
import appeng.core.AppEng;
/**
* @author GuntherDW
*/
public class PartRecipeFactory implements IRecipeFactory
{
@Override
public IRecipe parse( JsonContext context, JsonObject json )
{
String type = JSONUtils.getString( json, "type" );
if( type.contains( "shaped" ) )
{
return shapedFactory( context, json );
}
else if( type.contains( "shapeless" ) )
{
return shapelessFactory( context, json );
}
else
{
throw new JsonSyntaxException( "Applied Energistics 2 was given a custom recipe that it does not know how to handle!\n" + "Type should either be '" + AppEng.MOD_ID + ":shapeless' or '" + AppEng.MOD_ID + ":shaped', got '" + type + "'!" );
}
}
public static ItemStack getResult( JsonObject json, JsonContext context )
{
return getResult( json, context, "result" );
}
public static ItemStack getResult( JsonObject json, JsonContext context, String name )
{
JsonObject resultObject = JSONUtils.getJsonObject( json, name );
if( resultObject.has( "part" ) )
{
return getPart( resultObject );
}
else if( resultObject.has( "item" ) )
{
return CraftingHelper.getItemStack( resultObject, context );
}
else
{
throw new JsonSyntaxException( "Result has no 'part' or 'item' property." );
}
}
private static ItemStack getPart( JsonObject resultObject )
{
String ingredient = JSONUtils.getString( resultObject, "part" );
Object result = Api.INSTANCE.registries().recipes().resolveItem( AppEng.MOD_ID, ingredient );
if( result instanceof ResolverResult )
{
ResolverResult resolverResult = (ResolverResult) result;
Item item = Item.getByNameOrId( AppEng.MOD_ID + ":" + resolverResult.itemName );
if( item == null )
{
AELog.warn( "item was null for " + resolverResult.itemName + " ( " + ingredient + " )!" );
throw new JsonSyntaxException( "Got a null item for " + resolverResult.itemName + " ( " + ingredient + " ). This should never happen!" );
}
return new ItemStack( item, JSONUtils.getInt( resultObject, "count", 1 ), resolverResult.damageValue, resolverResult.compound );
}
else
{
throw new JsonSyntaxException( "Couldn't find the resulting item in AE. This means AE was provided a recipe that it shouldn't be handling.\n" + "Was looking for : '" + ingredient + "'." );
}
}
// Copied from ShapedOreRecipe.java, modified a bit.
private static ShapedOreRecipe shapedFactory( JsonContext context, JsonObject json )
{
String group = JSONUtils.getString( json, "group", "" );
Map<Character, Ingredient> ingMap = Maps.newHashMap();
for( Map.Entry<String, JsonElement> entry : JSONUtils.getJsonObject( json, "key" ).entrySet() )
{
if( entry.getKey().length() != 1 )
{
throw new JsonSyntaxException( "Invalid key entry: '" + entry.getKey() + "' is an invalid symbol (must be 1 character only)." );
}
if( " ".equals( entry.getKey() ) )
{
throw new JsonSyntaxException( "Invalid key entry: ' ' is a reserved symbol." );
}
ingMap.put( entry.getKey().toCharArray()[0], CraftingHelper.getIngredient( entry.getValue(), context ) );
}
ingMap.put( ' ', net.minecraft.item.crafting.Ingredient.EMPTY );
JsonArray patternJ = JSONUtils.getJsonArray( json, "pattern" );
if( patternJ.size() == 0 )
{
throw new JsonSyntaxException( "Invalid pattern: empty pattern not allowed" );
}
String[] pattern = new String[patternJ.size()];
for( int x = 0; x < pattern.length; ++x )
{
String line = JSONUtils.getString( patternJ.get( x ), "pattern[" + x + "]" );
if( x > 0 && pattern[0].length() != line.length() )
{
throw new JsonSyntaxException( "Invalid pattern: each row must be the same width" );
}
pattern[x] = line;
}
CraftingHelper.ShapedPrimer primer = new CraftingHelper.ShapedPrimer();
primer.width = pattern[0].length();
primer.height = pattern.length;
primer.mirrored = JSONUtils.getBoolean( json, "mirrored", true );
primer.input = NonNullList.withSize( primer.width * primer.height, net.minecraft.item.crafting.Ingredient.EMPTY );
Set<Character> keys = Sets.newHashSet( ingMap.keySet() );
keys.remove( ' ' );
int x = 0;
for( String line : pattern )
{
for( char chr : line.toCharArray() )
{
net.minecraft.item.crafting.Ingredient ing = ingMap.get( chr );
if( ing == null )
{
throw new JsonSyntaxException( "Pattern references symbol '" + chr + "' but it's not defined in the key" );
}
primer.input.set( x++, ing );
keys.remove( chr );
}
}
if( !keys.isEmpty() )
{
throw new JsonSyntaxException( "Key defines symbols that aren't used in pattern: " + keys );
}
return new ShapedOreRecipe( group.isEmpty() ? null : new ResourceLocation( group ), getResult( json, context ), primer );
}
// Copied from ShapelessOreRecipe.java, modified a bit.
private static ShapelessOreRecipe shapelessFactory( JsonContext context, JsonObject json )
{
String group = JSONUtils.getString( json, "group", "" );
NonNullList<Ingredient> ings = NonNullList.create();
for( JsonElement ele : JSONUtils.getJsonArray( json, "ingredients" ) )
{
ings.add( CraftingHelper.getIngredient( ele, context ) );
}
if( ings.isEmpty() )
{
throw new JsonParseException( "No ingredients for shapeless recipe" );
}
return new ShapelessOreRecipe( group.isEmpty() ? null : new ResourceLocation( group ), ings, getResult( json, context ) );
}
}
@@ -19,42 +19,46 @@
package appeng.recipes.game;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.util.NonNullList;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeHooks;
import appeng.api.AEApi;
import appeng.api.definitions.IBlocks;
import appeng.api.definitions.IDefinitions;
import appeng.api.definitions.IItemDefinition;
import appeng.api.definitions.IItems;
import appeng.api.definitions.IMaterials;
import appeng.api.definitions.*;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList;
import appeng.core.Api;
import appeng.core.AppEng;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.SpecialRecipe;
import net.minecraft.item.crafting.SpecialRecipeSerializer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public final class DisassembleRecipe extends net.minecraftforge.registries.IForgeRegistryEntry.Impl<IRecipe> implements IRecipe
public final class DisassembleRecipe extends SpecialRecipe
{
public static final IRecipeSerializer<DisassembleRecipe> SERIALIZER = new SpecialRecipeSerializer<>( DisassembleRecipe::new );
static
{
SERIALIZER.setRegistryName( new ResourceLocation( AppEng.MOD_ID, "disassemble_recipe" ) );
}
private static final ItemStack MISMATCHED_STACK = ItemStack.EMPTY;
private final Map<IItemDefinition, IItemDefinition> cellMappings;
private final Map<IItemDefinition, IItemDefinition> nonCellMappings;
public DisassembleRecipe()
public DisassembleRecipe( ResourceLocation id )
{
super( id );
final IDefinitions definitions = Api.INSTANCE.definitions();
final IBlocks blocks = definitions.blocks();
final IItems items = definitions.items();
@@ -76,12 +80,12 @@ public final class DisassembleRecipe extends net.minecraftforge.registries.IForg
}
@Override
public boolean matches(final CraftingInventory inv, final World w )
public boolean matches( @Nonnull final CraftingInventory inv, @Nonnull final World w )
{
return !this.getOutput( inv ).isEmpty();
}
@Nullable
@Nonnull
private ItemStack getOutput( final IInventory inventory )
{
int itemCount = 0;
@@ -159,9 +163,9 @@ public final class DisassembleRecipe extends net.minecraftforge.registries.IForg
return Optional.empty();
}
@Nullable
@Nonnull
@Override
public ItemStack getCraftingResult( final CraftingInventory inv )
public ItemStack getCraftingResult( @Nonnull final CraftingInventory inv )
{
return this.getOutput( inv );
}
@@ -172,16 +176,11 @@ public final class DisassembleRecipe extends net.minecraftforge.registries.IForg
return false;
}
@Nullable
@Nonnull
@Override
public ItemStack getRecipeOutput() // no default output..
public IRecipeSerializer<DisassembleRecipe> getSerializer()
{
return ItemStack.EMPTY;
return SERIALIZER;
}
@Override
public NonNullList<ItemStack> getRemainingItems( final CraftingInventory inv )
{
return ForgeHooks.defaultRecipeGetRemainingItems( inv );
}
}
@@ -19,29 +19,33 @@
package appeng.recipes.game;
import javax.annotation.Nullable;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.util.NonNullList;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeHooks;
import appeng.api.AEApi;
import appeng.api.definitions.IComparableDefinition;
import appeng.api.definitions.IDefinitions;
import appeng.core.Api;
import appeng.core.AppEng;
import appeng.items.parts.ItemFacade;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.SpecialRecipe;
import net.minecraft.item.crafting.SpecialRecipeSerializer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
public final class FacadeRecipe extends net.minecraftforge.registries.IForgeRegistryEntry.Impl<IRecipe> implements IRecipe
public final class FacadeRecipe extends SpecialRecipe
{
public static SpecialRecipeSerializer<FacadeRecipe> SERIALIZER = null;
private final IComparableDefinition anchor;
private final ItemFacade facade;
public FacadeRecipe( ItemFacade facade )
public FacadeRecipe( ResourceLocation id, ItemFacade facade )
{
super( id );
this.facade = facade;
final IDefinitions definitions = Api.INSTANCE.definitions();
@@ -49,12 +53,12 @@ public final class FacadeRecipe extends net.minecraftforge.registries.IForgeRegi
}
@Override
public boolean matches(final CraftingInventory inv, final World w )
public boolean matches( @Nonnull final CraftingInventory inv, @Nonnull final World w )
{
return !this.getOutput( inv, false ).isEmpty();
}
@Nullable
@Nonnull
private ItemStack getOutput( final IInventory inv, final boolean createFacade )
{
if( inv.getStackInSlot( 0 ).isEmpty() && inv.getStackInSlot( 2 ).isEmpty() && inv.getStackInSlot( 6 ).isEmpty() && inv.getStackInSlot( 8 ).isEmpty() )
@@ -75,7 +79,7 @@ public final class FacadeRecipe extends net.minecraftforge.registries.IForgeRegi
}
@Override
public ItemStack getCraftingResult( final CraftingInventory inv )
public ItemStack getCraftingResult( @Nonnull final CraftingInventory inv )
{
return this.getOutput( inv, true );
}
@@ -86,15 +90,21 @@ public final class FacadeRecipe extends net.minecraftforge.registries.IForgeRegi
return false;
}
@Nonnull
@Override
public ItemStack getRecipeOutput() // no default output..
public IRecipeSerializer<FacadeRecipe> getSerializer()
{
return ItemStack.EMPTY;
return getSerializer( facade );
}
@Override
public NonNullList<ItemStack> getRemainingItems( final CraftingInventory inv )
public static IRecipeSerializer<FacadeRecipe> getSerializer( ItemFacade facade )
{
return ForgeHooks.defaultRecipeGetRemainingItems( inv );
if( SERIALIZER == null )
{
SERIALIZER = new SpecialRecipeSerializer<>( id -> new FacadeRecipe( id, facade ) );
SERIALIZER.setRegistryName( new ResourceLocation( AppEng.MOD_ID, "facade_recipe" ) );
}
return SERIALIZER;
}
}
@@ -1,36 +0,0 @@
package appeng.recipes.handlers;
import com.google.gson.JsonObject;
import net.minecraft.item.ItemStack;
import net.minecraft.util.JSONUtils;
import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.common.crafting.JsonContext;
import net.minecraftforge.fml.common.registry.GameRegistry;
import appeng.recipes.IAERecipeFactory;
import appeng.recipes.factories.recipes.PartRecipeFactory;
public class SmeltingHandler implements IAERecipeFactory
{
@Override
public void register( JsonObject json, JsonContext ctx )
{
ItemStack result = PartRecipeFactory.getResult( json, ctx );
ItemStack[] input = CraftingHelper.getIngredient( json.get( "input" ), ctx ).getMatchingStacks();
float xp = 0.0f;
if( json.has( "xp" ) )
{
xp = JSONUtils.getFloat( json, "xp" );
}
for( int i = 0; i < input.length; ++i )
{
GameRegistry.addSmelting( input[i], result, xp );
}
}
}
@@ -0,0 +1,35 @@
package appeng.recipes.ingredients;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.Ingredient;
import java.util.Arrays;
import java.util.stream.Stream;
public class PartIngredient extends Ingredient
{
private final String partName;
protected PartIngredient( String partName, Stream<? extends IItemList> itemLists )
{
super( itemLists );
this.partName = partName;
}
public static PartIngredient fromStacks(String partName, ItemStack... stacks) {
return new PartIngredient( partName, Arrays.stream( stacks ).map( SingleItemList::new ) );
}
public static PartIngredient empty( String partName )
{
return new PartIngredient( partName, Stream.empty() );
}
public String getPartName()
{
return partName;
}
}
@@ -16,53 +16,79 @@
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes.factories.ingredients;
package appeng.recipes.ingredients;
import javax.annotation.Nonnull;
import com.google.gson.JsonObject;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.crafting.IIngredientFactory;
import net.minecraftforge.common.crafting.JsonContext;
import appeng.api.recipes.ResolverResult;
import appeng.api.recipes.ResolverResultSet;
import appeng.core.AELog;
import appeng.core.Api;
import appeng.core.AppEng;
import com.google.gson.JsonObject;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.crafting.IIngredientSerializer;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nonnull;
public class PartIngredientFactory implements IIngredientFactory
public class PartIngredientSerializer implements IIngredientSerializer<PartIngredient>
{
public static PartIngredientSerializer INSTANCE = new PartIngredientSerializer();
private PartIngredientSerializer()
{
}
@Nonnull
@Override
public net.minecraft.item.crafting.Ingredient parse( JsonContext context, JsonObject json )
public PartIngredient parse( JsonObject json )
{
final String partName = json.get( "part" ).getAsString();
return getPart( partName );
}
@Nonnull
@Override
public PartIngredient parse( PacketBuffer buffer )
{
return getPart( buffer.readString() );
}
@Override
public void write( @Nonnull PacketBuffer buffer, @Nonnull PartIngredient ingredient )
{
buffer.writeString( ingredient.getPartName() );
}
private PartIngredient getPart( String partName )
{
final Object result = Api.INSTANCE.registries().recipes().resolveItem( AppEng.MOD_ID, partName );
if( result instanceof ResolverResultSet )
{
final ResolverResultSet resolverResultSet = (ResolverResultSet) result;
return net.minecraft.item.crafting.Ingredient
.fromStacks( resolverResultSet.results.toArray( new ItemStack[resolverResultSet.results.size()] ) );
return PartIngredient
.fromStacks( partName, resolverResultSet.results.toArray( new ItemStack[0] ) );
}
else if( result instanceof ResolverResult )
{
final ResolverResult resolverResult = (ResolverResult) result;
final Item item = Item.getByNameOrId( AppEng.MOD_ID + ":" + resolverResult.itemName );
final ItemStack itemStack = new ItemStack( item, 1, resolverResult.damageValue, resolverResult.compound );
final Item item = ForgeRegistries.ITEMS.getValue( new ResourceLocation( AppEng.MOD_ID, resolverResult.itemName ) );
final ItemStack itemStack = new ItemStack( item, 1, resolverResult.compound );
return net.minecraft.item.crafting.Ingredient.fromStacks( itemStack );
itemStack.setDamage( resolverResult.damageValue );
return PartIngredient.fromStacks( partName, itemStack );
}
AELog.warn( "Looking for ingredient with name '" + partName + "' ended up with a null item!" );
return net.minecraft.item.crafting.Ingredient.EMPTY;
return PartIngredient.empty( partName );
}
}
@@ -1,36 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes.ores;
import net.minecraft.item.ItemStack;
public interface IOreListener
{
/**
* Called with various items registered in the dictionary.
* AppEng.oreDictionary.observe(...) to register them.
*
* @param name name of ore
* @param item item with name
*/
void oreRegistered( String name, ItemStack item );
}
@@ -1,91 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.recipes.ores;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.oredict.OreDictionary;
public class OreDictionaryHandler
{
public static final OreDictionaryHandler INSTANCE = new OreDictionaryHandler();
private final List<IOreListener> oreListeners = new ArrayList<>();
@SubscribeEvent
public void onOreDictionaryRegister( final OreDictionary.OreRegisterEvent event )
{
if( event.getName() == null || event.getOre().isEmpty() )
{
return;
}
if( this.shouldCare( event.getName() ) )
{
for( final IOreListener v : this.oreListeners )
{
v.oreRegistered( event.getName(), event.getOre() );
}
}
}
/**
* Just limit what items are sent to the final listeners, I got sick of strange items showing up...
*
* @param name name about cared item
*
* @return true if it should care
*/
private boolean shouldCare( final String name )
{
return true;
}
/**
* Adds a new IOreListener and immediately notifies it of any previous ores, any ores added latter will be added at
* that point.
*
* @param n to be added ore listener
*/
public void observe( final IOreListener n )
{
this.oreListeners.add( n );
// notify the listener of any ore already in existence.
for( final String name : OreDictionary.getOreNames() )
{
if( name != null && this.shouldCare( name ) )
{
for( final ItemStack item : OreDictionary.getOres( name ) )
{
if( !item.isEmpty() )
{
n.oreRegistered( name, item );
}
}
}
}
}
}