TShock used to call the StartInvasion method in Main with a type and a size, but then in some update it stopped taking a size. So you have to change a field to change the size, but the problem is that the field is reset when StartInavsion is called. This means that any effort to manually change the size would have failed due to the fact that the field is reset at the end. The order has been changed to account for this. In addition, the start size is now set, so the game can report progress correctly on the current invasion.
1548 lines
46 KiB
C#
1548 lines
46 KiB
C#
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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
|
|
{
|
|
/// <summary>
|
|
/// Utilities and other TShock core calls that don't fit anywhere else
|
|
/// </summary>
|
|
public class Utils
|
|
{
|
|
/// <summary>
|
|
/// The lowest id for a prefix.
|
|
/// </summary>
|
|
private const int FirstItemPrefix = 1;
|
|
|
|
/// <summary>
|
|
/// The highest id for a prefix.
|
|
/// </summary>
|
|
private const int LastItemPrefix = 83;
|
|
|
|
/// <summary>instance - an instance of the utils class</summary>
|
|
private static readonly Utils instance = new Utils();
|
|
|
|
/// <summary> This regex will look for the old MotD format for colors and replace them with the new chat format. </summary>
|
|
private Regex motdColorRegex = new Regex(@"\%\s*(?<r>\d{1,3})\s*,\s*(?<g>\d{1,3})\s*,\s*(?<b>\d{1,3})\s*\%(?<text>((?!(\%\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\%)|(\[[a-zA-Z]/[^:]+:[^\]]*\])).)*)");
|
|
|
|
/// <summary> Matches the start of a line with our legacy color format</summary>
|
|
private Regex startOfLineColorRegex = new Regex(@"^\%\s*(?<r>\d{1,3})\s*,\s*(?<g>\d{1,3})\s*,\s*(?<b>\d{1,3})\s*\%");
|
|
|
|
/// <summary>Utils - Creates a utilities object.</summary>
|
|
private Utils() {}
|
|
|
|
/// <summary>Instance - An instance of the utils class.</summary>
|
|
/// <value>value - the Utils instance</value>
|
|
public static Utils Instance { get { return instance; } }
|
|
|
|
/// <summary>
|
|
/// Provides the real IP address from a RemoteEndPoint string that contains a port and an IP
|
|
/// </summary>
|
|
/// <param name="mess">A string IPv4 address in IP:PORT form.</param>
|
|
/// <returns>A string IPv4 address.</returns>
|
|
public string GetRealIP(string mess)
|
|
{
|
|
return mess.Split(':')[0];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a list of current players on the server
|
|
/// </summary>
|
|
/// <param name="includeIDs">bool includeIDs - whether or not the string of each player name should include ID data</param>
|
|
/// <returns>List of strings with names</returns>
|
|
public List<string> GetPlayers(bool includeIDs)
|
|
{
|
|
var players = new List<string>();
|
|
|
|
foreach (TSPlayer ply in TShock.Players)
|
|
{
|
|
if (ply != null && ply.Active)
|
|
{
|
|
if (includeIDs)
|
|
{
|
|
players.Add(String.Format("{0} (IX: {1}{2})", ply.Name, ply.Index, ply.Account != null ? ", ID: " + ply.Account.ID : ""));
|
|
}
|
|
else
|
|
{
|
|
players.Add(ply.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return players;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a player and gets IP as string
|
|
/// </summary>
|
|
/// <param name="playername">string playername</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// It's a clamp function
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="value">Value to clamp</param>
|
|
/// <param name="max">Maximum bounds of the clamp</param>
|
|
/// <param name="min">Minimum bounds of the clamp</param>
|
|
/// <returns></returns>
|
|
public T Clamp<T>(T value, T max, T min)
|
|
where T : IComparable<T>
|
|
{
|
|
T result = value;
|
|
if (value.CompareTo(max) > 0)
|
|
result = max;
|
|
if (value.CompareTo(min) < 0)
|
|
result = min;
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the map data by calling the SaveManager and instructing it to save the world.
|
|
/// </summary>
|
|
public void SaveWorld()
|
|
{
|
|
SaveManager.Instance.SaveWorld();
|
|
}
|
|
|
|
/// <summary>Broadcast - Broadcasts a message to all players on the server, as well as the server console, and the logs.</summary>
|
|
/// <param name="msg">msg - The message to send</param>
|
|
/// <param name="red">red - The amount of red (0-255) in the color for supported destinations.</param>
|
|
/// <param name="green">green - The amount of green (0-255) in the color for supported destinations.</param>
|
|
/// <param name="blue">blue - The amount of blue (0-255) in the color for the supported destinations.</param>
|
|
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));
|
|
}
|
|
|
|
/// <summary>>Broadcast - Broadcasts a message to all players on the server, as well as the server console, and the logs.</summary>
|
|
/// <param name="msg">msg - The message to send</param>
|
|
/// <param name="color">color - The color object for supported destinations.</param>
|
|
public void Broadcast(string msg, Color color)
|
|
{
|
|
Broadcast(msg, color.R, color.G, color.B);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Broadcasts a message from a Terraria playerplayer, not TShock
|
|
/// </summary>
|
|
/// <param name="ply">ply - the Terraria player index that will send the packet</param>
|
|
/// <param name="msg">msg - The message to send</param>
|
|
/// <param name="red">red - The amount of red (0-255) in the color for supported destinations.</param>
|
|
/// <param name="green">green - The amount of green (0-255) in the color for supported destinations.</param>
|
|
/// <param name="blue">blue - The amount of blue (0-255) in the color for the supported destinations.</param>
|
|
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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends message to all players with 'logs' permission.
|
|
/// </summary>
|
|
/// <param name="log">Message to send</param>
|
|
/// <param name="color">Color of the message</param>
|
|
/// <param name="excludedPlayer">The player to not send the message to.</param>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the number of active players on the server.
|
|
/// </summary>
|
|
/// <returns>The number of active players on the server.</returns>
|
|
public int ActivePlayers()
|
|
{
|
|
return Main.player.Where(p => null != p && p.active).Count();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a TSPlayer based on name or ID
|
|
/// </summary>
|
|
/// <param name="plr">Player name or ID</param>
|
|
/// <returns>A list of matching players</returns>
|
|
public List<TSPlayer> FindPlayer(string plr)
|
|
{
|
|
var found = new List<TSPlayer>();
|
|
// Avoid errors caused by null search
|
|
if (plr == null)
|
|
return found;
|
|
|
|
byte plrID;
|
|
if (byte.TryParse(plr, out plrID) && plrID < Main.maxPlayers)
|
|
{
|
|
TSPlayer player = TShock.Players[plrID];
|
|
if (player != null && player.Active)
|
|
{
|
|
return new List<TSPlayer> { player };
|
|
}
|
|
}
|
|
|
|
string plrLower = plr.ToLower();
|
|
foreach (TSPlayer player in TShock.Players)
|
|
{
|
|
if (player != null)
|
|
{
|
|
// Must be an EXACT match
|
|
if (player.Name == plr)
|
|
return new List<TSPlayer> { player };
|
|
if (player.Name.ToLower().StartsWith(plrLower))
|
|
found.Add(player);
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
//Random should not be generated in a method
|
|
Random r = new Random();
|
|
|
|
/// <summary>
|
|
/// Gets a random clear tile in range
|
|
/// </summary>
|
|
/// <param name="startTileX">Bound X</param>
|
|
/// <param name="startTileY">Bound Y</param>
|
|
/// <param name="tileXRange">Range on the X axis</param>
|
|
/// <param name="tileYRange">Range on the Y axis</param>
|
|
/// <param name="tileX">X location</param>
|
|
/// <param name="tileY">Y location</param>
|
|
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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if a tile is valid.
|
|
/// </summary>
|
|
/// <param name="tileX">Location X</param>
|
|
/// <param name="tileY">Location Y</param>
|
|
/// <returns>If the tile is valid</returns>
|
|
public bool TilePlacementValid(int tileX, int tileY)
|
|
{
|
|
return tileX >= 0 && tileX < Main.maxTilesX && tileY >= 0 && tileY < Main.maxTilesY;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the tile is solid.
|
|
/// </summary>
|
|
/// <param name="tileX">Location X</param>
|
|
/// <param name="tileY">Location Y</param>
|
|
/// <returns>The tile's solidity.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of items by ID, Name or Tag.
|
|
/// </summary>
|
|
/// <param name="text">Item ID, Name or Tag.</param>
|
|
/// <returns>A list of matching items.</returns>
|
|
public List<Item> GetItemByIdOrName(string text)
|
|
{
|
|
int type = -1;
|
|
if (Int32.TryParse(text, out type))
|
|
{
|
|
if (type >= Main.maxItemTypes)
|
|
return new List<Item>();
|
|
return new List<Item> {GetItemById(type)};
|
|
}
|
|
Item item = GetItemFromTag(text);
|
|
if (item != null)
|
|
return new List<Item>() { item };
|
|
return GetItemByName(text);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an item by ID
|
|
/// </summary>
|
|
/// <param name="id">ID</param>
|
|
/// <returns>Item</returns>
|
|
public Item GetItemById(int id)
|
|
{
|
|
Item item = new Item();
|
|
item.netDefaults(id);
|
|
return item;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets items by name
|
|
/// </summary>
|
|
/// <param name="name">name</param>
|
|
/// <returns>List of Items</returns>
|
|
public List<Item> GetItemByName(string name)
|
|
{
|
|
var found = new List<Item>();
|
|
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> { 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> { item };
|
|
if (englishName.StartsWith(nameLower))
|
|
found.Add(item.Clone());
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an item based on a chat item tag.
|
|
/// </summary>
|
|
/// <param name="tag">A tag in the [i/s#/p#:netid] format.</param>
|
|
/// <returns>The item represented by the tag.</returns>
|
|
public Item GetItemFromTag(string tag)
|
|
{
|
|
Regex regex = new Regex(@"\[i(tem)?(?:\/s(?<Stack>\d{1,3}))?(?:\/p(?<Prefix>\d{1,3}))?:(?<NetID>-?\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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an NPC by ID or Name
|
|
/// </summary>
|
|
/// <param name="idOrName"></param>
|
|
/// <returns>List of NPCs</returns>
|
|
public List<NPC> GetNPCByIdOrName(string idOrName)
|
|
{
|
|
int type = -1;
|
|
if (int.TryParse(idOrName, out type))
|
|
{
|
|
if (type >= Main.maxNPCTypes)
|
|
return new List<NPC>();
|
|
return new List<NPC> { GetNPCById(type) };
|
|
}
|
|
return GetNPCByName(idOrName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an NPC by ID
|
|
/// </summary>
|
|
/// <param name="id">ID</param>
|
|
/// <returns>NPC</returns>
|
|
public NPC GetNPCById(int id)
|
|
{
|
|
NPC npc = new NPC();
|
|
npc.SetDefaults(id);
|
|
return npc;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a NPC by name
|
|
/// </summary>
|
|
/// <param name="name">Name</param>
|
|
/// <returns>List of matching NPCs</returns>
|
|
public List<NPC> GetNPCByName(string name)
|
|
{
|
|
var found = new List<NPC>();
|
|
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> { npc };
|
|
if (npc.FullName.ToLowerInvariant().StartsWith(nameLower) || npc.TypeName.ToLowerInvariant().StartsWith(nameLower)
|
|
|| englishName?.StartsWith(nameLower) == true)
|
|
found.Add((NPC)npc.Clone());
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a buff name by id
|
|
/// </summary>
|
|
/// <param name="id">ID</param>
|
|
/// <returns>name</returns>
|
|
public string GetBuffName(int id)
|
|
{
|
|
return (id > 0 && id < Main.maxBuffTypes) ? Lang.GetBuffName(id) : "null";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the description of a buff
|
|
/// </summary>
|
|
/// <param name="id">ID</param>
|
|
/// <returns>description</returns>
|
|
public string GetBuffDescription(int id)
|
|
{
|
|
return (id > 0 && id < Main.maxBuffTypes) ? Lang.GetBuffDescription(id) : "null";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of buffs by name
|
|
/// </summary>
|
|
/// <param name="name">name</param>
|
|
/// <returns>Matching list of buff ids</returns>
|
|
public List<int> 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<int> {i};
|
|
}
|
|
var found = new List<int>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a prefix based on its id
|
|
/// </summary>
|
|
/// <param name="id">ID</param>
|
|
/// <returns>Prefix name</returns>
|
|
public string GetPrefixById(int id)
|
|
{
|
|
return id < FirstItemPrefix || id > LastItemPrefix ? "" : Lang.prefix[id].ToString() ?? "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of prefixes by name
|
|
/// </summary>
|
|
/// <param name="name">Name</param>
|
|
/// <returns>List of prefix IDs</returns>
|
|
public List<int> GetPrefixByName(string name)
|
|
{
|
|
Item item = new Item();
|
|
item.SetDefaults(0);
|
|
string lowerName = name.ToLowerInvariant();
|
|
var found = new List<int>();
|
|
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<int>() { i };
|
|
else if (prefixName.StartsWith(lowerName) || englishName?.StartsWith(lowerName) == true) // Partial match
|
|
found.Add(i);
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a prefix by ID or name
|
|
/// </summary>
|
|
/// <param name="idOrName">ID or name</param>
|
|
/// <returns>List of prefix IDs</returns>
|
|
public List<int> GetPrefixByIdOrName(string idOrName)
|
|
{
|
|
int type = -1;
|
|
if (int.TryParse(idOrName, out type) && type >= FirstItemPrefix && type <= LastItemPrefix)
|
|
{
|
|
return new List<int> {type};
|
|
}
|
|
return GetPrefixByName(idOrName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Kicks all player from the server without checking for immunetokick permission.
|
|
/// </summary>
|
|
/// <param name="reason">string reason</param>
|
|
public void ForceKickAll(string reason)
|
|
{
|
|
foreach (TSPlayer player in TShock.Players)
|
|
{
|
|
if (player != null && player.Active)
|
|
{
|
|
ForceKick(player, reason, false, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops the server after kicking all players with a reason message, and optionally saving the world
|
|
/// </summary>
|
|
/// <param name="save">bool perform a world save before stop (default: true)</param>
|
|
/// <param name="reason">string reason (default: "Server shutting down!")</param>
|
|
public void StopServer(bool save = true, string reason = "Server shutting down!")
|
|
{
|
|
TShock.ShuttingDown = true;
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reloads all configuration settings, groups, regions and raises the reload event.
|
|
/// </summary>
|
|
public void Reload(TSPlayer player)
|
|
{
|
|
FileTools.SetupConfig();
|
|
TShock.HandleCommandLinePostConfigLoad(Environment.GetCommandLineArgs());
|
|
TShock.Groups.LoadPermisions();
|
|
TShock.Regions.Reload();
|
|
TShock.Itembans.UpdateItemBans();
|
|
TShock.ProjectileBans.UpdateBans();
|
|
TShock.TileBans.UpdateBans();
|
|
Hooks.GeneralHooks.OnReloadEvent(player);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Kicks a player from the server without checking for immunetokick permission.
|
|
/// </summary>
|
|
/// <param name="player">TSPlayer player</param>
|
|
/// <param name="reason">string reason</param>
|
|
/// <param name="silent">bool silent (default: false)</param>
|
|
/// <param name="saveSSI">bool saveSSI (default: false)</param>
|
|
public void ForceKick(TSPlayer player, string reason, bool silent = false, bool saveSSI = false)
|
|
{
|
|
Kick(player, reason, true, silent, null, saveSSI);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Kicks a player from the server..
|
|
/// </summary>
|
|
/// <param name="player">TSPlayer player</param>
|
|
/// <param name="reason">string reason</param>
|
|
/// <param name="force">bool force (default: false)</param>
|
|
/// <param name="silent">bool silent (default: false)</param>
|
|
/// <param name="adminUserName">string adminUserName (default: null)</param>
|
|
/// <param name="saveSSI">bool saveSSI (default: false)</param>
|
|
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.HasPermission(Permissions.immunetokick))
|
|
{
|
|
string playerName = player.Name;
|
|
player.SilentKickInProgress = silent;
|
|
if (player.IsLoggedIn && saveSSI)
|
|
player.SaveServerCharacter();
|
|
player.Disconnect(string.Format("Kicked: {0}", reason));
|
|
TShock.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()), Color.Green);
|
|
else
|
|
Broadcast(string.Format("{0} {1}kicked {2} for '{3}'", adminUserName, verb, playerName, reason.ToLower()), Color.Green);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bans and kicks a player from the server.
|
|
/// </summary>
|
|
/// <param name="player">TSPlayer player</param>
|
|
/// <param name="reason">string reason</param>
|
|
/// <param name="force">bool force (default: false)</param>
|
|
/// <param name="adminUserName">string adminUserName (default: null)</param>
|
|
public bool Ban(TSPlayer player, string reason, bool force = false, string adminUserName = null)
|
|
{
|
|
if (!player.ConnectionAlive)
|
|
return true;
|
|
if (force || !player.HasPermission(Permissions.immunetoban))
|
|
{
|
|
string ip = player.IP;
|
|
string uuid = player.UUID;
|
|
string playerName = player.Name;
|
|
TShock.Bans.AddBan2(ip, playerName, uuid, "", reason, false, adminUserName);
|
|
player.Disconnect(string.Format("Banned: {0}", reason));
|
|
string verb = force ? "force " : "";
|
|
if (string.IsNullOrWhiteSpace(adminUserName))
|
|
TSPlayer.All.SendInfoMessage("{0} was {1}banned for '{2}'.", playerName, verb, reason);
|
|
else
|
|
TSPlayer.All.SendInfoMessage("{0} {1}banned {2} for '{3}'.", adminUserName, verb, playerName, reason);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>HasBanExpired - Returns whether or not a ban has expired or not.</summary>
|
|
/// <param name="ban">ban - The ban object to check.</param>
|
|
/// <param name="byName">byName - Defines whether or not the ban should be checked by name.</param>
|
|
/// <returns>bool - True if the ban has expired.</returns>
|
|
public bool HasBanExpired(Ban ban, bool byName = false)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(ban.Expiration) && (ban.ExpirationDateTime != null) && (DateTime.UtcNow >= ban.ExpirationDateTime))
|
|
{
|
|
if (byName)
|
|
{
|
|
TShock.Bans.RemoveBan(ban.Name, true, true, false);
|
|
}
|
|
else
|
|
{
|
|
TShock.Bans.RemoveBan(ban.IP, false, false, false);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shows a file to the user.
|
|
/// </summary>
|
|
/// <param name="player">Player the file contents will be sent to</param>
|
|
/// <param name="file">Filename relative to <see cref="TShock.SavePath"></see></param>
|
|
public void ShowFileToUser(TSPlayer player, string file)
|
|
{
|
|
string foo = "";
|
|
bool containsOldFormat = false;
|
|
using (var tr = new StreamReader(file))
|
|
{
|
|
Color lineColor;
|
|
while ((foo = tr.ReadLine()) != null)
|
|
{
|
|
lineColor = Color.White;
|
|
if (string.IsNullOrWhiteSpace(foo))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foo = foo.Replace("%map%", (TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName));
|
|
foo = foo.Replace("%players%", String.Join(",", GetPlayers(false)));
|
|
|
|
var legacyColorMatch = startOfLineColorRegex.Match(foo);
|
|
if (legacyColorMatch.Success)
|
|
{
|
|
lineColor = new Color(Int32.Parse(legacyColorMatch.Groups["r"].Value),
|
|
Int32.Parse(legacyColorMatch.Groups["g"].Value),
|
|
Int32.Parse(legacyColorMatch.Groups["b"].Value));
|
|
foo = foo.Replace(legacyColorMatch.Groups[0].Value, "");
|
|
}
|
|
|
|
bool upgraded = false;
|
|
string newFoo = ReplaceDeprecatedColorCodes(foo, out upgraded);
|
|
if (upgraded && !containsOldFormat)
|
|
{
|
|
TShock.Log.ConsoleInfo($"You are using an old color format in file {file}.");
|
|
TShock.Log.ConsoleInfo("To send coloured text please use Terraria's inbuilt format of: [c/#hex:text].");
|
|
TShock.Log.ConsoleInfo("For example: [c/ff00aa:This is a message!].");
|
|
containsOldFormat = true;
|
|
}
|
|
foo = newFoo;
|
|
|
|
player.SendMessage(foo, lineColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a string with deprecated %###,###,###% formats replaced with the new chat format colors.
|
|
/// </summary>
|
|
/// <param name="input">The input string</param>
|
|
/// <param name="upgradedFormat">An out parameter that denotes if this line of text was upgraded.</param>
|
|
/// <returns>A replaced version of the input with the new chat color format.</returns>
|
|
private string ReplaceDeprecatedColorCodes(string input, out bool upgradedFormat)
|
|
{
|
|
String tempString = input;
|
|
Match match = null;
|
|
bool uFormat = false;
|
|
|
|
while ((match = motdColorRegex.Match(tempString)).Success)
|
|
{
|
|
uFormat = true;
|
|
tempString = tempString.Replace(match.Groups[0].Value, String.Format("[c/{0:X2}{1:X2}{2:X2}:{3}]", Int32.Parse(match.Groups["r"].Value), Int32.Parse(match.Groups["g"].Value), Int32.Parse(match.Groups["b"].Value), match.Groups["text"]));
|
|
}
|
|
|
|
upgradedFormat = uFormat;
|
|
return tempString;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Upgrades a legacy MotD file to the new terraria chat tags version.
|
|
/// </summary>
|
|
public void UpgradeMotD()
|
|
{
|
|
string foo = "";
|
|
StringBuilder motd = new StringBuilder();
|
|
bool informedOwner = false;
|
|
using (var tr = new StreamReader(FileTools.MotdPath))
|
|
{
|
|
Color lineColor;
|
|
while ((foo = tr.ReadLine()) != null)
|
|
{
|
|
lineColor = Color.White;
|
|
var legacyColorMatch = startOfLineColorRegex.Match(foo);
|
|
if (legacyColorMatch.Success)
|
|
{
|
|
lineColor = new Color(Int32.Parse(legacyColorMatch.Groups["r"].Value),
|
|
Int32.Parse(legacyColorMatch.Groups["g"].Value),
|
|
Int32.Parse(legacyColorMatch.Groups["b"].Value));
|
|
foo = foo.Replace(legacyColorMatch.Groups[0].Value, "");
|
|
}
|
|
|
|
bool upgraded = false;
|
|
string newFoo = ReplaceDeprecatedColorCodes(foo, out upgraded);
|
|
if (!informedOwner && upgraded)
|
|
{
|
|
informedOwner = true;
|
|
TShock.Log.ConsoleInfo("We have upgraded your MotD to the new format. A backup has been created.");
|
|
}
|
|
|
|
if (lineColor != Color.White)
|
|
motd.Append(String.Format("%{0:d3},{1:d3},{2:d3}%", lineColor.R, lineColor.G, lineColor.B));
|
|
|
|
motd.AppendLine(newFoo);
|
|
}
|
|
}
|
|
|
|
if (informedOwner)
|
|
{
|
|
File.Copy(FileTools.MotdPath, String.Format("{0}_{1}.backup", FileTools.MotdPath, DateTime.Now.ToString("ddMMMyy_hhmmss")));
|
|
File.WriteAllText(FileTools.MotdPath, motd.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a Group from the name of the group
|
|
/// </summary>
|
|
/// <param name="groupName">string groupName</param>
|
|
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 Group.DefaultGroup;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an IPv4 address from a DNS query
|
|
/// </summary>
|
|
/// <param name="hostname">string ip</param>
|
|
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 "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends the player an error message stating that more than one match was found
|
|
/// appending a csv list of the matches.
|
|
/// </summary>
|
|
/// <param name="ply">Player to send the message to</param>
|
|
/// <param name="matches">An enumerable list with the matches</param>
|
|
public void SendMultipleMatchError(TSPlayer ply, IEnumerable<object> matches)
|
|
{
|
|
ply.SendErrorMessage("More than one match found: ");
|
|
|
|
var lines = PaginationTools.BuildLinesFromTerms(matches.ToArray());
|
|
|
|
lines.ForEach(ply.SendInfoMessage);
|
|
|
|
ply.SendErrorMessage("Use \"my query\" for items with spaces.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if world has hit the max number of chests
|
|
/// </summary>
|
|
/// <returns>True if the entire chest array is used</returns>
|
|
public bool MaxChests()
|
|
{
|
|
for (int i = 0; i < Main.chest.Length; i++)
|
|
{
|
|
if (Main.chest[i] == null)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to parse a string as a timespan (_d_m_h_s).
|
|
/// </summary>
|
|
/// <param name="str">The time string.</param>
|
|
/// <param name="seconds">The seconds.</param>
|
|
/// <returns>Whether the string was parsed successfully.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Searches for a projectile by identity and owner
|
|
/// </summary>
|
|
/// <param name="identity">identity</param>
|
|
/// <param name="owner">owner</param>
|
|
/// <returns>projectile ID or -1 if not found</returns>
|
|
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 -1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumerates boundary points of the given region's rectangle.
|
|
/// </summary>
|
|
/// <param name="regionArea">The region's area to enumerate through.</param>
|
|
/// <returns>The enumerated boundary points.</returns>
|
|
public IEnumerable<Point> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>EncodeColor - Encodes a color as an int.</summary>
|
|
/// <param name="color">color - The color to encode</param>
|
|
/// <returns>int? - The encoded color</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>DecodeColor - Decodes a color encoded by the EncodeColor function.</summary>
|
|
/// <param name="encodedColor">encodedColor - The encoded color</param>
|
|
/// <returns>Color? - The decoded color</returns>
|
|
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]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encodes a Boolean Array as an int.
|
|
/// </summary>
|
|
/// <param name="bools">The boolean array to encode.</param>
|
|
/// <returns>The encoded int.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decodes a Boolean Array from an int.
|
|
/// </summary>
|
|
/// <param name="encodedbools">The encoded Boolean Array.</param>
|
|
/// <returns>The resulting Boolean Array.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>EncodeBitsByte - Encodes a BitsByte as a byte.</summary>
|
|
/// <param name="bitsByte">bitsByte - The BitsByte object</param>
|
|
/// <returns>byte? - The converted byte</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>DecodeBitsByte - Decodes a bitsbyte from an int.</summary>
|
|
/// <param name="encodedBitsByte">encodedBitsByte - The encoded bitsbyte object.</param>
|
|
/// <returns>BitsByte? - The decoded bitsbyte object</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>GetResponseNoException - Gets a web response without generating an exception.</summary>
|
|
/// <param name="req">req - The request to send.</param>
|
|
/// <returns>HttpWebResponse - The response object.</returns>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Colors the given text by correctly applying the color chat tag.
|
|
/// </summary>
|
|
/// <param name="text">The text to color.</param>
|
|
/// <param name="color">The color to apply.</param>
|
|
/// <returns>The <paramref name="text"/>, surrounded by the color tag with the appropriated hex code.</returns>
|
|
public string ColorTag(string text, Color color)
|
|
{
|
|
return String.Format("[c/{0}:{1}]", color.Hex3(), text);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts an item into its text representation using the item chat tag.
|
|
/// </summary>
|
|
/// <param name="item">The item to convert.</param>
|
|
/// <returns>The <paramref name="item"/> NetID surrounded by the item tag with proper stack/prefix data.</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of points selected by a mass-wiring tool.
|
|
/// </summary>
|
|
/// <param name="start">The starting point for the selection.</param>
|
|
/// <param name="end">The ending point for the selection.</param>
|
|
/// <param name="direction">False if facing left, True if facing right.</param>
|
|
/// <returns>
|
|
/// A list of coordinates containing the <paramref name="start"/> and <paramref name="end"/>
|
|
/// points and a list of points between them, forming an L shape based on <paramref name="direction"/>.
|
|
/// </returns>
|
|
public List<Point> GetMassWireOperationRange(Point start, Point end, bool direction = false)
|
|
{
|
|
List<Point> points = new List<Point>();
|
|
|
|
#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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dumps information and optionally exits afterwards
|
|
/// </summary>
|
|
/// <param name="exit"></param>
|
|
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();
|
|
}
|
|
|
|
/// <summary>Dumps a matrix of all permissions & all groups in Markdown table format.</summary>
|
|
/// <param name="path">The save destination.</param>
|
|
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<object[]> elements = new List<object[]>();
|
|
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<object[]> elements = new List<object[]>();
|
|
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<object[]> elements = new List<object[]>();
|
|
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<object[]> elements = new List<object[]>();
|
|
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<object[]> elements = new List<object[]>();
|
|
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<object[]> 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(",");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Starts an invasion on the server.</summary>
|
|
/// <param name="type">The invasion type id.</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Verifies that each stack in each chest is valid and not over the max stack count.</summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Updates the console title with some pertinent information.</summary>
|
|
/// <param name="empty">If the server is empty; determines if we should use Utils.ActivePlayers() for player count or 0.</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>Determines the distance between two vectors.</summary>
|
|
/// <param name="value1">The first vector location.</param>
|
|
/// <param name="value2">The second vector location.</param>
|
|
/// <returns>The distance between the two vectors.</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>Computes the max styles...</summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|