Jump to content

[1.16.4] Display Inventory of a different container


Titmar

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 comment
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 comment
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 comment
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 comment
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 comment
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 comment
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 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.