2020-04-11 02:19:18 +02:00
|
|
|
package com.plotsquared.bukkit.util;
|
2016-02-23 05:11:28 +01:00
|
|
|
|
2020-04-11 02:19:18 +02:00
|
|
|
import com.plotsquared.bukkit.player.BukkitPlayer;
|
|
|
|
import com.plotsquared.PlotSquared;
|
|
|
|
import com.plotsquared.location.Location;
|
|
|
|
import com.plotsquared.plot.Plot;
|
|
|
|
import com.plotsquared.player.PlotPlayer;
|
|
|
|
import com.plotsquared.util.ReflectionUtils.RefClass;
|
|
|
|
import com.plotsquared.util.ReflectionUtils.RefConstructor;
|
|
|
|
import com.plotsquared.util.ReflectionUtils.RefField;
|
|
|
|
import com.plotsquared.util.ReflectionUtils.RefMethod;
|
|
|
|
import com.plotsquared.util.tasks.TaskManager;
|
|
|
|
import com.plotsquared.util.uuid.UUIDHandler;
|
2019-11-04 18:44:23 +01:00
|
|
|
import com.sk89q.worldedit.math.BlockVector2;
|
2019-09-12 01:04:29 +02:00
|
|
|
import io.papermc.lib.PaperLib;
|
2019-02-04 15:02:21 +01:00
|
|
|
import org.bukkit.Bukkit;
|
|
|
|
import org.bukkit.Chunk;
|
|
|
|
import org.bukkit.World;
|
|
|
|
import org.bukkit.entity.Player;
|
|
|
|
|
2016-03-23 02:41:37 +01:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
2016-02-23 05:11:28 +01:00
|
|
|
import java.util.Map.Entry;
|
2019-02-04 15:02:21 +01:00
|
|
|
|
2020-04-11 02:19:18 +02:00
|
|
|
import static com.plotsquared.util.ReflectionUtils.getRefClass;
|
2018-08-10 17:01:10 +02:00
|
|
|
|
2016-02-23 05:11:28 +01:00
|
|
|
/**
|
2019-02-04 16:18:50 +01:00
|
|
|
* An utility that can be used to send chunks, rather than using bukkit code
|
|
|
|
* to do so (uses heavy NMS).
|
2016-02-23 05:11:28 +01:00
|
|
|
*/
|
|
|
|
public class SendChunk {
|
|
|
|
|
2019-02-04 15:02:21 +01:00
|
|
|
private final RefMethod methodGetHandlePlayer;
|
|
|
|
private final RefMethod methodGetHandleChunk;
|
|
|
|
private final RefConstructor mapChunk;
|
|
|
|
private final RefField connection;
|
|
|
|
private final RefMethod send;
|
|
|
|
private final RefMethod methodInitLighting;
|
2016-02-23 05:11:28 +01:00
|
|
|
|
2019-02-04 15:02:21 +01:00
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*/
|
|
|
|
public SendChunk() throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException {
|
|
|
|
RefClass classCraftPlayer = getRefClass("{cb}.entity.CraftPlayer");
|
|
|
|
this.methodGetHandlePlayer = classCraftPlayer.getMethod("getHandle");
|
|
|
|
RefClass classCraftChunk = getRefClass("{cb}.CraftChunk");
|
|
|
|
this.methodGetHandleChunk = classCraftChunk.getMethod("getHandle");
|
|
|
|
RefClass classChunk = getRefClass("{nms}.Chunk");
|
|
|
|
this.methodInitLighting = classChunk.getMethod("initLighting");
|
|
|
|
RefClass classMapChunk = getRefClass("{nms}.PacketPlayOutMapChunk");
|
2019-09-08 20:02:45 +02:00
|
|
|
this.mapChunk = classMapChunk.getConstructor(classChunk.getRealClass(), int.class);
|
2019-02-04 15:02:21 +01:00
|
|
|
RefClass classEntityPlayer = getRefClass("{nms}.EntityPlayer");
|
|
|
|
this.connection = classEntityPlayer.getField("playerConnection");
|
|
|
|
RefClass classPacket = getRefClass("{nms}.Packet");
|
|
|
|
RefClass classConnection = getRefClass("{nms}.PlayerConnection");
|
|
|
|
this.send = classConnection.getMethod("sendPacket", classPacket.getRealClass());
|
|
|
|
}
|
2016-02-23 05:11:28 +01:00
|
|
|
|
2016-03-23 02:41:37 +01:00
|
|
|
public void sendChunk(Collection<Chunk> input) {
|
2016-05-12 23:09:35 +02:00
|
|
|
HashSet<Chunk> chunks = new HashSet<>(input);
|
2016-03-23 02:41:37 +01:00
|
|
|
HashMap<String, ArrayList<Chunk>> map = new HashMap<>();
|
|
|
|
int view = Bukkit.getServer().getViewDistance();
|
|
|
|
for (Chunk chunk : chunks) {
|
|
|
|
String world = chunk.getWorld().getName();
|
2019-02-13 18:05:28 +01:00
|
|
|
ArrayList<Chunk> list = map.computeIfAbsent(world, k -> new ArrayList<>());
|
2016-02-23 05:11:28 +01:00
|
|
|
list.add(chunk);
|
2016-03-23 02:41:37 +01:00
|
|
|
Object c = this.methodGetHandleChunk.of(chunk).call();
|
|
|
|
this.methodInitLighting.of(c).call();
|
2016-02-23 05:11:28 +01:00
|
|
|
}
|
|
|
|
for (Entry<String, PlotPlayer> entry : UUIDHandler.getPlayers().entrySet()) {
|
|
|
|
PlotPlayer pp = entry.getValue();
|
2016-03-23 02:41:37 +01:00
|
|
|
Plot plot = pp.getCurrentPlot();
|
2016-06-02 17:38:47 +02:00
|
|
|
Location location = null;
|
2016-02-23 05:11:28 +01:00
|
|
|
String world;
|
|
|
|
if (plot != null) {
|
2017-03-23 01:10:29 +01:00
|
|
|
world = plot.getWorldName();
|
2016-02-23 05:11:28 +01:00
|
|
|
} else {
|
2016-06-02 17:38:47 +02:00
|
|
|
location = pp.getLocation();
|
|
|
|
world = location.getWorld();
|
2016-02-23 05:11:28 +01:00
|
|
|
}
|
2016-03-23 02:41:37 +01:00
|
|
|
ArrayList<Chunk> list = map.get(world);
|
2016-02-23 05:11:28 +01:00
|
|
|
if (list == null) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-06-02 17:38:47 +02:00
|
|
|
if (location == null) {
|
|
|
|
location = pp.getLocation();
|
2016-02-23 05:11:28 +01:00
|
|
|
}
|
2019-05-17 21:32:05 +02:00
|
|
|
int chunkX = location.getX() >> 4;
|
|
|
|
int chunkZ = location.getZ() >> 4;
|
2016-03-23 02:41:37 +01:00
|
|
|
Player player = ((BukkitPlayer) pp).player;
|
|
|
|
Object entity = this.methodGetHandlePlayer.of(player).call();
|
2016-02-23 05:11:28 +01:00
|
|
|
|
2019-02-04 15:02:21 +01:00
|
|
|
for (Chunk chunk : list) {
|
2019-05-17 21:32:05 +02:00
|
|
|
int dx = Math.abs(chunkX - chunk.getX());
|
|
|
|
int dz = Math.abs(chunkZ - chunk.getZ());
|
2019-02-04 15:02:21 +01:00
|
|
|
if ((dx > view) || (dz > view)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Object c = this.methodGetHandleChunk.of(chunk).call();
|
|
|
|
chunks.remove(chunk);
|
|
|
|
Object con = this.connection.of(entity).get();
|
|
|
|
Object packet = null;
|
|
|
|
try {
|
|
|
|
packet = this.mapChunk.create(c, 65535);
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
}
|
|
|
|
if (packet == null) {
|
|
|
|
PlotSquared.debug("Error with PacketPlayOutMapChunk reflection.");
|
|
|
|
}
|
|
|
|
this.send.of(con).call(packet);
|
|
|
|
}
|
2019-01-31 20:20:48 +01:00
|
|
|
}
|
2019-02-04 15:02:21 +01:00
|
|
|
for (final Chunk chunk : chunks) {
|
2019-02-13 18:05:28 +01:00
|
|
|
TaskManager.runTask(() -> {
|
|
|
|
try {
|
2019-04-23 19:45:24 +02:00
|
|
|
chunk.unload(true);
|
2019-02-13 18:05:28 +01:00
|
|
|
} catch (Throwable ignored) {
|
|
|
|
String worldName = chunk.getWorld().getName();
|
|
|
|
PlotSquared.debug(
|
|
|
|
"$4Could not save chunk: " + worldName + ';' + chunk.getX() + ";" + chunk
|
|
|
|
.getZ());
|
|
|
|
PlotSquared.debug("$3 - $4File may be open in another process (e.g. MCEdit)");
|
|
|
|
PlotSquared.debug("$3 - $4" + worldName + "/level.dat or " + worldName
|
|
|
|
+ "/level_old.dat may be corrupt (try repairing or removing these)");
|
2019-02-04 15:02:21 +01:00
|
|
|
}
|
|
|
|
});
|
2016-02-23 05:11:28 +01:00
|
|
|
}
|
2019-01-31 20:20:48 +01:00
|
|
|
}
|
|
|
|
|
2019-11-04 18:44:23 +01:00
|
|
|
public void sendChunk(String worldName, Collection<BlockVector2> chunkLocations) {
|
2019-02-04 15:02:21 +01:00
|
|
|
World myWorld = Bukkit.getWorld(worldName);
|
|
|
|
ArrayList<Chunk> chunks = new ArrayList<>();
|
2019-11-04 18:44:23 +01:00
|
|
|
for (BlockVector2 loc : chunkLocations) {
|
|
|
|
if (myWorld.isChunkLoaded(loc.getX(), loc.getZ())) {
|
|
|
|
PaperLib.getChunkAtAsync(myWorld, loc.getX(), loc.getZ()).thenAccept(chunks::add);
|
2019-02-04 15:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
sendChunk(chunks);
|
2016-02-23 05:11:28 +01:00
|
|
|
}
|
|
|
|
}
|