Solved Smooth sped-up furnace cook time?

Discussion in 'Spigot Plugin Development' started by Escad, Jun 8, 2018.

Thread Status:
Not open for further replies.
  1. I've been working at trying to make sped up furnaces (a bit longer than I'd like to admit simply figuring how changing the cook time works only to realize that what I was missing was updating the furnace).
    Right now, I'm using a BukkitRunnable to set the furnace's cook time every tick.
    Code (Java):
    @EventHandler
    public void onItemSmelt(FurnaceSmeltEvent e) {
        Furnace furnace = (Furnace)e.getBlock().getState();
        new BukkitRunnable() {
            public void run() {
                if (furnace.getCookTime() < 200) {
                    furnace.setCookTime((short)(furnace.getCookTime()+10));
                    furnace.update();
                } else {
                    cancel();
                }
            }
        }.runTaskTimer(plugin, 1, 1);
    }
    This gives the intended effect, except none of the items smelt, none of the furnace's contents can be removed, and the burn meter doesn't go down any; I guess it doesn't really give the intended effect, in that case..
    My guess would be that constantly updating the furnace causes the items to "glitch" back into the slot, which just results in item dupe central. Is there a more reliable way to go about this? If not, how would I go about fixing this issue? Version is 1.12
     
    #1 Escad, Jun 8, 2018
    Last edited: Jun 9, 2018
  2. I might be oversimplifying this, but couldn't you just update the items yourself? As in, totally ignore the items already in there, do a few calculations on how long it would take a item to cook, and use a secondary BukkitRunnable to set the item level to that amount? If you stored these values in a hashmap somewhere, you could also check for an inventory interact event and subtract the item amount for your specific furnace.
     
  3. I've already attempted this (without cancelling the event - I'll try again with doing that) without luck.
    -
    Nope, no luck even with cancelling it. If updating is the problem as I expect, then it's probably the same reason that the items can't be removed from the inventory, and even why those values won't change to begin with.
     
  4. hmmmmm, interesting. I'll try see if I can find a solution but it may take me a while. I'll edit this post if I can come up with something.

    EDIT: This fuel bug really is quirky, fuel does not actually become infinite, but is slowed to an absolute crawl. This effect is especially noticeable with sticks, which have a very slow burn time so the effect is much easier to observe. I'll look into what is causing this effect.

    EDIT2: The fuel level is being multiplied by something and is extending past the "100%" animation state of the fuel flames. I think some multiple needs to be subtracted from the burn time on burn event, though it may take me longer than I expected to figure out what that is. I got close with this:
    Code (Java):

    furnace.setCookTime((short) (furnace.getCookTime() + speedUp));
    furnace.setBurnTime((short) (furnace.getBurnTime() - speedUp));
     
    EDIT3: I have come to the conclusion that the cook time itself does not change. a single unit of fuel burns for the same amount of time as before, however the animation for the fuel running out stops triggering until the short is below 100, an interesting effect. As it is, your burn time is correct, just a buggy progress bar and because smelt times are sped up, more items are cooked. I'll look into both an avenue of fixing the progress bar, and also making it so each piece of fuel still cooks the correct amount of items, just quicker.

    EDIT4: Okay, I think I have a (nearly) functioning version that cooks the correct number of items in less time (It isn't perfect, my function doesn't work for lava, ladders, sticks, or coal blocks and items with the same cook times. All other fuels produce correctly). I'm still looking into the strange fuel progress meter malfunctioning. Here's this version so far:
    Code (Java):

    private static List<BlockVector> boosted = new ArrayList<>();

    @EventHandler
    public void onFurnace(FurnaceBurnEvent event) {
        if (!(boosted.contains(event.getBlock().getLocation().toVector().toBlockVector()))) {
            boosted.add(event.getBlock().getLocation().toVector().toBlockVector());
            new BukkitRunnable() {
                private short speedUp = 20;
                @Override
                public void run() {
                    Furnace furnace = (Furnace) event.getBlock().getState();
                        furnace.setBurnTime((short) (furnace.getBurnTime() - speedUp + speedUp / 10));
                    if (furnace.getInventory().getSmelting() != null && furnace.getInventory().getSmelting().getType() != Material.AIR
                            && (furnace.getCookTime() > 0 || furnace.getBurnTime() > 0)) {
                        furnace.setCookTime((short) (furnace.getCookTime() + speedUp));
                        Bukkit.getPlayer("GreatThane").sendMessage(String.valueOf(furnace.getBurnTime()));
                        furnace.update();
                    } else {
                        boosted.remove(event.getBlock().getLocation().toVector().toBlockVector());
                        cancel();
                    }
                }
            }.runTaskTimer(this, 0, 1);
        }
    }
     
    @Rabbitual
     
    #4 GreatThane, Jun 9, 2018
    Last edited: Jun 9, 2018
    • Friendly Friendly x 1
  5. And with this, I think I have it done. After way, WAY more hours of work than I am proud to admit as to avoid insulting myself.
    But here's my finish(ish)ed product.
    Code (Java):

    private static List<BlockVector> boosted = new ArrayList<>();

    @EventHandler
    public void onFurnace(FurnaceBurnEvent event) {
        if (!(boosted.contains(event.getBlock().getLocation().toVector().toBlockVector()))) {
            boosted.add(event.getBlock().getLocation().toVector().toBlockVector());
            new BukkitRunnable() {
                private short speedUp = 20;
                @Override
                public void run() {
                    Furnace furnace = (Furnace) event.getBlock().getState();
                    if (furnace.getInventory().getSmelting() != null && furnace.getInventory().getSmelting().getType() != Material.AIR
                            && (furnace.getCookTime() > 0 || furnace.getBurnTime() > 0)) {
                        furnace.setCookTime((short) (furnace.getCookTime() + speedUp));
                        short burnTime = (short) (furnace.getBurnTime() - speedUp + speedUp / 10); // *PLAY WITH THIS*
                        furnace.setBurnTime(burnTime);
                        furnace.update();
                        if (furnace.getInventory().getViewers().size() > 0) {

                            int windowID = ((CraftPlayer) furnace.getInventory().getViewers().get(0)).getHandle().activeContainer.windowId;

                            PacketContainer max = new PacketContainer(PacketType.Play.Server.WINDOW_DATA);
                            max.getIntegers().writeDefaults();
                            max.getIntegers().write(0, windowID);
                            max.getIntegers().write(1, 1);
                            max.getIntegers().write(2, getBurnTime(event.getFuel().getType()));

                            PacketContainer current = new PacketContainer(PacketType.Play.Server.WINDOW_DATA);
                            current.getIntegers().writeDefaults();
                            current.getIntegers().write(0, windowID);
                            current.getIntegers().write(1, 0);
                            int fraction = (burnTime * 100 / getBurnTime(event.getFuel().getType()));
                            Bukkit.getPlayer("GreatThane").sendMessage(String.valueOf(fraction));
                            current.getIntegers().write(2, fraction);
                            for (HumanEntity human : furnace.getInventory().getViewers()) {
                                try {
                                    protocolManager.sendServerPacket((Player) human, max);
                                    protocolManager.sendServerPacket((Player) human, current);
                                } catch (InvocationTargetException e) {
                                    throw new RuntimeException("Cannot receive packet.", e);
                                }
                            }
                        }
                    } else {
                        boosted.remove(event.getBlock().getLocation().toVector().toBlockVector());
                        cancel();
                    }
                }
            }.runTaskTimer(this, 0, 1);
        }
    }
     
    This is basically the desired result. You can control burn speed (through the "speedUp" variable), and burn time (Through the logic I tagged with "play with this," as this still has the same problems as in my 4th edit from the post above.).
    There is another method in there called "getBurnTime," which I just hacked together in order to get a proof of concept. There is likely a much better method of getting these values. I just manually put them in from this link: https://minecraft.gamepedia.com/Furnace/table
    If you're still interested in what I have, here it is:
    Code (Java):

    private static int getBurnTime(Material fuel) {
        switch (fuel) {
            case LAVA_BUCKET:
                return 20000;
            case COAL_BLOCK:
                return 16000;
            case COAL:
                return 1600;
            case BLAZE_ROD:
                return 2400;
            case BOAT:
            case BOAT_BIRCH:
            case BOAT_ACACIA:
            case BOAT_JUNGLE:
            case BOAT_SPRUCE:
            case BOAT_DARK_OAK:
                return 400;
            case LOG:
            case LOG_2:
            case WOOD:
            case WOOD_PLATE:
            case FENCE:
            case FENCE_GATE:
            case BIRCH_FENCE:
            case ACACIA_FENCE:
            case SPRUCE_FENCE:
            case JUNGLE_FENCE:
            case DARK_OAK_FENCE:
            case BIRCH_FENCE_GATE:
            case JUNGLE_FENCE_GATE:
            case ACACIA_FENCE_GATE:
            case SPRUCE_FENCE_GATE:
            case DARK_OAK_FENCE_GATE:
            case WOOD_STAIRS:
            case BIRCH_WOOD_STAIRS:
            case DARK_OAK_STAIRS:
            case ACACIA_STAIRS:
            case SPRUCE_WOOD_STAIRS:
            case JUNGLE_WOOD_STAIRS:
            case TRAP_DOOR:
            case WORKBENCH:
            case BOOKSHELF:
            case CHEST:
            case TRAPPED_CHEST:
            case DAYLIGHT_DETECTOR:
            case DAYLIGHT_DETECTOR_INVERTED:
            case JUKEBOX:
            case NOTE_BLOCK:
            case HUGE_MUSHROOM_1:
            case HUGE_MUSHROOM_2:
            case BANNER:
            case WALL_BANNER:
            case STANDING_BANNER:
            case WOOD_DOUBLE_STEP:
            case BOW:
            case FISHING_ROD:
            case LADDER:
                return 300;
            case WOOD_SPADE:
            case WOOD_HOE:
            case WOOD_AXE:
            case WOOD_SWORD:
            case SIGN:
            case SIGN_POST:
            case WOOD_DOOR:
            case BIRCH_DOOR:
            case ACACIA_DOOR:
            case JUNGLE_DOOR:
            case SPRUCE_DOOR:
            case WOODEN_DOOR:
            case DARK_OAK_DOOR:
            case BIRCH_DOOR_ITEM:
            case ACACIA_DOOR_ITEM:
            case JUNGLE_DOOR_ITEM:
            case SPRUCE_DOOR_ITEM:
            case DARK_OAK_DOOR_ITEM:
            case WOOD_PICKAXE:
                return 200;
            case WOOD_STEP:
                return 150;
            case BOWL:
            case STICK:
            case WOOD_BUTTON:
            case SAPLING:
            case WOOL:
                return 100;
            case CARPET:
                return 67;
                default:
                    return 1600;
        }
     
    PLEASE NOTE!
    There is still one major flaw in this (other than my slightly broken burnTime logic), which is if the fuel slot is set to air (or null), I currently have it just hit the "default" there, which really only works in the last item to fuel the furnace was coal. I'd recommend storing fuel somehow as a variable you can check if the fuel slot is empty to pass through to this method rather than relying on the default.

    Other than that, I fixed your weird burn progress bar with a sprinkle of packets. My method relies on ProtocolLib, but you could relatively easily just use the packet found in nms. Here's the packet link: http://wiki.vg/Protocol#Window_Property
    And the packet for playing it out is PacketPlayOutWindowData.

    I hope this helped, even if it is a bit late and broken!
     
    • Winner Winner x 1
  6. Well, I really have no words o_o I'll check that all out right now. Thank you very much~
     
  7. 'Welcome! I hope you're able to straighten out those last couple bugs. If not, let me know and I'll look back into it
     
    • Like Like x 1
  8. The only issue that I've stumbled across with this method really is that the cook bar goes down when a burning item finishes and the smelting is only partway through. Noticeable if you're smelting with certain items, like sticks (very low chance of successful smelt) and ladders (usually smelts). I'm not even really sure what to mess with for fixing that, but I could just use normal furnace speed for those items. It's for a factions server anyways, so I find it unlikely someone would use something other than coal.
    The burn time is fine and something I could look into myself, though items that last longer than coal also smelt a bit more than usual. I find that somewhat interesting and might just leave it there for a sort of bonus.
     
  9. Ah, yeah this is an issue I found myself but my number sense is absolutely horrid. the value you'll want to change is this:
    Code (Java):

    short burnTime = (short) (furnace.getBurnTime() - speedUp + speedUp / 10);
     
    this is the snippet responsible for how fast it should burn in correlation to how fast you've sped it up. This was as close as I could get it to a functioning version, however sticks under produce while coal blocks and lava over produce. you don't have to touch anything else (I used this a bunch so I stuck it in this handy dandy variable, sorry for not including better annotations). Simply mess with this equation a bunch until it does what you want. If you'd like, you could even brute force it and manually set it for each item based on your speed value, I just consider using an equation like this will be easier to work with if you change little things here and there. It's completely up to you!
     
    • Informative Informative x 1
Thread Status:
Not open for further replies.