blob: c40b050f79b80b5dd96d8c4e73a9ca6990921b3b [file] [log] [blame] [raw]
package net.glowstone;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import org.bukkit.BlockChangeDelegate;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.TreeType;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Boat;
import org.bukkit.entity.CreatureType;
import org.bukkit.entity.Item;
import org.bukkit.entity.LightningStrike;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Minecart;
import org.bukkit.entity.Player;
import org.bukkit.entity.PoweredMinecart;
import org.bukkit.entity.StorageMinecart;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import net.glowstone.block.GlowBlock;
import net.glowstone.io.ChunkIoService;
import net.glowstone.entity.GlowEntity;
import net.glowstone.entity.EntityManager;
import net.glowstone.entity.GlowLivingEntity;
import net.glowstone.entity.GlowPlayer;
import net.glowstone.msg.LoadChunkMessage;
import net.glowstone.msg.StateChangeMessage;
import net.glowstone.msg.TimeMessage;
import net.glowstone.world.WorldGenerator;
import org.bukkit.ChatColor;
import org.bukkit.entity.Entity;
/**
* A class which represents the in-game world.
* @author Graham Edgecombe
*/
public final class GlowWorld implements World {
/**
* The chunk manager.
*/
private final ChunkManager chunks;
/**
* The entity manager.
*/
private final EntityManager entities = new EntityManager();
/**
* A map between locations and cached Block objects.
*/
private final HashMap<Location, GlowBlock> blockCache = new HashMap<Location, GlowBlock>();
/**
* The spawn position.
*/
private Location spawnLocation = new Location(null, 0, 128, 0);
/**
* Whether PvP is allowed in this world.
*/
private boolean pvpAllowed = true;
/**
* Whether it is currently raining/snowing on this world.
*/
private boolean currentlyRaining = false;
/**
* How many ticks until the rain/snow status is expected to change.
*/
private int rainingTicks = 0;
/**
* Whether it is currently thundering on this world.
*/
private boolean currentlyThundering = false;
/**
* How many ticks until the thundering status is expected to change.
*/
private int thunderingTicks = 0;
/**
* The current world time.
*/
private long time = 0;
/**
* Creates a new world with the specified chunk I/O service and world
* generator.
* @param service The chunk I/O service.
* @param generator The world generator.
*/
public GlowWorld(ChunkIoService service, WorldGenerator generator) {
chunks = new ChunkManager(this, service, generator);
setStorm(false);
setThundering(false);
}
////////////////////////////////////////
// Various internal mechanisms
/**
* Updates all the entities within this world.
*/
public void pulse() {
ArrayList<GlowEntity> temp = new ArrayList<GlowEntity>(entities.getAll(GlowEntity.class));
for (GlowEntity entity : temp)
entity.pulse();
for (GlowEntity entity : temp)
entity.reset();
final int TIME_SCALE = 4;
// We currently tick at 1/4 the speed of regular MC
// Modulus by 12000 to force permanent day.
time = (time + TIME_SCALE) % 12000;
for (GlowPlayer player : getRawPlayers()) {
player.getSession().send(new TimeMessage(time));
}
rainingTicks -= TIME_SCALE;
if (rainingTicks <= 0) {
setStorm(!currentlyRaining);
}
thunderingTicks -= TIME_SCALE;
if (thunderingTicks <= 0) {
setThundering(!currentlyThundering);
}
if (currentlyRaining && currentlyThundering) {
if (Math.random() < .001) {
GlowChunk[] chunkList = chunks.getLoadedChunks();
GlowChunk chunk = chunkList[new Random().nextInt(chunkList.length)];
int x = (chunk.getX() << 4) + (int)(Math.random() * 16);
int z = (chunk.getZ() << 4) + (int)(Math.random() * 16);
int y = getHighestBlockYAt(x, z);
// strikeLightning(new Location(this, x, z, y));
broadcastMessage(ChatColor.GREEN + "Pretend lightning struck at " + x + "," + y + "," + z);
}
}
}
/**
* Gets the chunk manager.
* @return The chunk manager.
*/
public ChunkManager getChunkManager() {
return chunks;
}
/**
* Gets the entity manager.
* @return The entity manager.
*/
public EntityManager getEntityManager() {
return entities;
}
public Collection<GlowPlayer> getRawPlayers() {
return entities.getAll(GlowPlayer.class);
}
/**
* Broadcasts a message to every player.
* @param text The message text.
*/
public void broadcastMessage(String text) {
for (Player player : getPlayers())
player.sendMessage(text);
}
// GlowEntity lists
public List<Player> getPlayers() {
Collection<GlowPlayer> players = entities.getAll(GlowPlayer.class);
ArrayList<Player> result = new ArrayList<Player>();
for (Player p : players) {
result.add(p);
}
return result;
}
public List<Entity> getEntities() {
Collection<GlowEntity> list = entities.getAll(GlowEntity.class);
ArrayList<Entity> result = new ArrayList<Entity>();
for (Entity e : list) {
result.add(e);
}
return result;
}
public List<LivingEntity> getLivingEntities() {
Collection<GlowLivingEntity> list = entities.getAll(GlowLivingEntity.class);
ArrayList<LivingEntity> result = new ArrayList<LivingEntity>();
for (LivingEntity e : list) {
result.add(e);
}
return result;
}
// Spawn location
public Location getSpawnLocation() {
return spawnLocation;
}
public boolean setSpawnLocation(int x, int y, int z) {
spawnLocation = new Location(this, x, y, z);
return true;
}
// Pvp on/off
public boolean getPVP() {
return pvpAllowed;
}
public void setPVP(boolean pvp) {
pvpAllowed = pvp;
}
// force-save
public void save() {
for (GlowChunk chunk : chunks.getLoadedChunks()) {
chunks.forceSave(chunk.getX(), chunk.getZ());
}
}
// various fixed world properties
public Environment getEnvironment() {
return Environment.NORMAL;
}
public long getSeed() {
return 0;
}
public String getName() {
return "world";
}
public long getId() {
return getName().hashCode();
}
// get block, chunk, id, highest methods with coords
public GlowBlock getBlockAt(int x, int y, int z) {
if (blockCache.containsKey(new Location(this, x, y, z))) {
return blockCache.get(new Location(this, x, y, z));
} else {
GlowBlock block = new GlowBlock(getChunkAt(x >> 4, z >> 4), x, y, z);
blockCache.put(new Location(this, x, y, z), block);
return block;
}
}
public int getBlockTypeIdAt(int x, int y, int z) {
return ((GlowChunk) getChunkAt(x >> 4, z >> 4)).getType(x & 0xF, z & 0xF, y & 0x7F);
}
public int getHighestBlockYAt(int x, int z) {
for (int y = GlowChunk.DEPTH - 1; y >= 0; --y) {
if (getBlockTypeIdAt(x, y, z) != 0) {
return y;
}
}
return 0;
}
public GlowChunk getChunkAt(int x, int z) {
return chunks.getChunk(x, z);
}
// get block, chunk, id, highest with locations
public GlowBlock 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(), location.getBlockZ());
}
public Chunk getChunkAt(Block block) {
return getChunkAt(block.getX(), block.getZ());
}
// Chunk loading and unloading
public boolean isChunkLoaded(Chunk chunk) {
return isChunkLoaded(chunk.getX(), chunk.getZ());
}
public boolean isChunkLoaded(int x, int z) {
return chunks.isLoaded(x, z);
}
public Chunk[] getLoadedChunks() {
return chunks.getLoadedChunks();
}
public void loadChunk(Chunk chunk) {
loadChunk(chunk.getX(), chunk.getZ());
}
public void loadChunk(int x, int z) {
// Force load by getting chunk.
chunks.getChunk(x, z);
}
public boolean loadChunk(int x, int z, boolean generate) {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean unloadChunk(int x, int z) {
return unloadChunk(x, z, true);
}
public boolean unloadChunk(int x, int z, boolean save) {
return chunks.unloadChunk(x, z, save);
}
public boolean unloadChunk(int x, int z, boolean save, boolean safe) {
if (!safe) {
throw new UnsupportedOperationException("unloadChunk does not yet support unsafe unloading.");
}
return unloadChunk(x, z, save);
}
public boolean unloadChunkRequest(int x, int z) {
return unloadChunkRequest(x, z, true);
}
public boolean unloadChunkRequest(int x, int z, boolean safe) {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean regenerateChunk(int x, int z) {
return chunks.forceRegeneration(x, z);
}
public boolean refreshChunk(int x, int z) {
if (!isChunkLoaded(x, z)) {
return false;
}
GlowChunk.Key key = new GlowChunk.Key(x, z);
boolean result = false;
for (Player p : getPlayers()) {
GlowPlayer player = (GlowPlayer) p;
if (player.canSee(key)) {
player.getSession().send(new LoadChunkMessage(x, z, false));
player.getSession().send(new LoadChunkMessage(x, z, true));
player.getSession().send(getChunkAt(x, z).toMessage());
result = true;
}
}
return result;
}
// Map gen related things
public boolean generateTree(Location location, TreeType type) {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) {
throw new UnsupportedOperationException("Not supported yet.");
}
// GlowEntity spawning
public Item dropItem(Location location, ItemStack item) {
throw new UnsupportedOperationException("Not supported yet.");
}
public Item dropItemNaturally(Location location, ItemStack item) {
throw new UnsupportedOperationException("Not supported yet.");
}
public Arrow spawnArrow(Location location, Vector velocity, float speed, float spread) {
throw new UnsupportedOperationException("Not supported yet.");
}
public Minecart spawnMinecart(Location location) {
throw new UnsupportedOperationException("Not supported yet.");
}
public StorageMinecart spawnStorageMinecart(Location loc) {
throw new UnsupportedOperationException("Not supported yet.");
}
public PoweredMinecart spawnPoweredMinecart(Location loc) {
throw new UnsupportedOperationException("Not supported yet.");
}
public Boat spawnBoat(Location loc) {
throw new UnsupportedOperationException("Not supported yet.");
}
public LivingEntity spawnCreature(Location loc, CreatureType type) {
throw new UnsupportedOperationException("Not supported yet.");
}
public LightningStrike strikeLightning(Location loc) {
throw new UnsupportedOperationException("Not supported yet.");
}
public LightningStrike strikeLightningEffect(Location loc) {
throw new UnsupportedOperationException("Not supported yet.");
}
// Time related methods
public long getTime() {
return time;
}
public void setTime(long time) {
if (time < 0) time = (time % 24000) + 24000;
if (time > 24000) time %= 24000;
this.time = time;
}
public long getFullTime() {
return getTime();
}
public void setFullTime(long time) {
setTime(time);
}
// Weather related methods
public boolean hasStorm() {
return currentlyRaining;
}
public void setStorm(boolean hasStorm) {
currentlyRaining = hasStorm;
// Numbers borrowed from CraftBukkit.
if (currentlyRaining) {
setWeatherDuration(new Random().nextInt(12000) + 12000);
} else {
setWeatherDuration(new Random().nextInt(168000) + 12000);
}
for (GlowPlayer player : getRawPlayers()) {
player.getSession().send(new StateChangeMessage((byte)(currentlyRaining ? 1 : 2)));
}
}
public int getWeatherDuration() {
return rainingTicks;
}
public void setWeatherDuration(int duration) {
rainingTicks = duration;
}
public boolean isThundering() {
return currentlyThundering;
}
public void setThundering(boolean thundering) {
currentlyThundering = thundering;
// Numbers borrowed from CraftBukkit.
if (currentlyThundering) {
setThunderDuration(new Random().nextInt(12000) + 3600);
} else {
setThunderDuration(new Random().nextInt(168000) + 12000);
}
}
public int getThunderDuration() {
return thunderingTicks;
}
public void setThunderDuration(int duration) {
thunderingTicks = duration;
}
}