diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs
index 22f0d330..b93a594a 100644
--- a/TShockAPI/TSPlayer.cs
+++ b/TShockAPI/TSPlayer.cs
@@ -1,1024 +1,1044 @@
-/*
-TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program 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 General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see .
-*/
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Threading;
-using Terraria;
-using TShockAPI.Net;
-
-namespace TShockAPI
-{
- public class TSPlayer
- {
- ///
- /// This represents the server as a player.
- ///
- public static readonly TSServerPlayer Server = new TSServerPlayer();
-
- ///
- /// This player represents all the players.
- ///
- public static readonly TSPlayer All = new TSPlayer("All");
-
- ///
- /// The amount of tiles that the player has killed in the last second.
- ///
- public int TileKillThreshold { get; set; }
-
- ///
- /// The amount of tiles the player has placed in the last second.
- ///
- public int TilePlaceThreshold { get; set; }
-
- ///
- /// The amount of liquid( in tiles ) that the player has placed in the last second.
- ///
- public int TileLiquidThreshold { get; set; }
-
- ///
- /// The number of projectiles created by the player in the last second.
- ///
- public int ProjectileThreshold { get; set; }
-
- ///
- /// A queue of tiles destroyed by the player for reverting.
- ///
- public Dictionary TilesDestroyed { get; protected set; }
-
- ///
- /// A queue of tiles placed by the player for reverting.
- ///
- public Dictionary TilesCreated { get; protected set; }
-
- public int FirstMaxHP { get; set; }
-
- public int FirstMaxMP { get; set; }
-
- ///
- /// The player's group.
- ///
- public Group Group
- {
- get
- {
- if (tempGroup != null)
- return tempGroup;
- return group;
- }
- set { group = value; }
- }
-
- ///
- /// The player's temporary group. This overrides the user's actual group.
- ///
- public Group tempGroup = null;
-
- private Group group = null;
-
- public bool ReceivedInfo { get; set; }
-
- ///
- /// The players index in the player array( Main.players[] ).
- ///
- public int Index { get; protected set; }
-
- ///
- /// The last time the player changed their team or pvp status.
- ///
- public DateTime LastPvpChange;
-
- ///
- /// Temp points for use in regions and other plugins.
- ///
- public Point[] TempPoints = new Point[2];
-
- ///
- /// Whether the player is waiting to place/break a tile to set as a temp point.
- ///
- public int AwaitingTempPoint { get; set; }
-
- ///
- /// A list of command callbacks indexed by the command they need to do.
- ///
- public Dictionary> AwaitingResponse;
-
- public bool AwaitingName { get; set; }
-
- ///
- /// The last time a player broke a grief check.
- ///
- public DateTime LastThreat { get; set; }
-
- ///
- /// Not used, can be removed.
- ///
- public DateTime LastTileChangeNotify { get; set; }
-
- public bool InitSpawn;
-
- ///
- /// Whether the player should see logs.
- ///
- public bool DisplayLogs = true;
-
- public Vector2 oldSpawn = Vector2.Zero;
-
- ///
- /// The last player that the player whispered with( to or from ).
- ///
- public TSPlayer LastWhisper;
-
- ///
- /// The number of unsuccessful login attempts.
- ///
- public int LoginAttempts { get; set; }
-
- public Vector2 TeleportCoords = new Vector2(-1, -1);
-
- public Vector2 LastNetPosition = Vector2.Zero;
-
- ///
- /// The player's login name.
- ///
- public string UserAccountName { get; set; }
-
- ///
- /// Unused can be removed.
- ///
- public bool HasBeenSpammedWithBuildMessage;
-
- ///
- /// Whether the player is logged in or not.
- ///
- public bool IsLoggedIn;
-
- ///
- /// The player's user id( from the db ).
- ///
- public int UserID = -1;
-
- ///
- /// Whether the player has been nagged about logging in.
- ///
- public bool HasBeenNaggedAboutLoggingIn;
-
- public bool TPAllow = true;
-
- ///
- /// Whether the player is muted or not.
- ///
- public bool mute;
-
- public bool TpLock;
-
- private Player FakePlayer;
-
- public bool RequestedSection;
-
- ///
- /// The last time the player died.
- ///
- public DateTime LastDeath { get; set; }
-
- ///
- /// Whether the player is dead or not.
- ///
- public bool Dead;
-
- public string Country = "??";
-
- ///
- /// The players difficulty( normal[softcore], mediumcore, hardcore ).
- ///
- public int Difficulty;
-
- private string CacheIP;
-
- public string IgnoreActionsForInventory = "none";
-
- public string IgnoreActionsForCheating = "none";
-
- public string IgnoreActionsForDisabledArmor = "none";
-
- public bool IgnoreActionsForClearingTrashCan;
-
- ///
- /// The player's server side inventory data.
- ///
- public PlayerData PlayerData;
-
- ///
- /// Whether the player needs to specify a password upon connection( either server or user account ).
- ///
- public bool RequiresPassword;
-
- public bool SilentKickInProgress;
-
- public bool SilentJoinInProgress;
-
- ///
- /// A list of points where ice tiles have been placed.
- ///
- public List IceTiles;
-
- ///
- /// Unused, can be removed.
- ///
- public long RPm = 1;
-
- ///
- /// World protection message cool down.
- ///
- public long WPm = 1;
-
- ///
- /// Spawn protection message cool down.
- ///
- public long SPm = 1;
-
- ///
- /// Permission to build message cool down.
- ///
- public long BPm = 1;
-
- ///
- /// The time in ms when the player has logged in.
- ///
- public long LoginMS;
-
- ///
- /// Whether the player has been harrassed about logging in due to server side inventory or forced login.
- ///
- public bool LoginHarassed = false;
-
- ///
- /// Whether the player is a real, human, player on the server.
- ///
- public bool RealPlayer
- {
- get { return Index >= 0 && Index < Main.maxNetPlayers && Main.player[Index] != null; }
- }
-
- public bool ConnectionAlive
- {
- get
- {
- return RealPlayer &&
- (Netplay.serverSock[Index] != null && Netplay.serverSock[Index].active && !Netplay.serverSock[Index].kill);
- }
- }
-
- public int State
- {
- get { return Netplay.serverSock[Index].state; }
- set { Netplay.serverSock[Index].state = value; }
- }
-
- public string IP
- {
- get
- {
- if (string.IsNullOrEmpty(CacheIP))
- return
- CacheIP =
- RealPlayer
- ? (Netplay.serverSock[Index].tcpClient.Connected
- ? TShock.Utils.GetRealIP(Netplay.serverSock[Index].tcpClient.Client.RemoteEndPoint.ToString())
- : "")
- : "";
- else
- return CacheIP;
- }
- }
-
- ///
- /// Saves the player's inventory to SSI
- ///
- /// bool - True/false if it saved successfully
- public bool SaveServerInventory()
- {
- if (!TShock.Config.ServerSideInventory)
- {
- return false;
- }
- try
- {
- PlayerData.CopyInventory(this);
- TShock.InventoryDB.InsertPlayerData(this);
- return true;
- } catch (Exception e)
- {
- Log.Error(e.Message);
- return false;
- }
-
- }
-
- ///
- /// Terraria Player
- ///
- public Player TPlayer
- {
- get { return FakePlayer ?? Main.player[Index]; }
- }
-
- public string Name
- {
- get { return TPlayer.name; }
- }
-
- public bool Active
- {
- get { return TPlayer != null && TPlayer.active; }
- }
-
- public int Team
- {
- get { return TPlayer.team; }
- }
-
- public float X
- {
- get { return RealPlayer ? TPlayer.position.X : Main.spawnTileX*16; }
- }
-
- public float Y
- {
- get { return RealPlayer ? TPlayer.position.Y : Main.spawnTileY*16; }
- }
-
- public int TileX
- {
- get { return (int) (X/16); }
- }
-
- public int TileY
- {
- get { return (int) (Y/16); }
- }
-
- public bool InventorySlotAvailable
- {
- get
- {
- bool flag = false;
- if (RealPlayer)
- {
- for (int i = 0; i < 40; i++) //41 is trash can, 42-45 is coins, 46-49 is ammo
- {
- if (TPlayer.inventory[i] == null || !TPlayer.inventory[i].active || TPlayer.inventory[i].name == "")
- {
- flag = true;
- break;
- }
- }
- }
- return flag;
- }
- }
-
- public TSPlayer(int index)
- {
- TilesDestroyed = new Dictionary();
- TilesCreated = new Dictionary();
- Index = index;
- Group = new Group(TShock.Config.DefaultGuestGroupName);
- IceTiles = new List();
- AwaitingResponse = new Dictionary>();
- }
-
- protected TSPlayer(String playerName)
- {
- TilesDestroyed = new Dictionary();
- TilesCreated = new Dictionary();
- Index = -1;
- FakePlayer = new Player {name = playerName, whoAmi = -1};
- Group = new Group(TShock.Config.DefaultGuestGroupName);
- AwaitingResponse = new Dictionary>();
- }
-
- public virtual void Disconnect(string reason)
- {
- SendData(PacketTypes.Disconnect, reason);
- }
-
- public virtual void Flush()
- {
- var sock = Netplay.serverSock[Index];
- if (sock == null)
- return;
-
- TShock.PacketBuffer.Flush(sock);
- }
-
-
- public void SendWorldInfo(int tilex, int tiley, bool fakeid)
- {
- using (var ms = new MemoryStream())
- {
- var msg = new WorldInfoMsg
- {
- Time = (int) Main.time,
- DayTime = Main.dayTime,
- MoonPhase = (byte) Main.moonPhase,
- BloodMoon = Main.bloodMoon,
- MaxTilesX = Main.maxTilesX,
- MaxTilesY = Main.maxTilesY,
- SpawnX = tilex,
- SpawnY = tiley,
- WorldSurface = (int) Main.worldSurface,
- RockLayer = (int) Main.rockLayer,
- //Sending a fake world id causes the client to not be able to find a stored spawnx/y.
- //This fixes the bed spawn point bug. With a fake world id it wont be able to find the bed spawn.
- WorldID = !fakeid ? Main.worldID : -1,
- WorldFlags = (WorldGen.shadowOrbSmashed ? WorldInfoFlag.OrbSmashed : WorldInfoFlag.None) |
- (NPC.downedBoss1 ? WorldInfoFlag.DownedBoss1 : WorldInfoFlag.None) |
- (NPC.downedBoss2 ? WorldInfoFlag.DownedBoss2 : WorldInfoFlag.None) |
- (NPC.downedBoss3 ? WorldInfoFlag.DownedBoss3 : WorldInfoFlag.None) |
- (Main.hardMode ? WorldInfoFlag.HardMode : WorldInfoFlag.None) |
- (NPC.downedClown ? WorldInfoFlag.DownedClown : WorldInfoFlag.None),
- WorldName = TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName
- };
- msg.PackFull(ms);
- SendRawData(ms.ToArray());
- }
- }
-
- public bool Teleport(int tilex, int tiley)
- {
- InitSpawn = false;
-
- SendWorldInfo(tilex, tiley, true);
-
- //150 Should avoid all client crash errors
- //The error occurs when a tile trys to update which the client hasnt load yet, Clients only update tiles withen 150 blocks
- //Try 300 if it does not work (Higher number - Longer load times - Less chance of error)
- //Should we properly send sections so that clients don't get tiles twice?
- SendTileSquare(tilex, tiley, 150);
-
-/* //We shouldn't need this section anymore -it can prevent otherwise acceptable teleportation under some circumstances.
-
- if (!SendTileSquare(tilex, tiley, 150))
- {
- InitSpawn = true;
- SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false);
- return false;
- }
-
-*/
- Spawn(-1, -1);
-
- SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false);
-
- TPlayer.position.X = (float)(tilex * 16 + 8 - TPlayer.width /2);
- TPlayer.position.Y = (float)(tiley * 16 - TPlayer.height);
- //We need to send the tile data again to prevent clients from thinking they *really* destroyed blocks just now.
-
- SendTileSquare(tilex, tiley, 10);
-
- return true;
- }
-
- public void Spawn()
- {
- Spawn(TPlayer.SpawnX, TPlayer.SpawnY);
- }
-
- public void Spawn(int tilex, int tiley)
- {
- using (var ms = new MemoryStream())
- {
- var msg = new SpawnMsg
- {
- PlayerIndex = (byte) Index,
- TileX = tilex,
- TileY = tiley
- };
- msg.PackFull(ms);
- SendRawData(ms.ToArray());
- }
- }
-
- public void RemoveProjectile(int index, int owner)
- {
- using (var ms = new MemoryStream())
- {
- var msg = new ProjectileRemoveMsg
- {
- Index = (short) index,
- Owner = (byte) owner
- };
- msg.PackFull(ms);
- SendRawData(ms.ToArray());
- }
- }
-
- public virtual bool SendTileSquare(int x, int y, int size = 10)
- {
- try
- {
- int num = (size - 1)/2;
- int m_x=0;
- int m_y=0;
-
- if (x - num <0){
- m_x=0;
- }else{
- m_x = x - num;
- }
-
- if (y - num <0){
- m_y=0;
- }else{
- m_y = y - num;
- }
-
- if (m_x + size > Main.maxTilesX){
- m_x=Main.maxTilesX - size;
- }
-
- if (m_y + size > Main.maxTilesY){
- m_y=Main.maxTilesY - size;
- }
-
- SendData(PacketTypes.TileSendSquare, "", size, m_x, m_y);
- return true;
- }
- catch (IndexOutOfRangeException)
- {
-
- // This is expected if square exceeds array.
- }
- catch (Exception ex)
- {
- Log.Error(ex.ToString());
- }
- return false;
- }
-
- public bool GiveItemCheck(int type, string name, int width, int height, int stack, int prefix = 0)
- {
- if ((TShock.Itembans.ItemIsBanned(name) && TShock.Config.PreventBannedItemSpawn) &&
- (TShock.Itembans.ItemIsBanned(name, this) || !TShock.Config.AllowAllowedGroupsToSpawnBannedItems))
- return false;
-
- GiveItem(type,name,width,height,stack,prefix);
- return true;
- }
-
- public virtual void GiveItem(int type, string name, int width, int height, int stack, int prefix = 0)
- {
- int itemid = Item.NewItem((int) X, (int) Y, width, height, type, stack, true, prefix);
-
- // This is for special pickaxe/hammers/swords etc
- Main.item[itemid].SetDefaults(name);
- // The set default overrides the wet and stack set by NewItem
- Main.item[itemid].wet = Collision.WetCollision(Main.item[itemid].position, Main.item[itemid].width,
- Main.item[itemid].height);
- Main.item[itemid].stack = stack;
- Main.item[itemid].owner = Index;
- Main.item[itemid].prefix = (byte) prefix;
- NetMessage.SendData((int) PacketTypes.ItemDrop, -1, -1, "", itemid, 0f, 0f, 0f);
- NetMessage.SendData((int) PacketTypes.ItemOwner, -1, -1, "", itemid, 0f, 0f, 0f);
- }
-
- public virtual void SendInfoMessage(string msg)
- {
- SendMessage(msg, Color.Yellow);
- }
-
- public virtual void SendSuccessMessage(string msg)
- {
- SendMessage(msg, Color.Green);
- }
-
- public virtual void SendWarningMessage(string msg)
- {
- SendMessage(msg, Color.OrangeRed);
- }
-
- public virtual void SendErrorMessage(string msg)
- {
- SendMessage(msg, Color.Red);
- }
-
- [Obsolete("Use SendErrorMessage, SendInfoMessage, or SendWarningMessage, or a custom color instead.")]
- public virtual void SendMessage(string msg)
- {
- SendMessage(msg, 0, 255, 0);
- }
-
- public virtual void SendMessage(string msg, Color color)
- {
- SendMessage(msg, color.R, color.G, color.B);
- }
-
- public virtual void SendMessage(string msg, byte red, byte green, byte blue)
- {
- SendData(PacketTypes.ChatText, msg, 255, red, green, blue);
- }
-
- public virtual void SendMessageFromPlayer(string msg, byte red, byte green, byte blue, int ply)
- {
- SendDataFromPlayer(PacketTypes.ChatText, ply, msg, red, green, blue, 0);
- }
-
- public virtual void DamagePlayer(int damage)
- {
- NetMessage.SendData((int) PacketTypes.PlayerDamage, -1, -1, "", Index, ((new Random()).Next(-1, 1)), damage,
- (float) 0);
- }
-
- public virtual void SetTeam(int team)
- {
- Main.player[Index].team = team;
- SendData(PacketTypes.PlayerTeam, "", Index);
- }
-
- public virtual void Disable(string reason = "")
- {
- LastThreat = DateTime.UtcNow;
- SetBuff(33, 330, true); //Weak
- SetBuff(32, 330, true); //Slow
- SetBuff(23, 330, true); //Cursed
- if (!string.IsNullOrEmpty(reason))
- Log.ConsoleInfo(string.Format("Player {0} has been disabled for {1}.", Name, reason));
-
- var trace = new StackTrace();
- StackFrame frame = null;
- frame = trace.GetFrame(1);
- if (frame != null && frame.GetMethod().DeclaringType != null)
- Log.Debug(frame.GetMethod().DeclaringType.Name + " called Disable().");
- }
-
- public virtual void Whoopie(object time)
- {
- var time2 = (int) time;
- var launch = DateTime.UtcNow;
- var startname = Name;
- SendMessage("You are now being annoyed.", Color.Red);
- while ((DateTime.UtcNow - launch).TotalSeconds < time2 && startname == Name)
- {
- SendData(PacketTypes.NpcSpecial, number: Index, number2: 2f);
- Thread.Sleep(50);
- }
- }
-
- public virtual void SetBuff(int type, int time = 3600, bool bypass = false)
- {
- if ((DateTime.UtcNow - LastThreat).TotalMilliseconds < 5000 && !bypass)
- return;
-
- SendData(PacketTypes.PlayerAddBuff, number: Index, number2: type, number3: time);
- }
-
- //Todo: Separate this into a few functions. SendTo, SendToAll, etc
- public virtual void SendData(PacketTypes msgType, string text = "", int number = 0, float number2 = 0f,
- float number3 = 0f, float number4 = 0f, int number5 = 0)
- {
- if (RealPlayer && !ConnectionAlive)
- return;
-
- NetMessage.SendData((int) msgType, Index, -1, text, number, number2, number3, number4, number5);
- }
-
- public virtual void SendDataFromPlayer(PacketTypes msgType, int ply, string text = "", float number2 = 0f, float number3 = 0f, float number4 = 0f, int number5 = 0)
- {
- if (RealPlayer && !ConnectionAlive)
- return;
-
- NetMessage.SendData((int) msgType, Index, -1, text, ply, number2, number3, number4, number5);
- }
-
- public virtual bool SendRawData(byte[] data)
- {
- if (!RealPlayer || !ConnectionAlive)
- return false;
-
- return TShock.SendBytes(Netplay.serverSock[Index], data);
- }
-
- ///
- /// Adds a command callback to a specified command string.
- ///
- /// The string representing the command i.e "yes" == /yes
- /// The method that will be executed on confirmation ie user accepts
- public void AddResponse( string name, Action