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 }