Package org.getspout.spout.player

Source Code of org.getspout.spout.player.SpoutCraftPlayer

/*
* 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);
  }
}
TOP

Related Classes of org.getspout.spout.player.SpoutCraftPlayer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.