Package com.bergerkiller.bukkit.common.utils

Source Code of com.bergerkiller.bukkit.common.utils.ChunkUtil

package com.bergerkiller.bukkit.common.utils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;

import net.minecraft.server.Block;
import net.minecraft.server.Chunk;
import net.minecraft.server.ChunkCoordIntPair;
import net.minecraft.server.ChunkSection;
import net.minecraft.server.WorldServer;

import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.util.LongHash;
import org.bukkit.craftbukkit.util.LongHashSet;
import org.bukkit.craftbukkit.util.LongObjectHashMap;
import org.bukkit.entity.Player;

import com.bergerkiller.bukkit.common.collections.FilteredCollection;
import com.bergerkiller.bukkit.common.collections.List2D;
import com.bergerkiller.bukkit.common.conversion.Conversion;
import com.bergerkiller.bukkit.common.conversion.ConversionPairs;
import com.bergerkiller.bukkit.common.conversion.util.ConvertingList;
import com.bergerkiller.bukkit.common.internal.CommonNMS;
import com.bergerkiller.bukkit.common.internal.CommonPlugin;
import com.bergerkiller.bukkit.common.reflection.FieldAccessor;
import com.bergerkiller.bukkit.common.reflection.SafeField;
import com.bergerkiller.bukkit.common.reflection.classes.ChunkProviderServerRef;
import com.bergerkiller.bukkit.common.reflection.classes.ChunkRef;
import com.bergerkiller.bukkit.common.reflection.classes.ChunkRegionLoaderRef;
import com.bergerkiller.bukkit.common.reflection.classes.ChunkSectionRef;
import com.bergerkiller.bukkit.common.reflection.classes.EntityPlayerRef;
import com.bergerkiller.bukkit.common.reflection.classes.WorldServerRef;

/**
* Contains utilities to get and set chunks of a world
*/
public class ChunkUtil {
  private static boolean canUseLongObjectHashMap = CommonUtil.getCBClass("util.LongObjectHashMap") != null;
  private static boolean canUseLongHashSet = CommonUtil.getCBClass("util.LongHashSet") != null;
  public static final FieldAccessor<List<Object>> chunkListField;
  static {
    Field f;
    try {
      f = ChunkProviderServerRef.TEMPLATE.getType().getField("chunkList");
    } catch (Throwable t) {
      f = null;
    }
    chunkListField = f == null ? null : new SafeField<List<Object>>(f);
  }

  /**
   * Gets the height of a given column in a chunk
   *
   * @param chunk the column is in
   * @param x - coordinate of the block column
   * @param z - coordinate of the block column
   * @return column height
   */
  public static int getHeight(org.bukkit.Chunk chunk, int x, int z) {
    return ChunkRef.getHeight(CommonNMS.getNative(chunk), x, z);
  }

  /**
   * Gets the block light level
   *
   * @param chunk the block is in
   * @param x - coordinate of the block
   * @param y - coordinate of the block
   * @param z - coordinate of the block
   * @return Block light level
   */
  public static int getBlockLight(org.bukkit.Chunk chunk, int x, int y, int z) {
    return ChunkRef.getBlockLight(CommonNMS.getNative(chunk), x, y, z);
  }

  /**
   * Gets the sky light level
   *
   * @param chunk the block is in
   * @param x - coordinate of the block
   * @param y - coordinate of the block
   * @param z - coordinate of the block
   * @return Sky light level
   */
  public static int getSkyLight(org.bukkit.Chunk chunk, int x, int y, int z) {
    return ChunkRef.getSkyLight(CommonNMS.getNative(chunk), x, y, z);
  }

  /**
   * Gets the block data
   *
   * @param chunk the block is in
   * @param x - coordinate of the block
   * @param y - coordinate of the block
   * @param z - coordinate of the block
   * @return block data
   */
  public static int getBlockData(org.bukkit.Chunk chunk, int x, int y, int z) {
    return ChunkRef.getData(CommonNMS.getNative(chunk), x, y, z);
  }

  /**
   * Gets the block type Id
   *
   * @param chunk the block is in
   * @param x - coordinate of the block
   * @param y - coordinate of the block
   * @param z - coordinate of the block
   * @return block type Id
   */
  @Deprecated
  public static int getBlockTypeId(org.bukkit.Chunk chunk, int x, int y, int z) {
    return ChunkRef.getTypeId(CommonNMS.getNative(chunk), x, y, z);
  }

  /**
   * Gets the block type Id
   *
   * @param chunk the block is in
   * @param x - coordinate of the block
   * @param y - coordinate of the block
   * @param z - coordinate of the block
   * @return block type
   */
  @SuppressWarnings("deprecation")
  public static Material getBlockType(org.bukkit.Chunk chunk, int x, int y, int z) {
    return MaterialUtil.getType(ChunkRef.getTypeId(CommonNMS.getNative(chunk), x, y, z));
  }

  /**
   * Sets a block type id and data without causing physics or lighting updates
   *
   * @param chunk the block is in
   * @param x - coordinate of the block
   * @param y - coordinate of the block
   * @param z - coordinate of the block
   * @param typeId to set to
   * @param data to set to
   */
  public static void setBlockFast(org.bukkit.Chunk chunk, int x, int y, int z, int typeId, int data) {
    if (y < 0 || y >= chunk.getWorld().getMaxHeight()) {
      return;
    }
    Object[] sections = ChunkRef.getSections(CommonNMS.getNative(chunk));
    final int secIndex = y >> 4;
    Object section = sections[secIndex];
    if (section == null) {
      section = sections[secIndex] = new ChunkSection(y >> 4 << 4, !CommonNMS.getNative(chunk.getWorld()).worldProvider.g);
    }
    ChunkSectionRef.setTypeId(section, x, y, z, typeId);
    ChunkSectionRef.setData(section, x, y, z, data);
  }

  /**
   * Sets a block type id and data, causing physics and lighting updates
   *
   * @param chunk the block is in
   * @param x - coordinate of the block
   * @param y - coordinate of the block
   * @param z - coordinate of the block
   * @param typeId to set to
   * @param data to set to
   * @return True if a block got changed, False if not
   */
  @Deprecated
  public static boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, int typeId, int data) {
    return setBlock(chunk, x, y, z, MaterialUtil.getType(typeId), data);
  }

  /**
   * Sets a block type id and data, causing physics and lighting updates
   *
   * @param chunk the block is in
   * @param x - coordinate of the block
   * @param y - coordinate of the block
   * @param z - coordinate of the block
   * @param type to set to
   * @param data to set to
   * @return True if a block got changed, False if not
   */
  public static boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, Material type, int data) {
    boolean result = y >= 0 && y <= chunk.getWorld().getMaxHeight();
    WorldServer world = CommonNMS.getNative(chunk.getWorld());
    Block typeBlock = CommonNMS.getBlock(type);
    if (result) {
      result = ChunkRef.setBlock(Conversion.toChunkHandle.convert(chunk), x, y, z, typeBlock, data);
            world.methodProfiler.a("checkLight");
            world.z(x, y, z);
            world.methodProfiler.b();
    }
    if (result) {
      world.applyPhysics(x, y, z, typeBlock);
    }
    return result;
  }

  /**
   * Gets a live collection of all the entities in a chunk<br>
   * Changes to this collection are reflected back in the chunk
   *
   * @param chunk for which to get the entities
   * @return Live collection of entities in the chunk
   */
  public static List<org.bukkit.entity.Entity> getEntities(org.bukkit.Chunk chunk) {
    List<Object>[] entitySlices = ChunkRef.entitySlices.get(Conversion.toChunkHandle.convert(chunk));
    return new ConvertingList<org.bukkit.entity.Entity>(new List2D<Object>(entitySlices), ConversionPairs.entity);
  }

  /**
   * Checks if a chunk is about be loaded
   *
   * @param player who will receive the chunk
   * @param cx location for the chunk X
   * @param cz location for the chunk Z
   * @return chunk being loaded soon?
   */
  public static boolean isLoadRequested(Player player, int cx, int cz) {
    List<?> chunkQue = EntityPlayerRef.chunkQueue.get(Conversion.toEntityHandle.convert(player));
    for (Object location : chunkQue) {
      ChunkCoordIntPair loc = (ChunkCoordIntPair) location;
      if (loc.x == cx && loc.z == cz) {
        return true;
      }
    }
    return false;
  }

  /**
   * Gets whether a given chunk is readily available.
   * If this method returns False, the chunk is not yet generated.
   *
   * @param world the chunk is in
   * @param x - coordinate of the chunk
   * @param z - coordinate of the chunk
   * @return True if the chunk can be obtained without generating it, False if not
   */
  public static boolean isChunkAvailable(World world, int x, int z) {
    Object cps = WorldServerRef.chunkProviderServer.get(Conversion.toWorldHandle.convert(world));
    if (ChunkProviderServerRef.isChunkLoaded.invoke(cps, x, z)) {
      // Chunk is loaded into memory, True
      return true;
    } else {
      Object chunkLoader = ChunkProviderServerRef.chunkLoader.get(cps);
      if (ChunkRegionLoaderRef.TEMPLATE.isInstance(chunkLoader)) {
        // Chunk can be loaded from file
        return ChunkRegionLoaderRef.chunkExists(chunkLoader, world, x, z);
      } else {
        // Unable to find out...
        return false;
      }
    }
  }

  /**
   * Gets all the chunks loaded on a given world
   *
   * @param world to get the loaded chunks from
   * @return Loaded chunks
   */
  @SuppressWarnings("rawtypes")
  public static Collection<org.bukkit.Chunk> getChunks(World world) {
    // ChunkList field (if available)
    if (chunkListField != null) {
      Object cps = CommonNMS.getNative(world).chunkProviderServer;
      List<Object> rawChunkList = chunkListField.get(cps);
      Collection<org.bukkit.Chunk> chunkList = CommonNMS.getChunks(rawChunkList);
      return FilteredCollection.createNullFilter(chunkList);
    }
    // LongObjectHashMap mirror
    if (canUseLongObjectHashMap) {
      Object chunks = ChunkProviderServerRef.chunks.get(CommonNMS.getNative(world).chunkProviderServer);
      if (chunks != null) {
        try {
          if (canUseLongObjectHashMap && chunks instanceof LongObjectHashMap) {
            return FilteredCollection.createNullFilter(CommonNMS.getChunks(((LongObjectHashMap) chunks).values()));
          }
        } catch (Throwable t) {
          canUseLongObjectHashMap = false;
          CommonPlugin.getInstance().log(Level.WARNING, "Failed to access chunks using CraftBukkit's long object hashmap, support disabled");
          CommonUtil.filterStackTrace(t).printStackTrace();
        }
      }
    }
    // Bukkit alternative
    return FilteredCollection.createNullFilter(Arrays.asList(world.getLoadedChunks()));
  }

  /**
   * Gets a chunk from a world without loading or generating it
   *
   * @param world to obtain the chunk from
   * @param x - coordinate of the chunk
   * @param z - coordinate of the chunk
   * @return The chunk, or null if it is not loaded
   */
  @SuppressWarnings("rawtypes")
  public static org.bukkit.Chunk getChunk(World world, final int x, final int z) {
    final long key = LongHash.toLong(x, z);
    Object chunks = ChunkProviderServerRef.chunks.get(CommonNMS.getNative(world).chunkProviderServer);
    if (chunks != null) {
      if (canUseLongObjectHashMap && chunks instanceof LongObjectHashMap) {
        try {
          return CommonNMS.getChunk(((Chunk) ((LongObjectHashMap) chunks).get(key)));
        } catch (Throwable t) {
          canUseLongObjectHashMap = false;
          CommonPlugin.getInstance().log(Level.WARNING, "Failed to access chunks using CraftBukkit's long object hashmap, support disabled");
          CommonUtil.filterStackTrace(t).printStackTrace();
        }
      }
    }
    // Bukkit alternative
    if (world.isChunkLoaded(x, z)) {
      return world.getChunkAt(x, z);
    } else {
      return null;
    }
  }

  /**
   * Gets, loads or generated a chunk without loading or generating it on the main thread.
   * Allows the lazy-loading of chunks without locking the server.
   *
   * @param world to obtain the chunk from
   * @param x - coordinate of the chunk
   * @param z - coordinate of the chunk
   * @param runnable to execute once the chunk is loaded or obtained
   */
  public static void getChunkAsync(World world, final int x, final int z, Runnable runnable) {
    CommonNMS.getNative(world).chunkProviderServer.getChunkAt(x, z, runnable);
  }

  /**
   * Sets a given chunk coordinate to contain the chunk specified
   *
   * @param world to set the chunk in
   * @param x - coordinate of the chunk
   * @param z - coordinate of the chunk
   * @param chunk to set to (use null to remove)
   */
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public static void setChunk(World world, final int x, final int z, final org.bukkit.Chunk chunk) {
    final Chunk handle = CommonNMS.getNative(chunk);
    if (handle != null && handle.bukkitChunk == null) {
      throw new RuntimeException("Can not put a chunk that has no BukkitChunk");
    }
    if (canUseLongObjectHashMap) {
      Object cps = CommonNMS.getNative(world).chunkProviderServer;
      Object chunks = ChunkProviderServerRef.chunks.get(cps);
      if (chunks != null) {
        final long key = LongHash.toLong(x, z);
        try {
          if (canUseLongObjectHashMap && chunks instanceof LongObjectHashMap) {
            if (handle == null) {
              // Remove the chunk
              Object removed = ((LongObjectHashMap) chunks).remove(key);
              if (removed != null && chunkListField != null) {
                chunkListField.get(cps).remove(removed);
              }
            } else {
              // Add the chunk
              Object oldChunk = ((LongObjectHashMap) chunks).put(key, handle);
              if (chunkListField != null) {
                List<Object> chunkList = chunkListField.get(cps);
                if (oldChunk != null) {
                  chunkList.remove(oldChunk);
                }
                chunkList.add(handle);
              }
            }
            return;
          }
        } catch (Throwable t) {
          canUseLongObjectHashMap = false;
          CommonPlugin.LOGGER.log(Level.WARNING, "Failed to access chunks using CraftBukkit's long object hashmap, support disabled");
          CommonUtil.filterStackTrace(t).printStackTrace();
        }
      }
    }
    throw new RuntimeException("Failed to set chunk using a known method");
  }

  /**
   * Saves a single chunk to disk
   *
   * @param chunk to save
   */
  public static void saveChunk(org.bukkit.Chunk chunk) {
    CommonNMS.getNative(chunk.getWorld()).chunkProviderServer.saveChunk(CommonNMS.getNative(chunk));
  }

  /**
   * Gets whether a chunk needs to be saved
   *
   * @param chunk to check
   * @return True if it needs to be saved, False if not
   */
  public static boolean needsSaving(org.bukkit.Chunk chunk) {
    return ChunkRef.needsSaving(Conversion.toChunkHandle.convert(chunk));
  }

  /**
   * Sets whether a given chunk coordinate has to be unloaded
   *
   * @param world to set the unload request for
   * @param x - coordinate of the chunk
   * @param z - coordinate of the chunk
   * @param unload state to set to
   */
  public static void setChunkUnloading(World world, final int x, final int z, boolean unload) {
    if (canUseLongHashSet) {
      Object unloadQueue = ChunkProviderServerRef.unloadQueue.get(CommonNMS.getNative(world).chunkProviderServer);
      if (unloadQueue != null) {
        try {
          if (canUseLongHashSet && unloadQueue instanceof LongHashSet) {
            if (unload) {
              ((LongHashSet) unloadQueue).add(x, z);
            } else {
              ((LongHashSet) unloadQueue).remove(x, z);
            }
            return;
          }
        } catch (Throwable t) {
          canUseLongHashSet = false;
          CommonPlugin.LOGGER.log(Level.WARNING, "Failed to access chunks using CraftBukkit's long object hashmap, support disabled");
          CommonUtil.filterStackTrace(t).printStackTrace();
        }
      }
    }
    throw new RuntimeException("Failed to set unload queue using a known method");
  }

  /**
   * Obtains all the Block State tile entities available in a Chunk
   *
   * @param chunk to get the Block States for
   * @return collection of Block States (mutual)
   */
  public static Collection<BlockState> getBlockStates(org.bukkit.Chunk chunk) {
    return ConversionPairs.blockState.convertAll(CommonNMS.getNative(chunk).tileEntities.values());
  }

  /**
   * Adds an Entity to a Chunk
   *
   * @param chunk to add an entity to
   * @param entity to add
   */
  public static void addEntity(org.bukkit.Chunk chunk, org.bukkit.entity.Entity entity) {
    CommonNMS.getNative(chunk).a(CommonNMS.getNative(entity));
  }

  /**
   * Removes an Entity from a Chunk
   *
   * @param chunk to remove an entity from
   * @param entity to remove
   * @return True if the entity has been removed, False if not (not found)
   */
  @SuppressWarnings("unchecked")
  public static boolean removeEntity(org.bukkit.Chunk chunk, org.bukkit.entity.Entity entity) {
    final List<Object>[] slices = CommonNMS.getNative(chunk).entitySlices;
    final int sliceY = MathUtil.clamp(MathUtil.toChunk(EntityUtil.getLocY(entity)), 0, slices.length - 1);
    final Object handle = Conversion.toEntityHandle.convert(entity);
    if (slices[sliceY].remove(handle)) {
      return true;
    } else {
      for (int y = 0; y < slices.length; y++) {
        if (y != sliceY && slices[y].remove(handle)) {
          return true;
        }
      }
      return false;
    }
  }
}
TOP

Related Classes of com.bergerkiller.bukkit.common.utils.ChunkUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.