Jump to content
  • Home
  • Files
  • Docs
Topics
  • All Content

  • This Topic
  • This Forum

  • Advanced Search
  • Existing user? Sign In  

    Sign In



    • Not recommended on shared computers


    • Forgot your password?

  • Sign Up
  • All Activity
  • Home
  • Mod Developer Central
  • User Submitted Tutorials
  • Player-based Crafting Recipes
Currently Supported: 1.16.X (Latest) and 1.15.X (LTS)
Sign in to follow this  
Followers 2
diesieben07

Player-based Crafting Recipes

By diesieben07, September 2, 2014 in User Submitted Tutorials

  • Reply to this topic
  • Start new topic
  • Prev
  • 1
  • 2
  • Next
  • Page 1 of 2  

Recommended Posts

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted September 2, 2014 (edited)

Introduction

 

You may have seen Tutorials about this before, and they most likely used the ItemCraftedEvent from FML. That works (kinda) but has the disadvantage that the actual recipe-result is only computed once the player actually takes out the Item, so the preview does not work. Not nice.

I'm going to show you a different method today.

 

For our player-based crafting recipe we cannot use the normal crafting mechanic, but instead we need to implement the IRecipe interface directly.

The IRecipe interface comes with several methods:

  • boolean matches(InventoryCrafting, World)
    - Pretty obvious, checks if your recipe matches the current contents of the crafting grid
 
  • ItemStack getCraftingResult(InventoryCrafting)

    - Should also be obvious, creates the result based on the contents of the crafting grid

 
  • boolean canFit(int, int)

    - Whether the recipe can fit into a grid of the given size

 
  • ItemStack getRecipeOutput()

    - A generic output of your recipe, this one is never given to the player though

 

  • NonNullList<ItemStack> getRemainingItems(InventoryCrafting)

- The items that remain in the crafting grid after the recipe has been applied, you almost never need to override it.

 

  • NonNullList<Ingredient> getIngredients()

- A list of ingredients for this recipe, most recipe classes don't actually implement this.

 

  • boolean isHidden()

- Related to whether this recipe is unlocked by default.

 

  • String getGroup()

- The group of this recipe.

 

In this tutorial I will not go into how to implement any complex matching algorithms, instead we will be making the classic "Dirt to diamond" recipe.

 

Getting the player

Now, to the actual topic of this tutorial, getting the player currently crafting. As you probably have noticed, all we get from vanilla Minecraft to determine the output of our recipe is the World and the crafting grid (InventoryCrafting). The 2nd one is the key, because it holds a reference to the player, via some tricks.

If you look at the InventoryCrafting class you can see that it has a reference to the Container containing the crafting grid (field eventHandler). The field is private, so we use Java Reflection to access it.

There are 2 possibilities for this container that we care about:

  • ContainerPlayer, used when the player is just viewing the normal inventory and using the 2x2 crafting grid
  • ContainerWorkbench, used when the player is using a workbench to craft

 

There are more possibilities for this, it could for example be a Container by a mod with a custom crafting table or a ContainerSheep (yes, that exists). In case of another mod's workbench you'll have to find other ways to find the player, if you want those to be supported. We don't care about ContainerSheep.

 

Getting the player from ContainerPlayer

 

Once you've determined the type of container to be a ContainerPlayer (use an instanceof check for this), getting the player is easy: ContainerPlayer has a field player, which holds the player. That field is again private, but as before you can use Reflection to access it.

 

Getting the player from ContainerWorkbench

 

If the container is a ContainerWorkbench, the player instance is a bit harder to obtain. If you look at the class, there is no obvious reference to any player to be found. The first slot being added however is a SlotCrafting, which holds a reference to the player again. To get that Slot, use the getSlot method from the Container class to get the first Slot (index 0). Then you can get the field player from the SlotCrafting, again via reflection.

 

Wrapping up

 

Almost done.

To register your recipe you will need a few json files (check out the CraftingSystemTest class in the forge test mods), which I will not go into here.

 

Example

 

Yep, lot's of reflection tricky going on here, so let me provide you with an example (please don't just copy and paste it):

 

class DirtToDiamond implements IRecipe {

  @Override
  public boolean matches(InventoryCrafting inv, World world) {
    EntityPlayer player = findPlayer(inv);
    return player != null // player can be null if we can't determine it
      && player.getCommandSenderName().startsWith("d") // do whatever checks you need to about the player
      && hasDirt(inv); // the actual recipe check
  }

  @Override
  public ItemStack getCraftingResult(InventoryCrafting inv) {
    // only called if matches() returns true, so no need to check again
    return new ItemStack(Items.diamond);
  }

  @Override
  public boolean canFit(int width, int height) {
    return true;
  }

  @Override
   public ItemStack getRecipeOutput() {
    return new ItemStack(Items.diamond);
  }

  private static boolean hasDirt(InventoryCrafting inv) {
    Item dirt = Item.getItemFromBlock(Blocks.dirt);
    for (int i = 0; i < inv.getSizeInventory(); i++) {
      ItemStack stack = inv.getStackInSlot(i);
      if (stack != null && stack.getItem() == dirt) {
        return true;
      }
    }
    return false;
  }

  // TODO: SRG names for non-dev environment
  private static final Field eventHandlerField = ReflectionHelper.findField(InventoryCrafting.class, "eventHandler");
  private static final Field containerPlayerPlayerField = ReflectionHelper.findField(ContainerPlayer.class, "player");
  private static final Field slotCraftingPlayerField = ReflectionHelper.findField(SlotCrafting.class, "player");

  private static EntityPlayer findPlayer(InventoryCrafting inv) {
    try {
      Container container = (Container) eventHandlerField.get(inv);
      if (container instanceof ContainerPlayer) {
        return (EntityPlayer) containerPlayerPlayerField.get(container);
      } else if (container instanceof ContainerWorkbench) {
        return (EntityPlayer) slotCraftingPlayerField.get(container.getSlot(0));
      } else {
        // don't know the player
        return null;
      }
    } catch (Exception e) {
      throw Throwables.propagate(e);
    }
  }
}

 

Edited September 14, 2017 by diesieben07
Update for new forum formatting and newer Minecraft versions
  • Like 3
  • Quote

Share this post


Link to post
Share on other sites

imadnsn    37

imadnsn

imadnsn    37

  • Creeper Killer
  • imadnsn
  • Members
  • 37
  • 186 posts
Posted September 3, 2014

Although I never worked with Container or IRecipe before, but that's clever and will be useful later on. :)

Keep up the good work diesieben07 ;)

  • Quote

Share this post


Link to post
Share on other sites

shieldbug1    70

shieldbug1

shieldbug1    70

  • Diamond Finder
  • shieldbug1
  • Forge Modder
  • 70
  • 404 posts
Posted September 4, 2014

The SRG names are:

 

InventoryCrafting#eventHandler = field_70465_c

ContainerPlayer#thePlayer = field_82862_h

SlotCraftin#thePlayer = field_75238_b

  • Like 1
  • Quote

BEFORE ASKING FOR HELP READ THE EAQ!

 

I'll help if I can. Apologies if I do something obviously stupid. :D

 

If you don't know basic Java yet, go and follow these tutorials.

Share this post


Link to post
Share on other sites

bcwadsworth    1

bcwadsworth

bcwadsworth    1

  • Tree Puncher
  • bcwadsworth
  • Forge Modder
  • 1
  • 24 posts
Posted September 17, 2014

Is there a way to do recipies like this for the furnace?

  • Quote

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted September 17, 2014

No.

  • Quote

Share this post


Link to post
Share on other sites

GotoLink    381

GotoLink

GotoLink    381

  • World Shaper
  • GotoLink
  • Members
  • 381
  • 2012 posts
Posted September 17, 2014

Hum I wouldn't suggest throwing an exception just for a failed player detection in a recipe. Especially since you can still rely on ItemCraftedEvent to get you the final data with far less risk.

 

I am also unsure how that tutorial relates to any "preview" issue as stated in the introduction ?

 

Anyways, it kinda is nice to see a bit of reflection explained to the masses :)

  • Quote

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted September 17, 2014

Hum I wouldn't suggest throwing an exception just for a failed player detection in a recipe.

The code only throws an exception when the fields cannot be accessed via Reflection, which is a programmer problem and should never happen. If the exception is thrown it's a Bug and it's ok to crash.
Especially since you can still rely on ItemCraftedEvent to get you the final data with far less risk.
You cannot. ItemCraftedEvent is not designed to modify the results of the crafting.

I am also unsure how that tutorial relates to any "preview" issue as stated in the introduction ?

Assuming you cancel the crafting via ItemCraftedEvent for a certain Player X (ignoring the fact that that is not what the Event is made for). If that player now tries to craft, it first looks like it is possible (the result shows up) and only when they try to take out the result it will magically vanish. With the method explained here the result would not even show up in the first place if Player X tries to craft.

Anyways, it kinda is nice to see a bit of reflection explained to the masses :)

Thanks.
  • Quote

Share this post


Link to post
Share on other sites

GotoLink    381

GotoLink

GotoLink    381

  • World Shaper
  • GotoLink
  • Members
  • 381
  • 2012 posts
Posted September 17, 2014

The code only throws an exception when the fields cannot be accessed via Reflection, which is a programmer problem and should never happen. If the exception is thrown it's a Bug and it's ok to crash.

There is always the SecurityManager issue when dealing with Reflection. This matter is a bit more subtle than the usual bugs one can encounter in type-safety situations. Just wanted to point that out, since this could also count as an introductory tutorial on reflection.

 

You cannot. ItemCraftedEvent is not designed to modify the results of the crafting.

Except you can. ItemStack is mutable. AFAIK, this is the only point of the event, to add/change data to the craft. Or make statistics (meh. boring.)

 

Assuming you cancel the crafting via ItemCraftedEvent for a certain Player X (ignoring the fact that that is not what the Event is made for). If that player now tries to craft, it first looks like it is possible (the result shows up) and only when they try to take out the result it will magically vanish. With the method explained here the result would not even show up in the first place if Player X tries to craft.

Well you can't cancel that event so that solves it. Didn't think of such case though. Sounds kinda unfair to not give a specific player his item if he wants to spend the ressources for it...but still a valid point.

  • Quote

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted September 17, 2014

There is always the SecurityManager issue when dealing with Reflection. This matter is a bit more subtle than the usual bugs one can encounter in type-safety situations. Just wanted to point that out, since this could also count as an introductory tutorial on reflection.

This is by no means a tutorial on reflection. And in the Minecraft environment a Security Manager is not an issue (as of right now).

 

Except you can. ItemStack is mutable. AFAIK, this is the only point of the event, to add/change data to the craft. Or make statistics (meh. boring.)

Yes, ItemStack is mutable. Yes, you could change the result of the crafting. But that is the entire point of this tutorial: It would have the "preview" issue I described. If you e.g. change the resulting Item it will look weird when being taken out of the crafting grid, because it suddenly changes. Which it does not with this method.

 

Well you can't cancel that event so that solves it. Didn't think of such case though. Sounds kinda unfair to not give a specific player his item if he wants to spend the ressources for it...but still a valid point.

Well, people still use it for that. And yeah, see above.
  • Quote

Share this post


Link to post
Share on other sites

Atijaf    4

Atijaf

Atijaf    4

  • Creeper Killer
  • Atijaf
  • Members
  • 4
  • 198 posts
Posted August 25, 2015

Would there be a way to add a shaped recipe?

  • Quote

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted August 26, 2015

Of course.

  • Quote

Share this post


Link to post
Share on other sites

Atijaf    4

Atijaf

Atijaf    4

  • Creeper Killer
  • Atijaf
  • Members
  • 4
  • 198 posts
Posted August 26, 2015

Of course.

 

Where would I add the shaped recipe?  Within the class that you showed an example of, or with GameRegistry?

  • Quote

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted August 26, 2015

The easiest way would be to extend ShapedOreRecipe.

  • Quote

Share this post


Link to post
Share on other sites

Atijaf    4

Atijaf

Atijaf    4

  • Creeper Killer
  • Atijaf
  • Members
  • 4
  • 198 posts
Posted August 27, 2015

Thanks a bunch.  I thought I'd have to make a class for each recipe, but nope!  haha:)  Great news.  Thanks again!

  • Quote

Share this post


Link to post
Share on other sites

Atijaf    4

Atijaf

Atijaf    4

  • Creeper Killer
  • Atijaf
  • Members
  • 4
  • 198 posts
Posted August 28, 2015

One more question!:)

 

So in my class that extends ShapedOreRecipes, I have a member variable, named requiredLevel, that is assigned a value through a constructor.  How would I later obtain this value?  Better yet, how would I obtain this value through the event, "ItemCraftedEvent"?

 

Thanks again for all the help

  • Quote

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted August 28, 2015

I am not quite sure I understand your question. Why would you use the ItemCraftedEvent? You need to override the matches() method and in there check if the player is allowed to craft this recipe.

  • Quote

Share this post


Link to post
Share on other sites

Atijaf    4

Atijaf

Atijaf    4

  • Creeper Killer
  • Atijaf
  • Members
  • 4
  • 198 posts
Posted August 28, 2015

I am not quite sure I understand your question. Why would you use the ItemCraftedEvent? You need to override the matches() method and in there check if the player is allowed to craft this recipe.

 

Upon crafting the item, the player gets crafting xp which depends on the level that is required to craft it.  That level is stored as a member variable inside my customShapedRecipe class.

 

I use ItemCraftedEvent to give the player xp when he crafts it.

  • Quote

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted August 28, 2015

Uhm... you don't need to store that in the recipe.

  • Quote

Share this post


Link to post
Share on other sites

Atijaf    4

Atijaf

Atijaf    4

  • Creeper Killer
  • Atijaf
  • Members
  • 4
  • 198 posts
Posted August 28, 2015

I'm open to suggestions..  I'm just not sure how to go about setting requirements for crafting recipes without setting a variable for each recipe.

  • Quote

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted August 28, 2015

You store the required level somewhere, e.g. in a Map or whatever, it depends on the algorithm that determines this level.

Then in the recipe matches() method you check if that level is satisfied by the player.

Then in the ItemCraftedEvent you get it again (not from the recipe!) and then give that XP to the player.

  • Quote

Share this post


Link to post
Share on other sites

Atijaf    4

Atijaf

Atijaf    4

  • Creeper Killer
  • Atijaf
  • Members
  • 4
  • 198 posts
Posted August 28, 2015

You store the required level somewhere, e.g. in a Map or whatever, it depends on the algorithm that determines this level.

Then in the recipe matches() method you check if that level is satisfied by the player. (DONE)

Then in the ItemCraftedEvent you get it again (not from the recipe!) and then give that XP to the player.

 

So in the map, the key would be the crafting recipe I'm guessing.

 

Why shouldn't I store the value in my customShapedRecipe?

 

Here's the class, just in case you were wondering what exactly it is I'm doing.  (It mostly copies ShapedOreRecipe)

http://pastebin.com/NqyNydHM

 

How I register the crafting recipe

        GameRegistry.addRecipe(new customShapedRecipe(new ItemStack(Blocks.stone, 1), new Object[] {"CCC","CCC","CCC", 'C', Blocks.cobblestone, Ints.asList(5, 1), Arrays.asList("WoodCutting", "Mining"), Arrays.asList("IronMan", "Daniel wasn't there")}));

  • Quote

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted August 28, 2015

Ohgodwhy.

 

I said to extend ShapedOreRecipe so you don't need to do all that copy-pasta bullshit.

Only override matches. Call super and check an additional condition (enough XP). That's all.

 

You can't store it in the recipe, because in ItemCraftedEvent you don't have access to the recipe.

And no, the key in the Map would not be the recipe. It would be whatever you need to determine the XP required (Item? I don't know! It's your mod).

  • Quote

Share this post


Link to post
Share on other sites

Atijaf    4

Atijaf

Atijaf    4

  • Creeper Killer
  • Atijaf
  • Members
  • 4
  • 198 posts
Posted August 28, 2015

Oh man I feel dumb!  Haha and btw, I really enjoy how you respond.  With such horror:p. They're memorable.

 

As for overriding absolutely everything, I gave that a thought, but then life happened..  Thanks for answering my questions.  They solved my problem, as well as some of my ideas.

  • Quote

Share this post


Link to post
Share on other sites

Abastro    123

Abastro

Abastro    123

  • World Shaper
  • Abastro
  • Forge Modder
  • 123
  • 1075 posts
Posted September 18, 2015

In this way one cannot get player from crafting done by inventory.

I think there was forge hook on getting player..

  • Quote

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

Share this post


Link to post
Share on other sites

diesieben07    7586

diesieben07

diesieben07    7586

  • Reality Controller
  • diesieben07
  • Forum Team
  • 7586
  • 54922 posts
Posted September 18, 2015

In this way one cannot get player from crafting done by inventory.

Sure one can.
  • Quote

Share this post


Link to post
Share on other sites
  • Prev
  • 1
  • 2
  • Next
  • Page 1 of 2  

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

    • Insert image from URL
×
  • Desktop
  • Tablet
  • Phone
Sign in to follow this  
Followers 2
Go To Topic Listing



  • Recently Browsing

    No registered users viewing this page.

  • Posts

    • DaemonUmbra
      Update mod to 1.16.5

      By DaemonUmbra · Posted 8 minutes ago

      Is it a mod you made or are you trying to update someone else's mod?
    • DaemonUmbra
      Any places for support with 1.8

      By DaemonUmbra · Posted 9 minutes ago

      We don't keep track of places that provide support for old versions.
    • monkeysHK
      [1.16] Transparent Picture Render in GUI

      By monkeysHK · Posted 14 minutes ago

      Hello. I am having trouble making a picture render transparent on my Screen and I am not sure what the problem is. It renders correctly but not transparent. Here is my code in MyScreen::render Also I want to know how to remove the effect of RenderSystem afterwards for further renders. TextureManager tm = minecraft.getTextureManager(); ResourceLocation rl = new ResourceLocation("minecraft", "textures/gui/widgets.png"); tm.bindTexture(rl); matrixStack.push(); { RenderSystem.colorMask(true, true, true, true); RenderSystem.blendColor(1f, 1f, 1f, 0.5f); RenderSystem.enableBlend(); matrixStack.translate(instru_pos[currentNbo.ordinal()][0], instru_pos[currentNbo.ordinal()][1], 0); matrixStack.scale(scale, scale, scale); this.blit(matrixStack, 0, 0, 0, 146, 20, 20); } matrixStack.pop(); Any replies will be appreciated.
    • chuckdie5el
      Server Console errors

      By chuckdie5el · Posted 24 minutes ago

      I took a section of the log from well before it started showing those issues to a few thousand lines of it. Hope this helps debug.txt
    • SixSix
      I need help to learn

      By SixSix · Posted 50 minutes ago

      ¿Cómo puedo aprender a codificar para hacer mods? Llevo días preguntandome como poder hacerlo, aunque existan opciones faciles como mcreator. No me convence, puesto que quitaron los modelos .java del geckolib en blockbench y decidí buscar cursos de java, pero no sé que especificamente por donde empezar o si quiera si me servirán.
  • Topics

    • Heinzchen
      1
      Update mod to 1.16.5

      By Heinzchen
      Started 3 hours ago

    • iiDk_lol
      1
      Any places for support with 1.8

      By iiDk_lol
      Started 1 hour ago

    • monkeysHK
      0
      [1.16] Transparent Picture Render in GUI

      By monkeysHK
      Started 14 minutes ago

    • chuckdie5el
      3
      Server Console errors

      By chuckdie5el
      Started 4 hours ago

    • SixSix
      0
      I need help to learn

      By SixSix
      Started 50 minutes ago

  • Who's Online (See full list)

    • Godis_apan
    • DaemonUmbra
    • Ultrawebsling
    • starstorms21
    • Frederik2000
    • monkeysHK
    • tiktok
    • metword
    • Danebi
  • All Activity
  • Home
  • Mod Developer Central
  • User Submitted Tutorials
  • Player-based Crafting Recipes
  • Theme

Copyright © 2019 ForgeDevelopment LLC · Ads by Longitude Ads LLC Powered by Invision Community