/* 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.Text; using Microsoft.Xna.Framework; using Terraria; namespace TShockAPI { internal class Tools { 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]; } /// /// Gets the IP from a player index /// /// Player Index /// IP public static string GetPlayerIP(int plr) { return GetRealIP(Netplay.serverSock[plr].tcpClient.Client.RemoteEndPoint.ToString()); } /// /// 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(); for (int i = 0; i < Main.maxPlayers; i++) { if (Main.player[i].active) { if (sb.Length != 0) { sb.Append(", "); } sb.Append(Main.player[i].name); } } return sb.ToString(); } /// /// 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) { for (int i = 0; i < Main.player.Length; i++) { SendMessage(i, msg); } Log.Info(string.Format("Broadcast: {0}", msg)); } public static void Broadcast(string msg, float red, float green, float blue) { for (int i = 0; i < Main.player.Length; i++) { SendMessage(i, msg, red, green, blue); } Log.Info(string.Format("Broadcast: {0}", msg)); } /// /// Sends a message out to a single player /// /// int socket thingy for the player from the server socket /// String message public static void SendMessage(int ply, string msg, float red, float green, float blue) { NetMessage.SendData(0x19, ply, -1, msg, 255, red, green, blue); } /// /// Sends a message out to a single player /// /// int socket thingy for the player from the server socket /// String message /// Float containing red, blue, and green color values public static void SendMessage(int ply, string msg, Color color) { NetMessage.SendData(0x19, ply, -1, msg, 255, 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); for (int i = 0; i < Main.maxPlayers; i++) { if (TShock.players[i] == null) continue; if (!TShock.players[i].group.HasPermission("logs")) continue; SendMessage(i, log, color); } } /// /// Sends a green message to a player /// /// int socket thingy for the player from the server socket /// string message public static void SendMessage(int ply, string message) { NetMessage.SendData(0x19, ply, -1, message, 255, 0f, 255f, 0f); } /// /// The number of active players on the server. /// /// int playerCount public static int activePlayers() { int num = 0; for (int i = 0; i < Main.maxPlayers; i++) { if (Main.player[i].active) { num++; } } return num; } /// /// Finds the name of the player of the int given /// /// string player name /// int player public static int FindPlayer(string ply) { List found = new List(); for (int i = 0; i < Main.player.Length; i++) { if (Main.player[i].name.ToLower().Equals(ply.ToLower())) return i; if (Main.player[i].name.ToLower().Contains(ply.ToLower())) found.Add(i); } if (found.Count == 1) return found[0]; else if (found.Count > 1) return -2; else return -1; } /// /// Gets the given player's name /// /// int player /// string name public static string FindPlayer(int ply) { for (int i = 0; i < Main.player.Length; i++) { if (i == ply) { return Main.player[i].name; } } return "null"; } /// /// Creates an NPC /// /// Type is defined in the enum NPC list /// X coord of the desired npc /// Y coord of the desired npc /// int player that the npc targets public static void NewNPC(int type, int x, int y, int target) { switch (type) { case 0: //World Eater WorldGen.shadowOrbSmashed = true; WorldGen.shadowOrbCount = 3; int w = NPC.NewNPC(x, y, 13, 1); Main.npc[w].target = target; break; case 1: //Eye Main.time = 4861; Main.dayTime = false; WorldGen.spawnEye = true; break; case 2: //Skeletron int enpeecee = NPC.NewNPC(x, y, 0x23, 0); Main.npc[enpeecee].netUpdate = true; break; } } /// /// Kicks a player from the server without checking for immunetokick permission. /// /// int player /// string reason public static void ForceKick(int ply, string reason) { string ip = GetPlayerIP(ply); NetMessage.SendData(0x2, ply, -1, reason, 0x0, 0f, 0f, 0f); Log.Info(string.Format("{0} was force kicked for : {1}", ip, reason)); } /// /// Kicks a player from the server. /// /// int player /// string reason public static bool Kick(int ply, string reason, string adminUserName = "") { if (!Netplay.serverSock[ply].active || Netplay.serverSock[ply].kill) return true; if (!TShock.players[ply].group.HasPermission("immunetokick")) { string playerName = Main.player[ply].name; NetMessage.SendData(0x2, ply, -1, string.Format("Kicked: {0}", reason), 0x0, 0f, 0f, 0f); 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 Tools.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(int plr, string reason, string adminUserName = "") { if (!Netplay.serverSock[plr].active || Netplay.serverSock[plr].kill) return true; if (!TShock.players[plr].group.HasPermission("immunetoban")) { string ip = GetPlayerIP(plr); string playerName = Main.player[plr].name; TShock.Bans.AddBan(ip, playerName, reason); NetMessage.SendData(0x2, plr, -1, string.Format("Banned: {0}", reason), 0x0, 0f, 0f, 0f); 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 Tools.Broadcast(string.Format("{0} banned {1} for {2}", adminUserName, playerName, reason.ToLower())); return true; } return false; } public static bool HandleCheater(int ply, string reason) { return HandleBadPlayer(ply, "ignorecheatdetection", ConfigurationManager.banCheater, ConfigurationManager.kickCheater, reason); } public static bool HandleGriefer(int ply, string reason) { return HandleBadPlayer(ply, "ignoregriefdetection", ConfigurationManager.banGriefer, ConfigurationManager.kickGriefer, reason); } public static bool HandleTntUser(int ply, string reason) { return HandleBadPlayer(ply, "ignoregriefdetection", ConfigurationManager.banTnt, ConfigurationManager.kickTnt, reason); } public static bool HandleExplosivesUser(int ply, string reason) { return HandleBadPlayer(ply, "ignoregriefdetection", ConfigurationManager.banBoom, ConfigurationManager.kickBoom, reason); } private static bool HandleBadPlayer(int ply, string overridePermission, bool ban, bool kick, string reason) { if (!TShock.players[ply].group.HasPermission(overridePermission)) { if (ban) { return Ban(ply, reason); } else if (kick) { return Kick(ply, reason); } } return false; } [Obsolete("Use ShowFileToUser(int ply, string file) instead.")] public static void ShowMOTD(int ply) { ShowFileToUser(ply, "motd.txt"); } /// /// Shows a file to the user. /// /// int player /// string filename reletave to savedir public static void ShowFileToUser(int ply, string file) { string foo = ""; TextReader tr = new StreamReader(FileTools.SaveDir + 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 { SendMessage(ply, foo, Clamp(Convert.ToInt32(pCc[0]), 255, 0), Clamp(Convert.ToInt32(pCc[1]), 255, 0), Clamp(Convert.ToInt32(pCc[2]), 255, 0)); continue; } catch (Exception e) { FileTools.WriteError(e.Message); } } } SendMessage(ply, foo); } tr.Close(); } public static void LoadGroups() { groups = new List(); groups.Add(new SuperAdminGroup("superadmin")); StreamReader sr = new StreamReader(FileTools.SaveDir + "groups.txt"); 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].GetName().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].GetName().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].GetName().Equals(groupName)) { return groups[i]; } } //sigh :(, ok, you fucked up the config files, congrats. return null; } /// /// Returns a Group for a ip from users.txt /// /// string ip public static Group GetGroupForIP(string ip) { ip = GetRealIP(ip); StreamReader sr = new StreamReader(FileTools.SaveDir + "users.txt"); string data = sr.ReadToEnd(); data = data.Replace("\r", ""); string[] lines = data.Split('\n'); for (int i = 0; i < lines.Length; i++) { string[] args = lines[i].Split(' '); if (args.Length < 2) { continue; } if (lines[i].StartsWith("#")) { continue; } if (args[0].Equals(ip)) { return GetGroup(args[1]); } } sr.Close(); return GetGroup("default"); } } }