Jump to content

[1.16.5] save/transfer of capability


Luis_ST

Recommended Posts

Some time ago I created a capability that expands the enderchest inventory,

now I have encountered the following problem:

if the player dies or uses the end portal, the inventory is deleted.

I know that I have to use the PlayerEvent#Clone to clone the capability

now my questions:

  1. Do I need another event in which I have to clone / transfer the capability?
  2. how do i clone the capability exactly? that is my beginning with which i tried it:

 

	@SubscribeEvent
	public static void PlayerClone(PlayerEvent.Clone event) {
		
		PlayerEntity original = event.getOriginal();
		PlayerEntity player = event.getPlayer();
		IBackpackItemHandler backpackHandler = original.getCapability(BackpackCapability.BACKPACK, null)
				.orElseThrow(() -> new NullPointerException("The mod Capability<IBackpackItemHandler> is null"));
		IEnderChestItemHandler enderChestHandler = original.getCapability(EnderChestCapability.ENDERCHEST, null)
				.orElseThrow(() -> new NullPointerException("The mod Capability<IBackpackItemHandler> is null"));
		
		player.getCapability(BackpackCapability.BACKPACK, null).orElseGet(() -> backpackHandler);
		player.getCapability(EnderChestCapability.ENDERCHEST, null).orElseGet(() -> enderChestHandler);
		
	}

 

Link to comment
Share on other sites

47 minutes ago, Luis_ST said:

player.getCapability(BackpackCapability.BACKPACK, null).orElseGet(() -> backpackHandler);

This doesn't actually give the new player entity the capability data. Because orElseGet returns a value (in this case, your local variable, backpackHandler) in the event that the original getCap call returns null. No reference is made between the player object and this handler and as soon as your method returns, the inventory is lost.

 

You need to copy the inventory from one capability to the other.

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

10 minutes ago, Draco18s said:

This doesn't actually give the new player entity the capability data. Because orElseGet returns a value (in this case, your local variable, backpackHandler) in the event that the original getCap call returns null. No reference is made between the player object and this handler and as soon as your method returns, the inventory is lost.

You need to copy the inventory from one capability to the other.

I've already thought that, but how do I copy the capability from one player to the other since there is no setCapability method?

Link to comment
Share on other sites

Your capability contains item stacks, right?
Why not move the item stacks from one capability to the other?

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

34 minutes ago, Draco18s said:

Your capability contains item stacks, right?

yes

 

34 minutes ago, Draco18s said:

Why not move the item stacks from one capability to the other?

like this:

		PlayerEntity original = event.getOriginal();
		PlayerEntity player = event.getPlayer();
		IBackpackItemHandler oldBackpackHandler = original.getCapability(BackpackCapability.BACKPACK, null)
				.orElseThrow(() -> new NullPointerException("The mod Capability<IBackpackItemHandler> is null"));
		CombinedInvWrapper oldEnderChestHandler = original.getCapability(EnderChestCapability.ENDERCHEST, null)
				.orElseThrow(() -> new NullPointerException("The mod Capability<CombinedInvWrapper<IEnderChestItemHandler>> is null"));
		IBackpackItemHandler newBackpackHandler = player.getCapability(BackpackCapability.BACKPACK, null).orElse(null);
		CombinedInvWrapper newEnderChestHandler = player.getCapability(EnderChestCapability.ENDERCHEST, null).orElse(null);
		
		newBackpackHandler = oldBackpackHandler;
		newEnderChestHandler = oldEnderChestHandler;

 

what i don't understand is how to give the new one back to the game?

because somehow I have to tell the game that something has changed

Link to comment
Share on other sites

"Assign this local variable to the reference stored in this other local variable. Now, recycle these local variables."

 

You haven't actually done anything except create two pointers to the same capability data (which you then feed to the garbage collector).

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

40 minutes ago, Draco18s said:

"Assign this local variable to the reference stored in this other local variable. Now, recycle these local variables."

You haven't actually done anything except create two pointers to the same capability data (which you then feed to the garbage collector).

I know, but how?

how exactly do I have to hand over my capability? is it enough if I create a new one?

or do I have to replace the capability <IBackpackItemHandler> in my capability class

 

1 hour ago, Luis_ST said:

what i don't understand is how to give the new one back to the game?

because somehow I have to tell the game that something has changed

.

Link to comment
Share on other sites

Take the items out of one capability and put them into the other.

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

17 hours ago, Draco18s said:

Take the items out of one capability and put them into the other.

okay i got that, but i'm just too stupid for the rest of it.

which methods do I need to clone the capability?

 

Edit: do I have to create a new capability for the new player and then put the old ones into it?

update: I looked at a few github mods that also use capabilities, from which I created this:

		original.getCapability(BackpackCapability.BACKPACK, null).ifPresent(oldBackpack -> {
			
			player.getCapability(BackpackCapability.BACKPACK, null).ifPresent(newBackpack -> {
				
				newBackpack = oldBackpack;
				
			});
			
		});

 

Edited by Luis_ST
Link to comment
Share on other sites

36 minutes ago, loordgek said:

save the old oldBackpack to nbt and load it in the new one

okay i think i can use here serializeNBT and deserializeNBT of the capability provider

right? if so how do I get the provider from my capability?

if not what methods should i use instead?

Link to comment
Share on other sites

8 hours ago, Luis_ST said:

okay i got that, but i'm just too stupid for the rest of it.

which methods do I need to clone the capability?

 

Edit: do I have to create a new capability for the new player and then put the old ones into it?

update: I looked at a few github mods that also use capabilities, from which I created this:


		original.getCapability(BackpackCapability.BACKPACK, null).ifPresent(oldBackpack -> {
			
			player.getCapability(BackpackCapability.BACKPACK, null).ifPresent(newBackpack -> {
				
				newBackpack = oldBackpack;
				
			});
			
		});

 

Hooray! You did the same thing again! Only this time with lambdas!

 

Lets put it this way, you have a chest on one side of your house with raw beef in it and have just built a new kitchen and you want to move all that beef into a new chest in the kitchen next to a furnace.

 

Which makes more sense?

A) Use pistons to move the old chest to the new chest's location.

B) Take the beef out of the chest and place it in the new chest.

Edited by Draco18s

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

35 minutes ago, diesieben07 said:

You don't have to use NBT.

For all the data you have in your capability, copy it over.

since my capability does not contain a get/set method i have to create it first

do i create this in my provider because it contains the LazyOptional and BackpackItemStackHandler or in the actual capability class?

and in which class does vanilla make this?

since the enderchest inventory and the player invantar are actually only capabilities or that's not true

then I could understand how exactly I clone the capability and could then transfer this to my two capabilities,

which are only one extension of the ItemStackHandler

 

4 minutes ago, Draco18s said:

Which makes more sense?

B😄

Link to comment
Share on other sites

3 minutes ago, Luis_ST said:

B😄

Then why do you keep building piston contraptions?

image.png.78895c5cb8e58911b8c27186c9a14acf.png

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

14 minutes ago, diesieben07 said:

How do you do anything with your capability then if you can't access its data?

you explained to me how to create a capability (here at the forum)

this is my capability class

 

10 minutes ago, Draco18s said:

Then why do you keep building piston contraptions?

since this was my first approach, and it turned out to be wrong

 

Edited by Luis_ST
Link to comment
Share on other sites

Just now, diesieben07 said:

Okay so it's just an ItemStackHandler.

yes

 

1 minute ago, diesieben07 said:

Use ItemStackHandler#serializeNBT to write it to NBT then use ItemStackHandler#deserializeNBT to transfer it to the new capability.

because with getCapability I get an extension of IItemHandlerModifiable and no ItemStackHandler can I cast it to ItemStackHandler?

Link to comment
Share on other sites

20 minutes ago, diesieben07 said:

Yes you can cast it if you don't intend for other developers to implement your capability.

like that:

		original.getCapability(BackpackCapability.BACKPACK, null).ifPresent(oldBackpack -> {
			
			CompoundNBT nbt = ((ItemStackHandler) oldBackpack).serializeNBT();
					
			player.getCapability(BackpackCapability.BACKPACK, null).ifPresent(newBackpack -> {
				
				((ItemStackHandler) newBackpack).deserializeNBT(nbt);
				
			});
			
		});

 

Link to comment
Share on other sites

56 minutes ago, Luis_ST said:

since this was my first approach, and it turned out to be wrong

First, second, third, and fourth.

  • Haha 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

35 minutes ago, diesieben07 said:

You can use the Direction parameter of getCapability. If the direction is not null, return only your handler instead of the combined one.

okay this  is my capability class now

when I try to get the capability on the way:

		original.getCapability(EnderChestCapability.ENDERCHEST, Direction.WEST).ifPresent(oldEnderChest -> {
			
			CompoundNBT nbt = ((ItemStackHandler) oldEnderChest).serializeNBT();
					
			player.getCapability(EnderChestCapability.ENDERCHEST, Direction.WEST).ifPresent(newEnderChest -> {
				
				((ItemStackHandler) newEnderChest).deserializeNBT(nbt);
				
			});
			
		});

 

eclipse gives me the following error:

"Cannot cast from CombinedInvWrapper to ItemStackHandler"

Link to comment
Share on other sites

Update: I have changed a few things / tried but in most cases I get a ClassCastException,

because I cannot cast the CombinedInvWrapper to an ItemStackHandler,

although my capability should return an ItemStackHandler if I specify a direction.

 

that is the important part of my capability provider:

		private EnderChestItemStackHandler inventory = new EnderChestItemStackHandler(27);
		private PlayerEntity player;
		private LazyOptional<EnderChestItemStackHandler> lazyOptional = LazyOptional.of(() -> inventory);
		private LazyOptional<CombinedInvWrapper> optional = LazyOptional.of(() -> {
			
			EnderChestInventory enderChestInventory = player.getInventoryEnderChest();
			InvWrapper invWrapper = new InvWrapper(enderChestInventory);
			CombinedInvWrapper combinedInvWrapper = new CombinedInvWrapper(invWrapper, inventory);
			
			return combinedInvWrapper;
			
		});
		
		@Override
		@SuppressWarnings({ "unchecked" })
		public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
			
			LazyOptional<?> ret = side == null ? optional : lazyOptional;
			return cap == ENDERCHEST && cap != null ? (LazyOptional<T>) ret : LazyOptional.empty();
			
		}

 

and how I am currently trying to get the capability (I have to use IItemHandlerModifiable first because, as already said,

I cannot cast the CombinedInvWrapper to an ItemStackHandler and it should actually work anyway because getCapability should return an extension of the ItemStackHandler in this case)

		original.getCapability(EnderChestCapability.ENDERCHEST, Direction.WEST).ifPresent(oldEnderChest -> {
			
			IItemHandlerModifiable oldItemModifiable = oldEnderChest;
			ItemStackHandler oldItemHandler = (ItemStackHandler) oldItemModifiable;
			CompoundNBT nbt = oldItemHandler.serializeNBT();
					
			player.getCapability(EnderChestCapability.ENDERCHEST, Direction.WEST).ifPresent(newEnderChest -> {
				
				IItemHandlerModifiable newItemModifiable = newEnderChest;
				ItemStackHandler newItemHandler = (ItemStackHandler) newItemModifiable;
				newItemHandler.deserializeNBT(nbt);
				
			});
			
		});

 

what do i have to change?

 

Link to comment
Share on other sites

1 hour ago, Luis_ST said:

cap == ENDERCHEST && cap != null

if cap IS the enderchest, then it CAN'T be null and vice versa.

 

  • 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

38 minutes ago, Draco18s said:

if cap IS the enderchest, then it CAN'T be null and vice versa.

1 hour ago, diesieben07 said:

These variable names are terrible.

The code in your Git repository does not use a Direction to query the capability in your PlayerEvent.Clone handler. Please update your Git repo.

updated and changed the things you mentioned

Link to comment
Share on other sites

14 hours ago, diesieben07 said:

Here you define that your capability always returns a CombinedInvWrapper. Which is not true now.

okay thanks i changed the capability to IItemHandlerModifiable and now it works

do I have to clone the capability in another event?

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.



×
×
  • Create New...

Important Information

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