Помогите Как поставить задержку на действие ?

DemonikJR

Активный участник
Баллы
61
Имя в Minecraft
DemonikJR
Пишу плагин, при разрушение блока железной руды киркой тебе выдаётся железная руда, а на место сломанного блока ставится камень (потом нужно будет восстановить руду через минут 20, но с этим позже разберусь). Проблема в том, что если писать
Код:
Location loc = b.getLocation();
Block bl = loc.getBlock();
bl.setType(Material.STONE);
ничего не происходит, я так понимаю, что блок не успевает ставиться, поэтому нужна задержка.
Т.к. Threads.sleep усыпляет весь сервер, то нужно использовать BukkitScheduler, но при его использовании происходит такая ошибка.
upload_2021-3-9_23-31-57.png
Статью про шедулер смотрел, так и не понял в чём проблема. Если что Streams - основной класс, а это действие в классе Handler.
Код:
package dem.main;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.*********ventory.ItemStack;
import org.bukkit.plugin.Plugin;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitScheduler;

public class Handler implements Listener {
    
    @EventHandler
    public void join(PlayerJoinEvent e) {
        Player p = e.getPlayer();
        p.sendMessage("Здравствуй, странник! Ты попал на сервер ScrollsMC, больше о сервере ты можешь узнать у гида в хабе.");
    }
    
    @EventHandler
    public void block(BlockBreakEvent e) {
        Player p = e.getPlayer();
        Block b = e.getBlock();
        ItemStack item = new ItemStack(Material.IRON_ORE);
        ItemStack hitem = p.getItemInHand();
        
        
        if (b.getType() == Material.IRON_ORE) {
            
            if (hitem.getType() == Material.STONE_PICKAXE) {
                p.getInventory().addItem(item);
                p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText("§aТы добыл железную руду!"));
                Location loc = b.getLocation();
                Block bl = loc.getBlock();
                
                long time = 20L * 2L;
                Bukkit.getScheduler().runTaskTimerAsynchronously(Streams, () ->  bl.setType(Material.STONE), time);
                p.playSound(p.getLocation(), Sound.BLOCK_STONE_BREAK, 1, 2);
                
            }
            else return;
        }
        else return;
        

    }
}
 
1. Запили конструктор в классе, в который будешь передавать объект своего плагина:
Код:
private final Plugin plugin;

public Handler(Plugin plugin) {
    this.plugin = plugin;
}
2. Если ты регистрируешь свой Handler в основном классе плагина, замени new Handler() на new Handler(this) - так ты передашь экземпляр своего плагина в слушатель Handler.
3. Метод, который ты использовал для откладки, не подходит для твоей задачи. Во-первых, твоё действие будет повторяться снова и снова. Во-вторых, ты запускаешь это в асинхроне, и в итоге у тебя просто будет море ошибок каждые несколько секунд, ибо нельзя взаимодействовать с миром в асинхроне. Вместо этого:
Код:
Bukkit.getScheduler().runTaskLater(plugin, () -> bl.setType(Material.STONE, time)
 
Спасибо, помогло. Ещё вопрос, если ставить шедулер на минут 10 и таких будет штук 15 (шедулер на восстановления блока руды) . Это сильно нагружает сервер? Если да, то как это можно по-другому сделать. Я читал, что можно загружать блок в хэш мап и потом выгружать, но там же тоже нужно ставить задержку...
 
Храни локацию и время, можешь даже в ArrayList.
Код:
public class TimedBlock {
    private final Location loc;
    private final long until;

    public TimedBlock(Location loc, long delay) {
        this.loc = loc;
        this.until = System.currentTimeMillis() + delay;
    }

    public Location getLocation() {
        return loc;
    }

    public boolean isAvailable() {
        return System.currentTimeMillis () > until;
    }
}
(delay - время в миллисекундах, сколько нужно подождать для восстановления)
Добавляешь List<TimedBlock> blocks в свой слушатель. Каждый раз, когда ломаешь блок - добавляешь его в список.
Далее, вместо запуска шедулера при каждом сломанном блоке, просто запускаешь один в конструкторе. В этот раз тебе понадобится runTaskTimer, чтобы он повторялся.
Там используешь Iterator (погугли, если не знаешь) своего списка. Если isAvailable возвращает false, перестаешь итерацию(просто return); иначе getLocation().getBlock().setType(Material.STONE) и iterator.remove()
 
Назад
Верх