PlotSquared/Bukkit/src/main/java/com/plotsquared/bukkit/listener/ChunkListener.java

367 lines
14 KiB
Java
Raw Normal View History

/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
2020-04-15 21:26:54 +02:00
package com.plotsquared.bukkit.listener;
2020-07-10 22:12:37 +02:00
import com.google.inject.Inject;
import com.plotsquared.core.PlotSquared;
2020-04-16 06:14:33 +02:00
import com.plotsquared.core.configuration.Settings;
2020-04-15 21:26:54 +02:00
import com.plotsquared.core.location.Location;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
2020-07-10 17:32:07 +02:00
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.plot.world.SinglePlotArea;
2020-04-15 21:26:54 +02:00
import com.plotsquared.core.util.ReflectionUtils.RefClass;
import com.plotsquared.core.util.ReflectionUtils.RefField;
import com.plotsquared.core.util.ReflectionUtils.RefMethod;
2020-07-15 13:18:09 +02:00
import com.plotsquared.core.util.task.PlotSquaredTask;
2020-04-15 21:26:54 +02:00
import com.plotsquared.core.util.task.TaskManager;
2020-07-15 13:18:09 +02:00
import com.plotsquared.core.util.task.TaskTime;
import io.papermc.lib.PaperLib;
2015-04-07 16:47:02 +02:00
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
2015-04-18 15:47:13 +02:00
import org.bukkit.entity.Item;
2015-04-26 10:51:13 +02:00
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPhysicsEvent;
2015-04-26 10:51:13 +02:00
import org.bukkit.event.entity.CreatureSpawnEvent;
2015-04-18 15:47:13 +02:00
import org.bukkit.event.entity.ItemSpawnEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.reflect.Method;
import java.util.HashSet;
2020-07-17 17:39:41 +02:00
import java.util.Objects;
2020-04-15 21:26:54 +02:00
import static com.plotsquared.core.util.ReflectionUtils.getRefClass;
2020-04-30 12:01:52 +02:00
@SuppressWarnings("unused")
public class ChunkListener implements Listener {
2020-07-10 17:32:07 +02:00
private final PlotAreaManager plotAreaManager;
private final int version;
Merge branch 'v6' into feature/v6/platform # Conflicts: # Bukkit/build.gradle # Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java # Bukkit/src/main/java/com/plotsquared/bukkit/listener/ChunkListener.java # Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java # Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java # Bukkit/src/main/java/com/plotsquared/bukkit/uuid/SquirrelIdUUIDService.java # Core/build.gradle # Core/src/main/java/com/plotsquared/core/PlotSquared.java # Core/src/main/java/com/plotsquared/core/command/Claim.java # Core/src/main/java/com/plotsquared/core/command/Debug.java # Core/src/main/java/com/plotsquared/core/command/DebugExec.java # Core/src/main/java/com/plotsquared/core/command/Purge.java # Core/src/main/java/com/plotsquared/core/command/Trim.java # Core/src/main/java/com/plotsquared/core/components/ComponentPresetManager.java # Core/src/main/java/com/plotsquared/core/database/SQLManager.java # Core/src/main/java/com/plotsquared/core/generator/HybridPlotWorld.java # Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java # Core/src/main/java/com/plotsquared/core/generator/SquarePlotManager.java # Core/src/main/java/com/plotsquared/core/generator/SquarePlotWorld.java # Core/src/main/java/com/plotsquared/core/listener/ProcessedWEExtent.java # Core/src/main/java/com/plotsquared/core/player/ConsolePlayer.java # Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java # Core/src/main/java/com/plotsquared/core/plot/Plot.java # Core/src/main/java/com/plotsquared/core/plot/PlotArea.java # Core/src/main/java/com/plotsquared/core/plot/PlotInventory.java # Core/src/main/java/com/plotsquared/core/plot/expiration/ExpireManager.java # Core/src/main/java/com/plotsquared/core/plot/expiration/PlotAnalysis.java # Core/src/main/java/com/plotsquared/core/plot/flag/FlagContainer.java # Core/src/main/java/com/plotsquared/core/plot/flag/types/BlockTypeWrapper.java # Core/src/main/java/com/plotsquared/core/plot/message/PlotMessage.java # Core/src/main/java/com/plotsquared/core/util/LegacyConverter.java # Core/src/main/java/com/plotsquared/core/util/MainUtil.java # Core/src/main/java/com/plotsquared/core/util/RegionManager.java # Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java # Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java
2020-07-14 19:05:05 +02:00
private RefMethod methodGetHandleChunk;
private RefMethod methodGetHandleWorld;
private RefField mustSave;
/*
private RefMethod methodGetFullChunk;
private RefMethod methodGetBukkitChunk;
private RefMethod methodGetChunkProvider;
private RefMethod methodGetVisibleMap;
private RefField worldServer;
private RefField playerChunkMap;
private RefField updatingChunks;
private RefField visibleChunks;
*/
private Chunk lastChunk;
2018-08-10 17:01:10 +02:00
private boolean ignoreUnload = false;
private boolean isTrueForNotSave = true;
@Inject
public ChunkListener(final @NonNull PlotAreaManager plotAreaManager) {
2020-07-10 17:32:07 +02:00
this.plotAreaManager = plotAreaManager;
version = PlotSquared.platform().serverVersion()[1];
if (!Settings.Chunk_Processor.AUTO_TRIM) {
2015-09-13 06:04:31 +02:00
return;
}
try {
RefClass classCraftWorld = getRefClass("{cb}.CraftWorld");
this.methodGetHandleWorld = classCraftWorld.getMethod("getHandle");
RefClass classCraftChunk = getRefClass("{cb}.CraftChunk");
this.methodGetHandleChunk = classCraftChunk.getMethod("getHandle");
try {
if (version < 17) {
RefClass classChunk = getRefClass("{nms}.Chunk");
if (version == 13) {
this.mustSave = classChunk.getField("mustSave");
this.isTrueForNotSave = false;
} else {
this.mustSave = classChunk.getField("mustNotSave");
}
} else {
2021-08-17 23:43:58 +02:00
RefClass classChunk = getRefClass("net.minecraft.world.level.chunk.Chunk");
this.mustSave = classChunk.getField("mustNotSave");
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
} catch (Throwable ignored) {
Settings.Chunk_Processor.AUTO_TRIM = false;
}
for (World world : Bukkit.getWorlds()) {
world.setAutoSave(false);
}
if (version > 13) {
return;
}
TaskManager.runTaskRepeat(() -> {
try {
HashSet<Chunk> toUnload = new HashSet<>();
for (World world : Bukkit.getWorlds()) {
String worldName = world.getName();
2020-07-10 17:32:07 +02:00
if (!this.plotAreaManager.hasPlotArea(worldName)) {
continue;
}
Object craftWorld = methodGetHandleWorld.of(world).call();
if (version == 13) {
Object chunkMap = craftWorld.getClass().getDeclaredMethod("getPlayerChunkMap").invoke(craftWorld);
Method methodIsChunkInUse =
chunkMap.getClass().getDeclaredMethod("isChunkInUse", int.class, int.class);
Chunk[] chunks = world.getLoadedChunks();
for (Chunk chunk : chunks) {
if ((boolean) methodIsChunkInUse.invoke(chunkMap, chunk.getX(), chunk.getZ())) {
continue;
}
int x = chunk.getX();
int z = chunk.getZ();
if (!shouldSave(worldName, x, z)) {
unloadChunk(worldName, chunk, false);
continue;
}
toUnload.add(chunk);
}
}
}
if (toUnload.isEmpty()) {
return;
}
long start = System.currentTimeMillis();
for (Chunk chunk : toUnload) {
if (System.currentTimeMillis() - start > 5) {
return;
}
2019-04-23 19:45:24 +02:00
chunk.unload(true);
}
} catch (Throwable e) {
e.printStackTrace();
}
2020-07-15 13:18:09 +02:00
}, TaskTime.ticks(1L));
}
2016-03-23 02:41:37 +01:00
public boolean unloadChunk(String world, Chunk chunk, boolean safe) {
if (safe && shouldSave(world, chunk.getX(), chunk.getZ())) {
return false;
}
Object c = this.methodGetHandleChunk.of(chunk).call();
RefField.RefExecutor field = this.mustSave.of(c);
if ((Boolean) field.get() != isTrueForNotSave) {
field.set(isTrueForNotSave);
if (chunk.isLoaded()) {
ignoreUnload = true;
2019-04-23 19:45:24 +02:00
chunk.unload(false);
ignoreUnload = false;
}
}
return true;
}
public boolean shouldSave(String world, int chunkX, int chunkZ) {
int x = chunkX << 4;
int z = chunkZ << 4;
2016-03-23 02:41:37 +01:00
int x2 = x + 15;
int z2 = z + 15;
Location loc = Location.at(world, x, 1, z);
PlotArea plotArea = plotAreaManager.getPlotArea(loc);
if (plotArea != null) {
Plot plot = plotArea.getPlot(loc);
if (plot != null && plot.hasOwner()) {
return true;
}
2015-09-13 06:04:31 +02:00
}
loc = Location.at(world, x2, 1, z2);
plotArea = plotAreaManager.getPlotArea(loc);
if (plotArea != null) {
Plot plot = plotArea.getPlot(loc);
if (plot != null && plot.hasOwner()) {
return true;
}
2015-09-13 06:04:31 +02:00
}
loc = Location.at(world, x2, 1, z);
plotArea = plotAreaManager.getPlotArea(loc);
if (plotArea != null) {
Plot plot = plotArea.getPlot(loc);
if (plot != null && plot.hasOwner()) {
return true;
}
2015-09-13 06:04:31 +02:00
}
loc = Location.at(world, x, 1, z2);
plotArea = plotAreaManager.getPlotArea(loc);
if (plotArea != null) {
Plot plot = plotArea.getPlot(loc);
if (plot != null && plot.hasOwner()) {
return true;
}
}
loc = Location.at(world, x + 7, 1, z + 7);
plotArea = plotAreaManager.getPlotArea(loc);
if (plotArea == null) {
return false;
2015-09-13 06:04:31 +02:00
}
Plot plot = plotArea.getPlot(loc);
2016-06-02 17:38:47 +02:00
return plot != null && plot.hasOwner();
}
@EventHandler
public void onChunkUnload(ChunkUnloadEvent event) {
if (ignoreUnload) {
return;
}
2019-05-11 05:00:11 +02:00
Chunk chunk = event.getChunk();
if (Settings.Chunk_Processor.AUTO_TRIM) {
2016-03-23 02:41:37 +01:00
String world = chunk.getWorld().getName();
if ((!Settings.Enabled_Components.WORLDS || !SinglePlotArea.isSinglePlotWorld(world)) && this.plotAreaManager.hasPlotArea(
world)) {
if (unloadChunk(world, chunk, true)) {
2015-09-13 06:04:31 +02:00
return;
}
}
}
2015-09-13 06:04:31 +02:00
if (processChunk(event.getChunk(), true)) {
2019-05-11 05:00:11 +02:00
chunk.setForceLoaded(true);
2015-04-07 16:47:02 +02:00
}
}
2018-08-10 17:01:10 +02:00
@EventHandler
public void onChunkLoad(ChunkLoadEvent event) {
2015-04-07 16:47:02 +02:00
processChunk(event.getChunk(), false);
}
2018-08-10 17:01:10 +02:00
@EventHandler(priority = EventPriority.LOWEST)
public void onItemSpawn(ItemSpawnEvent event) {
2016-03-23 02:41:37 +01:00
Item entity = event.getEntity();
PaperLib.getChunkAtAsync(event.getLocation()).thenAccept(chunk -> {
if (chunk == this.lastChunk) {
event.getEntity().remove();
event.setCancelled(true);
return;
}
2020-07-10 17:32:07 +02:00
if (!this.plotAreaManager.hasPlotArea(chunk.getWorld().getName())) {
return;
}
Entity[] entities = chunk.getEntities();
if (entities.length > Settings.Chunk_Processor.MAX_ENTITIES) {
event.getEntity().remove();
event.setCancelled(true);
this.lastChunk = chunk;
} else {
this.lastChunk = null;
}
});
2015-04-26 10:51:13 +02:00
}
2018-08-10 17:01:10 +02:00
2015-09-11 12:09:22 +02:00
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
2016-03-23 02:41:37 +01:00
public void onBlockPhysics(BlockPhysicsEvent event) {
if (Settings.Chunk_Processor.DISABLE_PHYSICS) {
2015-07-18 13:18:45 +02:00
event.setCancelled(true);
}
}
2018-08-10 17:01:10 +02:00
2015-09-11 12:09:22 +02:00
@EventHandler(priority = EventPriority.LOWEST)
2016-03-23 02:41:37 +01:00
public void onEntitySpawn(CreatureSpawnEvent event) {
LivingEntity entity = event.getEntity();
PaperLib.getChunkAtAsync(event.getLocation()).thenAccept(chunk -> {
if (chunk == this.lastChunk) {
event.getEntity().remove();
event.setCancelled(true);
return;
}
2020-07-10 17:32:07 +02:00
if (!this.plotAreaManager.hasPlotArea(chunk.getWorld().getName())) {
return;
}
Entity[] entities = chunk.getEntities();
if (entities.length > Settings.Chunk_Processor.MAX_ENTITIES) {
event.getEntity().remove();
event.setCancelled(true);
this.lastChunk = chunk;
} else {
this.lastChunk = null;
}
});
2015-04-18 15:47:13 +02:00
}
2016-03-23 18:16:05 +01:00
private void cleanChunk(final Chunk chunk) {
final int currentIndex = TaskManager.index.incrementAndGet();
2020-07-15 13:18:09 +02:00
PlotSquaredTask task = TaskManager.runTaskRepeat(() -> {
if (!chunk.isLoaded()) {
2020-07-17 17:39:41 +02:00
Objects.requireNonNull(TaskManager.removeTask(currentIndex)).cancel();
2019-04-23 19:45:24 +02:00
chunk.unload(true);
return;
}
BlockState[] tiles = chunk.getTileEntities();
if (tiles.length == 0) {
2020-07-17 17:39:41 +02:00
Objects.requireNonNull(TaskManager.removeTask(currentIndex)).cancel();
2019-04-23 19:45:24 +02:00
chunk.unload(true);
return;
}
long start = System.currentTimeMillis();
int i = 0;
while (System.currentTimeMillis() - start < 250) {
if (i >= tiles.length - Settings.Chunk_Processor.MAX_TILES) {
2020-07-17 17:39:41 +02:00
Objects.requireNonNull(TaskManager.removeTask(currentIndex)).cancel();
2019-04-23 19:45:24 +02:00
chunk.unload(true);
2019-02-04 16:18:50 +01:00
return;
}
tiles[i].getBlock().setType(Material.AIR, false);
i++;
2015-04-07 16:47:02 +02:00
}
2020-07-15 13:18:09 +02:00
}, TaskTime.ticks(5L));
2020-07-17 17:39:41 +02:00
TaskManager.addTask(task, currentIndex);
}
2016-03-23 02:41:37 +01:00
public boolean processChunk(Chunk chunk, boolean unload) {
2020-07-10 17:32:07 +02:00
if (!this.plotAreaManager.hasPlotArea(chunk.getWorld().getName())) {
2015-09-13 06:04:31 +02:00
return false;
}
2016-03-23 02:41:37 +01:00
Entity[] entities = chunk.getEntities();
BlockState[] tiles = chunk.getTileEntities();
if (entities.length > Settings.Chunk_Processor.MAX_ENTITIES) {
int toRemove = entities.length - Settings.Chunk_Processor.MAX_ENTITIES;
int index = 0;
while (toRemove > 0 && index < entities.length) {
final Entity entity = entities[index++];
if (!(entity instanceof Player)) {
entity.remove();
toRemove--;
2015-04-26 10:51:13 +02:00
}
2015-04-09 07:41:14 +02:00
}
}
if (tiles.length > Settings.Chunk_Processor.MAX_TILES) {
2015-09-13 06:04:31 +02:00
if (unload) {
2015-04-07 16:47:02 +02:00
cleanChunk(chunk);
return true;
}
for (int i = 0; i < (tiles.length - Settings.Chunk_Processor.MAX_TILES); i++) {
tiles[i].getBlock().setType(Material.AIR, false);
}
}
2015-04-07 16:47:02 +02:00
return false;
}
}