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.util; 020 021import com.google.inject.Inject; 022import com.google.inject.Singleton; 023import com.plotsquared.core.generator.AugmentedUtils; 024import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; 025import com.plotsquared.core.location.Location; 026import com.plotsquared.core.location.PlotLoc; 027import com.plotsquared.core.player.PlotPlayer; 028import com.plotsquared.core.plot.Plot; 029import com.plotsquared.core.plot.PlotArea; 030import com.plotsquared.core.plot.PlotManager; 031import com.plotsquared.core.queue.GlobalBlockQueue; 032import com.plotsquared.core.queue.QueueCoordinator; 033import com.plotsquared.core.queue.ScopedQueueCoordinator; 034import com.plotsquared.core.util.ChunkManager; 035import com.plotsquared.core.util.RegionManager; 036import com.plotsquared.core.util.WorldUtil; 037import com.plotsquared.core.util.entity.EntityCategories; 038import com.plotsquared.core.util.task.RunnableVal; 039import com.sk89q.worldedit.bukkit.BukkitAdapter; 040import com.sk89q.worldedit.bukkit.BukkitWorld; 041import com.sk89q.worldedit.regions.CuboidRegion; 042import com.sk89q.worldedit.world.block.BaseBlock; 043import com.sk89q.worldedit.world.block.BlockTypes; 044import io.papermc.lib.PaperLib; 045import org.bukkit.Bukkit; 046import org.bukkit.Chunk; 047import org.bukkit.World; 048import org.bukkit.entity.Entity; 049import org.bukkit.entity.Player; 050import org.checkerframework.checker.nullness.qual.NonNull; 051import org.checkerframework.checker.nullness.qual.Nullable; 052 053import java.util.ArrayList; 054import java.util.HashSet; 055import java.util.List; 056import java.util.Set; 057 058import static com.plotsquared.core.util.entity.EntityCategories.CAP_ANIMAL; 059import static com.plotsquared.core.util.entity.EntityCategories.CAP_ENTITY; 060import static com.plotsquared.core.util.entity.EntityCategories.CAP_MISC; 061import static com.plotsquared.core.util.entity.EntityCategories.CAP_MOB; 062import static com.plotsquared.core.util.entity.EntityCategories.CAP_MONSTER; 063import static com.plotsquared.core.util.entity.EntityCategories.CAP_VEHICLE; 064 065@Singleton 066public class BukkitRegionManager extends RegionManager { 067 068 private final GlobalBlockQueue blockQueue; 069 070 @Inject 071 public BukkitRegionManager( 072 @NonNull WorldUtil worldUtil, @NonNull GlobalBlockQueue blockQueue, @NonNull 073 ProgressSubscriberFactory subscriberFactory 074 ) { 075 super(worldUtil, blockQueue, subscriberFactory); 076 this.blockQueue = blockQueue; 077 } 078 079 @Override 080 public boolean handleClear( 081 @NonNull Plot plot, 082 @Nullable Runnable whenDone, 083 @NonNull PlotManager manager, 084 @Nullable PlotPlayer<?> player 085 ) { 086 return false; 087 } 088 089 @Override 090 public int[] countEntities(@NonNull Plot plot) { 091 int[] existing = (int[]) plot.getMeta("EntityCount"); 092 if (existing != null && (System.currentTimeMillis() - (long) plot.getMeta("EntityCountTime") < 1000)) { 093 return existing; 094 } 095 PlotArea area = plot.getArea(); 096 World world = BukkitUtil.getWorld(area.getWorldName()); 097 Location bot = plot.getBottomAbs(); 098 Location top = plot.getTopAbs(); 099 int bx = bot.getX() >> 4; 100 int bz = bot.getZ() >> 4; 101 102 int tx = top.getX() >> 4; 103 int tz = top.getZ() >> 4; 104 105 int size = tx - bx << 4; 106 107 Set<Chunk> chunks = new HashSet<>(); 108 for (int X = bx; X <= tx; X++) { 109 for (int Z = bz; Z <= tz; Z++) { 110 if (world.isChunkLoaded(X, Z)) { 111 chunks.add(world.getChunkAt(X, Z)); 112 } 113 } 114 } 115 116 boolean doWhole = false; 117 List<Entity> entities = null; 118 if (size > 200 && chunks.size() > 200) { 119 entities = world.getEntities(); 120 if (entities.size() < 16 + size / 8) { 121 doWhole = true; 122 } 123 } 124 125 int[] count = new int[6]; 126 if (doWhole) { 127 for (Entity entity : entities) { 128 org.bukkit.Location location = entity.getLocation(); 129 PaperLib.getChunkAtAsync(location).thenAccept(chunk -> { 130 if (chunks.contains(chunk)) { 131 int X = chunk.getX(); 132 int Z = chunk.getZ(); 133 if (X > bx && X < tx && Z > bz && Z < tz) { 134 count(count, entity); 135 } else { 136 Plot other = area.getPlot(BukkitUtil.adapt(location)); 137 if (plot.equals(other)) { 138 count(count, entity); 139 } 140 } 141 } 142 }); 143 } 144 } else { 145 for (Chunk chunk : chunks) { 146 int X = chunk.getX(); 147 int Z = chunk.getZ(); 148 Entity[] entities1 = chunk.getEntities(); 149 for (Entity entity : entities1) { 150 if (X == bx || X == tx || Z == bz || Z == tz) { 151 Plot other = area.getPlot(BukkitUtil.adapt(entity.getLocation())); 152 if (plot.equals(other)) { 153 count(count, entity); 154 } 155 } else { 156 count(count, entity); 157 } 158 } 159 } 160 } 161 return count; 162 } 163 164 @Override 165 public boolean regenerateRegion( 166 final @NonNull Location pos1, 167 final @NonNull Location pos2, 168 final boolean ignoreAugment, 169 final @Nullable Runnable whenDone 170 ) { 171 final BukkitWorld world = (BukkitWorld) worldUtil.getWeWorld(pos1.getWorldName()); 172 173 final int p1x = pos1.getX(); 174 final int p1z = pos1.getZ(); 175 final int p2x = pos2.getX(); 176 final int p2z = pos2.getZ(); 177 final int bcx = p1x >> 4; 178 final int bcz = p1z >> 4; 179 final int tcx = p2x >> 4; 180 final int tcz = p2z >> 4; 181 182 final QueueCoordinator queue = blockQueue.getNewQueue(world); 183 final QueueCoordinator regenQueue = blockQueue.getNewQueue(world); 184 queue.addReadChunks(new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3()).getChunks()); 185 queue.setChunkConsumer(chunk -> { 186 187 int x = chunk.getX(); 188 int z = chunk.getZ(); 189 int xxb = x << 4; 190 int zzb = z << 4; 191 int xxt = xxb + 15; 192 int zzt = zzb + 15; 193 if (xxb >= p1x && xxt <= p2x && zzb >= p1z && zzt <= p2z) { 194 AugmentedUtils.bypass(ignoreAugment, () -> regenQueue.regenChunk(chunk.getX(), chunk.getZ())); 195 return; 196 } 197 boolean checkX1 = false; 198 199 int xxb2; 200 201 if (x == bcx) { 202 xxb2 = p1x - 1; 203 checkX1 = true; 204 } else { 205 xxb2 = xxb; 206 } 207 boolean checkX2 = false; 208 int xxt2; 209 if (x == tcx) { 210 xxt2 = p2x + 1; 211 checkX2 = true; 212 } else { 213 xxt2 = xxt; 214 } 215 boolean checkZ1 = false; 216 int zzb2; 217 if (z == bcz) { 218 zzb2 = p1z - 1; 219 checkZ1 = true; 220 } else { 221 zzb2 = zzb; 222 } 223 boolean checkZ2 = false; 224 int zzt2; 225 if (z == tcz) { 226 zzt2 = p2z + 1; 227 checkZ2 = true; 228 } else { 229 zzt2 = zzt; 230 } 231 final ContentMap map = new ContentMap(); 232 if (checkX1) { 233 map.saveRegion(world, xxb, xxb2, zzb2, zzt2); // 234 } 235 if (checkX2) { 236 map.saveRegion(world, xxt2, xxt, zzb2, zzt2); // 237 } 238 if (checkZ1) { 239 map.saveRegion(world, xxb2, xxt2, zzb, zzb2); // 240 } 241 if (checkZ2) { 242 map.saveRegion(world, xxb2, xxt2, zzt2, zzt); // 243 } 244 if (checkX1 && checkZ1) { 245 map.saveRegion(world, xxb, xxb2, zzb, zzb2); // 246 } 247 if (checkX2 && checkZ1) { 248 map.saveRegion(world, xxt2, xxt, zzb, zzb2); // ? 249 } 250 if (checkX1 && checkZ2) { 251 map.saveRegion(world, xxb, xxb2, zzt2, zzt); // ? 252 } 253 if (checkX2 && checkZ2) { 254 map.saveRegion(world, xxt2, xxt, zzt2, zzt); // 255 } 256 CuboidRegion currentPlotClear = new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3()); 257 map.saveEntitiesOut(Bukkit.getWorld(world.getName()).getChunkAt(x, z), currentPlotClear); 258 AugmentedUtils.bypass( 259 ignoreAugment, 260 () -> ChunkManager.setChunkInPlotArea(null, new RunnableVal<ScopedQueueCoordinator>() { 261 @Override 262 public void run(ScopedQueueCoordinator value) { 263 Location min = value.getMin(); 264 int bx = min.getX(); 265 int bz = min.getZ(); 266 for (int x1 = 0; x1 < 16; x1++) { 267 for (int z1 = 0; z1 < 16; z1++) { 268 PlotLoc plotLoc = new PlotLoc(bx + x1, bz + z1); 269 BaseBlock[] ids = map.allBlocks.get(plotLoc); 270 if (ids != null) { 271 int minY = value.getMin().getY(); 272 for (int yIndex = 0; yIndex < ids.length; yIndex++) { 273 int y = yIndex + minY; 274 BaseBlock id = ids[yIndex]; 275 if (id != null) { 276 value.setBlock(x1, y, z1, id); 277 } else { 278 value.setBlock(x1, y, z1, BlockTypes.AIR.getDefaultState()); 279 } 280 } 281 } 282 } 283 } 284 } 285 }, world.getName(), chunk) 286 ); 287 //map.restoreBlocks(worldObj, 0, 0); 288 map.restoreEntities(Bukkit.getWorld(world.getName())); 289 }); 290 regenQueue.setCompleteTask(whenDone); 291 queue.setCompleteTask(regenQueue::enqueue); 292 queue.enqueue(); 293 return true; 294 } 295 296 @Override 297 public void clearAllEntities(@NonNull Location pos1, @NonNull Location pos2) { 298 String world = pos1.getWorldName(); 299 300 final World bukkitWorld = BukkitUtil.getWorld(world); 301 final List<Entity> entities; 302 if (bukkitWorld != null) { 303 entities = new ArrayList<>(bukkitWorld.getEntities()); 304 } else { 305 entities = new ArrayList<>(); 306 } 307 308 int bx = pos1.getX(); 309 int bz = pos1.getZ(); 310 int tx = pos2.getX(); 311 int tz = pos2.getZ(); 312 for (Entity entity : entities) { 313 if (!(entity instanceof Player)) { 314 org.bukkit.Location location = entity.getLocation(); 315 if (location.getX() >= bx && location.getX() <= tx && location.getZ() >= bz && location.getZ() <= tz) { 316 if (entity.hasMetadata("ps-tmp-teleport")) { 317 continue; 318 } 319 entity.remove(); 320 } 321 } 322 } 323 } 324 325 private void count(int[] count, @NonNull Entity entity) { 326 final com.sk89q.worldedit.world.entity.EntityType entityType = BukkitAdapter.adapt(entity.getType()); 327 328 if (EntityCategories.PLAYER.contains(entityType)) { 329 return; 330 } else if (EntityCategories.PROJECTILE.contains(entityType) || EntityCategories.OTHER.contains(entityType) || EntityCategories.HANGING 331 .contains(entityType)) { 332 count[CAP_MISC]++; 333 } else if (EntityCategories.ANIMAL.contains(entityType) || EntityCategories.VILLAGER.contains(entityType) || EntityCategories.TAMEABLE 334 .contains(entityType)) { 335 count[CAP_MOB]++; 336 count[CAP_ANIMAL]++; 337 } else if (EntityCategories.VEHICLE.contains(entityType)) { 338 count[CAP_VEHICLE]++; 339 } else if (EntityCategories.HOSTILE.contains(entityType)) { 340 count[CAP_MOB]++; 341 count[CAP_MONSTER]++; 342 } 343 count[CAP_ENTITY]++; 344 } 345 346}