Créer une API (ajout de recette)
-
Sommaire
-
- Création de toute les packages et toutes les classes
- L’enum TutoApi
- Les interfaces de l’Api
- Les classes de l’Api
- Utilisation de l’Api
Introduction
Aujourd’hui nous allons apprendre à créer une API (Application Programming Interface) ou Interface de programmation en français (source).
La problématique est la suivante, imaginez que vous aillez un mod, il contient une machine quelconque possédant des recettes que vous avez préalablement établie dans
votre code source. Mais vous aimeriez qu’un autre moddeur puisse ajouter lui aussi des recettes à votre machine sans modifier votre mod.
La solution, vous la devinez et de créer une API afin qu’il puisse ajouter des recettes.Pré-requis
Code
Création de toute les packages et toutes les classes
Dans un premier temps nous allons créer un enum nommée TutoApi qui se trouvera dans le dossier src/api/java
Pour ce faire sélectionnez votre projet Eclipse contenant votre mod, clique droit -> New -> Source Folder
Dans folder name tapez : src/api/java, validez. Créez maintenant un package avec le nom que vous voulez.
Clique droit -> New -> Package et entrez le nom, validez.
Dans ce package créez un enum appelé TutoApi. Dans le même package créez une interface que vous appelerez ITutoApi.
Créez un sous-package nommé “features” dans lequel vous créez 3 interfaces :- IMachineTutoRecipe
- IMachineTutoRegistry
- IRegistryRecipes
Ensuite dans le package “common” de votre mod (dans le dossier “src/main/java”) créez un package “core”.
Dans “core” créez une classe ‘Api’ et un package ‘features’.
Dans “features” créez une classe ‘MachineTutoRecipe’ et un package ‘registries’
Dans “registries” créez une classe ‘RegistryRecipes’ et une classe ‘RegistryMachineTuto’Vous devriez avoir créé 9 fichier java.
L’enum TutoApi
Nom du fichier : TutoApi
Ce fichier dans le dossier : src/api/java/
Dans le package : modid.api
Nous avons créer un enum car nous n’avons pas besoin d’instance.public enum TutoApi { ; //Aucun champs, il FAUT mettre le point-virgule /** Chemin vers la classe Api */ private static final String API_CLASS_PATH = "prefix.common.core.Api"; /** Nom du champ contenant l'instance de la classe Api */ private static final String INSTANCE_FIELD = "INSTANCE"; /** //Instance de la classe Api */ private static ITutoApi API; /** * Utilisation de la réflection pour récupérer l'instance de la classe Api */ static { try { final Class apiClass = Class.forName(API_CLASS_PATH); final Field instanceField = apiClass.getDeclaredField(INSTANCE_FIELD); API = (ITutoApi)(instanceField.get(apiClass)); } catch (final ClassNotFoundException e) { e.printStackTrace(); } catch (final NoSuchFieldException e) { e.printStackTrace(); } catch (final IllegalAccessException e) { e.printStackTrace(); } } /** * @return L'instance de la classe Api */ public static ITutoApi instance() { return API; } }
Voilà, il n’y a pas grand-chose à expliquer, on utilise la réflection pour obtenir l’instance de la classe Api
Les interfaces de l’Api
ITutoApi
Dossier : src/api/java
Package : modid.apiCette interface permet de récupérer le registre contenant toutes les recettes que nous mettons à disposition via l’API
public interface ITutoApi { /** * @return Le registre contenant toute les recettes */ public IRegistryRecipes recipes(); }
IRegistryRecipes
Dossier : src/api/java
Package : modid.api.featuresCette interface permet de récupérer le registre contenant toute les recettes d’une certaines machine
public interface IRegistryRecipes { /** * @return Le registre des recettes du bloc MachineTuto */ public IMachineTutoRegistry machineTuto(); }
IMachineTutoRegistry
Dossier : src/api/java
Package : modid.api.featuresCette interface permet de récupérer de manipuler les recettes et le carburant du bloc MachineTuto
public interface IMachineTutoRegistry { /** * @return Une liste contenant les recettes du bloc MachineTuto */ public List <imachinetutorecipe>getRecipes(); /** * Permet d'ajouter une recette à la liste de recette * @param ItemStacks d'inputs * @param ItemStacks d'output */ public void addRecipe(ItemStack[] in, ItemStack[] out); /** * Permet de récupérer la recette pour un input donné * @param ItemStacks d'input de la recette cherchée * @return */ public IMachineTutoRecipe getRecipeForInput(ItemStack[] in); /** * Permet d'ajouter un carburant à la machine * @param Le carburant * @param La durée du carburant */ public void addFuel(ItemStack stack, Integer duration); /** * @param Un carburant * @return La durée du carburant */ public int getFuelDuration(ItemStack stack); }
IMachineTutoRecipe
Dossier : src/api/java
Package : modid.api.featuresCette interface représente une recette du bloc MachineTuto
public interface IMachineTutoRecipe { /** * @return ItemStacks d'input de la recette */ public ItemStack[] getInput(); /** * Permet de modifier les ItemStacks d'input de la recette * @param ItemStacks d'input de la recette */ public void setInput(ItemStack[] input); /** * @return ItemStacks d'output de la recette */ public ItemStack[] getOutput(); /** * Permet de modifier les ItemStacks d'output de la recette * @param ItemStacks d'output de la recette */ public void setOutput(ItemStack[] output); }
Comme vous pouvez le voir grâce à ces interfaces le moddeur peut facilement ajouter ou modifier une recette sans réécrire le mod.
Les classes de l’Api
Api
Dossier : src/main/java
Package : prefix.common.coreCette classe est celle dont on a spécifié le chemin dans l’interface TutoApi, elle implémente l’interface ITutoApi
public class Api implements ITutoApi { /** * Instance de l'Api qui est récupéré par réflexion par l'enum TutoApi * "INSTANCE" est le champs qui est renseigné dans le second champs de la classe TutoApi */ public static final Api INSTANCE = new Api(); /** * Instance privé de l'interface IRegistryRecipes */ private IRegistryRecipes registryRecipes; /** * Instanciation de l'interface IRegistryRecipes via la classe RegistryRecipes */ private Api() { registryRecipes = new RegistryRecipes(); } @Override public IRegistryRecipes recipes() { return registryRecipes; } }
RegistryRecipes
Dossier : src/main/java
Package : prefix.common.core.features.registriesCette classe implemente l’interface IRegistryRecipes
public class RegistryRecipes implements IRegistryRecipes { /** * Instance du registre du bloc MachineTuto */ private IMachineTutoRegistry machineTuto;; /** * Instanciation de la variable dans le constructeur */ public RegistryRecipes() { machineTuto = new RegistryMachineTuto(); } @Override public IMachineTutoRegistry machineTuto() { return machineTuto; } }
RegistryMachineTuto
Dossier : src/main/java
Package : prefix.common.core.features.registriesCette classe contient les recettes du bloc MachineTuto et la liste des fuels, elle implémente l’interface IMachineTutoRegistry
public class RegistryMachineTuto implements IMachineTutoRegistry { /** Mapping des carburant avec leur durée */ private Map <itemstack, integer="">fuelToTime = new HashMap<itemstack, integer="">(); /** List de recettes */ private List <imachinetutorecipe>recipes; /** * Ajout des recettes et des carburants de base */ public RegistryMachineTuto() { this.recipes = new ArrayList<imachinetutorecipe>(); this.addFuel(new ItemStack(Items.blaze_powder), 5000); this.addRecipe(new ItemStack[] { new ItemStack(Items.apple), new ItemStack(Items.apple), new ItemStack(Items.apple), new ItemStack(Items.apple) }, new ItemStack[] { new ItemStack(Items.apple), new ItemStack(Items.apple) }); } @Override public int getFuelDuration(ItemStack fuel) { Iterator<entry<itemstack, integer="">> iterator = this.fuelToTime.entrySet().iterator(); Entry <itemstack, integer="">entry; do { if(!iterator.hasNext()) { return 0; } entry = iterator.next(); } while(fuel.getItem() != entry.getKey().getItem() || fuel.getItemDamage() != entry.getKey().getItemDamage() ); return entry.getValue(); } @Override public void addFuel(ItemStack fuel, Integer time) { fuelToTime.put(fuel, time); } @Override public void addRecipe(ItemStack[] input, ItemStack[] output) { if(this.getRecipeForInput(input) == null); recipes.add(new MachineTutoRecipe(input, output)); } @Override public List <imachinetutorecipe>getRecipes() { return recipes; } @Override public IMachineTutoRecipe getRecipeForInput(ItemStack[] in) { if(in == null) return null; if(in.length != TileEntityMachineTuto.INPUTS_SLOTS) return null; for(IMachineTutoRecipe recipe : recipes) { boolean recipeOK = true; ItemStack[] inputs = recipe.getInput(); for(int i = 0; i < in.length; i++) { if(!ItemStack.areItemsEqual(in*, inputs*)) recipeOK = false; } if(recipeOK) return recipe; } return null; } }
MachineTutoRecipe
Dossier : src/main/java
Package : prefix.common.core.featuresCette classe est la classe d’une recette elle implémente l’interface IMachineTutoRecipe
public class MachineTutoRecipe implements IMachineTutoRecipe { /** ItemStacks d'input */ private ItemStack[] inputs; /** ItemStacks d'ouput */ private ItemStack[] outputs; public MachineTutoRecipe(ItemStack[] in, ItemStack[] out) { inputs = in; outputs = out; } @Override public ItemStack[] getInput() { return inputs; } @Override public void setInput(ItemStack[] input) { inputs = input; } @Override public ItemStack[] getOutput() { return outputs; } @Override public void setOutput(ItemStack[] output) { outputs = output; } }
Et voilà, c’est tout.
Utilisation de l’Api
Maintenant que tout est bon, il ne reste plus qu’à savoir s’en servir.
Premièrement, lorsqu’un mod veut utiliser votre API il lui suffit d’ajouter à son buildpath tout ce qui se trouve dans src/api/java
Ensuite quand il veut utiliser votre API il faut qu’il vérifie si votre mod est bien loadLoader.isModLoaded(modid de votre mod) { //Utilisation de votre API }
Exemple de d’utilisation :
Loader.isModLoaded(modid de votre mod) { TutoApi.instance().recipes().machineTuto().addFuel(new ItemStack(Items.bone), 50); }
Ce code ajoute comme carburant une os avec comme durée 50.
C’est aussi simple que cela.
-
Créer une API est une bonne idée pour organiser son mod !
En revanche précise que c’est une API pour les recettes car une API c’est très général.
-
Ici s’en est une pour les recettes parce qu’il fallait un exemple mais si comme tu le dit c’est général une API si on a compris le principe c’est simple de faire autre chose que rajouter des recettes. J’avoue que je ne suis pas encore super bon pour les API j’ai fait ça aujourd’hui en essayant de voir comment ça marche avec Applied donc si il y a des erreurs où autres n’hésite vraiment pas
-
A vrai dire, une API est différente selon les personnes, chez certains c’est plus complet, chez d’autres c’est plus simple, mais le principal c’est de tout expliquer pour que tout le monde le change un peu à sa manière et de ce que je vois (j’ai pas tout lu) c’est bien commenté
-
Ce qui fait bizarre, selon moi c’est que je me suis inspiré de l’API d’Applied qui est complète alors que dans le tutoriel je ne rajoute que le fait d’ajouter des recettes à un bloc ce qui fait qu’on a l’impression de brasser de l’air pour pas grand-chose
-
L’organisation c’est bien, dans mon mod CSP j’ai fait une bonne API pour mes blocks ce qui me permet de créer un block avec model et système d’énergie integré avec seulement quelques lignes : https://github.com/SCAREXgaming/CSP/blob/master/src/main/java/fr/scarex/csp/block/SolarPanelFrame.java