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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • I'm playing minecraft with a modpack I made myself, and for some reason, it's making my singleplayer worlds act as a multiplayer server. I randomly start lagging as if I have a bad connection to a server, and when I try to quit and save the world, I get stuck in an eternal "Saving World" screen, forcing me to close the game through Task Manager. All the articles I've seen online point to OptFine as the culprit, but I'm using embeddium instead. I even tried disabling it to see if it was the issue, but nothing changed. I'm not sure what to provide for anyone trying to help me, so here are my logs and modlist. Debug Log: https://gist.github.com/Juaum12412/a452d00019115c5be203649652c1a7ac Latest Log: https://gist.github.com/Juaum12412/8f77311dbd61c1b1c91917fa82e14130 Modlist: https://gist.github.com/Juaum12412/7f7c58e3ac14a5ba3fe3602b33912bf8 If you need anything else just tell me. Mc 1.20.1 Forge 47.3.29 8gb of ram (I can give the game up to 12 gbs)
    • I'd like to retexture the early loading screen used by Forge but I can't seem to find a way to texture them at all, at least in recent versions of Minecraft and Forge. I'm mainly looking to change the background color and Mojang logo, maybe change or remove the Forge animation at the bottom as well.
    • Hello! I'm developing a mod that will add abilities that players can use at will. I am currently trying to get hand animations in first person view, such as casting spells or charging energy, but I am running into a roadblock. I've looked for resources to help me but I haven't found any working ones yet, does anyone know how to implement this? Thank you!
    • Hey All, I have a unique issue with a modpack I'm playing: I know Forge usually combines all tags that have the same name across the mods for recipes. Unfortunately, a mod I'm playing with didn't follow this logic based on a typo. I have like 5 forms of steel ingots, but one of the mods accidentally has zombie_extreme:ignotsteel instead of something that I figure forge would catch like zombie_extreme:ingotsteel or something. I want to know how to either: - Forcibly add zombie_extreme:ignotsteel to the list of steel ingots that forge recognizes as the same recipe ingredient. If this is doable, I just need to know where in the files this list is and I can add it. - Forcibly change the name of zombie_extreme:ignotsteel to something more obvious and spelled properly so Forge can automatically combine them.   Let me know if this is possible and/or if I need to supply more information. Thank you
  • Topics

×
×
  • Create New...

Important Information

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