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