1148 lines
31 KiB
C#
1148 lines
31 KiB
C#
/*
|
|
TShock, a server mod for Terraria
|
|
Copyright (C) 2011-2013 Nyx Studios (fka. 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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
|
|
{
|
|
/// <summary>
|
|
/// This represents the server as a player.
|
|
/// </summary>
|
|
public static readonly TSServerPlayer Server = new TSServerPlayer();
|
|
|
|
/// <summary>
|
|
/// This player represents all the players.
|
|
/// </summary>
|
|
public static readonly TSPlayer All = new TSPlayer("All");
|
|
|
|
/// <summary>
|
|
/// The amount of tiles that the player has killed in the last second.
|
|
/// </summary>
|
|
public int TileKillThreshold { get; set; }
|
|
|
|
/// <summary>
|
|
/// The amount of tiles the player has placed in the last second.
|
|
/// </summary>
|
|
public int TilePlaceThreshold { get; set; }
|
|
|
|
/// <summary>
|
|
/// The amount of liquid( in tiles ) that the player has placed in the last second.
|
|
/// </summary>
|
|
public int TileLiquidThreshold { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of projectiles created by the player in the last second.
|
|
/// </summary>
|
|
public int ProjectileThreshold { get; set; }
|
|
|
|
/// <summary>
|
|
/// A timer to keep track of whether or not the player has recently thrown an explosive
|
|
// </summary>
|
|
public int RecentFuse = 0;
|
|
|
|
/// <summary>
|
|
/// A queue of tiles destroyed by the player for reverting.
|
|
/// </summary>
|
|
public Dictionary<Vector2, Tile> TilesDestroyed { get; protected set; }
|
|
|
|
/// <summary>
|
|
/// A queue of tiles placed by the player for reverting.
|
|
/// </summary>
|
|
public Dictionary<Vector2, Tile> TilesCreated { get; protected set; }
|
|
|
|
public int FirstMaxHP { get; set; }
|
|
|
|
public int FirstMaxMP { get; set; }
|
|
|
|
/// <summary>
|
|
/// The player's group.
|
|
/// </summary>
|
|
public Group Group
|
|
{
|
|
get
|
|
{
|
|
if (tempGroup != null)
|
|
return tempGroup;
|
|
return group;
|
|
}
|
|
set { group = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The player's temporary group. This overrides the user's actual group.
|
|
/// </summary>
|
|
public Group tempGroup = null;
|
|
|
|
private Group group = null;
|
|
|
|
public bool ReceivedInfo { get; set; }
|
|
|
|
/// <summary>
|
|
/// The players index in the player array( Main.players[] ).
|
|
/// </summary>
|
|
public int Index { get; protected set; }
|
|
|
|
/// <summary>
|
|
/// The last time the player changed their team or pvp status.
|
|
/// </summary>
|
|
public DateTime LastPvpChange;
|
|
|
|
/// <summary>
|
|
/// Temp points for use in regions and other plugins.
|
|
/// </summary>
|
|
public Point[] TempPoints = new Point[2];
|
|
|
|
/// <summary>
|
|
/// Whether the player is waiting to place/break a tile to set as a temp point.
|
|
/// </summary>
|
|
public int AwaitingTempPoint { get; set; }
|
|
|
|
/// <summary>
|
|
/// A list of command callbacks indexed by the command they need to do.
|
|
/// </summary>
|
|
public Dictionary<string, Action<object>> AwaitingResponse;
|
|
|
|
public bool AwaitingName { get; set; }
|
|
|
|
public string[] AwaitingNameParameters { get; set; }
|
|
|
|
/// <summary>
|
|
/// The last time a player broke a grief check.
|
|
/// </summary>
|
|
public DateTime LastThreat { get; set; }
|
|
|
|
/// <summary>
|
|
/// Not used, can be removed.
|
|
/// </summary>
|
|
public DateTime LastTileChangeNotify { get; set; }
|
|
|
|
public bool InitSpawn;
|
|
|
|
/// <summary>
|
|
/// Whether the player should see logs.
|
|
/// </summary>
|
|
public bool DisplayLogs = true;
|
|
|
|
public Vector2 oldSpawn = Vector2.Zero;
|
|
|
|
/// <summary>
|
|
/// The last player that the player whispered with( to or from ).
|
|
/// </summary>
|
|
public TSPlayer LastWhisper;
|
|
|
|
/// <summary>
|
|
/// The number of unsuccessful login attempts.
|
|
/// </summary>
|
|
public int LoginAttempts { get; set; }
|
|
|
|
public Vector2 TeleportCoords = new Vector2(-1, -1);
|
|
|
|
public Vector2 LastNetPosition = Vector2.Zero;
|
|
|
|
/// <summary>
|
|
/// The player's login name.
|
|
/// </summary>
|
|
public string UserAccountName { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether the player performed a valid login attempt (i.e. entered valid user name and password) but is still blocked
|
|
/// from logging in because of SSI.
|
|
/// </summary>
|
|
public bool LoginFailsBySsi { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether the player is logged in or not.
|
|
/// </summary>
|
|
public bool IsLoggedIn;
|
|
|
|
/// <summary>
|
|
/// Whether the player has sent their whole inventory to the server while connecting.
|
|
/// </summary>
|
|
public bool HasSentInventory { get; set; }
|
|
|
|
/// <summary>
|
|
/// The player's user id( from the db ).
|
|
/// </summary>
|
|
public int UserID = -1;
|
|
|
|
/// <summary>
|
|
/// Whether the player has been nagged about logging in.
|
|
/// </summary>
|
|
public bool HasBeenNaggedAboutLoggingIn;
|
|
|
|
public bool TPAllow = true;
|
|
|
|
/// <summary>
|
|
/// Whether the player is muted or not.
|
|
/// </summary>
|
|
public bool mute;
|
|
|
|
public bool TpLock;
|
|
|
|
private Player FakePlayer;
|
|
|
|
public bool RequestedSection;
|
|
|
|
/// <summary>
|
|
/// The last time the player died.
|
|
/// </summary>
|
|
public DateTime LastDeath { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether the player is dead or not.
|
|
/// </summary>
|
|
public bool Dead;
|
|
|
|
public string Country = "??";
|
|
|
|
/// <summary>
|
|
/// The players difficulty( normal[softcore], mediumcore, hardcore ).
|
|
/// </summary>
|
|
public int Difficulty;
|
|
|
|
private string CacheIP;
|
|
|
|
public string IgnoreActionsForInventory = "none";
|
|
|
|
public string IgnoreActionsForCheating = "none";
|
|
|
|
public string IgnoreActionsForDisabledArmor = "none";
|
|
|
|
public bool IgnoreActionsForClearingTrashCan;
|
|
|
|
/// <summary>
|
|
/// The player's server side inventory data.
|
|
/// </summary>
|
|
public PlayerData PlayerData;
|
|
|
|
/// <summary>
|
|
/// Whether the player needs to specify a password upon connection( either server or user account ).
|
|
/// </summary>
|
|
public bool RequiresPassword;
|
|
|
|
public bool SilentKickInProgress;
|
|
|
|
public bool SilentJoinInProgress;
|
|
|
|
/// <summary>
|
|
/// A list of points where ice tiles have been placed.
|
|
/// </summary>
|
|
public List<Point> IceTiles;
|
|
|
|
/// <summary>
|
|
/// Unused, can be removed.
|
|
/// </summary>
|
|
public long RPm = 1;
|
|
|
|
/// <summary>
|
|
/// World protection message cool down.
|
|
/// </summary>
|
|
public long WPm = 1;
|
|
|
|
/// <summary>
|
|
/// Spawn protection message cool down.
|
|
/// </summary>
|
|
public long SPm = 1;
|
|
|
|
/// <summary>
|
|
/// Permission to build message cool down.
|
|
/// </summary>
|
|
public long BPm = 1;
|
|
|
|
/// <summary>
|
|
/// The time in ms when the player has logged in.
|
|
/// </summary>
|
|
public long LoginMS;
|
|
|
|
/// <summary>
|
|
/// Whether the player has been harrassed about logging in due to server side inventory or forced login.
|
|
/// </summary>
|
|
public bool LoginHarassed = false;
|
|
|
|
/// <summary>
|
|
/// Whether the player is a real, human, player on the server.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the player's inventory to SSI
|
|
/// </summary>
|
|
/// <returns>bool - True/false if it saved successfully</returns>
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Terraria Player
|
|
/// </summary>
|
|
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 < 50; i++) //51 is trash can, 52-55 is coins, 56-59 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<Vector2, Tile>();
|
|
TilesCreated = new Dictionary<Vector2, Tile>();
|
|
Index = index;
|
|
Group = Group.DefaultGroup;
|
|
IceTiles = new List<Point>();
|
|
AwaitingResponse = new Dictionary<string, Action<object>>();
|
|
}
|
|
|
|
protected TSPlayer(String playerName)
|
|
{
|
|
TilesDestroyed = new Dictionary<Vector2, Tile>();
|
|
TilesCreated = new Dictionary<Vector2, Tile>();
|
|
Index = -1;
|
|
FakePlayer = new Player {name = playerName, whoAmi = -1};
|
|
Group = Group.DefaultGroup;
|
|
AwaitingResponse = new Dictionary<string, Action<object>>();
|
|
}
|
|
|
|
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,
|
|
MoonType = (byte)Main.moonType,
|
|
TreeX0 = Main.treeX[0],
|
|
TreeX1 = Main.treeX[1],
|
|
TreeX2 = Main.treeX[2],
|
|
TreeStyle0 = (byte)Main.treeStyle[0],
|
|
TreeStyle1 = (byte)Main.treeStyle[1],
|
|
TreeStyle2 = (byte)Main.treeStyle[2],
|
|
TreeStyle3 = (byte)Main.treeStyle[3],
|
|
CaveBackX0 = Main.caveBackX[0],
|
|
CaveBackX1 = Main.caveBackX[1],
|
|
CaveBackX2 = Main.caveBackX[2],
|
|
CaveBackStyle0 = (byte)Main.caveBackStyle[0],
|
|
CaveBackStyle1 = (byte)Main.caveBackStyle[1],
|
|
CaveBackStyle2 = (byte)Main.caveBackStyle[2],
|
|
CaveBackStyle3 = (byte)Main.caveBackStyle[3],
|
|
SetBG0 = (byte)WorldGen.treeBG,
|
|
SetBG1 = (byte)WorldGen.corruptBG,
|
|
SetBG2 = (byte)WorldGen.jungleBG,
|
|
SetBG3 = (byte)WorldGen.snowBG,
|
|
SetBG4 = (byte)WorldGen.hallowBG,
|
|
SetBG5 = (byte)WorldGen.crimsonBG,
|
|
SetBG6 = (byte)WorldGen.desertBG,
|
|
SetBG7 = (byte)WorldGen.oceanBG,
|
|
IceBackStyle = (byte)Main.iceBackStyle,
|
|
JungleBackStyle = (byte)Main.jungleBackStyle,
|
|
HellBackStyle = (byte)Main.hellBackStyle,
|
|
WindSpeed = Main.windSpeed,
|
|
NumberOfClouds = (byte)Main.numClouds,
|
|
BossFlags = (WorldGen.shadowOrbSmashed ? BossFlags.OrbSmashed : BossFlags.None) |
|
|
(NPC.downedBoss1 ? BossFlags.DownedBoss1 : BossFlags.None) |
|
|
(NPC.downedBoss2 ? BossFlags.DownedBoss2 : BossFlags.None) |
|
|
(NPC.downedBoss3 ? BossFlags.DownedBoss3 : BossFlags.None) |
|
|
(Main.hardMode ? BossFlags.HardMode : BossFlags.None) |
|
|
(NPC.downedClown ? BossFlags.DownedClown : BossFlags.None),
|
|
BossFlags2 = (NPC.downedMechBoss1 ? BossFlags2.DownedMechBoss1 : BossFlags2.None) |
|
|
(NPC.downedMechBoss2 ? BossFlags2.DownedMechBoss2 : BossFlags2.None) |
|
|
(NPC.downedMechBoss3 ? BossFlags2.DownedMechBoss3 : BossFlags2.None) |
|
|
(NPC.downedMechBossAny ? BossFlags2.DownedMechBossAny : BossFlags2.None) |
|
|
(Main.cloudBGActive == 1f ? BossFlags2.CloudBg : BossFlags2.None) |
|
|
(WorldGen.crimson ? BossFlags2.Crimson : BossFlags2.None),
|
|
Rain = Main.maxRaining,
|
|
WorldName = TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName
|
|
};
|
|
msg.PackFull(ms);
|
|
SendRawData(ms.ToArray());
|
|
}
|
|
}
|
|
|
|
public bool Teleport(float x, float y, byte style = 1)
|
|
{
|
|
if (x > Main.rightWorld - 992)
|
|
{
|
|
x = Main.rightWorld - 992;
|
|
}
|
|
if (x < 992)
|
|
{
|
|
x = 992;
|
|
}
|
|
if (y > Main.bottomWorld - 992)
|
|
{
|
|
y = Main.bottomWorld - 992;
|
|
}
|
|
if (y < 992)
|
|
{
|
|
y = 992;
|
|
}
|
|
|
|
TPlayer.Teleport(new Vector2(x, y), style);
|
|
NetMessage.SendData((int)PacketTypes.Teleport, -1, -1, "", 0, TPlayer.whoAmi, x, y, style);
|
|
return true;
|
|
}
|
|
|
|
public void Heal(int damage = 400)
|
|
{
|
|
NetMessage.SendData((int)PacketTypes.PlayerHealOther, -1, -1, "", this.TPlayer.whoAmi, damage);
|
|
}
|
|
|
|
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, true);
|
|
|
|
// 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;
|
|
Main.item[itemid].noGrabDelay = 1;
|
|
Main.item[itemid].velocity = Main.player[this.Index].velocity;
|
|
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 void SendInfoMessage(string format, params object[] args)
|
|
{
|
|
SendInfoMessage(string.Format(format, args));
|
|
}
|
|
|
|
public virtual void SendSuccessMessage(string msg)
|
|
{
|
|
SendMessage(msg, Color.Green);
|
|
}
|
|
|
|
public void SendSuccessMessage(string format, params object[] args)
|
|
{
|
|
SendSuccessMessage(string.Format(format, args));
|
|
}
|
|
|
|
public virtual void SendWarningMessage(string msg)
|
|
{
|
|
SendMessage(msg, Color.OrangeRed);
|
|
}
|
|
|
|
public void SendWarningMessage(string format, params object[] args)
|
|
{
|
|
SendWarningMessage(string.Format(format, args));
|
|
}
|
|
|
|
public virtual void SendErrorMessage(string msg)
|
|
{
|
|
SendMessage(msg, Color.Red);
|
|
}
|
|
|
|
public void SendErrorMessage(string format, params object[] args)
|
|
{
|
|
SendErrorMessage(string.Format(format, args));
|
|
}
|
|
|
|
[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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a command callback to a specified command string.
|
|
/// </summary>
|
|
/// <param name="name">The string representing the command i.e "yes" == /yes</param>
|
|
/// <param name="callback">The method that will be executed on confirmation ie user accepts</param>
|
|
public void AddResponse( string name, Action<object> callback)
|
|
{
|
|
if( AwaitingResponse.ContainsKey(name))
|
|
{
|
|
AwaitingResponse.Remove(name);
|
|
}
|
|
|
|
AwaitingResponse.Add(name, callback);
|
|
}
|
|
}
|
|
|
|
public class TSRestPlayer : TSPlayer
|
|
{
|
|
internal List<string> CommandOutput = new List<string>();
|
|
|
|
public TSRestPlayer(string playerName, Group playerGroup): base(playerName)
|
|
{
|
|
Group = playerGroup;
|
|
AwaitingResponse = new Dictionary<string, Action<object>>();
|
|
}
|
|
|
|
public override void SendMessage(string msg)
|
|
{
|
|
SendMessage(msg, 0, 255, 0);
|
|
}
|
|
|
|
public override void SendMessage(string msg, Color color)
|
|
{
|
|
SendMessage(msg, color.R, color.G, color.B);
|
|
}
|
|
|
|
public override void SendMessage(string msg, byte red, byte green, byte blue)
|
|
{
|
|
this.CommandOutput.Add(msg);
|
|
}
|
|
|
|
public override void SendInfoMessage(string msg)
|
|
{
|
|
SendMessage(msg, Color.Yellow);
|
|
}
|
|
|
|
public override void SendSuccessMessage(string msg)
|
|
{
|
|
SendMessage(msg, Color.Green);
|
|
}
|
|
|
|
public override void SendWarningMessage(string msg)
|
|
{
|
|
SendMessage(msg, Color.OrangeRed);
|
|
}
|
|
|
|
public override void SendErrorMessage(string msg)
|
|
{
|
|
SendMessage(msg, Color.Red);
|
|
}
|
|
|
|
public List<string> GetCommandOutput()
|
|
{
|
|
return this.CommandOutput;
|
|
}
|
|
}
|
|
|
|
public class TSServerPlayer : TSPlayer
|
|
{
|
|
public static string AccountName = "ServerConsole";
|
|
public TSServerPlayer()
|
|
: base("Server")
|
|
{
|
|
Group = new SuperAdminGroup();
|
|
UserAccountName = AccountName;
|
|
}
|
|
|
|
public override void SendErrorMessage(string msg)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
Console.WriteLine(msg);
|
|
Console.ResetColor();
|
|
}
|
|
|
|
public override void SendInfoMessage(string msg)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
|
Console.WriteLine(msg);
|
|
Console.ResetColor();
|
|
}
|
|
|
|
public override void SendSuccessMessage(string msg)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Green;
|
|
Console.WriteLine(msg);
|
|
Console.ResetColor();
|
|
}
|
|
|
|
public override void SendWarningMessage(string msg)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.DarkRed;
|
|
Console.WriteLine(msg);
|
|
Console.ResetColor();
|
|
}
|
|
|
|
public override void SendMessage(string msg)
|
|
{
|
|
SendMessage(msg, 0, 255, 0);
|
|
}
|
|
|
|
public override void SendMessage(string msg, Color color)
|
|
{
|
|
SendMessage(msg, color.R, color.G, color.B);
|
|
}
|
|
|
|
public override void SendMessage(string msg, byte red, byte green, byte blue)
|
|
{
|
|
Console.WriteLine(msg);
|
|
//RconHandler.Response += msg + "\n";
|
|
}
|
|
|
|
public void SetFullMoon(bool fullmoon)
|
|
{
|
|
Main.moonPhase = 0;
|
|
SetTime(false, 0);
|
|
}
|
|
|
|
public void SetBloodMoon(bool bloodMoon)
|
|
{
|
|
Main.bloodMoon = bloodMoon;
|
|
SetTime(false, 0);
|
|
}
|
|
|
|
public void SetEclipse(bool Eclipse)
|
|
{
|
|
Main.eclipse = Eclipse;
|
|
SetTime(true, 150);
|
|
}
|
|
|
|
public void SetTime(bool dayTime, double time)
|
|
{
|
|
Main.dayTime = dayTime;
|
|
Main.time = time;
|
|
NetMessage.SendData((int) PacketTypes.TimeSet, -1, -1, "", 0, 0, Main.sunModY, Main.moonModY);
|
|
NetMessage.syncPlayers();
|
|
}
|
|
|
|
public void SpawnNPC(int type, string name, int amount, int startTileX, int startTileY, int tileXRange = 100,
|
|
int tileYRange = 50)
|
|
{
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
int spawnTileX;
|
|
int spawnTileY;
|
|
TShock.Utils.GetRandomClearTileWithInRange(startTileX, startTileY, tileXRange, tileYRange, out spawnTileX,
|
|
out spawnTileY);
|
|
int npcid = NPC.NewNPC(spawnTileX*16, spawnTileY*16, type, 0);
|
|
// This is for special slimes
|
|
Main.npc[npcid].SetDefaults(name);
|
|
}
|
|
}
|
|
|
|
public void StrikeNPC(int npcid, int damage, float knockBack, int hitDirection)
|
|
{
|
|
// Main.rand is thread static.
|
|
if (Main.rand == null)
|
|
Main.rand = new Random();
|
|
|
|
Main.npc[npcid].StrikeNPC(damage, knockBack, hitDirection);
|
|
NetMessage.SendData((int) PacketTypes.NpcStrike, -1, -1, "", npcid, damage, knockBack, hitDirection);
|
|
}
|
|
|
|
public void RevertTiles(Dictionary<Vector2, Tile> tiles)
|
|
{
|
|
// Update Main.Tile first so that when tile sqaure is sent it is correct
|
|
foreach (KeyValuePair<Vector2, Tile> entry in tiles)
|
|
{
|
|
Main.tile[(int) entry.Key.X, (int) entry.Key.Y] = entry.Value;
|
|
}
|
|
// Send all players updated tile sqaures
|
|
foreach (Vector2 coords in tiles.Keys)
|
|
{
|
|
All.SendTileSquare((int) coords.X, (int) coords.Y, 3);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class PlayerData
|
|
{
|
|
public NetItem[] inventory = new NetItem[NetItem.maxNetInventory];
|
|
public int maxHealth = 100;
|
|
//public int maxMana = 100;
|
|
public bool exists;
|
|
|
|
public PlayerData(TSPlayer player)
|
|
{
|
|
for (int i = 0; i < NetItem.maxNetInventory; i++)
|
|
{
|
|
this.inventory[i] = new NetItem();
|
|
}
|
|
this.inventory[0].netID = -15;
|
|
this.inventory[0].stack = 1;
|
|
if (player.TPlayer.inventory[0] != null && player.TPlayer.inventory[0].netID == -15)
|
|
this.inventory[0].prefix = player.TPlayer.inventory[0].prefix;
|
|
this.inventory[1].netID = -13;
|
|
this.inventory[1].stack = 1;
|
|
if (player.TPlayer.inventory[1] != null && player.TPlayer.inventory[1].netID == -13)
|
|
this.inventory[1].prefix = player.TPlayer.inventory[1].prefix;
|
|
this.inventory[2].netID = -16;
|
|
this.inventory[2].stack = 1;
|
|
if (player.TPlayer.inventory[2] != null && player.TPlayer.inventory[2].netID == -16)
|
|
this.inventory[2].prefix = player.TPlayer.inventory[2].prefix;
|
|
}
|
|
|
|
public void StoreSlot(int slot, int netID, int prefix, int stack)
|
|
{
|
|
if(slot > (this.inventory.Length - 1)) //if the slot is out of range then dont save
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.inventory[slot].netID = netID;
|
|
if (this.inventory[slot].netID != 0)
|
|
{
|
|
this.inventory[slot].stack = stack;
|
|
this.inventory[slot].prefix = prefix;
|
|
}
|
|
else
|
|
{
|
|
this.inventory[slot].stack = 0;
|
|
this.inventory[slot].prefix = 0;
|
|
}
|
|
}
|
|
|
|
public void CopyInventory(TSPlayer player)
|
|
{
|
|
this.maxHealth = player.TPlayer.statLifeMax;
|
|
Item[] inventory = player.TPlayer.inventory;
|
|
Item[] armor = player.TPlayer.armor;
|
|
Item[] dye = player.TPlayer.dye;
|
|
for (int i = 0; i < NetItem.maxNetInventory; i++)
|
|
{
|
|
if (i < NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots))
|
|
{
|
|
if (player.TPlayer.inventory[i] != null)
|
|
{
|
|
this.inventory[i].netID = inventory[i].netID;
|
|
}
|
|
else
|
|
{
|
|
this.inventory[i].netID = 0;
|
|
}
|
|
|
|
if (this.inventory[i].netID != 0)
|
|
{
|
|
this.inventory[i].stack = inventory[i].stack;
|
|
this.inventory[i].prefix = inventory[i].prefix;
|
|
}
|
|
else
|
|
{
|
|
this.inventory[i].stack = 0;
|
|
this.inventory[i].prefix = 0;
|
|
}
|
|
}
|
|
else if (i < NetItem.maxNetInventory - NetItem.dyeSlots)
|
|
{
|
|
var index = i - (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots));
|
|
if (player.TPlayer.armor[index] != null)
|
|
{
|
|
this.inventory[i].netID = armor[index].netID;
|
|
}
|
|
else
|
|
{
|
|
this.inventory[i].netID = 0;
|
|
}
|
|
|
|
if (this.inventory[i].netID != 0)
|
|
{
|
|
this.inventory[i].stack = armor[index].stack;
|
|
this.inventory[i].prefix = armor[index].prefix;
|
|
}
|
|
else
|
|
{
|
|
this.inventory[i].stack = 0;
|
|
this.inventory[i].prefix = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var index = i - (NetItem.maxNetInventory - NetItem.dyeSlots);
|
|
if (player.TPlayer.dye[index] != null)
|
|
{
|
|
this.inventory[i].netID = armor[index].netID;
|
|
}
|
|
else
|
|
{
|
|
this.inventory[i].netID = 0;
|
|
}
|
|
|
|
if (this.inventory[i].netID != 0)
|
|
{
|
|
this.inventory[i].stack = armor[index].stack;
|
|
this.inventory[i].prefix = armor[index].prefix;
|
|
}
|
|
else
|
|
{
|
|
this.inventory[i].stack = 0;
|
|
this.inventory[i].prefix = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class NetItem
|
|
{
|
|
public static readonly int maxNetInventory = 73;
|
|
public static readonly int armorSlots = 11;
|
|
public static readonly int dyeSlots = 3;
|
|
public int netID;
|
|
public int stack;
|
|
public int prefix;
|
|
|
|
public static string ToString(NetItem[] inventory)
|
|
{
|
|
string inventoryString = "";
|
|
for (int i = 0; i < maxNetInventory; i++)
|
|
{
|
|
if (i != 0)
|
|
inventoryString += "~";
|
|
inventoryString += inventory[i].netID;
|
|
if (inventory[i].netID != 0)
|
|
{
|
|
inventoryString += "," + inventory[i].stack;
|
|
inventoryString += "," + inventory[i].prefix;
|
|
}
|
|
else
|
|
{
|
|
inventoryString += ",0,0";
|
|
}
|
|
}
|
|
return inventoryString;
|
|
}
|
|
|
|
public static NetItem[] Parse(string data)
|
|
{
|
|
NetItem[] inventory = new NetItem[maxNetInventory];
|
|
int i;
|
|
for (i = 0; i < maxNetInventory; i++)
|
|
{
|
|
inventory[i] = new NetItem();
|
|
}
|
|
string[] items = data.Split('~');
|
|
i = 0;
|
|
foreach (string item in items)
|
|
{
|
|
string[] idata = item.Split(',');
|
|
inventory[i].netID = int.Parse(idata[0]);
|
|
inventory[i].stack = int.Parse(idata[1]);
|
|
inventory[i].prefix = int.Parse(idata[2]);
|
|
i++;
|
|
}
|
|
return inventory;
|
|
}
|
|
}
|
|
}
|