Jump to content

[1.15.2] Furnace with different fluid fuels for different recipes


Recommended Posts

Posted

I am a beginner and i tried using all the recommendations. Any help or direction to a good example or idea to start this again differently is extremely helpful. I have already tried other methods so rewriting this project 

My project is on this branch : https://github.com/Razvan5/MinecraftFoodWorld/tree/workingpot but i also pasted the important classes here.

My tile entity is a "pot" that should be able to "boil" my recipes using the fluid determined by the custom recipe but i ran in a major problem: my screen cannot obtain the fluid from my container and i have a hard time remodeling the problem.

I use the vanilla ShapedCrafting and AbstractCookingRecipe for the Serializer and the recipes of my pot and an old moded furnace example that seemed much easier for me to modify but create more problems in the long run.

Big problems i am trying to fix: 

  • Use IItemHandler instead of IInventory for PotZoneContents
  • Modify PotStateData so i can save non-int data to the NBT
  • Displaying the fluid texture in the screen and all the progress bar with information on percentage (as i made the fluidRegistryName a static string, although useful for testing, I realized way too late why it is a bad idea)
  • The progress arrow or info stops if i implement listeners (methods like addListener() or removeListener() and UpdateProgressBar())
  • The PotBlock class has too many states for somthing identical NS and WE
  • Change the getters and setters in Abstract Pot Recipe so i only send the registry name of the fluid instead of a fluidstack object
  • Change the transferStackInSlot and validateSlot methods to actually work
  • Display the correct UV texture of the fluid not a stretched out version

 

What i want to achieve (I do not want to hardcode the fluids)

water.png.0b6ec9c2406133ae2696517e2e646ff5.pnglava.png.c116aa2cd1a907e777ffee90f8e3baa9.png      

public class PotContainer extends Container {
    // stuff for tracking string arrays instead of int?
    private final List<IntReferenceHolder> trackedIntReferences = Lists.newArrayList();
    private final List<IContainerListener> listeners = Lists.newArrayList();

    private String fluidName = "minecraft:empty";

    public static PotContainer createContainerServerSide(int windowID, PlayerInventory playerInventory,
                                                       PotZoneContents inputZoneContents,
                                                       PotZoneContents outputZoneContents,
                                                       PotZoneContents fuelZoneContents,
                                                       PotStateData potStateData) {
    return new PotContainer(windowID, playerInventory,
            inputZoneContents, outputZoneContents, fuelZoneContents, potStateData);
  }

  public static PotContainer createContainerClientSide(int windowID, PlayerInventory playerInventory, net.minecraft.network.PacketBuffer extraData) {
    //  don't need extraData for this example; if you want you can use it to provide extra information from the server, that you can use
    //  when creating the client container
    //  eg String detailedDescription = extraData.readString(128);
    PotZoneContents inputZoneContents = PotZoneContents.createForClientSideContainer(INPUT_SLOTS_COUNT);
    PotZoneContents outputZoneContents = PotZoneContents.createForClientSideContainer(OUTPUT_SLOTS_COUNT);
    PotZoneContents fuelZoneContents = PotZoneContents.createForClientSideContainer(FUEL_SLOTS_COUNT);
    PotStateData potStateData = new PotStateData();

    // on the client side there is no parent TileEntity to communicate with, so we:
    // 1) use dummy inventories and furnace state data (tracked ints)
    // 2) use "do nothing" lambda functions for canPlayerAccessInventory and markDirty
    return new PotContainer(windowID, playerInventory,
                                inputZoneContents, outputZoneContents, fuelZoneContents, potStateData);
  }

    // must assign a slot index to each of the slots used by the GUI.
	// For this container, we can see the furnace fuel, input, and output slots as well as the player inventory slots and the hotbar.
	// Each time we add a Slot to the container using addSlotToContainer(), it automatically increases the slotIndex, which means
	//  0 - 8 = hotbar slots (which will map to the InventoryPlayer slot numbers 0 - 8)
	//  9 - 35 = player inventory slots (which map to the InventoryPlayer slot numbers 9 - 35)
	//  36 - 39 = fuel slots (potStateData 0 - 3)
	//  40 - 44 = input slots (potStateData 4 - 8)
	//  45 - 49 = output slots (potStateData 9 - 13)

	private static final int HOTBAR_SLOT_COUNT = 9;
	private static final int PLAYER_INVENTORY_ROW_COUNT = 3;
	private static final int PLAYER_INVENTORY_COLUMN_COUNT = 9;
	private static final int PLAYER_INVENTORY_SLOT_COUNT = PLAYER_INVENTORY_COLUMN_COUNT * PLAYER_INVENTORY_ROW_COUNT;
	private static final int VANILLA_SLOT_COUNT = HOTBAR_SLOT_COUNT + PLAYER_INVENTORY_SLOT_COUNT;

	public static final int FUEL_SLOTS_COUNT = PotTileEntity.FUEL_SLOTS_COUNT;
	public static final int INPUT_SLOTS_COUNT = PotTileEntity.INPUT_SLOTS_COUNT;
	public static final int OUTPUT_SLOTS_COUNT = PotTileEntity.OUTPUT_SLOTS_COUNT;
	public static final int FURNACE_SLOTS_COUNT = FUEL_SLOTS_COUNT + INPUT_SLOTS_COUNT + OUTPUT_SLOTS_COUNT;

	// slot index is the unique index for all slots in this container i.e. 0 - 35 for invPlayer then 36 - 49 for furnaceContents
	private static final int VANILLA_FIRST_SLOT_INDEX = 0;
    private static final int HOTBAR_FIRST_SLOT_INDEX = VANILLA_FIRST_SLOT_INDEX;
    private static final int PLAYER_INVENTORY_FIRST_SLOT_INDEX = HOTBAR_FIRST_SLOT_INDEX + HOTBAR_SLOT_COUNT;
	private static final int FIRST_FUEL_SLOT_INDEX = PLAYER_INVENTORY_FIRST_SLOT_INDEX + PLAYER_INVENTORY_SLOT_COUNT;
	private static final int FIRST_INPUT_SLOT_INDEX = FIRST_FUEL_SLOT_INDEX + FUEL_SLOTS_COUNT;
	private static final int FIRST_OUTPUT_SLOT_INDEX = FIRST_INPUT_SLOT_INDEX + INPUT_SLOTS_COUNT;

	// slot number is the slot number within each component;
  // i.e. invPlayer slots 0 - 35 (hotbar 0 - 8 then main inventory 9 to 35)
  // and furnace: inputZone slots 0 - 4, outputZone slots 0 - 4, fuelZone 0 - 3

	public PotContainer(int windowID, PlayerInventory invPlayer,
                        PotZoneContents inputZoneContents,
                        PotZoneContents outputZoneContents,
                        PotZoneContents fuelZoneContents,
                        PotStateData potStateData) {
    super(StartupCommon.POT_CONTAINER, windowID);
    if (StartupCommon.POT_CONTAINER == null)
      throw new IllegalStateException("Must initialise containerTypeContainerFurnace before constructing a PotContainer!");
    this.inputZoneContents = inputZoneContents;
    this.outputZoneContents = outputZoneContents;
    this.fuelZoneContents = fuelZoneContents;
    this.potStateData = potStateData;
    this.world = invPlayer.player.world;
    //test
    this.fluid =Registry.FLUID.getValue(new ResourceLocation(fluidName)).orElseThrow(() -> {
        return new JsonSyntaxException("Unknown item '" + fluidName + "'");
    });

    this.detectAndSendChanges();
    trackIntArray(potStateData);// tell vanilla to keep the potStateData synchronised between client and server Containers

		final int SLOT_X_SPACING = 18;
		final int SLOT_Y_SPACING = 18;
		final int HOTBAR_XPOS = 8;
		final int HOTBAR_YPOS = 142;
		// Add the players hotbar to the gui - the [xpos, ypos] location of each item
		for (int x = 0; x < HOTBAR_SLOT_COUNT; x++) {
			int slotNumber = x;
			addSlot(new Slot(invPlayer, slotNumber, HOTBAR_XPOS + SLOT_X_SPACING * x, HOTBAR_YPOS));
		}

		final int PLAYER_INVENTORY_XPOS = 8;
		final int PLAYER_INVENTORY_YPOS = 84;
		// Add the rest of the players inventory to the gui
		for (int y = 0; y < PLAYER_INVENTORY_ROW_COUNT; y++) {
			for (int x = 0; x < PLAYER_INVENTORY_COLUMN_COUNT; x++) {
				int slotNumber = HOTBAR_SLOT_COUNT + y * PLAYER_INVENTORY_COLUMN_COUNT + x;
				int xpos = PLAYER_INVENTORY_XPOS + x * SLOT_X_SPACING;
				int ypos = PLAYER_INVENTORY_YPOS + y * SLOT_Y_SPACING;
				addSlot(new Slot(invPlayer, slotNumber,  xpos, ypos));
			}
		}

		final int FUEL_SLOTS_XPOS = 8;
		final int FUEL_SLOTS_YPOS = 53;
		// Add the tile fuel slots
		for (int x = 0; x < FUEL_SLOTS_COUNT; x++) {
			int slotNumber = x;
			addSlot(new SlotFuel(fuelZoneContents, slotNumber, FUEL_SLOTS_XPOS, FUEL_SLOTS_YPOS));
		}

		final int INPUT_SLOTS_XPOS = 44;
		final int INPUT_SLOTS_YPOS = 17;
		// Add the tile input slots
		for (int y = 0; y < INPUT_SLOTS_COUNT/3; y++) {
		    for(int x =0; x< INPUT_SLOTS_COUNT/3; x++){
                int slotNumber = (y*3)+x;
                addSlot(new SlotSmeltableInput(inputZoneContents, slotNumber, INPUT_SLOTS_XPOS+(SLOT_X_SPACING * x), INPUT_SLOTS_YPOS+ (SLOT_Y_SPACING * y)));
            }
		}

		final int OUTPUT_SLOTS_XPOS = 138;
		final int OUTPUT_SLOTS_YPOS = 35;
		// Add the tile output slots
		for (int y = 0; y < OUTPUT_SLOTS_COUNT; y++) {
			int slotNumber = y;
			addSlot(new SlotOutput(outputZoneContents, slotNumber, OUTPUT_SLOTS_XPOS, OUTPUT_SLOTS_YPOS + SLOT_Y_SPACING * y));
		}
	}

    //New stuff to make tracking code for fluid

    //

	// Checks each tick to make sure the player is still able to access the inventory and if not closes the gui
	@Override
	public boolean canInteractWith(PlayerEntity player)
	{
		return fuelZoneContents.isUsableByPlayer(player) && inputZoneContents.isUsableByPlayer(player)
            && outputZoneContents.isUsableByPlayer(player);
	}

	// This is where you specify what happens when a player shift clicks a slot in the gui
	//  (when you shift click a slot in the TileEntity Inventory, it moves it to the first available position in the hotbar and/or
	//    player inventory.  When you you shift-click a hotbar or player inventory item, it moves it to the first available
	//    position in the TileEntity inventory - either input or fuel as appropriate for the item you clicked)
	// At the very least you must override this and return ItemStack.EMPTY or the game will crash when the player shift clicks a slot.
	// returns ItemStack.EMPTY if the source slot is empty, or if none of the source slot item could be moved.
	//   otherwise, returns a copy of the source stack
  //  Code copied & refactored from vanilla furnace AbstractFurnaceContainer
	@Override
	public ItemStack transferStackInSlot(PlayerEntity player, int sourceSlotIndex)
	{
		Slot sourceSlot = inventorySlots.get(sourceSlotIndex);
		if (sourceSlot == null || !sourceSlot.getHasStack()) return ItemStack.EMPTY;
    ItemStack sourceItemStack = sourceSlot.getStack();
    ItemStack sourceStackBeforeMerge = sourceItemStack.copy();
    boolean successfulTransfer = false;

    SlotZone sourceZone = SlotZone.getZoneFromIndex(sourceSlotIndex);

    switch (sourceZone) {
      case OUTPUT_ZONE: // taking out of the output zone - try the hotbar first, then main inventory.  fill from the end.
        successfulTransfer = mergeInto(SlotZone.PLAYER_HOTBAR, sourceItemStack, true);
        if (!successfulTransfer) {
          successfulTransfer = mergeInto(SlotZone.PLAYER_MAIN_INVENTORY, sourceItemStack, true);
        }
        if (successfulTransfer) {  // removing from output means we have just crafted an item -> need to inform
          sourceSlot.onSlotChange(sourceItemStack, sourceStackBeforeMerge);
        }
        break;

      case INPUT_ZONE:
      case FUEL_ZONE: // taking out of input zone or fuel zone - try player main inv first, then hotbar.  fill from the start
        successfulTransfer = mergeInto(SlotZone.PLAYER_MAIN_INVENTORY, sourceItemStack, false);
        if (!successfulTransfer) {
          successfulTransfer = mergeInto(SlotZone.PLAYER_HOTBAR, sourceItemStack, false);
        }
        break;

      case PLAYER_HOTBAR:
      case PLAYER_MAIN_INVENTORY: // taking out of inventory - find the appropriate furnace zone
        if (!PotTileEntity.getSmeltingResultForItem(world, this.inputZoneContents).isEmpty()) { // smeltable -> add to input
          successfulTransfer = mergeInto(SlotZone.INPUT_ZONE, sourceItemStack, false);
        }
        if (!successfulTransfer && PotTileEntity.getItemBurnTime(world, inputZoneContents) > 0) { //burnable -> add to fuel from the bottom slot first
          successfulTransfer = mergeInto(SlotZone.FUEL_ZONE, sourceItemStack, true);
        }
        if (!successfulTransfer) {  // didn't fit into furnace; try player main inventory or hotbar
          if (sourceZone == SlotZone.PLAYER_HOTBAR) { // main inventory
            successfulTransfer = mergeInto(SlotZone.PLAYER_MAIN_INVENTORY, sourceItemStack, false);
          } else {
            successfulTransfer = mergeInto(SlotZone.PLAYER_HOTBAR, sourceItemStack, false);
          }
        }
        break;

      default:
        throw new IllegalArgumentException("unexpected sourceZone:" + sourceZone);
    }
    if (!successfulTransfer) return ItemStack.EMPTY;

		// If source stack is empty (the entire stack was moved) set slot contents to empty
		if (sourceItemStack.isEmpty()) {
			sourceSlot.putStack(ItemStack.EMPTY);
		} else {
			sourceSlot.onSlotChanged();
		}

		// if source stack is still the same as before the merge, the transfer failed somehow?  not expected.
    if (sourceItemStack.getCount() == sourceStackBeforeMerge.getCount()) {
      return ItemStack.EMPTY;
    }
		sourceSlot.onTake(player, sourceItemStack);
    return sourceStackBeforeMerge;
	}

  /**
   * Try to merge from the given source ItemStack into the given SlotZone.
   * @param destinationZone the zone to merge into
   * @param sourceItemStack the itemstack to merge from
   * @param fillFromEnd if true: try to merge from the end of the zone instead of from the start
   * @return true if a successful transfer occurred
   */
	private boolean mergeInto(SlotZone destinationZone, ItemStack sourceItemStack, boolean fillFromEnd) {
	  return mergeItemStack(sourceItemStack, destinationZone.firstIndex, destinationZone.lastIndexPlus1, fillFromEnd);
  }

  // -------- methods used by the ContainerScreen to render parts of the display

  /**
   * Returns the amount of fuel remaining on the currently burning item in the given fuel slot.
   * @fuelSlot the number of the fuel slot (0..3)
   * @return fraction remaining, between 0.0 - 1.0
   */
  public double fractionOfFuelRemaining(int fuelSlot) {
    if (potStateData.burnTimeInitialValues <= 0 ) return 0;
    double fraction = potStateData.burnTimeRemainings / (double) potStateData.burnTimeInitialValues;
    return MathHelper.clamp(fraction, 0.0, 1.0);
  }

  /*test*/
    public Fluid getFluid(){

        fluid = Registry.FLUID.getValue(new ResourceLocation(fluidName)).orElseThrow(() -> {
            return new JsonSyntaxException("Unknown item '" +  fluidName + "'");
        });
        LOGGER.info("TEST1 " + potStateData.fluidRegistryName);
        LOGGER.info("TEST2 " + fluid.getRegistryName().toString());
        LOGGER.info("TEST3 " + fluidName);
        return fluid;
    }
    public ResourceLocation getFluidTexture(){
        fluid = Registry.FLUID.getValue(new ResourceLocation(fluidName)).orElseThrow(() -> {
            return new JsonSyntaxException("Unknown item '" +  fluidName + "'");
        });
        return fluid.getAttributes().getStillTexture();
    }

    //detect and send changes

//    @Nonnull
//    @Override
//    protected IntReferenceHolder trackInt(IntReferenceHolder intIn) {
//        this.trackedIntReferences.add(intIn);
//        return intIn;
//    }
//
//    @Override
//    protected void trackIntArray(IIntArray arrayIn) {
//        for(int i = 0; i < arrayIn.size(); ++i) {
//            this.trackInt(IntReferenceHolder.create(arrayIn, i));
//        }
//
//    }

    @Override
    public void detectAndSendChanges() {
        fluidName = potStateData.fluidRegistryName;
        super.detectAndSendChanges();
        LOGGER.info("PLS FROM CONTAINER:" + potStateData.fluidRegistryName);
    }

//    @Override
//    public void updateProgressBar(int id, int data) {
//        this.trackedIntReferences.get(id).set(data);
//    }


//    @Override
//    public void addListener(IContainerListener listener) {
//        if (!this.listeners.contains(listener)) {
//            this.listeners.add(listener);
//            listener.sendAllContents(this, this.getInventory());
//            this.detectAndSendChanges();
//        }
//    }
//
//    @OnlyIn(Dist.CLIENT)
//    public void removeListener(IContainerListener listener) {
//        this.listeners.remove(listener);
//    }

  /**
   * return the remaining burn time of the fuel in the given slot
   * @param fuelSlot the number of the fuel slot (0..3)
   * @return seconds remaining
   */
  public int secondsOfFuelRemaining(int fuelSlot)	{
    if (potStateData.burnTimeRemainings <= 0 ) return 0;
    return potStateData.burnTimeRemainings / 20; // 20 ticks per second
  }

  /**
   * Returns the amount of cook time completed on the currently cooking item.
   * @return fraction remaining, between 0 - 1
   */
  public double fractionOfCookTimeComplete() {
    if (potStateData.cookTimeForCompletion == 0) return 0;
    double fraction = potStateData.cookTimeElapsed / (double) potStateData.cookTimeForCompletion;
    return MathHelper.clamp(fraction, 0.0, 1.0);
  }

  // --------- Customise the different slots (in particular - what items they will accept)


	// SlotFuel is a slot for fuel items
	public class SlotFuel extends Slot {
		public SlotFuel(IInventory inventoryIn, int index, int xPosition, int yPosition) {
			super(inventoryIn, index, xPosition, yPosition);
		}

		// if this function returns false, the player won't be able to insert the given item into this slot
		@Override
		public boolean isItemValid(ItemStack stack) {
			return PotTileEntity.isItemValidForFuelSlot(stack);
		}
	}

	// SlotSmeltableInput is a slot for input item
	public class SlotSmeltableInput extends Slot {
		public SlotSmeltableInput(IInventory inventoryIn, int index, int xPosition, int yPosition) {
			super(inventoryIn, index, xPosition, yPosition);
		}

		// if this function returns false, the player won't be able to insert the given item into this slot
		@Override
		public boolean isItemValid(ItemStack stack) {
			return PotTileEntity.isItemValidForInputSlot(stack);
		}
	}

	// SlotOutput is a slot that will not accept any item
	public class SlotOutput extends Slot {
		public SlotOutput(IInventory inventoryIn, int index, int xPosition, int yPosition) {
			super(inventoryIn, index, xPosition, yPosition);
		}

		// if this function returns false, the player won't be able to insert the given item into this slot
		@Override
		public boolean isItemValid(ItemStack stack) {
			return PotTileEntity.isItemValidForOutputSlot(stack);
		}
	}

  private PotZoneContents inputZoneContents;
  private PotZoneContents outputZoneContents;
  private PotZoneContents fuelZoneContents;
  private PotStateData potStateData;
  private Fluid fluid = Registry.FLUID.getValue(new ResourceLocation("empty")).orElseThrow(() -> {
      return new JsonSyntaxException("Unknown item '" + "empty" + "'");
  });

  private World world; //needed for some helper methods
  private static final Logger LOGGER = LogManager.getLogger();

  /**
   * Helper enum to make the code more readable
   */
  private enum SlotZone {
    FUEL_ZONE(FIRST_FUEL_SLOT_INDEX, FUEL_SLOTS_COUNT),
    INPUT_ZONE(FIRST_INPUT_SLOT_INDEX, INPUT_SLOTS_COUNT),
    OUTPUT_ZONE(FIRST_OUTPUT_SLOT_INDEX, OUTPUT_SLOTS_COUNT),
    PLAYER_MAIN_INVENTORY(PLAYER_INVENTORY_FIRST_SLOT_INDEX, PLAYER_INVENTORY_SLOT_COUNT),
    PLAYER_HOTBAR(HOTBAR_FIRST_SLOT_INDEX, HOTBAR_SLOT_COUNT);

    SlotZone(int firstIndex, int numberOfSlots) {
      this.firstIndex = firstIndex;
      this.slotCount = numberOfSlots;
      this.lastIndexPlus1 = firstIndex + numberOfSlots;
    }

    public final int firstIndex;
    public final int slotCount;
    public final int lastIndexPlus1;

    public static SlotZone getZoneFromIndex(int slotIndex) {
      for (SlotZone slotZone : SlotZone.values()) {
        if (slotIndex >= slotZone.firstIndex && slotIndex < slotZone.lastIndexPlus1) return slotZone;
      }
      throw new IndexOutOfBoundsException("Unexpected slotIndex");
    }
  }
}
public class PotTileEntity extends TileEntity implements INamedContainerProvider, ITickableTileEntity {
	private static final Logger LOGGER = LogManager.getLogger();


	public static final int FUEL_SLOTS_COUNT = 1;
	public static final int INPUT_SLOTS_COUNT = 9;
	public static final int OUTPUT_SLOTS_COUNT = 1;
	public static final int TOTAL_SLOTS_COUNT = FUEL_SLOTS_COUNT + INPUT_SLOTS_COUNT + OUTPUT_SLOTS_COUNT;
	public  FluidStack fluid = FluidStack.EMPTY;

	private PotZoneContents fuelZoneContents;
  	private PotZoneContents inputZoneContents;
  	private PotZoneContents outputZoneContents;

	private final PotStateData potStateData = new PotStateData();

	public PotTileEntity(){
	  super(StartupCommon.POT_TILE_ENTITY);
	  fuelZoneContents = PotZoneContents.createForTileEntity(FUEL_SLOTS_COUNT,
            this::canPlayerAccessInventory, this::markDirty);
	  inputZoneContents = PotZoneContents.createForTileEntity(INPUT_SLOTS_COUNT,
            this::canPlayerAccessInventory, this::markDirty);
	  outputZoneContents = PotZoneContents.createForTileEntity(OUTPUT_SLOTS_COUNT,
            this::canPlayerAccessInventory, this::markDirty);
	}

  // Return true if the given player is able to use this block. In this case it checks that
  // 1) the world tileentity hasn't been replaced in the meantime, and
  // 2) the player isn't too far away from the centre of the block
  public boolean canPlayerAccessInventory(PlayerEntity player) {
    if (this.world.getTileEntity(this.pos) != this) return false;
    final double X_CENTRE_OFFSET = 0.5;
    final double Y_CENTRE_OFFSET = 0.5;
    final double Z_CENTRE_OFFSET = 0.5;
    final double MAXIMUM_DISTANCE_SQ = 8.0 * 8.0;
    return player.getDistanceSq(pos.getX() + X_CENTRE_OFFSET, pos.getY() + Y_CENTRE_OFFSET, pos.getZ() + Z_CENTRE_OFFSET) < MAXIMUM_DISTANCE_SQ;
  }

	/**
	 * Get the number of slots which have fuel burning in them.
	 * @return number of slots with burning fuel, 0 - FUEL_SLOTS_COUNT
	 */
	public int numberOfBurningFuelSlots()	{
		int burningCount = 0;
		int burnTime = potStateData.burnTimeRemainings;
			if (burnTime > 0) ++burningCount;

		return burningCount;
	}

	// This method is called every tick to update the tile entity, i.e.
	// - see if the fuel has run out, and if so turn the furnace "off" and slowly uncook the current item (if any)
	// - see if the current smelting input item has finished smelting; if so, convert it to output
  // - burn fuel slots
	// It runs both on the server and the client but we only need to do updates on the server side.
	@Override
	public void tick() {
	  if (world.isRemote) return; // do nothing on client.
    ItemStack currentlySmeltingItem =  getCurrentlySmeltingInputItem();

    // if user has changed the input slots, reset the smelting time
    if (!ItemStack.areItemsEqual(currentlySmeltingItem, currentlySmeltingItemLastTick)) {  // == and != don't work!
      potStateData.cookTimeElapsed = 0;
    }
    currentlySmeltingItemLastTick = currentlySmeltingItem.copy();

		if (!currentlySmeltingItem.isEmpty()) {
			int numberOfFuelBurning = burnFuel();

			// If fuel is available, keep cooking the item, otherwise start "uncooking" it at double speed
			if (numberOfFuelBurning > 0) {
        potStateData.cookTimeElapsed += numberOfFuelBurning;
			}	else {
        potStateData.cookTimeElapsed -= 2;
			}
			if (potStateData.cookTimeElapsed < 0) potStateData.cookTimeElapsed = 0;

			int cookTimeForCurrentItem = getCookTime(this.world, inputZoneContents);
			potStateData.cookTimeForCompletion = cookTimeForCurrentItem;
			// If cookTime has reached maxCookTime smelt the item and reset cookTime
			if (potStateData.cookTimeElapsed >= cookTimeForCurrentItem) {
				smeltFirstSuitableInputItem();
        potStateData.cookTimeElapsed = 0;
			}
		}	else {
      potStateData.cookTimeElapsed = 0;
		}

		// when the number of burning slots changes, we need to force the block to re-render, otherwise the change in
		//   state will not be visible.  Likewise, we need to force a lighting recalculation.
		// The block update (for renderer) is only required on client side, but the lighting is required on both, since
		//    the client needs it for rendering and the server needs it for crop growth etc
			int numberBurning = numberOfBurningFuelSlots();
			BlockState currentBlockState = world.getBlockState(this.pos);
			BlockState newBlockState = currentBlockState.with(PotInventoryBlock.BURNING_SIDES_COUNT, numberBurning);
			if (!newBlockState.equals(currentBlockState)) {
			final int FLAGS = SetBlockStateFlag.get(SetBlockStateFlag.BLOCK_UPDATE, SetBlockStateFlag.SEND_TO_CLIENTS);
      		world.setBlockState(this.pos, newBlockState, FLAGS);
      		markDirty();
		}
  }

	/**
	 * 	for each fuel slot: decreases the burn time, checks if burnTimeRemainings = 0 and tries to consume a new piece of fuel if one is available
	 * @return the number of fuel slots which are burning
	 */
	private int burnFuel() {
		int burningCount = 0;
		boolean inventoryChanged = false;

		for (int fuelIndex = 0; fuelIndex < FUEL_SLOTS_COUNT; fuelIndex++) {
			if (potStateData.burnTimeRemainings> 0) {
				--potStateData.burnTimeRemainings;
				++burningCount;
			}

			if (potStateData.burnTimeRemainings == 0) {
			  ItemStack fuelItemStack = fuelZoneContents.getStackInSlot(fuelIndex);
				Optional<PotRecipe> matchingRecipe = getMatchingRecipeForInput(world, inputZoneContents);
				//!!!Be careful used for the texture
				fluid = matchingRecipe.get().getFluidStack();
				if (!fuelItemStack.isEmpty() && getItemBurnTime(this.world, inputZoneContents) > 0 && fuelItemStack.isItemEqual(matchingRecipe.get().getFluidItem())) {
					// If the stack in this slot isn't empty and is fuel, set burnTimeRemainings & burnTimeInitialValues to the
					// item's burn time and decrease the stack size
          int burnTimeForItem = getItemBurnTime(this.world, inputZoneContents);
          potStateData.burnTimeRemainings = burnTimeForItem;
          potStateData.burnTimeInitialValues = burnTimeForItem;
          fuelZoneContents.decrStackSize(fuelIndex, 1);
					++burningCount;
					inventoryChanged = true;

				// If the stack size now equals 0 set the slot contents to the item container item. This is for fuel
				// item such as lava buckets so that the bucket is not consumed. If the item dose not have
				// a container item, getContainerItem returns ItemStack.EMPTY which sets the slot contents to empty
          if (fuelItemStack.isEmpty()) {
            ItemStack containerItem = fuelItemStack.getContainerItem();
            fuelZoneContents.setInventorySlotContents(fuelIndex, containerItem);
					}
				}
			}
		}
		if (inventoryChanged) markDirty();
		return burningCount;
	}

	/**
	 * Check if any of the input item are smeltable and there is sufficient space in the output slots
	 * @return the ItemStack of the first input item that can be smelted; ItemStack.EMPTY if none
	 */
	private ItemStack getCurrentlySmeltingInputItem() {return smeltFirstSuitableInputItem(false);}

	/**
	 * Smelt an input item into an output slot, if possible
	 */
	private void smeltFirstSuitableInputItem() {
    smeltFirstSuitableInputItem(true);
	}

	/**
	 * checks that there is an item to be smelted in one of the input slots and that there is room for the result in the output slots
	 * If desired, performs the smelt
	 * @param performSmelt if true, perform the smelt.  if false, check whether smelting is possible, but don't change the inventory
	 * @return a copy of the ItemStack of the input item smelted or to-be-smelted
	 */
	private ItemStack smeltFirstSuitableInputItem(boolean performSmelt)
	{
		Integer firstSuitableInputSlot = null;
		Integer firstSuitableOutputSlot = null;
		ItemStack result = ItemStack.EMPTY;

		// finds the first input slot which is smeltable and whose result fits into an output slot (stacking if possible)
		for (int inputIndex = 0; inputIndex < INPUT_SLOTS_COUNT; inputIndex++)	{
      ItemStack itemStackToSmelt = inputZoneContents.getStackInSlot(inputIndex);
      if (!itemStackToSmelt.isEmpty()) {
				result = getSmeltingResultForItem(this.world, inputZoneContents);
  			if (!result.isEmpty()) {
					// find the first suitable output slot- either empty, or with identical item that has enough space
					for (int outputIndex = 0; outputIndex < OUTPUT_SLOTS_COUNT; outputIndex++) {
						if (willItemStackFit(outputZoneContents, outputIndex, result)) {
							firstSuitableInputSlot = inputIndex;
							firstSuitableOutputSlot = outputIndex;
							break;
						}
					}
					if (firstSuitableInputSlot != null) break;
				}
			}
		}

		if (firstSuitableInputSlot == null) return ItemStack.EMPTY;

    ItemStack returnvalue = inputZoneContents.getStackInSlot(firstSuitableInputSlot).copy();
    if (!performSmelt) return returnvalue;

		// alter input and output
		for(int slotIndex =0;slotIndex<inputZoneContents.getSizeInventory();slotIndex++){
			if(!inputZoneContents.isStackEmpty(slotIndex)){
				inputZoneContents.decrStackSize(slotIndex, 1);
			}
		}
    	outputZoneContents.increaseStackSize(firstSuitableOutputSlot, result);

		markDirty();
		return returnvalue;
	}


  /**
   * Will the given ItemStack fully fit into the target slot?
   * @param potZoneContents
   * @param slotIndex
   * @param itemStackOrigin
   * @return true if the given ItemStack will fit completely; false otherwise
   */
	public boolean willItemStackFit(PotZoneContents potZoneContents, int slotIndex, ItemStack itemStackOrigin) {
    ItemStack itemStackDestination = potZoneContents.getStackInSlot(slotIndex);

    if (itemStackDestination.isEmpty() || itemStackOrigin.isEmpty()) {
      return true;
    }

    if (!itemStackOrigin.isItemEqual(itemStackDestination)) {
      return false;
    }

    int sizeAfterMerge = itemStackDestination.getCount() + itemStackOrigin.getCount();
    if (sizeAfterMerge <= potZoneContents.getInventoryStackLimit() && sizeAfterMerge <= itemStackDestination.getMaxStackSize()) {
      return true;
    }
    return false;
  }

	// returns the smelting result for the given stack. Returns ItemStack.EMPTY if the given stack can not be smelted
	public static ItemStack getSmeltingResultForItem(World world, PotZoneContents potZoneContents) {
	  Optional<PotRecipe> matchingRecipe = getMatchingRecipeForInput(world, potZoneContents);
	  if (!matchingRecipe.isPresent()) return ItemStack.EMPTY;
    	return matchingRecipe.get().getRecipeOutput().copy();  // beware! You must deep copy otherwise you will alter the recipe itself
	}

	// returns the number of ticks the given item will burn. Returns 0 if the given item is not a valid fuel
	public static int getItemBurnTime(World world,PotZoneContents potZoneContents)
	{
		Optional<PotRecipe> matchingRecipe = getMatchingRecipeForInput(world, potZoneContents);
		if (!matchingRecipe.isPresent()) return 0;
		return matchingRecipe.get().getFluidTime();  // beware! You must deep copy otherwise you will alter the recipe itself
	}

	// gets the recipe which matches the given input, or Missing if none.
  public static Optional<PotRecipe> getMatchingRecipeForInput(World world, PotZoneContents potZoneContents) {
    RecipeManager recipeManager = world.getRecipeManager();
    Optional<PotRecipe> matchingRecipe = recipeManager.getRecipe(StartupCommon.POT_RECIPE_TYPE, potZoneContents, world);
    return matchingRecipe;
  }

  /**
   * Gets the cooking time for this recipe input
   * @param world
   * @param potZoneContents the input itemS to be smelted
   * @return cooking time (ticks) or 0 if no matching recipe
   */
  public static int getCookTime(World world, PotZoneContents potZoneContents) {
	  Optional<PotRecipe> matchingRecipe = getMatchingRecipeForInput(world, potZoneContents);
	  if (!matchingRecipe.isPresent()) return 0;
    	return matchingRecipe.get().getCookTime();
  }

	// Return true if the given stack is allowed to be inserted in the given slot
	// Unlike the vanilla furnace, we allow anything to be placed in the fuel slots
	static public boolean isItemValidForFuelSlot(ItemStack itemStack)
	{
		return true;
	}

	// Return true if the given stack is allowed to be inserted in the given slot
	// Unlike the vanilla furnace, we allow anything to be placed in the input slots
	static public boolean isItemValidForInputSlot(ItemStack itemStack)
	{
		return true;
	}

	// Return true if the given stack is allowed to be inserted in the given slot
	static public boolean isItemValidForOutputSlot(ItemStack itemStack)
	{
		return false;
	}

	//------------------------------
  private final String FUEL_SLOTS_NBT = "fuelSlots";
  private final String INPUT_SLOTS_NBT = "inputSlots";
  private final String OUTPUT_SLOTS_NBT = "outputSlots";
  private final String FLUID_NAME_NBT = "fluidRegistryName";

  // This is where you save any data that you don't want to lose when the tile entity unloads
	// In this case, it saves the state of the furnace (burn time etc) and the itemstacks stored in the fuel, input, and output slots
	@Override
	public CompoundNBT write(CompoundNBT parentNBTTagCompound)
	{
		super.write(parentNBTTagCompound); // The super call is required to save and load the tile's location

		potStateData.putIntoNBT(parentNBTTagCompound);

		parentNBTTagCompound.put(FUEL_SLOTS_NBT, fuelZoneContents.serializeNBT());
    	parentNBTTagCompound.put(INPUT_SLOTS_NBT, inputZoneContents.serializeNBT());
    	parentNBTTagCompound.put(OUTPUT_SLOTS_NBT, outputZoneContents.serializeNBT());
    //test
		LOGGER.info(fluid.getFluid().getRegistryName().toString());
		potStateData.fluidRegistryName = fluid.getFluid().getRegistryName().toString();

		parentNBTTagCompound.putString(FLUID_NAME_NBT,fluid.getFluid().getRegistryName().toString());
    return parentNBTTagCompound;
	}

	// This is where you load the data that you saved in writeToNBT
	@Override
	public void read(CompoundNBT nbtTagCompound) {
		super.read(nbtTagCompound); // The super call is required to save and load the tile's location

    potStateData.readFromNBT(nbtTagCompound);

    CompoundNBT inventoryNBT = nbtTagCompound.getCompound(FUEL_SLOTS_NBT);
    fuelZoneContents.deserializeNBT(inventoryNBT);

    inventoryNBT = nbtTagCompound.getCompound(INPUT_SLOTS_NBT);
    inputZoneContents.deserializeNBT(inventoryNBT);

    inventoryNBT = nbtTagCompound.getCompound(OUTPUT_SLOTS_NBT);
    outputZoneContents.deserializeNBT(inventoryNBT);

    fluid = new FluidStack(Registry.FLUID.getValue(new ResourceLocation(nbtTagCompound.getString(FLUID_NAME_NBT))).orElseThrow(() -> {
			return new JsonSyntaxException("Unknown item '" +  nbtTagCompound.getString(FLUID_NAME_NBT) + "'");
    }),1000);

    potStateData.fluidRegistryName = nbtTagCompound.getString(FLUID_NAME_NBT);
    LOGGER.info("TileEntitydoi:-"+potStateData.fluidRegistryName);


    if (fuelZoneContents.getSizeInventory() != FUEL_SLOTS_COUNT
        || inputZoneContents.getSizeInventory() != INPUT_SLOTS_COUNT
        || outputZoneContents.getSizeInventory() != OUTPUT_SLOTS_COUNT
        )
      throw new IllegalArgumentException("Corrupted NBT: Number of inventory slots did not match expected.");
	}

//	// When the world loads from disk, the server needs to send the TileEntity information to the client
//	//  it uses getUpdatePacket(), getUpdateTag(), onDataPacket(), and handleUpdateTag() to do this
  @Override
  @Nullable
  public SUpdateTileEntityPacket getUpdatePacket()
  {
    CompoundNBT updateTagDescribingTileEntityState = getUpdateTag();
    final int METADATA = 42; // arbitrary.
    return new SUpdateTileEntityPacket(this.pos, METADATA, updateTagDescribingTileEntityState);
  }

  @Override
  public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) {
    CompoundNBT updateTagDescribingTileEntityState = pkt.getNbtCompound();
    handleUpdateTag(updateTagDescribingTileEntityState);
  }

  /* Creates a tag containing the TileEntity information, used by vanilla to transmit from server to client
     Warning - although our getUpdatePacket() uses this method, vanilla also calls it directly, so don't remove it.
   */
  @Override
  public CompoundNBT getUpdateTag()
  {
		CompoundNBT nbtTagCompound = new CompoundNBT();
		write(nbtTagCompound);
    return nbtTagCompound;
  }

  /* Populates this TileEntity with information from the tag, used by vanilla to transmit from server to client
   *  The vanilla default is suitable for this example but I've included an explicit definition anyway.
   */
  @Override
  public void handleUpdateTag(CompoundNBT tag) { read(tag); }

  /**
   * When this tile entity is destroyed, drop all of its contents into the world
   * @param world
   * @param blockPos
   */
  public void dropAllContents(World world, BlockPos blockPos) {
    InventoryHelper.dropInventoryItems(world, blockPos, fuelZoneContents);
    InventoryHelper.dropInventoryItems(world, blockPos, inputZoneContents);
    InventoryHelper.dropInventoryItems(world, blockPos, outputZoneContents);
  }

  // -------------  The following two methods are used to make the TileEntity perform as a NamedContainerProvider, i.e.
  //  1) Provide a name used when displaying the container, and
  //  2) Creating an instance of container on the server, and linking it to the inventory items stored within the TileEntity

  /**
   *  standard code to look up what the human-readable name is.
   *  Can be useful when the tileentity has a customised name (eg "David's footlocker")
   */
  @Override
  public ITextComponent getDisplayName() {
    return new TranslationTextComponent("container.foodworld.pot");
  }

  /**
   * The name is misleading; createMenu has nothing to do with creating a Screen, it is used to create the Container on the server only
   * @param windowID
   * @param playerInventory
   * @param playerEntity
   * @return
   */
  @Nullable
  @Override
  public Container createMenu(int windowID, PlayerInventory playerInventory, PlayerEntity playerEntity) {
    return PotContainer.createContainerServerSide(windowID, playerInventory,
                                  inputZoneContents, outputZoneContents, fuelZoneContents, potStateData);
  }

  private ItemStack currentlySmeltingItemLastTick = ItemStack.EMPTY;


}
public class PotStateData implements IIntArray {

  private static final Logger LOGGER = LogManager.getLogger();

  public static final int FUEL_SLOTS_COUNT = PotTileEntity.FUEL_SLOTS_COUNT;

  /**The number of ticks that the current item has been cooking*/
  public int cookTimeElapsed;
  // The number of ticks required to cook the current item (i.e complete when cookTimeElapsed == cookTimeForCompletion
  public int cookTimeForCompletion;

  /** The initial fuel value of the currently burning fuel in each slot (in ticks of burn duration) */
  public int burnTimeInitialValues;
  /** The number of burn ticks remaining on the current piece of fuel in each slot */
  public int burnTimeRemainings;

  public String fluidRegistryName = "empty";

  // --------- read/write to NBT for permanent storage (on disk, or packet transmission) - used by the TileEntity only

  public void putIntoNBT(CompoundNBT nbtTagCompound) {
    nbtTagCompound.putInt("CookTimeElapsed", cookTimeElapsed);
    nbtTagCompound.putInt("CookTimeForCompletion", cookTimeElapsed);
    nbtTagCompound.putInt("burnTimeRemainings", burnTimeRemainings);
    nbtTagCompound.putInt("burnTimeInitial", burnTimeInitialValues);
    nbtTagCompound.putString("fluidRegistryName", fluidRegistryName);

  }

  public void readFromNBT(CompoundNBT nbtTagCompound) {
      // Trim the arrays (or pad with 0) to make sure they have the correct number of elements
    cookTimeElapsed = nbtTagCompound.getInt("CookTimeElapsed");
    cookTimeForCompletion = nbtTagCompound.getInt("CookTimeForCompletion");
    burnTimeRemainings = nbtTagCompound.getInt("burnTimeRemainings");
    burnTimeInitialValues = nbtTagCompound.getInt("burnTimeInitialValues");
    fluidRegistryName = nbtTagCompound.getString("fluidRegistryName");
//    LOGGER.info("PotState:-"+nbtTagCompound.getString("fluidRegistryName"));

  }

  // -------- used by vanilla, not intended for mod code
//  * The ints are mapped (internally) as:
//  * 0 = cookTimeElapsed
//  * 1 = cookTimeForCompletion
//  * 2 .. FUEL_SLOTS_COUNT+1 = burnTimeInitialValues[]
//  * FUEL_SLOTS_COUNT + 2 .. 2*FUEL_SLOTS_COUNT +1 = burnTimeRemainings[]
//  *

  private final int COOKTIME_INDEX = 0;
  private final int COOKTIME_FOR_COMPLETION_INDEX = 1;
  private final int BURNTIME_INITIAL_VALUE_INDEX = 2;
  private final int BURNTIME_REMAINING_INDEX = BURNTIME_INITIAL_VALUE_INDEX + FUEL_SLOTS_COUNT;
  private final int FLUID_NAME_INDEX = BURNTIME_REMAINING_INDEX + 1 ;
  private final int END_OF_DATA_INDEX_PLUS_ONE = BURNTIME_REMAINING_INDEX + FUEL_SLOTS_COUNT;

  @Override
  public int get(int index) {
    validateIndex(index);
    LOGGER.info(fluidRegistryName);

    if (index == COOKTIME_INDEX) {
      return cookTimeElapsed;
    } else if (index == COOKTIME_FOR_COMPLETION_INDEX) {
      return cookTimeForCompletion;
    } else if (index >= BURNTIME_INITIAL_VALUE_INDEX && index < BURNTIME_REMAINING_INDEX) {
      return burnTimeInitialValues;
    } else {
      return burnTimeRemainings;
    }
//    else{
//      return int(fluidRegistryName);
//    }
  }

  @Override
  public void set(int index, int value) {
    validateIndex(index);
    if (index == COOKTIME_INDEX) {
      cookTimeElapsed = value;
    } else if (index == COOKTIME_FOR_COMPLETION_INDEX) {
      cookTimeForCompletion = value;
    } else if (index >= BURNTIME_INITIAL_VALUE_INDEX && index < BURNTIME_REMAINING_INDEX) {
      burnTimeInitialValues = value;
    } else if (index >= BURNTIME_REMAINING_INDEX && index<FLUID_NAME_INDEX){
      burnTimeRemainings = value;
    }
    else{
      fluidRegistryName = String.valueOf(value);
    }
  }

  @Override
  public int size() {
    return END_OF_DATA_INDEX_PLUS_ONE;
  }

  private void validateIndex(int index) throws IndexOutOfBoundsException {
    if (index < 0 || index >= size()) {
      throw new IndexOutOfBoundsException("Index out of bounds:"+index);
    }
  }
}
public class PotZoneContents implements IInventory {

  /**
   * Use this constructor to create a PotZoneContents which is linked to its parent TileEntity.
   * On the server, this link will be used by the Container to request information and provide notifications to the parent
   * On the client, the link will be unused.
   * There are additional notificationLambdas available; these two are explicitly specified because your TileEntity will
   *   nearly always need to implement at least these two
   * @param size  the max number of ItemStacks in the inventory
   * @param canPlayerAccessInventoryLambda the function that the container should call in order to decide if the given player
   *                                       can access the container's contents not.  Usually, this is a check to see
   *                                       if the player is closer than 8 blocks away.
   * @param markDirtyNotificationLambda  the function that the container should call in order to tell the parent TileEntity
   *                                     that the contents of its inventory have been changed and need to be saved.  Usually,
   *                                     this is TileEntity::markDirty
   * @return the new ChestContents.
   */
  public static PotZoneContents createForTileEntity(int size,
                                                    Predicate<PlayerEntity> canPlayerAccessInventoryLambda,
                                                    Notify markDirtyNotificationLambda) {
     return new PotZoneContents(size,canPlayerAccessInventoryLambda, markDirtyNotificationLambda);
  }

  /**
   * Use this constructor to create a PotZoneContents which is not linked to any parent TileEntity; i.e.
   *   is used by the client side container:
   * * does not permanently store items
   * * cannot ask questions/provide notifications to a parent TileEntity
   * @param size  the max number of ItemStacks in the inventory
   * @return the new ChestContents
   */
  public static PotZoneContents createForClientSideContainer(int size) {
    return new PotZoneContents(size);
  }

  // ----Methods used to load / save the contents to NBT

  /**
   * Writes the chest contents to a CompoundNBT tag (used to save the contents to disk)
   * @return the tag containing the contents
   */
  public CompoundNBT serializeNBT()  {
    return potComponentContents.serializeNBT();
  }

  /**
   * Fills the chest contents from the nbt; resizes automatically to fit.  (used to load the contents from disk)
   * @param nbt
   */
  public void deserializeNBT(CompoundNBT nbt)   {
    potComponentContents.deserializeNBT(nbt);
  }

  //  ------------- linking methods  -------------
  //  The following group of methods are used to establish a link between the parent TileEntity and the chest contents,
  //    so that the container can communicate with the parent TileEntity without having to talk to it directly.
  //  This is important because the link to the TileEntity only exists on the server side.  On the client side, the
  //    container gets a dummy link instead- there is no link to the client TileEntity.  Linking to the client TileEntity
  //    is prohibited because of synchronisation clashes, i.e. vanilla would attempt to synchronise the TileEntity in two
  //    different ways at the same time: via the tileEntity server->client packets and via the container directly poking
  //    around in the inventory contents.
  //  I've used lambdas to make the decoupling more explicit.  You could instead
  //  * provide an Optional TileEntity to the ChestContents constructor (and ignore the markDirty() etc calls), or
  //  * implement IInventory directly in your TileEntity, and construct your client-side container using an Inventory
  //    instead of passing it a TileEntity.  (This is how vanilla does it)
  //

  /**
   * sets the function that the container should call in order to decide if the given player can access the container's
   *   contents not.  The lambda function is only used on the server side
   */
  public void setCanPlayerAccessInventoryLambda(Predicate<PlayerEntity> canPlayerAccessInventoryLambda) {
    this.canPlayerAccessInventoryLambda = canPlayerAccessInventoryLambda;
  }

  // the function that the container should call in order to tell the parent TileEntity that the
  // contents of its inventory have been changed.
  // default is "do nothing"
  public void setMarkDirtyNotificationLambda(Notify markDirtyNotificationLambda) {
    this.markDirtyNotificationLambda = markDirtyNotificationLambda;
  }

  // the function that the container should call in order to tell the parent TileEntity that the
  // container has been opened by a player (eg so that the chest can animate its lid being opened)
  // default is "do nothing"
  public void setOpenInventoryNotificationLambda(Notify openInventoryNotificationLambda) {
    this.openInventoryNotificationLambda = openInventoryNotificationLambda;
  }

  // the function that the container should call in order to tell the parent TileEntity that the
  // container has been closed by a player
  // default is "do nothing"
  public void setCloseInventoryNotificationLambda(Notify closeInventoryNotificationLambda) {
    this.closeInventoryNotificationLambda = closeInventoryNotificationLambda;
  }

  // ---------- These methods are used by the container to ask whether certain actions are permitted
  //  If you need special behaviour (eg a chest can only be used by a particular player) then either modify this method
  //    or ask the parent TileEntity.

  @Override
  public boolean isUsableByPlayer(PlayerEntity player) {
    return canPlayerAccessInventoryLambda.test(player);  // on the client, this does nothing. on the server, ask our parent TileEntity.
  }

  @Override
  public boolean isItemValidForSlot(int index, ItemStack stack) {
    return potComponentContents.isItemValid(index, stack);
  }

  // ----- Methods used to inform the parent tile entity that something has happened to the contents
  //  you can make direct calls to the parent if you like, I've used lambdas because I think it shows the separation
  //   of responsibilities more clearly.

  @FunctionalInterface
  public interface Notify {   // Some folks use Runnable, but I prefer not to use it for non-thread-related tasks
    void invoke();
  }

  @Override
  public void markDirty() {
    markDirtyNotificationLambda.invoke();
  }

  @Override
  public void openInventory(PlayerEntity player) {
    openInventoryNotificationLambda.invoke();
  }

  @Override
  public void closeInventory(PlayerEntity player) {
    closeInventoryNotificationLambda.invoke();
  }

  //---------These following methods are called by Vanilla container methods to manipulate the inventory contents ---

  @Override
  public int getSizeInventory() {
    return potComponentContents.getSlots();
  }

  @Override
  public boolean isEmpty() {
    for (int i = 0; i < potComponentContents.getSlots(); ++i) {
      if (!potComponentContents.getStackInSlot(i).isEmpty()) return false;
    }
    return true;
  }

  @Override
  public ItemStack getStackInSlot(int index) {
    return potComponentContents.getStackInSlot(index);
  }

  @Override
  public ItemStack decrStackSize(int index, int count) {
    if (count < 0) throw new IllegalArgumentException("count should be >= 0:" + count);
    return potComponentContents.extractItem(index, count, false);
  }

  public boolean isStackEmpty(int slotIndex){
    return potComponentContents.getStackInSlot(slotIndex).isEmpty();
  }


  @Override
  public ItemStack removeStackFromSlot(int index) {
    int maxPossibleItemStackSize = potComponentContents.getSlotLimit(index);
    return potComponentContents.extractItem(index, maxPossibleItemStackSize, false);
  }

  @Override
  public void setInventorySlotContents(int index, ItemStack stack) {
    potComponentContents.setStackInSlot(index, stack);
  }

  @Override
  public void clear() {
    for (int i = 0; i < potComponentContents.getSlots(); ++i) {
      potComponentContents.setStackInSlot(i, ItemStack.EMPTY);
    }
  }

  //--------- useful functions that aren't in IInventory but are useful anyway

  /**
   *  Tries to insert the given ItemStack into the given slot.
   * @param index the slot to insert into
   * @param itemStackToInsert the itemStack to insert.  Is not mutated by the function.
   * @return if successful insertion: ItemStack.EMPTY.  Otherwise, the leftover itemstack
   *         (eg if ItemStack has a size of 23, and only 12 will fit, then ItemStack with a size of 11 is returned
   */
  public ItemStack increaseStackSize(int index, ItemStack itemStackToInsert) {
    ItemStack leftoverItemStack = potComponentContents.insertItem(index, itemStackToInsert, false);
    return leftoverItemStack;
  }

  /**
   *  Checks if the given slot will accept all of the given itemStack
   * @param index the slot to insert into
   * @param itemStackToInsert the itemStack to insert
   * @return if successful insertion: ItemStack.EMPTY.  Otherwise, the leftover itemstack
   *         (eg if ItemStack has a size of 23, and only 12 will fit, then ItemStack with a size of 11 is returned
   */
  public boolean doesItemStackFit(int index, ItemStack itemStackToInsert) {
    ItemStack leftoverItemStack = potComponentContents.insertItem(index, itemStackToInsert, true);
    return leftoverItemStack.isEmpty();
  }

  // ---------

  private PotZoneContents(int size) {
    this.potComponentContents = new ItemStackHandler(size);
  }

  private PotZoneContents(int size, Predicate<PlayerEntity> canPlayerAccessInventoryLambda, Notify markDirtyNotificationLambda) {
    this.potComponentContents = new ItemStackHandler(size);
    this.canPlayerAccessInventoryLambda = canPlayerAccessInventoryLambda;
    this.markDirtyNotificationLambda = markDirtyNotificationLambda;
  }

  // the function that the container should call in order to decide if the
  // given player can access the container's Inventory or not.  Only valid server side
  //  default is "true".
  private Predicate<PlayerEntity> canPlayerAccessInventoryLambda = x-> true;

  // the function that the container should call in order to tell the parent TileEntity that the
  // contents of its inventory have been changed.
  // default is "do nothing"
  private Notify markDirtyNotificationLambda = ()->{};

  // the function that the container should call in order to tell the parent TileEntity that the
  // container has been opened by a player (eg so that the chest can animate its lid being opened)
  // default is "do nothing"
  private Notify openInventoryNotificationLambda = ()->{};

  // the function that the container should call in order to tell the parent TileEntity that the
  // container has been closed by a player
  // default is "do nothing"
  private Notify closeInventoryNotificationLambda = ()->{};

  private final ItemStackHandler potComponentContents;

  public int getHeight(){
    return 3;
  }

  public int getWidth(){
    return 3;
  }

}
public class PotContainerScreen extends ContainerScreen<PotContainer> {

	private PotContainer potContainer;
	public PotContainerScreen(PotContainer potContainer, PlayerInventory playerInventory, ITextComponent title) {
		super(potContainer, playerInventory, title);
    this.potContainer = potContainer;

		// Set the width and height of the gui.  Should match the size of the texture!
		xSize = 176;
		ySize = 207;
	}

	// some [x,y] coordinates of graphical elements
	final int COOK_BAR_XPOS = 103;
	final int COOK_BAR_YPOS = 35;
	final int COOK_BAR_ICON_U = 176;   // texture position of white arrow icon [u,v]
	final int COOK_BAR_ICON_V = 0;
	final int COOK_BAR_WIDTH = 24;
	final int COOK_BAR_HEIGHT = 17;

	final int FLAME_XPOS = 7;
	final int FLAME_YPOS = 15;
	final int FLAME_ICON_U = 176;   // texture position of flame icon [u,v]
	final int FLAME_ICON_V = 17;
	final int FLAME_WIDTH = 18;
	final int FLAME_HEIGHT = 37;
	final int FLAME_X_SPACING = 18;

  public void render(int mouseX, int mouseY, float partialTicks) {
    this.renderBackground();
    super.render(mouseX, mouseY, partialTicks);
    this.renderHoveredToolTip(mouseX, mouseY);
  }

  // Draw the Tool tip text if hovering over something of interest on the screen
  protected void renderHoveredToolTip(int mouseX, int mouseY) {
    if (!this.minecraft.player.inventory.getItemStack().isEmpty()) return;  // no tooltip if the player is dragging something

    List<String> hoveringText = new ArrayList<String>();

    // If the mouse is over the progress bar add the progress bar hovering text
    if (isInRect(guiLeft + COOK_BAR_XPOS, guiTop + COOK_BAR_YPOS, COOK_BAR_WIDTH, COOK_BAR_HEIGHT, mouseX, mouseY)){
      hoveringText.add("Progress:");
      int cookPercentage =(int)(potContainer.fractionOfCookTimeComplete() * 100);
      hoveringText.add(cookPercentage + "%");
    }

    // If the mouse is over one of the burn time indicators, add the burn time indicator hovering text
    for (int i = 0; i < PotContainer.FUEL_SLOTS_COUNT; ++i) {
      if (isInRect(guiLeft + FLAME_XPOS + FLAME_X_SPACING * i, guiTop + FLAME_YPOS, FLAME_WIDTH, FLAME_HEIGHT, mouseX, mouseY)) {
        hoveringText.add("Fluid: "+potContainer.getFluid().getRegistryName().toString());
        hoveringText.add("Time: " +potContainer.secondsOfFuelRemaining(i) + "s");
      }
    }

    // If hoveringText is not empty draw the hovering text.  Otherwise, use vanilla to render tooltip for the slots
    if (!hoveringText.isEmpty()){
      renderTooltip(hoveringText, mouseX, mouseY);
    } else {
      super.renderHoveredToolTip(mouseX, mouseY);
    }
  }


  @Override
	protected void drawGuiContainerBackgroundLayer(float partialTicks, int x, int y) {
    RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
    this.minecraft.getTextureManager().bindTexture(TEXTURE);

    // width and height are the size provided to the window when initialised after creation.
    // xSize, ySize are the expected size of the texture-? usually seems to be left as a default.
    // The code below is typical for vanilla containers, so I've just copied that- it appears to centre the texture within
    //  the available window
    int edgeSpacingX = (this.width - this.xSize) / 2;
    int edgeSpacingY = (this.height - this.ySize) / 2;
    this.blit(edgeSpacingX, edgeSpacingY, 0, 0, this.xSize, this.ySize);

    	// draw the cook progress bar
		double cookProgress = potContainer.fractionOfCookTimeComplete();
		blit(guiLeft + COOK_BAR_XPOS, guiTop + COOK_BAR_YPOS, COOK_BAR_ICON_U, COOK_BAR_ICON_V,
         (int)(cookProgress * COOK_BAR_WIDTH), COOK_BAR_HEIGHT);

		// draw the fuel remaining bar for each fuel slot flame
		for (int i = 0; i < PotContainer.FUEL_SLOTS_COUNT; ++i) {
//			if(PotTileEntity.getFluidTexture()!=null) this.minecraft.getTextureManager().bindTexture(PotTileEntity.getFluidTexture());
//			Minecraft.getInstance().getTextureManager().bindTexture(Fluids.WATER.getAttributes().getStillTexture())

			ResourceLocation stillLocation = potContainer.getFluidTexture();
			int color = potContainer.getFluid().getAttributes().getColor();
			float r = ((color >> 16) & 0xFF) / 255f; // red
			float g = ((color >> 8) & 0xFF) / 255f; // green
			float b = ((color >> 0) & 0xFF) / 255f; // blue
			float a = ((color >> 24) & 0xFF) / 255f; // alpha
			GlStateManager.color4f(r,g,b,a);
			TextureAtlasSprite sprite = Minecraft.getInstance().getAtlasSpriteGetter(PlayerContainer.LOCATION_BLOCKS_TEXTURE).apply(stillLocation);
			ResourceLocation spriteLocation = sprite.getName();
			ResourceLocation texture = new ResourceLocation(spriteLocation.getNamespace(), "textures/" + spriteLocation.getPath() + ".png");
			minecraft.getTextureManager().bindTexture(texture);
			double burnRemaining = potContainer.fractionOfFuelRemaining(i);
			int yOffset = (int)((1.0 - burnRemaining) * FLAME_HEIGHT);
			blit(guiLeft + FLAME_XPOS + FLAME_X_SPACING * i, guiTop + FLAME_YPOS + yOffset,
					10,0 + yOffset, FLAME_WIDTH, FLAME_HEIGHT - yOffset);
//			blit(100,100,100,100,100,sprite);
		}
	}

	@Override
	protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
		super.drawGuiContainerForegroundLayer(mouseX, mouseY);
		this.font.drawString(this.title.getFormattedText(),8.0f,6.0f,Color.darkGray.getRGB());
		this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(),8.0f,73.0f,Color.darkGray.getRGB());
	}

	// Returns true if the given x,y coordinates are within the given rectangle
	public static boolean isInRect(int x, int y, int xSize, int ySize, int mouseX, int mouseY){
		return ((mouseX >= x && mouseX <= x+xSize) && (mouseY >= y && mouseY <= y+ySize));
	}

  // This is the resource location for the background image
  private static final ResourceLocation TEXTURE = new ResourceLocation("foodworld", "textures/gui/pot.png");
}

 

Posted
5 hours ago, Electrosugar said:

my screen cannot obtain the fluid from my container and i have a hard time remodeling the problem.

So imagine it like this. Technically, with a packet to synchronize it to the client, the client doesn't know anything about a slot either. What it does is send the container, slot id, and item stack. The container holds the window id (which should be the same on both the client and server version). The slot id holds the current slot it's in (once again synchronized by the client and server each creating their own version). Finally all that's left is the item stack sent. Once it puts all three of these together, it compares the open container window id with the packet version. If equal, it gets the client slot and pushes the stack to it.

 

Your implementation of this would be similar. Any time you detect a change in the slot, you will send a packet to the client with the container window id and the current fluid stack. If you have more than one fluid, you can handle it similarly to a slot by creating a list and grabbing it from that. If you only have one, you can create a public getter and push the value to yours.

 

Once you get this working, I will review the other problems you have. It's best to solve one problem at a time.

  • Like 1
Posted
On 9/26/2020 at 4:12 AM, ChampionAsh5357 said:

you will send a packet to the client with the container window id and the current fluid stack.

I am sorry for responding so late but i tried again and again but i have no idea how to change the fluid in the container correctly. Every time i correctly access int values  that are tracked by trackIntArray(PotState (which just extends IIntArray)) but if i use the detectAndSendChanges() (where i think i have to detect the slot change?) it is changed in that function only. Any getter for the fluidName String is useless. If you know about an example of a packet sending or anything that is similar to sending any kind of non-int data from the container to the gui, it would be extremely helpful.

Posted
1 minute ago, Electrosugar said:

If you know about an example of a packet sending or anything that is similar to sending any kind of non-int data from the container to the gui, it would be extremely helpful.

I would definitely recommend reading over the forge docs on this then. This is basically what you need to do for the fluid stack to send it across the network and then update the value in the container itself. The detect and send method just sends the information to the client. The track int arrays are a result of those values being sent using that method. You are basically hooking into detect and send, getting if the fluid stack has changed, and if so sending a packet to the container. I have already outlined above what packets you should need. If you need an example, ServerPlayerEntity should have the defined versions of the IContainerListener methods. You can see how the packet is structured and model yours similarly.

  • Like 1

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

    • Im playing RlCraft, im about 80+ hours in the game and It started to randomly crash in certain areas. This is the crash log It gives me: https://mclo.gs/H1zdyjG Any Idea what is going on? and if It can be fixed? Thanks  
    • hello, when launching my modded minecraft server (1.20.1) in Fabric, my server constantly reboots with this error  if you have any questions or solutions I will get back to you as soon as possible. sorry, my English is pretty bad     [00:40:24] [main/ERROR]: A mod crashed on startup! net.fabricmc.loader.impl.FormattedException: java.lang.RuntimeException: Could not execute entrypoint stage 'preLaunch' due to errors, provided by 'spectrelib' at 'com.illusivesoulworks.spectrelib.SpectrePreLaunchFabricMod'!         at net.fabricmc.loader.impl.FormattedException.ofLocalized(FormattedException.java:63) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:162) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:68) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotServer.main(KnotServer.java:23) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.server.FabricServerLauncher.main(FabricServerLauncher.java:69) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.installer.ServerLauncher.main(ServerLauncher.java:69) ~[fabric-server-launcher.jar:1.0.1] Caused by: java.lang.RuntimeException: Could not execute entrypoint stage 'preLaunch' due to errors, provided by 'spectrelib' at 'com.illusivesoulworks.spectrelib.SpectrePreLaunchFabricMod'!         at net.fabricmc.loader.impl.FabricLoaderImpl.lambda$invokeEntrypoints$2(FabricLoaderImpl.java:403) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.util.ExceptionUtil.gatherExceptions(ExceptionUtil.java:33) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.FabricLoaderImpl.invokeEntrypoints(FabricLoaderImpl.java:401) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:160) ~[fabric-loader-0.16.5.jar:?]         ... 4 more Caused by: java.lang.RuntimeException: Could not execute entrypoint stage 'spectrelib' due to errors, provided by 'veinmining'!         at com.illusivesoulworks.spectrelib.EntrypointUtils.lambda$invokeEntrypoints$0(EntrypointUtils.java:25) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at com.illusivesoulworks.spectrelib.EntrypointUtils.gatherExceptions(EntrypointUtils.java:41) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at com.illusivesoulworks.spectrelib.EntrypointUtils.invokeEntrypoints(EntrypointUtils.java:25) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at com.illusivesoulworks.spectrelib.SpectrePreLaunchFabricMod.onPreLaunch(SpectrePreLaunchFabricMod.java:32) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at net.fabricmc.loader.impl.FabricLoaderImpl.invokeEntrypoints(FabricLoaderImpl.java:399) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:160) ~[fabric-loader-0.16.5.jar:?]         ... 4 more Caused by: java.util.ServiceConfigurationError: com.illusivesoulworks.veinmining.common.platform.services.IPlatform: com.illusivesoulworks.veinmining.platform.FabricPlatform Unable to get public no-arg constructor         at java.util.ServiceLoader.fail(ServiceLoader.java:586) ~[?:?]         at java.util.ServiceLoader.getConstructor(ServiceLoader.java:679) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1240) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1273) ~[?:?]         at java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1309) ~[?:?]         at java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1393) ~[?:?]         at java.util.ServiceLoader.findFirst(ServiceLoader.java:1811) ~[?:?]         at com.illusivesoulworks.veinmining.common.platform.Services.load(Services.java:31) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.platform.Services.<clinit>(Services.java:27) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.veinmining.enchantment.VeinMiningEnchantment.<init>(VeinMiningEnchantment.java:33) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningMod.<clinit>(VeinMiningMod.java:28) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningConfigInitializer.onInitializeConfig(VeinMiningConfigInitializer.java:9) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.spectrelib.EntrypointUtils.invokeEntrypoints(EntrypointUtils.java:23) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at com.illusivesoulworks.spectrelib.SpectrePreLaunchFabricMod.onPreLaunch(SpectrePreLaunchFabricMod.java:32) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at net.fabricmc.loader.impl.FabricLoaderImpl.invokeEntrypoints(FabricLoaderImpl.java:399) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:160) ~[fabric-loader-0.16.5.jar:?]         ... 4 more Caused by: java.lang.RuntimeException: Mixin transformation of net.minecraft.class_1297 failed         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.getPostMixinClassByteArray(KnotClassDelegate.java:427) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:323) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.Class.getDeclaredConstructors0(Native Method) ~[?:?]         at java.lang.Class.privateGetDeclaredConstructors(Class.java:3373) ~[?:?]         at java.lang.Class.getConstructor0(Class.java:3578) ~[?:?]         at java.lang.Class.getConstructor(Class.java:2271) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:666) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:663) ~[?:?]         at java.security.AccessController.doPrivileged(AccessController.java:569) ~[?:?]         at java.util.ServiceLoader.getConstructor(ServiceLoader.java:674) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1240) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1273) ~[?:?]         at java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1309) ~[?:?]         at java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1393) ~[?:?]         at java.util.ServiceLoader.findFirst(ServiceLoader.java:1811) ~[?:?]         at com.illusivesoulworks.veinmining.common.platform.Services.load(Services.java:31) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.platform.Services.<clinit>(Services.java:27) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.veinmining.enchantment.VeinMiningEnchantment.<init>(VeinMiningEnchantment.java:33) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningMod.<clinit>(VeinMiningMod.java:28) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningConfigInitializer.onInitializeConfig(VeinMiningConfigInitializer.java:9) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.spectrelib.EntrypointUtils.invokeEntrypoints(EntrypointUtils.java:23) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at com.illusivesoulworks.spectrelib.SpectrePreLaunchFabricMod.onPreLaunch(SpectrePreLaunchFabricMod.java:32) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at net.fabricmc.loader.impl.FabricLoaderImpl.invokeEntrypoints(FabricLoaderImpl.java:399) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:160) ~[fabric-loader-0.16.5.jar:?]         ... 4 more Caused by: org.spongepowered.asm.mixin.transformer.throwables.MixinTransformerError: An unexpected critical error was encountered         at org.spongepowered.asm.mixin.transformer.MixinProcessor.applyMixins(MixinProcessor.java:392) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinTransformer.transformClass(MixinTransformer.java:234) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinTransformer.transformClassBytes(MixinTransformer.java:202) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.getPostMixinClassByteArray(KnotClassDelegate.java:422) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:323) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.Class.getDeclaredConstructors0(Native Method) ~[?:?]         at java.lang.Class.privateGetDeclaredConstructors(Class.java:3373) ~[?:?]         at java.lang.Class.getConstructor0(Class.java:3578) ~[?:?]         at java.lang.Class.getConstructor(Class.java:2271) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:666) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:663) ~[?:?]         at java.security.AccessController.doPrivileged(AccessController.java:569) ~[?:?]         at java.util.ServiceLoader.getConstructor(ServiceLoader.java:674) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1240) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1273) ~[?:?]         at java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1309) ~[?:?]         at java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1393) ~[?:?]         at java.util.ServiceLoader.findFirst(ServiceLoader.java:1811) ~[?:?]         at com.illusivesoulworks.veinmining.common.platform.Services.load(Services.java:31) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.platform.Services.<clinit>(Services.java:27) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.veinmining.enchantment.VeinMiningEnchantment.<init>(VeinMiningEnchantment.java:33) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningMod.<clinit>(VeinMiningMod.java:28) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningConfigInitializer.onInitializeConfig(VeinMiningConfigInitializer.java:9) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.spectrelib.EntrypointUtils.invokeEntrypoints(EntrypointUtils.java:23) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at com.illusivesoulworks.spectrelib.SpectrePreLaunchFabricMod.onPreLaunch(SpectrePreLaunchFabricMod.java:32) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at net.fabricmc.loader.impl.FabricLoaderImpl.invokeEntrypoints(FabricLoaderImpl.java:399) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:160) ~[fabric-loader-0.16.5.jar:?]         ... 4 more Caused by: org.spongepowered.asm.mixin.transformer.throwables.MixinPreProcessorException: Attach error for mixins.sodiumdynamiclights.json:lightsource.EntityMixin from mod sodiumdynamiclights during activity: [Transform -> Method sodiumdynamiclights$scheduleTrackedChunksRebuild(Lnet/minecraft/class_761;)V -> INVOKESTATIC -> net/minecraft/class_310::method_1551:()Lnet/minecraft/class_310;]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.attach(MixinPreProcessorStandard.java:313) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.createContextFor(MixinPreProcessorStandard.java:277) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinInfo.createContextFor(MixinInfo.java:1292) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinApplicatorStandard.apply(MixinApplicatorStandard.java:203) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.TargetClassContext.apply(TargetClassContext.java:437) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.TargetClassContext.applyMixins(TargetClassContext.java:418) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinProcessor.applyMixins(MixinProcessor.java:363) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinTransformer.transformClass(MixinTransformer.java:234) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinTransformer.transformClassBytes(MixinTransformer.java:202) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.getPostMixinClassByteArray(KnotClassDelegate.java:422) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:323) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.Class.getDeclaredConstructors0(Native Method) ~[?:?]         at java.lang.Class.privateGetDeclaredConstructors(Class.java:3373) ~[?:?]         at java.lang.Class.getConstructor0(Class.java:3578) ~[?:?]         at java.lang.Class.getConstructor(Class.java:2271) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:666) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:663) ~[?:?]         at java.security.AccessController.doPrivileged(AccessController.java:569) ~[?:?]         at java.util.ServiceLoader.getConstructor(ServiceLoader.java:674) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1240) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1273) ~[?:?]         at java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1309) ~[?:?]         at java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1393) ~[?:?]         at java.util.ServiceLoader.findFirst(ServiceLoader.java:1811) ~[?:?]         at com.illusivesoulworks.veinmining.common.platform.Services.load(Services.java:31) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.platform.Services.<clinit>(Services.java:27) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.veinmining.enchantment.VeinMiningEnchantment.<init>(VeinMiningEnchantment.java:33) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningMod.<clinit>(VeinMiningMod.java:28) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningConfigInitializer.onInitializeConfig(VeinMiningConfigInitializer.java:9) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.spectrelib.EntrypointUtils.invokeEntrypoints(EntrypointUtils.java:23) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at com.illusivesoulworks.spectrelib.SpectrePreLaunchFabricMod.onPreLaunch(SpectrePreLaunchFabricMod.java:32) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at net.fabricmc.loader.impl.FabricLoaderImpl.invokeEntrypoints(FabricLoaderImpl.java:399) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:160) ~[fabric-loader-0.16.5.jar:?]         ... 4 more Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: net.minecraft.class_310         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.transformMemberReference(MixinPreProcessorStandard.java:791) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.transformMethod(MixinPreProcessorStandard.java:777) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.transform(MixinPreProcessorStandard.java:743) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.attach(MixinPreProcessorStandard.java:307) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.createContextFor(MixinPreProcessorStandard.java:277) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinInfo.createContextFor(MixinInfo.java:1292) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinApplicatorStandard.apply(MixinApplicatorStandard.java:203) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.TargetClassContext.apply(TargetClassContext.java:437) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.TargetClassContext.applyMixins(TargetClassContext.java:418) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinProcessor.applyMixins(MixinProcessor.java:363) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinTransformer.transformClass(MixinTransformer.java:234) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinTransformer.transformClassBytes(MixinTransformer.java:202) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.getPostMixinClassByteArray(KnotClassDelegate.java:422) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:323) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.Class.getDeclaredConstructors0(Native Method) ~[?:?]         at java.lang.Class.privateGetDeclaredConstructors(Class.java:3373) ~[?:?]         at java.lang.Class.getConstructor0(Class.java:3578) ~[?:?]         at java.lang.Class.getConstructor(Class.java:2271) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:666) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:663) ~[?:?]         at java.security.AccessController.doPrivileged(AccessController.java:569) ~[?:?]         at java.util.ServiceLoader.getConstructor(ServiceLoader.java:674) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1240) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1273) ~[?:?]         at java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1309) ~[?:?]         at java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1393) ~[?:?]         at java.util.ServiceLoader.findFirst(ServiceLoader.java:1811) ~[?:?]         at com.illusivesoulworks.veinmining.common.platform.Services.load(Services.java:31) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.platform.Services.<clinit>(Services.java:27) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.veinmining.enchantment.VeinMiningEnchantment.<init>(VeinMiningEnchantment.java:33) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningMod.<clinit>(VeinMiningMod.java:28) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningConfigInitializer.onInitializeConfig(VeinMiningConfigInitializer.java:9) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.spectrelib.EntrypointUtils.invokeEntrypoints(EntrypointUtils.java:23) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at com.illusivesoulworks.spectrelib.SpectrePreLaunchFabricMod.onPreLaunch(SpectrePreLaunchFabricMod.java:32) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at net.fabricmc.loader.impl.FabricLoaderImpl.invokeEntrypoints(FabricLoaderImpl.java:399) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:160) ~[fabric-loader-0.16.5.jar:?]         ... 4 more Caused by: java.lang.ClassNotFoundException: net.minecraft.class_310         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.transformMemberReference(MixinPreProcessorStandard.java:791) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.transformMethod(MixinPreProcessorStandard.java:777) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.transform(MixinPreProcessorStandard.java:743) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.attach(MixinPreProcessorStandard.java:307) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.createContextFor(MixinPreProcessorStandard.java:277) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinInfo.createContextFor(MixinInfo.java:1292) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinApplicatorStandard.apply(MixinApplicatorStandard.java:203) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.TargetClassContext.apply(TargetClassContext.java:437) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.TargetClassContext.applyMixins(TargetClassContext.java:418) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinProcessor.applyMixins(MixinProcessor.java:363) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinTransformer.transformClass(MixinTransformer.java:234) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at org.spongepowered.asm.mixin.transformer.MixinTransformer.transformClassBytes(MixinTransformer.java:202) ~[sponge-mixin-0.15.3+mixin.0.8.7.jar:0.15.3+mixin.0.8.7]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.getPostMixinClassByteArray(KnotClassDelegate.java:422) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:323) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.ClassLoader.defineClass1(Native Method) ~[?:?]         at java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[?:?]         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[?:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.defineClassFwd(KnotClassLoader.java:160) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.tryLoadClass(KnotClassDelegate.java:355) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.loadClass(KnotClassDelegate.java:218) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.KnotClassLoader.loadClass(KnotClassLoader.java:119) ~[fabric-loader-0.16.5.jar:?]         at java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[?:?]         at java.lang.Class.getDeclaredConstructors0(Native Method) ~[?:?]         at java.lang.Class.privateGetDeclaredConstructors(Class.java:3373) ~[?:?]         at java.lang.Class.getConstructor0(Class.java:3578) ~[?:?]         at java.lang.Class.getConstructor(Class.java:2271) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:666) ~[?:?]         at java.util.ServiceLoader$1.run(ServiceLoader.java:663) ~[?:?]         at java.security.AccessController.doPrivileged(AccessController.java:569) ~[?:?]         at java.util.ServiceLoader.getConstructor(ServiceLoader.java:674) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1240) ~[?:?]         at java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1273) ~[?:?]         at java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1309) ~[?:?]         at java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1393) ~[?:?]         at java.util.ServiceLoader.findFirst(ServiceLoader.java:1811) ~[?:?]         at com.illusivesoulworks.veinmining.common.platform.Services.load(Services.java:31) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.platform.Services.<clinit>(Services.java:27) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.common.veinmining.enchantment.VeinMiningEnchantment.<init>(VeinMiningEnchantment.java:33) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningMod.<clinit>(VeinMiningMod.java:28) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.veinmining.VeinMiningConfigInitializer.onInitializeConfig(VeinMiningConfigInitializer.java:9) ~[veinmining-fabric-1.3.1+1.20.1.jar:?]         at com.illusivesoulworks.spectrelib.EntrypointUtils.invokeEntrypoints(EntrypointUtils.java:23) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at com.illusivesoulworks.spectrelib.SpectrePreLaunchFabricMod.onPreLaunch(SpectrePreLaunchFabricMod.java:32) ~[spectrelib-0.13.15+1.20.1-7f355525f2546358.jar:?]         at net.fabricmc.loader.impl.FabricLoaderImpl.invokeEntrypoints(FabricLoaderImpl.java:399) ~[fabric-loader-0.16.5.jar:?]         at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:160) ~[fabric-loader-0.16.5.jar:?]         ... 4 more
    • An uneducated guess: can you move the layers by a 1000th in any direction? It works on custom models via Blockbench.
  • Topics

  • Who's Online (See full list)

    • There are no registered users currently online
×
×
  • Create New...

Important Information

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