/*
* This file is part of SpoutcraftPlugin.
*
* Copyright (c) 2011 SpoutcraftDev <http://spoutcraft.org//>
* SpoutcraftPlugin is licensed under the GNU Lesser General Public License.
*
* SpoutcraftPlugin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SpoutcraftPlugin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.getspout.spout.player;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.server.v1_6_R3.ContainerPlayer;
import net.minecraft.server.v1_6_R3.DedicatedServer;
import net.minecraft.server.v1_6_R3.Entity;
import net.minecraft.server.v1_6_R3.EntityPlayer;
import net.minecraft.server.v1_6_R3.IInventory;
import net.minecraft.server.v1_6_R3.INetworkManager;
import net.minecraft.server.v1_6_R3.MinecraftServer;
import net.minecraft.server.v1_6_R3.PlayerConnection;
import net.minecraft.server.v1_6_R3.ServerConnection;
import net.minecraft.server.v1_6_R3.TileEntityDispenser;
import net.minecraft.server.v1_6_R3.TileEntityFurnace;
import net.minecraft.server.v1_6_R3.TileEntitySign;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.Sign;
import org.bukkit.craftbukkit.v1_6_R3.CraftServer;
import org.bukkit.craftbukkit.v1_6_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_6_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_6_R3.inventory.CraftInventory;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerVelocityEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.Vector;
import org.getspout.spout.PacketCompressionThread;
import org.getspout.spout.SpoutPlayerConnection;
import org.getspout.spout.SpoutPermissibleBase;
import org.getspout.spout.config.ConfigReader;
import org.getspout.spout.config.Waypoint;
import org.getspout.spout.inventory.SpoutCraftInventoryPlayer;
import org.getspout.spout.inventory.SpoutCraftingInventory;
import org.getspout.spout.packet.CustomPacket;
import org.getspout.spout.packet.standard.MCCraftPacket;
import org.getspout.spout.packet.standard.MCCraftPacket0KeepAlive;
import org.getspout.spoutapi.SpoutManager;
import org.getspout.spoutapi.event.permission.PlayerPermissionEvent;
import org.getspout.spoutapi.gui.GenericOverlayScreen;
import org.getspout.spoutapi.gui.InGameScreen;
import org.getspout.spoutapi.gui.OverlayScreen;
import org.getspout.spoutapi.gui.Screen;
import org.getspout.spoutapi.gui.ScreenType;
import org.getspout.spoutapi.inventory.SpoutPlayerInventory;
import org.getspout.spoutapi.io.CRCStore.URLCheck;
import org.getspout.spoutapi.io.CRCStoreRunnable;
import org.getspout.spoutapi.keyboard.Keyboard;
import org.getspout.spoutapi.packet.*;
import org.getspout.spoutapi.packet.standard.MCPacket;
import org.getspout.spoutapi.player.EntitySkinType;
import org.getspout.spoutapi.player.PlayerInformation;
import org.getspout.spoutapi.player.RenderDistance;
import org.getspout.spoutapi.player.SpoutPlayer;
import org.getspout.spoutapi.player.accessories.AccessoryType;
public class SpoutCraftPlayer extends CraftPlayer implements SpoutPlayer {
protected SpoutCraftInventoryPlayer inventory = null;
protected Keyboard forward = Keyboard.KEY_UNKNOWN;
protected Keyboard back = Keyboard.KEY_UNKNOWN;
protected Keyboard left = Keyboard.KEY_UNKNOWN;
protected Keyboard right = Keyboard.KEY_UNKNOWN;
protected Keyboard jump = Keyboard.KEY_UNKNOWN;
protected Keyboard inventoryKey = Keyboard.KEY_UNKNOWN;
protected Keyboard drop = Keyboard.KEY_UNKNOWN;
protected Keyboard chat = Keyboard.KEY_UNKNOWN;
protected Keyboard toggleFog = Keyboard.KEY_UNKNOWN;
protected Keyboard sneak = Keyboard.KEY_UNKNOWN;
private int buildVersion = -1;
public RenderDistance currentRender = null;
protected RenderDistance maximumRender = null;
protected RenderDistance minimumRender = null;
protected String clipboard = null;
protected InGameScreen mainScreen;
protected Permissible perm;
private double gravityMod = 1;
private double swimmingMod = 1;
private double walkingMod = 1;
private double jumpingMod = 1;
private double airspeedMod = 1;
private boolean fly;
private String versionString = "not set";
private Location lastClicked = null;
private boolean precachingComplete = false;
private ScreenType activeScreen = ScreenType.GAME_SCREEN;
private GenericOverlayScreen currentScreen = null;
private Location lastTickLocation = null;
private boolean screenOpenThisTick = false;
public LinkedList<SpoutPacket> queued = new LinkedList<SpoutPacket>();
private LinkedList<SpoutPacket> delayedPackets = new LinkedList<SpoutPacket>();
public long velocityAdjustmentTime = System.currentTimeMillis();
private long firstPlayed = 0;
private long lastPlayed = 0;
private boolean hasPlayed = false;
private GameMode prevMode;
private Map<String, String> addons;
private Map<AccessoryType, String> accessories = new HashMap<AccessoryType, String>();
private int updateCounter;
public SpoutCraftPlayer(CraftServer server, EntityPlayer entity) {
super(server, entity);
createInventory(null);
if (entity.playerConnection != null) {
CraftPlayer player = entity.playerConnection.getPlayer();
perm = new SpoutPermissibleBase(player.addAttachment(Bukkit.getServer().getPluginManager().getPlugin("Spout")).getPermissible());
perm.recalculatePermissions();
hasPlayed = player.hasPlayedBefore();
lastPlayed = player.getLastPlayed();
firstPlayed = player.getFirstPlayed();
} else {
perm = new SpoutPermissibleBase(new PermissibleBase(this));
perm.recalculatePermissions();
}
mainScreen = new InGameScreen(this.getEntityId());
mainScreen.toggleSurvivalHUD(!getGameMode().equals(GameMode.CREATIVE));
prevMode = getGameMode();
fly = MinecraftServer.getServer().getAllowFlight();
}
@Override
public boolean hasPlayedBefore() {
return hasPlayed;
}
@Override
public long getFirstPlayed() {
return firstPlayed;
}
@Override
public long getLastPlayed() {
return lastPlayed;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof OfflinePlayer)) {
return false;
}
OfflinePlayer other = (OfflinePlayer) obj;
return this.getName() != null && this.getName().equalsIgnoreCase(other.getName());
}
@Override
public int hashCode() {
int hash = 5;
hash = 97 * hash + (this.getName() != null ? this.getName().toLowerCase().hashCode() : 0);
return hash;
}
/* Interface Overridden Public Methods */
@Override
public boolean isPermissionSet(String name) {
return perm.isPermissionSet(name);
}
@Override
public boolean isPermissionSet(Permission perm) {
return this.perm.isPermissionSet(perm);
}
@Override
public boolean hasPermission(String name) {
boolean defaultResult = this.perm.hasPermission(name);
PlayerPermissionEvent event = new PlayerPermissionEvent(this, name, Bukkit.getServer().getPluginManager().getPermission(name), defaultResult);
Bukkit.getServer().getPluginManager().callEvent(event);
return event.getResult();
}
@Override
public boolean hasPermission(Permission perm) {
boolean defaultResult = this.perm.hasPermission(perm);
PlayerPermissionEvent event = new PlayerPermissionEvent(this, perm.getName(), perm, defaultResult);
Bukkit.getServer().getPluginManager().callEvent(event);
return event.getResult();
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
return perm.addAttachment(plugin, name, value);
}
@Override
public PermissionAttachment addAttachment(Plugin plugin) {
return perm.addAttachment(plugin);
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
return perm.addAttachment(plugin, name, value, ticks);
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
return perm.addAttachment(plugin, ticks);
}
@Override
public void removeAttachment(PermissionAttachment attachment) {
perm.removeAttachment(attachment);
}
@Override
public void recalculatePermissions() {
perm.recalculatePermissions();
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
return perm.getEffectivePermissions();
}
@Override
public SpoutPlayerInventory getInventory() {
if (this.inventory == null) {
createInventory(null);
} else if (!(this.inventory).getHandle().equals(this.getHandle().inventory)) {
createInventory(this.inventory.getName());
}
return this.inventory;
}
@Override
public void setMaximumAir(int time) {
if (time <= 0) {
throw new IllegalArgumentException("The Maximum Air can not be below 1");
}
if (isSpoutCraftEnabled()) {
sendPacket(new PacketAirTime(time, this.getRemainingAir()));
}
super.setMaximumAir(time);
}
@Override
public void setLastDamage(double i) {
super.setLastDamage((double) i);
}
@Override
public int _INVALID_getLastDamage() {
return (int) super.getLastDamage();
}
@Override
public void _INVALID_setLastDamage(int damage) {
super.setLastDamage((double) damage);
}
@Override
public void setRemainingAir(int time) {
if (time < 0) {
throw new IllegalArgumentException("The Remaining Air can not be below 0");
}
if (isSpoutCraftEnabled()) {
sendPacket(new PacketAirTime(this.getMaximumAir(), time));
}
super.setRemainingAir(time);
}
@Override
public void setVelocity(Vector velocity) {
super.setVelocity(velocity);
if (isSpoutCraftEnabled()) {
PlayerVelocityEvent event = new PlayerVelocityEvent(this, velocity);
Bukkit.getServer().getPluginManager().callEvent(event);
if (!event.isCancelled()) {
sendPacket(new PacketSetVelocity(getEntityId(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()));
}
double speedX = Math.abs(event.getVelocity().getX() * event.getVelocity().getX());
double speedY = Math.abs(event.getVelocity().getY() * event.getVelocity().getY());
double speedZ = Math.abs(event.getVelocity().getZ() * event.getVelocity().getZ());
double speed = speedX + speedY + speedZ;
velocityAdjustmentTime = System.currentTimeMillis() + (long) (speed * 5);
getHandle().velocityChanged = false; // Prevents nms from sending an override packet later, but still tells the server about the new velocity
}
}
/* Interface New Public Methods */
@Override
public boolean closeActiveWindow() {
this.closeInventory();
return true;
}
@Override
public boolean openInventoryWindow(Inventory inventory) {
return openInventoryWindow(inventory, null, false);
}
@Override
public boolean openInventoryWindow(Inventory inventory, Location location) {
return openInventoryWindow(inventory, location, false);
}
@Override
public boolean openInventoryWindow(Inventory inventory, Location location, boolean ignoreDistance) {
IInventory dialog = ((CraftInventory) inventory).getInventory();
if (dialog instanceof TileEntityDispenser) {
getHandle().openDispenser((TileEntityDispenser) dialog);
} else if (dialog instanceof TileEntityFurnace) {
getHandle().openFurnace((TileEntityFurnace) dialog);
} else {
getHandle().openContainer(dialog);
}
return true;
}
@Override
public boolean openWorkbenchWindow(Location location) {
this.openEnchanting(location, true);
return true;
}
@Override
public InGameScreen getMainScreen() {
return mainScreen;
}
@Override
public Screen getCurrentScreen() {
if (getActiveScreen() == ScreenType.GAME_SCREEN) {
return getMainScreen();
} else {
return currentScreen;
}
}
@Override
public boolean isSpoutCraftEnabled() {
return getBuildVersion() > -1;
}
@Override
public Keyboard getForwardKey() {
return forward;
}
@Override
public Keyboard getBackwardKey() {
return back;
}
@Override
public Keyboard getLeftKey() {
return left;
}
@Override
public Keyboard getRightKey() {
return right;
}
@Override
public Keyboard getJumpKey() {
return jump;
}
@Override
public Keyboard getInventoryKey() {
return inventoryKey;
}
@Override
public Keyboard getDropItemKey() {
return drop;
}
@Override
public Keyboard getChatKey() {
return chat;
}
@Override
public Keyboard getToggleFogKey() {
return toggleFog;
}
@Override
public Keyboard getSneakKey() {
return sneak;
}
@Override
public RenderDistance getRenderDistance() {
return currentRender;
}
@Override
public void setRenderDistance(RenderDistance distance) {
if (isSpoutCraftEnabled()) {
currentRender = distance;
sendPacket(new PacketRenderDistance(distance, null, null));
}
}
@Override
public void setRenderDistance(RenderDistance distance, boolean update) {
if (update) {
setRenderDistance(distance);
} else {
currentRender = distance;
}
}
@Override
public RenderDistance getMaximumRenderDistance() {
return maximumRender;
}
@Override
public void setMaximumRenderDistance(RenderDistance maximum) {
if (isSpoutCraftEnabled()) {
maximumRender = maximum;
sendPacket(new PacketRenderDistance(null, maximum, null));
}
}
@Override
public void resetMaximumRenderDistance() {
if (isSpoutCraftEnabled()) {
maximumRender = null;
sendPacket(new PacketRenderDistance(true, false));
}
}
@Override
public RenderDistance getMinimumRenderDistance() {
return minimumRender;
}
@Override
public void setMinimumRenderDistance(RenderDistance minimum) {
if (isSpoutCraftEnabled()) {
minimumRender = minimum;
sendPacket(new PacketRenderDistance(null, null, minimum));
}
}
@Override
public void resetMinimumRenderDistance() {
if (isSpoutCraftEnabled()) {
minimumRender = null;
sendPacket(new PacketRenderDistance(false, true));
}
}
@Override
public void sendNotification(String title, String message, Material toRender) {
if (isSpoutCraftEnabled()) {
if (toRender == null || toRender == Material.AIR) {
throw new IllegalArgumentException("The item to render may not be null or air");
}
if (ChatColor.stripColor(title).length() > 26 || title.length() > 78) {
throw new UnsupportedOperationException("Notification titles can not be greater than 26 chars + 26 colors");
}
if (ChatColor.stripColor(message).length() > 26 || message.length() > 78) {
throw new UnsupportedOperationException("Notification messages can not be greater than 26 chars + 26 colors");
}
sendPacket(new PacketAlert(title, message, toRender.getId()));
}
}
@Override
public void sendNotification(String title, String message, Material toRender, short data, int time) {
if (isSpoutCraftEnabled()) {
if (toRender == null || toRender == Material.AIR) {
throw new IllegalArgumentException("The item to render may not be null or air");
}
if (ChatColor.stripColor(title).length() > 26 || title.length() > 78) {
throw new UnsupportedOperationException("Notification titles can not be greater than 26 chars + 26 colors");
}
if (ChatColor.stripColor(message).length() > 26 || message.length() > 78) {
throw new UnsupportedOperationException("Notification messages can not be greater than 26 chars + 26 colors");
}
sendPacket(new PacketNotification(title, message, toRender.getId(), data, time));
}
}
@Override
public void sendNotification(String title, String message, ItemStack item, int time) {
if (isSpoutCraftEnabled()) {
if (item == null || item.getTypeId() == Material.AIR.getId()) {
throw new IllegalArgumentException("The item to render may not be null or air");
}
if (ChatColor.stripColor(title).length() > 26 || title.length() > 78) {
throw new UnsupportedOperationException("Notification titles can not be greater than 26 chars + 26 colors");
}
if (ChatColor.stripColor(message).length() > 26 || message.length() > 78) {
throw new UnsupportedOperationException("Notification messages can not be greater than 26 chars + 26 colors");
}
sendPacket(new PacketNotification(title, message, item.getTypeId(), item.getDurability(), time));
}
}
@Override
public String getClipboardText() {
return clipboard;
}
@Override
public void setClipboardText(String text) {
setClipboardText(text, true);
}
@Override
public void setTexturePack(String url) {
if (isSpoutCraftEnabled()) {
if (url == null || url.length() < 5) {
throw new IllegalArgumentException("Invalid URL!");
}
if (!url.toLowerCase().endsWith(".zip")) {
throw new IllegalArgumentException("A Texture Pack must be in a .zip format");
}
final String finalURL = url;
URLCheck urlCheck = new URLCheck(url, new byte[16384], new CRCStoreRunnable() {
Long CRC;
@Override
public void setCRC(Long CRC) {
this.CRC = CRC;
}
@Override
public void run() {
sendPacket(new PacketTexturePack(finalURL, CRC));
}
});
urlCheck.start();
}
}
@Override
public void resetTexturePack() {
if (isSpoutCraftEnabled()) {
sendPacket(new PacketTexturePack("[none]", 0));
}
}
@Override
public void setClipboardText(String text, boolean updateClient) {
if (isSpoutCraftEnabled()) {
clipboard = text;
if (updateClient) {
sendPacket(new PacketClipboardText(text));
}
}
}
@Override
public Location getActiveInventoryLocation() {
return null;
}
@Override
public void setActiveInventoryLocation(Location loc) {
}
@Override
public void reconnect(String hostname) {
reconnect(null, hostname);
}
@Override
public void reconnect(String hostname, int port) {
reconnect(null, hostname, port);
}
@Override
public void reconnect(String message, String hostname, int port) {
if (hostname.contains(":")) {
throw new IllegalArgumentException("Hostnames may not contain the : symbol");
}
if (message == null) {
message = "[Redirect] Please reconnect to";
} else if (message.contains(":")) {
throw new IllegalArgumentException("Kick messages may not contain the : symbol");
}
if (port == 25565) {
this.kickPlayer(message + " : " + hostname);
} else {
this.kickPlayer(message + " : " + hostname + ":" + port);
}
}
@Override
public void reconnect(String message, String hostname) {
if (hostname.contains(":")) {
String[] split = hostname.split(":");
if (split.length != 2) {
throw new IllegalArgumentException("Improperly formatted hostname: " + hostname);
}
try {
reconnect(message, split[0], Integer.parseInt(split[1]));
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException("Unable to parse port number: " + split[1] + " in " + hostname);
}
} else {
reconnect(message, hostname, 25565);
}
}
@Override
public PlayerInformation getInformation() {
return SpoutManager.getPlayerChunkMap().getPlayerInfo(this);
}
@Override
public ScreenType getActiveScreen() {
return activeScreen;
}
@Override
public void openScreen(ScreenType type) {
openScreen(type, true);
}
@Override
public void sendScreenshotRequest() {
PacketScreenshot packets = new PacketScreenshot();
sendPacket(packets);
}
@Override
public void openScreen(ScreenType type, boolean packet) {
if (type == activeScreen || screenOpenThisTick) {
return;
}
screenOpenThisTick = packet;
activeScreen = type;
if (packet) {
sendPacket(new PacketOpenScreen(type));
}
if (activeScreen != ScreenType.GAME_SCREEN && activeScreen != ScreenType.CUSTOM_SCREEN) {
currentScreen = (GenericOverlayScreen) new GenericOverlayScreen(getEntityId(), getActiveScreen()).setX(0).setY(0);
PacketWidget packetW = new PacketWidget(currentScreen, currentScreen.getId());
sendPacket(packetW);
currentScreen.onTick();
} else {
currentScreen = null;
}
}
@Override
public double getGravityMultiplier() {
return gravityMod;
}
@Override
public double getSwimmingMultiplier() {
return swimmingMod;
}
@Override
public double getWalkingMultiplier() {
return walkingMod;
}
@Override
public void setGravityMultiplier(double multiplier) {
if (getGravityMultiplier() != multiplier) {
gravityMod = multiplier;
updateMovement();
}
}
@Override
public void setSwimmingMultiplier(double multiplier) {
if (getSwimmingMultiplier() != multiplier) {
swimmingMod = multiplier;
updateMovement();
}
}
@Override
public void setWalkingMultiplier(double multiplier) {
if (getWalkingMultiplier() != multiplier) {
walkingMod = multiplier;
updateMovement();
}
}
@Override
public double getJumpingMultiplier() {
return jumpingMod;
}
@Override
public void setJumpingMultiplier(double multiplier) {
if (getJumpingMultiplier() != multiplier) {
this.jumpingMod = multiplier;
updateMovement();
}
}
@Override
public double getAirSpeedMultiplier() {
return airspeedMod;
}
@Override
public void setAirSpeedMultiplier(double multiplier) {
if (getAirSpeedMultiplier() != multiplier) {
airspeedMod = multiplier;
updateMovement();
}
}
@Override
public void resetMovement() {
gravityMod = 1;
walkingMod = 1;
swimmingMod = 1;
jumpingMod = 1;
airspeedMod = 1;
updateMovement();
}
@Override
public boolean canFly() {
return fly;
}
@Override
public void setCanFly(boolean fly) {
this.fly = fly;
}
@Override
public boolean sendInventoryEvent() {
return true;
}
@Override
public Location getLastClickedLocation() {
if (lastClicked != null) {
return lastClicked.clone();
}
return null;
}
@Override
public void setPreCachingComplete(boolean complete) {
if (!precachingComplete) {
precachingComplete = complete;
activeScreen = ScreenType.GAME_SCREEN; // Fix from Precache Loading Screen being unknown to client.
}
}
@Override
public boolean isPreCachingComplete() {
return !isSpoutCraftEnabled() || precachingComplete;
}
@Override
public void openSignEditGUI(Sign sign) {
if (sign != null && isSpoutCraftEnabled()) {
sendPacket(new PacketOpenSignGUI(sign.getX(), sign.getY(), sign.getZ()));
TileEntitySign tes = (TileEntitySign) ((CraftWorld) (sign.getBlock()).getWorld()).getTileEntityAt(sign.getX(), sign.getY(), sign.getZ()); // Found a hidden trace to The Elder Scrolls. Bethesda's Lawyers are right!
tes.isEditable = true;
}
}
@Override
public void updateKeys(byte[] keys) {
this.forward = Keyboard.getKey(keys[0]);
this.back = Keyboard.getKey(keys[2]);
this.left = Keyboard.getKey(keys[1]);
this.right = Keyboard.getKey(keys[3]);
this.jump = Keyboard.getKey(keys[4]);
this.inventoryKey = Keyboard.getKey(keys[5]);
this.drop = Keyboard.getKey(keys[6]);
this.chat = Keyboard.getKey(keys[7]);
this.toggleFog = Keyboard.getKey(keys[8]);
this.sneak = Keyboard.getKey(keys[9]);
}
// Sends a packet delayed by 1 tick
@Override
public void sendDelayedPacket(SpoutPacket packet) {
delayedPackets.add(packet);
}
@SuppressWarnings("deprecation")
@Override
public void sendPacket(SpoutPacket packet) {
if (!isSpoutCraftEnabled()) {
if (queued != null) {
queued.add(packet);
}
} else {
if (packet instanceof CompressiblePacket) {
CompressiblePacket compressible = ((CompressiblePacket) packet);
// Uncompressed, send it to the compression thread
if (!compressible.isCompressed()) {
PacketCompressionThread.add(compressible, this);
return;
}
}
getPlayerConnection().sendPacket(new CustomPacket(packet));
}
}
@Override
public void sendPacket(MCPacket packet) {
if (!(packet instanceof MCCraftPacket)) {
throw new IllegalArgumentException("Packet not of type MCCraftPacket");
}
MCCraftPacket p = (MCCraftPacket) packet;
getHandle().playerConnection.sendPacket(p.getPacket());
}
@Override
public void sendImmediatePacket(MCPacket packet) {
if (!(packet instanceof MCCraftPacket)) {
throw new IllegalArgumentException("Packet not of type MCCraftPacket");
}
MCCraftPacket p = (MCCraftPacket) packet;
if (getHandle().playerConnection instanceof SpoutPlayerConnection) {
getPlayerConnection().sendImmediatePacket(p.getPacket());
} else {
sendPacket(packet);
}
}
@Override
public void sendImmediatePacket(SpoutPacket packet) {
if (!(packet instanceof SpoutPacket)) {
throw new IllegalArgumentException("Packet not of type SpoutPacket");
}
if (getHandle().playerConnection instanceof SpoutPlayerConnection) {
getPlayerConnection().sendPacket(new CustomPacket(packet));
} else {
sendPacket(packet);
}
}
@Override
public void checkUrl(String url) {
if (url == null || url.length() < 5) {
throw new UnsupportedOperationException("Invalid URL [" + url + "]");
}
if (!url.substring(url.length() - 4, url.length()).equalsIgnoreCase(".png")) {
throw new UnsupportedOperationException("Invalid URL [" + url + "], all skins must be a PNG image");
}
if (url.length() > 255) {
throw new UnsupportedOperationException("Invalid URL [" + url + "], all URLs must be shorter than 256 characters");
}
}
private String skin = "http://s3.amazonaws.com/MinecraftSkins/" + getName() + ".png";
private HashMap<String, String> skinsFor = new HashMap<String, String>();
private String cape = "http://s3.amazonaws.com/MinecraftCloaks/" + getName() + ".png";
private HashMap<String, String> capesFor = new HashMap<String, String>();
private String title = getName();
private HashMap<String, String> titlesFor = new HashMap<String, String>();
@Override
public void updateEntitySkins(List<LivingEntity> entities) {
PlayerInformation info = getInformation();
PlayerInformation global = SpoutManager.getPlayerChunkMap().getGlobalInfo();
for (LivingEntity le : entities) {
for (EntitySkinType type : EntitySkinType.values()) {
String skin = null;
if (info != null) {
skin = getInformation().getEntitySkin(le, type);
}
if (skin == null) {
skin = global.getEntitySkin(le, type);
}
if (skin != null) {
sendDelayedPacket(new PacketEntitySkin(le, skin, type.getId()));
}
}
String title = org.getspout.spoutapi.Spout.getServer().getTitle(le);
if (title != null) {
sendDelayedPacket(new PacketEntityTitle(le.getEntityId(), title));
}
}
}
public void updateEntitySkins(LivingEntity entity) {
PlayerInformation info = getInformation();
PlayerInformation global = SpoutManager.getPlayerChunkMap()
.getGlobalInfo();
for (EntitySkinType type : EntitySkinType.values()) {
String skin = null;
if (info != null) {
skin = getInformation().getEntitySkin(entity, type);
}
if (skin == null) {
skin = global.getEntitySkin(entity, type);
}
if (skin != null) {
sendDelayedPacket(new PacketEntitySkin(entity, skin,
type.getId()));
}
}
String title = org.getspout.spoutapi.Spout.getServer().getTitle(entity);
if (title != null) {
sendDelayedPacket(new PacketEntityTitle(entity.getEntityId(), title));
}
}
public void updateAppearance(SpoutPlayer viewer) {
if (!isSpoutCraftEnabled()) {
return;
}
viewer.sendDelayedPacket(new PacketSkinURL(getEntityId(),
getSkin(viewer), getCape(viewer)));
viewer.sendDelayedPacket(new PacketEntityTitle(getEntityId(),
getTitleFor(viewer)));
for (AccessoryType type : AccessoryType.values()) {
if (hasAccessory(type)) {
viewer.sendDelayedPacket(new PacketAccessory(getName(), type,
getAccessoryURL(type)));
}
}
}
@Override
public void setSkin(String url) {
checkUrl(url);
skin = url;
for (Player p : getWorld().getPlayers()) {
if (p instanceof SpoutPlayer) {
((SpoutPlayer) p).sendPacket(new PacketSkinURL(getEntityId(), getSkin((SpoutPlayer) p)));
}
}
}
@Override
public void setSkinFor(SpoutPlayer viewingPlayer, String url) {
checkUrl(url);
skinsFor.put(viewingPlayer.getName(), url);
viewingPlayer.sendPacket(new PacketSkinURL(getEntityId(), url));
}
@Override
public String getSkin() {
return skin;
}
@Override
public String getSkin(SpoutPlayer viewingPlayer) {
if (skinsFor.containsKey(viewingPlayer.getName())) {
return skinsFor.get(viewingPlayer.getName());
}
return getSkin();
}
@Override
public void resetSkin() {
setSkin("http://s3.amazonaws.com/MinecraftSkins/" + getName() + ".png");
}
@Override
public void resetSkinFor(SpoutPlayer viewingPlayer) {
setSkinFor(viewingPlayer, "http://s3.amazonaws.com/MinecraftSkins/" + getName() + ".png");
}
@Override
public void setCape(String url) {
checkUrl(url);
cape = url;
for (Player p : getWorld().getPlayers()) {
if (p instanceof SpoutPlayer) {
((SpoutPlayer) p).sendPacket(new PacketSkinURL(getCape((SpoutPlayer) p), getEntityId()));
}
}
}
@Override
public void setCapeFor(SpoutPlayer viewingPlayer, String url) {
checkUrl(url);
capesFor.put(viewingPlayer.getName(), url);
viewingPlayer.sendPacket(new PacketSkinURL(url, getEntityId()));
}
@Override
public String getCape() {
return cape;
}
@Override
public String getCape(SpoutPlayer viewingPlayer) {
if (capesFor.containsKey(viewingPlayer.getName())) {
return capesFor.get(viewingPlayer.getName());
}
return getCape();
}
@Override
public void resetCape() {
setCape("http://s3.amazonaws.com/MinecraftCloaks/" + getName() + ".png");
}
@Override
public void resetCapeFor(SpoutPlayer viewingPlayer) {
setCapeFor(viewingPlayer, "http://s3.amazonaws.com/MinecraftCloaks/" + getName() + ".png");
}
@Override
public void setTitle(String title) {
this.title = title;
for (Player p : getWorld().getPlayers()) {
if (p instanceof SpoutPlayer) {
((SpoutPlayer) p).sendPacket(new PacketEntityTitle(getEntityId(), getTitleFor((SpoutPlayer) p)));
}
}
}
@Override
public void setTitleFor(SpoutPlayer viewingPlayer, String title) {
titlesFor.put(viewingPlayer.getName(), title);
viewingPlayer.sendPacket(new PacketEntityTitle(getEntityId(), title));
}
@Override
public String getTitle() {
return title;
}
@Override
public String getTitleFor(SpoutPlayer viewingPlayer) {
if (titlesFor.containsKey(viewingPlayer.getName())) {
return titlesFor.get(viewingPlayer.getName());
}
return getTitle();
}
@Override
public void hideTitle() {
setTitle("[hide]");
}
@Override
public void hideTitleFrom(SpoutPlayer viewingPlayer) {
setTitleFor(viewingPlayer, "[hide]");
}
@Override
public void resetTitle() {
setTitle(getName());
}
@Override
public void resetTitleFor(SpoutPlayer viewingPlayer) {
setTitleFor(viewingPlayer, getName());
}
@Override
public void setEntitySkin(LivingEntity target, String url, EntitySkinType type) {
getInformation().setEntitySkin(target, url, type);
sendDelayedPacket(new PacketEntitySkin(target, url, type.getId()));
}
@Override
public void resetEntitySkin(LivingEntity target) {
getInformation().setEntitySkin(target, null);
sendPacket(new PacketEntitySkin(target, "[reset]", (byte) 0));
}
/* Non-Interface public methods */
public Location getLastTickLocation() {
return lastTickLocation;
}
public void setLastTickLocation(Location loc) {
lastTickLocation = loc;
}
public Location getRawLastClickedLocation() {
return lastClicked;
}
public void setLastClickedLocation(Location location) {
lastClicked = location;
}
public void createInventory(String name) {
if (this.getHandle().activeContainer instanceof ContainerPlayer) {
this.inventory = new SpoutCraftInventoryPlayer(this.getHandle().inventory, new SpoutCraftingInventory(((ContainerPlayer) this.getHandle().activeContainer).craftInventory, ((ContainerPlayer) this.getHandle().activeContainer).resultInventory));
if (name != null) {
this.inventory.setName(name);
}
} else {
this.inventory = new SpoutCraftInventoryPlayer(this.getHandle().inventory, new SpoutCraftingInventory(((ContainerPlayer) this.getHandle().defaultContainer).craftInventory, ((ContainerPlayer) this.getHandle().defaultContainer).resultInventory));
if (name != null) {
this.inventory.setName(name);
}
}
}
public int getActiveWindowId() {
Field id;
try {
id = EntityPlayer.class.getDeclaredField("bX");
id.setAccessible(true);
return (Integer) id.get(getHandle());
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public void updateWindowId() {
Method id;
try {
id = EntityPlayer.class.getDeclaredMethod("aq");
id.setAccessible(true);
id.invoke(getHandle());
} catch (Exception e) {
e.printStackTrace();
}
}
public SpoutPlayerConnection getPlayerConnection() {
if (!(getHandle().playerConnection instanceof SpoutPlayerConnection)) {
updatePlayerConnection(this);
}
return (SpoutPlayerConnection) getHandle().playerConnection;
}
public int getBuildVersion() {
return buildVersion;
}
public void setBuildVersion(int build) {
buildVersion = build;
if (isSpoutCraftEnabled() && queued != null) {
for (SpoutPacket packet : queued) {
sendPacket(packet);
}
}
queued = null;
}
public void setVersionString(String versionString) {
this.versionString = versionString;
}
public String getVersionString() {
return versionString;
}
public void onTick() {
mainScreen.onTick();
Screen currentScreen = getCurrentScreen();
if (currentScreen != null && currentScreen instanceof OverlayScreen) {
currentScreen.onTick();
}
screenOpenThisTick = false;
// Because the player teleport event doesn't always fire :(
Location current = getLocation();
if (lastTickLocation != null) {
if (!lastTickLocation.getWorld().equals(current.getWorld())) {
doPostPlayerChangeWorld();
}
}
lastTickLocation = current;
for (SpoutPacket packet : delayedPackets) {
sendPacket(packet);
}
delayedPackets.clear();
if (!getGameMode().equals(prevMode)) {
prevMode = getGameMode();
mainScreen.toggleSurvivalHUD(!getGameMode().equals(GameMode.CREATIVE));
}
++this.updateCounter;
// Keeps client connected while precache completes.
if (!this.precachingComplete && this.updateCounter % 20 == 0) {
sendImmediatePacket(new MCCraftPacket0KeepAlive());
}
// Do this last!
getPlayerConnection().syncFlushPacketQueue();
}
public void doPostPlayerChangeWorld() {
SpoutCraftPlayer.updateBukkitEntity(this);
if (isSpoutCraftEnabled()) {
updateMovement();
updateAppearance(this);
}
}
public void updateMovement() {
if (isSpoutCraftEnabled()) {
sendPacket(new PacketMovementModifiers(gravityMod, walkingMod, swimmingMod, jumpingMod, airspeedMod));
}
}
/* Non Interface public static methods */
public static boolean setPlayerConnection(INetworkManager nm, PlayerConnection nsh) {
try {
Field p = nm.getClass().getDeclaredField("connection");
p.setAccessible(true);
p.set(nm, nsh);
} catch (NoSuchFieldException e) {
e.printStackTrace();
return false;
} catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
} catch (IllegalAccessException e) {
e.printStackTrace();
return false;
}
return true;
}
public static boolean resetPlayerConnection(Player player) {
CraftPlayer cp = (CraftPlayer) player;
CraftServer server = (CraftServer) Bukkit.getServer();
if (cp.getHandle().playerConnection instanceof SpoutPlayerConnection) {
PlayerConnection oldHandler = cp.getHandle().playerConnection;
/*Set<ChunkCoordIntPair> chunkUpdateQueue = ((SpoutPlayerConnection) cp.getHandle().playerConnection).getChunkUpdateQueue();
for (ChunkCoordIntPair c : chunkUpdateQueue) {
cp.getHandle().chunkCoordIntPairQueue.add(c);
}
((SpoutPlayerConnection) cp.getHandle().playerConnection).flushUnloadQueue();*/
cp.getHandle().playerConnection.a();
Location loc = player.getLocation();
PlayerConnection handler = new PlayerConnection(MinecraftServer.getServer(), cp.getHandle().playerConnection.networkManager, cp.getHandle());
handler.a(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
cp.getHandle().playerConnection = handler;
INetworkManager nm = cp.getHandle().playerConnection.networkManager;
setPlayerConnection(nm, cp.getHandle().playerConnection);
oldHandler.disconnected = true;
return true;
}
return false;
}
public static boolean updatePlayerConnection(Player player) {
CraftPlayer cp = (CraftPlayer) player;
CraftServer server = (CraftServer) Bukkit.getServer();
if (!(cp.getHandle().playerConnection instanceof SpoutPlayerConnection)) {
PlayerConnection oldHandler = cp.getHandle().playerConnection;
Location loc = player.getLocation();
SpoutPlayerConnection handler = new SpoutPlayerConnection(MinecraftServer.getServer(), cp.getHandle().playerConnection.networkManager, cp.getHandle());
/*for (Object o : cp.getHandle().playerChunkCoordIntPairs) {
ChunkCoordIntPair c = (ChunkCoordIntPair) o;
handler.addActiveChunk(c);
}*/
handler.a(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
cp.getHandle().playerConnection = handler;
INetworkManager nm = cp.getHandle().playerConnection.networkManager;
setPlayerConnection(nm, cp.getHandle().playerConnection);
Field handlerList = null;
try {
handlerList = ServerConnection.class.getDeclaredField("c");
handlerList.setAccessible(true);
ServerConnection sc = ((DedicatedServer) MinecraftServer.getServer()).ag();
List rhandlerList = (List) handlerList.get(sc);
rhandlerList.remove(oldHandler);
rhandlerList.add(handler);
} catch (NoSuchFieldException ex) {
Logger.getLogger(SpoutCraftPlayer.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
Logger.getLogger(SpoutCraftPlayer.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(SpoutCraftPlayer.class.getName()).log(Level.SEVERE, null, ex);
}
oldHandler.disconnected = true;
return true;
}
return false;
}
public static boolean updateBukkitEntity(Player player) {
if (!(player instanceof SpoutCraftPlayer)) {
CraftPlayer cp = (CraftPlayer) player;
EntityPlayer ep = cp.getHandle();
return updateBukkitEntity(ep);
}
return false;
}
public static boolean updateBukkitEntity(EntityPlayer ep) {
Field bukkitEntity;
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
bukkitEntity.setAccessible(true);
org.bukkit.entity.Entity e = (org.bukkit.entity.Entity) bukkitEntity.get(ep);
if (e == null || !e.getClass().equals(SpoutCraftPlayer.class)) {
bukkitEntity.set(ep, new SpoutCraftPlayer((CraftServer) Bukkit.getServer(), ep));
}
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static SpoutPlayer getPlayer(Player player) {
if (player instanceof SpoutCraftPlayer) {
return (SpoutCraftPlayer) player;
}
if ((((CraftPlayer) player).getHandle()).getBukkitEntity() instanceof SpoutCraftPlayer) {
return (SpoutCraftPlayer) ((((CraftPlayer) player).getHandle()).getBukkitEntity());
}
// We should never get here
//Logger.getLogger("Minecraft").warning("Player: " + player.getName() + " was not properly updated during login!");
updateBukkitEntity(player);
return (SpoutCraftPlayer) ((((CraftPlayer) player).getHandle()).getBukkitEntity());
}
@Override
public Map<String, String> getAddons() {
return addons;
}
@Override
public void setAddons(String[] addons, String[] versions) {
this.addons = new HashMap<String, String>();
for (int i = 0; i < addons.length; i++) {
this.addons.put(addons[i], versions[i]);
}
}
@Override
public void updatePermission(String node) {
updatePermissions(node);
}
@Override
public void updatePermissions(String... nodes) {
HashMap<String, Boolean> values = new HashMap<String, Boolean>();
for (String node : nodes) {
boolean allow = hasPermission(node);
values.put(node, allow);
}
sendPacket(new PacketPermissionUpdate(values));
}
@Override
public void updatePermissions() {
recalculatePermissions();
HashMap<String, Boolean> values = new HashMap<String, Boolean>();
HashSet<String> allPerms = new HashSet<String>();
// Hackish workaround for Bukkit not giving us all the permissions
Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(false);
for (Permission permission : defaults) {
allPerms.add(permission.getName());
}
defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(true);
for (Permission permission : defaults) {
allPerms.add(permission.getName());
}
// Overwrite with actual permissions, if applicable
for (PermissionAttachmentInfo info : perm.getEffectivePermissions()) {
allPerms.add(info.getPermission());
}
for (String p : allPerms) {
values.put(p, hasPermission(p));
}
sendPacket(new PacketPermissionUpdate(values));
}
@Override
public boolean spawnTextEntity(String text, Location location, float scale, int duration, Vector movement) {
sendPacket(new PacketSpawnTextEntity(text, location, scale, duration, movement));
return isSpoutCraftEnabled();
}
@Override
public void addWaypoint(String name, double x, double y, double z) {
sendPacket(new PacketWaypoint(x, y, z, name));
}
public void updateWaypoints() {
List<Waypoint> waypoints = ConfigReader.getWaypoints(getWorld().getName().toLowerCase());
for (Waypoint p : waypoints) {
addWaypoint(p.getName(), p.getX(), p.getY(), p.getZ());
}
}
@Override
public boolean hasAccessory(AccessoryType type) {
return accessories.containsKey(type);
}
@Override
public void addAccessory(AccessoryType type, String url) {
accessories.put(type, url);
for (Player p : getWorld().getPlayers()) {
if (p instanceof SpoutPlayer) {
((SpoutPlayer) p).sendPacket(new PacketAccessory(getName(), type, url));
}
}
}
@Override
public String removeAccessory(AccessoryType type) {
for (Player p : getWorld().getPlayers()) {
if (p instanceof SpoutPlayer) {
((SpoutPlayer) p).sendPacket(new PacketAccessory(getName(), type, "", false));
}
}
return accessories.remove(type);
}
@Override
public String getAccessoryURL(AccessoryType type) {
return accessories.get(type);
}
@Override
public void sendLink(String link) throws MalformedURLException {
sendPacket(new PacketSendLink(link));
}
@Override
public void sendLink(URL link) {
sendPacket(new PacketSendLink(link));
}
@Override
public void damage(double i) {
super.damage((double) i);
}
@Override
public void damage(double i, org.bukkit.entity.Entity entity) {
super.damage((double) i, entity);
}
@Override
public void setHealth(double i) {
super.setHealth((double) i);
}
@Override
public void setMaxHealth(double i) {
super.setMaxHealth((double) i);
}
@Override
public void _INVALID_damage(int amount) {
super.damage((double) amount);
}
@Override
public void _INVALID_damage(int amount, org.bukkit.entity.Entity source) {
super.damage((double) amount, source);
}
@Override
public int _INVALID_getHealth() {
return (int) super.getHealth();
}
@Override
public void _INVALID_setHealth(int health) {
super.setHealth((double) health);
}
@Override
public int _INVALID_getMaxHealth() {
return (int) super.getMaxHealth();
}
@Override
public void _INVALID_setMaxHealth(int health) {
super.setMaxHealth((double) health);
}
}