blob: 0b8b8c4889e569f9830a5f031cf00dd07999e8eb [file] [log] [blame] [raw]
package net.glowstone.entity;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import org.bukkit.Achievement;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.entity.Player;
import net.glowstone.GlowChunk;
import net.glowstone.GlowWorld;
import net.glowstone.inventory.GlowInventory;
import net.glowstone.inventory.GlowPlayerInventory;
import net.glowstone.inventory.InventoryViewer;
import net.glowstone.msg.BlockChangeMessage;
import net.glowstone.msg.ChatMessage;
import net.glowstone.msg.DestroyEntityMessage;
import net.glowstone.msg.LoadChunkMessage;
import net.glowstone.msg.Message;
import net.glowstone.msg.PlayNoteMessage;
import net.glowstone.msg.PositionRotationMessage;
import net.glowstone.msg.RespawnMessage;
import net.glowstone.msg.SetWindowSlotMessage;
import net.glowstone.msg.SpawnPositionMessage;
import net.glowstone.msg.StateChangeMessage;
import net.glowstone.net.Session;
import org.bukkit.inventory.ItemStack;
/**
* Represents an in-game player.
* @author Graham Edgecombe
*/
public final class GlowPlayer extends GlowHumanEntity implements Player, InventoryViewer {
/**
* The normal height of a player's eyes above their feet.
*/
public static final double EYE_HEIGHT = 1.62D;
/**
* This player's session.
*/
private final Session session;
/**
* The display name of this player, for chat purposes.
*/
private String displayName;
/**
* The player's compass target.
*/
private Location compassTarget;
/**
* The entities that the client knows about.
*/
private Set<GlowEntity> knownEntities = new HashSet<GlowEntity>();
/**
* The chunks that the client knows about.
*/
private final Set<GlowChunk.Key> knownChunks = new HashSet<GlowChunk.Key>();
/**
* The item the player has on their cursor.
*/
private ItemStack itemOnCursor;
/**
* Creates a new player and adds it to the world.
* @param session The player's session.
* @param name The player's name.
*/
public GlowPlayer(Session session, String name) {
super(session.getServer(), (GlowWorld) session.getServer().getWorlds().get(0), name);
this.session = session;
streamBlocks(); // stream the initial set of blocks
setCompassTarget(world.getSpawnLocation()); // set our compass target
teleport(world.getSpawnLocation()); // take us to spawn position
session.send(new StateChangeMessage((byte)(getWorld().hasStorm() ? 1 : 2))); // send the world's weather
getInventory().addViewer(this);
}
/**
* Destroys this entity by removing it from the world and marking it as not
* being active.
*/
@Override
public void remove() {
getInventory().removeViewer(this);
super.remove();
}
@Override
public void pulse() {
super.pulse();
streamBlocks();
for (Iterator<GlowEntity> it = knownEntities.iterator(); it.hasNext(); ) {
GlowEntity entity = it.next();
boolean withinDistance = !entity.isDead() && isWithinDistance(entity);
if (withinDistance) {
Message msg = entity.createUpdateMessage();
if (msg != null)
session.send(msg);
} else {
session.send(new DestroyEntityMessage(entity.getEntityId()));
it.remove();
}
}
for (GlowEntity entity : world.getEntityManager()) {
if (entity == this)
continue;
boolean withinDistance = !entity.isDead() && isWithinDistance(entity);
if (withinDistance && !knownEntities.contains(entity)) {
knownEntities.add(entity);
session.send(entity.createSpawnMessage());
}
}
}
/**
* Streams chunks to the player's client.
*/
private void streamBlocks() {
Set<GlowChunk.Key> previousChunks = new HashSet<GlowChunk.Key>(knownChunks);
int centralX = ((int) location.getX()) >> 4;
int centralZ = ((int) location.getZ()) >> 4;
for (int x = (centralX - GlowChunk.VISIBLE_RADIUS); x <= (centralX + GlowChunk.VISIBLE_RADIUS); x++) {
for (int z = (centralZ - GlowChunk.VISIBLE_RADIUS); z <= (centralZ + GlowChunk.VISIBLE_RADIUS); z++) {
GlowChunk.Key key = new GlowChunk.Key(x, z);
if (!knownChunks.contains(key)) {
knownChunks.add(key);
session.send(new LoadChunkMessage(x, z, true));
session.send(world.getChunkManager().getChunk(x, z).toMessage());
}
previousChunks.remove(key);
}
}
for (GlowChunk.Key key : previousChunks) {
session.send(new LoadChunkMessage(key.getX(), key.getZ(), false));
knownChunks.remove(key);
}
previousChunks.clear();
}
/**
* Checks whether the player can see the given chunk.
* @return If the chunk is known to the player's client.
*/
public boolean canSee(GlowChunk.Key chunk) {
return knownChunks.contains(chunk);
}
/**
* Checks whether the player can see the given entity.
* @return If the entity is known to the player's client.
*/
public boolean canSee(GlowEntity entity) {
return knownEntities.contains(entity);
}
/**
* Teleport the player.
* @param location The destination to teleport to.
* @return Whether the teleport was a success.
*/
@Override
public boolean teleport(Location location) {
if (location.getWorld() != world) {
world.getEntityManager().deallocate(this);
world = (GlowWorld) location.getWorld();
world.getEntityManager().allocate(this);
for (GlowChunk.Key key : knownChunks) {
session.send(new LoadChunkMessage(key.getX(), key.getZ(), false));
}
knownChunks.clear();
session.send(new RespawnMessage((byte) world.getEnvironment().getId()));
streamBlocks(); // stream blocks
setCompassTarget(world.getSpawnLocation()); // set our compass target
this.session.send(new PositionRotationMessage(location.getX(), location.getY() + EYE_HEIGHT + 0.01, location.getZ(), location.getY(), (float) location.getYaw(), (float) location.getPitch(), true));
this.location = location; // take us to spawn position
session.send(new StateChangeMessage((byte)(getWorld().hasStorm() ? 1 : 2))); // send the world's weather
reset();
} else {
this.session.send(new PositionRotationMessage(location.getX(), location.getY() + EYE_HEIGHT + 0.01, location.getZ(), location.getY(), (float) location.getYaw(), (float) location.getPitch(), true));
this.location = location;
reset();
}
return true;
}
/**
* Gets the session.
* @return The session.
*/
public Session getSession() {
return session;
}
// Inventory-related
/**
* Inform the client that an item has changed.
* @param inventory The GlowInventory in which a slot has changed.
* @param slot The slot number which has changed.
* @param item The ItemStack which the slot has changed to.
*/
public void onSlotSet(GlowInventory inventory, int slot, ItemStack item) {
slot = GlowPlayerInventory.inventorySlotToNetwork(slot);
if (item == null) {
session.send(new SetWindowSlotMessage(inventory.getId(), slot));
} else {
session.send(new SetWindowSlotMessage(inventory.getId(), slot, item.getTypeId(), item.getAmount(), item.getDurability()));
}
}
/**
* Get the current item on the player's cursor, for inventory screen purposes.
* @return The ItemStack the player is holding.
*/
public ItemStack getItemOnCursor() {
return itemOnCursor;
}
/**
* Set the item on the player's cursor, for inventory screen purposes.
* @param item The ItemStack to set the cursor to.
*/
public void setItemOnCursor(ItemStack item) {
itemOnCursor = item;
if (item == null) {
session.send(new SetWindowSlotMessage(-1, -1));
} else {
session.send(new SetWindowSlotMessage(-1, -1, item.getTypeId(), item.getAmount(), item.getDurability()));
}
}
// More implementation
public boolean isOnline() {
return true;
}
public String getDisplayName() {
return displayName == null ? getName() : displayName;
}
public void setDisplayName(String name) {
displayName = name;
}
public void setCompassTarget(Location loc) {
compassTarget = loc;
session.send(new SpawnPositionMessage(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
public Location getCompassTarget() {
return compassTarget;
}
public InetSocketAddress getAddress() {
return session.getAddress();
}
public void sendRawMessage(String message) {
session.send(new ChatMessage(message));
}
public void kickPlayer(String message) {
session.disconnect(message);
}
/**
* Says a message (or runs a command).
*
* @param text message to print
*/
public void chat(String text) {
if (text.startsWith("/")) {
try {
if (!performCommand(text.substring(1))) {
sendMessage(ChatColor.RED + "Your command could not be executed.");
}
}
catch (Exception ex) {
sendMessage(ChatColor.RED + "An exception occured while executing your command.");
getServer().getLogger().log(Level.SEVERE, "Error while executing command: {0}", ex.getMessage());
ex.printStackTrace();
}
} else {
getServer().broadcastMessage("<" + getName() + "> " + text);
getServer().getLogger().log(Level.INFO, "<{0}> {1}", new Object[]{getName(), text});
}
}
public boolean performCommand(String command) {
return getServer().dispatchCommand(this, command);
}
public boolean isSneaking() {
throw new UnsupportedOperationException("Not supported yet.");
}
public void setSneaking(boolean sneak) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void saveData() {
throw new UnsupportedOperationException("Not supported yet.");
}
public void loadData() {
throw new UnsupportedOperationException("Not supported yet.");
}
public void setSleepingIgnored(boolean isSleeping) {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean isSleepingIgnored() {
throw new UnsupportedOperationException("Not supported yet.");
}
public void playNote(Location loc, byte instrument, byte note) {
session.send(new PlayNoteMessage(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), instrument, note));
}
public void sendBlockChange(Location loc, Material material, byte data) {
sendBlockChange(loc, material.getId(), data);
}
public void sendBlockChange(Location loc, int material, byte data) {
session.send(new BlockChangeMessage(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), material, data));
}
public void updateInventory() {
throw new UnsupportedOperationException("Not supported yet.");
}
public void awardAchievement(Achievement achievement) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void incrementStatistic(Statistic statistic) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void incrementStatistic(Statistic statistic, int amount) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void incrementStatistic(Statistic statistic, Material material) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void incrementStatistic(Statistic statistic, Material material, int amount) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void sendMessage(String message) {
do {
int len = message.length() > 100 ? 100 : message.length();
sendRawMessage(message.substring(0, len));
message = message.substring(len);
} while (message.length() > 0);
}
public boolean isOp() {
return getServer().getOpsList().contains(getName());
}
public boolean sendChunkChange(Location loc, int sx, int sy, int sz, byte[] data) {
throw new UnsupportedOperationException("Not supported yet.");
}
}