Posted December 6, 20204 yr 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.
December 6, 20204 yr Hi You might find this example code useful https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe31_inventory_furnace -tgg
December 6, 20204 yr Author 1 hour ago, TheGreyGhost said: Hi You might find this example code useful https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe31_inventory_furnace -tgg 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?
December 6, 20204 yr Author 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); } } }
December 6, 20204 yr Author 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.