Jump to content

[1.15.1] Get player inventory of previous tick


SonicScrew12

Recommended Posts

Hey there,

 

I'm trying to check if a player's inventory has changed by comparing their current inventory to the inventory of the previous tick. I'm currently using PlayerTickEvent. Here's what I have tried:

private NonNullList<ItemStack> previousInventory = ...;

@SubscribeEvent
public void onPlayerTick(TickEvent.PlayerTickEvent e) {
    if (!e.player.world.isRemote) return;
    NonNullList<ItemStack> currentInventory = e.player.inventory.mainInventory;
	
    /* Comparing the previous inventory's first ItemStack is always the same as the current inventory's first ItemStack. It appears like this:
    Previous first item: 6 dirt
    Current first item: 6 dirt
  
    But the desired effect would be:
    Previous first item: 1 air
    Current first item: 6 dirt
    */ 
    System.out.println("Previous first item: " + previousInventory.get(0));
    System.out.println("Current first item: " + currentInventory.get(0));
  
    if (!currentInventory.equals(previousInventory))
        System.out.println("Inventory changed"); // This never runs

    previousInventory = currentInventory;
}

 

However, I'm running into an issue where the previous inventory is ALWAYS equal to the current inventory. To prove this, I compared the first ItemStack of the previous and current inventory, and they're always the same. Why doesn't this work, and how would I get the player's inventory from the previous frame and compare it with the player's inventory of the current frame?

 

(I know System.out.println() severely slows down performance. I don't care about it right now)

Edited by SonicScrew12
Clarification
Link to comment
Share on other sites

In Java, objects are passed around as pointers.

The default implementation of equals is simply checking whether the two variables are actually pointing towards the same object.

Since the inventory is never replaced by another instance, it will always be equal to the previous inventory.

To check whether the inventory has changed, you will need to iterate through the inventory and compare the ItemStacks one by one with the previous inventory.

 

What are you trying to create though? There might be an easier approach depending on what you are trying to create.

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

20 minutes ago, DavidM said:

In Java, objects are passed around as pointers.

The default implementation of equals is simply checking whether the two variables are actually pointing towards the same object.

Since the inventory is never replaced by another instance, it will always be equal to the previous inventory.

To check whether the inventory has changed, you will need to iterate through the inventory and compare the ItemStacks one by one with the previous inventory.

 

What are you trying to create though? There might be an easier approach depending on what you are trying to create.

That makes sense. However, the problem that I ran into was that although I iterated through both inventories, their contents would still end up being the same, as demonstrated by the comment on my original post. This leads me to believe that something is happening on Forge or Minecraft’s end where one tick isn’t actually performing as I expect it to; that is, run top-to-bottom twice because of start and end phases.

 

Making it only run on one phase also doesn’t seem to fix the issue. It’s certainly not exhibiting similar behaviour to Unity3D’s Update() function, which happens every frame.

 

To answer your question, I’m trying to create a mod that shares a single inventory among every player. I saw it on a YouTube video and thought it would be a fun exercise. I was mistaken. I wish there were more resources on how the game tick works beyond that of Forge’s docs. I can’t figure out the order of how things happen.

 

I figured each player would check for changes in their inventories and other players would call copyInventory() when a change was detected by looping through the player list in the server.

Edited by SonicScrew12
Answering question
Link to comment
Share on other sites

35 minutes ago, SonicScrew12 said:

Making it only run on one phase also doesn’t seem to fix the issue. It’s certainly not exhibiting similar behaviour to Unity3D’s Update() function, which happens every frame.

Minecraft's tick is not relevant to the rendering tick; nor is it asynchronous like Unity's Update. However, these differences does not apply to your situation. You can use it like Unity's MonoBehavior#Update method.

 

1 hour ago, SonicScrew12 said:

However, the problem that I ran into was that although I iterated through both inventories, their contents would still end up being the same, as demonstrated by the comment on my original post.

Please post your code regarding the iteration of the inventories, including the checking of ItemStacks.

 

1 hour ago, SonicScrew12 said:

I figured each player would check for changes in their inventories and other players would call copyInventory() when a change was detected by looping through the player list in the server.

This is potentially not thread-safe AFAIK. Two simultaneous changes made to two different players might result in only one of the inventories being copied. However I don't think there is currently a way to hook into "whenever PlayerInventory changes" to make the operation thread-safe.

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

4 hours ago, DavidM said:

Please post your code regarding the iteration of the inventories, including the checking of ItemStacks.

Okay, I've modified the code to not use equals and instead use ItemStack's built-in comparing method

NonNullList<ItemStack> currentInventory = e.player.inventory.mainInventory;

for (int i = 0; i < currentInventory.size(); i++) {
	if (!ItemStack.areItemStacksEqual(currentInventory.get(i).getStack(), previousInventory.get(i).getStack())) {
        System.out.println("Inventory changed");
        break;
    }
}

previousInventory = currentInventory;

This still does not log the inventory change, because I've found that the previous inventory's contents and the current inventory's contents are always the same by logging it to the console.

Link to comment
Share on other sites

It appears I have fixed the issue by changing the NonNullList into an ArrayList. I have no idea why this worked, but it also allowed me to use ArrayList.equals() to compare the two inventories. It has something to do with Minecraft's NonNullList, but I don't know exactly what. Here's the code if anyone is interested:

ArrayList<ItemStack> currentInventory = new ArrayList<>(e.player.inventory.mainInventory);

if (!currentInventory.equals(previousInventory))
    System.out.println("Inventory changed");

previousInventory = currentInventory;

Thanks to DavidM for the insight

Link to comment
Share on other sites

29 minutes ago, SonicScrew12 said:

It has something to do with Minecraft's NonNullList, but I don't know exactly what.

Because this line:

currentInventory = e.player.inventory.mainInventory;

Creates a copy of a pointer to a list, it does not clone the list (changing one inherently changes the other).

  • Thanks 1

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  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.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.