Jump to content

Titmar

Members
  • Posts

    23
  • Joined

  • Last visited

Posts posted by Titmar

  1. Okay, I am at a loss - once again. Somehow resetting the container.inventorySlots field does not work as intended. When the MarketContainer is created it is starting out with the first inventory in the list and displays that. If that is a single chest and i switch to a double chest and back i get thrown the ioob exception. If the first inventory is a double chest and I switch to a single chest, it keeps showing the second part of the double chest but when i click one of those slots i still get the ioob exception.

    I uploaded it to my github, because I cant even get close to where the issue is...

    https://github.com/titmar/Caravans/blob/main/src/main/java/io/github/titmar/caravans/common/container/MarketContainer.java

  2. Yeah but I need to give the slot an object of IInventory (in this case the chestTE) which for some reason only sees the single inventory. I could override the slot class to use IItemHandler instead of IInventory but that seems a bit overkill. Or is there a way to get the slot to use the IItemHandler that I dont see?

  3. I am currently trying to display the contents of a double chest in the container of a different block. For single chests and barrels it works perfectly fine. The problem is, when adding Slots to the container it uses the IInventory vanilla implementation. So when looping over the chest contents with the ItemHandler capability i get thrown indexoutofbound exceptions since vanilla handles double chests quite weirdly. The capability shows me the full 54 size but vanilla thinks its only 27. Anyways, I tried getting around that by switching the IInventory of the newly generated Slot to the second part of the chest but now im getting an unlocated IndexOutOfBoundsException. I cant trace it back to where the issue is exactly. Maybe Im just overcomplicating things and there is a easier way.

    Anyway here the code from the container class:

    Spoiler
    
    public void setActiveInventory(BlockPos pos) {
    		TileEntity t = this.te.getWorld().getTileEntity(pos);
    		if (t == null || !(t instanceof LockableLootTileEntity) || pos.equals(this.activeContainer)) {
    			return;
    		}
    		BlockPos secondChest = VanillaChestHelper.isGetDoubleChest(this.te.getWorld(), pos);
    		LockableLootTileEntity tile = (LockableLootTileEntity) this.te.getWorld().getTileEntity(pos);
    		IItemHandler handler = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null);
    		if (handler != null) {
    			this.inventorySlots.clear();
    			int startX = 108;
    			int startY = 34;
    			int row = 0;
    			int col = 0;
    			if (secondChest == null) {
    				for (int i = 0; i < 27; i++) {
    					this.addSlot(new LockedSlot(tile, i, startX + (col * 18), startY + (row * 18)));
    					col++;
    					if (col == 9) {
    						col = 1;
    						row++;
    					}
    				}
    			} else {
    				for (int i = 0; i < 27; i++) {
    					this.addSlot(new LockedSlot(tile, i, startX + (col * 18), startY + (row * 18)));
    					col++;
    					if (col == 9) {
    						col = 1;
    						row++;
    					}
    				}
    				tile = (LockableLootTileEntity) this.te.getWorld().getTileEntity(secondChest);
    				handler = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null);
    				if (handler != null) {
    					for (int i = 28; i < 54; i++) {
    						this.addSlot(new LockedSlot(tile, i - 27, startX + (col * 18), startY + (row * 18)));
    						col++;
    						if (col == 9) {
    							col = 1;
    							row++;
    						}
    					}
    				}
    			}
    			this.activeContainer = pos;
    		}
    	}

     

    LockedSlot is just a extension of Slot where you cant put and take items from.

     

    And the error log:

    Spoiler
    
    [Render thread/FATAL] [minecraft/ThreadTaskExecutor]: Error executing task on Client
    java.lang.IndexOutOfBoundsException: Index: 52, Size: 27
    	at java.util.ArrayList.rangeCheck(Unknown Source) ~[?:1.8.0_281] {}
    	at java.util.ArrayList.get(Unknown Source) ~[?:1.8.0_281] {}
    	at net.minecraft.inventory.container.Container.getSlot(Container.java:165) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading}
    	at net.minecraft.inventory.container.Container.putStackInSlot(Container.java:480) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading}
    	at net.minecraft.client.network.play.ClientPlayNetHandler.handleSetSlot(ClientPlayNetHandler.java:1189) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A}
    	at net.minecraft.network.play.server.SSetSlotPacket.processPacket(SSetSlotPacket.java:29) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading}
    	at net.minecraft.network.play.server.SSetSlotPacket.processPacket(SSetSlotPacket.java:11) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading}
    	at net.minecraft.network.PacketThreadUtil.lambda$checkThreadAndEnqueue$0(PacketThreadUtil.java:19) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading}
    	at net.minecraft.util.concurrent.ThreadTaskExecutor.run(ThreadTaskExecutor.java:139) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading,pl:accesstransformer:B}
    	at net.minecraft.util.concurrent.RecursiveEventLoop.run(RecursiveEventLoop.java:22) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading}
    	at net.minecraft.util.concurrent.ThreadTaskExecutor.driveOne(ThreadTaskExecutor.java:109) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading,pl:accesstransformer:B}
    	at net.minecraft.util.concurrent.ThreadTaskExecutor.drainTasks(ThreadTaskExecutor.java:97) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading,pl:accesstransformer:B}
    	at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:973) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}
    	at net.minecraft.client.Minecraft.run(Minecraft.java:612) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}
    	at net.minecraft.client.main.Main.main(Main.java:184) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A}
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_281] {}
    	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_281] {}
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_281] {}
    	at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_281] {}
    	at net.minecraftforge.userdev.FMLUserdevClientLaunchProvider.lambda$launchService$0(FMLUserdevClientLaunchProvider.java:52) ~[forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {}
    	at cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:37) [modlauncher-8.0.6.jar:?] {}
    	at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:54) [modlauncher-8.0.6.jar:?] {}
    	at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:72) [modlauncher-8.0.6.jar:?] {}
    	at cpw.mods.modlauncher.Launcher.run(Launcher.java:82) [modlauncher-8.0.6.jar:?] {}
    	at cpw.mods.modlauncher.Launcher.main(Launcher.java:66) [modlauncher-8.0.6.jar:?] {}
    	at net.minecraftforge.userdev.LaunchTesting.main(LaunchTesting.java:105) [forge-1.16.4-35.0.2_mapped_snapshot_20201028-1.16.3-recomp.jar:?] {}

     

     

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

  5. Do you know basic java and how to capture Events?

     

    Pseudo code:

    public static void catchEvent(GuiOpenEvent even) {
    	WorkBenchContainer con = new WorkBenchContainer(id, playerInventory);
    	CraftingScreen screen = new CraftingScreen(con, playerInventory, title);
    	event.setGUI(screen);
    }

    id and playerInventory should be readable from the old screen. Check out the classes how to access those.

  6. You would need to give it an Object of CraftingScreen which in itself needs a WorkbenchContainer, the playerInventory and a title. You just pass the CraftingScreen object to the setGUI method

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

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

  9. 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?

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

  11. 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?

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

    • Ahh, it has been the markDirty part. I knew i missed some crucial part that calls the read/write.
    • As for the HashSet, this is subject to change anyway. In the future i will need to store way more data alongside the position. For now I just wanted to get a proof of concept which I can expand on.
    • I guess I was using the wrong nethod there, it should be getTileData(). The idea was to check if anything has been writen onto the NBTTag. Because if anything has been writen in there and it still didnt work the problem would have been in the read method. As we know now neither was the case.

    Thank you for your help!

  13. 11 minutes ago, diesieben07 said:

    Elaborate.

    When im accessing the NBT data from a different point (right clicking  the block and outputting into the logger) it shows me an empty NBTList while the private list of the TE has content. After reloading the world all data is lost. Since i dont know how to trigger a read/write i cant tell where the mistake is. Adding a debug message right into the read/write methods does not work either. I dont get any Logger output at all. Seems weird that both functions arent triggered at all...  Will upload to git so you have a full overview.

     

    https://github.com/titmar/Caravans/blob/main/src/main/java/io/github/titmar/caravans/common/tile/MarketTileEntity.java

  14. This is actually what i was looking for - a sucessor of the NBTTagList. Thank you sieben. Makes it look way cleaner.

    It still does not work though.

     

    	@Override
    	public CompoundNBT write(CompoundNBT compound) {
    		super.write(compound);
    		ListNBT nbt = new ListNBT();
    		for(BlockPos pos : containers) {
    			nbt.add(NBTUtil.writeBlockPos(pos));
    		}
    		compound.put("containerArray", nbt);
    		return compound;
    	}
    
    	@Override
    	public void read(BlockState state, CompoundNBT compound) {
    		super.read(state, compound);
    		ArrayList<BlockPos> list = new ArrayList<BlockPos>();
    		ListNBT nbt = (ListNBT) compound.get("containerArray");
    		for(int i = 0; i < nbt.size(); i++) {
    			list.add(NBTUtil.readBlockPos(nbt.getCompound(i)));
    		}
    		this.containers = list;
    	}

    Or would i use the compound#getList(name, type) method instead? And if so, what do i put as type?

  15. So basically the idea i had. I just tested it and I have no idea why it wont work.

     

    	@Override
    	public CompoundNBT write(CompoundNBT compound) {
    		super.write(compound);
    		compound.putInt("containerArray", containers.size());
    		int i = 0;
    		for (BlockPos bp : containers) {
    			compound.put("pos" + i, NBTUtil.writeBlockPos(bp));
    			i++;
    		}
    		return compound;
    	}
    
    	@Override
    	public void read(BlockState state, CompoundNBT nbt) {
    		super.read(state, nbt);
    		int size = nbt.getInt("containerArray");
    		for (int i = 0; i <= size; i++) {
    			BlockPos bp = NBTUtil.readBlockPos((CompoundNBT) nbt.get("pos" + i));
    			containers.add(bp);
    		}
    	}

     

  16. I am currently stuck at storing (and reading) an ArrayList of BlockPos to NBT. I found a thread where they tell you to use a NBTTagList. This does not seem to exist in 1.16 anymore. I know there is the NBTUtil methods for BlockPos which is returning me a new CompoundNBT. The idea I have is to use the NBTCompound#put(String key, INBT value) method and just add a incremental number to my key for every object in my ArrayList. But that sounds dirty to me. Is there a better way?

  17. I cant for the life of me find out why my ItemStack wont get updated properly in the Item::onItemUse method. The nbt data is working properly but it wont shrink the stack. Or rather it does shrink it in the local functions but somehow it does get reset afterwards.

     

    @Override
    	public ActionResultType onItemUse(ItemUseContext context) {
    		World world = context.getWorld();
    		if (!world.isRemote) {
    			TileEntity te = world.getTileEntity(context.getPos());
    			ItemStack stack = context.getPlayer().getHeldItem(context.getHand());
    			if (te instanceof MarketTileEntity) {
    				this.useOnMarket(stack, te);
    				context.getPlayer().sendStatusMessage(new StringTextComponent("used on market"), false);
    			}
    			if (te instanceof ChestTileEntity || te instanceof BarrelTileEntity) {
    				this.useOnContainer(stack, te);
    				context.getPlayer().sendStatusMessage(new StringTextComponent("used on container"), false);
    			}
    
    			return ActionResultType.CONSUME;
    		}
    
    		return ActionResultType.SUCCESS;
    	}
    
    	private void useOnMarket(ItemStack stack, TileEntity tile) {
    		CompoundNBT nbt = stack.getTag() != null ? stack.getTag() : new CompoundNBT();
    		BlockPos pos = tile.getPos();
    
    		// If no container data is stored, store market
    		if (!nbt.getBoolean("containerInit")) {
    			nbt.putInt("marketX", pos.getX());
    			nbt.putInt("marketY", pos.getY());
    			nbt.putInt("marketZ", pos.getZ());
    			nbt.putBoolean("marketInit", true);
    			stack.setTag(nbt);
    		} else {
    			pos = new BlockPos(nbt.getInt("containerX"), nbt.getInt("containerY"), nbt.getInt("containerZ"));
    			((MarketTileEntity) tile).registerContainer(pos);
    			nbt.putBoolean("marketInit", false);
    			nbt.putBoolean("containerInit", false);
    			stack.setTag(nbt);
    			stack.shrink(1);
    		}
    	}
    
    	private void useOnContainer(ItemStack stack, TileEntity tile) {
    		CompoundNBT nbt = stack.getTag() != null ? stack.getTag() : new CompoundNBT();
    		BlockPos pos = tile.getPos();
    
    		// If no market data is stored, store container
    		if (!nbt.getBoolean("marketInit")) {
    			nbt.putInt("containerX", pos.getX());
    			nbt.putInt("containerY", pos.getY());
    			nbt.putInt("containerZ", pos.getZ());
    			nbt.putBoolean("containerInit", true);
    			stack.setTag(nbt);
    		} else {
    			pos = new BlockPos(nbt.getInt("marketX"), nbt.getInt("marketY"), nbt.getInt("marketZ"));
    			((MarketTileEntity) tile.getWorld().getTileEntity(pos)).registerContainer(tile.getPos());
    			nbt.putBoolean("marketInit", false);
    			nbt.putBoolean("containerInit", false);
    			stack.setTag(nbt);
    			stack.shrink(1);
    		}
    	}

     

  18. Im currently creating a mod with a similar food system like TerrafirmaCraft where food decays depending on where it is stored. For the calcualtion of said decay i need to know the place where an item is stored. The playerinventory is easy, i can just check for inventoryTicks. Terrafirmacraft seems to have a whole rework going for the tick system where all storage options send ticks to items inside. That negates vanilla storage and other mods storage systems to detect where it is stored. Is there any way i can find out where an item is being stored? Same goes for when the item is dropped on the ground, can i get those coordinates somehow?

     

×
×
  • Create New...

Important Information

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