/* TShock, a server mod for Terraria Copyright (C) 2011 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 { 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(); } /// /// 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() { WorldGen.saveWorld(); Broadcast("World saved.", Color.Yellow); Log.Info(string.Format("World saved at ({0})", Main.worldPathName)); } /// /// 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. /// /// /// 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() { int num = 0; foreach (TSPlayer player in TShock.Players) { if (player != null && player.Active) { num++; } } return num; } /// /// /// /// /// public List FindPlayer(string ply) { var found = new List(); ply = ply.ToLower(); foreach (TSPlayer player in TShock.Players) { if (player == null) continue; string name = player.Name.ToLower(); if (name.Equals(ply)) return new List { player }; if (name.Contains(ply)) found.Add(player); } return found; } 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)); } private bool TileValid(int tileX, int tileY) { return tileX >= 0 && tileX <= Main.maxTilesX && tileY >= 0 && tileY <= Main.maxTilesY; } private bool TileClear(int tileX, int tileY) { return !Main.tile[tileX, tileY].active; } public List GetItemByIdOrName(string idOrName) { int type = -1; if (int.TryParse(idOrName, out type)) { return new List { GetItemById(type) }; } return GetItemByName(idOrName); } public Item GetItemById(int id) { Item item = new Item(); item.netDefaults(id); return item; } 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; } public List GetNPCByIdOrName(string idOrName) { int type = -1; if (int.TryParse(idOrName, out type)) { return new List { GetNPCById(type) }; } return GetNPCByName(idOrName); } public NPC GetNPCById(int id) { NPC npc = new NPC(); npc.netDefaults(id); return npc; } 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; } public string GetBuffName(int id) { return (id > 0 && id < Main.maxBuffs) ? Main.buffName[id] : "null"; } public string GetBuffDescription(int id) { return (id > 0 && id < Main.maxBuffs) ? Main.buffTip[id] : "null"; } 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; } public string GetPrefixById(int id) { var item = new Item(); item.SetDefaults(0); item.prefix = (byte)id; item.AffixName(); return item.name.Trim(); } public List GetPrefixByName(string name) { Item item = new Item(); item.SetDefaults(0); for (int i = 1; i < 83; i++) { item.prefix = (byte) i; if (item.AffixName().Trim() == name) return new List { i }; } var found = new List(); for (int i = 1; i < 83; i++) { try { item.prefix = (byte) i; if (item.AffixName().Trim().ToLower() == name.ToLower()) return new List { i }; if (item.AffixName().Trim().ToLower().StartsWith(name.ToLower())) found.Add(i); } catch { } } return found; } public List GetPrefixByIdOrName(string idOrName) { int type = -1; if (int.TryParse(idOrName, out type) && type > 0 && type < 84) { 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); } } } /// /// Kicks a player from the server without checking for immunetokick permission. /// /// int player /// string reason public void ForceKick(TSPlayer player, string reason) { if (!player.ConnectionAlive) return; player.Disconnect(reason); Log.ConsoleInfo(string.Format("{0} was force kicked for : {1}", player.IP, reason)); } /// /// Kicks a player from the server. /// /// int player /// string reason public bool Kick(TSPlayer player, string reason, string adminUserName = "") { if (!player.ConnectionAlive) return true; if (!player.Group.HasPermission(Permissions.immunetokick)) { string playerName = player.Name; player.Disconnect(string.Format("Kicked: {0}", reason)); Log.ConsoleInfo(string.Format("Kicked {0} for : {1}", playerName, reason)); if (adminUserName.Length == 0) Broadcast(string.Format("{0} was kicked for {1}", playerName, reason.ToLower())); else Broadcast(string.Format("{0} kicked {1} for {2}", adminUserName, playerName, reason.ToLower())); return true; } return false; } /// /// Bans and kicks a player from the server. /// /// int player /// string reason public bool Ban(TSPlayer player, string reason, string adminUserName = "") { if (!player.ConnectionAlive) return true; if (!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)); if (adminUserName.Length == 0) Broadcast(string.Format("{0} was banned for {1}", playerName, reason.ToLower())); else Broadcast(string.Format("{0} banned {1} for {2}", adminUserName, 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("default"); } /// /// 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 = "md5"; 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; } public int SearchProjectile(short identity) { for (int i = 0; i < Main.maxProjectiles; i++) { if (Main.projectile[i].identity == identity) return i; } return 1000; } 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); } } }