package com.plotsquared.bukkit.util; import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.object.ChunkLoc; import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.ReflectionUtils.RefClass; import com.intellectualcrafters.plot.util.ReflectionUtils.RefConstructor; import com.intellectualcrafters.plot.util.ReflectionUtils.RefField; import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod; /** * An utility that can be used to send chunks, rather than using bukkit code to do so (uses heavy NMS) * * @author Empire92 */ public class SendChunk { // Ref Class private final RefClass classWorld = getRefClass("{nms}.World"); private final RefClass classEntityPlayer = getRefClass("{nms}.EntityPlayer"); private final RefClass classChunkCoordIntPair = getRefClass("{nms}.ChunkCoordIntPair"); private final RefClass classCraftChunk = getRefClass("{cb}.CraftChunk"); private final RefClass classChunk = getRefClass("{nms}.Chunk"); private boolean v1_7_10 = PS.get().checkVersion(PS.get().IMP.getServerVersion(), 1, 7, 10) && !PS.get().checkVersion(PS.get().IMP.getServerVersion(), 1, 8, 0); // Ref Method private RefMethod methodGetHandle; // Ref Field private RefField chunkCoordIntPairQueue; private RefField players; private RefField locX; private RefField locZ; private RefField world; // Ref Constructor private RefConstructor ChunkCoordIntPairCon; /** * Constructor * * @throws NoSuchMethodException */ public SendChunk() throws NoSuchMethodException { methodGetHandle = classCraftChunk.getMethod("getHandle"); chunkCoordIntPairQueue = classEntityPlayer.getField("chunkCoordIntPairQueue"); players = classWorld.getField("players"); locX = classEntityPlayer.getField("locX"); locZ = classEntityPlayer.getField("locZ"); world = classChunk.getField("world"); ChunkCoordIntPairCon = classChunkCoordIntPair.getConstructor(int.class, int.class); } public void sendChunk(final Collection chunks) { int diffx, diffz; final int view = Bukkit.getServer().getViewDistance() << 4; for (final Chunk chunk : chunks) { if (!chunk.isLoaded()) { continue; } boolean unload = true; final Object c = methodGetHandle.of(chunk).call(); final Object w = world.of(c).get(); final Object p = players.of(w).get(); for (final Object ep : (List) p) { final int x = ((Double) locX.of(ep).get()).intValue(); final int z = ((Double) locZ.of(ep).get()).intValue(); diffx = Math.abs(x - (chunk.getX() << 4)); diffz = Math.abs(z - (chunk.getZ() << 4)); if ((diffx <= view) && (diffz <= view)) { unload = false; if (v1_7_10) { chunk.getWorld().refreshChunk(chunk.getX(), chunk.getZ()); chunk.load(true); } else { final Object pair = ChunkCoordIntPairCon.create(chunk.getX(), chunk.getZ()); final Object pq = chunkCoordIntPairQueue.of(ep).get(); ((List) pq).add(pair); } } } if (unload) { TaskManager.runTask(new Runnable() { @Override public void run() { try { chunk.unload(true, true); } catch (Exception e) { String worldname = chunk.getWorld().getName(); PS.debug("$4Could not save chunk: " + worldname + ";" + chunk.getX() + ";" + chunk.getZ()); PS.debug("$3 - $4File may be open in another process (e.g. MCEdit)"); PS.debug("$3 - $4" + worldname + "/level.dat or " + worldname + "level_old.dat may be corrupt (try repairing or removing these)"); } } }); } } } public void sendChunk(final String worldname, final List locs) { final World myworld = Bukkit.getWorld(worldname); final ArrayList chunks = new ArrayList<>(); for (final ChunkLoc loc : locs) { chunks.add(myworld.getChunkAt(loc.x, loc.z)); } sendChunk(chunks); } }