diff --git a/TShock.sln b/TShock.sln
index db301cae..ad3df12f 100644
--- a/TShock.sln
+++ b/TShock.sln
@@ -1,71 +1,71 @@
-
-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockAPI", "TShockAPI\TShockAPI.csproj", "{49606449-072B-4CF5-8088-AA49DA586694}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockDBEditor", "DBEditor\TShockDBEditor.csproj", "{F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{F3742F51-D7BF-4754-A68A-CD944D2A21FF}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{755F5B05-0924-47E9-9563-26EB20FE3F67}"
- ProjectSection(SolutionItems) = preProject
- Local.testsettings = Local.testsettings
- Terraria.vsmdi = Terraria.vsmdi
- EndProjectSection
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockRestTestPlugin", "TShockRestTestPlugin\TShockRestTestPlugin.csproj", "{F2FEDAFB-58DE-4611-9168-A86112C346C7}"
-EndProject
-Global
- GlobalSection(TestCaseManagementSettings) = postSolution
- CategoryFile = Terraria.vsmdi
- EndGlobalSection
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|Mixed Platforms = Debug|Mixed Platforms
- Debug|x86 = Debug|x86
- Release|Any CPU = Release|Any CPU
- Release|Mixed Platforms = Release|Mixed Platforms
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {49606449-072B-4CF5-8088-AA49DA586694}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {49606449-072B-4CF5-8088-AA49DA586694}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {49606449-072B-4CF5-8088-AA49DA586694}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {49606449-072B-4CF5-8088-AA49DA586694}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {49606449-072B-4CF5-8088-AA49DA586694}.Debug|x86.ActiveCfg = Debug|Any CPU
- {49606449-072B-4CF5-8088-AA49DA586694}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {49606449-072B-4CF5-8088-AA49DA586694}.Release|Any CPU.Build.0 = Release|Any CPU
- {49606449-072B-4CF5-8088-AA49DA586694}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {49606449-072B-4CF5-8088-AA49DA586694}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {49606449-072B-4CF5-8088-AA49DA586694}.Release|x86.ActiveCfg = Release|Any CPU
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|Any CPU.ActiveCfg = Debug|x86
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|Mixed Platforms.Build.0 = Debug|x86
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|x86.ActiveCfg = Debug|x86
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|x86.Build.0 = Debug|x86
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|Any CPU.ActiveCfg = Release|x86
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|Mixed Platforms.ActiveCfg = Release|x86
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|Mixed Platforms.Build.0 = Release|x86
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|x86.ActiveCfg = Release|x86
- {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|x86.Build.0 = Release|x86
- {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Release|x86.ActiveCfg = Release|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Any CPU.Build.0 = Release|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|x86.ActiveCfg = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 11
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{755F5B05-0924-47E9-9563-26EB20FE3F67}"
+ ProjectSection(SolutionItems) = preProject
+ Local.testsettings = Local.testsettings
+ Terraria.vsmdi = Terraria.vsmdi
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockAPI", "TShockAPI\TShockAPI.csproj", "{49606449-072B-4CF5-8088-AA49DA586694}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockDBEditor", "DBEditor\TShockDBEditor.csproj", "{F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{F3742F51-D7BF-4754-A68A-CD944D2A21FF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockRestTestPlugin", "TShockRestTestPlugin\TShockRestTestPlugin.csproj", "{F2FEDAFB-58DE-4611-9168-A86112C346C7}"
+EndProject
+Global
+ GlobalSection(TestCaseManagementSettings) = postSolution
+ CategoryFile = Terraria.vsmdi
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|Mixed Platforms = Debug|Mixed Platforms
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|Mixed Platforms = Release|Mixed Platforms
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {49606449-072B-4CF5-8088-AA49DA586694}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {49606449-072B-4CF5-8088-AA49DA586694}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {49606449-072B-4CF5-8088-AA49DA586694}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {49606449-072B-4CF5-8088-AA49DA586694}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {49606449-072B-4CF5-8088-AA49DA586694}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {49606449-072B-4CF5-8088-AA49DA586694}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {49606449-072B-4CF5-8088-AA49DA586694}.Release|Any CPU.Build.0 = Release|Any CPU
+ {49606449-072B-4CF5-8088-AA49DA586694}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {49606449-072B-4CF5-8088-AA49DA586694}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {49606449-072B-4CF5-8088-AA49DA586694}.Release|x86.ActiveCfg = Release|Any CPU
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|x86.ActiveCfg = Debug|x86
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Debug|x86.Build.0 = Debug|x86
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|Any CPU.ActiveCfg = Release|x86
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|Mixed Platforms.Build.0 = Release|x86
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|x86.ActiveCfg = Release|x86
+ {F1AE395C-6B4D-40E0-8BF8-0D8A126488D3}.Release|x86.Build.0 = Release|x86
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|x86.ActiveCfg = Release|Any CPU
+ {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {F3742F51-D7BF-4754-A68A-CD944D2A21FF}.Release|x86.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs
index 697ae2f9..e9ba843d 100644
--- a/TShockAPI/Utils.cs
+++ b/TShockAPI/Utils.cs
@@ -1,822 +1,822 @@
-/*
-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.IO;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Security.Cryptography;
-using System.Text;
-using Terraria;
-
-namespace TShockAPI
-{
- public class Utils
- {
- private readonly static int firstItemPrefix = 1;
- private readonly static int lastItemPrefix = 83;
- // Utils is a Singleton
- private static readonly Utils instance = new Utils();
- private Utils() {}
- public static Utils Instance { get { return instance; } }
-
- public Random Random = new Random();
- //private static List groups = new List();
-
- ///
- /// Provides the real IP address from a RemoteEndPoint string that contains a port and an IP
- ///
- /// A string IPv4 address in IP:PORT form.
- /// A string IPv4 address.
- public string GetRealIP(string mess)
- {
- return mess.Split(':')[0];
- }
-
- ///
- /// Used for some places where a list of players might be used.
- ///
- /// String of players seperated by commas.
- public string GetPlayers()
- {
- var sb = new StringBuilder();
- foreach (TSPlayer player in TShock.Players)
- {
- if (player != null && player.Active)
- {
- if (sb.Length != 0)
- {
- sb.Append(", ");
- }
- sb.Append(player.Name);
- }
- }
- return sb.ToString();
- }
-
- ///
- /// Used for some places where a list of players might be used.
- ///
- /// String of players and their id seperated by commas.
- public string GetPlayersWithIds()
- {
- var sb = new StringBuilder();
- foreach (TSPlayer player in TShock.Players)
- {
- if (player != null && player.Active)
- {
- if (sb.Length != 0)
- {
- sb.Append(", ");
- }
- sb.Append(player.Name);
- string id = "(ID: " + Convert.ToString(TShock.Users.GetUserID(player.UserAccountName)) + ", IX:" + player.Index + ")";
- sb.Append(id);
- }
- }
- return sb.ToString();
- }
-
- ///
- /// Finds a player and gets IP as string
- ///
- /// Player name
- public string GetPlayerIP(string playername)
- {
- foreach (TSPlayer player in TShock.Players)
- {
- if (player != null && player.Active)
- {
- if (playername.ToLower() == player.Name.ToLower())
- {
- return player.IP;
- }
- }
- }
- return null;
- }
-
- ///
- /// It's a clamp function
- ///
- ///
- /// Value to clamp
- /// Maximum bounds of the clamp
- /// Minimum bounds of the clamp
- ///
- public T Clamp(T value, T max, T min)
- where T : IComparable
- {
- T result = value;
- if (value.CompareTo(max) > 0)
- result = max;
- if (value.CompareTo(min) < 0)
- result = min;
- return result;
- }
-
- ///
- /// Saves the map data
- ///
- public void SaveWorld()
- {
- SaveManager.Instance.SaveWorld();
- }
-
- ///
- /// Broadcasts a message to all players
- ///
- /// string message
- public void Broadcast(string msg)
- {
- Broadcast(msg, Color.Green);
- }
-
- public void Broadcast(string msg, byte red, byte green, byte blue)
- {
- TSPlayer.All.SendMessage(msg, red, green, blue);
- TSPlayer.Server.SendMessage(msg, red, green, blue);
- Log.Info(string.Format("Broadcast: {0}", msg));
- }
-
- public void Broadcast(string msg, Color color)
- {
- Broadcast(msg, color.R, color.G, color.B);
- }
-
- ///
- /// Sends message to all users with 'logs' permission.
- ///
- /// Message to send
- /// Color of the message
- public void SendLogs(string log, Color color)
- {
- Log.Info(log);
- TSPlayer.Server.SendMessage(log, color);
- foreach (TSPlayer player in TShock.Players)
- {
- if (player != null && player.Active && player.Group.HasPermission(Permissions.logs) && player.DisplayLogs &&
- TShock.Config.DisableSpewLogs == false)
- player.SendMessage(log, color);
- }
- }
-
- ///
- /// The number of active players on the server.
- ///
- /// int playerCount
- public int ActivePlayers()
- {
- return Main.player.Where(p => null != p && p.active).Count();
- }
-
- ///
- /// Finds a TSPlayer based on name or id
- ///
- /// Player name
- ///
- public List FindPlayer(string ply)
- {
- var found = new List();
- // Avoid errors caused by null search
- if (null == ply)
- return found;
- ply = ply.ToLower();
- foreach (TSPlayer player in TShock.Players)
- {
- if (player == null)
- continue;
-
- try
- {
- if (Convert.ToInt32(ply) == player.Index && player.Active)
- {
- return new List { player };
- }
- }
- catch (Exception e)
- {
- // Conversion failed
- }
-
- string name = player.Name.ToLower();
- if (name.Equals(ply))
- return new List {player};
- if (name.Contains(ply))
- found.Add(player);
- }
- return found;
- }
-
- ///
- /// Gets a random clear tile in range
- ///
- /// Bound X
- /// Bound Y
- /// Range on the X axis
- /// Range on the Y axis
- /// X location
- /// Y location
- public void GetRandomClearTileWithInRange(int startTileX, int startTileY, int tileXRange, int tileYRange,
- out int tileX, out int tileY)
- {
- int j = 0;
- do
- {
- if (j == 100)
- {
- tileX = startTileX;
- tileY = startTileY;
- break;
- }
-
- tileX = startTileX + Random.Next(tileXRange*-1, tileXRange);
- tileY = startTileY + Random.Next(tileYRange*-1, tileYRange);
- j++;
- } while (TileValid(tileX, tileY) && !TileClear(tileX, tileY));
- }
-
- ///
- /// Determines if a tile is valid
- ///
- /// Location X
- /// Location Y
- /// If the tile is valid
- private bool TileValid(int tileX, int tileY)
- {
- return tileX >= 0 && tileX <= Main.maxTilesX && tileY >= 0 && tileY <= Main.maxTilesY;
- }
-
- ///
- /// Clears a tile
- ///
- /// Location X
- /// Location Y
- /// The state of the tile
- private bool TileClear(int tileX, int tileY)
- {
- return !Main.tile[tileX, tileY].active;
- }
-
- ///
- /// Gets a list of items by ID or name
- ///
- /// Item ID or name
- /// List of Items
- public List- GetItemByIdOrName(string idOrName)
- {
- int type = -1;
- if (int.TryParse(idOrName, out type))
- {
- return new List
- {GetItemById(type)};
- }
- return GetItemByName(idOrName);
- }
-
- ///
- /// Gets an item by ID
- ///
- /// ID
- /// Item
- public Item GetItemById(int id)
- {
- Item item = new Item();
- item.netDefaults(id);
- return item;
- }
-
- ///
- /// Gets items by name
- ///
- /// name
- /// List of Items
- public List
- GetItemByName(string name)
- {
- //Method #1 - must be exact match, allows support for different pickaxes/hammers/swords etc
- for (int i = 1; i < Main.maxItemTypes; i++)
- {
- Item item = new Item();
- item.SetDefaults(name);
- if (item.name == name)
- return new List
- {item};
- }
- //Method #2 - allows impartial matching
- var found = new List
- ();
- for (int i = -24; i < Main.maxItemTypes; i++)
- {
- try
- {
- Item item = new Item();
- item.netDefaults(i);
- if (item.name.ToLower() == name.ToLower())
- return new List
- {item};
- if (item.name.ToLower().StartsWith(name.ToLower()))
- found.Add(item);
- }
- catch
- {
- }
- }
- return found;
- }
-
- ///
- /// Gets an NPC by ID or Name
- ///
- ///
- /// List of NPCs
- public List GetNPCByIdOrName(string idOrName)
- {
- int type = -1;
- if (int.TryParse(idOrName, out type))
- {
- return new List {GetNPCById(type)};
- }
- return GetNPCByName(idOrName);
- }
-
- ///
- /// Gets an NPC by ID
- ///
- /// ID
- /// NPC
- public NPC GetNPCById(int id)
- {
- NPC npc = new NPC();
- npc.netDefaults(id);
- return npc;
- }
-
- ///
- /// Gets a NPC by name
- ///
- /// Name
- /// List of matching NPCs
- public List GetNPCByName(string name)
- {
- //Method #1 - must be exact match, allows support for different coloured slimes
- for (int i = -17; i < Main.maxNPCTypes; i++)
- {
- NPC npc = new NPC();
- npc.SetDefaults(name);
- if (npc.name == name)
- return new List {npc};
- }
- //Method #2 - allows impartial matching
- var found = new List();
- for (int i = 1; i < Main.maxNPCTypes; i++)
- {
- NPC npc = new NPC();
- npc.netDefaults(i);
- if (npc.name.ToLower() == name.ToLower())
- return new List {npc};
- if (npc.name.ToLower().StartsWith(name.ToLower()))
- found.Add(npc);
- }
- return found;
- }
-
- ///
- /// Gets a buff name by id
- ///
- /// ID
- /// name
- public string GetBuffName(int id)
- {
- return (id > 0 && id < Main.maxBuffs) ? Main.buffName[id] : "null";
- }
-
- ///
- /// Gets the description of a buff
- ///
- /// ID
- /// description
- public string GetBuffDescription(int id)
- {
- return (id > 0 && id < Main.maxBuffs) ? Main.buffTip[id] : "null";
- }
-
- ///
- /// Gets a list of buffs by name
- ///
- /// name
- /// Matching list of buff ids
- public List GetBuffByName(string name)
- {
- for (int i = 1; i < Main.maxBuffs; i++)
- {
- if (Main.buffName[i].ToLower() == name)
- return new List {i};
- }
- var found = new List();
- for (int i = 1; i < Main.maxBuffs; i++)
- {
- if (Main.buffName[i].ToLower().StartsWith(name.ToLower()))
- found.Add(i);
- }
- return found;
- }
-
- ///
- /// Gets a prefix based on its id
- ///
- /// ID
- /// Prefix name
- public string GetPrefixById(int id)
- {
- var item = new Item();
- item.SetDefaults(0);
- item.prefix = (byte) id;
- item.AffixName();
- return item.name.Trim();
- }
-
- ///
- /// Gets a list of prefixes by name
- ///
- /// Name
- /// List of prefix IDs
- public List GetPrefixByName(string name)
- {
- Item item = new Item();
- item.SetDefaults(0);
- string lowerName = name.ToLower();
- var found = new List();
- for (int i = firstItemPrefix; i <= lastItemPrefix; i++)
- {
- try
- {
- item.prefix = (byte)i;
- string trimmed = item.AffixName().Trim();
- if (trimmed == name)
- {
- // Exact match
- found.Add(i);
- return found;
- }
- else
- {
- string trimmedLower = trimmed.ToLower();
- if (trimmedLower == lowerName)
- {
- // Exact match (caseinsensitive)
- found.Add(i);
- return found;
- }
- else if (trimmedLower.StartsWith(lowerName)) // Partial match
- found.Add(i);
- }
- }
- catch
- {
- }
- }
- return found;
- }
-
- ///
- /// Gets a prefix by ID or name
- ///
- /// ID or name
- /// List of prefix IDs
- public List GetPrefixByIdOrName(string idOrName)
- {
- int type = -1;
- if (int.TryParse(idOrName, out type) && type >= firstItemPrefix && type <= lastItemPrefix)
- {
- return new List {type};
- }
- return GetPrefixByName(idOrName);
- }
-
- ///
- /// Kicks all player from the server without checking for immunetokick permission.
- ///
- /// int player
- /// string reason
- public void ForceKickAll(string reason)
- {
- foreach (TSPlayer player in TShock.Players)
- {
- if (player != null && player.Active)
- {
- ForceKick(player, reason);
- }
- }
- }
-
- ///
- /// Stops the server after kicking all players with a reason message, and optionally saving the world
- ///
- /// bool perform a world save before stop (default: true)
- /// string reason (default: "Server shutting down!")
- public void StopServer(bool save = true, string reason = "Server shutting down!")
- {
- ForceKickAll(reason);
- if (save)
- SaveManager.Instance.SaveWorld();
-
- // Save takes a while so kick again
- ForceKickAll(reason);
-
- // Broadcast so console can see we are shutting down as well
- TShock.Utils.Broadcast(reason, Color.Red);
-
- // Disconnect after kick as that signifies server is exiting and could cause a race
- Netplay.disconnect = true;
- }
-
-#if COMPAT_SIGS
- [Obsolete("This method is for signature compatibility for external code only")]
- public void ForceKick(TSPlayer player, string reason)
- {
- Kick(player, reason, true, false, string.Empty);
- }
-#endif
- ///
- /// Kicks a player from the server without checking for immunetokick permission.
- ///
- /// int player
- /// string reason
- /// bool silent (default: false)
- public void ForceKick(TSPlayer player, string reason, bool silent = false, bool saveSSI = false)
- {
- Kick(player, reason, true, silent, null, saveSSI);
- }
-
-#if COMPAT_SIGS
- [Obsolete("This method is for signature compatibility for external code only")]
- public bool Kick(TSPlayer player, string reason, string adminUserName)
- {
- return Kick(player, reason, false, false, adminUserName);
- }
-#endif
- ///
- /// Kicks a player from the server..
- ///
- /// int player
- /// string reason
- /// bool force (default: false)
- /// bool silent (default: false)
- /// string adminUserName (default: null)
- /// bool saveSSI (default: false)
- public bool Kick(TSPlayer player, string reason, bool force = false, bool silent = false, string adminUserName = null, bool saveSSI = false)
- {
- if (!player.ConnectionAlive)
- return true;
- if (force || !player.Group.HasPermission(Permissions.immunetokick))
- {
- string playerName = player.Name;
- player.SilentKickInProgress = silent;
- if (player.IsLoggedIn && saveSSI)
- player.SaveServerInventory();
- player.Disconnect(string.Format("Kicked: {0}", reason));
- Log.ConsoleInfo(string.Format("Kicked {0} for : {1}", playerName, reason));
- string verb = force ? "force " : "";
- if (!silent)
- {
- if (string.IsNullOrWhiteSpace(adminUserName))
- Broadcast(string.Format("{0} was {1}kicked for {2}", playerName, verb, reason.ToLower()));
- else
- Broadcast(string.Format("{0} {1}kicked {2} for {3}", adminUserName, verb, playerName, reason.ToLower()));
- }
- return true;
- }
- return false;
- }
-
-#if COMPAT_SIGS
- [Obsolete("This method is for signature compatibility for external code only")]
- public bool Ban(TSPlayer player, string reason, string adminUserName)
- {
- return Ban(player, reason, false, adminUserName);
- }
-#endif
- ///
- /// Bans and kicks a player from the server.
- ///
- /// int player
- /// string reason
- /// bool force (default: false)
- /// bool silent (default: null)
- public bool Ban(TSPlayer player, string reason, bool force = false, string adminUserName = null)
- {
- if (!player.ConnectionAlive)
- return true;
- if (force || !player.Group.HasPermission(Permissions.immunetoban))
- {
- string ip = player.IP;
- string playerName = player.Name;
- TShock.Bans.AddBan(ip, playerName, reason);
- player.Disconnect(string.Format("Banned: {0}", reason));
- Log.ConsoleInfo(string.Format("Banned {0} for : {1}", playerName, reason));
- string verb = force ? "force " : "";
- if (string.IsNullOrWhiteSpace(adminUserName))
- Broadcast(string.Format("{0} was {1}banned for {1}", playerName, verb, reason.ToLower()));
- else
- Broadcast(string.Format("{0} {1}banned {1} for {2}", adminUserName, verb, playerName, reason.ToLower()));
- return true;
- }
- return false;
- }
-
- ///
- /// Shows a file to the user.
- ///
- /// int player
- /// string filename reletave to savedir
- //Todo: Fix this
- public void ShowFileToUser(TSPlayer player, string file)
- {
- string foo = "";
- using (var tr = new StreamReader(Path.Combine(TShock.SavePath, file)))
- {
- while ((foo = tr.ReadLine()) != null)
- {
- foo = foo.Replace("%map%", Main.worldName);
- foo = foo.Replace("%players%", GetPlayers());
- //foo = SanitizeString(foo);
- if (foo.Substring(0, 1) == "%" && foo.Substring(12, 1) == "%") //Look for a beginning color code.
- {
- string possibleColor = foo.Substring(0, 13);
- foo = foo.Remove(0, 13);
- float[] pC = {0, 0, 0};
- possibleColor = possibleColor.Replace("%", "");
- string[] pCc = possibleColor.Split(',');
- if (pCc.Length == 3)
- {
- try
- {
- player.SendMessage(foo, (byte) Convert.ToInt32(pCc[0]), (byte) Convert.ToInt32(pCc[1]),
- (byte) Convert.ToInt32(pCc[2]));
- continue;
- }
- catch (Exception e)
- {
- Log.Error(e.ToString());
- }
- }
- }
- player.SendMessage(foo);
- }
- }
- }
-
- ///
- /// Returns a Group from the name of the group
- ///
- /// string groupName
- public Group GetGroup(string groupName)
- {
- //first attempt on cached groups
- for (int i = 0; i < TShock.Groups.groups.Count; i++)
- {
- if (TShock.Groups.groups[i].Name.Equals(groupName))
- {
- return TShock.Groups.groups[i];
- }
- }
- return new Group(TShock.Config.DefaultGuestGroupName);
- }
-
- ///
- /// Returns an IPv4 address from a DNS query
- ///
- /// string ip
- public string GetIPv4Address(string hostname)
- {
- try
- {
- //Get the ipv4 address from GetHostAddresses, if an ip is passed it will return that ip
- var ip = Dns.GetHostAddresses(hostname).FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetwork);
- //if the dns query was successful then return it, otherwise return an empty string
- return ip != null ? ip.ToString() : "";
- }
- catch (SocketException)
- {
- }
- return "";
- }
-
- public string HashAlgo = "sha512";
-
- public readonly Dictionary> HashTypes = new Dictionary>
- {
- {"sha512", () => new SHA512Managed()},
- {"sha256", () => new SHA256Managed()},
- {"md5", () => new MD5Cng()},
- {"sha512-xp", () => SHA512.Create()},
- {"sha256-xp", () => SHA256.Create()},
- {"md5-xp", () => MD5.Create()},
- };
-
- ///
- /// Returns a Sha256 string for a given string
- ///
- /// bytes to hash
- /// string sha256
- public string HashPassword(byte[] bytes)
- {
- if (bytes == null)
- throw new NullReferenceException("bytes");
- Func func;
- if (!HashTypes.TryGetValue(HashAlgo.ToLower(), out func))
- throw new NotSupportedException("Hashing algorithm {0} is not supported".SFormat(HashAlgo.ToLower()));
-
- using (var hash = func())
- {
- var ret = hash.ComputeHash(bytes);
- return ret.Aggregate("", (s, b) => s + b.ToString("X2"));
- }
- }
-
- ///
- /// Returns a Sha256 string for a given string
- ///
- /// bytes to hash
- /// string sha256
- public string HashPassword(string password)
- {
- if (string.IsNullOrEmpty(password) || password == "non-existant password")
- return "non-existant password";
- return HashPassword(Encoding.UTF8.GetBytes(password));
- }
-
- ///
- /// Checks if the string contains any unprintable characters
- ///
- /// String to check
- /// True if the string only contains printable characters
- public bool ValidString(string str)
- {
- foreach (var c in str)
- {
- if (c < 0x20 || c > 0xA9)
- return false;
- }
- return true;
- }
-
- ///
- /// Checks if world has hit the max number of chests
- ///
- /// True if the entire chest array is used
- public bool MaxChests()
- {
- for (int i = 0; i < Main.chest.Length; i++)
- {
- if (Main.chest[i] == null)
- return false;
- }
- return true;
- }
-
- ///
- /// Searches for a projectile by identity and owner
- ///
- /// identity
- /// owner
- /// projectile ID
- public int SearchProjectile(short identity, int owner)
- {
- for (int i = 0; i < Main.maxProjectiles; i++)
- {
- if (Main.projectile[i].identity == identity && Main.projectile[i].owner == owner)
- return i;
- }
- return 1000;
- }
-
- ///
- /// Sanitizes input strings
- ///
- /// string
- /// sanitized string
- public string SanitizeString(string str)
- {
- var returnstr = str.ToCharArray();
- for (int i = 0; i < str.Length; i++)
- {
- if (!ValidString(str[i].ToString()))
- returnstr[i] = ' ';
- }
- return new string(returnstr);
- }
- }
-}
+/*
+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.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Security.Cryptography;
+using System.Text;
+using Terraria;
+
+namespace TShockAPI
+{
+ public class Utils
+ {
+ private readonly static int firstItemPrefix = 1;
+ private readonly static int lastItemPrefix = 83;
+ // Utils is a Singleton
+ private static readonly Utils instance = new Utils();
+ private Utils() {}
+ public static Utils Instance { get { return instance; } }
+
+ public Random Random = new Random();
+ //private static List groups = new List();
+
+ ///
+ /// Provides the real IP address from a RemoteEndPoint string that contains a port and an IP
+ ///
+ /// A string IPv4 address in IP:PORT form.
+ /// A string IPv4 address.
+ public string GetRealIP(string mess)
+ {
+ return mess.Split(':')[0];
+ }
+
+ ///
+ /// Used for some places where a list of players might be used.
+ ///
+ /// String of players seperated by commas.
+ public string GetPlayers()
+ {
+ var sb = new StringBuilder();
+ foreach (TSPlayer player in TShock.Players)
+ {
+ if (player != null && player.Active)
+ {
+ if (sb.Length != 0)
+ {
+ sb.Append(", ");
+ }
+ sb.Append(player.Name);
+ }
+ }
+ return sb.ToString();
+ }
+
+ ///
+ /// Used for some places where a list of players might be used.
+ ///
+ /// String of players and their id seperated by commas.
+ public string GetPlayersWithIds()
+ {
+ var sb = new StringBuilder();
+ foreach (TSPlayer player in TShock.Players)
+ {
+ if (player != null && player.Active)
+ {
+ if (sb.Length != 0)
+ {
+ sb.Append(", ");
+ }
+ sb.Append(player.Name);
+ string id = "(ID: " + Convert.ToString(TShock.Users.GetUserID(player.UserAccountName)) + ", IX:" + player.Index + ")";
+ sb.Append(id);
+ }
+ }
+ return sb.ToString();
+ }
+
+ ///
+ /// Finds a player and gets IP as string
+ ///
+ /// Player name
+ public string GetPlayerIP(string playername)
+ {
+ foreach (TSPlayer player in TShock.Players)
+ {
+ if (player != null && player.Active)
+ {
+ if (playername.ToLower() == player.Name.ToLower())
+ {
+ return player.IP;
+ }
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// It's a clamp function
+ ///
+ ///
+ /// Value to clamp
+ /// Maximum bounds of the clamp
+ /// Minimum bounds of the clamp
+ ///
+ public T Clamp(T value, T max, T min)
+ where T : IComparable
+ {
+ T result = value;
+ if (value.CompareTo(max) > 0)
+ result = max;
+ if (value.CompareTo(min) < 0)
+ result = min;
+ return result;
+ }
+
+ ///
+ /// Saves the map data
+ ///
+ public void SaveWorld()
+ {
+ SaveManager.Instance.SaveWorld();
+ }
+
+ ///
+ /// Broadcasts a message to all players
+ ///
+ /// string message
+ public void Broadcast(string msg)
+ {
+ Broadcast(msg, Color.Green);
+ }
+
+ public void Broadcast(string msg, byte red, byte green, byte blue)
+ {
+ TSPlayer.All.SendMessage(msg, red, green, blue);
+ TSPlayer.Server.SendMessage(msg, red, green, blue);
+ Log.Info(string.Format("Broadcast: {0}", msg));
+ }
+
+ public void Broadcast(string msg, Color color)
+ {
+ Broadcast(msg, color.R, color.G, color.B);
+ }
+
+ ///
+ /// Sends message to all users with 'logs' permission.
+ ///
+ /// Message to send
+ /// Color of the message
+ public void SendLogs(string log, Color color)
+ {
+ Log.Info(log);
+ TSPlayer.Server.SendMessage(log, color);
+ foreach (TSPlayer player in TShock.Players)
+ {
+ if (player != null && player.Active && player.Group.HasPermission(Permissions.logs) && player.DisplayLogs &&
+ TShock.Config.DisableSpewLogs == false)
+ player.SendMessage(log, color);
+ }
+ }
+
+ ///
+ /// The number of active players on the server.
+ ///
+ /// int playerCount
+ public int ActivePlayers()
+ {
+ return Main.player.Where(p => null != p && p.active).Count();
+ }
+
+ ///
+ /// Finds a TSPlayer based on name or id
+ ///
+ /// Player name
+ ///
+ public List FindPlayer(string ply)
+ {
+ var found = new List();
+ // Avoid errors caused by null search
+ if (null == ply)
+ return found;
+ ply = ply.ToLower();
+ foreach (TSPlayer player in TShock.Players)
+ {
+ if (player == null)
+ continue;
+
+ try
+ {
+ if (Convert.ToInt32(ply) == player.Index && player.Active)
+ {
+ return new List { player };
+ }
+ }
+ catch (Exception e)
+ {
+ // Conversion failed
+ }
+
+ string name = player.Name.ToLower();
+ if (name.Equals(ply))
+ return new List {player};
+ if (name.Contains(ply))
+ found.Add(player);
+ }
+ return found;
+ }
+
+ ///
+ /// Gets a random clear tile in range
+ ///
+ /// Bound X
+ /// Bound Y
+ /// Range on the X axis
+ /// Range on the Y axis
+ /// X location
+ /// Y location
+ public void GetRandomClearTileWithInRange(int startTileX, int startTileY, int tileXRange, int tileYRange,
+ out int tileX, out int tileY)
+ {
+ int j = 0;
+ do
+ {
+ if (j == 100)
+ {
+ tileX = startTileX;
+ tileY = startTileY;
+ break;
+ }
+
+ tileX = startTileX + Random.Next(tileXRange*-1, tileXRange);
+ tileY = startTileY + Random.Next(tileYRange*-1, tileYRange);
+ j++;
+ } while (TileValid(tileX, tileY) && !TileClear(tileX, tileY));
+ }
+
+ ///
+ /// Determines if a tile is valid
+ ///
+ /// Location X
+ /// Location Y
+ /// If the tile is valid
+ private bool TileValid(int tileX, int tileY)
+ {
+ return tileX >= 0 && tileX <= Main.maxTilesX && tileY >= 0 && tileY <= Main.maxTilesY;
+ }
+
+ ///
+ /// Clears a tile
+ ///
+ /// Location X
+ /// Location Y
+ /// The state of the tile
+ private bool TileClear(int tileX, int tileY)
+ {
+ return !Main.tile[tileX, tileY].active;
+ }
+
+ ///
+ /// Gets a list of items by ID or name
+ ///
+ /// Item ID or name
+ /// List of Items
+ public List
- GetItemByIdOrName(string idOrName)
+ {
+ int type = -1;
+ if (int.TryParse(idOrName, out type))
+ {
+ return new List
- {GetItemById(type)};
+ }
+ return GetItemByName(idOrName);
+ }
+
+ ///
+ /// Gets an item by ID
+ ///
+ /// ID
+ /// Item
+ public Item GetItemById(int id)
+ {
+ Item item = new Item();
+ item.netDefaults(id);
+ return item;
+ }
+
+ ///
+ /// Gets items by name
+ ///
+ /// name
+ /// List of Items
+ public List
- GetItemByName(string name)
+ {
+ //Method #1 - must be exact match, allows support for different pickaxes/hammers/swords etc
+ for (int i = 1; i < Main.maxItemTypes; i++)
+ {
+ Item item = new Item();
+ item.SetDefaults(name);
+ if (item.name == name)
+ return new List
- {item};
+ }
+ //Method #2 - allows impartial matching
+ var found = new List
- ();
+ for (int i = -24; i < Main.maxItemTypes; i++)
+ {
+ try
+ {
+ Item item = new Item();
+ item.netDefaults(i);
+ if (item.name.ToLower() == name.ToLower())
+ return new List
- {item};
+ if (item.name.ToLower().StartsWith(name.ToLower()))
+ found.Add(item);
+ }
+ catch
+ {
+ }
+ }
+ return found;
+ }
+
+ ///
+ /// Gets an NPC by ID or Name
+ ///
+ ///
+ /// List of NPCs
+ public List GetNPCByIdOrName(string idOrName)
+ {
+ int type = -1;
+ if (int.TryParse(idOrName, out type))
+ {
+ return new List {GetNPCById(type)};
+ }
+ return GetNPCByName(idOrName);
+ }
+
+ ///
+ /// Gets an NPC by ID
+ ///
+ /// ID
+ /// NPC
+ public NPC GetNPCById(int id)
+ {
+ NPC npc = new NPC();
+ npc.netDefaults(id);
+ return npc;
+ }
+
+ ///
+ /// Gets a NPC by name
+ ///
+ /// Name
+ /// List of matching NPCs
+ public List GetNPCByName(string name)
+ {
+ //Method #1 - must be exact match, allows support for different coloured slimes
+ for (int i = -17; i < Main.maxNPCTypes; i++)
+ {
+ NPC npc = new NPC();
+ npc.SetDefaults(name);
+ if (npc.name == name)
+ return new List {npc};
+ }
+ //Method #2 - allows impartial matching
+ var found = new List();
+ for (int i = 1; i < Main.maxNPCTypes; i++)
+ {
+ NPC npc = new NPC();
+ npc.netDefaults(i);
+ if (npc.name.ToLower() == name.ToLower())
+ return new List {npc};
+ if (npc.name.ToLower().StartsWith(name.ToLower()))
+ found.Add(npc);
+ }
+ return found;
+ }
+
+ ///
+ /// Gets a buff name by id
+ ///
+ /// ID
+ /// name
+ public string GetBuffName(int id)
+ {
+ return (id > 0 && id < Main.maxBuffs) ? Main.buffName[id] : "null";
+ }
+
+ ///
+ /// Gets the description of a buff
+ ///
+ /// ID
+ /// description
+ public string GetBuffDescription(int id)
+ {
+ return (id > 0 && id < Main.maxBuffs) ? Main.buffTip[id] : "null";
+ }
+
+ ///
+ /// Gets a list of buffs by name
+ ///
+ /// name
+ /// Matching list of buff ids
+ public List GetBuffByName(string name)
+ {
+ for (int i = 1; i < Main.maxBuffs; i++)
+ {
+ if (Main.buffName[i].ToLower() == name)
+ return new List {i};
+ }
+ var found = new List();
+ for (int i = 1; i < Main.maxBuffs; i++)
+ {
+ if (Main.buffName[i].ToLower().StartsWith(name.ToLower()))
+ found.Add(i);
+ }
+ return found;
+ }
+
+ ///
+ /// Gets a prefix based on its id
+ ///
+ /// ID
+ /// Prefix name
+ public string GetPrefixById(int id)
+ {
+ var item = new Item();
+ item.SetDefaults(0);
+ item.prefix = (byte) id;
+ item.AffixName();
+ return item.name.Trim();
+ }
+
+ ///
+ /// Gets a list of prefixes by name
+ ///
+ /// Name
+ /// List of prefix IDs
+ public List GetPrefixByName(string name)
+ {
+ Item item = new Item();
+ item.SetDefaults(0);
+ string lowerName = name.ToLower();
+ var found = new List();
+ for (int i = firstItemPrefix; i <= lastItemPrefix; i++)
+ {
+ try
+ {
+ item.prefix = (byte)i;
+ string trimmed = item.AffixName().Trim();
+ if (trimmed == name)
+ {
+ // Exact match
+ found.Add(i);
+ return found;
+ }
+ else
+ {
+ string trimmedLower = trimmed.ToLower();
+ if (trimmedLower == lowerName)
+ {
+ // Exact match (caseinsensitive)
+ found.Add(i);
+ return found;
+ }
+ else if (trimmedLower.StartsWith(lowerName)) // Partial match
+ found.Add(i);
+ }
+ }
+ catch
+ {
+ }
+ }
+ return found;
+ }
+
+ ///
+ /// Gets a prefix by ID or name
+ ///
+ /// ID or name
+ /// List of prefix IDs
+ public List GetPrefixByIdOrName(string idOrName)
+ {
+ int type = -1;
+ if (int.TryParse(idOrName, out type) && type >= firstItemPrefix && type <= lastItemPrefix)
+ {
+ return new List {type};
+ }
+ return GetPrefixByName(idOrName);
+ }
+
+ ///
+ /// Kicks all player from the server without checking for immunetokick permission.
+ ///
+ /// int player
+ /// string reason
+ public void ForceKickAll(string reason)
+ {
+ foreach (TSPlayer player in TShock.Players)
+ {
+ if (player != null && player.Active)
+ {
+ ForceKick(player, reason);
+ }
+ }
+ }
+
+ ///
+ /// Stops the server after kicking all players with a reason message, and optionally saving the world
+ ///
+ /// bool perform a world save before stop (default: true)
+ /// string reason (default: "Server shutting down!")
+ public void StopServer(bool save = true, string reason = "Server shutting down!")
+ {
+ ForceKickAll(reason);
+ if (save)
+ SaveManager.Instance.SaveWorld();
+
+ // Save takes a while so kick again
+ ForceKickAll(reason);
+
+ // Broadcast so console can see we are shutting down as well
+ TShock.Utils.Broadcast(reason, Color.Red);
+
+ // Disconnect after kick as that signifies server is exiting and could cause a race
+ Netplay.disconnect = true;
+ }
+
+#if COMPAT_SIGS
+ [Obsolete("This method is for signature compatibility for external code only")]
+ public void ForceKick(TSPlayer player, string reason)
+ {
+ Kick(player, reason, true, false, string.Empty);
+ }
+#endif
+ ///
+ /// Kicks a player from the server without checking for immunetokick permission.
+ ///
+ /// int player
+ /// string reason
+ /// bool silent (default: false)
+ public void ForceKick(TSPlayer player, string reason, bool silent = false, bool saveSSI = false)
+ {
+ Kick(player, reason, true, silent, null, saveSSI);
+ }
+
+#if COMPAT_SIGS
+ [Obsolete("This method is for signature compatibility for external code only")]
+ public bool Kick(TSPlayer player, string reason, string adminUserName)
+ {
+ return Kick(player, reason, false, false, adminUserName);
+ }
+#endif
+ ///
+ /// Kicks a player from the server..
+ ///
+ /// int player
+ /// string reason
+ /// bool force (default: false)
+ /// bool silent (default: false)
+ /// string adminUserName (default: null)
+ /// bool saveSSI (default: false)
+ public bool Kick(TSPlayer player, string reason, bool force = false, bool silent = false, string adminUserName = null, bool saveSSI = false)
+ {
+ if (!player.ConnectionAlive)
+ return true;
+ if (force || !player.Group.HasPermission(Permissions.immunetokick))
+ {
+ string playerName = player.Name;
+ player.SilentKickInProgress = silent;
+ if (player.IsLoggedIn && saveSSI)
+ player.SaveServerInventory();
+ player.Disconnect(string.Format("Kicked: {0}", reason));
+ Log.ConsoleInfo(string.Format("Kicked {0} for : {1}", playerName, reason));
+ string verb = force ? "force " : "";
+ if (!silent)
+ {
+ if (string.IsNullOrWhiteSpace(adminUserName))
+ Broadcast(string.Format("{0} was {1}kicked for {2}", playerName, verb, reason.ToLower()));
+ else
+ Broadcast(string.Format("{0} {1}kicked {2} for {3}", adminUserName, verb, playerName, reason.ToLower()));
+ }
+ return true;
+ }
+ return false;
+ }
+
+#if COMPAT_SIGS
+ [Obsolete("This method is for signature compatibility for external code only")]
+ public bool Ban(TSPlayer player, string reason, string adminUserName)
+ {
+ return Ban(player, reason, false, adminUserName);
+ }
+#endif
+ ///
+ /// Bans and kicks a player from the server.
+ ///
+ /// int player
+ /// string reason
+ /// bool force (default: false)
+ /// bool silent (default: null)
+ public bool Ban(TSPlayer player, string reason, bool force = false, string adminUserName = null)
+ {
+ if (!player.ConnectionAlive)
+ return true;
+ if (force || !player.Group.HasPermission(Permissions.immunetoban))
+ {
+ string ip = player.IP;
+ string playerName = player.Name;
+ TShock.Bans.AddBan(ip, playerName, reason);
+ player.Disconnect(string.Format("Banned: {0}", reason));
+ Log.ConsoleInfo(string.Format("Banned {0} for : {1}", playerName, reason));
+ string verb = force ? "force " : "";
+ if (string.IsNullOrWhiteSpace(adminUserName))
+ Broadcast(string.Format("{0} was {1}banned for {1}", playerName, verb, reason.ToLower()));
+ else
+ Broadcast(string.Format("{0} {1}banned {1} for {2}", adminUserName, verb, playerName, reason.ToLower()));
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Shows a file to the user.
+ ///
+ /// int player
+ /// string filename reletave to savedir
+ //Todo: Fix this
+ public void ShowFileToUser(TSPlayer player, string file)
+ {
+ string foo = "";
+ using (var tr = new StreamReader(Path.Combine(TShock.SavePath, file)))
+ {
+ while ((foo = tr.ReadLine()) != null)
+ {
+ foo = foo.Replace("%map%", Main.worldName);
+ foo = foo.Replace("%players%", GetPlayers());
+ //foo = SanitizeString(foo);
+ if (foo.Substring(0, 1) == "%" && foo.Substring(12, 1) == "%") //Look for a beginning color code.
+ {
+ string possibleColor = foo.Substring(0, 13);
+ foo = foo.Remove(0, 13);
+ float[] pC = {0, 0, 0};
+ possibleColor = possibleColor.Replace("%", "");
+ string[] pCc = possibleColor.Split(',');
+ if (pCc.Length == 3)
+ {
+ try
+ {
+ player.SendMessage(foo, (byte) Convert.ToInt32(pCc[0]), (byte) Convert.ToInt32(pCc[1]),
+ (byte) Convert.ToInt32(pCc[2]));
+ continue;
+ }
+ catch (Exception e)
+ {
+ Log.Error(e.ToString());
+ }
+ }
+ }
+ player.SendMessage(foo);
+ }
+ }
+ }
+
+ ///
+ /// Returns a Group from the name of the group
+ ///
+ /// string groupName
+ public Group GetGroup(string groupName)
+ {
+ //first attempt on cached groups
+ for (int i = 0; i < TShock.Groups.groups.Count; i++)
+ {
+ if (TShock.Groups.groups[i].Name.Equals(groupName))
+ {
+ return TShock.Groups.groups[i];
+ }
+ }
+ return new Group(TShock.Config.DefaultGuestGroupName);
+ }
+
+ ///
+ /// Returns an IPv4 address from a DNS query
+ ///
+ /// string ip
+ public string GetIPv4Address(string hostname)
+ {
+ try
+ {
+ //Get the ipv4 address from GetHostAddresses, if an ip is passed it will return that ip
+ var ip = Dns.GetHostAddresses(hostname).FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetwork);
+ //if the dns query was successful then return it, otherwise return an empty string
+ return ip != null ? ip.ToString() : "";
+ }
+ catch (SocketException)
+ {
+ }
+ return "";
+ }
+
+ public string HashAlgo = "sha512";
+
+ public readonly Dictionary> HashTypes = new Dictionary>
+ {
+ {"sha512", () => new SHA512Managed()},
+ {"sha256", () => new SHA256Managed()},
+ {"md5", () => new MD5Cng()},
+ {"sha512-xp", () => SHA512.Create()},
+ {"sha256-xp", () => SHA256.Create()},
+ {"md5-xp", () => MD5.Create()},
+ };
+
+ ///
+ /// Returns a Sha256 string for a given string
+ ///
+ /// bytes to hash
+ /// string sha256
+ public string HashPassword(byte[] bytes)
+ {
+ if (bytes == null)
+ throw new NullReferenceException("bytes");
+ Func func;
+ if (!HashTypes.TryGetValue(HashAlgo.ToLower(), out func))
+ throw new NotSupportedException("Hashing algorithm {0} is not supported".SFormat(HashAlgo.ToLower()));
+
+ using (var hash = func())
+ {
+ var ret = hash.ComputeHash(bytes);
+ return ret.Aggregate("", (s, b) => s + b.ToString("X2"));
+ }
+ }
+
+ ///
+ /// Returns a Sha256 string for a given string
+ ///
+ /// bytes to hash
+ /// string sha256
+ public string HashPassword(string password)
+ {
+ if (string.IsNullOrEmpty(password) || password == "non-existant password")
+ return "non-existant password";
+ return HashPassword(Encoding.UTF8.GetBytes(password));
+ }
+
+ ///
+ /// Checks if the string contains any unprintable characters
+ ///
+ /// String to check
+ /// True if the string only contains printable characters
+ public bool ValidString(string str)
+ {
+ foreach (var c in str)
+ {
+ if (c < 0x20 || c > 0xA9)
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Checks if world has hit the max number of chests
+ ///
+ /// True if the entire chest array is used
+ public bool MaxChests()
+ {
+ for (int i = 0; i < Main.chest.Length; i++)
+ {
+ if (Main.chest[i] == null)
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Searches for a projectile by identity and owner
+ ///
+ /// identity
+ /// owner
+ /// projectile ID
+ public int SearchProjectile(short identity, int owner)
+ {
+ for (int i = 0; i < Main.maxProjectiles; i++)
+ {
+ if (Main.projectile[i].identity == identity && Main.projectile[i].owner == owner)
+ return i;
+ }
+ return 1000;
+ }
+
+ ///
+ /// Sanitizes input strings
+ ///
+ /// string
+ /// sanitized string
+ public string SanitizeString(string str)
+ {
+ var returnstr = str.ToCharArray();
+ for (int i = 0; i < str.Length; i++)
+ {
+ if (!ValidString(str[i].ToString()))
+ returnstr[i] = ' ';
+ }
+ return new string(returnstr);
+ }
+ }
+}
diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj
index 923856ac..bf71afcf 100644
--- a/UnitTests/UnitTests.csproj
+++ b/UnitTests/UnitTests.csproj
@@ -1,147 +1,176 @@
-
-
-
- Debug
- AnyCPU
-
-
- 2.0
- {F3742F51-D7BF-4754-A68A-CD944D2A21FF}
- Library
- Properties
- UnitTests
- UnitTests
- v4.0
- 512
- {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- false
- publish\
- true
- Disk
- false
- Foreground
- 7
- Days
- false
- false
- true
- 0
- 1.0.0.%2a
- false
- true
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
- ..\SqlBins\Mono.Data.Sqlite.dll
-
-
- ..\SqlBins\MySql.Data.dll
-
-
- ..\SqlBins\MySql.Web.dll
-
-
-
- 3.5
-
-
-
-
- False
- .exe
- ..\TerrariaServerBins\TerrariaServer.exe
-
-
-
-
- False
-
-
-
-
-
-
-
-
-
-
-
- {49606449-072B-4CF5-8088-AA49DA586694}
- TShockAPI
-
-
- {F2FEDAFB-58DE-4611-9168-A86112C346C7}
- TShockRestTestPlugin
-
-
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
-
- Always
-
-
-
-
- False
- Microsoft .NET Framework 4 %28x86 and x64%29
- true
-
-
- False
- .NET Framework 3.5 SP1 Client Profile
- false
-
-
- False
- .NET Framework 3.5 SP1
- false
-
-
- False
- Windows Installer 3.1
- true
-
-
-
-
- xcopy .\..\..\..\SqlBins\sqlite3.dll . /Y
-
-
+
+
+
+ Debug
+ AnyCPU
+
+
+ 2.0
+ {F3742F51-D7BF-4754-A68A-CD944D2A21FF}
+ Library
+ Properties
+ UnitTests
+ UnitTests
+ v4.0
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ false
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ true
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ WebTest
+
+
+
+
+ 4.0
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+ ..\SqlBins\Mono.Data.Sqlite.dll
+
+
+ ..\SqlBins\MySql.Data.dll
+
+
+ ..\SqlBins\MySql.Web.dll
+
+
+
+ 3.5
+
+
+
+
+ False
+ .exe
+ ..\TerrariaServerBins\TerrariaServer.exe
+
+
+
+
+ False
+
+
+
+
+
+
+
+
+
+
+
+ {49606449-072B-4CF5-8088-AA49DA586694}
+ TShockAPI
+
+
+ {F2FEDAFB-58DE-4611-9168-A86112C346C7}
+ TShockRestTestPlugin
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+ Always
+
+
+
+
+ False
+ Microsoft .NET Framework 4 %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+ False
+ Windows Installer 3.1
+ true
+
+
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+
+
+
+ xcopy .\..\..\..\SqlBins\sqlite3.dll . /Y
+
+
\ No newline at end of file