All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bukkit.craftbukkit.CraftWorld Maven / Gradle / Ivy

The newest version!
package org.bukkit.craftbukkit;

import com.google.common.base.Preconditions;
import net.minecraft.server.*;
import org.apache.commons.lang.Validate;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
import org.bukkit.WorldBorder;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.block.CraftBlock;
import org.bukkit.craftbukkit.block.CraftBlockState;
import org.bukkit.craftbukkit.entity.CraftItem;
import org.bukkit.craftbukkit.entity.CraftLightningStrike;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.metadata.BlockMetadataStore;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.util.LongHash;
import org.bukkit.entity.Entity;
import org.bukkit.entity.*;
import org.bukkit.entity.Item;
import org.bukkit.entity.minecart.PoweredMinecart;
import org.bukkit.entity.minecart.StorageMinecart;
import org.bukkit.entity.minecart.*;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.world.SpawnChangeEvent;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.messaging.StandardMessenger;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.util.*;

public class CraftWorld implements World {
  public static final int CUSTOM_DIMENSION_OFFSET = 10;
  private static final Random rand = new Random();
  private final WorldServer world;
  private final CraftServer server = (CraftServer) Bukkit.getServer();
  private final ChunkGenerator generator;
  private final List populators = new ArrayList();
  private final BlockMetadataStore blockMetadata = new BlockMetadataStore(this);
  // Spigot start
  private final Spigot spigot = new Spigot() {
    @Override
    public void playEffect(Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius) {
      Validate.notNull(location, "Location cannot be null");
      Validate.notNull(effect, "Effect cannot be null");
      Validate.notNull(location.getWorld(), "World cannot be null");
      Packet packet;
      if (effect.getType() != Effect.Type.PARTICLE) {
        int packetData = effect.getId();
        packet = new PacketPlayOutWorldEvent(packetData, new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()), id, false);
      } else {
        net.minecraft.server.EnumParticle particle = null;
        int[] extra = null;
        for (net.minecraft.server.EnumParticle p : net.minecraft.server.EnumParticle.values()) {
          if (effect.getName().startsWith(p.b().replace("_", ""))) {
            particle = p;
            if (effect.getData() != null) {
              if (effect.getData().equals(org.bukkit.Material.class)) {
                extra = new int[]{id};
              } else {
                extra = new int[]{(data << 12) | (id & 0xFFF)};
              }
            }
            break;
          }
        }
        if (extra == null) {
          extra = new int[0];
        }
        packet = new PacketPlayOutWorldParticles(particle, true, (float) location.getX(), (float) location.getY(), (float) location.getZ(), offsetX, offsetY, offsetZ, speed, particleCount, extra);
      }
      int distance;
      radius *= radius;
      for (Player player : getPlayers()) {
        if (((CraftPlayer) player).getHandle().playerConnection == null) {
          continue;
        }
        if (!location.getWorld().equals(player.getWorld())) {
          continue;
        }
        distance = (int) player.getLocation().distanceSquared(location);
        if (distance <= radius) {
          ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
        }
      }
    }

    @Override
    public void playEffect(Location location, Effect effect) {
      CraftWorld.this.playEffect(location, effect, 0);
    }

    @Override
    public LightningStrike strikeLightning(Location loc, boolean isSilent) {
      EntityLightning lightning = new EntityLightning(world, loc.getX(), loc.getY(), loc.getZ(), false, isSilent);
      world.strikeLightning(lightning);
      return new CraftLightningStrike(server, lightning);
    }

    @Override
    public LightningStrike strikeLightningEffect(Location loc, boolean isSilent) {
      EntityLightning lightning = new EntityLightning(world, loc.getX(), loc.getY(), loc.getZ(), true, isSilent);
      world.strikeLightning(lightning);
      return new CraftLightningStrike(server, lightning);
    }
  };
  private WorldBorder worldBorder;
  private Environment environment;
  private int monsterSpawn = -1;
  private int animalSpawn = -1;
  private int waterAnimalSpawn = -1;
  private int ambientSpawn = -1;
  private int chunkLoadCount = 0;
  private int chunkGCTickCount;

  public CraftWorld(WorldServer world, ChunkGenerator gen, Environment env) {
    this.world = world;
    this.generator = gen;

    environment = env;

    if (server.chunkGCPeriod > 0) {
      chunkGCTickCount = rand.nextInt(server.chunkGCPeriod);
    }
  }

  private static void randomLocationWithinBlock(Location loc, double xs, double ys, double zs) {
    double prevX = loc.getX();
    double prevY = loc.getY();
    double prevZ = loc.getZ();
    loc.add(xs, ys, zs);
    if (loc.getX() < Math.floor(prevX)) {
      loc.setX(Math.floor(prevX));
    }
    if (loc.getX() >= Math.ceil(prevX)) {
      loc.setX(Math.ceil(prevX - 0.01));
    }
    if (loc.getY() < Math.floor(prevY)) {
      loc.setY(Math.floor(prevY));
    }
    if (loc.getY() >= Math.ceil(prevY)) {
      loc.setY(Math.ceil(prevY - 0.01));
    }
    if (loc.getZ() < Math.floor(prevZ)) {
      loc.setZ(Math.floor(prevZ));
    }
    if (loc.getZ() >= Math.ceil(prevZ)) {
      loc.setZ(Math.ceil(prevZ - 0.01));
    }
  }

  public Block getBlockAt(int x, int y, int z) {
    return getChunkAt(x >> 4, z >> 4).getBlock(x & 0xF, y, z & 0xF);
  }

  public int getBlockTypeIdAt(int x, int y, int z) {
    return CraftMagicNumbers.getId(world.getType(new BlockPosition(x, y, z)).getBlock());
  }

  public int getHighestBlockYAt(int x, int z) {
    if (!isChunkLoaded(x >> 4, z >> 4)) {
      loadChunk(x >> 4, z >> 4);
    }

    return world.getHighestBlockYAt(new BlockPosition(x, 0, z)).getY();
  }

  public Location getSpawnLocation() {
    BlockPosition spawn = world.getSpawn();
    return new Location(this, spawn.getX(), spawn.getY(), spawn.getZ());
  }

  public boolean setSpawnLocation(int x, int y, int z) {
    try {
      Location previousLocation = getSpawnLocation();
      world.worldData.setSpawn(new BlockPosition(x, y, z));

      // Notify anyone who's listening.
      SpawnChangeEvent event = new SpawnChangeEvent(this, previousLocation);
      server.getPluginManager().callEvent(event);

      return true;
    } catch (Exception e) {
      return false;
    }
  }

  public Chunk getChunkAt(int x, int z) {
    return this.world.chunkProviderServer.getChunkAt(x, z).bukkitChunk;
  }

  public Chunk getChunkAt(Block block) {
    return getChunkAt(block.getX() >> 4, block.getZ() >> 4);
  }

  public boolean isChunkLoaded(int x, int z) {
    return world.chunkProviderServer.isChunkLoaded(x, z);
  }

  public Chunk[] getLoadedChunks() {
    Object[] chunks = world.chunkProviderServer.chunks.values().toArray();
    org.bukkit.Chunk[] craftChunks = new CraftChunk[chunks.length];

    for (int i = 0; i < chunks.length; i++) {
      net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) chunks[i];
      craftChunks[i] = chunk.bukkitChunk;
    }

    return craftChunks;
  }

  public void loadChunk(int x, int z) {
    loadChunk(x, z, true);
  }

  public boolean unloadChunk(Chunk chunk) {
    return unloadChunk(chunk.getX(), chunk.getZ());
  }

  public boolean unloadChunk(int x, int z) {
    return unloadChunk(x, z, true);
  }

  public boolean unloadChunk(int x, int z, boolean save) {
    return unloadChunk(x, z, save, false);
  }

  public boolean unloadChunkRequest(int x, int z) {
    return unloadChunkRequest(x, z, true);
  }

  public boolean unloadChunkRequest(int x, int z, boolean safe) {
    org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
    if (safe && isChunkInUse(x, z)) {
      return false;
    }

    world.chunkProviderServer.queueUnload(x, z);

    return true;
  }

  public boolean unloadChunk(int x, int z, boolean save, boolean safe) {
    org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
    if (safe && isChunkInUse(x, z)) {
      return false;
    }

    net.minecraft.server.Chunk chunk = world.chunkProviderServer.getOrCreateChunk(x, z);
    if (chunk.mustSave) {   // If chunk had previously been queued to save, must do save to avoid loss of that data
      save = true;
    }

    chunk.removeEntities(); // Always remove entities - even if discarding, need to get them out of world table

    if (save && !(chunk instanceof EmptyChunk)) {
      world.chunkProviderServer.saveChunk(chunk);
      world.chunkProviderServer.saveChunkNOP(chunk);
    }

    world.chunkProviderServer.unloadQueue.remove(x, z);
    world.chunkProviderServer.chunks.remove(LongHash.toLong(x, z));

    return true;
  }

  public boolean regenerateChunk(int x, int z) {
    unloadChunk(x, z, false, false);

    world.chunkProviderServer.unloadQueue.remove(x, z);

    net.minecraft.server.Chunk chunk = null;

    if (world.chunkProviderServer.chunkProvider == null) {
      chunk = world.chunkProviderServer.emptyChunk;
    } else {
      chunk = world.chunkProviderServer.chunkProvider.getOrCreateChunk(x, z);
    }

    chunkLoadPostProcess(chunk, x, z);

    refreshChunk(x, z);

    return chunk != null;
  }

  public boolean refreshChunk(int x, int z) {
    if (!isChunkLoaded(x, z)) {
      return false;
    }

    int px = x << 4;
    int pz = z << 4;

    // If there are more than 64 updates to a chunk at once, it will update all 'touched' sections within the chunk
    // And will include biome data if all sections have been 'touched'
    // This flags 65 blocks distributed across all the sections of the chunk, so that everything is sent, including biomes
    int height = getMaxHeight() / 16;
    for (int idx = 0; idx < 64; idx++) {
      world.notify(new BlockPosition(px + (idx / height), ((idx % height) * 16), pz));
    }
    world.notify(new BlockPosition(px + 15, (height * 16) - 1, pz + 15));

    return true;
  }

  public boolean isChunkInUse(int x, int z) {
    return world.getPlayerChunkMap().isChunkInUse(x, z);
  }

  public boolean loadChunk(int x, int z, boolean generate) {
    org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
    chunkLoadCount++;
    if (generate) {
      // Use the default variant of loadChunk when generate == true.
      return world.chunkProviderServer.getChunkAt(x, z) != null;
    }

    world.chunkProviderServer.unloadQueue.remove(x, z);
    net.minecraft.server.Chunk chunk = world.chunkProviderServer.chunks.get(LongHash.toLong(x, z));

    if (chunk == null) {
      world.timings.syncChunkLoadTimer.startTiming(); // Spigot
      chunk = world.chunkProviderServer.loadChunk(x, z);

      chunkLoadPostProcess(chunk, x, z);
      world.timings.syncChunkLoadTimer.stopTiming(); // Spigot
    }
    return chunk != null;
  }

  private void chunkLoadPostProcess(net.minecraft.server.Chunk chunk, int cx, int cz) {
    if (chunk != null) {
      world.chunkProviderServer.chunks.put(LongHash.toLong(cx, cz), chunk);

      chunk.addEntities();

      // Update neighbor counts
      for (int x = -2; x < 3; x++) {
        for (int z = -2; z < 3; z++) {
          if (x == 0 && z == 0) {
            continue;
          }

          net.minecraft.server.Chunk neighbor = world.chunkProviderServer.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z);
          if (neighbor != null) {
            neighbor.setNeighborLoaded(-x, -z);
            chunk.setNeighborLoaded(x, z);
          }
        }
      }
      // CraftBukkit end

      chunk.loadNearby(world.chunkProviderServer, world.chunkProviderServer, cx, cz);
    }
  }

  public boolean isChunkLoaded(Chunk chunk) {
    return isChunkLoaded(chunk.getX(), chunk.getZ());
  }

  public void loadChunk(Chunk chunk) {
    loadChunk(chunk.getX(), chunk.getZ());
    ((CraftChunk) getChunkAt(chunk.getX(), chunk.getZ())).getHandle().bukkitChunk = chunk;
  }

  public WorldServer getHandle() {
    return world;
  }

  public org.bukkit.entity.Item dropItem(Location loc, ItemStack item) {
    Validate.notNull(item, "Cannot drop a Null item.");
    Validate.isTrue(item.getTypeId() != 0, "Cannot drop AIR.");
    EntityItem entity = new EntityItem(world, loc.getX(), loc.getY(), loc.getZ(), CraftItemStack.asNMSCopy(item));
    entity.pickupDelay = 10;
    world.addEntity(entity);
    // TODO this is inconsistent with how Entity.getBukkitEntity() works.
    // However, this entity is not at the moment backed by a server entity class so it may be left.
    return new CraftItem(world.getServer(), entity);
  }

  public Item dropItemNaturally(Location loc, ItemStack item) {
    double xs = world.random.nextFloat() * 0.7F - 0.35D;
    double ys = world.random.nextFloat() * 0.7F - 0.35D;
    double zs = world.random.nextFloat() * 0.7F - 0.35D;
    loc = loc.clone();
    // Makes sure the new item is created within the block the location points to.
    // This prevents item spill in 1-block wide farms.
    randomLocationWithinBlock(loc, xs, ys, zs);
    return dropItem(loc, item);
  }

  public Arrow spawnArrow(Location loc, Vector velocity, float speed, float spread) {
    Validate.notNull(loc, "Can not spawn arrow with a null location");
    Validate.notNull(velocity, "Can not spawn arrow with a null velocity");

    EntityArrow arrow = new EntityArrow(world);
    arrow.setPositionRotation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
    arrow.shoot(velocity.getX(), velocity.getY(), velocity.getZ(), speed, spread);
    world.addEntity(arrow);
    return (Arrow) arrow.getBukkitEntity();
  }

  @Deprecated
  public LivingEntity spawnCreature(Location loc, CreatureType creatureType) {
    return spawnCreature(loc, creatureType.toEntityType());
  }

  @Deprecated
  public LivingEntity spawnCreature(Location loc, EntityType creatureType) {
    Validate.isTrue(creatureType.isAlive(), "EntityType not instance of LivingEntity");
    return (LivingEntity) spawnEntity(loc, creatureType);
  }

  public Entity spawnEntity(Location loc, EntityType entityType) {
    return spawn(loc, entityType.getEntityClass());
  }

  public LightningStrike strikeLightning(Location loc) {
    EntityLightning lightning = new EntityLightning(world, loc.getX(), loc.getY(), loc.getZ());
    world.strikeLightning(lightning);
    return new CraftLightningStrike(server, lightning);
  }

  public LightningStrike strikeLightningEffect(Location loc) {
    EntityLightning lightning = new EntityLightning(world, loc.getX(), loc.getY(), loc.getZ(), true);
    world.strikeLightning(lightning);
    return new CraftLightningStrike(server, lightning);
  }

  public boolean generateTree(Location loc, TreeType type) {
    net.minecraft.server.WorldGenerator gen;
    switch (type) {
      case BIG_TREE:
        gen = new WorldGenBigTree(true);
        break;
      case BIRCH:
        gen = new WorldGenForest(true, false);
        break;
      case REDWOOD:
        gen = new WorldGenTaiga2(true);
        break;
      case TALL_REDWOOD:
        gen = new WorldGenTaiga1();
        break;
      case JUNGLE:
        IBlockData iblockdata1 = Blocks.LOG.getBlockData().set(BlockLog1.VARIANT, BlockWood.EnumLogVariant.JUNGLE);
        IBlockData iblockdata2 = Blocks.LEAVES.getBlockData().set(BlockLeaves1.VARIANT, BlockWood.EnumLogVariant.JUNGLE).set(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false));
        gen = new WorldGenJungleTree(true, 10, 20, iblockdata1, iblockdata2); // Magic values as in BlockSapling
        break;
      case SMALL_JUNGLE:
        iblockdata1 = Blocks.LOG.getBlockData().set(BlockLog1.VARIANT, BlockWood.EnumLogVariant.JUNGLE);
        iblockdata2 = Blocks.LEAVES.getBlockData().set(BlockLeaves1.VARIANT, BlockWood.EnumLogVariant.JUNGLE).set(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false));
        gen = new WorldGenTrees(true, 4 + rand.nextInt(7), iblockdata1, iblockdata2, false);
        break;
      case COCOA_TREE:
        iblockdata1 = Blocks.LOG.getBlockData().set(BlockLog1.VARIANT, BlockWood.EnumLogVariant.JUNGLE);
        iblockdata2 = Blocks.LEAVES.getBlockData().set(BlockLeaves1.VARIANT, BlockWood.EnumLogVariant.JUNGLE).set(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false));
        gen = new WorldGenTrees(true, 4 + rand.nextInt(7), iblockdata1, iblockdata2, true);
        break;
      case JUNGLE_BUSH:
        iblockdata1 = Blocks.LOG.getBlockData().set(BlockLog1.VARIANT, BlockWood.EnumLogVariant.JUNGLE);
        iblockdata2 = Blocks.LEAVES.getBlockData().set(BlockLeaves1.VARIANT, BlockWood.EnumLogVariant.OAK).set(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false));
        gen = new WorldGenGroundBush(iblockdata1, iblockdata2);
        break;
      case RED_MUSHROOM:
        gen = new WorldGenHugeMushroom(Blocks.RED_MUSHROOM_BLOCK);
        break;
      case BROWN_MUSHROOM:
        gen = new WorldGenHugeMushroom(Blocks.BROWN_MUSHROOM_BLOCK);
        break;
      case SWAMP:
        gen = new WorldGenSwampTree();
        break;
      case ACACIA:
        gen = new WorldGenAcaciaTree(true);
        break;
      case DARK_OAK:
        gen = new WorldGenForestTree(true);
        break;
      case MEGA_REDWOOD:
        gen = new WorldGenMegaTree(false, rand.nextBoolean());
        break;
      case TALL_BIRCH:
        gen = new WorldGenForest(true, true);
        break;
      case TREE:
      default:
        gen = new WorldGenTrees(true);
        break;
    }

    return gen.generate(world, rand, new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
  }

  public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) {
    world.captureTreeGeneration = true;
    world.captureBlockStates = true;
    boolean grownTree = generateTree(loc, type);
    world.captureBlockStates = false;
    world.captureTreeGeneration = false;
    if (grownTree) { // Copy block data to delegate
      for (BlockState blockstate : world.capturedBlockStates) {
        int x = blockstate.getX();
        int y = blockstate.getY();
        int z = blockstate.getZ();
        BlockPosition position = new BlockPosition(x, y, z);
        net.minecraft.server.Block oldBlock = world.getType(position).getBlock();
        int typeId = blockstate.getTypeId();
        int data = blockstate.getRawData();
        int flag = ((CraftBlockState) blockstate).getFlag();
        delegate.setTypeIdAndData(x, y, z, typeId, data);
        net.minecraft.server.Block newBlock = world.getType(position).getBlock();
        world.notifyAndUpdatePhysics(position, null, oldBlock, newBlock, flag);
      }
      world.capturedBlockStates.clear();
      return true;
    } else {
      world.capturedBlockStates.clear();
      return false;
    }
  }

  public TileEntity getTileEntityAt(final int x, final int y, final int z) {
    return world.getTileEntity(new BlockPosition(x, y, z));
  }

  public String getName() {
    return world.worldData.getName();
  }

  @Deprecated
  public long getId() {
    return world.worldData.getSeed();
  }

  public UUID getUID() {
    return world.getDataManager().getUUID();
  }

  @Override
  public String toString() {
    return "CraftWorld{name=" + getName() + '}';
  }

  public long getTime() {
    long time = getFullTime() % 24000;
    if (time < 0) time += 24000;
    return time;
  }

  public void setTime(long time) {
    long margin = (time - getFullTime()) % 24000;
    if (margin < 0) margin += 24000;
    setFullTime(getFullTime() + margin);
  }

  public long getFullTime() {
    return world.getDayTime();
  }

  public void setFullTime(long time) {
    world.setDayTime(time);

    // Forces the client to update to the new time immediately
    for (Player p : getPlayers()) {
      CraftPlayer cp = (CraftPlayer) p;
      if (cp.getHandle().playerConnection == null) continue;

      cp.getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateTime(cp.getHandle().world.getTime(), cp.getHandle().getPlayerTime(), cp.getHandle().world.getGameRules().getBoolean("doDaylightCycle")));
    }
  }

  public boolean createExplosion(double x, double y, double z, float power) {
    return createExplosion(x, y, z, power, false, true);
  }

  public boolean createExplosion(double x, double y, double z, float power, boolean setFire) {
    return createExplosion(x, y, z, power, setFire, true);
  }

  public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks) {
    return !world.createExplosion(null, x, y, z, power, setFire, breakBlocks).wasCanceled;
  }

  public boolean createExplosion(Location loc, float power) {
    return createExplosion(loc, power, false);
  }

  public boolean createExplosion(Location loc, float power, boolean setFire) {
    return createExplosion(loc.getX(), loc.getY(), loc.getZ(), power, setFire);
  }

  public Environment getEnvironment() {
    return environment;
  }

  public void setEnvironment(Environment env) {
    if (environment != env) {
      environment = env;
      world.worldProvider = WorldProvider.byDimension(environment.getId());
    }
  }

  public Block getBlockAt(Location location) {
    return getBlockAt(location.getBlockX(), location.getBlockY(), location.getBlockZ());
  }

  public int getBlockTypeIdAt(Location location) {
    return getBlockTypeIdAt(location.getBlockX(), location.getBlockY(), location.getBlockZ());
  }

  public int getHighestBlockYAt(Location location) {
    return getHighestBlockYAt(location.getBlockX(), location.getBlockZ());
  }

  public Chunk getChunkAt(Location location) {
    return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4);
  }

  public ChunkGenerator getGenerator() {
    return generator;
  }

  public List getPopulators() {
    return populators;
  }

  public Block getHighestBlockAt(int x, int z) {
    return getBlockAt(x, getHighestBlockYAt(x, z), z);
  }

  public Block getHighestBlockAt(Location location) {
    return getHighestBlockAt(location.getBlockX(), location.getBlockZ());
  }

  public Biome getBiome(int x, int z) {
    return CraftBlock.biomeBaseToBiome(this.world.getBiome(new BlockPosition(x, 0, z)));
  }

  public void setBiome(int x, int z, Biome bio) {
    BiomeBase bb = CraftBlock.biomeToBiomeBase(bio);
    if (this.world.isLoaded(new BlockPosition(x, 0, z))) {
      net.minecraft.server.Chunk chunk = this.world.getChunkAtWorldCoords(new BlockPosition(x, 0, z));

      if (chunk != null) {
        byte[] biomevals = chunk.getBiomeIndex();
        biomevals[((z & 0xF) << 4) | (x & 0xF)] = (byte) bb.id;
      }
    }
  }

  public double getTemperature(int x, int z) {
    return this.world.getBiome(new BlockPosition(x, 0, z)).temperature;
  }

  public double getHumidity(int x, int z) {
    return this.world.getBiome(new BlockPosition(x, 0, z)).humidity;
  }

  public List getEntities() {
    List list = new ArrayList();

    for (Object o : world.entityList) {
      if (o instanceof net.minecraft.server.Entity) {
        net.minecraft.server.Entity mcEnt = (net.minecraft.server.Entity) o;
        Entity bukkitEntity = mcEnt.getBukkitEntity();

        // Assuming that bukkitEntity isn't null
        if (bukkitEntity != null) {
          list.add(bukkitEntity);
        }
      }
    }

    return list;
  }

  public List getLivingEntities() {
    List list = new ArrayList();

    for (Object o : world.entityList) {
      if (o instanceof net.minecraft.server.Entity) {
        net.minecraft.server.Entity mcEnt = (net.minecraft.server.Entity) o;
        Entity bukkitEntity = mcEnt.getBukkitEntity();

        // Assuming that bukkitEntity isn't null
        if (bukkitEntity != null && bukkitEntity instanceof LivingEntity) {
          list.add((LivingEntity) bukkitEntity);
        }
      }
    }

    return list;
  }

  @SuppressWarnings("unchecked")
  @Deprecated
  public  Collection getEntitiesByClass(Class... classes) {
    return (Collection) getEntitiesByClasses(classes);
  }

  @SuppressWarnings("unchecked")
  public  Collection getEntitiesByClass(Class clazz) {
    Collection list = new ArrayList();

    for (Object entity : world.entityList) {
      if (entity instanceof net.minecraft.server.Entity) {
        Entity bukkitEntity = ((net.minecraft.server.Entity) entity).getBukkitEntity();

        if (bukkitEntity == null) {
          continue;
        }

        Class bukkitClass = bukkitEntity.getClass();

        if (clazz.isAssignableFrom(bukkitClass)) {
          list.add((T) bukkitEntity);
        }
      }
    }

    return list;
  }

  public Collection getEntitiesByClasses(Class... classes) {
    Collection list = new ArrayList();

    for (Object entity : world.entityList) {
      if (entity instanceof net.minecraft.server.Entity) {
        Entity bukkitEntity = ((net.minecraft.server.Entity) entity).getBukkitEntity();

        if (bukkitEntity == null) {
          continue;
        }

        Class bukkitClass = bukkitEntity.getClass();

        for (Class clazz : classes) {
          if (clazz.isAssignableFrom(bukkitClass)) {
            list.add(bukkitEntity);
            break;
          }
        }
      }
    }

    return list;
  }

  @Override
  public Collection getNearbyEntities(Location location, double x, double y, double z) {
    if (location == null || !location.getWorld().equals(this)) {
      return Collections.emptyList();
    }

    AxisAlignedBB bb = new AxisAlignedBB(location.getX() - x, location.getY() - y, location.getZ() - z, location.getX() + x, location.getY() + y, location.getZ() + z);
    List entityList = getHandle().a((net.minecraft.server.Entity) null, bb, null); // PAIL : rename
    List bukkitEntityList = new ArrayList(entityList.size());
    for (Object entity : entityList) {
      bukkitEntityList.add(((net.minecraft.server.Entity) entity).getBukkitEntity());
    }
    return bukkitEntityList;
  }

  public List getPlayers() {
    List list = new ArrayList(world.players.size());

    for (EntityHuman human : world.players) {
      HumanEntity bukkitEntity = human.getBukkitEntity();

      if ((bukkitEntity != null) && (bukkitEntity instanceof Player)) {
        list.add((Player) bukkitEntity);
      }
    }

    return list;
  }

  public void save() {
    // Spigot start
    save(true);
  }

  public void save(boolean forceSave) {
    // Spigot end
    this.server.checkSaveState();
    try {
      boolean oldSave = world.savingDisabled;

      world.savingDisabled = false;
      world.save(forceSave, null); // Spigot

      world.savingDisabled = oldSave;
    } catch (ExceptionWorldConflict ex) {
      ex.printStackTrace();
    }
  }

  public boolean isAutoSave() {
    return !world.savingDisabled;
  }

  public void setAutoSave(boolean value) {
    world.savingDisabled = !value;
  }

  public Difficulty getDifficulty() {
    return Difficulty.getByValue(this.getHandle().getDifficulty().ordinal());
  }

  public void setDifficulty(Difficulty difficulty) {
    this.getHandle().worldData.setDifficulty(EnumDifficulty.getById(difficulty.getValue()));
  }

  public BlockMetadataStore getBlockMetadata() {
    return blockMetadata;
  }

  public boolean hasStorm() {
    return world.worldData.hasStorm();
  }

  public void setStorm(boolean hasStorm) {
    world.worldData.setStorm(hasStorm);
  }

  public int getWeatherDuration() {
    return world.worldData.getWeatherDuration();
  }

  public void setWeatherDuration(int duration) {
    world.worldData.setWeatherDuration(duration);
  }

  public boolean isThundering() {
    return world.worldData.isThundering();
  }

  public void setThundering(boolean thundering) {
    world.worldData.setThundering(thundering);
  }

  public int getThunderDuration() {
    return world.worldData.getThunderDuration();
  }

  public void setThunderDuration(int duration) {
    world.worldData.setThunderDuration(duration);
  }

  public long getSeed() {
    return world.worldData.getSeed();
  }

  public boolean getPVP() {
    return world.pvpMode;
  }

  public void setPVP(boolean pvp) {
    world.pvpMode = pvp;
  }

  public void playEffect(Player player, Effect effect, int data) {
    playEffect(player.getLocation(), effect, data, 0);
  }

  public void playEffect(Location location, Effect effect, int data) {
    playEffect(location, effect, data, 64);
  }

  public  void playEffect(Location loc, Effect effect, T data) {
    playEffect(loc, effect, data, 64);
  }

  public  void playEffect(Location loc, Effect effect, T data, int radius) {
    if (data != null) {
      Validate.isTrue(data.getClass().isAssignableFrom(effect.getData()), "Wrong kind of data for this effect!");
    } else {
      Validate.isTrue(effect.getData() == null, "Wrong kind of data for this effect!");
    }

    if (data != null && data.getClass().equals(org.bukkit.material.MaterialData.class)) {
      org.bukkit.material.MaterialData materialData = (org.bukkit.material.MaterialData) data;
      Validate.isTrue(materialData.getItemType().isBlock(), "Material must be block");
      spigot().playEffect(loc, effect, materialData.getItemType().getId(), materialData.getData(), 0, 0, 0, 1, 1, radius);
    } else {
      int dataValue = data == null ? 0 : CraftEffect.getDataValue(effect, data);
      playEffect(loc, effect, dataValue, radius);
    }
  }

  public void playEffect(Location location, Effect effect, int data, int radius) {
    spigot().playEffect(location, effect, data, 0, 0, 0, 0, 1, 1, radius);
  }

  public  T spawn(Location location, Class clazz) throws IllegalArgumentException {
    return spawn(location, clazz, SpawnReason.CUSTOM);
  }

  public FallingBlock spawnFallingBlock(Location location, org.bukkit.Material material, byte data) throws IllegalArgumentException {
    Validate.notNull(location, "Location cannot be null");
    Validate.notNull(material, "Material cannot be null");
    Validate.isTrue(material.isBlock(), "Material must be a block");

    double x = location.getBlockX() + 0.5;
    double y = location.getBlockY() + 0.5;
    double z = location.getBlockZ() + 0.5;

    EntityFallingBlock entity = new EntityFallingBlock(world, x, y, z, net.minecraft.server.Block.getById(material.getId()).fromLegacyData(data));
    entity.ticksLived = 1;

    world.addEntity(entity, SpawnReason.CUSTOM);
    return (FallingBlock) entity.getBukkitEntity();
  }

  public FallingBlock spawnFallingBlock(Location location, int blockId, byte blockData) throws IllegalArgumentException {
    return spawnFallingBlock(location, org.bukkit.Material.getMaterial(blockId), blockData);
  }

  @SuppressWarnings("unchecked")
  public net.minecraft.server.Entity createEntity(Location location, Class clazz) throws IllegalArgumentException {
    if (location == null || clazz == null) {
      throw new IllegalArgumentException("Location or entity class cannot be null");
    }

    net.minecraft.server.Entity entity = null;

    double x = location.getX();
    double y = location.getY();
    double z = location.getZ();
    float pitch = location.getPitch();
    float yaw = location.getYaw();

    // order is important for some of these
    if (Boat.class.isAssignableFrom(clazz)) {
      entity = new EntityBoat(world, x, y, z);
    } else if (FallingBlock.class.isAssignableFrom(clazz)) {
      x = location.getBlockX();
      y = location.getBlockY();
      z = location.getBlockZ();
      IBlockData blockData = world.getType(new BlockPosition(x, y, z));
      int type = CraftMagicNumbers.getId(blockData.getBlock());
      int data = blockData.getBlock().toLegacyData(blockData);

      entity = new EntityFallingBlock(world, x + 0.5, y + 0.5, z + 0.5, net.minecraft.server.Block.getById(type).fromLegacyData(data));
    } else if (Projectile.class.isAssignableFrom(clazz)) {
      if (Snowball.class.isAssignableFrom(clazz)) {
        entity = new EntitySnowball(world, x, y, z);
      } else if (Egg.class.isAssignableFrom(clazz)) {
        entity = new EntityEgg(world, x, y, z);
      } else if (Arrow.class.isAssignableFrom(clazz)) {
        entity = new EntityArrow(world);
        entity.setPositionRotation(x, y, z, 0, 0);
      } else if (ThrownExpBottle.class.isAssignableFrom(clazz)) {
        entity = new EntityThrownExpBottle(world);
        entity.setPositionRotation(x, y, z, 0, 0);
      } else if (EnderPearl.class.isAssignableFrom(clazz)) {
        entity = new EntityEnderPearl(world, null);
        entity.setPositionRotation(x, y, z, 0, 0);
      } else if (ThrownPotion.class.isAssignableFrom(clazz)) {
        entity = new EntityPotion(world, x, y, z, CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.POTION, 1)));
      } else if (Fireball.class.isAssignableFrom(clazz)) {
        if (SmallFireball.class.isAssignableFrom(clazz)) {
          entity = new EntitySmallFireball(world);
        } else if (WitherSkull.class.isAssignableFrom(clazz)) {
          entity = new EntityWitherSkull(world);
        } else {
          entity = new EntityLargeFireball(world);
        }
        entity.setPositionRotation(x, y, z, yaw, pitch);
        Vector direction = location.getDirection().multiply(10);
        ((EntityFireball) entity).setDirection(direction.getX(), direction.getY(), direction.getZ());
      }
    } else if (Minecart.class.isAssignableFrom(clazz)) {
      if (PoweredMinecart.class.isAssignableFrom(clazz)) {
        entity = new EntityMinecartFurnace(world, x, y, z);
      } else if (StorageMinecart.class.isAssignableFrom(clazz)) {
        entity = new EntityMinecartChest(world, x, y, z);
      } else if (ExplosiveMinecart.class.isAssignableFrom(clazz)) {
        entity = new EntityMinecartTNT(world, x, y, z);
      } else if (HopperMinecart.class.isAssignableFrom(clazz)) {
        entity = new EntityMinecartHopper(world, x, y, z);
      } else if (SpawnerMinecart.class.isAssignableFrom(clazz)) {
        entity = new EntityMinecartMobSpawner(world, x, y, z);
      } else { // Default to rideable minecart for pre-rideable compatibility
        entity = new EntityMinecartRideable(world, x, y, z);
      }
    } else if (EnderSignal.class.isAssignableFrom(clazz)) {
      entity = new EntityEnderSignal(world, x, y, z);
    } else if (EnderCrystal.class.isAssignableFrom(clazz)) {
      entity = new EntityEnderCrystal(world);
      entity.setPositionRotation(x, y, z, 0, 0);
    } else if (LivingEntity.class.isAssignableFrom(clazz)) {
      if (Chicken.class.isAssignableFrom(clazz)) {
        entity = new EntityChicken(world);
      } else if (Cow.class.isAssignableFrom(clazz)) {
        if (MushroomCow.class.isAssignableFrom(clazz)) {
          entity = new EntityMushroomCow(world);
        } else {
          entity = new EntityCow(world);
        }
      } else if (Golem.class.isAssignableFrom(clazz)) {
        if (Snowman.class.isAssignableFrom(clazz)) {
          entity = new EntitySnowman(world);
        } else if (IronGolem.class.isAssignableFrom(clazz)) {
          entity = new EntityIronGolem(world);
        }
      } else if (Creeper.class.isAssignableFrom(clazz)) {
        entity = new EntityCreeper(world);
      } else if (Ghast.class.isAssignableFrom(clazz)) {
        entity = new EntityGhast(world);
      } else if (Pig.class.isAssignableFrom(clazz)) {
        entity = new EntityPig(world);
      } else if (Player.class.isAssignableFrom(clazz)) {
        // need a net server handler for this one
      } else if (Sheep.class.isAssignableFrom(clazz)) {
        entity = new EntitySheep(world);
      } else if (Horse.class.isAssignableFrom(clazz)) {
        entity = new EntityHorse(world);
      } else if (Skeleton.class.isAssignableFrom(clazz)) {
        entity = new EntitySkeleton(world);
      } else if (Slime.class.isAssignableFrom(clazz)) {
        if (MagmaCube.class.isAssignableFrom(clazz)) {
          entity = new EntityMagmaCube(world);
        } else {
          entity = new EntitySlime(world);
        }
      } else if (Spider.class.isAssignableFrom(clazz)) {
        if (CaveSpider.class.isAssignableFrom(clazz)) {
          entity = new EntityCaveSpider(world);
        } else {
          entity = new EntitySpider(world);
        }
      } else if (Squid.class.isAssignableFrom(clazz)) {
        entity = new EntitySquid(world);
      } else if (Tameable.class.isAssignableFrom(clazz)) {
        if (Wolf.class.isAssignableFrom(clazz)) {
          entity = new EntityWolf(world);
        } else if (Ocelot.class.isAssignableFrom(clazz)) {
          entity = new EntityOcelot(world);
        }
      } else if (PigZombie.class.isAssignableFrom(clazz)) {
        entity = new EntityPigZombie(world);
      } else if (Zombie.class.isAssignableFrom(clazz)) {
        entity = new EntityZombie(world);
      } else if (Giant.class.isAssignableFrom(clazz)) {
        entity = new EntityGiantZombie(world);
      } else if (Silverfish.class.isAssignableFrom(clazz)) {
        entity = new EntitySilverfish(world);
      } else if (Enderman.class.isAssignableFrom(clazz)) {
        entity = new EntityEnderman(world);
      } else if (Blaze.class.isAssignableFrom(clazz)) {
        entity = new EntityBlaze(world);
      } else if (Villager.class.isAssignableFrom(clazz)) {
        entity = new EntityVillager(world);
      } else if (Witch.class.isAssignableFrom(clazz)) {
        entity = new EntityWitch(world);
      } else if (Wither.class.isAssignableFrom(clazz)) {
        entity = new EntityWither(world);
      } else if (ComplexLivingEntity.class.isAssignableFrom(clazz)) {
        if (EnderDragon.class.isAssignableFrom(clazz)) {
          entity = new EntityEnderDragon(world);
        }
      } else if (Ambient.class.isAssignableFrom(clazz)) {
        if (Bat.class.isAssignableFrom(clazz)) {
          entity = new EntityBat(world);
        }
      } else if (Rabbit.class.isAssignableFrom(clazz)) {
        entity = new EntityRabbit(world);
      } else if (Endermite.class.isAssignableFrom(clazz)) {
        entity = new EntityEndermite(world);
      } else if (Guardian.class.isAssignableFrom(clazz)) {
        entity = new EntityGuardian(world);
      } else if (ArmorStand.class.isAssignableFrom(clazz)) {
        entity = new EntityArmorStand(world, x, y, z);
      }

      if (entity != null) {
        entity.setLocation(x, y, z, yaw, pitch);
      }
    } else if (Hanging.class.isAssignableFrom(clazz)) {
      Block block = getBlockAt(location);
      BlockFace face = BlockFace.SELF;

      int width = 16; // 1 full block, also painting smallest size.
      int height = 16; // 1 full block, also painting smallest size.

      if (ItemFrame.class.isAssignableFrom(clazz)) {
        width = 12;
        height = 12;
      } else if (LeashHitch.class.isAssignableFrom(clazz)) {
        width = 9;
        height = 9;
      }

      BlockFace[] faces = new BlockFace[]{BlockFace.EAST, BlockFace.NORTH, BlockFace.WEST, BlockFace.SOUTH};
      final BlockPosition pos = new BlockPosition((int) x, (int) y, (int) z);
      for (BlockFace dir : faces) {
        net.minecraft.server.Block nmsBlock = CraftMagicNumbers.getBlock(block.getRelative(dir));
        if (nmsBlock.getMaterial().isBuildable() || BlockDiodeAbstract.d(nmsBlock)) {
          boolean taken = false;
          AxisAlignedBB bb = EntityHanging.calculateBoundingBox(pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height);
          List list = world.getEntities(null, bb);
          for (Iterator it = list.iterator(); !taken && it.hasNext(); ) {
            net.minecraft.server.Entity e = it.next();
            if (e instanceof EntityHanging) {
              taken = true; // Hanging entities do not like hanging entities which intersect them.
            }
          }

          if (!taken) {
            face = dir;
            break;
          }
        }
      }

      EnumDirection dir = CraftBlock.blockFaceToNotch(face).opposite();

      if (Painting.class.isAssignableFrom(clazz)) {
        entity = new EntityPainting(world, new BlockPosition((int) x, (int) y, (int) z), dir);
      } else if (ItemFrame.class.isAssignableFrom(clazz)) {
        entity = new EntityItemFrame(world, new BlockPosition((int) x, (int) y, (int) z), dir);
      } else if (LeashHitch.class.isAssignableFrom(clazz)) {
        entity = new EntityLeash(world, new BlockPosition((int) x, (int) y, (int) z));
        entity.attachedToPlayer = true;
      }

      if (entity != null && !((EntityHanging) entity).survives()) {
        throw new IllegalArgumentException("Cannot spawn hanging entity for " + clazz.getName() + " at " + location);
      }
    } else if (TNTPrimed.class.isAssignableFrom(clazz)) {
      entity = new EntityTNTPrimed(world, x, y, z, null);
    } else if (ExperienceOrb.class.isAssignableFrom(clazz)) {
      entity = new EntityExperienceOrb(world, x, y, z, 0);
    } else if (Weather.class.isAssignableFrom(clazz)) {
      // not sure what this can do
      if (LightningStrike.class.isAssignableFrom(clazz)) {
        entity = new EntityLightning(world, x, y, z);
        // what is this, I don't even
      }
    } else if (Firework.class.isAssignableFrom(clazz)) {
      entity = new EntityFireworks(world, x, y, z, null);
    }

    if (entity != null) {
      // Spigot start
      if (entity instanceof EntityOcelot) {
        ((EntityOcelot) entity).spawnBonus = false;
      }
      // Spigot end
      return entity;
    }

    throw new IllegalArgumentException("Cannot spawn an entity for " + clazz.getName());
  }

  @SuppressWarnings("unchecked")
  public  T addEntity(net.minecraft.server.Entity entity, SpawnReason reason) throws IllegalArgumentException {
    Preconditions.checkArgument(entity != null, "Cannot spawn null entity");

    if (entity instanceof EntityInsentient) {
      ((EntityInsentient) entity).prepare(getHandle().E(new BlockPosition(entity)), null);
    }

    world.addEntity(entity, reason);
    return (T) entity.getBukkitEntity();
  }

  public  T spawn(Location location, Class clazz, SpawnReason reason) throws IllegalArgumentException {
    net.minecraft.server.Entity entity = createEntity(location, clazz);

    return addEntity(entity, reason);
  }

  public ChunkSnapshot getEmptyChunkSnapshot(int x, int z, boolean includeBiome, boolean includeBiomeTempRain) {
    return CraftChunk.getEmptyChunkSnapshot(x, z, this, includeBiome, includeBiomeTempRain);
  }

  public void setSpawnFlags(boolean allowMonsters, boolean allowAnimals) {
    world.setSpawnFlags(allowMonsters, allowAnimals);
  }

  public boolean getAllowAnimals() {
    return world.allowAnimals;
  }

  public boolean getAllowMonsters() {
    return world.allowMonsters;
  }

  public int getMaxHeight() {
    return world.getHeight();
  }

  public int getSeaLevel() {
    return 64;
  }

  public boolean getKeepSpawnInMemory() {
    return world.keepSpawnInMemory;
  }

  public void setKeepSpawnInMemory(boolean keepLoaded) {
    world.keepSpawnInMemory = keepLoaded;
    // Grab the worlds spawn chunk
    BlockPosition chunkcoordinates = this.world.getSpawn();
    int chunkCoordX = chunkcoordinates.getX() >> 4;
    int chunkCoordZ = chunkcoordinates.getZ() >> 4;
    // Cycle through the 25x25 Chunks around it to load/unload the chunks.
    for (int x = -12; x <= 12; x++) {
      for (int z = -12; z <= 12; z++) {
        if (keepLoaded) {
          loadChunk(chunkCoordX + x, chunkCoordZ + z);
        } else {
          if (isChunkLoaded(chunkCoordX + x, chunkCoordZ + z)) {
            if (this.getHandle().getChunkAt(chunkCoordX + x, chunkCoordZ + z) instanceof EmptyChunk) {
              unloadChunk(chunkCoordX + x, chunkCoordZ + z, false);
            } else {
              unloadChunk(chunkCoordX + x, chunkCoordZ + z);
            }
          }
        }
      }
    }
  }

  @Override
  public int hashCode() {
    return getUID().hashCode();
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == null) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }

    final CraftWorld other = (CraftWorld) obj;

    return this.getUID() == other.getUID();
  }

  public File getWorldFolder() {
    return world.getDataManager().getDirectory();
  }

  public void sendPluginMessage(Plugin source, String channel, byte[] message) {
    StandardMessenger.validatePluginMessage(server.getMessenger(), source, channel, message);

    for (Player player : getPlayers()) {
      player.sendPluginMessage(source, channel, message);
    }
  }

  public Set getListeningPluginChannels() {
    Set result = new HashSet();

    for (Player player : getPlayers()) {
      result.addAll(player.getListeningPluginChannels());
    }

    return result;
  }

  public org.bukkit.WorldType getWorldType() {
    return org.bukkit.WorldType.getByName(world.getWorldData().getType().name());
  }

  public boolean canGenerateStructures() {
    return world.getWorldData().shouldGenerateMapFeatures();
  }

  public long getTicksPerAnimalSpawns() {
    return world.ticksPerAnimalSpawns;
  }

  public void setTicksPerAnimalSpawns(int ticksPerAnimalSpawns) {
    world.ticksPerAnimalSpawns = ticksPerAnimalSpawns;
  }

  public long getTicksPerMonsterSpawns() {
    return world.ticksPerMonsterSpawns;
  }

  public void setTicksPerMonsterSpawns(int ticksPerMonsterSpawns) {
    world.ticksPerMonsterSpawns = ticksPerMonsterSpawns;
  }

  public void setMetadata(String metadataKey, MetadataValue newMetadataValue) {
    server.getWorldMetadata().setMetadata(this, metadataKey, newMetadataValue);
  }

  public List getMetadata(String metadataKey) {
    return server.getWorldMetadata().getMetadata(this, metadataKey);
  }

  public boolean hasMetadata(String metadataKey) {
    return server.getWorldMetadata().hasMetadata(this, metadataKey);
  }

  public void removeMetadata(String metadataKey, Plugin owningPlugin) {
    server.getWorldMetadata().removeMetadata(this, metadataKey, owningPlugin);
  }

  public int getMonsterSpawnLimit() {
    if (monsterSpawn < 0) {
      return server.getMonsterSpawnLimit();
    }

    return monsterSpawn;
  }

  public void setMonsterSpawnLimit(int limit) {
    monsterSpawn = limit;
  }

  public int getAnimalSpawnLimit() {
    if (animalSpawn < 0) {
      return server.getAnimalSpawnLimit();
    }

    return animalSpawn;
  }

  public void setAnimalSpawnLimit(int limit) {
    animalSpawn = limit;
  }

  public int getWaterAnimalSpawnLimit() {
    if (waterAnimalSpawn < 0) {
      return server.getWaterAnimalSpawnLimit();
    }

    return waterAnimalSpawn;
  }

  public void setWaterAnimalSpawnLimit(int limit) {
    waterAnimalSpawn = limit;
  }

  public int getAmbientSpawnLimit() {
    if (ambientSpawn < 0) {
      return server.getAmbientSpawnLimit();
    }

    return ambientSpawn;
  }

  public void setAmbientSpawnLimit(int limit) {
    ambientSpawn = limit;
  }

  public void playSound(Location loc, Sound sound, float volume, float pitch) {
    if (loc == null || sound == null) return;

    double x = loc.getX();
    double y = loc.getY();
    double z = loc.getZ();

    getHandle().makeSound(x, y, z, CraftSound.getSound(sound), volume, pitch);
  }

  public String getGameRuleValue(String rule) {
    return getHandle().getGameRules().get(rule);
  }

  public boolean setGameRuleValue(String rule, String value) {
    // No null values allowed
    if (rule == null || value == null) return false;

    if (!isGameRule(rule)) return false;

    getHandle().getGameRules().set(rule, value);
    return true;
  }

  public String[] getGameRules() {
    return getHandle().getGameRules().getGameRules();
  }

  public boolean isGameRule(String rule) {
    return getHandle().getGameRules().contains(rule);
  }

  @Override
  public WorldBorder getWorldBorder() {
    if (this.worldBorder == null) {
      this.worldBorder = new CraftWorldBorder(this);
    }

    return this.worldBorder;
  }

  public void processChunkGC() {
    chunkGCTickCount++;

    if (chunkLoadCount >= server.chunkGCLoadThresh && server.chunkGCLoadThresh > 0) {
      chunkLoadCount = 0;
    } else if (chunkGCTickCount >= server.chunkGCPeriod && server.chunkGCPeriod > 0) {
      chunkGCTickCount = 0;
    } else {
      return;
    }

    ChunkProviderServer cps = world.chunkProviderServer;
    for (net.minecraft.server.Chunk chunk : cps.chunks.values()) {
      // If in use, skip it
      if (isChunkInUse(chunk.locX, chunk.locZ)) {
        continue;
      }

      // Already unloading?
      if (cps.unloadQueue.contains(chunk.locX, chunk.locZ)) {
        continue;
      }

      // Add unload request
      cps.queueUnload(chunk.locX, chunk.locZ);
    }
  }

  public Spigot spigot() {
    return spigot;
  }
  // Spigot end

  @Override
  public int compareTo(@NotNull World o) {
    return getUID().compareTo(o.getUID());
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy