/* TShock, a server mod for Terraria Copyright (C) 2011-2017 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 . */ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using Terraria; using Terraria.ID; using Terraria.Utilities; using TShockAPI.DB; using Microsoft.Xna.Framework; using Terraria.Localization; using TShockAPI.Localization; namespace TShockAPI { /// /// Utilities and other TShock core calls that don't fit anywhere else /// public class Utils { /// /// The lowest id for a prefix. /// private const int FirstItemPrefix = 1; /// /// The highest id for a prefix. /// private const int LastItemPrefix = 83; /// instance - an instance of the utils class private static readonly Utils instance = new Utils(); /// Utils - Creates a utilities object. private Utils() {} /// Instance - An instance of the utils class. /// value - the Utils instance public static Utils Instance { get { return instance; } } /// /// 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]; } /// /// 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 by calling the SaveManager and instructing it to save the world. /// public void SaveWorld() { SaveManager.Instance.SaveWorld(); } /// Broadcast - Broadcasts a message to all players on the server, as well as the server console, and the logs. /// msg - The message to send /// red - The amount of red (0-255) in the color for supported destinations. /// green - The amount of green (0-255) in the color for supported destinations. /// blue - The amount of blue (0-255) in the color for the supported destinations. 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); TShock.Log.Info(string.Format("Broadcast: {0}", msg)); } /// >Broadcast - Broadcasts a message to all players on the server, as well as the server console, and the logs. /// msg - The message to send /// color - The color object for supported destinations. public void Broadcast(string msg, Color color) { Broadcast(msg, color.R, color.G, color.B); } /// /// Broadcasts a message from a Terraria playerplayer, not TShock /// /// ply - the Terraria player index that will send the packet /// msg - The message to send /// red - The amount of red (0-255) in the color for supported destinations. /// green - The amount of green (0-255) in the color for supported destinations. /// blue - The amount of blue (0-255) in the color for the supported destinations. public void Broadcast(int ply, string msg, byte red, byte green, byte blue) { TSPlayer.All.SendMessageFromPlayer(msg, red, green, blue, ply); TSPlayer.Server.SendMessage(Main.player[ply].name + ": " + msg, red, green, blue); TShock.Log.Info(string.Format("Broadcast: {0}", Main.player[ply].name + ": " + msg)); } /// /// Sends message to all players with 'logs' permission. /// /// Message to send /// Color of the message /// The player to not send the message to. public void SendLogs(string log, Color color, TSPlayer excludedPlayer = null) { TShock.Log.Info(log); TSPlayer.Server.SendMessage(log, color); foreach (TSPlayer player in TShock.Players) { if (player != null && player != excludedPlayer && player.Active && player.HasPermission(Permissions.logs) && player.DisplayLogs && TShock.Config.DisableSpewLogs == false) player.SendMessage(log, color); } } /// /// Gets the number of active players on the server. /// /// The number of active players on the server. public int ActivePlayers() { return Main.player.Where(p => null != p && p.active).Count(); } //Random should not be generated in a method Random r = new Random(); /// /// 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 + r.Next(tileXRange*-1, tileXRange); tileY = startTileY + r.Next(tileYRange*-1, tileYRange); j++; } while (TilePlacementValid(tileX, tileY) && TileSolid(tileX, tileY)); } /// /// Determines if a tile is valid. /// /// Location X /// Location Y /// If the tile is valid public bool TilePlacementValid(int tileX, int tileY) { return tileX >= 0 && tileX < Main.maxTilesX && tileY >= 0 && tileY < Main.maxTilesY; } /// /// Checks if the tile is solid. /// /// Location X /// Location Y /// The tile's solidity. public bool TileSolid(int tileX, int tileY) { return TilePlacementValid(tileX, tileY) && Main.tile[tileX, tileY] != null && Main.tile[tileX, tileY].active() && Main.tileSolid[Main.tile[tileX, tileY].type] && !Main.tile[tileX, tileY].inActive() && !Main.tile[tileX, tileY].halfBrick() && Main.tile[tileX, tileY].slope() == 0 && Main.tile[tileX, tileY].type != TileID.Bubble; } /// /// Gets a list of items by ID, Name or Tag. /// /// Item ID, Name or Tag. /// A list of matching items. public List GetItemByIdOrName(string text) { int type = -1; if (Int32.TryParse(text, out type)) { if (type >= Main.maxItemTypes) return new List(); return new List {GetItemById(type)}; } Item item = GetItemFromTag(text); if (item != null) return new List() { item }; return GetItemByName(text); } /// /// 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) { var found = new List(); Item item = new Item(); string nameLower = name.ToLowerInvariant(); var checkEnglish = Language.ActiveCulture != GameCulture.English; for (int i = 1; i < Main.maxItemTypes; i++) { item.netDefaults(i); if (!String.IsNullOrWhiteSpace(item.Name)) { if (item.Name.ToLowerInvariant() == nameLower) return new List { item }; if (item.Name.ToLowerInvariant().StartsWith(nameLower)) found.Add(item.Clone()); } if (!checkEnglish) { continue; } string englishName = EnglishLanguage.GetItemNameById(i).ToLowerInvariant(); if (!String.IsNullOrEmpty(englishName)) { if (englishName == nameLower) return new List { item }; if (englishName.StartsWith(nameLower)) found.Add(item.Clone()); } } return found; } /// /// Gets an item based on a chat item tag. /// /// A tag in the [i/s#/p#:netid] format. /// The item represented by the tag. public Item GetItemFromTag(string tag) { Regex regex = new Regex(@"\[i(tem)?(?:\/s(?\d{1,3}))?(?:\/p(?\d{1,3}))?:(?-?\d{1,4})\]"); Match match = regex.Match(tag); if (!match.Success) return null; Item item = new Item(); item.netDefaults(Int32.Parse(match.Groups["NetID"].Value)); if (!String.IsNullOrWhiteSpace(match.Groups["Stack"].Value)) item.stack = Int32.Parse(match.Groups["Stack"].Value); if (!String.IsNullOrWhiteSpace(match.Groups["Prefix"].Value)) item.prefix = Byte.Parse(match.Groups["Prefix"].Value); return item; } /// /// Gets an NPC by ID or Name /// /// /// List of NPCs public List GetNPCByIdOrName(string idOrName) { int type = -1; if (int.TryParse(idOrName, out type)) { if (type >= Main.maxNPCTypes) return new List(); 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.SetDefaults(id); return npc; } /// /// Gets a NPC by name /// /// Name /// List of matching NPCs public List GetNPCByName(string name) { var found = new List(); NPC npc = new NPC(); string nameLower = name.ToLowerInvariant(); for (int i = -17; i < Main.maxNPCTypes; i++) { string englishName = EnglishLanguage.GetNpcNameById(i).ToLowerInvariant(); npc.SetDefaults(i); if (npc.FullName.ToLowerInvariant() == nameLower || npc.TypeName.ToLowerInvariant() == nameLower || nameLower == englishName) return new List { npc }; if (npc.FullName.ToLowerInvariant().StartsWith(nameLower) || npc.TypeName.ToLowerInvariant().StartsWith(nameLower) || englishName?.StartsWith(nameLower) == true) found.Add((NPC)npc.Clone()); } return found; } /// /// Gets a buff name by id /// /// ID /// name public string GetBuffName(int id) { return (id > 0 && id < Main.maxBuffTypes) ? Lang.GetBuffName(id) : null; } /// /// Gets the description of a buff /// /// ID /// description public string GetBuffDescription(int id) { return (id > 0 && id < Main.maxBuffTypes) ? Lang.GetBuffDescription(id) : null; } /// /// Gets a list of buffs by name /// /// name /// Matching list of buff ids public List GetBuffByName(string name) { string nameLower = name.ToLower(); string buffname; for (int i = 1; i < Main.maxBuffTypes; i++) { buffname = Lang.GetBuffName(i); if (!String.IsNullOrWhiteSpace(buffname) && buffname.ToLower() == nameLower) return new List {i}; } var found = new List(); for (int i = 1; i < Main.maxBuffTypes; i++) { buffname = Lang.GetBuffName(i); if (!String.IsNullOrWhiteSpace(buffname) && buffname.ToLower().StartsWith(nameLower)) found.Add(i); } return found; } /// /// Gets a prefix based on its id /// /// ID /// Prefix name public string GetPrefixById(int id) { return id < FirstItemPrefix || id > LastItemPrefix ? "" : Lang.prefix[id].ToString() ?? ""; } /// /// 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.ToLowerInvariant(); var found = new List(); for (int i = FirstItemPrefix; i <= LastItemPrefix; i++) { item.prefix = (byte)i; string prefixName = item.AffixName().Trim().ToLowerInvariant(); string englishName = EnglishLanguage.GetPrefixById(i).ToLowerInvariant(); if (prefixName == lowerName || englishName == lowerName) return new List() { i }; else if (prefixName.StartsWith(lowerName) || englishName?.StartsWith(lowerName) == true) // Partial match found.Add(i); } 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); } /// /// 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!") { TShock.ShuttingDown = true; if (save) SaveManager.Instance.SaveWorld(); TSPlayer.All.Kick(reason, true, true, null, true); // 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; } /// /// Reloads all configuration settings, groups, regions and raises the reload event. /// public void Reload() { FileTools.SetupConfig(); TShock.HandleCommandLinePostConfigLoad(Environment.GetCommandLineArgs()); TShock.Groups.LoadPermisions(); TShock.Regions.Reload(); TShock.Itembans.UpdateItemBans(); TShock.ProjectileBans.UpdateBans(); TShock.TileBans.UpdateBans(); } /// /// Returns an IPv4 address from a DNS query /// /// string ip public string GetIPv4AddressFromHostname(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 ""; } /// /// Checks if world has hit the max number of chests /// /// True if the entire chest array is used public bool HasWorldReachedMaxChests() { for (int i = 0; i < Main.chest.Length; i++) { if (Main.chest[i] == null) return false; } return true; } /// /// Attempts to parse a string as a timespan (_d_m_h_s). /// /// The time string. /// The seconds. /// Whether the string was parsed successfully. public bool TryParseTime(string str, out int seconds) { seconds = 0; var sb = new StringBuilder(3); for (int i = 0; i < str.Length; i++) { if (Char.IsDigit(str[i]) || (str[i] == '-' || str[i] == '+')) sb.Append(str[i]); else { int num; if (!int.TryParse(sb.ToString(), out num)) return false; sb.Clear(); switch (str[i]) { case 's': seconds += num; break; case 'm': seconds += num * 60; break; case 'h': seconds += num * 60 * 60; break; case 'd': seconds += num * 60 * 60 * 24; break; default: return false; } } } if (sb.Length != 0) 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; } /// /// Enumerates boundary points of the given region's rectangle. /// /// The region's area to enumerate through. /// The enumerated boundary points. public IEnumerable EnumerateRegionBoundaries(Rectangle regionArea) { for (int x = 0; x < regionArea.Width + 1; x++) { yield return new Point(regionArea.Left + x, regionArea.Top); yield return new Point(regionArea.Left + x, regionArea.Bottom); } for (int y = 1; y < regionArea.Height; y++) { yield return new Point(regionArea.Left, regionArea.Top + y); yield return new Point(regionArea.Right, regionArea.Top + y); } } /// EncodeColor - Encodes a color as an int. /// color - The color to encode /// int? - The encoded color public int? EncodeColor(Color? color) { if (color == null) return null; return BitConverter.ToInt32(new[] { color.Value.R, color.Value.G, color.Value.B, color.Value.A }, 0); } /// DecodeColor - Decodes a color encoded by the EncodeColor function. /// encodedColor - The encoded color /// Color? - The decoded color public Color? DecodeColor(int? encodedColor) { if (encodedColor == null) return null; byte[] data = BitConverter.GetBytes(encodedColor.Value); return new Color(data[0], data[1], data[2], data[3]); } /// /// Encodes a Boolean Array as an int. /// /// The boolean array to encode. /// The encoded int. public int? EncodeBoolArray(bool[] bools) { if (bools == null) return null; int result = 0; for (int i = 0; i < bools.Length; i++) if (bools[i]) result |= (1 << i); return result; } /// /// Decodes a Boolean Array from an int. /// /// The encoded Boolean Array. /// The resulting Boolean Array. public bool[] DecodeBoolArray(int? encodedbools) { if (encodedbools == null) return null; bool[] result = new bool[10]; for (int i = 0; i < result.Length; i++) result[i] = (encodedbools & 1 << i) != 0; return result; } /// EncodeBitsByte - Encodes a BitsByte as a byte. /// bitsByte - The BitsByte object /// byte? - The converted byte public byte? EncodeBitsByte(BitsByte? bitsByte) { if (bitsByte == null) return null; byte result = 0; for (int i = 0; i < 8; i++) if (bitsByte.Value[i]) result |= (byte)(1 << i); return result; } /// DecodeBitsByte - Decodes a bitsbyte from an int. /// encodedBitsByte - The encoded bitsbyte object. /// BitsByte? - The decoded bitsbyte object public BitsByte? DecodeBitsByte(int? encodedBitsByte) { if (encodedBitsByte == null) return null; BitsByte result = new BitsByte(); for (int i = 0; i < 8; i++) result[i] = (encodedBitsByte & 1 << i) != 0; return result; } /// GetResponseNoException - Gets a web response without generating an exception. /// req - The request to send. /// HttpWebResponse - The response object. public HttpWebResponse GetResponseNoException(HttpWebRequest req) { try { return (HttpWebResponse)req.GetResponse(); } catch (WebException we) { var resp = we.Response as HttpWebResponse; if (resp == null) throw; return resp; } } /// /// Colors the given text by correctly applying the color chat tag. /// /// The text to color. /// The color to apply. /// The , surrounded by the color tag with the appropriated hex code. public string ColorTag(string text, Color color) { return String.Format("[c/{0}:{1}]", color.Hex3(), text); } /// /// Converts an item into its text representation using the item chat tag. /// /// The item to convert. /// The NetID surrounded by the item tag with proper stack/prefix data. public string ItemTag(Item item) { int netID = item.netID; int stack = item.stack; int prefix = item.prefix; string options = stack > 1 ? "/s" + stack : prefix != 0 ? "/p" + prefix : ""; return String.Format("[i{0}:{1}]", options, netID); } /// /// Gets a list of points selected by a mass-wiring tool. /// /// The starting point for the selection. /// The ending point for the selection. /// False if facing left, True if facing right. /// /// A list of coordinates containing the and /// points and a list of points between them, forming an L shape based on . /// public List GetMassWireOperationRange(Point start, Point end, bool direction = false) { List points = new List(); #region Tile Selection Logic stolen from Wiring.cs // Slightly modified version of Wiring.MassWireOperationInner, ignores a player's wire count int num = Math.Sign(end.X - start.X); int num2 = Math.Sign(end.Y - start.Y); Point pt = new Point(); int num3; int num4; int num5; if (direction) { pt.X = start.X; num3 = start.Y; num4 = end.Y; num5 = num2; } else { pt.Y = start.Y; num3 = start.X; num4 = end.X; num5 = num; } int num6 = num3; while (num6 != num4) { if (direction) { pt.Y = num6; } else { pt.X = num6; } points.Add(pt); num6 += num5; } if (direction) { pt.Y = end.Y; num3 = start.X; num4 = end.X; num5 = num; } else { pt.X = end.X; num3 = start.Y; num4 = end.Y; num5 = num2; } int num7 = num3; while (num7 != num4) { if (!direction) { pt.Y = num7; } else { pt.X = num7; } points.Add(pt); num7 += num5; } points.Add(end); #endregion return points; } /// /// Dumps information and optionally exits afterwards /// /// public void Dump(bool exit = true) { PrepareLangForDump(); // Lang.setLang(true); ConfigFile.DumpDescriptions(); Permissions.DumpDescriptions(); ServerSideCharacters.ServerSideConfig.DumpDescriptions(); RestManager.DumpDescriptions(); DumpBuffs("BuffList.txt"); DumpItems("Items-1_0.txt", 1, 235); DumpItems("Items-1_1.txt", 235, 604); DumpItems("Items-1_2.txt", 604, 2749); DumpItems("Items-1_3.txt", 2749, Main.maxItemTypes); DumpNPCs("NPCs.txt"); DumpProjectiles("Projectiles.txt"); DumpPrefixes("Prefixes.txt"); if (exit) { Environment.Exit(1); } } internal void PrepareLangForDump() { for(int i = 0; i < Main.recipe.Length; i++) Main.recipe[i] = new Recipe(); } /// Dumps a matrix of all permissions & all groups in Markdown table format. /// The save destination. internal void DumpPermissionMatrix(string path) { StringBuilder output = new StringBuilder(); output.Append("|Permission|"); // Traverse to build group name list foreach (Group g in TShock.Groups.groups) { output.Append(g.Name); output.Append("|"); } output.AppendLine(); output.Append("|-------|"); foreach (Group g in TShock.Groups.groups) { output.Append("-------|"); } output.AppendLine(); foreach (var field in typeof(Permissions).GetFields().OrderBy(f => f.Name)) { output.Append("|"); output.Append((string) field.GetValue(null)); output.Append("|"); foreach (Group g in TShock.Groups.groups) { if (g.HasPermission((string) field.GetValue(null))) { output.Append("✔|"); } else { output.Append("|"); } } output.AppendLine(); } File.WriteAllText(path, output.ToString()); } public void DumpBuffs(string path) { StringBuilder buffer = new StringBuilder(); buffer.AppendLine("[block:parameters]").AppendLine("{").AppendLine(" \"data\": {"); buffer.AppendLine(" \"h-0\": \"ID\","); buffer.AppendLine(" \"h-1\": \"Name\","); buffer.AppendLine(" \"h-2\": \"Description\","); List elements = new List(); for (int i = 0; i < Main.maxBuffTypes; i++) { if (!String.IsNullOrEmpty(Lang.GetBuffName(i))) { object[] element = new object[] { i, Lang.GetBuffName(i), Lang.GetBuffDescription(i) }; elements.Add(element); } } var rows = elements.Count; var columns = 0; if (rows > 0) { columns = elements[0].Length; } OutputElementsForDump(buffer, elements, rows, columns); buffer.AppendLine(); buffer.AppendLine(" },"); buffer.AppendLine(String.Format(" \"cols\": {0},", columns)).AppendLine(String.Format(" \"rows\": {0}", rows)); buffer.AppendLine("}").Append("[/block]"); File.WriteAllText(path, buffer.ToString()); } public void DumpItems(string path, int start, int end) { Main.player[Main.myPlayer] = new Player(); StringBuilder buffer = new StringBuilder(); Regex newLine = new Regex(@"\n"); buffer.AppendLine("[block:parameters]").AppendLine("{").AppendLine(" \"data\": {"); buffer.AppendLine(" \"h-0\": \"ID\","); buffer.AppendLine(" \"h-1\": \"Name\","); buffer.AppendLine(" \"h-2\": \"Tooltip\","); List elements = new List(); for (int i = start; i < end; i++) { Item item = new Item(); item.SetDefaults(i); string tt = ""; for (int x = 0; x < item.ToolTip.Lines; x++) { tt += item.ToolTip.GetLine(x) + "\n"; } if (!String.IsNullOrEmpty(item.Name)) { object[] element = new object[] { i, newLine.Replace(item.Name, @" "), newLine.Replace(tt, @" "), }; elements.Add(element); } } var rows = elements.Count; var columns = 0; if (rows > 0) { columns = elements[0].Length; } OutputElementsForDump(buffer, elements, rows, columns); buffer.AppendLine(); buffer.AppendLine(" },"); buffer.AppendLine(String.Format(" \"cols\": {0},", columns)).AppendLine(String.Format(" \"rows\": {0}", rows)); buffer.AppendLine("}").Append("[/block]"); File.WriteAllText(path, buffer.ToString()); } public void DumpNPCs(string path) { StringBuilder buffer = new StringBuilder(); buffer.AppendLine("[block:parameters]").AppendLine("{").AppendLine(" \"data\": {"); buffer.AppendLine(" \"h-0\": \"ID\","); buffer.AppendLine(" \"h-1\": \"Full Name\","); buffer.AppendLine(" \"h-2\": \"Type Name\","); List elements = new List(); for (int i = -65; i < Main.maxNPCTypes; i++) { NPC npc = new NPC(); npc.SetDefaults(i); if (!String.IsNullOrEmpty(npc.FullName)) { object[] element = new object[] { i, npc.FullName, npc.TypeName }; elements.Add(element); } } var rows = elements.Count; var columns = 0; if (rows > 0) { columns = elements[0].Length; } OutputElementsForDump(buffer, elements, rows, columns); buffer.AppendLine(); buffer.AppendLine(" },"); buffer.AppendLine(String.Format(" \"cols\": {0},", columns)).AppendLine(String.Format(" \"rows\": {0}", rows)); buffer.AppendLine("}").Append("[/block]"); File.WriteAllText(path, buffer.ToString()); } public void DumpProjectiles(string path) { Main.rand = new UnifiedRandom(); StringBuilder buffer = new StringBuilder(); buffer.AppendLine("[block:parameters]").AppendLine("{").AppendLine(" \"data\": {"); buffer.AppendLine(" \"h-0\": \"ID\","); buffer.AppendLine(" \"h-1\": \"Name\","); List elements = new List(); for (int i = 0; i < Main.maxProjectileTypes; i++) { Projectile projectile = new Projectile(); projectile.SetDefaults(i); if (!String.IsNullOrEmpty(projectile.Name)) { object[] element = new object[] { i, projectile.Name }; elements.Add(element); } } var rows = elements.Count; var columns = 0; if (rows > 0) { columns = elements[0].Length; } OutputElementsForDump(buffer, elements, rows, columns); buffer.AppendLine(); buffer.AppendLine(" },"); buffer.AppendLine(String.Format(" \"cols\": {0},", columns)).AppendLine(String.Format(" \"rows\": {0}", rows)); buffer.AppendLine("}").Append("[/block]"); File.WriteAllText(path, buffer.ToString()); } public void DumpPrefixes(string path) { StringBuilder buffer = new StringBuilder(); buffer.AppendLine("[block:parameters]").AppendLine("{").AppendLine(" \"data\": {"); buffer.AppendLine(" \"h-0\": \"ID\","); buffer.AppendLine(" \"h-1\": \"Name\","); List elements = new List(); for (int i = 0; i < PrefixID.Count; i++) { string prefix = Lang.prefix[i].ToString(); if (!String.IsNullOrEmpty(prefix)) { object[] element = new object[] {i, prefix}; elements.Add(element); } } var rows = elements.Count; var columns = 0; if (rows > 0) { columns = elements[0].Length; } OutputElementsForDump(buffer, elements, rows, columns); buffer.AppendLine(); buffer.AppendLine(" },"); buffer.AppendLine(String.Format(" \"cols\": {0},", columns)).AppendLine(String.Format(" \"rows\": {0}", rows)); buffer.AppendLine("}").Append("[/block]"); File.WriteAllText(path, buffer.ToString()); } private void OutputElementsForDump(StringBuilder buffer, List elements, int rows, int columns) { if (rows > 0) { columns = elements[0].Length; for (int i = 0; i < columns; i++) { for (int j = 0; j < rows; j++) { buffer.Append(String.Format(" \"{0}-{1}\": \"{2}\"", j, i, elements[j][i])); if (j != rows - 1 || i != columns - 1) buffer.AppendLine(","); } } } } /// Starts an invasion on the server. /// The invasion type id. internal void StartInvasion(int type) { int invasionSize = 0; if (TShock.Config.InfiniteInvasion) { // Not really an infinite size invasionSize = 20000000; } else { invasionSize = 100 + (TShock.Config.InvasionMultiplier * ActivePlayers()); } // Order matters // StartInvasion will reset the invasion size Main.StartInvasion(type); // Note: This is a workaround to previously providing the size as a parameter in StartInvasion // Have to set start size to report progress correctly Main.invasionSizeStart = invasionSize; Main.invasionSize = invasionSize; } /// Verifies that each stack in each chest is valid and not over the max stack count. internal void FixChestStacks() { if (TShock.Config.IgnoreChestStacksOnLoad) return; foreach (Chest chest in Main.chest) { if (chest != null) { foreach (Item item in chest.item) { if (item != null && item.stack > item.maxStack) item.stack = item.maxStack; } } } } /// Updates the console title with some pertinent information. /// If the server is empty; determines if we should use Utils.ActivePlayers() for player count or 0. internal void SetConsoleTitle(bool empty) { Console.Title = string.Format("{0}{1}/{2} on {3} @ {4}:{5} (TShock for Terraria v{6})", !string.IsNullOrWhiteSpace(TShock.Config.ServerName) ? TShock.Config.ServerName + " - " : "", empty ? 0 : ActivePlayers(), TShock.Config.MaxSlots, Main.worldName, Netplay.ServerIP.ToString(), Netplay.ListenPort, TShock.VersionNum); } /// Determines the distance between two vectors. /// The first vector location. /// The second vector location. /// The distance between the two vectors. public static float Distance(Vector2 value1, Vector2 value2) { float num2 = value1.X - value2.X; float num = value1.Y - value2.Y; float num3 = (num2 * num2) + (num * num); return (float)Math.Sqrt(num3); } /// Checks to see if a location is in the spawn protection area. /// The x coordinate to check. /// The y coordinate to check. /// If the given x,y location is in the spawn area. public static bool IsInSpawn(int x, int y) { Vector2 tile = new Vector2(x, y); Vector2 spawn = new Vector2(Main.spawnTileX, Main.spawnTileY); return Distance(spawn, tile) <= TShock.Config.SpawnProtectionRadius; } /// Computes the max styles... internal void ComputeMaxStyles() { var item = new Item(); for (int i = 0; i < Main.maxItemTypes; i++) { item.netDefaults(i); if (item.placeStyle > 0) { if (GetDataHandlers.MaxPlaceStyles.ContainsKey(item.createTile)) { if (item.placeStyle > GetDataHandlers.MaxPlaceStyles[item.createTile]) GetDataHandlers.MaxPlaceStyles[item.createTile] = item.placeStyle; } else GetDataHandlers.MaxPlaceStyles.Add(item.createTile, item.placeStyle); } } } } }