org.bukkit.craftbukkit.CraftWorld Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of chest-server Show documentation
Show all versions of chest-server Show documentation
A spigot fork to kotlin structure and news.
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 extends Entity> 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