/* 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.Security.Cryptography; using System.Text; using System.Net; using Microsoft.Xna.Framework; using Terraria; using System.Linq; namespace TShockAPI { internal class Tools { public static 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 static 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 static 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 static 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 static 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; } /// /// Broadcasts a message to all players /// /// string message public static void Broadcast(string msg) { Broadcast(msg, Color.Green); } public static 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 static void Broadcast(string msg, Color color) { Broadcast(msg, color.R, color.G, color.B); } /// /// Sends message to all users with 'logs' permission. /// /// /// public static 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("logs") && player.DisplayLogs) player.SendMessage(log, color); } } /// /// The number of active players on the server. /// /// int playerCount public static int ActivePlayers() { int num = 0; foreach (TSPlayer player in TShock.Players) { if (player != null && player.Active) { num++; } } return num; } /// /// /// /// /// public static 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 static 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 static bool TileValid(int tileX, int tileY) { return tileX >= 0 && tileX <= Main.maxTilesX && tileY >= 0 && tileY <= Main.maxTilesY; } private static bool TileClear(int tileX, int tileY) { return !Main.tile[tileX, tileY].active; } public static List GetItemByIdOrName(string idOrName) { int type = -1; if (int.TryParse(idOrName, out type)) { return new List { GetItemById(type) }; } return GetItemByName(idOrName); } public static Item GetItemById(int id) { Item item = new Item(); item.SetDefaults(id); return item; } public static 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 = 1; i < Main.maxItemTypes; i++) { try { Item item = new Item(); item.SetDefaults(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 static List GetNPCByIdOrName(string idOrName) { int type = -1; if (int.TryParse(idOrName, out type)) { return new List { GetNPCById(type) }; } return GetNPCByName(idOrName); } public static NPC GetNPCById(int id) { NPC npc = new NPC(); npc.SetDefaults(id); return npc; } public static List GetNPCByName(string name) { //Method #1 - must be exact match, allows support for different coloured slimes for (int i = 1; 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.SetDefaults(i); if (npc.name.ToLower() == name.ToLower()) return new List { npc }; if (npc.name.ToLower().StartsWith(name.ToLower())) found.Add(npc); } return found; } /// /// Kicks all player from the server without checking for immunetokick permission. /// /// int player /// string reason public static 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 static void ForceKick(TSPlayer player, string reason) { if (!player.ConnectionAlive) return; player.Disconnect(reason); Log.Info(string.Format("{0} was force kicked for : {1}", player.IP, reason)); } /// /// Kicks a player from the server. /// /// int player /// string reason public static bool Kick(TSPlayer player, string reason, string adminUserName = "") { if (!player.ConnectionAlive) return true; if (!player.Group.HasPermission("immunetokick")) { string playerName = player.Name; player.Disconnect(string.Format("Kicked: {0}", reason)); Log.Info(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 static bool Ban(TSPlayer player, string reason, string adminUserName = "") { if (!player.ConnectionAlive) return true; if (!player.Group.HasPermission("immunetoban")) { string ip = player.IP; string playerName = player.Name; TShock.Bans.AddBan(ip, playerName, reason); player.Disconnect(string.Format("Banned: {0}", reason)); Log.Info(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; } public static bool HandleCheater(TSPlayer player, string reason) { return HandleBadPlayer(player, "ignorecheatdetection", TShock.Config.BanCheaters, TShock.Config.KickCheaters, reason); } public static bool HandleGriefer(TSPlayer player, string reason) { return HandleBadPlayer(player, "ignoregriefdetection", TShock.Config.BanGriefers, TShock.Config.KickGriefers, reason); } public static bool HandleTntUser(TSPlayer player, string reason) { return HandleBadPlayer(player, "ignoregriefdetection", TShock.Config.BanKillTileAbusers, TShock.Config.KickKillTileAbusers, reason); } public static bool HandleExplosivesUser(TSPlayer player, string reason) { return HandleBadPlayer(player, "ignoregriefdetection", TShock.Config.BanExplosives, TShock.Config.KickExplosives, reason); } private static bool HandleBadPlayer(TSPlayer player, string overridePermission, bool ban, bool kick, string reason) { if (!player.Group.HasPermission(overridePermission)) { if (ban) { return Ban(player, reason); } if (kick) { return Kick(player, reason); } } return false; } /// /// Shows a file to the user. /// /// int player /// string filename reletave to savedir //Todo: Fix this public static void ShowFileToUser(TSPlayer player, string file) { string foo = ""; TextReader tr = new StreamReader(Path.Combine(TShock.SavePath, file)); while ((foo = tr.ReadLine()) != null) { foo = foo.Replace("%map%", Main.worldName); foo = foo.Replace("%players%", GetPlayers()); 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); } tr.Close(); } public static void LoadGroups() { groups = new List(); groups.Add(new SuperAdminGroup()); StreamReader sr = new StreamReader(FileTools.GroupsPath); string data = sr.ReadToEnd(); data = data.Replace("\r", ""); string[] lines = data.Split('\n'); for (int i = 0; i < lines.Length; i++) { if (lines[i].StartsWith("#")) { continue; } string[] args = lines[i].Split(' '); if (args.Length < 2) { continue; } string name = args[0]; string parent = args[1]; Group group = null; if (parent.Equals("null")) { group = new Group(name); } else { for (int j = 0; j < groups.Count; j++) { if (groups[j].Name.Equals(parent)) { group = new Group(name, groups[j]); break; } } } if (group == null) { throw new Exception("Something in the groups.txt is fucked up"); } else { for (int j = 2; j < args.Length; j++) { string permission = args[j]; if (permission.StartsWith("!")) { group.NegatePermission(args[j].Replace("!", "")); } else { group.AddPermission(args[j]); } } } groups.Add(group); } sr.Close(); } /// /// Returns a Group from the name of the group /// /// string groupName public static Group GetGroup(string groupName) { //first attempt on cached groups for (int i = 0; i < groups.Count; i++) { if (groups[i].Name.Equals(groupName)) { return groups[i]; } } //shit, it didnt work, reload and try again LoadGroups(); for (int i = 0; i < groups.Count; i++) { if (groups[i].Name.Equals(groupName)) { return groups[i]; } } return new Group("null"); } /// /// Returns an IPv4 address from a DNS query /// /// string ip public static string GetIPv4Address(string hostname) { string IP4Address = String.Empty; foreach (IPAddress IPA in Dns.GetHostAddresses(hostname)) { if (IPA.AddressFamily.ToString() == "InterNetwork") { IP4Address = IPA.ToString(); break; } } return IP4Address; } /// /// Returns a Sha256 string for a given string /// /// string password /// string sha256 public static string HashPassword(string password) { using (var sha = new SHA512CryptoServiceProvider()) { var bytes = sha.ComputeHash(Encoding.ASCII.GetBytes(password)); return bytes.Aggregate("", (s, b) => s + b.ToString("X2")); } } } }