public class BukkitPlatformImpl implements IPlatformImpl, IBukkitExtension {

    private static final String DEFAULT_IMPL_NAME = "craftbukkit";
    /**
     * CONFIG
     */
    private final String CONFIG_TITLE = "general";
    private final String CONFIG_DEBUG = CONFIG_TITLE + ".debug";
    private final String CONFIG_ENABLE_METRICS = CONFIG_TITLE + ".enable-metrics";
    private final String CONFIG_ENABLE_COMPATIBILITY_MODE = CONFIG_TITLE + ".enable-compatibility-mode";
    private final String CONFIG_FORCE_ENABLE_LEGACY = CONFIG_TITLE + ".force-enable-legacy";
    private final String CONFIG_SPECIFIC_HANDLER_PATH = CONFIG_TITLE + ".specific-handler-path";
    private final String CONFIG_HANDLERS_TITLE = CONFIG_TITLE + ".handlers";
    private final int BSTATS_ID = 13051;
    private final BukkitPlugin mPlugin;
    private boolean DEBUG = false;
    private boolean isInit = false;
    private boolean forceLegacy = false;
    private boolean compatibilityMode = false;
    private IHandler mHandler;
    private IChunkObserver mChunkObserver;
    private ILightEngine mLightEngine;
    private IBackgroundService mBackgroundService;
    private IExtension mExtension;
    private UUID mUUID;

    public BukkitPlatformImpl(BukkitPlugin plugin) {
        this.mPlugin = plugin;
    }

    public void toggleDebug() {
        DEBUG = !DEBUG;
        log("Debug mode is " + (DEBUG ? "en" : "dis") + "abled");
    }

    private FileConfiguration getConfig() {
        return getPlugin().getConfig();
    }

    private void generateConfig() {
        // create config
        try {
            File file = new File(getPlugin().getDataFolder(), "config.yml");
            if (!file.exists()) {
                getConfig().set(CONFIG_DEBUG, false);
                getConfig().set(CONFIG_ENABLE_METRICS, true);
                getConfig().set(CONFIG_ENABLE_COMPATIBILITY_MODE, false);
                if (Build.API_VERSION == Build.PREVIEW) { // only for PREVIEW build
                    getConfig().set(CONFIG_FORCE_ENABLE_LEGACY, true);
                } else {
                    getConfig().set(CONFIG_FORCE_ENABLE_LEGACY, false);
                }
                getPlugin().saveConfig();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean upgradeConfig() {
        boolean needSave = false;
        if (getConfig().isSet("general.specific-storage-provider")) {
            getConfig().set("general.specific-storage-provider", null);
            needSave = true;
        }
        if (getConfig().isSet("handler.specific-handler-path")) {
            getConfig().set("handler.specific-handler-path", null);
            needSave = true;
        }
        if (getConfig().isSet("handler.craftbukkit.factory-path")) {
            getConfig().set("handler.craftbukkit.factory-path", null);
            needSave = true;
        }
        if (needSave) {
            getConfig().set("handler", null);
        }
        return needSave;
    }

    private void checkAndSetDefaults() {
        boolean needSave = upgradeConfig();
        if (!getConfig().isSet(CONFIG_ENABLE_COMPATIBILITY_MODE)) {
            getConfig().set(CONFIG_ENABLE_COMPATIBILITY_MODE, false);
            needSave = true;
        }
        if (!getConfig().isSet(CONFIG_SPECIFIC_HANDLER_PATH)) {
            getConfig().set(CONFIG_SPECIFIC_HANDLER_PATH, "none");
            needSave = true;
        }
        if (!getConfig().isSet(CONFIG_HANDLERS_TITLE + "." + DEFAULT_IMPL_NAME + ".factory-path")) {
            getConfig().set(CONFIG_HANDLERS_TITLE + "." + DEFAULT_IMPL_NAME + ".factory-path",
                    "ru.beykerykt.minecraft.lightapi.bukkit.internal.handler." + DEFAULT_IMPL_NAME + ".HandlerFactory");
            needSave = true;
        }
        if (needSave) {
            getPlugin().saveConfig();
        }
    }

    private void initHandler() throws Exception {
        checkAndSetDefaults();
        // load specific handler if available
        String specificPkg = getConfig().getString(CONFIG_SPECIFIC_HANDLER_PATH);
        if (specificPkg != null && !specificPkg.equalsIgnoreCase("none")) {
            info("Initial load specific handler");
            mHandler = (IHandler) Class.forName(specificPkg).getConstructor().newInstance();
            info("Custom handler is loaded: " + mHandler.getClass().getName());
            return;
        }

        // compatibility mode (1.17+)
        compatibilityMode = getConfig().getBoolean(CONFIG_ENABLE_COMPATIBILITY_MODE);
        if (compatibilityMode) {
            if (VersionUtil.compareBukkitVersionTo("1.17") >= 0) {
                info("Compatibility mode is enabled");
                mHandler = new CompatibilityHandler();
                mHandler.onInitialization(this);
                return;
            } else {
                error("Compatibility mode can only work on versions > 1.17");
            }
        }

        // First, check Bukkit server implementation, since Bukkit is only an API, and there
        // may be several implementations (for example: Spigot, Paper, Glowstone and etc)
        String implName = Bukkit.getName().toLowerCase();
        debug("Server implementation name: " + implName);

        String modFactoryPath = getConfig().getString(CONFIG_HANDLERS_TITLE + "." + implName + ".factory-path");
        try {
            Class.forName(modFactoryPath);
        } catch (Exception ex) {
            debug("Specific HandlerFactory for " + implName + " is not detected. Switch to default: "
                    + DEFAULT_IMPL_NAME);
            implName = DEFAULT_IMPL_NAME;
            modFactoryPath = getConfig().getString(CONFIG_HANDLERS_TITLE + "." + implName + ".factory-path");
        }
        IHandlerFactory factory = (IHandlerFactory) Class.forName(modFactoryPath).getConstructor().newInstance();
        mHandler = factory.createHandler(this);
        debug("Handler is loaded: " + mHandler.getClass().getName());
    }

    private void enableMetrics() {
        boolean enableMetrics = getConfig().getBoolean(CONFIG_ENABLE_METRICS);
        if (enableMetrics) {
            Metrics metrics = new Metrics(getPlugin(), BSTATS_ID);
        }
        info("Metrics is " + (enableMetrics ? "en" : "dis") + "abled!");
    }

    @Override
    public int prepare() {
        // general default config
        generateConfig();

        // debug mode
        this.DEBUG = getConfig().getBoolean(CONFIG_DEBUG);
        return ResultCode.SUCCESS;
    }

    @Override
    public int initialization() {
        // enable force legacy
        forceLegacy = getConfig().getBoolean(CONFIG_FORCE_ENABLE_LEGACY);
        if (forceLegacy) {
            info("Force legacy is enabled");
        }

        // init handler
        try {
            initHandler();
            mHandler.onInitialization(this);
        } catch (Exception e) {
            e.printStackTrace();
            return ResultCode.FAILED;
        }

        // init background service
        mBackgroundService = new BukkitBackgroundServiceImpl(this, getHandler());
        mBackgroundService.onStart();

        // init chunk observer
        mChunkObserver = new BukkitScheduledChunkObserverImpl(this, getBackgroundService(), getHandler());
        mChunkObserver.onStart();

        // init light engine
        mLightEngine = new BukkitScheduledLightEngineImpl(this, getBackgroundService(), getHandler());
        mLightEngine.onStart();

        // init extension
        mExtension = this;

        isInit = true;

        // enable metrics
        enableMetrics();

        return ResultCode.SUCCESS;
    }

    @Override
    public void shutdown() {
        mLightEngine.onShutdown();
        mChunkObserver.onShutdown();
        mBackgroundService.onShutdown();
        mHandler.onShutdown(this);
        mHandler = null;
        isInit = false;
    }

    @Override
    public boolean isInitialized() {
        return isInit;
    }

    @Override
    public void log(String msg) {
        StringBuilder builder = new StringBuilder(ChatColor.AQUA + "<LightAPI>: ");
        builder.append(ChatColor.WHITE).append(msg);
        mPlugin.getServer().getConsoleSender().sendMessage(builder.toString());
    }

    @Override
    public void info(String msg) {
        log("[INFO] " + msg);
    }

    @Override
    public void debug(String msg) {
        if (DEBUG) {
            log(ChatColor.YELLOW + "[DEBUG] " + msg);
        }
    }

    @Override
    public void error(String msg) {
        log(ChatColor.RED + "[ERROR] " + msg);
    }

    @Override
    public PlatformType getPlatformType() {
        return getHandler().getPlatformType();
    }

    @Override
    public ILightEngine getLightEngine() {
        return mLightEngine;
    }

    @Override
    public IChunkObserver getChunkObserver() {
        return mChunkObserver;
    }

    @Override
    public IBackgroundService getBackgroundService() {
        return mBackgroundService;
    }

    @Override
    public IExtension getExtension() {
        return mExtension;
    }

    @Override
    public boolean isWorldAvailable(String worldName) {
        return getPlugin().getServer().getWorld(worldName) != null;
    }

    /* @hide */
    public int sendCmdLocked(int cmdId, Object... args) {
        int resultCode = ResultCode.SUCCESS;
        // handle internal codes
        switch (cmdId) {
            case InternalCode.UPDATE_UUID:
                mUUID = (UUID) args[0];
                break;
            default:
                resultCode = getHandler().sendCmd(cmdId, args);
                break;
        }
        return resultCode;
    }

    @Override
    public int sendCmd(int cmdId, Object... args) {
        return sendCmdLocked(cmdId, args);
    }

    @Override
    public UUID getUUID() {
        return mUUID;
    }

    public BukkitPlugin getPlugin() {
        return mPlugin;
    }

    @Override
    public IHandler getHandler() {
        return mHandler;
    }

    @Override
    public int getLightLevel(World world, int blockX, int blockY, int blockZ) {
        return getLightLevel(world, blockX, blockY, blockZ, LightFlag.BLOCK_LIGHTING);
    }

    @Override
    public int getLightLevel(World world, int blockX, int blockY, int blockZ, int lightFlags) {
        return getLightEngine().getLightLevel(world.getName(), blockX, blockY, blockZ, lightFlags);
    }

    @Override
    public int setLightLevel(World world, int blockX, int blockY, int blockZ, int lightLevel) {
        return setLightLevel(world, blockX, blockY, blockZ, lightLevel, LightFlag.BLOCK_LIGHTING, EditPolicy.DEFERRED,
                SendPolicy.DEFERRED, null);
    }

    @Override
    public int setLightLevel(World world, int blockX, int blockY, int blockZ, int lightLevel, int lightFlags) {
        return setLightLevel(world, blockX, blockY, blockZ, lightLevel, lightFlags, EditPolicy.DEFERRED,
                SendPolicy.DEFERRED, null);
    }

    @Override
    public int setLightLevel(World world, int blockX, int blockY, int blockZ, int lightLevel, int lightFlags,
            ICallback callback) {
        return setLightLevel(world, blockX, blockY, blockZ, lightLevel, lightFlags, EditPolicy.DEFERRED,
                SendPolicy.DEFERRED, callback);
    }

    @Override
    public int setLightLevel(World world, int blockX, int blockY, int blockZ, int lightLevel, int lightFlags,
            EditPolicy editPolicy, SendPolicy sendPolicy, ICallback callback) {
        return getLightEngine().setLightLevel(world.getName(), blockX, blockY, blockZ, lightLevel, lightFlags,
                editPolicy, sendPolicy, callback);
    }

    @Override
    public boolean isBackwardAvailable() {
        boolean flag = Build.API_VERSION == Build.PREVIEW;
        try {
            Class.forName("ru.beykerykt.lightapi.LightAPI");
            return forceLegacy ? true : (flag & true);
        } catch (ClassNotFoundException ex) {
            return false;
        }
    }

    @Override
    public boolean isCompatibilityMode() {
        return compatibilityMode;
    }
} public abstract class BaseNMSHandler implements IHandler {

    private BukkitPlatformImpl mPlatformImpl;

    @Override
    public void onInitialization(BukkitPlatformImpl impl) throws Exception {
        this.mPlatformImpl = impl;
    }

    protected BukkitPlatformImpl getPlatformImpl() {
        return mPlatformImpl;
    }

    @Override
    public PlatformType getPlatformType() {
        return PlatformType.CRAFTBUKKIT;
    }

    @Override
    public boolean isMainThread() {
        return Bukkit.isPrimaryThread();
    }
} public class VersionUtil {

    public static String serverVersion() {
        return Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
    }

    public static String serverName() {
        return Bukkit.getVersion().split("-")[1];
    }

    public static String bukkitName() {
        return Bukkit.getName();
    }

    public static String bukkitVersion() {
        return (Bukkit.getBukkitVersion() + "-").split("-")[0];
    }

    private static String paddedVersion(String value) {
        return VersionUtil.leftPad(value, "\\d+", '0', 8);
    }

    private static String paddedBukkitVersion() {
        return paddedVersion(bukkitVersion());
    }

    public static int compareBukkitVersionTo(String version) {
        return paddedBukkitVersion().compareTo(paddedVersion(version));
    }

    private static String leftPad(String text, String regex, char padCharacter, int width) {
        StringBuilder sb = new StringBuilder();
        Matcher m = Pattern.compile(regex).matcher(text);
        String chars = String.format("%" + width + "s", "").replace(' ', padCharacter);
        int last = 0;
        while (m.find()) {
            int start = m.start();
            int n = m.end() - start;
            if (n < width) {
                sb.append(text, last, start);
                sb.append(chars, n, width);
                last = start;
            }
        }
        if (last == 0) {
            return text;
        }
        if (last < text.length()) {
            sb.append(text, last, text.length());
        }
        return sb.toString();
    }
} public class ResultCode {

    /**
     * N/A
     */
    public static final int MOVED_TO_DEFERRED = 1;

    /**
     * The task has been successfully completed
     */
    public static final int SUCCESS = 0;

    /**
     * The task has been failed
     */
    public static final int FAILED = -1;

    /**
     * Current world is not available
     */
    public static final int WORLD_NOT_AVAILABLE = -2;

    /**
     * (1.14+) No lighting changes in current world
     */
    public static final int RECALCULATE_NO_CHANGES = -3;

    /**
     * (1.14+) SkyLight data is not available in current world
     */
    public static final int SKYLIGHT_DATA_NOT_AVAILABLE = -4;

    /**
     * (1.14+) BlockLight data is not available in current world
     */
    public static final int BLOCKLIGHT_DATA_NOT_AVAILABLE = -5;

    /**
     * Current function is not implemented
     */
    public static final int NOT_IMPLEMENTED = -6;

    /**
     * Chunk is not loaded
     */
    public static final int CHUNK_NOT_LOADED = -7;
} public class LightFlag {

    /**
     * N/A
     */
    public static final int NONE = 0;

    /**
     * A flag for editing block light layer
     */
    public static final int BLOCK_LIGHTING = 1;

    /**
     * A flag for editing sky light layer
     */
    public static final int SKY_LIGHTING = 2;

    /**
     * A flag for storing the light level in the storage provider
     */
    @Deprecated
    public static final int USE_STORAGE_PROVIDER = 4;
} public class BitChunkData extends ChunkData {

    private static final int DEFAULT_SIZE = 2048;
    private final BitSet skyLightUpdateBits;
    private final BitSet blockLightUpdateBits;

    private final int topSection;
    private final int bottomSection;

    public BitChunkData(String worldName, int chunkX, int chunkZ, int topSection, int bottomSection) {
        super(worldName, chunkX, chunkZ);
        this.skyLightUpdateBits = new BitSet(DEFAULT_SIZE);
        this.blockLightUpdateBits = new BitSet(DEFAULT_SIZE);
        this.topSection = topSection;
        this.bottomSection = bottomSection;
    }

    public BitSet getSkyLightUpdateBits() {
        return skyLightUpdateBits;
    }

    public BitSet getBlockLightUpdateBits() {
        return blockLightUpdateBits;
    }

    /**
     * Max chunk section
     */
    // getHeight() - this.world.countVerticalSections() + 2;
    // getTopY() - this.getBottomY() + this.getHeight();
    public int getTopSection() {
        return topSection;
    }

    /**
     * Min chunk section
     */
    // getBottomY() - this.world.getBottomSectionCoord() - 1
    public int getBottomSection() {
        return bottomSection;
    }

    @Override
    public void markSectionForUpdate(int lightFlags, int sectionY) {
        int minY = getBottomSection();
        int maxY = getTopSection();
        if (sectionY < minY || sectionY > maxY) {
            return;
        }
        int l = sectionY - minY;

        if (FlagUtils.isFlagSet(lightFlags, LightFlag.SKY_LIGHTING)) {
            skyLightUpdateBits.set(l);
        }

        if (FlagUtils.isFlagSet(lightFlags, LightFlag.BLOCK_LIGHTING)) {
            blockLightUpdateBits.set(l);
        }
    }

    @Override
    public void clearUpdate() {
        this.skyLightUpdateBits.clear();
        this.blockLightUpdateBits.clear();
    }

    @Override
    public void setFullSections() {
        // TODO: Mark full sections
        for (int i = getBottomSection(); i < getTopSection(); i++) {
            markSectionForUpdate(LightFlag.SKY_LIGHTING | LightFlag.BLOCK_LIGHTING, i);
        }
    }

    @Override
    public String toString() {
        return "BitChunkData{" + "worldName=" + getWorldName() + ", chunkX=" + getChunkX() + ", chunkZ=" + getChunkZ()
                + ", skyLightUpdateBits=" + skyLightUpdateBits + ", blockLightUpdateBits=" + blockLightUpdateBits + '}';
    }
} public interface IChunkData {

    /**
     * @return World name
     */
    String getWorldName();

    /**
     * @return Chunk X Coordinate
     */
    int getChunkX();

    /**
     * @return Chunk Z Coordinate
     */
    int getChunkZ();

    /**
     * N/A
     */
    void markSectionForUpdate(int lightFlags, int sectionY);

    /**
     * N/A
     */
    void clearUpdate();

    /**
     * N/A
     */
    void setFullSections();
} public enum LightEngineType {
    UNKNOWN(0), VANILLA(1), STARLIGHT(2), COMPATIBILITY(3);

    private final int id;

    LightEngineType(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
} public enum LightEngineVersion {

    /**
     * N/A
     */
    UNKNOWN(0),

    /**
     * Minecraft version is before 1.14. For update lighting in client-side, need send full chunk.
     */
    V1(1),

    /**
     * Minecraft version is equals or after 1.14. For update lighting in client-side, need send only
     * light update packet.
     */
    V2(2);

    private final int id;

    LightEngineVersion(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
} public class FlagUtils {

    /**
     * N/A
     */
    public static int addFlag(int flags, int targetFlag) {
        return flags |= targetFlag;
    }

    /**
     * N/A
     */
    public static int removeFlag(int flags, int targetFlag) {
        return flags &= ~targetFlag;
    }

    /**
     * N/A
     */
    public static boolean isFlagSet(int flags, int targetFlag) {
        return (flags & targetFlag) == targetFlag;
    }
}
import com.google.common.collect.Lists;
import net.minecraft.core.BlockPosition;
import net.minecraft.core.SectionPosition;
import net.minecraft.network.protocol.game.PacketPlayOutLightUpdate;
import net.minecraft.server.level.EntityPlayer;
import net.minecraft.server.level.LightEngineThreaded;
import net.minecraft.server.level.WorldServer;
import net.minecraft.util.thread.ThreadedMailbox;
import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.EnumSkyBlock;
import net.minecraft.world.level.chunk.Chunk;
import net.minecraft.world.level.lighting.LightEngineBlock;
import net.minecraft.world.level.lighting.LightEngineGraph;
import net.minecraft.world.level.lighting.LightEngineLayer;
import net.minecraft.world.level.lighting.LightEngineSky;
import net.minecraft.world.level.lighting.LightEngineStorage;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.BitSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import ru.beykerykt.minecraft.lightapi.bukkit.internal.BukkitPlatformImpl;
import ru.beykerykt.minecraft.lightapi.bukkit.internal.handler.craftbukkit.nms.BaseNMSHandler;
import ru.beykerykt.minecraft.lightapi.bukkit.internal.utils.VersionUtil;
import ru.beykerykt.minecraft.lightapi.common.api.ResultCode;
import ru.beykerykt.minecraft.lightapi.common.api.engine.LightFlag;
import ru.beykerykt.minecraft.lightapi.common.internal.chunks.data.BitChunkData;
import ru.beykerykt.minecraft.lightapi.common.internal.chunks.data.IChunkData;
import ru.beykerykt.minecraft.lightapi.common.internal.engine.LightEngineType;
import ru.beykerykt.minecraft.lightapi.common.internal.engine.LightEngineVersion;
import ru.beykerykt.minecraft.lightapi.common.internal.utils.FlagUtils;
/*
 * The MIT License (MIT)
 *
 * Copyright 2021 Vladimir Mikhailov <beykerykt@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package ru.beykerykt.minecraft.lightapi.bukkit.internal.handler.craftbukkit.nms.v1_17_R1;






public class VanillaNMSHandler extends BaseNMSHandler {

    protected Field lightEngine_ThreadedMailbox;
    private Field threadedMailbox_State;
    private Method threadedMailbox_DoLoopStep;
    private Field lightEngineLayer_d;
    private Method lightEngineStorage_d;
    private Method lightEngineGraph_a;

    protected static RuntimeException toRuntimeException(Throwable e) {
        if (e instanceof RuntimeException) {
            return (RuntimeException) e;
        }
        Class<? extends Throwable> cls = e.getClass();
        return new RuntimeException(String.format("(%s) %s",
                RuntimeException.class.getPackage().equals(cls.getPackage()) ? cls.getSimpleName() : cls.getName(),
                e.getMessage()), e);
    }

    private int getDeltaLight(int x, int dx) {
        return (((x ^ ((-dx >> 4) & 15)) + 1) & (-(dx & 1)));
    }

    protected void executeSync(LightEngineThreaded lightEngine, Runnable task) {
        try {
            // ##### STEP 1: Pause light engine mailbox to process its tasks. #####
            ThreadedMailbox<Runnable> threadedMailbox = (ThreadedMailbox<Runnable>) lightEngine_ThreadedMailbox.get(
                    lightEngine);
            // State flags bit mask:
            // 0x0001 - Closing flag (ThreadedMailbox is closing if non zero).
            // 0x0002 - Busy flag (ThreadedMailbox performs a task from queue if non zero).
            AtomicInteger stateFlags = (AtomicInteger) threadedMailbox_State.get(threadedMailbox);
            int flags; // to hold values from stateFlags
            long timeToWait = -1;
            // Trying to set bit 1 in state bit mask when it is not set yet.
            // This will break the loop in other thread where light engine mailbox processes the taks.
            while (!stateFlags.compareAndSet(flags = stateFlags.get() & ~2, flags | 2)) {
                if ((flags & 1) != 0) {
                    // ThreadedMailbox is closing. The light engine mailbox may also stop processing tasks.
                    // The light engine mailbox can be close due to server shutdown or unloading (closing) the
                    // world.
                    // I am not sure is it unsafe to process our tasks while the world is closing is closing,
                    // but will try it (one can throw exception here if it crashes the server).
                    if (timeToWait == -1) {
                        // Try to wait 3 seconds until light engine mailbox is busy.
                        timeToWait = System.currentTimeMillis() + 3 * 1000;
                        getPlatformImpl().debug("ThreadedMailbox is closing. Will wait...");
                    } else if (System.currentTimeMillis() >= timeToWait) {
                        throw new RuntimeException("Failed to enter critical section while ThreadedMailbox is closing");
                    }
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException ignored) {
                    }
                }
            }
            try {
                // ##### STEP 2: Safely running the task while the mailbox process is stopped. #####
                task.run();
            } finally {
                // STEP 3: ##### Continue light engine mailbox to process its tasks. #####
                // Firstly: Clearing busy flag to allow ThreadedMailbox to use it for running light engine
                // tasks.
                while (!stateFlags.compareAndSet(flags = stateFlags.get(), flags & ~2))
                    ;
                // Secondly: IMPORTANT! The main loop of ThreadedMailbox was broken. Not completed tasks may
                // still be
                // in the queue. Therefore, it is important to start the loop again to process tasks from
                // the queue.
                // Otherwise, the main server thread may be frozen due to tasks stuck in the queue.
                threadedMailbox_DoLoopStep.invoke(threadedMailbox);
            }
        } catch (InvocationTargetException e) {
            throw toRuntimeException(e.getCause());
        } catch (IllegalAccessException e) {
            throw toRuntimeException(e);
        }
    }

    private void lightEngineLayer_a(LightEngineLayer<?, ?> les, BlockPosition var0, int var1) {
        try {
            LightEngineStorage<?> ls = (LightEngineStorage<?>) lightEngineLayer_d.get(les);
            lightEngineStorage_d.invoke(ls);
            lightEngineGraph_a.invoke(les, 9223372036854775807L, var0.asLong(), 15 - var1, true);
        } catch (InvocationTargetException e) {
            throw toRuntimeException(e.getCause());
        } catch (IllegalAccessException e) {
            throw toRuntimeException(e);
        }
    }

    private IChunkData createBitChunkData(String worldName, int chunkX, int chunkZ) {
        World world = Bukkit.getWorld(worldName);
        WorldServer worldServer = ((CraftWorld) world).getHandle();
        final LightEngineThreaded lightEngine = worldServer.getChunkProvider().getLightEngine();
        int bottom = lightEngine.c();
        int top = lightEngine.d();
        return new BitChunkData(worldName, chunkX, chunkZ, top, bottom);
    }

    @Override
    public void onInitialization(BukkitPlatformImpl impl) throws Exception {