001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.bukkit.queue;
020
021import com.plotsquared.bukkit.schematic.StateWrapper;
022import com.plotsquared.core.queue.DelegateQueueCoordinator;
023import com.sk89q.jnbt.CompoundTag;
024import com.sk89q.worldedit.bukkit.BukkitAdapter;
025import com.sk89q.worldedit.entity.Entity;
026import com.sk89q.worldedit.function.pattern.Pattern;
027import com.sk89q.worldedit.math.BlockVector3;
028import com.sk89q.worldedit.world.block.BaseBlock;
029import com.sk89q.worldedit.world.block.BlockState;
030import org.apache.logging.log4j.LogManager;
031import org.apache.logging.log4j.Logger;
032import org.bukkit.Location;
033import org.bukkit.entity.EntityType;
034import org.bukkit.generator.LimitedRegion;
035import org.checkerframework.checker.nullness.qual.NonNull;
036
037/**
038 * Wraps a {@link LimitedRegion} inside a {@link com.plotsquared.core.queue.QueueCoordinator} so it can be written to.
039 *
040 * @since 6.9.0
041 */
042public class LimitedRegionWrapperQueue extends DelegateQueueCoordinator {
043
044    private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + LimitedRegionWrapperQueue.class.getSimpleName());
045
046    private final LimitedRegion limitedRegion;
047    private boolean useOtherRestoreTagMethod = false;
048
049    /**
050     * @since 6.9.0
051     */
052    public LimitedRegionWrapperQueue(LimitedRegion limitedRegion) {
053        super(null);
054        this.limitedRegion = limitedRegion;
055    }
056
057    @Override
058    public boolean setBlock(final int x, final int y, final int z, @NonNull final Pattern pattern) {
059        return setBlock(x, y, z, pattern.applyBlock(BlockVector3.at(x, y, z)));
060    }
061
062    @Override
063    public boolean setBlock(final int x, final int y, final int z, @NonNull final BaseBlock id) {
064        boolean result = setBlock(x, y, z, id.toImmutableState());
065        if (result && id.hasNbtData()) {
066            CompoundTag tag = id.getNbtData();
067            StateWrapper sw = new StateWrapper(tag);
068            try {
069                if (useOtherRestoreTagMethod && getWorld() != null) {
070                    sw.restoreTag(getWorld().getName(), x, y, z);
071                } else {
072                    sw.restoreTag(limitedRegion.getBlockState(x, y, z).getBlock());
073                }
074            } catch (IllegalArgumentException e) {
075                LOGGER.error("Error attempting to populate tile entity into the world at location {},{},{}", x, y, z, e);
076                return false;
077            } catch (IllegalStateException e) {
078                useOtherRestoreTagMethod = true;
079                LOGGER.warn("IllegalStateException attempting to populate tile entity into the world at location {},{},{}. " +
080                        "Possibly on <=1.17.1, switching to secondary method.", x, y, z, e);
081            }
082        }
083        return result;
084    }
085
086    @Override
087    public boolean setBlock(final int x, final int y, final int z, @NonNull final BlockState id) {
088        try {
089            limitedRegion.setType(x, y, z, BukkitAdapter.adapt(id.getBlockType()));
090            limitedRegion.setBlockData(x, y, z, BukkitAdapter.adapt(id));
091        } catch (IllegalArgumentException e) {
092            LOGGER.error("Error attempting to populate block into the world at location {},{},{}", x, y, z, e);
093            return false;
094        }
095        return true;
096    }
097
098    @Override
099    public boolean setEntity(@NonNull final Entity entity) {
100        EntityType type = BukkitAdapter.adapt(entity.getState().getType());
101        double x = entity.getLocation().getX();
102        double y = entity.getLocation().getY();
103        double z = entity.getLocation().getZ();
104        Location location = new Location(limitedRegion.getWorld(), x, y, z);
105        try {
106            limitedRegion.spawnEntity(location, type);
107        } catch (IllegalArgumentException e) {
108            LOGGER.error("Error attempting to populate entity into the world at location {},{},{}", (int) x, (int) y, (int) z, e);
109            return false;
110        }
111        return true;
112    }
113
114    @Override
115    public boolean setTile(final int x, final int y, final int z, @NonNull final CompoundTag tag) {
116        StateWrapper sw = new StateWrapper(tag);
117        try {
118            return sw.restoreTag(limitedRegion.getBlockState(x, y, z).getBlock());
119        } catch (IllegalArgumentException e) {
120            LOGGER.error("Error attempting to populate tile entity into the world at location {},{},{}", x, y, z, e);
121            return false;
122        }
123    }
124
125    @Override
126    public boolean isSettingTiles() {
127        return true;
128    }
129
130}