package org.bukkit.craftbukkit.entity;
import com.destroystokyo.paper.Title;
import com.google.common.base.Preconditions;
import com.koloboke.collect.set.hash.HashObjSets;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.BaseEncoding;
import com.mojang.authlib.GameProfile;
import io.netty.buffer.Unpooled;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.md_5.bungee.api.chat.BaseComponent;
import net.minecraft.server.*;
import net.minecraft.server.PacketPlayOutTitle.EnumTitleAction;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.NotImplementedException;
import org.bukkit.*;
import org.bukkit.Achievement;
import org.bukkit.BanList;
import org.bukkit.Statistic;
import org.bukkit.Material;
import org.bukkit.Statistic.Type;
import org.bukkit.World;
import org.bukkit.configuration.serialization.DelegateDeserialization;
import org.bukkit.conversations.Conversation;
import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.conversations.ManuallyAbandonedConversationCanceller;
import org.bukkit.craftbukkit.*;
import org.bukkit.craftbukkit.block.CraftSign;
import org.bukkit.craftbukkit.conversations.ConversationTracker;
import org.bukkit.craftbukkit.map.CraftMapView;
import org.bukkit.craftbukkit.map.RenderData;
import org.bukkit.craftbukkit.scoreboard.CraftScoreboard;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerRegisterChannelEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerUnregisterChannelEvent;
import org.bukkit.inventory.InventoryView.Property;
import org.bukkit.map.MapCursor;
import org.bukkit.map.MapView;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.messaging.StandardMessenger;
import org.bukkit.scoreboard.Scoreboard;
@DelegateDeserialization(CraftOfflinePlayer.class)
public class CraftPlayer extends CraftHumanEntity implements Player {
private long firstPlayed = 0;
private long lastPlayed = 0;
private boolean hasPlayedBefore = false;
private final ConversationTracker conversationTracker = new ConversationTracker();
private final Set<String> channels = HashObjSets.newMutableSet();
private final Set<UUID> hiddenPlayers = HashObjSets.newMutableSet();
private int hash = 0;
private double health = 20;
private boolean scaledHealth = false;
private double healthScale = 20;
// Paper start
private org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus;
private String resourcePackHash;
// Paper end
public CraftPlayer(CraftServer server, EntityPlayer entity) {
super(server, entity);
firstPlayed = System.currentTimeMillis();
}
public GameProfile getProfile() {
return getHandle().getProfile();
}
@Override
public boolean isOp() {
return server.getHandle().isOp(getProfile());
}
@Override
public void setOp(boolean value) {
if (value == isOp()) return;
if (value) {
server.getHandle().addOp(getProfile());
} else {
server.getHandle().removeOp(getProfile());
}
perm.recalculatePermissions();
}
@Override
public boolean isOnline() {
return server.getPlayer(getUniqueId()) != null;
}
@Override
public InetSocketAddress getAddress() {
if (getHandle().playerConnection == null) return null;
SocketAddress addr = getHandle().playerConnection.networkManager.getSocketAddress();
if (addr instanceof InetSocketAddress) {
return (InetSocketAddress) addr;
} else {
return null;
}
}
@Override
public double getEyeHeight() {
return getEyeHeight(false);
}
@Override
public double getEyeHeight(boolean ignoreSneaking) {
if (ignoreSneaking) {
return 1.62D;
} else {
if (isSneaking()) {
return 1.54D;
} else {
return 1.62D;
}
}
}
@Override
public void sendRawMessage(String message) {
if (getHandle().playerConnection == null) return;
for (IChatBaseComponent component : CraftChatMessage.fromString(message)) {
getHandle().playerConnection.sendPacket(new PacketPlayOutChat(component));
}
}
@Override
public void sendMessage(String message) {
if (!conversationTracker.isConversingModaly()) {
this.sendRawMessage(message);
}
}
@Override
public void sendMessage(String[] messages) {
for (String message : messages) {
sendMessage(message);
}
}
// Paper start
@Override
public void sendActionBar(String message) {
if (getHandle().playerConnection == null || message == null || message.isEmpty()) return;
getHandle().playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), (byte) 2));
}
@Override
public void sendActionBar(char alternateChar, String message) {
if (message == null || message.isEmpty()) return;
sendActionBar(org.bukkit.ChatColor.translateAlternateColorCodes(alternateChar, message));
}
@Override
public void setPlayerListHeaderFooter(BaseComponent[] header, BaseComponent[] footer) {
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
packet.header = header;
packet.footer = footer;
getHandle().playerConnection.sendPacket(packet);
}
@Override
public void setPlayerListHeaderFooter(BaseComponent header, BaseComponent footer) {
this.setPlayerListHeaderFooter(header == null ? null : new BaseComponent[]{header},
footer == null ? null : new BaseComponent[]{footer});
}
@Override
public void setTitleTimes(int fadeInTicks, int stayTicks, int fadeOutTicks) {
getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TIMES, (BaseComponent[]) null, fadeInTicks, stayTicks, fadeOutTicks));
}
@Override
public void setSubtitle(BaseComponent[] subtitle) {
getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, subtitle, 0, 0, 0));
}
@Override
public void setSubtitle(BaseComponent subtitle) {
setSubtitle(new BaseComponent[]{subtitle});
}
@Override
public void showTitle(BaseComponent[] title) {
getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, title, 0, 0, 0));
}
@Override
public void showTitle(BaseComponent title) {
showTitle(new BaseComponent[]{title});
}
@Override
public void showTitle(BaseComponent[] title, BaseComponent[] subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks) {
setTitleTimes(fadeInTicks, stayTicks, fadeOutTicks);
setSubtitle(subtitle);
showTitle(title);
}
@Override
public void showTitle(BaseComponent title, BaseComponent subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks) {
setTitleTimes(fadeInTicks, stayTicks, fadeOutTicks);
setSubtitle(subtitle);
showTitle(title);
}
@Override
public void sendTitle(Title title) {
Preconditions.checkNotNull(title, "Title is null");
setTitleTimes(title.getFadeIn(), title.getStay(), title.getFadeOut());
setSubtitle(title.getSubtitle() == null ? new BaseComponent[0] : title.getSubtitle());
showTitle(title.getTitle());
}
@Override
public void updateTitle(Title title) {
Preconditions.checkNotNull(title, "Title is null");
setTitleTimes(title.getFadeIn(), title.getStay(), title.getFadeOut());
if (title.getSubtitle() != null) {
setSubtitle(title.getSubtitle());
}
showTitle(title.getTitle());
}
@Override
public void hideTitle() {
getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.CLEAR, (BaseComponent[]) null, 0, 0, 0));
}
// Paper end
@Override
public String getDisplayName() {
return getHandle().displayName;
}
@Override
public void setDisplayName(final String name) {
getHandle().displayName = name == null ? getName() : name;
}
@Override
public String getPlayerListName() {
return getHandle().listName == null ? getName() : CraftChatMessage.fromComponent(getHandle().listName, EnumChatFormat.WHITE);
}
@Override
public void setPlayerListName(String name) {
if (name == null) {
name = getName();
}
getHandle().listName = name.equals(getName()) ? null : CraftChatMessage.fromString(name)[0];
for (EntityPlayer player : server.getHandle().players) {
if (player.getBukkitEntity().canSee(this)) {
player.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, getHandle()));
}
}
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof OfflinePlayer)) {
return false;
}
OfflinePlayer other = (OfflinePlayer) obj;
if ((this.getUniqueId() == null) || (other.getUniqueId() == null)) {
return false;
}
boolean uuidEquals = this.getUniqueId().equals(other.getUniqueId());
boolean idEquals = true;
if (other instanceof CraftPlayer) {
idEquals = this.getEntityId() == ((CraftPlayer) other).getEntityId();
}
return uuidEquals && idEquals;
}
@Override
public void kickPlayer(String message) {
org.spigotmc.AsyncCatcher.catchOp( "player kick"); // Spigot
if (getHandle().playerConnection == null) return;
getHandle().playerConnection.disconnect(message == null ? "" : message);
}
@Override
public void setCompassTarget(Location loc) {
if (getHandle().playerConnection == null) return;
// Do not directly assign here, from the packethandler we'll assign it.
getHandle().playerConnection.sendPacket(new PacketPlayOutSpawnPosition(new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())));
}
@Override
public Location getCompassTarget() {
return getHandle().compassTarget;
}
@Override
public void chat(String msg) {
if (getHandle().playerConnection == null) return;
getHandle().playerConnection.chat(msg, false);
}
@Override
public boolean performCommand(String command) {
return server.dispatchCommand(this, command);
}
@Override
public void playNote(Location loc, byte instrument, byte note) {
if (getHandle().playerConnection == null) return;
String instrumentName = null;
switch (instrument) {
case 0:
instrumentName = "harp";
break;
case 1:
instrumentName = "basedrum";
break;
case 2:
instrumentName = "snare";
break;
case 3:
instrumentName = "hat";
break;
case 4:
instrumentName = "bass";
break;
}
float f = (float) Math.pow(2.0D, (note - 12.0D) / 12.0D);
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note." + instrumentName), net.minecraft.server.SoundCategory.MUSIC, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
}
@Override
public void playNote(Location loc, Instrument instrument, Note note) {
if (getHandle().playerConnection == null) return;
String instrumentName = null;
switch (instrument.ordinal()) {
case 0:
instrumentName = "harp";
break;
case 1:
instrumentName = "basedrum";
break;
case 2:
instrumentName = "snare";
break;
case 3:
instrumentName = "hat";
break;
case 4:
instrumentName = "bass";
break;
}
float f = (float) Math.pow(2.0D, (note.getId() - 12.0D) / 12.0D);
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note." + instrumentName), net.minecraft.server.SoundCategory.MUSIC, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
}
@Override
public void playSound(Location loc, Sound sound, float volume, float pitch) {
playSound(loc, sound, org.bukkit.SoundCategory.MASTER, volume, pitch);
}
@Override
public void playSound(Location loc, String sound, float volume, float pitch) {
playSound(loc, sound, org.bukkit.SoundCategory.MASTER, volume, pitch);
}
@Override
public void playSound(Location loc, Sound sound, org.bukkit.SoundCategory category, float volume, float pitch) {
if (loc == null || sound == null || category == null || getHandle().playerConnection == null) return;
PacketPlayOutNamedSoundEffect packet = new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect(CraftSound.getSound(sound)), net.minecraft.server.SoundCategory.valueOf(category.name()), loc.getX(), loc.getY(), loc.getZ(), volume, pitch);
getHandle().playerConnection.sendPacket(packet);
}
@Override
public void playSound(Location loc, String sound, org.bukkit.SoundCategory category, float volume, float pitch) {
if (loc == null || sound == null || category == null || getHandle().playerConnection == null) return;
PacketPlayOutCustomSoundEffect packet = new PacketPlayOutCustomSoundEffect(sound, net.minecraft.server.SoundCategory.valueOf(category.name()), loc.getX(), loc.getY(), loc.getZ(), volume, pitch);
getHandle().playerConnection.sendPacket(packet);
}
@Override
public void stopSound(Sound sound) {
stopSound(sound, null);
}
@Override
public void stopSound(String sound) {
stopSound(sound, null);
}
@Override
public void stopSound(Sound sound, org.bukkit.SoundCategory category) {
stopSound(CraftSound.getSound(sound), category);
}
@Override
public void stopSound(String sound, org.bukkit.SoundCategory category) {
if (getHandle().playerConnection == null) return;
PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer());
packetdataserializer.a(category == null ? "" : net.minecraft.server.SoundCategory.valueOf(category.name()).a());
packetdataserializer.a(sound);
getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|StopSound", packetdataserializer));
}
@Override
public void playEffect(Location loc, Effect effect, int data) {
if (getHandle().playerConnection == null) return;
spigot().playEffect(loc, effect, data, 0, 0, 0, 0, 1, 1, 64); // Spigot
}
@Override
public <T> void playEffect(Location loc, Effect effect, T data) {
if (data != null) {
Validate.isTrue(effect.getData() != null && effect.getData().isAssignableFrom(data.getClass()), "Wrong kind of data for this effect!");
} else {
Validate.isTrue(effect.getData() == null, "Wrong kind of data for this effect!");
}
int datavalue = data == null ? 0 : CraftEffect.getDataValue(effect, data);
playEffect(loc, effect, datavalue);
}
@Override
public void sendBlockChange(Location loc, Material material, byte data) {
sendBlockChange(loc, material.getId(), data);
}
@Override
public void sendBlockChange(Location loc, int material, byte data) {
if (getHandle().playerConnection == null) return;
PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(((CraftWorld) loc.getWorld()).getHandle(), new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
packet.block = CraftMagicNumbers.getBlock(material).fromLegacyData(data);
getHandle().playerConnection.sendPacket(packet);
}
@Override
public void sendSignChange(Location loc, String[] lines) {
if (getHandle().playerConnection == null) {
return;
}
if (lines == null) {
lines = new String[4];
}
Validate.notNull(loc, "Location can not be null");
if (lines.length < 4) {
throw new IllegalArgumentException("Must have at least 4 lines");
}
IChatBaseComponent[] components = CraftSign.sanitizeLines(lines);
TileEntitySign sign = new TileEntitySign();
sign.setPosition(new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
System.arraycopy(components, 0, sign.lines, 0, sign.lines.length);
getHandle().playerConnection.sendPacket(sign.getUpdatePacket());
}
@Override
public boolean sendChunkChange(Location loc, int sx, int sy, int sz, byte[] data) {
if (getHandle().playerConnection == null) return false;
/*
int x = loc.getBlockX();
int y = loc.getBlockY();
int z = loc.getBlockZ();
int cx = x >> 4;
int cz = z >> 4;
if (sx <= 0 || sy <= 0 || sz <= 0) {
return false;
}
if ((x + sx - 1) >> 4 != cx || (z + sz - 1) >> 4 != cz || y < 0 || y + sy > 128) {
return false;
}
if (data.length != (sx * sy * sz * 5) / 2) {
return false;
}
Packet51MapChunk packet = new Packet51MapChunk(x, y, z, sx, sy, sz, data);
getHandle().playerConnection.sendPacket(packet);
return true;
*/
throw new NotImplementedException("Chunk changes do not yet work"); // TODO: Chunk changes.
}
@Override
public void sendMap(MapView map) {
if (getHandle().playerConnection == null) return;
RenderData data = ((CraftMapView) map).render(this);
Collection<MapIcon> icons = new ArrayList<MapIcon>();
for (MapCursor cursor : data.cursors) {
if (cursor.isVisible()) {
icons.add(new MapIcon(MapIcon.Type.a(cursor.getRawType()), cursor.getX(), cursor.getY(), cursor.getDirection()));
}
}
PacketPlayOutMap packet = new PacketPlayOutMap(map.getId(), map.getScale().getValue(), true, icons, data.buffer, 0, 0, 128, 128);
getHandle().playerConnection.sendPacket(packet);
}
@Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
EntityPlayer entity = getHandle();
if (getHealth() == 0 || entity.dead) {
return false;
}
if (entity.playerConnection == null) {
return false;
}
if (entity.isVehicle()) {
return false;
}
// From = Players current Location
Location from = this.getLocation();
// To = Players new Location if Teleport is Successful
Location to = location;
// Create & Call the Teleport Event.
PlayerTeleportEvent event = new PlayerTeleportEvent(this, from, to, cause);
server.getPluginManager().callEvent(event);
// Return False to inform the Plugin that the Teleport was unsuccessful/cancelled.
if (event.isCancelled()) {
return false;
}
// If this player is riding another entity, we must dismount before teleporting.
entity.stopRiding();
// Update the From Location
from = event.getFrom();
// Grab the new To Location dependent on whether the event was cancelled.
to = event.getTo();
// Grab the To and From World Handles.
WorldServer fromWorld = ((CraftWorld) from.getWorld()).getHandle();
WorldServer toWorld = ((CraftWorld) to.getWorld()).getHandle();
// Close any foreign inventory
if (getHandle().activeContainer != getHandle().defaultContainer) {
getHandle().closeInventory();
}
// Check if the fromWorld and toWorld are the same.
if (fromWorld == toWorld) {
entity.playerConnection.teleport(to);
} else {
// Paper - Configurable suffocation check
server.getHandle().moveToWorld(entity, toWorld.dimension, true, to, !toWorld.paperConfig.disableTeleportationSuffocationCheck);
}
return true;
}
// Paper start - Ugly workaround for SPIGOT-1915 & GH-114
@Override
public boolean setPassenger(org.bukkit.entity.Entity passenger) {
boolean wasSet = super.setPassenger(passenger);
if (wasSet) {
this.getHandle().playerConnection.sendPacket(new net.minecraft.server.PacketPlayOutMount(this.getHandle()));
}
return wasSet;
}
// Paper end
@Override
public void setSneaking(boolean sneak) {
getHandle().setSneaking(sneak);
}
@Override
public boolean isSneaking() {
return getHandle().isSneaking();
}
@Override
public boolean isSprinting() {
return getHandle().isSprinting();
}
@Override
public void setSprinting(boolean sprinting) {
getHandle().setSprinting(sprinting);
}
@Override
public void loadData() {
server.getHandle().playerFileData.load(getHandle());
}
@Override
public void saveData() {
server.getHandle().playerFileData.save(getHandle());
}
@Deprecated
@Override
public void updateInventory() {
getHandle().updateInventory(getHandle().activeContainer);
}
@Override
public void setSleepingIgnored(boolean isSleeping) {
getHandle().fauxSleeping = isSleeping;
((CraftWorld) getWorld()).getHandle().checkSleepStatus();
}
@Override
public boolean isSleepingIgnored() {
return getHandle().fauxSleeping;
}
@Override
public void awardAchievement(Achievement achievement) {
Validate.notNull(achievement, "Achievement cannot be null");
if (achievement.hasParent() && !hasAchievement(achievement.getParent())) {
awardAchievement(achievement.getParent());
}
getHandle().getStatisticManager().setStatistic(getHandle(), CraftStatistic.getNMSAchievement(achievement), 1);
getHandle().getStatisticManager().updateStatistics(getHandle());
}
@Override
public void removeAchievement(Achievement achievement) {
Validate.notNull(achievement, "Achievement cannot be null");
for (Achievement achieve : Achievement.values()) {
if (achieve.getParent() == achievement && hasAchievement(achieve)) {
removeAchievement(achieve);
}
}
getHandle().getStatisticManager().setStatistic(getHandle(), CraftStatistic.getNMSAchievement(achievement), 0);
}
@Override
public boolean hasAchievement(Achievement achievement) {
Validate.notNull(achievement, "Achievement cannot be null");
return getHandle().getStatisticManager().hasAchievement(CraftStatistic.getNMSAchievement(achievement));
}
@Override
public void incrementStatistic(Statistic statistic) {
incrementStatistic(statistic, 1);
}
@Override
public void decrementStatistic(Statistic statistic) {
decrementStatistic(statistic, 1);
}
@Override
public int getStatistic(Statistic statistic) {
Validate.notNull(statistic, "Statistic cannot be null");
Validate.isTrue(statistic.getType() == Type.UNTYPED, "Must supply additional paramater for this statistic");
return getHandle().getStatisticManager().getStatisticValue(CraftStatistic.getNMSStatistic(statistic));
}
@Override
public void incrementStatistic(Statistic statistic, int amount) {
Validate.isTrue(amount > 0, "Amount must be greater than 0");
setStatistic(statistic, getStatistic(statistic) + amount);
}
@Override
public void decrementStatistic(Statistic statistic, int amount) {
Validate.isTrue(amount > 0, "Amount must be greater than 0");
setStatistic(statistic, getStatistic(statistic) - amount);
}
@Override
public void setStatistic(Statistic statistic, int newValue) {
Validate.notNull(statistic, "Statistic cannot be null");
Validate.isTrue(statistic.getType() == Type.UNTYPED, "Must supply additional paramater for this statistic");
Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0");
net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getNMSStatistic(statistic);
getHandle().getStatisticManager().setStatistic(getHandle(), nmsStatistic, newValue);
}
@Override
public void incrementStatistic(Statistic statistic, Material material) {
incrementStatistic(statistic, material, 1);
}
@Override
public void decrementStatistic(Statistic statistic, Material material) {
decrementStatistic(statistic, material, 1);
}
@Override
public int getStatistic(Statistic statistic, Material material) {
Validate.notNull(statistic, "Statistic cannot be null");
Validate.notNull(material, "Material cannot be null");
Validate.isTrue(statistic.getType() == Type.BLOCK || statistic.getType() == Type.ITEM, "This statistic does not take a Material parameter");
net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getMaterialStatistic(statistic, material);
Validate.notNull(nmsStatistic, "The supplied Material does not have a corresponding statistic");
return getHandle().getStatisticManager().getStatisticValue(nmsStatistic);
}
@Override
public void incrementStatistic(Statistic statistic, Material material, int amount) {
Validate.isTrue(amount > 0, "Amount must be greater than 0");
setStatistic(statistic, material, getStatistic(statistic, material) + amount);
}
@Override
public void decrementStatistic(Statistic statistic, Material material, int amount) {
Validate.isTrue(amount > 0, "Amount must be greater than 0");
setStatistic(statistic, material, getStatistic(statistic, material) - amount);
}
@Override
public void setStatistic(Statistic statistic, Material material, int newValue) {
Validate.notNull(statistic, "Statistic cannot be null");
Validate.notNull(material, "Material cannot be null");
Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0");
Validate.isTrue(statistic.getType() == Type.BLOCK || statistic.getType() == Type.ITEM, "This statistic does not take a Material parameter");
net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getMaterialStatistic(statistic, material);
Validate.notNull(nmsStatistic, "The supplied Material does not have a corresponding statistic");
getHandle().getStatisticManager().setStatistic(getHandle(), nmsStatistic, newValue);
}
@Override
public void incrementStatistic(Statistic statistic, EntityType entityType) {
incrementStatistic(statistic, entityType, 1);
}
@Override
public void decrementStatistic(Statistic statistic, EntityType entityType) {
decrementStatistic(statistic, entityType, 1);
}
@Override
public int getStatistic(Statistic statistic, EntityType entityType) {
Validate.notNull(statistic, "Statistic cannot be null");
Validate.notNull(entityType, "EntityType cannot be null");
Validate.isTrue(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter");
net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType);
Validate.notNull(nmsStatistic, "The supplied EntityType does not have a corresponding statistic");
return getHandle().getStatisticManager().getStatisticValue(nmsStatistic);
}
@Override
public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) {
Validate.isTrue(amount > 0, "Amount must be greater than 0");
setStatistic(statistic, entityType, getStatistic(statistic, entityType) + amount);
}
@Override
public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) {
Validate.isTrue(amount > 0, "Amount must be greater than 0");
setStatistic(statistic, entityType, getStatistic(statistic, entityType) - amount);
}
@Override
public void setStatistic(Statistic statistic, EntityType entityType, int newValue) {
Validate.notNull(statistic, "Statistic cannot be null");
Validate.notNull(entityType, "EntityType cannot be null");
Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0");
Validate.isTrue(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter");
net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType);
Validate.notNull(nmsStatistic, "The supplied EntityType does not have a corresponding statistic");
getHandle().getStatisticManager().setStatistic(getHandle(), nmsStatistic, newValue);
}
@Override
public void setPlayerTime(long time, boolean relative) {
getHandle().timeOffset = time;
getHandle().relativeTime = relative;
}
@Override
public long getPlayerTimeOffset() {
return getHandle().timeOffset;
}
@Override
public long getPlayerTime() {
return getHandle().getPlayerTime();
}
@Override
public boolean isPlayerTimeRelative() {
return getHandle().relativeTime;
}
@Override
public void resetPlayerTime() {
setPlayerTime(0, true);
}
@Override
public void setPlayerWeather(WeatherType type) {
getHandle().setPlayerWeather(type, true);
}
@Override
public WeatherType getPlayerWeather() {
return getHandle().getPlayerWeather();
}
@Override
public void resetPlayerWeather() {
getHandle().resetPlayerWeather();
}
@Override
public boolean isBanned() {
return server.getBanList(BanList.Type.NAME).isBanned(getName());
}
@Override
public void setBanned(boolean value) {
if (value) {
server.getBanList(BanList.Type.NAME).addBan(getName(), null, null, null);
} else {
server.getBanList(BanList.Type.NAME).pardon(getName());
}
}
@Override
public boolean isWhitelisted() {
return server.getHandle().getWhitelist().isWhitelisted(getProfile());
}
@Override
public void setWhitelisted(boolean value) {
if (value) {
server.getHandle().addWhitelist(getProfile());
} else {
server.getHandle().removeWhitelist(getProfile());
}
}
@Override
public void setGameMode(GameMode mode) {
if (getHandle().playerConnection == null) return;
if (mode == null) {
throw new IllegalArgumentException("Mode cannot be null");
}
getHandle().a(EnumGamemode.getById(mode.getValue()));
}
@Override
public GameMode getGameMode() {
return GameMode.getByValue(getHandle().playerInteractManager.getGameMode().getId());
}
@Override
public void giveExp(int exp) {
getHandle().giveExp(exp);
}
@Override
public void giveExpLevels(int levels) {
getHandle().levelDown(levels);
}
@Override
public float getExp() {
return getHandle().exp;
}
@Override
public void setExp(float exp) {
Preconditions.checkArgument(exp >= 0.0 && exp <= 1.0, "Experience progress must be between 0.0 and 1.0 (%s)", exp);
getHandle().exp = exp;
getHandle().lastSentExp = -1;
}
@Override
public int getLevel() {
return getHandle().expLevel;
}
@Override
public void setLevel(int level) {
getHandle().expLevel = level;
getHandle().lastSentExp = -1;
}
@Override
public int getTotalExperience() {
return getHandle().expTotal;
}
@Override
public void setTotalExperience(int exp) {
getHandle().expTotal = exp;
}
@Override
public float getExhaustion() {
return getHandle().getFoodData().exhaustionLevel;
}
@Override
public void setExhaustion(float value) {
getHandle().getFoodData().exhaustionLevel = value;
}
@Override
public float getSaturation() {
return getHandle().getFoodData().saturationLevel;
}
@Override
public void setSaturation(float value) {
getHandle().getFoodData().saturationLevel = value;
}
@Override
public int getFoodLevel() {
return getHandle().getFoodData().foodLevel;
}
@Override
public void setFoodLevel(int value) {
getHandle().getFoodData().foodLevel = value;
}
@Override
public Location getBedSpawnLocation() {
World world = getServer().getWorld(getHandle().spawnWorld);
BlockPosition bed = getHandle().getBed();
if (world != null && bed != null) {
bed = EntityHuman.getBed(((CraftWorld) world).getHandle(), bed, getHandle().isRespawnForced());
if (bed != null) {
return new Location(world, bed.getX(), bed.getY(), bed.getZ());
}
}
return null;
}
@Override
public void setBedSpawnLocation(Location location) {
setBedSpawnLocation(location, false);
}
@Override
public void setBedSpawnLocation(Location location, boolean override) {
if (location == null) {
getHandle().setRespawnPosition(null, override);
} else {
getHandle().setRespawnPosition(new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()), override);
getHandle().spawnWorld = location.getWorld().getName();
}
}
@Override
public void hidePlayer(Player player) {
Validate.notNull(player, "hidden player cannot be null");
if (getHandle().playerConnection == null) return;
if (equals(player)) return;
if (hiddenPlayers.contains(player.getUniqueId())) return;
hiddenPlayers.add(player.getUniqueId());
// Remove this player from the hidden player's EntityTrackerEntry
EntityTracker tracker = ((WorldServer) entity.world).tracker;
EntityPlayer other = ((CraftPlayer) player).getHandle();
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null) {
entry.clear(getHandle());
}
// Remove the hidden player from this player user list, if they're on it
if (other.sentListPacket) {
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, other));
}
}
@Override
public void showPlayer(Player player) {
Validate.notNull(player, "shown player cannot be null");
if (getHandle().playerConnection == null) return;
if (equals(player)) return;
if (!hiddenPlayers.contains(player.getUniqueId())) return;
hiddenPlayers.remove(player.getUniqueId());
EntityTracker tracker = ((WorldServer) entity.world).tracker;
EntityPlayer other = ((CraftPlayer) player).getHandle();
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
entry.updatePlayer(getHandle());
}
}
public void removeDisconnectingPlayer(Player player) {
hiddenPlayers.remove(player.getUniqueId());
}
@Override
public boolean canSee(Player player) {
return !hiddenPlayers.contains(player.getUniqueId());
}
@Override
public Map<String, Object> serialize() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put("name", getName());
return result;
}
@Override
public Player getPlayer() {
return this;
}
@Override
public EntityPlayer getHandle() {
return (EntityPlayer) entity;
}
public void setHandle(final EntityPlayer entity) {
super.setHandle(entity);
}
@Override
public String toString() {
return "CraftPlayer{" + "name=" + getName() + '}';
}
@Override
public int hashCode() {
if (hash == 0 || hash == 485) {
hash = 97 * 5 + (this.getUniqueId() != null ? this.getUniqueId().hashCode() : 0);
}
return hash;
}
@Override
public long getFirstPlayed() {
return firstPlayed;
}
@Override
public long getLastPlayed() {
return lastPlayed;
}
@Override
public boolean hasPlayedBefore() {
return hasPlayedBefore;
}
public void setFirstPlayed(long firstPlayed) {
this.firstPlayed = firstPlayed;
}
public void readExtraData(NBTTagCompound nbttagcompound) {
hasPlayedBefore = true;
if (nbttagcompound.hasKey("bukkit")) {
NBTTagCompound data = nbttagcompound.getCompound("bukkit");
if (data.hasKey("firstPlayed")) {
firstPlayed = data.getLong("firstPlayed");
lastPlayed = data.getLong("lastPlayed");
}
if (data.hasKey("newExp")) {
EntityPlayer handle = getHandle();
handle.newExp = data.getInt("newExp");
handle.newTotalExp = data.getInt("newTotalExp");
handle.newLevel = data.getInt("newLevel");
handle.expToDrop = data.getInt("expToDrop");
handle.keepLevel = data.getBoolean("keepLevel");
}
}
}
public void setExtraData(NBTTagCompound nbttagcompound) {
if (!nbttagcompound.hasKey("bukkit")) {
nbttagcompound.set("bukkit", new NBTTagCompound());
}
NBTTagCompound data = nbttagcompound.getCompound("bukkit");
EntityPlayer handle = getHandle();
data.setInt("newExp", handle.newExp);
data.setInt("newTotalExp", handle.newTotalExp);
data.setInt("newLevel", handle.newLevel);
data.setInt("expToDrop", handle.expToDrop);
data.setBoolean("keepLevel", handle.keepLevel);
data.setLong("firstPlayed", getFirstPlayed());
data.setLong("lastPlayed", System.currentTimeMillis());
data.setString("lastKnownName", handle.getName());
}
@Override
public boolean beginConversation(Conversation conversation) {
return conversationTracker.beginConversation(conversation);
}
@Override
public void abandonConversation(Conversation conversation) {
conversationTracker.abandonConversation(conversation, new ConversationAbandonedEvent(conversation, new ManuallyAbandonedConversationCanceller()));
}
@Override
public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) {
conversationTracker.abandonConversation(conversation, details);
}
@Override
public void acceptConversationInput(String input) {
conversationTracker.acceptConversationInput(input);
}
@Override
public boolean isConversing() {
return conversationTracker.isConversing();
}
@Override
public void sendPluginMessage(Plugin source, String channel, byte[] message) {
StandardMessenger.validatePluginMessage(server.getMessenger(), source, channel, message);
if (getHandle().playerConnection == null) return;
if (channels.contains(channel)) {
PacketPlayOutCustomPayload packet = new PacketPlayOutCustomPayload(channel, new PacketDataSerializer(Unpooled.wrappedBuffer(message)));
getHandle().playerConnection.sendPacket(packet);
}
}
@Override
public void setTexturePack(String url) {
setResourcePack(url);
}
@Override
public void setResourcePack(String url) {
Validate.notNull(url, "Resource pack URL cannot be null");
getHandle().setResourcePack(url, "null");
}
@Override
public void setResourcePack(String url, byte[] hash) {
Validate.notNull(url, "Resource pack URL cannot be null");
Validate.notNull(hash, "Resource pack hash cannot be null");
Validate.isTrue(hash.length == 20, "Resource pack hash should be 20 bytes long but was " + hash.length);
getHandle().setResourcePack(url, BaseEncoding.base16().lowerCase().encode(hash));
}
public void addChannel(String channel) {
com.google.common.base.Preconditions.checkState( channels.size() < 128, "Too many channels registered" ); // Spigot
if (channels.add(channel)) {
server.getPluginManager().callEvent(new PlayerRegisterChannelEvent(this, channel));
}
}
public void removeChannel(String channel) {
if (channels.remove(channel)) {
server.getPluginManager().callEvent(new PlayerUnregisterChannelEvent(this, channel));
}
}
@Override
public Set<String> getListeningPluginChannels() {
return ImmutableSet.copyOf(channels);
}
public void sendSupportedChannels() {
if (getHandle().playerConnection == null) return;
Set<String> listening = server.getMessenger().getIncomingChannels();
if (!listening.isEmpty()) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
for (String channel : listening) {
try {
stream.write(channel.getBytes("UTF8"));
stream.write((byte) 0);
} catch (IOException ex) {
Logger.getLogger(CraftPlayer.class.getName()).log(Level.SEVERE, "Could not send Plugin Channel REGISTER to " + getName(), ex);
}
}
getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload("REGISTER", new PacketDataSerializer(Unpooled.wrappedBuffer(stream.toByteArray()))));
}
}
@Override
public EntityType getType() {
return EntityType.PLAYER;
}
@Override
public void setMetadata(String metadataKey, MetadataValue newMetadataValue) {
server.getPlayerMetadata().setMetadata(this, metadataKey, newMetadataValue);
}
@Override
public List<MetadataValue> getMetadata(String metadataKey) {
return server.getPlayerMetadata().getMetadata(this, metadataKey);
}
@Override
public boolean hasMetadata(String metadataKey) {
return server.getPlayerMetadata().hasMetadata(this, metadataKey);
}
@Override
public void removeMetadata(String metadataKey, Plugin owningPlugin) {
server.getPlayerMetadata().removeMetadata(this, metadataKey, owningPlugin);
}
@Override
public boolean setWindowProperty(Property prop, int value) {
Container container = getHandle().activeContainer;
if (container.getBukkitView().getType() != prop.getType()) {
return false;
}
getHandle().setContainerData(container, prop.getId(), value);
return true;
}
public void disconnect(String reason) {
conversationTracker.abandonAllConversations();
perm.clearPermissions();
}
@Override
public boolean isFlying() {
return getHandle().abilities.isFlying;
}
@Override
public void setFlying(boolean value) {
boolean needsUpdate = getHandle().abilities.isFlying != value; // Paper - Only refresh abilities if needed
if (!getAllowFlight() && value) {
throw new IllegalArgumentException("Cannot make player fly if getAllowFlight() is false");
}
getHandle().abilities.isFlying = value;
if (needsUpdate) getHandle().updateAbilities(); // Paper - Only refresh abilities if needed
}
@Override
public boolean getAllowFlight() {
return getHandle().abilities.canFly;
}
@Override
public void setAllowFlight(boolean value) {
if (isFlying() && !value) {
getHandle().abilities.isFlying = false;
}
getHandle().abilities.canFly = value;
getHandle().updateAbilities();
}
@Override
public int getNoDamageTicks() {
// Torch start - fix incorrect calculation of getNoDamageTicks
// see: https://github.com/TacoSpigot/TacoSpigot/blob/master/Paper-Server-Patches/0009-Fix-incorrect-getNoDamageTicks.patch
/* if (getHandle().invulnerableTicks > 0) {
if (getHandle().invulnerableTicks > 0) {
return Math.max(getHandle().invulnerableTicks, getHandle().noDamageTicks);
} else {
return getHandle().noDamageTicks;
} */
return Math.max(getHandle().invulnerableTicks, Math.max(0, getHandle().noDamageTicks - getHandle().maxNoDamageTicks / 2));
// Torch end
}
@Override
public void setFlySpeed(float value) {
validateSpeed(value);
EntityPlayer player = getHandle();
player.abilities.flySpeed = value / 2f;
player.updateAbilities();
}
@Override
public void setWalkSpeed(float value) {
validateSpeed(value);
EntityPlayer player = getHandle();
player.abilities.walkSpeed = value / 2f;
player.updateAbilities();
}
@Override
public float getFlySpeed() {
return getHandle().abilities.flySpeed * 2f;
}
@Override
public float getWalkSpeed() {
return getHandle().abilities.walkSpeed * 2f;
}
private void validateSpeed(float value) {
if (value < 0) {
if (value < -1f) {
throw new IllegalArgumentException(value + " is too low");
}
} else {
if (value > 1f) {
throw new IllegalArgumentException(value + " is too high");
}
}
}
@Override
public void setMaxHealth(double amount) {
super.setMaxHealth(amount);
this.health = Math.min(this.health, health);
getHandle().triggerHealthUpdate();
}
@Override
public void resetMaxHealth() {
super.resetMaxHealth();
getHandle().triggerHealthUpdate();
}
@Override
public CraftScoreboard getScoreboard() {
return this.server.getScoreboardManager().getPlayerBoard(this);
}
@Override
public void setScoreboard(Scoreboard scoreboard) {
Validate.notNull(scoreboard, "Scoreboard cannot be null");
PlayerConnection playerConnection = getHandle().playerConnection;
if (playerConnection == null) {
throw new IllegalStateException("Cannot set scoreboard yet");
}
if (playerConnection.isDisconnected()) {
// throw new IllegalStateException("Cannot set scoreboard for invalid CraftPlayer"); // Spigot - remove this as Mojang's semi asynchronous Netty implementation can lead to races
}
this.server.getScoreboardManager().setPlayerBoard(this, scoreboard);
}
@Override
public void setHealthScale(double value) {
Validate.isTrue((float) value > 0F, "Must be greater than 0");
healthScale = value;
scaledHealth = true;
updateScaledHealth();
}
@Override
public double getHealthScale() {
return healthScale;
}
@Override
public void setHealthScaled(boolean scale) {
if (scaledHealth != (scaledHealth = scale)) {
updateScaledHealth();
}
}
@Override
public boolean isHealthScaled() {
return scaledHealth;
}
public float getScaledHealth() {
return (float) (isHealthScaled() ? getHealth() * getHealthScale() / getMaxHealth() : getHealth());
}
@Override
public double getHealth() {
return health;
}
public void setRealHealth(double health) {
if (Double.isNaN(health)) {return;} // Paper
this.health = health;
}
public void updateScaledHealth() {
AttributeMapServer attributemapserver = (AttributeMapServer) getHandle().getAttributeMap();
Collection set = attributemapserver.c(); // PAIL: Rename
injectScaledMaxHealth(set, true);
getHandle().getDataWatcher().set(EntityLiving.HEALTH, getScaledHealth());
sendHealthUpdate();
getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(getHandle().getId(), set));
getHandle().maxHealthCache = getMaxHealth();
}
public void sendHealthUpdate() {
getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
}
public void injectScaledMaxHealth(Collection collection, boolean force) {
if (!scaledHealth && !force) {
return;
}
for (Object genericInstance : collection) {
IAttribute attribute = ((AttributeInstance) genericInstance).getAttribute();
if (attribute.getName().equals("generic.maxHealth")) {
collection.remove(genericInstance);
break;
}
}
// Spigot start
double healthMod = scaledHealth ? healthScale : getMaxHealth();
if ( healthMod >= Float.MAX_VALUE || healthMod <= 0 )
{
healthMod = 20; // Reset health
getServer().getLogger().warning( getName() + " tried to crash the server with a large health attribute" );
}
collection.add(new AttributeModifiable(getHandle().getAttributeMap(), (new AttributeRanged(null, "generic.maxHealth", healthMod, 0.0D, Float.MAX_VALUE)).a("Max Health").a(true)));
// Spigot end
}
@Override
public org.bukkit.entity.Entity getSpectatorTarget() {
Entity followed = getHandle().getSpecatorTarget();
return followed == getHandle() ? null : followed.getBukkitEntity();
}
@Override
public void setSpectatorTarget(org.bukkit.entity.Entity entity) {
Preconditions.checkArgument(getGameMode() == GameMode.SPECTATOR, "Player must be in spectator mode");
getHandle().setSpectatorTarget((entity == null) ? null : ((CraftEntity) entity).getHandle());
}
@Override
public void sendTitle(String title, String subtitle) {
sendTitle(title, subtitle, 10, 70, 20);
}
@Override
public void sendTitle(String title, String subtitle, int fadeIn, int stay, int fadeOut) {
if (title != null) {
PacketPlayOutTitle packetTitle = new PacketPlayOutTitle(EnumTitleAction.TITLE, CraftChatMessage.fromString(title)[0]);
getHandle().playerConnection.sendPacket(packetTitle);
}
if (subtitle != null) {
PacketPlayOutTitle packetSubtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, CraftChatMessage.fromString(subtitle)[0]);
getHandle().playerConnection.sendPacket(packetSubtitle);
}
PacketPlayOutTitle times = new PacketPlayOutTitle(fadeIn, stay, fadeOut);
getHandle().playerConnection.sendPacket(times);
}
@Override
public void resetTitle() {
PacketPlayOutTitle packetReset = new PacketPlayOutTitle(EnumTitleAction.RESET, null);
getHandle().playerConnection.sendPacket(packetReset);
}
@Override
public void spawnParticle(Particle particle, Location location, int count) {
spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count);
}
@Override
public void spawnParticle(Particle particle, double x, double y, double z, int count) {
spawnParticle(particle, x, y, z, count, null);
}
@Override
public <T> void spawnParticle(Particle particle, Location location, int count, T data) {
spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, data);
}
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, T data) {
spawnParticle(particle, x, y, z, count, 0, 0, 0, data);
}
@Override
public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ) {
spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ);
}
@Override
public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ) {
spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, null);
}
@Override
public <T> void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, data);
}
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, 1, data);
}
@Override
public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double extra) {
spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra);
}
@Override
public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra) {
spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null);
}
@Override
public <T> void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) {
spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data);
}
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) {
if (data != null && !particle.getDataType().isInstance(data)) {
throw new IllegalArgumentException("data should be " + particle.getDataType() + " got " + data.getClass());
}
PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(CraftParticle.toNMS(particle), true, (float) x, (float) y, (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count, CraftParticle.toData(particle, data));
getHandle().playerConnection.sendPacket(packetplayoutworldparticles);
}
@Override
public void setAffectsSpawning(boolean affects) {
this.getHandle().affectsSpawning = affects;
}
@Override
public boolean getAffectsSpawning() {
return this.getHandle().affectsSpawning;
}
@Override
public int getViewDistance() {
return getHandle().getViewDistance();
}
@Override
public void setViewDistance(int viewDistance) {
((WorldServer) getHandle().world).getPlayerChunkMap().updateViewDistance(getHandle(), viewDistance);
}
@Override
public void setResourcePack(String url, String hash) {
Validate.notNull(url, "Resource pack URL cannot be null");
Validate.notNull(hash, "Hash cannot be null");
this.getHandle().setResourcePack(url, hash);
}
@Override
public org.bukkit.event.player.PlayerResourcePackStatusEvent.Status getResourcePackStatus() {
return this.resourcePackStatus;
}
@Override
public String getResourcePackHash() {
return this.resourcePackHash;
}
@Override
public boolean hasResourcePack() {
return this.resourcePackStatus == org.bukkit.event.player.PlayerResourcePackStatusEvent.Status.SUCCESSFULLY_LOADED;
}
public void setResourcePackStatus(org.bukkit.event.player.PlayerResourcePackStatusEvent.Status status) {
this.resourcePackStatus = status;
}
// Spigot start
private final Player.Spigot spigot = new Player.Spigot()
{
@Override
public InetSocketAddress getRawAddress()
{
return (InetSocketAddress) getHandle().playerConnection.networkManager.getRawAddress();
}
@Override
public boolean getCollidesWithEntities() {
return CraftPlayer.this.isCollidable();
}
@Override
public void setCollidesWithEntities(boolean collides) {
CraftPlayer.this.setCollidable(collides);
}
@Override
public void respawn()
{
if ( getHealth() <= 0 && isOnline() )
{
server.getServer().getPlayerList().moveToWorld( getHandle(), 0, false );
}
}
@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;
if ( getHandle().playerConnection == null )
{
return;
}
if ( !location.getWorld().equals( getWorld() ) )
{
return;
}
distance = (int) getLocation().distanceSquared( location );
if ( distance <= radius )
{
getHandle().playerConnection.sendPacket( packet );
}
}
@Override
public String getLocale()
{
// Paper start - Locale change event
final String locale = getHandle().locale;
return locale != null ? locale : "en_US";
// Paper end
}
@Override
public Set<Player> getHiddenPlayers()
{
Set<Player> ret = HashObjSets.newMutableSet();
for ( UUID u : hiddenPlayers )
{
ret.add( getServer().getPlayer( u ) );
}
return HashObjSets.newImmutableSet(ret);
}
@Override
public void sendMessage(BaseComponent component) {
sendMessage( new BaseComponent[] { component } );
}
@Override
public void sendMessage(BaseComponent... components) {
if ( getHandle().playerConnection == null ) return;
PacketPlayOutChat packet = new PacketPlayOutChat();
packet.components = components;
getHandle().playerConnection.sendPacket(packet);
}
@Override
public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent component) {
sendMessage( position, new BaseComponent[] { component } );
}
@Override
public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent... components) {
if ( getHandle().playerConnection == null ) return;
PacketPlayOutChat packet = new PacketPlayOutChat(null, (byte) position.ordinal());
packet.components = components;
getHandle().playerConnection.sendPacket(packet);
}
@Override
public int getPing()
{
return getHandle().ping;
}
};
@Override
public Player.Spigot spigot()
{
return spigot;
}
// Spigot end
}