Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

[1.16.4] Display Inventory of a different container


Recommended Posts

I am currently wokring on a block which lets you register and see the inventory of registered blocks. I.e. i want to display what is inside a chest which could be 10 blocks away. I am not looking to change its inventory, just look at it. Since i can't just use the LockableLootTileEntity::getItems method I have to set with recreating the inventory by looping through with LockableLootTileEntity::getStackInSlot. Problem is I only get an empty ItemStack (1 air block) from that method. No matter which index. For reference:

private void changeInventory(LockableLootTileEntity te) {
		int size = te.getSizeInventory();
		this.activeInventory = NonNullList.create();
		for(int i = 0; i < size; i++) {
			this.activeInventory.add(te.getStackInSlot(i));
		}
	}

What am I missing. Is there a better way to read an inventory of a different block? Going the normal way via the container does not work either since that field is protected as well.

Link to post
Share on other sites

I thought there would be a forge way to get access to the inventory. I havent yet fully wrapped my head around the capability system which is probably why I didnt think of it. I will get back to you if I cant figure it out by myself.

 

The above code is located in the screen class of my "controler" block. Im using the inventory there in order to just render the ItemStacks. For now I dont plan to add other functionality in the screen. Later on I would want to add a villager interaction but thats some time away.

 

As a side question: This forum is not yet switched over to the official mappings? And if not, will it in the near future? Or will you support both for now?

Link to post
Share on other sites
56 minutes ago, Titmar said:

The above code is located in the screen class of my "controler" block. Im using the inventory there in order to just render the ItemStacks.

There is your issue. Inventories are not known on the client unless you are currently looking at them in a GUI or the block takes extra care to sync them (none of the vanilla blocks do this).

Link to post
Share on other sites
57 minutes ago, Titmar said:

As a side question: This forum is not yet switched over to the official mappings? And if not, will it in the near future? Or will you support both for now?

Personally I switched all my workspaces, but if you ask something in MCP mappings I can convert. But I'll probably answer in Mojang names :P

Link to post
Share on other sites
25 minutes ago, diesieben07 said:

There is your issue. Inventories are not known on the client unless you are currently looking at them in a GUI or the block takes extra care to sync them (none of the vanilla blocks do this).

okay, so even with the capability i wouldnt be able to get the inventory clientside i suppose. How would i sync from server to client in this case? Because apparently

private void changeInventory(LockableLootTileEntity te) {
		IItemHandler handler = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null); 
		if(handler == null) return;
		this.activeInventory = NonNullList.create();
		for(int i = 0; i < handler.getSlots(); i++) {
			this.activeInventory.add(handler.getStackInSlot(i));
		}
	}

does not resolve the issue

Link to post
Share on other sites
19 minutes ago, loordgek said:

you need to look server side for items and sent them to the client

 

I know how to send from client to server via the SimpleChannel Network but I have no idea how to send packets the other way. As in, I could send a request to the server from the client but how would I answer it? How do i send backwards?

Link to post
Share on other sites

I do have said block which you can register inventory blocks to, i call it market. When opening its GUI I have a bunch of buttons which are connected to those registered inventories. When pressing a button i want to show the inventory of its connected block until i chose another (similar to the selection of an active trade with a villager). Now I could of course set some global variable such that it shows the same inventory for every player. But I would like it independent such that every player can browse independently from others.

Link to post
Share on other sites

Okay so I had a bit of a deeper look into the syncing of containers. Vanilla is using the Container::detectAndSendChanges method which in turn uses a list of private listeners it sends those changes to. My current code

Spoiler

In my container class


	public void setActiveInventory(BlockPos pos) {
		if(!(this.te.getWorld().getTileEntity(pos) instanceof LockableLootTileEntity)) {
			return;
		}
		LockableLootTileEntity tile = (LockableLootTileEntity) this.te.getWorld().getTileEntity(pos);
		IItemHandler handler = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null);
		if (handler != null) {
			activeInventory = NonNullList.create();
			this.inventorySlots.clear();
			int startX = 108;
			int startY = 34;
			int row = 1;
			int col = 1;
			for (int i = 0; i < handler.getSlots(); i++) {
				this.addSlot(new Slot(tile, i, startX + (col * 16), startY + (row * 16)));
				col++;
				if (col > 9) {
					col = 1;
					row++;
				}
				activeInventory.add(handler.getStackInSlot(i));
			}
			detectAndSendChanges();
		}
	}

 

You see I am resetting the slots and recreate them for the new Inventory which makes the vanilla method unable to detect any changes. Reason being that I would like to be able to support custom sized inventories. Of course I could set a maximum size for the inventory and just clear all slots before refilling them. But that is just limiting functionality.

Now I cant override the Container::detectAndSendChanges method since it uses said private field of listeners making it inaccessible for child classes.

Also, when I call above function setActiveInventory from my Screen class, does that even solve the whole sync issue? Or is there a standard way of doing this? Since opening the GUI creates a new Container every time and I cant access that from anywhere...

Link to post
Share on other sites

If you just want to display the data I wouldn't use normal Slots (they come with a lot of baggage to support interacting with the slot). So I'd just check for changes in the inventory in detectAndSendChanges (similar to how vanilla does it for normal slots) and then send any changes using a custom packet.

Link to post
Share on other sites

Alright, I got it all working now. In the end it was easy after finding out abut the player.openContainer field...

On Button press, send a custom packet. This custom packet tells the server to update the players openContainer like so:

Spoiler

public static void handle(ChangeActiveInventoryMessage message, Supplier<NetworkEvent.Context> contextSupplier) {
		NetworkEvent.Context context = contextSupplier.get();
		context.enqueueWork(() -> {
			ServerPlayerEntity player = context.getSender();
			if(player.openContainer instanceof MarketContainer) {
				((MarketContainer)player.openContainer).setActiveInventory(message.pos);
			}
		});
		context.setPacketHandled(true);
	}

 

Nothing else to do afterwards. Vanilla takes care of the rest apparently. At least for now I havent found any bugs, though I only tested it with a single player and only on a single player world.

Link to post
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.

Guest
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 Privacy Policy.