Jump to content

[1.16.4] TileEntity not syncing with client if values aren't changing


Recommended Posts

Posted

I have a TileEntity that needs to sync a craft progress with the client. Everything works perfectly except if I exit the gui while its crafting and then wait until it finishes. When I open it back up it will display the crafting progress frozen at where it was when I exited the gui. The only way to clear/fix the progress is to give it something new to craft or to log out and back in again, It doesn't fix if I take items out or put them in. I poked around in the code and it seems like IIntArray only updates if its different from the last value it remembers (in detectAndSendChanges). if there was a way I can force update it when I open the gui then the problem would be solved right?

 

I looked at vanilla furnace code but didn't find anything I was missing. I also just noticed it will do the same thing if I pump the items out with a hopper while it's crafting.

Posted
1 hour ago, TheGreyGhost said:

I checked it out but can't find much of a difference in how it functions besides mine being a bit simpler. Still have no idea what it could be though. 

 

Here is how I have my IIntArray set up.

 

In TileEntity:

...
public final IIntArray tiledata = new IIntArray() {
	@Override
	public int size() {
		return 3;
	}

	@Override
	public void set(int index, int value) {
		switch (index) {
			case 0:
				DETileEntity_TinkeringTable.this.ticksleft = value;
				break;
			case 1:
				DETileEntity_TinkeringTable.this.currentcrafttime = value;
				break;
			case 2:
				DETileEntity_TinkeringTable.this.requiresfluid = value;
		}
	}

	@Override
	public int get(int index) {
		switch (index) {
			case 0:
				return DETileEntity_TinkeringTable.this.ticksleft;
			case 1:
				return DETileEntity_TinkeringTable.this.currentcrafttime;
			case 2:
				return DETileEntity_TinkeringTable.this.requiresfluid;
		}
		return index;
	}
};
...

 

I only access the "ticksleft", "currentcrafttime", and "requiresfluid" directly within the TileEntity.

 

Container:

...
public DEContainer_TinkeringTable(int id, World world, BlockPos pos, PlayerInventory playerInventory, PlayerEntity playerin) {
	...

	if (tileentity != null) {
		...

		tiledata = ((DETileEntity_TinkeringTable) tileentity).tiledata;
	}

	...

	trackIntArray(tiledata);
}

@OnlyIn(Dist.CLIENT)
public float getCraftProgress() {
	return 1 - (float) tiledata.get(0) / tiledata.get(1);
}

@OnlyIn(Dist.CLIENT)
public boolean getRequiresFluid() {
	return tiledata.get(2) == 1;
}
...

 

And that's all I have referencing my IIntArray, which should be all I need right?

Posted
11 hours ago, diesieben07 said:

Can't see anything wrong with that. Please post a Git repository that reproduces this problem so we can use the debugger.

 

I don't want to upload all my code online yet, sorry. But I can post the main classes of just the block here and maybe you all can check if I'm missing something?

 

Block Class:

public class DEBlock_TinkeringTable extends Block implements DEMachineNetworkHandler.IDENetworkBlock{

	public DEBlock_TinkeringTable() {
		super(Properties.create(Material.ROCK).hardnessAndResistance(2.0f).sound(SoundType.METAL));
		this.setDefaultState(stateContainer.getBaseState().with(BlockStateProperties.HORIZONTAL_FACING, Direction.NORTH));
	}

	@SuppressWarnings("deprecation")
	@Override
	public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult trace) {
		if (!world.isRemote) {
			TileEntity tileEntity = world.getTileEntity(pos);
			if (tileEntity instanceof DETileEntity_TinkeringTable) {
				INamedContainerProvider containerProvider = new INamedContainerProvider() {
					@Override
					public ITextComponent getDisplayName() {
						return new TranslationTextComponent("block.demonenergistics.tinkeringtable");
					}

					@Override
					public Container createMenu(int id, PlayerInventory playerInventory, PlayerEntity playerEntity) {
						return new DEContainer_TinkeringTable(id, world, pos, playerInventory, playerEntity);
					}
				};
				NetworkHooks.openGui((ServerPlayerEntity) player, containerProvider, tileEntity.getPos());
			}
		}
		return ActionResultType.SUCCESS;
	}

	@Nullable
	@Override
	public BlockState getStateForPlacement(BlockItemUseContext context) {
		return getDefaultState().with(BlockStateProperties.HORIZONTAL_FACING, context.getPlacementHorizontalFacing().getOpposite());
	}

	@Override
	protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) {
		builder.add(BlockStateProperties.HORIZONTAL_FACING);
	}

	@Override
	public boolean hasTileEntity(BlockState state) {
		return true;
	}

	@Nullable
	@Override
	public TileEntity createTileEntity(BlockState state, IBlockReader world) {
		return new DETileEntity_TinkeringTable();
	}
}

 

TileEntity Class:

public class DETileEntity_TinkeringTable extends TileEntity implements ITickableTileEntity, DEMachineNetworkHandler.IDESapReciever {

	private final int CELL_SLOT = 9;
	private final int OUTPUT_SLOT = 10;
	private int ticksleft = 0;
	private int currentcrafttime = 0;
	private int requiresfluid = 0;

	public final IIntArray tiledata = new IIntArray() {
		@Override
		public int size() {
			return 3;
		}

		@Override
		public void set(int index, int value) {
			switch (index) {
				case 0:
					DETileEntity_TinkeringTable.this.ticksleft = value;
					break;
				case 1:
					DETileEntity_TinkeringTable.this.currentcrafttime = value;
					break;
				case 2:
					DETileEntity_TinkeringTable.this.requiresfluid = value;
			}
		}

		@Override
		public int get(int index) {
			switch (index) {
				case 0:
					return DETileEntity_TinkeringTable.this.ticksleft;
				case 1:
					return DETileEntity_TinkeringTable.this.currentcrafttime;
				case 2:
					return DETileEntity_TinkeringTable.this.requiresfluid;
			}
			return index;
		}
	};

	private ItemStackHandler itemhandler = new ItemStackHandler(11) {

		@Override
		protected void onContentsChanged(int slot) {
			markDirty();
		}

		@Override
		public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
			if (slot == CELL_SLOT && stack.getItem() instanceof DEItem_DemonCell) {
				return true;
			} else {
				return slot < CELL_SLOT;
			}
		}
	};

	private LazyOptional<IItemHandler> items = LazyOptional.of(() -> itemhandler);

	public DETileEntity_TinkeringTable() {
		super(DEBlocks.TINKERINGTABLE_TILEENTITY.get());
	}

	@Override
	public void tick() {
		if (!world.isRemote && itemhandler.getStackInSlot(OUTPUT_SLOT).isEmpty()) {
			RecipeTinkeringTable tocraft = null;
			for (final IRecipe<?> recipe : DERecipes.getRecipes(DERecipes.TINKERINGTABLE, world.getRecipeManager()).values()) {
				if (recipe instanceof RecipeTinkeringTable) {
					final RecipeTinkeringTable tinkeringtablerecipe = (RecipeTinkeringTable) recipe;
					if (tinkeringtablerecipe.isValid(itemhandler)) {
						tocraft = tinkeringtablerecipe;
						break;
					}
				}
			}

			if (tocraft != null) {
				if (tocraft.getRequiredFluid() > this.getHeldFluid()) {
					requiresfluid = 1;
					stopIfCrafting();
				} else {
					requiresfluid = 0;
					if (isCrafting()) {
						ticksleft--;
						if (ticksleft == 0) {
							doCraft(tocraft);
							currentcrafttime = 0;
						}
					} else {
						startCraft(tocraft);
					}
				}
			} else {
				requiresfluid = 0;
				stopIfCrafting();
			}
		}
	}

	private void stopIfCrafting() {
		if (isCrafting()) {
			ticksleft = 0;
			currentcrafttime = 0;
			requiresfluid = 0;
			this.markDirty();
		}
	}

	private boolean isCrafting() {
		return ticksleft > 0;
	}

	private void startCraft(RecipeTinkeringTable recipe) {
		currentcrafttime = recipe.getCraftTime();
		ticksleft = recipe.getCraftTime();
	}

	private void doCraft(RecipeTinkeringTable recipe) {
		if (recipe.getRequiredFluid() == 0) {
			itemhandler.setStackInSlot(OUTPUT_SLOT, recipe.getRecipeOutput().copy());
			for (int i = 0; i < RecipeTinkeringTable.INPUT_SIZE; i++) {
				itemhandler.extractItem(i, 1, false);
			}
		} else {
			ItemStack cell = itemhandler.getStackInSlot(CELL_SLOT);
			if (!cell.isEmpty()) {
				IFluidHandlerItem flhd = FluidUtil.getFluidHandler(cell).orElse(null);
				if (flhd != null) {
					Optional<FluidStack> containedFluid = FluidUtil.getFluidContained(cell);
					if (containedFluid.isPresent()) {
						if (containedFluid.get().getAmount() >= recipe.getRequiredFluid()) {
							flhd.drain(new FluidStack(DEFluids.DEMONSAP.get(), recipe.getRequiredFluid()), IFluidHandler.FluidAction.EXECUTE);
							for (int i = 0; i < RecipeTinkeringTable.INPUT_SIZE; i++) {
								itemhandler.extractItem(i, 1, false);
							}
							itemhandler.setStackInSlot(OUTPUT_SLOT, recipe.getRecipeOutput().copy());
							this.stopIfCrafting();
						}
					}
				}
			}
		}
	}

	private int getHeldFluid() {
		ItemStack cell = itemhandler.getStackInSlot(CELL_SLOT);
		if (!cell.isEmpty()) {
			IFluidHandlerItem flhd = FluidUtil.getFluidHandler(cell).orElse(null);
			if (flhd != null) {
				Optional<FluidStack> containedFluid = FluidUtil.getFluidContained(cell);
				if (containedFluid.isPresent()) {
					return containedFluid.get().getAmount();
				}
			}
		}
		return 0;
	}

	@Override
	public void remove() {
		super.remove();
		items.invalidate();
	}

	@Override
	public void read(BlockState state, CompoundNBT tag) {
		itemhandler.deserializeNBT(tag.getCompound("inventory"));
		ticksleft = tag.getInt("ticksleft");
		currentcrafttime = tag.getInt("currentcrafttime");
		requiresfluid = tag.getInt("requiresfluid");
		super.read(state, tag);
	}

	@Override
	public CompoundNBT write(CompoundNBT tag) {
		tag.put("inventory", itemhandler.serializeNBT());
		tag.putInt("ticksleft", ticksleft);
		tag.putInt("currentcrafttime", currentcrafttime);
		tag.putInt("requiresfluid", requiresfluid);
		return super.write(tag);
	}

	@Nonnull
	@Override
	public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
		if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
			return items.cast();
		}
		return super.getCapability(cap, side);
	}

	@Override
	public List<Integer> getCellSlots() {
		return Arrays.asList(CELL_SLOT);
	}

	@Override
	public ItemStackHandler getInventory() {
		return itemhandler;
	}

}

 

Container Class:

public class DEContainer_TinkeringTable extends Container {

	private TileEntity tileentity;
	private PlayerEntity player;
	private IItemHandler playerinv;
	private IIntArray tiledata;

	public DEContainer_TinkeringTable(int id, World world, BlockPos pos, PlayerInventory playerInventory, PlayerEntity playerin) {
		super(DEBlocks.TINKERINGTABLE_CONTAINER.get(), id);
		playerinv = new InvWrapper(playerInventory);
		tileentity = world.getTileEntity(pos);
		player = playerin;

		if (tileentity != null) {
			tileentity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).ifPresent(handler -> {
				int index = 0;
				for (int i = 0; i < 3; ++i) {
					for (int j = 0; j < 3; ++j) {
						addSlot(new SlotItemHandler(handler, index++, 30 + j * 18, 17 + i * 18));
					}
				}

				addSlot(new SlotItemHandler(handler, index++, 98, 53));
				addSlot(new SlotItemHandler(handler, index, 132, 26) {
					@Override
					public boolean isItemValid(ItemStack stack) {
						return false;
					}
				});
			});

			tiledata = ((DETileEntity_TinkeringTable) tileentity).tiledata;
		}

		for (int i = 0; i < 3; ++i) {
			for (int j = 0; j < 9; ++j) {
				addSlot(new SlotItemHandler(playerinv, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
			}
		}
		for (int k = 0; k < 9; ++k) {
			addSlot(new SlotItemHandler(playerinv, k, 8 + k * 18, 142));
		}

		trackIntArray(tiledata);
	}

	@OnlyIn(Dist.CLIENT)
	public float getCraftProgress() {
		return 1 - (float) tiledata.get(0) / tiledata.get(1);
	}

	@OnlyIn(Dist.CLIENT)
	public boolean getRequiresFluid() {
		return tiledata.get(2) == 1;
	}

	@Override
	public boolean canInteractWith(PlayerEntity playerIn) {
		return isWithinUsableDistance(IWorldPosCallable.of(tileentity.getWorld(), tileentity.getPos()), playerIn, DEBlocks.TINKERINGTABLE.get());
	}

	@Override
	public ItemStack transferStackInSlot(PlayerEntity playerIn, int slotid) {
		ItemStack newitem = ItemStack.EMPTY;
		Slot slot = this.inventorySlots.get(slotid);
		if (slot != null && slot.getHasStack()) {
			ItemStack iteminslot = slot.getStack();
			newitem = iteminslot.copy();
			if (slotid < 11) {
				if (!this.mergeItemStack(iteminslot, 11, this.inventorySlots.size(), true)) {
					return ItemStack.EMPTY;
				}
			} else if (iteminslot.getItem() instanceof DEItem_DemonCell) {
				if (!this.mergeItemStack(iteminslot, 9, 10, false)) {
					return ItemStack.EMPTY;
				}
			} else if (!this.mergeItemStack(iteminslot, 0, 11, false)) {
				return ItemStack.EMPTY;
			}

			if (iteminslot.isEmpty()) {
				slot.putStack(ItemStack.EMPTY);
			} else {
				slot.onSlotChanged();
			}
		}

		return newitem;
	}
}

 

Screen Class:

public class DEScreen_TinkeringTable extends ContainerScreen<DEContainer_TinkeringTable> {

	public static final ResourceLocation GUI = new ResourceLocation(DEMod.MODID, "textures/gui/tinkeringtable.png");

	public DEScreen_TinkeringTable(DEContainer_TinkeringTable screenContainer, PlayerInventory inv, ITextComponent titleIn) {
		super(screenContainer, inv, titleIn);
	}

	@Override
	public void render(MatrixStack matrixStack, int mouseX, int mouseY, float partialTicks) {
		this.renderBackground(matrixStack);
		super.render(matrixStack, mouseX, mouseY, partialTicks);
		this.renderHoveredTooltip(matrixStack, mouseX, mouseY);
	}

	@Override
	protected void drawGuiContainerForegroundLayer(MatrixStack matrixStack, int mouseX, int mouseY) {
		int relX = (this.width - this.xSize) / 2;
		int relY = (this.height - this.ySize) / 2;

		if (mouseX >= relX + 99 && mouseY >= relY + 10 && mouseX <= relX + 114 && mouseY <= relY + 25) {
			if (container.getRequiresFluid()) {
				renderTooltip(matrixStack, new TranslationTextComponent("container.demonenergistics.requiresfluid"), mouseX - relX, mouseY - relY);
			}
		}

	}

	@Override
	protected void drawGuiContainerBackgroundLayer(MatrixStack matrixStack, float partialTicks, int mouseX, int mouseY) {
		RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
		this.minecraft.getTextureManager().bindTexture(GUI);
		int relX = (this.width - this.xSize) / 2;
		int relY = (this.height - this.ySize) / 2;
		this.blit(matrixStack, relX, relY, 0, 0, this.xSize, this.ySize);

		this.blit(matrixStack, relX + 95, relY + 26, 176, 23, (int) (container.getCraftProgress() * 24), 16);

		if (container.getRequiresFluid()) {
			this.blit(matrixStack, relX + 96, relY + 7, 198, 0, 21, 22);
		} else if (container.getCraftProgress() > 0) {
			this.blit(matrixStack, relX + 96, relY + 7, 176, 0, 21, 22);
		}
	}
}

 

Posted

I managed to figure out what I was doing wrong thanks to a link on the page that TheGreyGhost suggested.

 

The IIntArray I was setting was getting the TileEntity for both client and server, meaning the client was using its version of the TileEntity instead of the servers one.

 

I fixed it by adding a separate Container constructor for server and client, where the server one accepts an IIntArray parameter and the client one instigates a new one. Then in the createMenu, call the servers constructor with the servers tiledata.

 

@Override
public Container createMenu(int id, PlayerInventory playerInventory, PlayerEntity playerEntity) {
	return new DEContainer_TinkeringTable(id, world, pos, playerInventory, playerEntity, ((DETileEntity_TinkeringTable) tileEntity).tiledata);
}
public DEContainer_TinkeringTable(int id, World world, BlockPos pos, PlayerInventory playerInventory, PlayerEntity playerin) {
	this(id, world, pos, playerInventory, playerin, new IntArray(3));
}

public DEContainer_TinkeringTable(int id, World world, BlockPos pos, PlayerInventory playerInventory, PlayerEntity playerin, IIntArray data) {
	super(DEBlocks.TINKERINGTABLE_CONTAINER.get(), id);
	
	...
  
	assertIntArraySize(data, 3);
	tiledata = data;
	trackIntArray(data);
}

 

Thanks for the help and sorry if I wasted anyone's time.

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.