Guest User

Untitled

a guest
Dec 4th, 2018
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 185.30 KB | None | 0 0
  1. /*
  2. TShock, a server mod for Terraria
  3. Copyright (C) 2011-2018 Pryaxis & TShock Contributors
  4.  
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. */
  18.  
  19. using Newtonsoft.Json;
  20. using System;
  21. using System.Collections.Generic;
  22. using System.Collections.ObjectModel;
  23. using System.Diagnostics;
  24. using System.IO;
  25. using System.Linq;
  26. using System.Text;
  27. using System.Threading;
  28. using Terraria;
  29. using Terraria.ID;
  30. using Terraria.Localization;
  31. using TShockAPI.DB;
  32. using TerrariaApi.Server;
  33. using TShockAPI.Hooks;
  34. using Terraria.GameContent.Events;
  35. using Microsoft.Xna.Framework;
  36. using OTAPI.Tile;
  37. using TShockAPI.Localization;
  38. using System.Text.RegularExpressions;
  39.  
  40. namespace TShockAPI
  41. {
  42.     public delegate void CommandDelegate(CommandArgs args);
  43.  
  44.     public class CommandArgs : EventArgs
  45.     {
  46.         public string Message { get; private set; }
  47.         public TSPlayer Player { get; private set; }
  48.         public bool Silent { get; private set; }
  49.  
  50.         /// <summary>
  51.         /// Parameters passed to the arguement. Does not include the command name.
  52.         /// IE '/kick "jerk face"' will only have 1 argument
  53.         /// </summary>
  54.         public List<string> Parameters { get; private set; }
  55.  
  56.         public Player TPlayer
  57.         {
  58.             get { return Player.TPlayer; }
  59.         }
  60.  
  61.         public CommandArgs(string message, TSPlayer ply, List<string> args)
  62.         {
  63.             Message = message;
  64.             Player = ply;
  65.             Parameters = args;
  66.             Silent = false;
  67.         }
  68.  
  69.         public CommandArgs(string message, bool silent, TSPlayer ply, List<string> args)
  70.         {
  71.             Message = message;
  72.             Player = ply;
  73.             Parameters = args;
  74.             Silent = silent;
  75.         }
  76.     }
  77.  
  78.     public class Command
  79.     {
  80.         /// <summary>
  81.         /// Gets or sets whether to allow non-players to use this command.
  82.         /// </summary>
  83.         public bool AllowServer { get; set; }
  84.         /// <summary>
  85.         /// Gets or sets whether to do logging of this command.
  86.         /// </summary>
  87.         public bool DoLog { get; set; }
  88.         /// <summary>
  89.         /// Gets or sets the help text of this command.
  90.         /// </summary>
  91.         public string HelpText { get; set; }
  92.         /// <summary>
  93.         /// Gets or sets an extended description of this command.
  94.         /// </summary>
  95.         public string[] HelpDesc { get; set; }
  96.         /// <summary>
  97.         /// Gets the name of the command.
  98.         /// </summary>
  99.         public string Name { get { return Names[0]; } }
  100.         /// <summary>
  101.         /// Gets the names of the command.
  102.         /// </summary>
  103.         public List<string> Names { get; protected set; }
  104.         /// <summary>
  105.         /// Gets the permissions of the command.
  106.         /// </summary>
  107.         public List<string> Permissions { get; protected set; }
  108.  
  109.         private CommandDelegate commandDelegate;
  110.         public CommandDelegate CommandDelegate
  111.         {
  112.             get { return commandDelegate; }
  113.             set
  114.             {
  115.                 if (value == null)
  116.                     throw new ArgumentNullException();
  117.  
  118.                 commandDelegate = value;
  119.             }
  120.         }
  121.  
  122.         public Command(List<string> permissions, CommandDelegate cmd, params string[] names)
  123.             : this(cmd, names)
  124.         {
  125.             Permissions = permissions;
  126.         }
  127.  
  128.         public Command(string permissions, CommandDelegate cmd, params string[] names)
  129.             : this(cmd, names)
  130.         {
  131.             Permissions = new List<string> { permissions };
  132.         }
  133.  
  134.         public Command(CommandDelegate cmd, params string[] names)
  135.         {
  136.             if (cmd == null)
  137.                 throw new ArgumentNullException("cmd");
  138.             if (names == null || names.Length < 1)
  139.                 throw new ArgumentException("names");
  140.  
  141.             AllowServer = true;
  142.             CommandDelegate = cmd;
  143.             DoLog = true;
  144.             HelpText = "No help available.";
  145.             HelpDesc = null;
  146.             Names = new List<string>(names);
  147.             Permissions = new List<string>();
  148.         }
  149.  
  150.         public bool Run(string msg, bool silent, TSPlayer ply, List<string> parms)
  151.         {
  152.             if (!CanRun(ply))
  153.                 return false;
  154.  
  155.             try
  156.             {
  157.                 CommandDelegate(new CommandArgs(msg, silent, ply, parms));
  158.             }
  159.             catch (Exception e)
  160.             {
  161.                 ply.SendErrorMessage("Command failed, check logs for more details.");
  162.                 TShock.Log.Error(e.ToString());
  163.             }
  164.  
  165.             return true;
  166.         }
  167.  
  168.         public bool Run(string msg, TSPlayer ply, List<string> parms)
  169.         {
  170.             return Run(msg, false, ply, parms);
  171.         }
  172.  
  173.         public bool HasAlias(string name)
  174.         {
  175.             return Names.Contains(name);
  176.         }
  177.  
  178.         public bool CanRun(TSPlayer ply)
  179.         {
  180.             if (Permissions == null || Permissions.Count < 1)
  181.                 return true;
  182.             foreach (var Permission in Permissions)
  183.             {
  184.                 if (ply.HasPermission(Permission))
  185.                     return true;
  186.             }
  187.             return false;
  188.         }
  189.     }
  190.  
  191.     public static class Commands
  192.     {
  193.         public static List<Command> ChatCommands = new List<Command>();
  194.         public static ReadOnlyCollection<Command> TShockCommands = new ReadOnlyCollection<Command>(new List<Command>());
  195.  
  196.         /// <summary>
  197.         /// The command specifier, defaults to "/"
  198.         /// </summary>
  199.         public static string Specifier
  200.         {
  201.             get { return string.IsNullOrWhiteSpace(TShock.Config.CommandSpecifier) ? "/" : TShock.Config.CommandSpecifier; }
  202.         }
  203.  
  204.         /// <summary>
  205.         /// The silent command specifier, defaults to "."
  206.         /// </summary>
  207.         public static string SilentSpecifier
  208.         {
  209.             get { return string.IsNullOrWhiteSpace(TShock.Config.CommandSilentSpecifier) ? "." : TShock.Config.CommandSilentSpecifier; }
  210.         }
  211.  
  212.         private delegate void AddChatCommand(string permission, CommandDelegate command, params string[] names);
  213.  
  214.         public static void InitCommands()
  215.         {
  216.             List<Command> tshockCommands = new List<Command>(100);
  217.             Action<Command> add = (cmd) =>
  218.             {
  219.                 tshockCommands.Add(cmd);
  220.                 ChatCommands.Add(cmd);
  221.             };
  222.  
  223.             add(new Command(SetupToken, "setup")
  224.             {
  225.                 AllowServer = false,
  226.                 HelpText = "Used to authenticate as superadmin when first setting up TShock."
  227.             });
  228.             add(new Command(Permissions.user, ManageUsers, "user")
  229.             {
  230.                 DoLog = false,
  231.                 HelpText = "Manages user accounts."
  232.             });
  233.  
  234.             #region Account Commands
  235.             add(new Command(Permissions.canlogin, AttemptLogin, "login")
  236.             {
  237.                 AllowServer = false,
  238.                 DoLog = false,
  239.                 HelpText = "Logs you into an account."
  240.             });
  241.             add(new Command(Permissions.canlogout, Logout, "logout")
  242.             {
  243.                 AllowServer = false,
  244.                 DoLog = false,
  245.                 HelpText = "Logs you out of your current account."
  246.             });
  247.             add(new Command(Permissions.canchangepassword, PasswordUser, "password")
  248.             {
  249.                 AllowServer = false,
  250.                 DoLog = false,
  251.                 HelpText = "Changes your account's password."
  252.             });
  253.             add(new Command(Permissions.canregister, RegisterUser, "register")
  254.             {
  255.                 AllowServer = false,
  256.                 DoLog = false,
  257.                 HelpText = "Registers you an account."
  258.             });
  259.             add(new Command(Permissions.checkaccountinfo, ViewAccountInfo, "accountinfo", "ai")
  260.             {
  261.                 HelpText = "Shows information about a user."
  262.             });
  263.             #endregion
  264.             #region Admin Commands
  265.             add(new Command(Permissions.ban, Ban, "ban")
  266.             {
  267.                 HelpText = "Manages player bans."
  268.             });
  269.             add(new Command(Permissions.broadcast, Broadcast, "broadcast", "bc", "say")
  270.             {
  271.                 HelpText = "Broadcasts a message to everyone on the server."
  272.             });
  273.             add(new Command(Permissions.logs, DisplayLogs, "displaylogs")
  274.             {
  275.                 HelpText = "Toggles whether you receive server logs."
  276.             });
  277.             add(new Command(Permissions.managegroup, Group, "group")
  278.             {
  279.                 HelpText = "Manages groups."
  280.             });
  281.             add(new Command(Permissions.manageitem, ItemBan, "itemban")
  282.             {
  283.                 HelpText = "Manages item bans."
  284.             });
  285.             add(new Command(Permissions.manageprojectile, ProjectileBan, "projban")
  286.             {
  287.                 HelpText = "Manages projectile bans."
  288.             });
  289.             add(new Command(Permissions.managetile, TileBan, "tileban")
  290.             {
  291.                 HelpText = "Manages tile bans."
  292.             });
  293.             add(new Command(Permissions.manageregion, Region, "region")
  294.             {
  295.                 HelpText = "Manages regions."
  296.             });
  297.             add(new Command(Permissions.kick, Kick, "kick")
  298.             {
  299.                 HelpText = "Removes a player from the server."
  300.             });
  301.             add(new Command(Permissions.mute, Mute, "mute", "unmute")
  302.             {
  303.                 HelpText = "Prevents a player from talking."
  304.             });
  305.             add(new Command(Permissions.savessc, OverrideSSC, "overridessc", "ossc")
  306.             {
  307.                 HelpText = "Overrides serverside characters for a player, temporarily."
  308.             });
  309.             add(new Command(Permissions.savessc, SaveSSC, "savessc")
  310.             {
  311.                 HelpText = "Saves all serverside characters."
  312.             });
  313.             add(new Command(Permissions.uploaddata, UploadJoinData, "uploadssc")
  314.             {
  315.                 HelpText = "Upload the account information when you joined the server as your Server Side Character data."
  316.             });
  317.             add(new Command(Permissions.settempgroup, TempGroup, "tempgroup")
  318.             {
  319.                 HelpText = "Temporarily sets another player's group."
  320.             });
  321.             add(new Command(Permissions.su, SubstituteUser, "su")
  322.             {
  323.                 HelpText = "Temporarily elevates you to Super Admin."
  324.             });
  325.             add(new Command(Permissions.su, SubstituteUserDo, "sudo")
  326.             {
  327.                 HelpText = "Executes a command as the super admin."
  328.             });
  329.             add(new Command(Permissions.userinfo, GrabUserUserInfo, "userinfo", "ui")
  330.             {
  331.                 HelpText = "Shows information about a player."
  332.             });
  333.             #endregion
  334.             #region Annoy Commands
  335.             add(new Command(Permissions.annoy, Annoy, "annoy")
  336.             {
  337.                 HelpText = "Annoys a player for an amount of time."
  338.             });
  339.             add(new Command(Permissions.annoy, Confuse, "confuse")
  340.             {
  341.                 HelpText = "Confuses a player for an amount of time."
  342.             });
  343.             add(new Command(Permissions.annoy, Rocket, "rocket")
  344.             {
  345.                 HelpText = "Rockets a player upwards. Requires SSC."
  346.             });
  347.             add(new Command(Permissions.annoy, FireWork, "firework")
  348.             {
  349.                 HelpText = "Spawns fireworks at a player."
  350.             });
  351.             #endregion
  352.             #region Configuration Commands
  353.             add(new Command(Permissions.maintenance, CheckUpdates, "checkupdates")
  354.             {
  355.                 HelpText = "Checks for TShock updates."
  356.             });
  357.             add(new Command(Permissions.maintenance, Off, "off", "exit", "stop")
  358.             {
  359.                 HelpText = "Shuts down the server while saving."
  360.             });
  361.             add(new Command(Permissions.maintenance, OffNoSave, "off-nosave", "exit-nosave", "stop-nosave")
  362.             {
  363.                 HelpText = "Shuts down the server without saving."
  364.             });
  365.             add(new Command(Permissions.cfgreload, Reload, "reload")
  366.             {
  367.                 HelpText = "Reloads the server configuration file."
  368.             });
  369.             add(new Command(Permissions.cfgpassword, ServerPassword, "serverpassword")
  370.             {
  371.                 HelpText = "Changes the server password."
  372.             });
  373.             add(new Command(Permissions.maintenance, GetVersion, "version")
  374.             {
  375.                 HelpText = "Shows the TShock version."
  376.             });
  377.             add(new Command(Permissions.whitelist, Whitelist, "whitelist")
  378.             {
  379.                 HelpText = "Manages the server whitelist."
  380.             });
  381.             #endregion
  382.             #region Item Commands
  383.             add(new Command(Permissions.give, Give, "give", "g")
  384.             {
  385.                 HelpText = "Gives another player an item."
  386.             });
  387.             add(new Command(Permissions.item, Item, "item", "i")
  388.             {
  389.                 AllowServer = false,
  390.                 HelpText = "Gives yourself an item."
  391.             });
  392.             #endregion
  393.             #region NPC Commands
  394.             add(new Command(Permissions.butcher, Butcher, "butcher")
  395.             {
  396.                 HelpText = "Kills hostile NPCs or NPCs of a certain type."
  397.             });
  398.             add(new Command(Permissions.renamenpc, RenameNPC, "renamenpc")
  399.             {
  400.                 HelpText = "Renames an NPC."
  401.             });
  402.             add(new Command(Permissions.invade, Invade, "invade")
  403.             {
  404.                 HelpText = "Starts an NPC invasion."
  405.             });
  406.             add(new Command(Permissions.maxspawns, MaxSpawns, "maxspawns")
  407.             {
  408.                 HelpText = "Sets the maximum number of NPCs."
  409.             });
  410.             add(new Command(Permissions.spawnboss, SpawnBoss, "spawnboss", "sb")
  411.             {
  412.                 AllowServer = false,
  413.                 HelpText = "Spawns a number of bosses around you."
  414.             });
  415.             add(new Command(Permissions.spawnmob, SpawnMob, "spawnmob", "sm")
  416.             {
  417.                 AllowServer = false,
  418.                 HelpText = "Spawns a number of mobs around you."
  419.             });
  420.             add(new Command(Permissions.spawnrate, SpawnRate, "spawnrate")
  421.             {
  422.                 HelpText = "Sets the spawn rate of NPCs."
  423.             });
  424.             add(new Command(Permissions.clearangler, ClearAnglerQuests, "clearangler")
  425.             {
  426.                 HelpText = "Resets the list of users who have completed an angler quest that day."
  427.             });
  428.             #endregion
  429.             #region TP Commands
  430.             add(new Command(Permissions.home, Home, "home")
  431.             {
  432.                 AllowServer = false,
  433.                 HelpText = "Sends you to your spawn point."
  434.             });
  435.             add(new Command(Permissions.spawn, Spawn, "spawn")
  436.             {
  437.                 AllowServer = false,
  438.                 HelpText = "Sends you to the world's spawn point."
  439.             });
  440.             add(new Command(Permissions.tp, TP, "tp")
  441.             {
  442.                 AllowServer = false,
  443.                 HelpText = "Teleports a player to another player."
  444.             });
  445.             add(new Command(Permissions.tpothers, TPHere, "tphere")
  446.             {
  447.                 AllowServer = false,
  448.                 HelpText = "Teleports a player to yourself."
  449.             });
  450.             add(new Command(Permissions.tpnpc, TPNpc, "tpnpc")
  451.             {
  452.                 AllowServer = false,
  453.                 HelpText = "Teleports you to an npc."
  454.             });
  455.             add(new Command(Permissions.tppos, TPPos, "tppos")
  456.             {
  457.                 AllowServer = false,
  458.                 HelpText = "Teleports you to tile coordinates."
  459.             });
  460.             add(new Command(Permissions.getpos, GetPos, "pos")
  461.             {
  462.                 AllowServer = false,
  463.                 HelpText = "Returns the user's or specified user's current position."
  464.             });
  465.             add(new Command(Permissions.tpallow, TPAllow, "tpallow")
  466.             {
  467.                 AllowServer = false,
  468.                 HelpText = "Toggles whether other people can teleport you."
  469.             });
  470.             #endregion
  471.             #region World Commands
  472.             add(new Command(Permissions.toggleexpert, ToggleExpert, "expert", "expertmode")
  473.             {
  474.                 HelpText = "Toggles expert mode."
  475.             });
  476.             add(new Command(Permissions.antibuild, ToggleAntiBuild, "antibuild")
  477.             {
  478.                 HelpText = "Toggles build protection."
  479.             });
  480.             add(new Command(Permissions.bloodmoon, Bloodmoon, "bloodmoon")
  481.             {
  482.                 HelpText = "Sets a blood moon."
  483.             });
  484.             add(new Command(Permissions.grow, Grow, "grow")
  485.             {
  486.                 AllowServer = false,
  487.                 HelpText = "Grows plants at your location."
  488.             });
  489.             add(new Command(Permissions.dropmeteor, DropMeteor, "dropmeteor")
  490.             {
  491.                 HelpText = "Drops a meteor somewhere in the world."
  492.             });
  493.             add(new Command(Permissions.eclipse, Eclipse, "eclipse")
  494.             {
  495.                 HelpText = "Sets an eclipse."
  496.             });
  497.             add(new Command(Permissions.halloween, ForceHalloween, "forcehalloween")
  498.             {
  499.                 HelpText = "Toggles halloween mode (goodie bags, pumpkins, etc)."
  500.             });
  501.             add(new Command(Permissions.xmas, ForceXmas, "forcexmas")
  502.             {
  503.                 HelpText = "Toggles christmas mode (present spawning, santa, etc)."
  504.             });
  505.             add(new Command(Permissions.fullmoon, Fullmoon, "fullmoon")
  506.             {
  507.                 HelpText = "Sets a full moon."
  508.             });
  509.             add(new Command(Permissions.hardmode, Hardmode, "hardmode")
  510.             {
  511.                 HelpText = "Toggles the world's hardmode status."
  512.             });
  513.             add(new Command(Permissions.editspawn, ProtectSpawn, "protectspawn")
  514.             {
  515.                 HelpText = "Toggles spawn protection."
  516.             });
  517.             add(new Command(Permissions.sandstorm, Sandstorm, "sandstorm")
  518.             {
  519.                 HelpText = "Toggles sandstorms."
  520.             });
  521.             add(new Command(Permissions.rain, Rain, "rain")
  522.             {
  523.                 HelpText = "Toggles the rain."
  524.             });
  525.             add(new Command(Permissions.worldsave, Save, "save")
  526.             {
  527.                 HelpText = "Saves the world file."
  528.             });
  529.             add(new Command(Permissions.worldspawn, SetSpawn, "setspawn")
  530.             {
  531.                 AllowServer = false,
  532.                 HelpText = "Sets the world's spawn point to your location."
  533.             });
  534.             add(new Command(Permissions.dungeonposition, SetDungeon, "setdungeon")
  535.             {
  536.                 AllowServer = false,
  537.                 HelpText = "Sets the dungeon's position to your location."
  538.             });
  539.             add(new Command(Permissions.worldsettle, Settle, "settle")
  540.             {
  541.                 HelpText = "Forces all liquids to update immediately."
  542.             });
  543.             add(new Command(Permissions.time, Time, "time")
  544.             {
  545.                 HelpText = "Sets the world time."
  546.             });
  547.             add(new Command(Permissions.wind, Wind, "wind")
  548.             {
  549.                 HelpText = "Changes the wind speed."
  550.             });
  551.             add(new Command(Permissions.worldinfo, WorldInfo, "world")
  552.             {
  553.                 HelpText = "Shows information about the current world."
  554.             });
  555.             #endregion
  556.             #region Other Commands
  557.             add(new Command(Permissions.buff, Buff, "buff")
  558.             {
  559.                 AllowServer = false,
  560.                 HelpText = "Gives yourself a buff for an amount of time."
  561.             });
  562.             add(new Command(Permissions.clear, Clear, "clear")
  563.             {
  564.                 HelpText = "Clears item drops or projectiles."
  565.             });
  566.             add(new Command(Permissions.buffplayer, GBuff, "gbuff", "buffplayer")
  567.             {
  568.                 HelpText = "Gives another player a buff for an amount of time."
  569.             });
  570.             add(new Command(Permissions.godmode, ToggleGodMode, "godmode")
  571.             {
  572.                 HelpText = "Toggles godmode on a player."
  573.             });
  574.             add(new Command(Permissions.heal, Heal, "heal")
  575.             {
  576.                 HelpText = "Heals a player in HP and MP."
  577.             });
  578.             add(new Command(Permissions.kill, Kill, "kill")
  579.             {
  580.                 HelpText = "Kills another player."
  581.             });
  582.             add(new Command(Permissions.cantalkinthird, ThirdPerson, "me")
  583.             {
  584.                 HelpText = "Sends an action message to everyone."
  585.             });
  586.             add(new Command(Permissions.canpartychat, PartyChat, "party", "p")
  587.             {
  588.                 AllowServer = false,
  589.                 HelpText = "Sends a message to everyone on your team."
  590.             });
  591.             add(new Command(Permissions.whisper, Reply, "reply", "r")
  592.             {
  593.                 HelpText = "Replies to a PM sent to you."
  594.             });
  595.             add(new Command(Rests.RestPermissions.restmanage, ManageRest, "rest")
  596.             {
  597.                 HelpText = "Manages the REST API."
  598.             });
  599.             add(new Command(Permissions.slap, Slap, "slap")
  600.             {
  601.                 HelpText = "Slaps a player, dealing damage."
  602.             });
  603.             add(new Command(Permissions.serverinfo, ServerInfo, "serverinfo")
  604.             {
  605.                 HelpText = "Shows the server information."
  606.             });
  607.             add(new Command(Permissions.warp, Warp, "warp")
  608.             {
  609.                 HelpText = "Teleports you to a warp point or manages warps."
  610.             });
  611.             add(new Command(Permissions.whisper, Whisper, "whisper", "w", "tell")
  612.             {
  613.                 HelpText = "Sends a PM to a player."
  614.             });
  615.             add(new Command(Permissions.createdumps, CreateDumps, "dump-reference-data")
  616.             {
  617.                 HelpText = "Creates a reference tables for Terraria data types and the TShock permission system in the server folder."
  618.             });
  619.             #endregion
  620.  
  621.             add(new Command(Aliases, "aliases")
  622.             {
  623.                 HelpText = "Shows a command's aliases."
  624.             });
  625.             add(new Command(Help, "help")
  626.             {
  627.                 HelpText = "Lists commands or gives help on them."
  628.             });
  629.             add(new Command(Motd, "motd")
  630.             {
  631.                 HelpText = "Shows the message of the day."
  632.             });
  633.             add(new Command(ListConnectedPlayers, "playing", "online", "who")
  634.             {
  635.                 HelpText = "Shows the currently connected players."
  636.             });
  637.             add(new Command(Rules, "rules")
  638.             {
  639.                 HelpText = "Shows the server's rules."
  640.             });
  641.  
  642.             TShockCommands = new ReadOnlyCollection<Command>(tshockCommands);
  643.         }
  644.  
  645.         public static bool HandleCommand(TSPlayer player, string text)
  646.         {
  647.             string cmdText = text.Remove(0, 1);
  648.             string cmdPrefix = text[0].ToString();
  649.             bool silent = false;
  650.  
  651.             if (cmdPrefix == SilentSpecifier)
  652.                 silent = true;
  653.  
  654.             int index = -1;
  655.             for (int i = 0; i < cmdText.Length; i++)
  656.             {
  657.                 if (IsWhiteSpace(cmdText[i]))
  658.                 {
  659.                     index = i;
  660.                     break;
  661.                 }
  662.             }
  663.             string cmdName;
  664.             if (index == 0) // Space after the command specifier should not be supported
  665.             {
  666.                 player.SendErrorMessage("Invalid command entered. Type {0}help for a list of valid commands.", Specifier);
  667.                 return true;
  668.             }
  669.             else if (index < 0)
  670.                 cmdName = cmdText.ToLower();
  671.             else
  672.                 cmdName = cmdText.Substring(0, index).ToLower();
  673.  
  674.             List<string> args;
  675.             if (index < 0)
  676.                 args = new List<string>();
  677.             else
  678.                 args = ParseParameters(cmdText.Substring(index));
  679.  
  680.             IEnumerable<Command> cmds = ChatCommands.FindAll(c => c.HasAlias(cmdName));
  681.  
  682.             if (Hooks.PlayerHooks.OnPlayerCommand(player, cmdName, cmdText, args, ref cmds, cmdPrefix))
  683.                 return true;
  684.  
  685.             if (cmds.Count() == 0)
  686.             {
  687.                 if (player.AwaitingResponse.ContainsKey(cmdName))
  688.                 {
  689.                     Action<CommandArgs> call = player.AwaitingResponse[cmdName];
  690.                     player.AwaitingResponse.Remove(cmdName);
  691.                     call(new CommandArgs(cmdText, player, args));
  692.                     return true;
  693.                 }
  694.                 player.SendErrorMessage("Invalid command entered. Type {0}help for a list of valid commands.", Specifier);
  695.                 return true;
  696.             }
  697.             foreach (Command cmd in cmds)
  698.             {
  699.                 if (!cmd.CanRun(player))
  700.                 {
  701.                     TShock.Utils.SendLogs(string.Format("{0} tried to execute {1}{2}.", player.Name, Specifier, cmdText), Color.PaleVioletRed, player);
  702.                     player.SendErrorMessage("You do not have access to this command.");
  703.                     if (player.HasPermission(Permissions.su))
  704.                     {
  705.                         player.SendInfoMessage("You can use '{0}sudo {0}{1}' to override this check.", Specifier, cmdText);
  706.                     }
  707.                 }
  708.                 else if (!cmd.AllowServer && !player.RealPlayer)
  709.                 {
  710.                     player.SendErrorMessage("You must use this command in-game.");
  711.                 }
  712.                 else
  713.                 {
  714.                     if (cmd.DoLog)
  715.                         TShock.Utils.SendLogs(string.Format("{0} executed: {1}{2}.", player.Name, silent ? SilentSpecifier : Specifier, cmdText), Color.PaleVioletRed, player);
  716.                     cmd.Run(cmdText, silent, player, args);
  717.                 }
  718.             }
  719.             return true;
  720.         }
  721.  
  722.         /// <summary>
  723.         /// Parses a string of parameters into a list. Handles quotes.
  724.         /// </summary>
  725.         /// <param name="str"></param>
  726.         /// <returns></returns>
  727.         private static List<String> ParseParameters(string str)
  728.         {
  729.             var ret = new List<string>();
  730.             var sb = new StringBuilder();
  731.             bool instr = false;
  732.             for (int i = 0; i < str.Length; i++)
  733.             {
  734.                 char c = str[i];
  735.  
  736.                 if (c == '\\' && ++i < str.Length)
  737.                 {
  738.                     if (str[i] != '"' && str[i] != ' ' && str[i] != '\\')
  739.                         sb.Append('\\');
  740.                     sb.Append(str[i]);
  741.                 }
  742.                 else if (c == '"')
  743.                 {
  744.                     instr = !instr;
  745.                     if (!instr)
  746.                     {
  747.                         ret.Add(sb.ToString());
  748.                         sb.Clear();
  749.                     }
  750.                     else if (sb.Length > 0)
  751.                     {
  752.                         ret.Add(sb.ToString());
  753.                         sb.Clear();
  754.                     }
  755.                 }
  756.                 else if (IsWhiteSpace(c) && !instr)
  757.                 {
  758.                     if (sb.Length > 0)
  759.                     {
  760.                         ret.Add(sb.ToString());
  761.                         sb.Clear();
  762.                     }
  763.                 }
  764.                 else
  765.                     sb.Append(c);
  766.             }
  767.             if (sb.Length > 0)
  768.                 ret.Add(sb.ToString());
  769.  
  770.             return ret;
  771.         }
  772.  
  773.         private static bool IsWhiteSpace(char c)
  774.         {
  775.             return c == ' ' || c == '\t' || c == '\n';
  776.         }
  777.  
  778.         #region Account commands
  779.  
  780.         private static void AttemptLogin(CommandArgs args)
  781.         {
  782.             if (args.Player.LoginAttempts > TShock.Config.MaximumLoginAttempts && (TShock.Config.MaximumLoginAttempts != -1))
  783.             {
  784.                 TShock.Log.Warn(String.Format("{0} ({1}) had {2} or more invalid login attempts and was kicked automatically.",
  785.                     args.Player.IP, args.Player.Name, TShock.Config.MaximumLoginAttempts));
  786.                 args.Player.Kick("Too many invalid login attempts.");
  787.                 return;
  788.             }
  789.  
  790.             if (args.Player.IsLoggedIn)
  791.             {
  792.                 args.Player.SendErrorMessage("You are already logged in, and cannot login again.");
  793.                 return;
  794.             }
  795.  
  796.             UserAccount account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name);
  797.             string password = "";
  798.             bool usingUUID = false;
  799.             if (args.Parameters.Count == 0 && !TShock.Config.DisableUUIDLogin)
  800.             {
  801.                 if (PlayerHooks.OnPlayerPreLogin(args.Player, args.Player.Name, ""))
  802.                     return;
  803.                 usingUUID = true;
  804.             }
  805.             else if (args.Parameters.Count == 1)
  806.             {
  807.                 if (PlayerHooks.OnPlayerPreLogin(args.Player, args.Player.Name, args.Parameters[0]))
  808.                     return;
  809.                 password = args.Parameters[0];
  810.             }
  811.             else if (args.Parameters.Count == 2 && TShock.Config.AllowLoginAnyUsername)
  812.             {
  813.                 if (String.IsNullOrEmpty(args.Parameters[0]))
  814.                 {
  815.                     args.Player.SendErrorMessage("Bad login attempt.");
  816.                     return;
  817.                 }
  818.  
  819.                 if (PlayerHooks.OnPlayerPreLogin(args.Player, args.Parameters[0], args.Parameters[1]))
  820.                     return;
  821.  
  822.                 account = TShock.UserAccounts.GetUserAccountByName(args.Parameters[0]);
  823.                 password = args.Parameters[1];
  824.             }
  825.             else
  826.             {
  827.                 args.Player.SendErrorMessage("Syntax: {0}login - Logs in using your UUID and character name", Specifier);
  828.                 args.Player.SendErrorMessage("        {0}login <password> - Logs in using your password and character name", Specifier);
  829.                 args.Player.SendErrorMessage("        {0}login <username> <password> - Logs in using your username and password", Specifier);
  830.                 args.Player.SendErrorMessage("If you forgot your password, there is no way to recover it.");
  831.                 return;
  832.             }
  833.             try
  834.             {
  835.                 if (account == null)
  836.                 {
  837.                     args.Player.SendErrorMessage("A user account by that name does not exist.");
  838.                 }
  839.                 else if (account.VerifyPassword(password) ||
  840.                         (usingUUID && account.UUID == args.Player.UUID && !TShock.Config.DisableUUIDLogin &&
  841.                         !String.IsNullOrWhiteSpace(args.Player.UUID)))
  842.                 {
  843.                     args.Player.PlayerData = TShock.CharacterDB.GetPlayerData(args.Player, account.ID);
  844.  
  845.                     var group = TShock.Groups.GetGroupByName(account.Group);
  846.  
  847.                     args.Player.Group = group;
  848.                     args.Player.tempGroup = null;
  849.                     args.Player.Account = account;
  850.                     args.Player.IsLoggedIn = true;
  851.                     args.Player.IsDisabledForSSC = false;
  852.  
  853.                     if (Main.ServerSideCharacter)
  854.                     {
  855.                         if (args.Player.HasPermission(Permissions.bypassssc))
  856.                         {
  857.                             args.Player.PlayerData.CopyCharacter(args.Player);
  858.                             TShock.CharacterDB.InsertPlayerData(args.Player);
  859.                         }
  860.                         args.Player.PlayerData.RestoreCharacter(args.Player);
  861.                     }
  862.                     args.Player.LoginFailsBySsi = false;
  863.  
  864.                     if (args.Player.HasPermission(Permissions.ignorestackhackdetection))
  865.                         args.Player.IsDisabledForStackDetection = false;
  866.  
  867.                     if (args.Player.HasPermission(Permissions.usebanneditem))
  868.                         args.Player.IsDisabledForBannedWearable = false;
  869.  
  870.                     args.Player.SendSuccessMessage("Authenticated as " + account.Name + " successfully.");
  871.  
  872.                     TShock.Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user: " + account.Name + ".");
  873.                     if ((args.Player.LoginHarassed) && (TShock.Config.RememberLeavePos))
  874.                     {
  875.                         if (TShock.RememberedPos.GetLeavePos(args.Player.Name, args.Player.IP) != Vector2.Zero)
  876.                         {
  877.                             Vector2 pos = TShock.RememberedPos.GetLeavePos(args.Player.Name, args.Player.IP);
  878.                             args.Player.Teleport((int)pos.X * 16, (int)pos.Y * 16);
  879.                         }
  880.                         args.Player.LoginHarassed = false;
  881.  
  882.                     }
  883.                     TShock.UserAccounts.SetUserAccountUUID(account, args.Player.UUID);
  884.  
  885.                     Hooks.PlayerHooks.OnPlayerPostLogin(args.Player);
  886.                 }
  887.                 else
  888.                 {
  889.                     if (usingUUID && !TShock.Config.DisableUUIDLogin)
  890.                     {
  891.                         args.Player.SendErrorMessage("UUID does not match this character!");
  892.                     }
  893.                     else
  894.                     {
  895.                         args.Player.SendErrorMessage("Invalid password!");
  896.                     }
  897.                     TShock.Log.Warn(args.Player.IP + " failed to authenticate as user: " + account.Name + ".");
  898.                     args.Player.LoginAttempts++;
  899.                 }
  900.             }
  901.             catch (Exception ex)
  902.             {
  903.                 args.Player.SendErrorMessage("There was an error processing your request.");
  904.                 TShock.Log.Error(ex.ToString());
  905.             }
  906.         }
  907.  
  908.         private static void Logout(CommandArgs args)
  909.         {
  910.             if (!args.Player.IsLoggedIn)
  911.             {
  912.                 args.Player.SendErrorMessage("You are not logged in.");
  913.                 return;
  914.             }
  915.  
  916.             args.Player.Logout();
  917.             args.Player.SendSuccessMessage("You have been successfully logged out of your account.");
  918.             if (Main.ServerSideCharacter)
  919.             {
  920.                 args.Player.SendWarningMessage("Server side characters are enabled. You need to be logged in to play.");
  921.             }
  922.         }
  923.  
  924.         private static void PasswordUser(CommandArgs args)
  925.         {
  926.             try
  927.             {
  928.                 if (args.Player.IsLoggedIn && args.Parameters.Count == 2)
  929.                 {
  930.                     string password = args.Parameters[0];
  931.                     if (args.Player.Account.VerifyPassword(password))
  932.                     {
  933.                         try
  934.                         {
  935.                             args.Player.SendSuccessMessage("You changed your password!");
  936.                             TShock.UserAccounts.SetUserAccountPassword(args.Player.Account, args.Parameters[1]); // SetUserPassword will hash it for you.
  937.                             TShock.Log.ConsoleInfo(args.Player.IP + " named " + args.Player.Name + " changed the password of account " +
  938.                                                    args.Player.Account.Name + ".");
  939.                         }
  940.                         catch (ArgumentOutOfRangeException)
  941.                         {
  942.                             args.Player.SendErrorMessage("Password must be greater than or equal to " + TShock.Config.MinimumPasswordLength + " characters.");
  943.                         }
  944.                     }
  945.                     else
  946.                     {
  947.                         args.Player.SendErrorMessage("You failed to change your password!");
  948.                         TShock.Log.ConsoleError(args.Player.IP + " named " + args.Player.Name + " failed to change password for account: " +
  949.                                                 args.Player.Account.Name + ".");
  950.                     }
  951.                 }
  952.                 else
  953.                 {
  954.                     args.Player.SendErrorMessage("Not logged in or invalid syntax! Proper syntax: {0}password <oldpassword> <newpassword>", Specifier);
  955.                 }
  956.             }
  957.             catch (UserAccountManagerException ex)
  958.             {
  959.                 args.Player.SendErrorMessage("Sorry, an error occured: " + ex.Message + ".");
  960.                 TShock.Log.ConsoleError("PasswordUser returned an error: " + ex);
  961.             }
  962.         }
  963.  
  964.         private static void RegisterUser(CommandArgs args)
  965.         {
  966.             try
  967.             {
  968.                 var account = new UserAccount();
  969.                 string echoPassword = "";
  970.                 if (args.Parameters.Count == 1)
  971.                 {
  972.                     account.Name = args.Player.Name;
  973.                     echoPassword = args.Parameters[0];
  974.                     try
  975.                     {
  976.                         account.CreateBCryptHash(args.Parameters[0]);
  977.                     }
  978.                     catch (ArgumentOutOfRangeException)
  979.                     {
  980.                         args.Player.SendErrorMessage("Password must be greater than or equal to " + TShock.Config.MinimumPasswordLength + " characters.");
  981.                         return;
  982.                     }
  983.                 }
  984.                 else if (args.Parameters.Count == 2 && TShock.Config.AllowRegisterAnyUsername)
  985.                 {
  986.                     account.Name = args.Parameters[0];
  987.                     echoPassword = args.Parameters[1];
  988.                     try
  989.                     {
  990.                         account.CreateBCryptHash(args.Parameters[1]);
  991.                     }
  992.                     catch (ArgumentOutOfRangeException)
  993.                     {
  994.                         args.Player.SendErrorMessage("Password must be greater than or equal to " + TShock.Config.MinimumPasswordLength + " characters.");
  995.                         return;
  996.                     }
  997.                 }
  998.                 else
  999.                 {
  1000.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}register <password>", Specifier);
  1001.                     return;
  1002.                 }
  1003.  
  1004.                 account.Group = TShock.Config.DefaultRegistrationGroupName; // FIXME -- we should get this from the DB. --Why?
  1005.                 account.UUID = args.Player.UUID;
  1006.  
  1007.                 if (TShock.UserAccounts.GetUserAccountByName(account.Name) == null && account.Name != TSServerPlayer.AccountName) // Cheap way of checking for existance of a user
  1008.                 {
  1009.                     args.Player.SendSuccessMessage("Account \"{0}\" has been registered.", account.Name);
  1010.                     args.Player.SendSuccessMessage("Your password is {0}.", echoPassword);
  1011.                     TShock.UserAccounts.AddUserAccount(account);
  1012.                     TShock.Log.ConsoleInfo("{0} registered an account: \"{1}\".", args.Player.Name, account.Name);
  1013.                 }
  1014.                 else
  1015.                 {
  1016.                     args.Player.SendErrorMessage("Sorry, " + account.Name + " was already taken by another person.");
  1017.                     args.Player.SendErrorMessage("Please try a different username.");
  1018.                     TShock.Log.ConsoleInfo(args.Player.Name + " failed to register an existing account: " + account.Name);
  1019.                 }
  1020.             }
  1021.             catch (UserAccountManagerException ex)
  1022.             {
  1023.                 args.Player.SendErrorMessage("Sorry, an error occured: " + ex.Message + ".");
  1024.                 TShock.Log.ConsoleError("RegisterUser returned an error: " + ex);
  1025.             }
  1026.         }
  1027.  
  1028.         private static void ManageUsers(CommandArgs args)
  1029.         {
  1030.             // This guy needs to be here so that people don't get exceptions when they type /user
  1031.             if (args.Parameters.Count < 1)
  1032.             {
  1033.                 args.Player.SendErrorMessage("Invalid user syntax. Try {0}user help.", Specifier);
  1034.                 return;
  1035.             }
  1036.  
  1037.             string subcmd = args.Parameters[0];
  1038.  
  1039.             // Add requires a username, password, and a group specified.
  1040.             if (subcmd == "add" && args.Parameters.Count == 4)
  1041.             {
  1042.                 var account = new UserAccount();
  1043.  
  1044.                 account.Name = args.Parameters[1];
  1045.                 try
  1046.                 {
  1047.                     account.CreateBCryptHash(args.Parameters[2]);
  1048.                 }
  1049.                 catch (ArgumentOutOfRangeException)
  1050.                 {
  1051.                     args.Player.SendErrorMessage("Password must be greater than or equal to " + TShock.Config.MinimumPasswordLength + " characters.");
  1052.                     return;
  1053.                 }
  1054.                 account.Group = args.Parameters[3];
  1055.  
  1056.                 try
  1057.                 {
  1058.                     TShock.UserAccounts.AddUserAccount(account);
  1059.                     args.Player.SendSuccessMessage("Account " + account.Name + " has been added to group " + account.Group + "!");
  1060.                     TShock.Log.ConsoleInfo(args.Player.Name + " added Account " + account.Name + " to group " + account.Group);
  1061.                 }
  1062.                 catch (GroupNotExistsException)
  1063.                 {
  1064.                     args.Player.SendErrorMessage("Group " + account.Group + " does not exist!");
  1065.                 }
  1066.                 catch (UserAccountExistsException)
  1067.                 {
  1068.                     args.Player.SendErrorMessage("User " + account.Name + " already exists!");
  1069.                 }
  1070.                 catch (UserAccountManagerException e)
  1071.                 {
  1072.                     args.Player.SendErrorMessage("User " + account.Name + " could not be added, check console for details.");
  1073.                     TShock.Log.ConsoleError(e.ToString());
  1074.                 }
  1075.             }
  1076.             // User deletion requires a username
  1077.             else if (subcmd == "del" && args.Parameters.Count == 2)
  1078.             {
  1079.                 var account = new UserAccount();
  1080.                 account.Name = args.Parameters[1];
  1081.  
  1082.                 try
  1083.                 {
  1084.                     TShock.UserAccounts.RemoveUserAccount(account);
  1085.                     args.Player.SendSuccessMessage("Account removed successfully.");
  1086.                     TShock.Log.ConsoleInfo(args.Player.Name + " successfully deleted account: " + args.Parameters[1] + ".");
  1087.                 }
  1088.                 catch (UserAccountNotExistException)
  1089.                 {
  1090.                     args.Player.SendErrorMessage("The user " + account.Name + " does not exist! Deleted nobody!");
  1091.                 }
  1092.                 catch (UserAccountManagerException ex)
  1093.                 {
  1094.                     args.Player.SendErrorMessage(ex.Message);
  1095.                     TShock.Log.ConsoleError(ex.ToString());
  1096.                 }
  1097.             }
  1098.  
  1099.             // Password changing requires a username, and a new password to set
  1100.             else if (subcmd == "password" && args.Parameters.Count == 3)
  1101.             {
  1102.                 var account = new UserAccount();
  1103.                 account.Name = args.Parameters[1];
  1104.  
  1105.                 try
  1106.                 {
  1107.                     TShock.UserAccounts.SetUserAccountPassword(account, args.Parameters[2]);
  1108.                     TShock.Log.ConsoleInfo(args.Player.Name + " changed the password of account " + account.Name);
  1109.                     args.Player.SendSuccessMessage("Password change succeeded for " + account.Name + ".");
  1110.                 }
  1111.                 catch (UserAccountNotExistException)
  1112.                 {
  1113.                     args.Player.SendErrorMessage("User " + account.Name + " does not exist!");
  1114.                 }
  1115.                 catch (UserAccountManagerException e)
  1116.                 {
  1117.                     args.Player.SendErrorMessage("Password change for " + account.Name + " failed! Check console!");
  1118.                     TShock.Log.ConsoleError(e.ToString());
  1119.                 }
  1120.                 catch (ArgumentOutOfRangeException)
  1121.                 {
  1122.                     args.Player.SendErrorMessage("Password must be greater than or equal to " + TShock.Config.MinimumPasswordLength + " characters.");
  1123.                 }
  1124.             }
  1125.             // Group changing requires a username or IP address, and a new group to set
  1126.             else if (subcmd == "group" && args.Parameters.Count == 3)
  1127.             {
  1128.                 var account = new UserAccount();
  1129.                 account.Name = args.Parameters[1];
  1130.  
  1131.                 try
  1132.                 {
  1133.                     TShock.UserAccounts.SetUserGroup(account, args.Parameters[2]);
  1134.                     TShock.Log.ConsoleInfo(args.Player.Name + " changed account " + account.Name + " to group " + args.Parameters[2] + ".");
  1135.                     args.Player.SendSuccessMessage("Account " + account.Name + " has been changed to group " + args.Parameters[2] + "!");
  1136.                 }
  1137.                 catch (GroupNotExistsException)
  1138.                 {
  1139.                     args.Player.SendErrorMessage("That group does not exist!");
  1140.                 }
  1141.                 catch (UserAccountNotExistException)
  1142.                 {
  1143.                     args.Player.SendErrorMessage("User " + account.Name + " does not exist!");
  1144.                 }
  1145.                 catch (UserAccountManagerException e)
  1146.                 {
  1147.                     args.Player.SendErrorMessage("User " + account.Name + " could not be added. Check console for details.");
  1148.                     TShock.Log.ConsoleError(e.ToString());
  1149.                 }
  1150.             }
  1151.             else if (subcmd == "help")
  1152.             {
  1153.                 args.Player.SendInfoMessage("Use command help:");
  1154.                 args.Player.SendInfoMessage("{0}user add username password group   -- Adds a specified user", Specifier);
  1155.                 args.Player.SendInfoMessage("{0}user del username                  -- Removes a specified user", Specifier);
  1156.                 args.Player.SendInfoMessage("{0}user password username newpassword -- Changes a user's password", Specifier);
  1157.                 args.Player.SendInfoMessage("{0}user group username newgroup       -- Changes a user's group", Specifier);
  1158.             }
  1159.             else
  1160.             {
  1161.                 args.Player.SendErrorMessage("Invalid user syntax. Try {0}user help.", Specifier);
  1162.             }
  1163.         }
  1164.  
  1165.         #endregion
  1166.  
  1167.         #region Stupid commands
  1168.  
  1169.         private static void ServerInfo(CommandArgs args)
  1170.         {
  1171.             args.Player.SendInfoMessage("Memory usage: " + Process.GetCurrentProcess().WorkingSet64);
  1172.             args.Player.SendInfoMessage("Allocated memory: " + Process.GetCurrentProcess().VirtualMemorySize64);
  1173.             args.Player.SendInfoMessage("Total processor time: " + Process.GetCurrentProcess().TotalProcessorTime);
  1174.             args.Player.SendInfoMessage("WinVer: " + Environment.OSVersion);
  1175.             args.Player.SendInfoMessage("Proc count: " + Environment.ProcessorCount);
  1176.             args.Player.SendInfoMessage("Machine name: " + Environment.MachineName);
  1177.         }
  1178.  
  1179.         private static void WorldInfo(CommandArgs args)
  1180.         {
  1181.             args.Player.SendInfoMessage("World name: " + (TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName));
  1182.             args.Player.SendInfoMessage("World size: {0}x{1}", Main.maxTilesX, Main.maxTilesY);
  1183.             args.Player.SendInfoMessage("World ID: " + Main.worldID);
  1184.         }
  1185.  
  1186.         #endregion
  1187.  
  1188.         #region Player Management Commands
  1189.  
  1190.         private static void GrabUserUserInfo(CommandArgs args)
  1191.         {
  1192.             if (args.Parameters.Count < 1)
  1193.             {
  1194.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}userinfo <player>", Specifier);
  1195.                 return;
  1196.             }
  1197.  
  1198.             var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
  1199.             if (players.Count < 1)
  1200.                 args.Player.SendErrorMessage("Invalid player.");
  1201.             else if (players.Count > 1)
  1202.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  1203.             else
  1204.             {
  1205.                 var message = new StringBuilder();
  1206.                 message.Append("IP Address: ").Append(players[0].IP);
  1207.                 if (players[0].Account != null && players[0].IsLoggedIn)
  1208.                     message.Append(" | Logged in as: ").Append(players[0].Account.Name).Append(" | Group: ").Append(players[0].Group.Name);
  1209.                 args.Player.SendSuccessMessage(message.ToString());
  1210.             }
  1211.         }
  1212.  
  1213.         private static void ViewAccountInfo(CommandArgs args)
  1214.         {
  1215.             if (args.Parameters.Count < 1)
  1216.             {
  1217.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}accountinfo <username>", Specifier);
  1218.                 return;
  1219.             }
  1220.  
  1221.             string username = String.Join(" ", args.Parameters);
  1222.             if (!string.IsNullOrWhiteSpace(username))
  1223.             {
  1224.                 var account = TShock.UserAccounts.GetUserAccountByName(username);
  1225.                 if (account != null)
  1226.                 {
  1227.                     DateTime LastSeen;
  1228.                     string Timezone = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).Hours.ToString("+#;-#");
  1229.  
  1230.                     if (DateTime.TryParse(account.LastAccessed, out LastSeen))
  1231.                     {
  1232.                         LastSeen = DateTime.Parse(account.LastAccessed).ToLocalTime();
  1233.                         args.Player.SendSuccessMessage("{0}'s last login occured {1} {2} UTC{3}.", account.Name, LastSeen.ToShortDateString(),
  1234.                             LastSeen.ToShortTimeString(), Timezone);
  1235.                     }
  1236.  
  1237.                     if (args.Player.Group.HasPermission(Permissions.advaccountinfo))
  1238.                     {
  1239.                         List<string> KnownIps = JsonConvert.DeserializeObject<List<string>>(account.KnownIps?.ToString() ?? string.Empty);
  1240.                         string ip = KnownIps?[KnownIps.Count - 1] ?? "N/A";
  1241.                         DateTime Registered = DateTime.Parse(account.Registered).ToLocalTime();
  1242.  
  1243.                         args.Player.SendSuccessMessage("{0}'s group is {1}.", account.Name, account.Group);
  1244.                         args.Player.SendSuccessMessage("{0}'s last known IP is {1}.", account.Name, ip);
  1245.                         args.Player.SendSuccessMessage("{0}'s register date is {1} {2} UTC{3}.", account.Name, Registered.ToShortDateString(), Registered.ToShortTimeString(), Timezone);
  1246.                     }
  1247.                 }
  1248.                 else
  1249.                     args.Player.SendErrorMessage("User {0} does not exist.", username);
  1250.             }
  1251.             else args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}accountinfo <username>", Specifier);
  1252.         }
  1253.  
  1254.         private static void Kick(CommandArgs args)
  1255.         {
  1256.             if (args.Parameters.Count < 1)
  1257.             {
  1258.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}kick <player> [reason]", Specifier);
  1259.                 return;
  1260.             }
  1261.             if (args.Parameters[0].Length == 0)
  1262.             {
  1263.                 args.Player.SendErrorMessage("Missing player name.");
  1264.                 return;
  1265.             }
  1266.  
  1267.             string plStr = args.Parameters[0];
  1268.             var players = TSPlayer.FindByNameOrID(plStr);
  1269.             if (players.Count == 0)
  1270.             {
  1271.                 args.Player.SendErrorMessage("Invalid player!");
  1272.             }
  1273.             else if (players.Count > 1)
  1274.             {
  1275.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  1276.             }
  1277.             else
  1278.             {
  1279.                 string reason = args.Parameters.Count > 1
  1280.                                     ? String.Join(" ", args.Parameters.GetRange(1, args.Parameters.Count - 1))
  1281.                                     : "Misbehaviour.";
  1282.                 if (!players[0].Kick(reason, !args.Player.RealPlayer, false, args.Player.Name))
  1283.                 {
  1284.                     args.Player.SendErrorMessage("You can't kick another admin!");
  1285.                 }
  1286.             }
  1287.         }
  1288.  
  1289.         private static void Ban(CommandArgs args)
  1290.         {
  1291.             string subcmd = args.Parameters.Count == 0 ? "help" : args.Parameters[0].ToLower();
  1292.             switch (subcmd)
  1293.             {
  1294.                 case "add":
  1295.                     #region Add Ban
  1296.                     {
  1297.                         if (args.Parameters.Count < 2)
  1298.                         {
  1299.                             args.Player.SendErrorMessage("Invalid command. Format: {0}ban add <player> [time] [reason]", Specifier);
  1300.                             args.Player.SendErrorMessage("Example: {0}ban add Shank 10d Hacking and cheating", Specifier);
  1301.                             args.Player.SendErrorMessage("Example: {0}ban add Ash", Specifier);
  1302.                             args.Player.SendErrorMessage("Use the time 0 (zero) for a permanent ban.");
  1303.                             return;
  1304.                         }
  1305.  
  1306.                         // Used only to notify if a ban was successful and who the ban was about
  1307.                         bool success = false;
  1308.                         string targetGeneralizedName = "";
  1309.  
  1310.                         // Effective ban target assignment
  1311.                         List<TSPlayer> players = TSPlayer.FindByNameOrID(args.Parameters[1]);
  1312.  
  1313.                         // Bad case: Players contains more than 1 person so we can't ban them
  1314.                         if (players.Count > 1)
  1315.                         {
  1316.                             //Fail fast
  1317.                             args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  1318.                             return;
  1319.                         }
  1320.                        
  1321.                         UserAccount offlineUserAccount = TShock.UserAccounts.GetUserAccountByName(args.Parameters[1]);
  1322.  
  1323.                         // Storage variable to determine if the command executor is the server console
  1324.                         // If it is, we assume they have full control and let them override permission checks
  1325.                         bool callerIsServerConsole = args.Player is TSServerPlayer;
  1326.  
  1327.                         // The ban reason the ban is going to have
  1328.                         string banReason = "Unknown.";
  1329.  
  1330.                         // The default ban length
  1331.                         // 0 is permanent ban, otherwise temp ban
  1332.                         int banLengthInSeconds = 0;
  1333.  
  1334.                         // Figure out if param 2 is a time or 0 or garbage
  1335.                         if (args.Parameters.Count >= 3)
  1336.                         {
  1337.                             bool parsedOkay = false;
  1338.                             if (args.Parameters[2] != "0")
  1339.                             {
  1340.                                 parsedOkay = TShock.Utils.TryParseTime(args.Parameters[2], out banLengthInSeconds);
  1341.                             }
  1342.                             else
  1343.                             {
  1344.                                 parsedOkay = true;
  1345.                             }
  1346.  
  1347.                             if (!parsedOkay)
  1348.                             {
  1349.                                 args.Player.SendErrorMessage("Invalid time format. Example: 10d 5h 3m 2s.");
  1350.                                 args.Player.SendErrorMessage("Use 0 (zero) for a permanent ban.");
  1351.                                 return;
  1352.                             }
  1353.                         }
  1354.  
  1355.                         // If a reason exists, use the given reason.
  1356.                         if (args.Parameters.Count > 3)
  1357.                         {
  1358.                             banReason = String.Join(" ", args.Parameters.Skip(3));
  1359.                         }
  1360.  
  1361.                         // Good case: Online ban for matching character.
  1362.                         if (players.Count == 1)
  1363.                         {
  1364.                             TSPlayer target = players[0];
  1365.  
  1366.                             if (target.HasPermission(Permissions.immunetoban) && !callerIsServerConsole)
  1367.                             {
  1368.                                 args.Player.SendErrorMessage("Permission denied. Target {0} is immune to ban.", target.Name);
  1369.                                 return;
  1370.                             }
  1371.  
  1372.                             targetGeneralizedName = target.Name;
  1373.                             success = TShock.Bans.AddBan(target.IP, target.Name, target.UUID, target.Account?.Name ?? "", banReason, false, args.Player.Account.Name,
  1374.                                 banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s"));
  1375.  
  1376.                             // Since this is an online ban, we need to dc the player and tell them now.
  1377.                             if (success)
  1378.                             {
  1379.                                 if (banLengthInSeconds == 0)
  1380.                                 {
  1381.                                     target.Disconnect(String.Format("Permanently banned for {0}", banReason));
  1382.                                 }
  1383.                                 else
  1384.                                 {
  1385.                                     target.Disconnect(String.Format("Banned for {0} seconds for {1}", banLengthInSeconds, banReason));
  1386.                                 }
  1387.                             }
  1388.                         }
  1389.  
  1390.                         // Case: Players & user are invalid, could be IP?
  1391.                         // Note: Order matters. If this method is above the online player check,
  1392.                         // This enables you to ban an IP even if the player exists in the database as a player.
  1393.                         // You'll get two bans for the price of one, in theory, because both IP and user named IP will be banned.
  1394.                         // ??? edge cases are weird, but this is going to happen
  1395.                         // The only way around this is to either segregate off the IP code or do something else.
  1396.                         if (players.Count == 0)
  1397.                         {
  1398.                             // If the target is a valid IP...
  1399.                             string pattern = @"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
  1400.                             Regex r = new Regex(pattern, RegexOptions.IgnoreCase);
  1401.                             if (r.IsMatch(args.Parameters[1]))
  1402.                             {
  1403.                                 targetGeneralizedName = "IP: " + args.Parameters[1];
  1404.                                 success = TShock.Bans.AddBan(args.Parameters[1], "", "", "", banReason,
  1405.                                     false, args.Player.Account.Name, banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s"));
  1406.                                 if (success && offlineUserAccount != null)
  1407.                                 {
  1408.                                     args.Player.SendSuccessMessage("Target IP {0} was banned successfully.", targetGeneralizedName);
  1409.                                     args.Player.SendErrorMessage("Note: An account named with this IP address also exists.");
  1410.                                     args.Player.SendErrorMessage("Note: It will also be banned.");
  1411.                                 }
  1412.                             }
  1413.                             else
  1414.                             {
  1415.                                 // Apparently there is no way to not IP ban someone
  1416.                                 // This means that where we would normally just ban a "character name" here
  1417.                                 // We can't because it requires some IP as a primary key.
  1418.                                 if (offlineUserAccount == null)
  1419.                                 {
  1420.                                     args.Player.SendErrorMessage("Unable to ban target {0}.", args.Parameters[1]);
  1421.                                     args.Player.SendErrorMessage("Target is not a valid IP address, a valid online player, or a known offline user.");
  1422.                                     return;
  1423.                                 }
  1424.                             }
  1425.  
  1426.                         }
  1427.  
  1428.                         // Case: Offline ban
  1429.                         if (players.Count == 0 && offlineUserAccount != null)
  1430.                         {
  1431.                             // Catch: we don't know an offline player's last login character name
  1432.                             // This means that we're banning their *user name* on the assumption that
  1433.                             // user name == character name
  1434.                             // (which may not be true)
  1435.                             // This needs to be fixed in a future implementation.
  1436.                             targetGeneralizedName = offlineUserAccount.Name;
  1437.  
  1438.                             if (TShock.Groups.GetGroupByName(offlineUserAccount.Group).HasPermission(Permissions.immunetoban) &&
  1439.                                 !callerIsServerConsole)
  1440.                             {
  1441.                                 args.Player.SendErrorMessage("Permission denied. Target {0} is immune to ban.", targetGeneralizedName);
  1442.                                 return;
  1443.                             }
  1444.  
  1445.                             if (offlineUserAccount.KnownIps == null)
  1446.                             {
  1447.                                 args.Player.SendErrorMessage("Unable to ban target {0} because they have no valid IP to ban.", targetGeneralizedName);
  1448.                                 return;
  1449.                             }
  1450.  
  1451.                             string lastIP = JsonConvert.DeserializeObject<List<string>>(offlineUserAccount.KnownIps).Last();
  1452.  
  1453.                             success =
  1454.                                 TShock.Bans.AddBan(lastIP,
  1455.                                     "", offlineUserAccount.UUID, offlineUserAccount.Name, banReason, false, args.Player.Account.Name,
  1456.                                     banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s"));
  1457.                         }
  1458.  
  1459.                         if (success)
  1460.                         {
  1461.                             args.Player.SendSuccessMessage("{0} was successfully banned.", targetGeneralizedName);
  1462.                             args.Player.SendInfoMessage("Length: {0}", banLengthInSeconds == 0 ? "Permanent." : banLengthInSeconds + " seconds.");
  1463.                             args.Player.SendInfoMessage("Reason: {0}", banReason);
  1464.                             if (!args.Silent)
  1465.                             {
  1466.                                 if (banLengthInSeconds == 0)
  1467.                                 {
  1468.                                     TSPlayer.All.SendErrorMessage("{0} was permanently banned by {1} for: {2}",
  1469.                                         targetGeneralizedName, args.Player.Account.Name, banReason);
  1470.                                 }
  1471.                                 else
  1472.                                 {
  1473.                                     TSPlayer.All.SendErrorMessage("{0} was temp banned for {1} seconds by {2} for: {3}",
  1474.                                         targetGeneralizedName, banLengthInSeconds, args.Player.Account.Name, banReason);
  1475.                                 }
  1476.                             }
  1477.                         }
  1478.                         else
  1479.                         {
  1480.                             args.Player.SendErrorMessage("{0} was NOT banned due to a database error or other system problem.", targetGeneralizedName);
  1481.                             args.Player.SendErrorMessage("If this player is online, they have NOT been kicked.");
  1482.                             args.Player.SendErrorMessage("Check the system logs for details.");
  1483.                         }
  1484.  
  1485.                         return;
  1486.                     }
  1487.                     #endregion
  1488.                 case "del":
  1489.                     #region Delete ban
  1490.                     {
  1491.                         if (args.Parameters.Count != 2)
  1492.                         {
  1493.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}ban del <player>", Specifier);
  1494.                             return;
  1495.                         }
  1496.  
  1497.                         string plStr = args.Parameters[1];
  1498.                         Ban ban = TShock.Bans.GetBanByName(plStr, false);
  1499.                         if (ban != null)
  1500.                         {
  1501.                             if (TShock.Bans.RemoveBan(ban.Name, true))
  1502.                                 args.Player.SendSuccessMessage("Unbanned {0} ({1}).", ban.Name, ban.IP);
  1503.                             else
  1504.                                 args.Player.SendErrorMessage("Failed to unban {0} ({1}), check logs.", ban.Name, ban.IP);
  1505.                         }
  1506.                         else
  1507.                             args.Player.SendErrorMessage("No bans for {0} exist.", plStr);
  1508.                     }
  1509.                     #endregion
  1510.                     return;
  1511.                 case "delip":
  1512.                     #region Delete IP ban
  1513.                     {
  1514.                         if (args.Parameters.Count != 2)
  1515.                         {
  1516.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}ban delip <ip>", Specifier);
  1517.                             return;
  1518.                         }
  1519.  
  1520.                         string ip = args.Parameters[1];
  1521.                         Ban ban = TShock.Bans.GetBanByIp(ip);
  1522.                         if (ban != null)
  1523.                         {
  1524.                             if (TShock.Bans.RemoveBan(ban.IP, false))
  1525.                                 args.Player.SendSuccessMessage("Unbanned IP {0} ({1}).", ban.IP, ban.Name);
  1526.                             else
  1527.                                 args.Player.SendErrorMessage("Failed to unban IP {0} ({1}), check logs.", ban.IP, ban.Name);
  1528.                         }
  1529.                         else
  1530.                             args.Player.SendErrorMessage("IP {0} is not banned.", ip);
  1531.                     }
  1532.                     #endregion
  1533.                     return;
  1534.                 case "help":
  1535.                     #region Help
  1536.                     {
  1537.                         int pageNumber;
  1538.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  1539.                             return;
  1540.  
  1541.                         var lines = new List<string>
  1542.                         {
  1543.                             "add <target> <time> [reason] - Bans a player or user account if the player is not online.",
  1544.                             "del <player> - Unbans a player.",
  1545.                             "delip <ip> - Unbans an IP.",
  1546.                             "list [page] - Lists all player bans.",
  1547.                             "listip [page] - Lists all IP bans."
  1548.                         };
  1549.  
  1550.                         PaginationTools.SendPage(args.Player, pageNumber, lines,
  1551.                             new PaginationTools.Settings
  1552.                             {
  1553.                                 HeaderFormat = "Ban Sub-Commands ({0}/{1}):",
  1554.                                 FooterFormat = "Type {0}ban help {{0}} for more sub-commands.".SFormat(Specifier)
  1555.                             }
  1556.                         );
  1557.                     }
  1558.                     #endregion
  1559.                     return;
  1560.                 case "list":
  1561.                     #region List bans
  1562.                     {
  1563.                         int pageNumber;
  1564.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  1565.                         {
  1566.                             return;
  1567.                         }
  1568.  
  1569.                         List<Ban> bans = TShock.Bans.GetBans();
  1570.  
  1571.                         var nameBans = from ban in bans
  1572.                                        where !String.IsNullOrEmpty(ban.Name)
  1573.                                        select ban.Name;
  1574.  
  1575.                         PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(nameBans),
  1576.                             new PaginationTools.Settings
  1577.                             {
  1578.                                 HeaderFormat = "Bans ({0}/{1}):",
  1579.                                 FooterFormat = "Type {0}ban list {{0}} for more.".SFormat(Specifier),
  1580.                                 NothingToDisplayString = "There are currently no bans."
  1581.                             });
  1582.                     }
  1583.                     #endregion
  1584.                     return;
  1585.                 case "listip":
  1586.                     #region List IP bans
  1587.                     {
  1588.                         int pageNumber;
  1589.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  1590.                         {
  1591.                             return;
  1592.                         }
  1593.  
  1594.                         List<Ban> bans = TShock.Bans.GetBans();
  1595.  
  1596.                         var ipBans = from ban in bans
  1597.                                      where String.IsNullOrEmpty(ban.Name)
  1598.                                      select ban.IP;
  1599.  
  1600.                         PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(ipBans),
  1601.                             new PaginationTools.Settings
  1602.                             {
  1603.                                 HeaderFormat = "IP Bans ({0}/{1}):",
  1604.                                 FooterFormat = "Type {0}ban listip {{0}} for more.".SFormat(Specifier),
  1605.                                 NothingToDisplayString = "There are currently no IP bans."
  1606.                             });
  1607.                     }
  1608.                     #endregion
  1609.                     return;
  1610.                 default:
  1611.                     args.Player.SendErrorMessage("Invalid subcommand! Type {0}ban help for more information.", Specifier);
  1612.                     return;
  1613.             }
  1614.         }
  1615.  
  1616.         private static void Whitelist(CommandArgs args)
  1617.         {
  1618.             if (args.Parameters.Count == 1)
  1619.             {
  1620.                 using (var tw = new StreamWriter(FileTools.WhitelistPath, true))
  1621.                 {
  1622.                     tw.WriteLine(args.Parameters[0]);
  1623.                 }
  1624.                 args.Player.SendSuccessMessage("Added " + args.Parameters[0] + " to the whitelist.");
  1625.             }
  1626.         }
  1627.  
  1628.         private static void DisplayLogs(CommandArgs args)
  1629.         {
  1630.             args.Player.DisplayLogs = (!args.Player.DisplayLogs);
  1631.             args.Player.SendSuccessMessage("You will " + (args.Player.DisplayLogs ? "now" : "no longer") + " receive logs.");
  1632.         }
  1633.  
  1634.         private static void SaveSSC(CommandArgs args)
  1635.         {
  1636.             if (Main.ServerSideCharacter)
  1637.             {
  1638.                 args.Player.SendSuccessMessage("SSC has been saved.");
  1639.                 foreach (TSPlayer player in TShock.Players)
  1640.                 {
  1641.                     if (player != null && player.IsLoggedIn && !player.IsDisabledPendingTrashRemoval)
  1642.                     {
  1643.                         TShock.CharacterDB.InsertPlayerData(player, true);
  1644.                     }
  1645.                 }
  1646.             }
  1647.         }
  1648.  
  1649.         private static void OverrideSSC(CommandArgs args)
  1650.         {
  1651.             if (!Main.ServerSideCharacter)
  1652.             {
  1653.                 args.Player.SendErrorMessage("Server Side Characters is disabled.");
  1654.                 return;
  1655.             }
  1656.             if (args.Parameters.Count < 1)
  1657.             {
  1658.                 args.Player.SendErrorMessage("Correct usage: {0}overridessc|{0}ossc <player name>", Specifier);
  1659.                 return;
  1660.             }
  1661.  
  1662.             string playerNameToMatch = string.Join(" ", args.Parameters);
  1663.             var matchedPlayers = TSPlayer.FindByNameOrID(playerNameToMatch);
  1664.             if (matchedPlayers.Count < 1)
  1665.             {
  1666.                 args.Player.SendErrorMessage("No players matched \"{0}\".", playerNameToMatch);
  1667.                 return;
  1668.             }
  1669.             else if (matchedPlayers.Count > 1)
  1670.             {
  1671.                 args.Player.SendMultipleMatchError(matchedPlayers.Select(p => p.Name));
  1672.                 return;
  1673.             }
  1674.  
  1675.             TSPlayer matchedPlayer = matchedPlayers[0];
  1676.             if (matchedPlayer.IsLoggedIn)
  1677.             {
  1678.                 args.Player.SendErrorMessage("Player \"{0}\" is already logged in.", matchedPlayer.Name);
  1679.                 return;
  1680.             }
  1681.             if (!matchedPlayer.LoginFailsBySsi)
  1682.             {
  1683.                 args.Player.SendErrorMessage("Player \"{0}\" has to perform a /login attempt first.", matchedPlayer.Name);
  1684.                 return;
  1685.             }
  1686.             if (matchedPlayer.IsDisabledPendingTrashRemoval)
  1687.             {
  1688.                 args.Player.SendErrorMessage("Player \"{0}\" has to reconnect first.", matchedPlayer.Name);
  1689.                 return;
  1690.             }
  1691.  
  1692.             TShock.CharacterDB.InsertPlayerData(matchedPlayer);
  1693.             args.Player.SendSuccessMessage("SSC of player \"{0}\" has been overriden.", matchedPlayer.Name);
  1694.         }
  1695.  
  1696.         private static void UploadJoinData(CommandArgs args)
  1697.         {
  1698.             TSPlayer targetPlayer = args.Player;
  1699.             if (args.Parameters.Count == 1 && args.Player.HasPermission(Permissions.uploadothersdata))
  1700.             {
  1701.                 List<TSPlayer> players = TSPlayer.FindByNameOrID(args.Parameters[0]);
  1702.                 if (players.Count > 1)
  1703.                 {
  1704.                     args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  1705.                     return;
  1706.                 }
  1707.                 else if (players.Count == 0)
  1708.                 {
  1709.                     args.Player.SendErrorMessage("No player was found matching'{0}'", args.Parameters[0]);
  1710.                     return;
  1711.                 }
  1712.                 else
  1713.                 {
  1714.                     targetPlayer = players[0];
  1715.                 }
  1716.             }
  1717.             else if (args.Parameters.Count == 1)
  1718.             {
  1719.                 args.Player.SendErrorMessage("You do not have permission to upload another player's character data.");
  1720.                 return;
  1721.             }
  1722.             else if (args.Parameters.Count > 0)
  1723.             {
  1724.                 args.Player.SendErrorMessage("Usage: /uploadssc [playername]");
  1725.                 return;
  1726.             }
  1727.             else if (args.Parameters.Count == 0 && args.Player is TSServerPlayer)
  1728.             {
  1729.                 args.Player.SendErrorMessage("A console can not upload their player data.");
  1730.                 args.Player.SendErrorMessage("Usage: /uploadssc [playername]");
  1731.                 return;
  1732.             }
  1733.  
  1734.             if (targetPlayer.IsLoggedIn)
  1735.             {
  1736.                 if (TShock.CharacterDB.InsertSpecificPlayerData(targetPlayer, targetPlayer.DataWhenJoined))
  1737.                 {
  1738.                     targetPlayer.DataWhenJoined.RestoreCharacter(targetPlayer);
  1739.                     targetPlayer.SendSuccessMessage("Your local character data has been uploaded to the server.");
  1740.                     args.Player.SendSuccessMessage("The player's character data was successfully uploaded.");
  1741.                 }
  1742.                 else
  1743.                 {
  1744.                     args.Player.SendErrorMessage("Failed to upload your character data, are you logged in to an account?");
  1745.                 }
  1746.             }
  1747.             else
  1748.             {
  1749.                 args.Player.SendErrorMessage("The target player has not logged in yet.");
  1750.             }
  1751.         }
  1752.  
  1753.         private static void ForceHalloween(CommandArgs args)
  1754.         {
  1755.             TShock.Config.ForceHalloween = !TShock.Config.ForceHalloween;
  1756.             Main.checkHalloween();
  1757.             if (args.Silent)
  1758.                 args.Player.SendInfoMessage("{0}abled halloween mode!", (TShock.Config.ForceHalloween ? "en" : "dis"));
  1759.             else
  1760.                 TSPlayer.All.SendInfoMessage("{0} {1}abled halloween mode!", args.Player.Name, (TShock.Config.ForceHalloween ? "en" : "dis"));
  1761.         }
  1762.  
  1763.         private static void ForceXmas(CommandArgs args)
  1764.         {
  1765.             TShock.Config.ForceXmas = !TShock.Config.ForceXmas;
  1766.             Main.checkXMas();
  1767.             if (args.Silent)
  1768.                 args.Player.SendInfoMessage("{0}abled Christmas mode!", (TShock.Config.ForceXmas ? "en" : "dis"));
  1769.             else
  1770.                 TSPlayer.All.SendInfoMessage("{0} {1}abled Christmas mode!", args.Player.Name, (TShock.Config.ForceXmas ? "en" : "dis"));
  1771.         }
  1772.  
  1773.         private static void TempGroup(CommandArgs args)
  1774.         {
  1775.             if (args.Parameters.Count < 2)
  1776.             {
  1777.                 args.Player.SendInfoMessage("Invalid usage");
  1778.                 args.Player.SendInfoMessage("Usage: {0}tempgroup <username> <new group> [time]", Specifier);
  1779.                 return;
  1780.             }
  1781.  
  1782.             List<TSPlayer> ply = TSPlayer.FindByNameOrID(args.Parameters[0]);
  1783.             if (ply.Count < 1)
  1784.             {
  1785.                 args.Player.SendErrorMessage("Could not find player {0}.", args.Parameters[0]);
  1786.                 return;
  1787.             }
  1788.  
  1789.             if (ply.Count > 1)
  1790.             {
  1791.                 args.Player.SendMultipleMatchError(ply.Select(p => p.Account.Name));
  1792.             }
  1793.  
  1794.             if (!TShock.Groups.GroupExists(args.Parameters[1]))
  1795.             {
  1796.                 args.Player.SendErrorMessage("Could not find group {0}", args.Parameters[1]);
  1797.                 return;
  1798.             }
  1799.  
  1800.             if (args.Parameters.Count > 2)
  1801.             {
  1802.                 int time;
  1803.                 if (!TShock.Utils.TryParseTime(args.Parameters[2], out time))
  1804.                 {
  1805.                     args.Player.SendErrorMessage("Invalid time string! Proper format: _d_h_m_s, with at least one time specifier.");
  1806.                     args.Player.SendErrorMessage("For example, 1d and 10h-30m+2m are both valid time strings, but 2 is not.");
  1807.                     return;
  1808.                 }
  1809.  
  1810.                 ply[0].tempGroupTimer = new System.Timers.Timer(time * 1000);
  1811.                 ply[0].tempGroupTimer.Elapsed += ply[0].TempGroupTimerElapsed;
  1812.                 ply[0].tempGroupTimer.Start();
  1813.             }
  1814.  
  1815.             Group g = TShock.Groups.GetGroupByName(args.Parameters[1]);
  1816.  
  1817.             ply[0].tempGroup = g;
  1818.  
  1819.             if (args.Parameters.Count < 3)
  1820.             {
  1821.                 args.Player.SendSuccessMessage(String.Format("You have changed {0}'s group to {1}", ply[0].Name, g.Name));
  1822.                 ply[0].SendSuccessMessage(String.Format("Your group has temporarily been changed to {0}", g.Name));
  1823.             }
  1824.             else
  1825.             {
  1826.                 args.Player.SendSuccessMessage(String.Format("You have changed {0}'s group to {1} for {2}",
  1827.                     ply[0].Name, g.Name, args.Parameters[2]));
  1828.                 ply[0].SendSuccessMessage(String.Format("Your group has been changed to {0} for {1}",
  1829.                     g.Name, args.Parameters[2]));
  1830.             }
  1831.         }
  1832.  
  1833.         private static void SubstituteUser(CommandArgs args)
  1834.         {
  1835.  
  1836.             if (args.Player.tempGroup != null)
  1837.             {
  1838.                 args.Player.tempGroup = null;
  1839.                 args.Player.tempGroupTimer.Stop();
  1840.                 args.Player.SendSuccessMessage("Your previous permission set has been restored.");
  1841.                 return;
  1842.             }
  1843.             else
  1844.             {
  1845.                 args.Player.tempGroup = new SuperAdminGroup();
  1846.                 args.Player.tempGroupTimer = new System.Timers.Timer(600 * 1000);
  1847.                 args.Player.tempGroupTimer.Elapsed += args.Player.TempGroupTimerElapsed;
  1848.                 args.Player.tempGroupTimer.Start();
  1849.                 args.Player.SendSuccessMessage("Your account has been elevated to Super Admin for 10 minutes.");
  1850.                 return;
  1851.             }
  1852.         }
  1853.  
  1854.         #endregion Player Management Commands
  1855.  
  1856.         #region Server Maintenence Commands
  1857.  
  1858.         // Executes a command as a superuser if you have sudo rights.
  1859.         private static void SubstituteUserDo(CommandArgs args)
  1860.         {
  1861.             if (args.Parameters.Count == 0)
  1862.             {
  1863.                 args.Player.SendErrorMessage("Usage: /sudo [command].");
  1864.                 args.Player.SendErrorMessage("Example: /sudo /ban add Shank 2d Hacking.");
  1865.                 return;
  1866.             }
  1867.  
  1868.             string replacementCommand = String.Join(" ", args.Parameters);
  1869.             args.Player.tempGroup = new SuperAdminGroup();
  1870.             HandleCommand(args.Player, replacementCommand);
  1871.             args.Player.tempGroup = null;
  1872.             return;
  1873.         }
  1874.  
  1875.         private static void Broadcast(CommandArgs args)
  1876.         {
  1877.             string message = string.Join(" ", args.Parameters);
  1878.  
  1879.             TShock.Utils.Broadcast(
  1880.                 "(Server Broadcast) " + message,
  1881.                 Convert.ToByte(TShock.Config.BroadcastRGB[0]), Convert.ToByte(TShock.Config.BroadcastRGB[1]),
  1882.                 Convert.ToByte(TShock.Config.BroadcastRGB[2]));
  1883.         }
  1884.  
  1885.         private static void Off(CommandArgs args)
  1886.         {
  1887.  
  1888.             if (Main.ServerSideCharacter)
  1889.             {
  1890.                 foreach (TSPlayer player in TShock.Players)
  1891.                 {
  1892.                     if (player != null && player.IsLoggedIn && !player.IsDisabledPendingTrashRemoval)
  1893.                     {
  1894.                         player.SaveServerCharacter();
  1895.                     }
  1896.                 }
  1897.             }
  1898.  
  1899.             string reason = ((args.Parameters.Count > 0) ? "Server shutting down: " + String.Join(" ", args.Parameters) : "Server shutting down!");
  1900.             TShock.Utils.StopServer(true, reason);
  1901.         }
  1902.  
  1903.         private static void OffNoSave(CommandArgs args)
  1904.         {
  1905.             string reason = ((args.Parameters.Count > 0) ? "Server shutting down: " + String.Join(" ", args.Parameters) : "Server shutting down!");
  1906.             TShock.Utils.StopServer(false, reason);
  1907.         }
  1908.  
  1909.         private static void CheckUpdates(CommandArgs args)
  1910.         {
  1911.             args.Player.SendInfoMessage("An update check has been queued.");
  1912.             try
  1913.             {
  1914.                 TShock.UpdateManager.UpdateCheckAsync(null);
  1915.             }
  1916.             catch (Exception)
  1917.             {
  1918.                 //swallow the exception
  1919.                 return;
  1920.             }
  1921.         }
  1922.  
  1923.         private static void ManageRest(CommandArgs args)
  1924.         {
  1925.             string subCommand = "help";
  1926.             if (args.Parameters.Count > 0)
  1927.                 subCommand = args.Parameters[0];
  1928.  
  1929.             switch (subCommand.ToLower())
  1930.             {
  1931.                 case "listusers":
  1932.                     {
  1933.                         int pageNumber;
  1934.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  1935.                             return;
  1936.  
  1937.                         Dictionary<string, int> restUsersTokens = new Dictionary<string, int>();
  1938.                         foreach (Rests.SecureRest.TokenData tokenData in TShock.RestApi.Tokens.Values)
  1939.                         {
  1940.                             if (restUsersTokens.ContainsKey(tokenData.Username))
  1941.                                 restUsersTokens[tokenData.Username]++;
  1942.                             else
  1943.                                 restUsersTokens.Add(tokenData.Username, 1);
  1944.                         }
  1945.  
  1946.                         List<string> restUsers = new List<string>(
  1947.                             restUsersTokens.Select(ut => string.Format("{0} ({1} tokens)", ut.Key, ut.Value)));
  1948.  
  1949.                         PaginationTools.SendPage(
  1950.                             args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(restUsers), new PaginationTools.Settings
  1951.                             {
  1952.                                 NothingToDisplayString = "There are currently no active REST users.",
  1953.                                 HeaderFormat = "Active REST Users ({0}/{1}):",
  1954.                                 FooterFormat = "Type {0}rest listusers {{0}} for more.".SFormat(Specifier)
  1955.                             }
  1956.                         );
  1957.  
  1958.                         break;
  1959.                     }
  1960.                 case "destroytokens":
  1961.                     {
  1962.                         TShock.RestApi.Tokens.Clear();
  1963.                         args.Player.SendSuccessMessage("All REST tokens have been destroyed.");
  1964.                         break;
  1965.                     }
  1966.                 default:
  1967.                     {
  1968.                         args.Player.SendInfoMessage("Available REST Sub-Commands:");
  1969.                         args.Player.SendMessage("listusers - Lists all REST users and their current active tokens.", Color.White);
  1970.                         args.Player.SendMessage("destroytokens - Destroys all current REST tokens.", Color.White);
  1971.                         break;
  1972.                     }
  1973.             }
  1974.         }
  1975.  
  1976.         #endregion Server Maintenence Commands
  1977.  
  1978.         #region Cause Events and Spawn Monsters Commands
  1979.  
  1980.         private static void DropMeteor(CommandArgs args)
  1981.         {
  1982.             WorldGen.spawnMeteor = false;
  1983.             WorldGen.dropMeteor();
  1984.             if (args.Silent)
  1985.             {
  1986.                 args.Player.SendInfoMessage("A meteor has been triggered.");
  1987.             }
  1988.             else
  1989.             {
  1990.                 TSPlayer.All.SendInfoMessage("{0} triggered a meteor.", args.Player.Name);
  1991.             }
  1992.         }
  1993.  
  1994.         private static void Fullmoon(CommandArgs args)
  1995.         {
  1996.             TSPlayer.Server.SetFullMoon();
  1997.             if (args.Silent)
  1998.             {
  1999.                 args.Player.SendInfoMessage("Started a full moon.");
  2000.             }
  2001.             else
  2002.             {
  2003.                 TSPlayer.All.SendInfoMessage("{0} started a full moon.", args.Player.Name);
  2004.             }
  2005.         }
  2006.  
  2007.         private static void Bloodmoon(CommandArgs args)
  2008.         {
  2009.             TSPlayer.Server.SetBloodMoon(!Main.bloodMoon);
  2010.             if (args.Silent)
  2011.             {
  2012.                 args.Player.SendInfoMessage("{0}ed a blood moon.", Main.bloodMoon ? "start" : "stopp");
  2013.             }
  2014.             else
  2015.             {
  2016.                 TSPlayer.All.SendInfoMessage("{0} {1}ed a blood moon.", args.Player.Name, Main.bloodMoon ? "start" : "stopp");
  2017.             }
  2018.         }
  2019.  
  2020.         private static void Eclipse(CommandArgs args)
  2021.         {
  2022.             TSPlayer.Server.SetEclipse(!Main.eclipse);
  2023.             if (args.Silent)
  2024.             {
  2025.                 args.Player.SendInfoMessage("{0}ed an eclipse.", Main.eclipse ? "start" : "stopp");
  2026.             }
  2027.             else
  2028.             {
  2029.                 TSPlayer.All.SendInfoMessage("{0} {1}ed an eclipse.", args.Player.Name, Main.eclipse ? "start" : "stopp");
  2030.             }
  2031.         }
  2032.  
  2033.         private static void Invade(CommandArgs args)
  2034.         {
  2035.             if (Main.invasionSize <= 0)
  2036.             {
  2037.                 if (args.Parameters.Count < 1)
  2038.                 {
  2039.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}invade <invasion type> [wave]", Specifier);
  2040.                     return;
  2041.                 }
  2042.  
  2043.                 int wave = 1;
  2044.                 switch (args.Parameters[0].ToLower())
  2045.                 {
  2046.                     case "goblin":
  2047.                     case "goblins":
  2048.                         TSPlayer.All.SendInfoMessage("{0} has started a goblin army invasion.", args.Player.Name);
  2049.                         TShock.Utils.StartInvasion(1);
  2050.                         break;
  2051.  
  2052.                     case "snowman":
  2053.                     case "snowmen":
  2054.                         TSPlayer.All.SendInfoMessage("{0} has started a snow legion invasion.", args.Player.Name);
  2055.                         TShock.Utils.StartInvasion(2);
  2056.                         break;
  2057.  
  2058.                     case "pirate":
  2059.                     case "pirates":
  2060.                         TSPlayer.All.SendInfoMessage("{0} has started a pirate invasion.", args.Player.Name);
  2061.                         TShock.Utils.StartInvasion(3);
  2062.                         break;
  2063.  
  2064.                     case "pumpkin":
  2065.                     case "pumpkinmoon":
  2066.                         if (args.Parameters.Count > 1)
  2067.                         {
  2068.                             if (!int.TryParse(args.Parameters[1], out wave) || wave <= 0)
  2069.                             {
  2070.                                 args.Player.SendErrorMessage("Invalid wave!");
  2071.                                 break;
  2072.                             }
  2073.                         }
  2074.  
  2075.                         TSPlayer.Server.SetPumpkinMoon(true);
  2076.                         Main.bloodMoon = false;
  2077.                         NPC.waveKills = 0f;
  2078.                         NPC.waveNumber = wave;
  2079.                         TSPlayer.All.SendInfoMessage("{0} started the pumpkin moon at wave {1}!", args.Player.Name, wave);
  2080.                         break;
  2081.  
  2082.                     case "frost":
  2083.                     case "frostmoon":
  2084.                         if (args.Parameters.Count > 1)
  2085.                         {
  2086.                             if (!int.TryParse(args.Parameters[1], out wave) || wave <= 0)
  2087.                             {
  2088.                                 args.Player.SendErrorMessage("Invalid wave!");
  2089.                                 return;
  2090.                             }
  2091.                         }
  2092.  
  2093.                         TSPlayer.Server.SetFrostMoon(true);
  2094.                         Main.bloodMoon = false;
  2095.                         NPC.waveKills = 0f;
  2096.                         NPC.waveNumber = wave;
  2097.                         TSPlayer.All.SendInfoMessage("{0} started the frost moon at wave {1}!", args.Player.Name, wave);
  2098.                         break;
  2099.  
  2100.                     case "martian":
  2101.                     case "martians":
  2102.                         TSPlayer.All.SendInfoMessage("{0} has started a martian invasion.", args.Player.Name);
  2103.                         TShock.Utils.StartInvasion(4);
  2104.                         break;
  2105.                 }
  2106.             }
  2107.             else if (DD2Event.Ongoing)
  2108.             {
  2109.                 DD2Event.StopInvasion();
  2110.                 TSPlayer.All.SendInfoMessage("{0} has ended the Old One's Army event.", args.Player.Name);
  2111.             }
  2112.             else
  2113.             {
  2114.                 TSPlayer.All.SendInfoMessage("{0} has ended the invasion.", args.Player.Name);
  2115.                 Main.invasionSize = 0;
  2116.             }
  2117.         }
  2118.  
  2119.         private static void ClearAnglerQuests(CommandArgs args)
  2120.         {
  2121.             if (args.Parameters.Count > 0)
  2122.             {
  2123.                 var result = Main.anglerWhoFinishedToday.RemoveAll(s => s.ToLower().Equals(args.Parameters[0].ToLower()));
  2124.                 if (result > 0)
  2125.                 {
  2126.                     args.Player.SendSuccessMessage("Removed {0} players from the angler quest completion list for today.", result);
  2127.                     foreach (TSPlayer ply in TShock.Players.Where(p => p != null && p.Active && p.TPlayer.name.ToLower().Equals(args.Parameters[0].ToLower())))
  2128.                     {
  2129.                         //this will always tell the client that they have not done the quest today.
  2130.                         ply.SendData((PacketTypes)74, "");
  2131.                     }
  2132.                 }
  2133.                 else
  2134.                     args.Player.SendErrorMessage("Failed to find any users by that name on the list.");
  2135.  
  2136.             }
  2137.             else
  2138.             {
  2139.                 Main.anglerWhoFinishedToday.Clear();
  2140.                 NetMessage.SendAnglerQuest(-1);
  2141.                 args.Player.SendSuccessMessage("Cleared all users from the angler quest completion list for today.");
  2142.             }
  2143.         }
  2144.  
  2145.         private static void ToggleExpert(CommandArgs args)
  2146.         {
  2147.             Main.expertMode = !Main.expertMode;
  2148.             TSPlayer.All.SendData(PacketTypes.WorldInfo);
  2149.             args.Player.SendSuccessMessage("Expert mode is now {0}.", Main.expertMode ? "on" : "off");
  2150.         }
  2151.  
  2152.         private static void Hardmode(CommandArgs args)
  2153.         {
  2154.             if (Main.hardMode)
  2155.             {
  2156.                 Main.hardMode = false;
  2157.                 TSPlayer.All.SendData(PacketTypes.WorldInfo);
  2158.                 args.Player.SendSuccessMessage("Hardmode is now off.");
  2159.             }
  2160.             else if (!TShock.Config.DisableHardmode)
  2161.             {
  2162.                 WorldGen.StartHardmode();
  2163.                 args.Player.SendSuccessMessage("Hardmode is now on.");
  2164.             }
  2165.             else
  2166.             {
  2167.                 args.Player.SendErrorMessage("Hardmode is disabled via config.");
  2168.             }
  2169.         }
  2170.  
  2171.         private static void SpawnBoss(CommandArgs args)
  2172.         {
  2173.             if (args.Parameters.Count < 1 || args.Parameters.Count > 2)
  2174.             {
  2175.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}spawnboss <boss type> [amount]", Specifier);
  2176.                 return;
  2177.             }
  2178.  
  2179.             int amount = 1;
  2180.             if (args.Parameters.Count == 2 && (!int.TryParse(args.Parameters[1], out amount) || amount <= 0))
  2181.             {
  2182.                 args.Player.SendErrorMessage("Invalid boss amount!");
  2183.                 return;
  2184.             }
  2185.  
  2186.             NPC npc = new NPC();
  2187.             switch (args.Parameters[0].ToLower())
  2188.             {
  2189.                 case "*":
  2190.                 case "all":
  2191.                     int[] npcIds = { 4, 13, 35, 50, 125, 126, 127, 134, 222, 245, 262, 266, 370, 398 };
  2192.                     TSPlayer.Server.SetTime(false, 0.0);
  2193.                     foreach (int i in npcIds)
  2194.                     {
  2195.                         npc.SetDefaults(i);
  2196.                         TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2197.                     }
  2198.                     TSPlayer.All.SendSuccessMessage("{0} has spawned all bosses {1} time(s).", args.Player.Name, amount);
  2199.                     return;
  2200.                 case "brain":
  2201.                 case "brain of cthulhu":
  2202.                     npc.SetDefaults(266);
  2203.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2204.                     TSPlayer.All.SendSuccessMessage("{0} has spawned the Brain of Cthulhu {1} time(s).", args.Player.Name, amount);
  2205.                     return;
  2206.                 case "destroyer":
  2207.                     npc.SetDefaults(134);
  2208.                     TSPlayer.Server.SetTime(false, 0.0);
  2209.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2210.                     TSPlayer.All.SendSuccessMessage("{0} has spawned the Destroyer {1} time(s).", args.Player.Name, amount);
  2211.                     return;
  2212.                 case "duke":
  2213.                 case "duke fishron":
  2214.                 case "fishron":
  2215.                     npc.SetDefaults(370);
  2216.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2217.                     TSPlayer.All.SendSuccessMessage("{0} has spawned Duke Fishron {1} time(s).", args.Player.Name, amount);
  2218.                     return;
  2219.                 case "eater":
  2220.                 case "eater of worlds":
  2221.                     npc.SetDefaults(13);
  2222.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2223.                     TSPlayer.All.SendSuccessMessage("{0} has spawned the Eater of Worlds {1} time(s).", args.Player.Name, amount);
  2224.                     return;
  2225.                 case "eye":
  2226.                 case "eye of cthulhu":
  2227.                     npc.SetDefaults(4);
  2228.                     TSPlayer.Server.SetTime(false, 0.0);
  2229.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2230.                     TSPlayer.All.SendSuccessMessage("{0} has spawned the Eye of Cthulhu {1} time(s).", args.Player.Name, amount);
  2231.                     return;
  2232.                 case "golem":
  2233.                     npc.SetDefaults(245);
  2234.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2235.                     TSPlayer.All.SendSuccessMessage("{0} has spawned Golem {1} time(s).", args.Player.Name, amount);
  2236.                     return;
  2237.                 case "king":
  2238.                 case "king slime":
  2239.                     npc.SetDefaults(50);
  2240.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2241.                     TSPlayer.All.SendSuccessMessage("{0} has spawned King Slime {1} time(s).", args.Player.Name, amount);
  2242.                     return;
  2243.                 case "plantera":
  2244.                     npc.SetDefaults(262);
  2245.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2246.                     TSPlayer.All.SendSuccessMessage("{0} has spawned Plantera {1} time(s).", args.Player.Name, amount);
  2247.                     return;
  2248.                 case "prime":
  2249.                 case "skeletron prime":
  2250.                     npc.SetDefaults(127);
  2251.                     TSPlayer.Server.SetTime(false, 0.0);
  2252.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2253.                     TSPlayer.All.SendSuccessMessage("{0} has spawned Skeletron Prime {1} time(s).", args.Player.Name, amount);
  2254.                     return;
  2255.                 case "queen":
  2256.                 case "queen bee":
  2257.                     npc.SetDefaults(222);
  2258.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2259.                     TSPlayer.All.SendSuccessMessage("{0} has spawned Queen Bee {1} time(s).", args.Player.Name, amount);
  2260.                     return;
  2261.                 case "skeletron":
  2262.                     npc.SetDefaults(35);
  2263.                     TSPlayer.Server.SetTime(false, 0.0);
  2264.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2265.                     TSPlayer.All.SendSuccessMessage("{0} has spawned Skeletron {1} time(s).", args.Player.Name, amount);
  2266.                     return;
  2267.                 case "twins":
  2268.                     TSPlayer.Server.SetTime(false, 0.0);
  2269.                     npc.SetDefaults(125);
  2270.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2271.                     npc.SetDefaults(126);
  2272.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2273.                     TSPlayer.All.SendSuccessMessage("{0} has spawned the Twins {1} time(s).", args.Player.Name, amount);
  2274.                     return;
  2275.                 case "wof":
  2276.                 case "wall of flesh":
  2277.                     if (Main.wof >= 0)
  2278.                     {
  2279.                         args.Player.SendErrorMessage("There is already a Wall of Flesh!");
  2280.                         return;
  2281.                     }
  2282.                     if (args.Player.Y / 16f < Main.maxTilesY - 205)
  2283.                     {
  2284.                         args.Player.SendErrorMessage("You must spawn the Wall of Flesh in hell!");
  2285.                         return;
  2286.                     }
  2287.                     NPC.SpawnWOF(new Vector2(args.Player.X, args.Player.Y));
  2288.                     TSPlayer.All.SendSuccessMessage("{0} has spawned the Wall of Flesh.", args.Player.Name);
  2289.                     return;
  2290.                 case "moon":
  2291.                 case "moon lord":
  2292.                     npc.SetDefaults(398);
  2293.                     TSPlayer.Server.SpawnNPC(npc.type, npc.FullName, amount, args.Player.TileX, args.Player.TileY);
  2294.                     TSPlayer.All.SendSuccessMessage("{0} has spawned the Moon Lord {1} time(s).", args.Player.Name, amount);
  2295.                     return;
  2296.                 default:
  2297.                     args.Player.SendErrorMessage("Invalid boss type!");
  2298.                     return;
  2299.             }
  2300.         }
  2301.  
  2302.         private static void SpawnMob(CommandArgs args)
  2303.         {
  2304.             if (args.Parameters.Count < 1 || args.Parameters.Count > 2)
  2305.             {
  2306.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}spawnmob <mob type> [amount]", Specifier);
  2307.                 return;
  2308.             }
  2309.             if (args.Parameters[0].Length == 0)
  2310.             {
  2311.                 args.Player.SendErrorMessage("Invalid mob type!");
  2312.                 return;
  2313.             }
  2314.  
  2315.             int amount = 1;
  2316.             if (args.Parameters.Count == 2 && !int.TryParse(args.Parameters[1], out amount))
  2317.             {
  2318.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}spawnmob <mob type> [amount]", Specifier);
  2319.                 return;
  2320.             }
  2321.  
  2322.             amount = Math.Min(amount, Main.maxNPCs);
  2323.  
  2324.             var npcs = TShock.Utils.GetNPCByIdOrName(args.Parameters[0]);
  2325.             if (npcs.Count == 0)
  2326.             {
  2327.                 args.Player.SendErrorMessage("Invalid mob type!");
  2328.             }
  2329.             else if (npcs.Count > 1)
  2330.             {
  2331.                 args.Player.SendMultipleMatchError(npcs.Select(n => $"{n.FullName}({n.type})"));
  2332.             }
  2333.             else
  2334.             {
  2335.                 var npc = npcs[0];
  2336.                 if (npc.type >= 1 && npc.type < Main.maxNPCTypes && npc.type != 113)
  2337.                 {
  2338.                     TSPlayer.Server.SpawnNPC(npc.netID, npc.FullName, amount, args.Player.TileX, args.Player.TileY, 50, 20);
  2339.                     if (args.Silent)
  2340.                     {
  2341.                         args.Player.SendSuccessMessage("Spawned {0} {1} time(s).", npc.FullName, amount);
  2342.                     }
  2343.                     else
  2344.                     {
  2345.                         TSPlayer.All.SendSuccessMessage("{0} has spawned {1} {2} time(s).", args.Player.Name, npc.FullName, amount);
  2346.                     }
  2347.                 }
  2348.                 else if (npc.type == 113)
  2349.                 {
  2350.                     if (Main.wof >= 0 || (args.Player.Y / 16f < (Main.maxTilesY - 205)))
  2351.                     {
  2352.                         args.Player.SendErrorMessage("Can't spawn Wall of Flesh!");
  2353.                         return;
  2354.                     }
  2355.                     NPC.SpawnWOF(new Vector2(args.Player.X, args.Player.Y));
  2356.                     if (args.Silent)
  2357.                     {
  2358.                         args.Player.SendSuccessMessage("Spawned Wall of Flesh!");
  2359.                     }
  2360.                     else
  2361.                     {
  2362.                         TSPlayer.All.SendSuccessMessage("{0} has spawned a Wall of Flesh!", args.Player.Name);
  2363.                     }
  2364.                 }
  2365.                 else
  2366.                 {
  2367.                     args.Player.SendErrorMessage("Invalid mob type!");
  2368.                 }
  2369.             }
  2370.         }
  2371.  
  2372.         #endregion Cause Events and Spawn Monsters Commands
  2373.  
  2374.         #region Teleport Commands
  2375.  
  2376.         private static void Home(CommandArgs args)
  2377.         {
  2378.             args.Player.Spawn();
  2379.             args.Player.SendSuccessMessage("Teleported to your spawnpoint.");
  2380.         }
  2381.  
  2382.         private static void Spawn(CommandArgs args)
  2383.         {
  2384.             if (args.Player.Teleport(Main.spawnTileX * 16, (Main.spawnTileY * 16) - 48))
  2385.                 args.Player.SendSuccessMessage("Teleported to the map's spawnpoint.");
  2386.         }
  2387.  
  2388.         private static void TP(CommandArgs args)
  2389.         {
  2390.             if (args.Parameters.Count != 1 && args.Parameters.Count != 2)
  2391.             {
  2392.                 if (args.Player.HasPermission(Permissions.tpothers))
  2393.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tp <player> [player 2]", Specifier);
  2394.                 else
  2395.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tp <player>", Specifier);
  2396.                 return;
  2397.             }
  2398.  
  2399.             if (args.Parameters.Count == 1)
  2400.             {
  2401.                 var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
  2402.                 if (players.Count == 0)
  2403.                     args.Player.SendErrorMessage("Invalid player!");
  2404.                 else if (players.Count > 1)
  2405.                     args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  2406.                 else
  2407.                 {
  2408.                     var target = players[0];
  2409.                     if (!target.TPAllow && !args.Player.HasPermission(Permissions.tpoverride))
  2410.                     {
  2411.                         args.Player.SendErrorMessage("{0} has disabled players from teleporting.", target.Name);
  2412.                         return;
  2413.                     }
  2414.                     if (args.Player.Teleport(target.TPlayer.position.X, target.TPlayer.position.Y))
  2415.                     {
  2416.                         args.Player.SendSuccessMessage("Teleported to {0}.", target.Name);
  2417.                         if (!args.Player.HasPermission(Permissions.tpsilent))
  2418.                             target.SendInfoMessage("{0} teleported to you.", args.Player.Name);
  2419.                     }
  2420.                 }
  2421.             }
  2422.             else
  2423.             {
  2424.                 if (!args.Player.HasPermission(Permissions.tpothers))
  2425.                 {
  2426.                     args.Player.SendErrorMessage("You do not have access to this command.");
  2427.                     return;
  2428.                 }
  2429.  
  2430.                 var players1 = TSPlayer.FindByNameOrID(args.Parameters[0]);
  2431.                 var players2 = TSPlayer.FindByNameOrID(args.Parameters[1]);
  2432.  
  2433.                 if (players2.Count == 0)
  2434.                     args.Player.SendErrorMessage("Invalid player!");
  2435.                 else if (players2.Count > 1)
  2436.                     args.Player.SendMultipleMatchError(players2.Select(p => p.Name));
  2437.                 else if (players1.Count == 0)
  2438.                 {
  2439.                     if (args.Parameters[0] == "*")
  2440.                     {
  2441.                         if (!args.Player.HasPermission(Permissions.tpallothers))
  2442.                         {
  2443.                             args.Player.SendErrorMessage("You do not have access to this command.");
  2444.                             return;
  2445.                         }
  2446.  
  2447.                         var target = players2[0];
  2448.                         foreach (var source in TShock.Players.Where(p => p != null && p != args.Player))
  2449.                         {
  2450.                             if (!target.TPAllow && !args.Player.HasPermission(Permissions.tpoverride))
  2451.                                 continue;
  2452.                             if (source.Teleport(target.TPlayer.position.X, target.TPlayer.position.Y))
  2453.                             {
  2454.                                 if (args.Player != source)
  2455.                                 {
  2456.                                     if (args.Player.HasPermission(Permissions.tpsilent))
  2457.                                         source.SendSuccessMessage("You were teleported to {0}.", target.Name);
  2458.                                     else
  2459.                                         source.SendSuccessMessage("{0} teleported you to {1}.", args.Player.Name, target.Name);
  2460.                                 }
  2461.                                 if (args.Player != target)
  2462.                                 {
  2463.                                     if (args.Player.HasPermission(Permissions.tpsilent))
  2464.                                         target.SendInfoMessage("{0} was teleported to you.", source.Name);
  2465.                                     if (!args.Player.HasPermission(Permissions.tpsilent))
  2466.                                         target.SendInfoMessage("{0} teleported {1} to you.", args.Player.Name, source.Name);
  2467.                                 }
  2468.                             }
  2469.                         }
  2470.                         args.Player.SendSuccessMessage("Teleported everyone to {0}.", target.Name);
  2471.                     }
  2472.                     else
  2473.                         args.Player.SendErrorMessage("Invalid player!");
  2474.                 }
  2475.                 else if (players1.Count > 1)
  2476.                     args.Player.SendMultipleMatchError(players1.Select(p => p.Name));
  2477.                 else
  2478.                 {
  2479.                     var source = players1[0];
  2480.                     if (!source.TPAllow && !args.Player.HasPermission(Permissions.tpoverride))
  2481.                     {
  2482.                         args.Player.SendErrorMessage("{0} has disabled players from teleporting.", source.Name);
  2483.                         return;
  2484.                     }
  2485.                     var target = players2[0];
  2486.                     if (!target.TPAllow && !args.Player.HasPermission(Permissions.tpoverride))
  2487.                     {
  2488.                         args.Player.SendErrorMessage("{0} has disabled players from teleporting.", target.Name);
  2489.                         return;
  2490.                     }
  2491.                     args.Player.SendSuccessMessage("Teleported {0} to {1}.", source.Name, target.Name);
  2492.                     if (source.Teleport(target.TPlayer.position.X, target.TPlayer.position.Y))
  2493.                     {
  2494.                         if (args.Player != source)
  2495.                         {
  2496.                             if (args.Player.HasPermission(Permissions.tpsilent))
  2497.                                 source.SendSuccessMessage("You were teleported to {0}.", target.Name);
  2498.                             else
  2499.                                 source.SendSuccessMessage("{0} teleported you to {1}.", args.Player.Name, target.Name);
  2500.                         }
  2501.                         if (args.Player != target)
  2502.                         {
  2503.                             if (args.Player.HasPermission(Permissions.tpsilent))
  2504.                                 target.SendInfoMessage("{0} was teleported to you.", source.Name);
  2505.                             if (!args.Player.HasPermission(Permissions.tpsilent))
  2506.                                 target.SendInfoMessage("{0} teleported {1} to you.", args.Player.Name, source.Name);
  2507.                         }
  2508.                     }
  2509.                 }
  2510.             }
  2511.         }
  2512.  
  2513.         private static void TPHere(CommandArgs args)
  2514.         {
  2515.             if (args.Parameters.Count < 1)
  2516.             {
  2517.                 if (args.Player.HasPermission(Permissions.tpallothers))
  2518.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tphere <player|*>", Specifier);
  2519.                 else
  2520.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tphere <player>", Specifier);
  2521.                 return;
  2522.             }
  2523.  
  2524.             string playerName = String.Join(" ", args.Parameters);
  2525.             var players = TSPlayer.FindByNameOrID(playerName);
  2526.             if (players.Count == 0)
  2527.             {
  2528.                 if (playerName == "*")
  2529.                 {
  2530.                     if (!args.Player.HasPermission(Permissions.tpallothers))
  2531.                     {
  2532.                         args.Player.SendErrorMessage("You do not have permission to use this command.");
  2533.                         return;
  2534.                     }
  2535.                     for (int i = 0; i < Main.maxPlayers; i++)
  2536.                     {
  2537.                         if (Main.player[i].active && (Main.player[i] != args.TPlayer))
  2538.                         {
  2539.                             if (TShock.Players[i].Teleport(args.TPlayer.position.X, args.TPlayer.position.Y))
  2540.                                 TShock.Players[i].SendSuccessMessage(String.Format("You were teleported to {0}.", args.Player.Name));
  2541.                         }
  2542.                     }
  2543.                     args.Player.SendSuccessMessage("Teleported everyone to yourself.");
  2544.                 }
  2545.                 else
  2546.                     args.Player.SendErrorMessage("Invalid player!");
  2547.             }
  2548.             else if (players.Count > 1)
  2549.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  2550.             else
  2551.             {
  2552.                 var plr = players[0];
  2553.                 if (plr.Teleport(args.TPlayer.position.X, args.TPlayer.position.Y))
  2554.                 {
  2555.                     plr.SendInfoMessage("You were teleported to {0}.", args.Player.Name);
  2556.                     args.Player.SendSuccessMessage("Teleported {0} to yourself.", plr.Name);
  2557.                 }
  2558.             }
  2559.         }
  2560.  
  2561.         private static void TPNpc(CommandArgs args)
  2562.         {
  2563.             if (args.Parameters.Count < 1)
  2564.             {
  2565.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tpnpc <NPC>", Specifier);
  2566.                 return;
  2567.             }
  2568.  
  2569.             var npcStr = string.Join(" ", args.Parameters);
  2570.             var matches = new List<NPC>();
  2571.             foreach (var npc in Main.npc.Where(npc => npc.active))
  2572.             {
  2573.                 var englishName = EnglishLanguage.GetNpcNameById(npc.netID);
  2574.  
  2575.                 if (string.Equals(npc.FullName, npcStr, StringComparison.InvariantCultureIgnoreCase) ||
  2576.                     string.Equals(englishName, npcStr, StringComparison.InvariantCultureIgnoreCase))
  2577.                 {
  2578.                     matches = new List<NPC> { npc };
  2579.                     break;
  2580.                 }
  2581.                 if (npc.FullName.ToLowerInvariant().StartsWith(npcStr.ToLowerInvariant()) ||
  2582.                     englishName?.StartsWith(npcStr, StringComparison.InvariantCultureIgnoreCase) == true)
  2583.                     matches.Add(npc);
  2584.             }
  2585.  
  2586.             if (matches.Count > 1)
  2587.             {
  2588.                 args.Player.SendMultipleMatchError(matches.Select(n => $"{n.FullName}({n.whoAmI})"));
  2589.                 return;
  2590.             }
  2591.             if (matches.Count == 0)
  2592.             {
  2593.                 args.Player.SendErrorMessage("Invalid NPC!");
  2594.                 return;
  2595.             }
  2596.  
  2597.             var target = matches[0];
  2598.             args.Player.Teleport(target.position.X, target.position.Y);
  2599.             args.Player.SendSuccessMessage("Teleported to the '{0}'.", target.FullName);
  2600.         }
  2601.  
  2602.         private static void GetPos(CommandArgs args)
  2603.         {
  2604.             var player = args.Player.Name;
  2605.             if (args.Parameters.Count > 0)
  2606.             {
  2607.                 player = String.Join(" ", args.Parameters);
  2608.             }
  2609.  
  2610.             var players = TSPlayer.FindByNameOrID(player);
  2611.             if (players.Count == 0)
  2612.             {
  2613.                 args.Player.SendErrorMessage("Invalid player!");
  2614.             }
  2615.             else if (players.Count > 1)
  2616.             {
  2617.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  2618.             }
  2619.             else
  2620.             {
  2621.                 args.Player.SendSuccessMessage("Location of {0} is ({1}, {2}).", players[0].Name, players[0].TileX, players[0].TileY);
  2622.             }
  2623.         }
  2624.  
  2625.         private static void TPPos(CommandArgs args)
  2626.         {
  2627.             if (args.Parameters.Count != 2)
  2628.             {
  2629.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tppos <tile x> <tile y>", Specifier);
  2630.                 return;
  2631.             }
  2632.  
  2633.             int x, y;
  2634.             if (!int.TryParse(args.Parameters[0], out x) || !int.TryParse(args.Parameters[1], out y))
  2635.             {
  2636.                 args.Player.SendErrorMessage("Invalid tile positions!");
  2637.                 return;
  2638.             }
  2639.             x = Math.Max(0, x);
  2640.             y = Math.Max(0, y);
  2641.             x = Math.Min(x, Main.maxTilesX - 1);
  2642.             y = Math.Min(y, Main.maxTilesY - 1);
  2643.  
  2644.             args.Player.Teleport(16 * x, 16 * y);
  2645.             args.Player.SendSuccessMessage("Teleported to {0}, {1}!", x, y);
  2646.         }
  2647.  
  2648.         private static void TPAllow(CommandArgs args)
  2649.         {
  2650.             if (!args.Player.TPAllow)
  2651.                 args.Player.SendSuccessMessage("You have removed your teleportation protection.");
  2652.             if (args.Player.TPAllow)
  2653.                 args.Player.SendSuccessMessage("You have enabled teleportation protection.");
  2654.             args.Player.TPAllow = !args.Player.TPAllow;
  2655.         }
  2656.  
  2657.         private static void Warp(CommandArgs args)
  2658.         {
  2659.             bool hasManageWarpPermission = args.Player.HasPermission(Permissions.managewarp);
  2660.             if (args.Parameters.Count < 1)
  2661.             {
  2662.                 if (hasManageWarpPermission)
  2663.                 {
  2664.                     args.Player.SendInfoMessage("Invalid syntax! Proper syntax: {0}warp [command] [arguments]", Specifier);
  2665.                     args.Player.SendInfoMessage("Commands: add, del, hide, list, send, [warpname]");
  2666.                     args.Player.SendInfoMessage("Arguments: add [warp name], del [warp name], list [page]");
  2667.                     args.Player.SendInfoMessage("Arguments: send [player] [warp name], hide [warp name] [Enable(true/false)]");
  2668.                     args.Player.SendInfoMessage("Examples: {0}warp add foobar, {0}warp hide foobar true, {0}warp foobar", Specifier);
  2669.                     return;
  2670.                 }
  2671.                 else
  2672.                 {
  2673.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}warp [name] or {0}warp list <page>", Specifier);
  2674.                     return;
  2675.                 }
  2676.             }
  2677.  
  2678.             if (args.Parameters[0].Equals("list"))
  2679.             {
  2680.                 #region List warps
  2681.                 int pageNumber;
  2682.                 if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  2683.                     return;
  2684.                 IEnumerable<string> warpNames = from warp in TShock.Warps.Warps
  2685.                                                 where !warp.IsPrivate
  2686.                                                 select warp.Name;
  2687.                 PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(warpNames),
  2688.                     new PaginationTools.Settings
  2689.                     {
  2690.                         HeaderFormat = "Warps ({0}/{1}):",
  2691.                         FooterFormat = "Type {0}warp list {{0}} for more.".SFormat(Specifier),
  2692.                         NothingToDisplayString = "There are currently no warps defined."
  2693.                     });
  2694.                 #endregion
  2695.             }
  2696.             else if (args.Parameters[0].ToLower() == "add" && hasManageWarpPermission)
  2697.             {
  2698.                 #region Add warp
  2699.                 if (args.Parameters.Count == 2)
  2700.                 {
  2701.                     string warpName = args.Parameters[1];
  2702.                     if (warpName == "list" || warpName == "hide" || warpName == "del" || warpName == "add")
  2703.                     {
  2704.                         args.Player.SendErrorMessage("Name reserved, use a different name.");
  2705.                     }
  2706.                     else if (TShock.Warps.Add(args.Player.TileX, args.Player.TileY, warpName))
  2707.                     {
  2708.                         args.Player.SendSuccessMessage("Warp added: " + warpName);
  2709.                     }
  2710.                     else
  2711.                     {
  2712.                         args.Player.SendErrorMessage("Warp " + warpName + " already exists.");
  2713.                     }
  2714.                 }
  2715.                 else
  2716.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}warp add [name]", Specifier);
  2717.                 #endregion
  2718.             }
  2719.             else if (args.Parameters[0].ToLower() == "del" && hasManageWarpPermission)
  2720.             {
  2721.                 #region Del warp
  2722.                 if (args.Parameters.Count == 2)
  2723.                 {
  2724.                     string warpName = args.Parameters[1];
  2725.                     if (TShock.Warps.Remove(warpName))
  2726.                     {
  2727.                         args.Player.SendSuccessMessage("Warp deleted: " + warpName);
  2728.                     }
  2729.                     else
  2730.                         args.Player.SendErrorMessage("Could not find the specified warp.");
  2731.                 }
  2732.                 else
  2733.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}warp del [name]", Specifier);
  2734.                 #endregion
  2735.             }
  2736.             else if (args.Parameters[0].ToLower() == "hide" && hasManageWarpPermission)
  2737.             {
  2738.                 #region Hide warp
  2739.                 if (args.Parameters.Count == 3)
  2740.                 {
  2741.                     string warpName = args.Parameters[1];
  2742.                     bool state = false;
  2743.                     if (Boolean.TryParse(args.Parameters[2], out state))
  2744.                     {
  2745.                         if (TShock.Warps.Hide(args.Parameters[1], state))
  2746.                         {
  2747.                             if (state)
  2748.                                 args.Player.SendSuccessMessage("Warp " + warpName + " is now private.");
  2749.                             else
  2750.                                 args.Player.SendSuccessMessage("Warp " + warpName + " is now public.");
  2751.                         }
  2752.                         else
  2753.                             args.Player.SendErrorMessage("Could not find specified warp.");
  2754.                     }
  2755.                     else
  2756.                         args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}warp hide [name] <true/false>", Specifier);
  2757.                 }
  2758.                 else
  2759.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}warp hide [name] <true/false>", Specifier);
  2760.                 #endregion
  2761.             }
  2762.             else if (args.Parameters[0].ToLower() == "send" && args.Player.HasPermission(Permissions.tpothers))
  2763.             {
  2764.                 #region Warp send
  2765.                 if (args.Parameters.Count < 3)
  2766.                 {
  2767.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}warp send [player] [warpname]", Specifier);
  2768.                     return;
  2769.                 }
  2770.  
  2771.                 var foundplr = TSPlayer.FindByNameOrID(args.Parameters[1]);
  2772.                 if (foundplr.Count == 0)
  2773.                 {
  2774.                     args.Player.SendErrorMessage("Invalid player!");
  2775.                     return;
  2776.                 }
  2777.                 else if (foundplr.Count > 1)
  2778.                 {
  2779.                     args.Player.SendMultipleMatchError(foundplr.Select(p => p.Name));
  2780.                     return;
  2781.                 }
  2782.  
  2783.                 string warpName = args.Parameters[2];
  2784.                 var warp = TShock.Warps.Find(warpName);
  2785.                 var plr = foundplr[0];
  2786.                 if (warp.Position != Point.Zero)
  2787.                 {
  2788.                     if (plr.Teleport(warp.Position.X * 16, warp.Position.Y * 16))
  2789.                     {
  2790.                         plr.SendSuccessMessage(String.Format("{0} warped you to {1}.", args.Player.Name, warpName));
  2791.                         args.Player.SendSuccessMessage(String.Format("You warped {0} to {1}.", plr.Name, warpName));
  2792.                     }
  2793.                 }
  2794.                 else
  2795.                 {
  2796.                     args.Player.SendErrorMessage("Specified warp not found.");
  2797.                 }
  2798.                 #endregion
  2799.             }
  2800.             else
  2801.             {
  2802.                 string warpName = String.Join(" ", args.Parameters);
  2803.                 var warp = TShock.Warps.Find(warpName);
  2804.                 if (warp != null)
  2805.                 {
  2806.                     if (args.Player.Teleport(warp.Position.X * 16, warp.Position.Y * 16))
  2807.                         args.Player.SendSuccessMessage("Warped to " + warpName + ".");
  2808.                 }
  2809.                 else
  2810.                 {
  2811.                     args.Player.SendErrorMessage("The specified warp was not found.");
  2812.                 }
  2813.             }
  2814.         }
  2815.  
  2816.         #endregion Teleport Commands
  2817.  
  2818.         #region Group Management
  2819.  
  2820.         private static void Group(CommandArgs args)
  2821.         {
  2822.             string subCmd = args.Parameters.Count == 0 ? "help" : args.Parameters[0].ToLower();
  2823.  
  2824.             switch (subCmd)
  2825.             {
  2826.                 case "add":
  2827.                     #region Add group
  2828.                     {
  2829.                         if (args.Parameters.Count < 2)
  2830.                         {
  2831.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group add <group name> [permissions]", Specifier);
  2832.                             return;
  2833.                         }
  2834.  
  2835.                         string groupName = args.Parameters[1];
  2836.                         args.Parameters.RemoveRange(0, 2);
  2837.                         string permissions = String.Join(",", args.Parameters);
  2838.  
  2839.                         try
  2840.                         {
  2841.                             TShock.Groups.AddGroup(groupName, null, permissions, TShockAPI.Group.defaultChatColor);
  2842.                             args.Player.SendSuccessMessage("The group was added successfully!");
  2843.                         }
  2844.                         catch (GroupExistsException)
  2845.                         {
  2846.                             args.Player.SendErrorMessage("That group already exists!");
  2847.                         }
  2848.                         catch (GroupManagerException ex)
  2849.                         {
  2850.                             args.Player.SendErrorMessage(ex.ToString());
  2851.                         }
  2852.                     }
  2853.                     #endregion
  2854.                     return;
  2855.                 case "addperm":
  2856.                     #region Add permissions
  2857.                     {
  2858.                         if (args.Parameters.Count < 3)
  2859.                         {
  2860.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group addperm <group name> <permissions...>", Specifier);
  2861.                             return;
  2862.                         }
  2863.  
  2864.                         string groupName = args.Parameters[1];
  2865.                         args.Parameters.RemoveRange(0, 2);
  2866.                         if (groupName == "*")
  2867.                         {
  2868.                             foreach (Group g in TShock.Groups)
  2869.                             {
  2870.                                 TShock.Groups.AddPermissions(g.Name, args.Parameters);
  2871.                             }
  2872.                             args.Player.SendSuccessMessage("Modified all groups.");
  2873.                             return;
  2874.                         }
  2875.                         try
  2876.                         {
  2877.                             string response = TShock.Groups.AddPermissions(groupName, args.Parameters);
  2878.                             if (response.Length > 0)
  2879.                             {
  2880.                                 args.Player.SendSuccessMessage(response);
  2881.                             }
  2882.                             return;
  2883.                         }
  2884.                         catch (GroupManagerException ex)
  2885.                         {
  2886.                             args.Player.SendErrorMessage(ex.ToString());
  2887.                         }
  2888.                     }
  2889.                     #endregion
  2890.                     return;
  2891.                 case "help":
  2892.                     #region Help
  2893.                     {
  2894.                         int pageNumber;
  2895.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  2896.                             return;
  2897.  
  2898.                         var lines = new List<string>
  2899.                         {
  2900.                             "add <name> <permissions...> - Adds a new group.",
  2901.                             "addperm <group> <permissions...> - Adds permissions to a group.",
  2902.                             "color <group> <rrr,ggg,bbb> - Changes a group's chat color.",
  2903.                             "rename <group> <new name> - Changes a group's name.",
  2904.                             "del <group> - Deletes a group.",
  2905.                             "delperm <group> <permissions...> - Removes permissions from a group.",
  2906.                             "list [page] - Lists groups.",
  2907.                             "listperm <group> [page] - Lists a group's permissions.",
  2908.                             "parent <group> <parent group> - Changes a group's parent group.",
  2909.                             "prefix <group> <prefix> - Changes a group's prefix.",
  2910.                             "suffix <group> <suffix> - Changes a group's suffix."
  2911.                         };
  2912.  
  2913.                         PaginationTools.SendPage(args.Player, pageNumber, lines,
  2914.                             new PaginationTools.Settings
  2915.                             {
  2916.                                 HeaderFormat = "Group Sub-Commands ({0}/{1}):",
  2917.                                 FooterFormat = "Type {0}group help {{0}} for more sub-commands.".SFormat(Specifier)
  2918.                             }
  2919.                         );
  2920.                     }
  2921.                     #endregion
  2922.                     return;
  2923.                 case "parent":
  2924.                     #region Parent
  2925.                     {
  2926.                         if (args.Parameters.Count < 2)
  2927.                         {
  2928.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group parent <group name> [new parent group name]", Specifier);
  2929.                             return;
  2930.                         }
  2931.  
  2932.                         string groupName = args.Parameters[1];
  2933.                         Group group = TShock.Groups.GetGroupByName(groupName);
  2934.                         if (group == null)
  2935.                         {
  2936.                             args.Player.SendErrorMessage("No such group \"{0}\".", groupName);
  2937.                             return;
  2938.                         }
  2939.  
  2940.                         if (args.Parameters.Count > 2)
  2941.                         {
  2942.                             string newParentGroupName = string.Join(" ", args.Parameters.Skip(2));
  2943.                             if (!string.IsNullOrWhiteSpace(newParentGroupName) && !TShock.Groups.GroupExists(newParentGroupName))
  2944.                             {
  2945.                                 args.Player.SendErrorMessage("No such group \"{0}\".", newParentGroupName);
  2946.                                 return;
  2947.                             }
  2948.  
  2949.                             try
  2950.                             {
  2951.                                 TShock.Groups.UpdateGroup(groupName, newParentGroupName, group.Permissions, group.ChatColor, group.Suffix, group.Prefix);
  2952.  
  2953.                                 if (!string.IsNullOrWhiteSpace(newParentGroupName))
  2954.                                     args.Player.SendSuccessMessage("Parent of group \"{0}\" set to \"{1}\".", groupName, newParentGroupName);
  2955.                                 else
  2956.                                     args.Player.SendSuccessMessage("Removed parent of group \"{0}\".", groupName);
  2957.                             }
  2958.                             catch (GroupManagerException ex)
  2959.                             {
  2960.                                 args.Player.SendErrorMessage(ex.Message);
  2961.                             }
  2962.                         }
  2963.                         else
  2964.                         {
  2965.                             if (group.Parent != null)
  2966.                                 args.Player.SendSuccessMessage("Parent of \"{0}\" is \"{1}\".", group.Name, group.Parent.Name);
  2967.                             else
  2968.                                 args.Player.SendSuccessMessage("Group \"{0}\" has no parent.", group.Name);
  2969.                         }
  2970.                     }
  2971.                     #endregion
  2972.                     return;
  2973.                 case "suffix":
  2974.                     #region Suffix
  2975.                     {
  2976.                         if (args.Parameters.Count < 2)
  2977.                         {
  2978.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group suffix <group name> [new suffix]", Specifier);
  2979.                             return;
  2980.                         }
  2981.  
  2982.                         string groupName = args.Parameters[1];
  2983.                         Group group = TShock.Groups.GetGroupByName(groupName);
  2984.                         if (group == null)
  2985.                         {
  2986.                             args.Player.SendErrorMessage("No such group \"{0}\".", groupName);
  2987.                             return;
  2988.                         }
  2989.  
  2990.                         if (args.Parameters.Count > 2)
  2991.                         {
  2992.                             string newSuffix = string.Join(" ", args.Parameters.Skip(2));
  2993.  
  2994.                             try
  2995.                             {
  2996.                                 TShock.Groups.UpdateGroup(groupName, group.ParentName, group.Permissions, group.ChatColor, newSuffix, group.Prefix);
  2997.  
  2998.                                 if (!string.IsNullOrWhiteSpace(newSuffix))
  2999.                                     args.Player.SendSuccessMessage("Suffix of group \"{0}\" set to \"{1}\".", groupName, newSuffix);
  3000.                                 else
  3001.                                     args.Player.SendSuccessMessage("Removed suffix of group \"{0}\".", groupName);
  3002.                             }
  3003.                             catch (GroupManagerException ex)
  3004.                             {
  3005.                                 args.Player.SendErrorMessage(ex.Message);
  3006.                             }
  3007.                         }
  3008.                         else
  3009.                         {
  3010.                             if (!string.IsNullOrWhiteSpace(group.Suffix))
  3011.                                 args.Player.SendSuccessMessage("Suffix of \"{0}\" is \"{1}\".", group.Name, group.Suffix);
  3012.                             else
  3013.                                 args.Player.SendSuccessMessage("Group \"{0}\" has no suffix.", group.Name);
  3014.                         }
  3015.                     }
  3016.                     #endregion
  3017.                     return;
  3018.                 case "prefix":
  3019.                     #region Prefix
  3020.                     {
  3021.                         if (args.Parameters.Count < 2)
  3022.                         {
  3023.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group prefix <group name> [new prefix]", Specifier);
  3024.                             return;
  3025.                         }
  3026.  
  3027.                         string groupName = args.Parameters[1];
  3028.                         Group group = TShock.Groups.GetGroupByName(groupName);
  3029.                         if (group == null)
  3030.                         {
  3031.                             args.Player.SendErrorMessage("No such group \"{0}\".", groupName);
  3032.                             return;
  3033.                         }
  3034.  
  3035.                         if (args.Parameters.Count > 2)
  3036.                         {
  3037.                             string newPrefix = string.Join(" ", args.Parameters.Skip(2));
  3038.  
  3039.                             try
  3040.                             {
  3041.                                 TShock.Groups.UpdateGroup(groupName, group.ParentName, group.Permissions, group.ChatColor, group.Suffix, newPrefix);
  3042.  
  3043.                                 if (!string.IsNullOrWhiteSpace(newPrefix))
  3044.                                     args.Player.SendSuccessMessage("Prefix of group \"{0}\" set to \"{1}\".", groupName, newPrefix);
  3045.                                 else
  3046.                                     args.Player.SendSuccessMessage("Removed prefix of group \"{0}\".", groupName);
  3047.                             }
  3048.                             catch (GroupManagerException ex)
  3049.                             {
  3050.                                 args.Player.SendErrorMessage(ex.Message);
  3051.                             }
  3052.                         }
  3053.                         else
  3054.                         {
  3055.                             if (!string.IsNullOrWhiteSpace(group.Prefix))
  3056.                                 args.Player.SendSuccessMessage("Prefix of \"{0}\" is \"{1}\".", group.Name, group.Prefix);
  3057.                             else
  3058.                                 args.Player.SendSuccessMessage("Group \"{0}\" has no prefix.", group.Name);
  3059.                         }
  3060.                     }
  3061.                     #endregion
  3062.                     return;
  3063.                 case "color":
  3064.                     #region Color
  3065.                     {
  3066.                         if (args.Parameters.Count < 2 || args.Parameters.Count > 3)
  3067.                         {
  3068.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group color <group name> [new color(000,000,000)]", Specifier);
  3069.                             return;
  3070.                         }
  3071.  
  3072.                         string groupName = args.Parameters[1];
  3073.                         Group group = TShock.Groups.GetGroupByName(groupName);
  3074.                         if (group == null)
  3075.                         {
  3076.                             args.Player.SendErrorMessage("No such group \"{0}\".", groupName);
  3077.                             return;
  3078.                         }
  3079.  
  3080.                         if (args.Parameters.Count == 3)
  3081.                         {
  3082.                             string newColor = args.Parameters[2];
  3083.  
  3084.                             String[] parts = newColor.Split(',');
  3085.                             byte r;
  3086.                             byte g;
  3087.                             byte b;
  3088.                             if (parts.Length == 3 && byte.TryParse(parts[0], out r) && byte.TryParse(parts[1], out g) && byte.TryParse(parts[2], out b))
  3089.                             {
  3090.                                 try
  3091.                                 {
  3092.                                     TShock.Groups.UpdateGroup(groupName, group.ParentName, group.Permissions, newColor, group.Suffix, group.Prefix);
  3093.  
  3094.                                     args.Player.SendSuccessMessage("Color of group \"{0}\" set to \"{1}\".", groupName, newColor);
  3095.                                 }
  3096.                                 catch (GroupManagerException ex)
  3097.                                 {
  3098.                                     args.Player.SendErrorMessage(ex.Message);
  3099.                                 }
  3100.                             }
  3101.                             else
  3102.                             {
  3103.                                 args.Player.SendErrorMessage("Invalid syntax for color, expected \"rrr,ggg,bbb\"");
  3104.                             }
  3105.                         }
  3106.                         else
  3107.                         {
  3108.                             args.Player.SendSuccessMessage("Color of \"{0}\" is \"{1}\".", group.Name, group.ChatColor);
  3109.                         }
  3110.                     }
  3111.                     #endregion
  3112.                     return;
  3113.                 case "rename":
  3114.                     #region Rename group
  3115.                     {
  3116.                         if (args.Parameters.Count != 3)
  3117.                         {
  3118.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group rename <group> <new name>", Specifier);
  3119.                             return;
  3120.                         }
  3121.  
  3122.                         string group = args.Parameters[1];
  3123.                         string newName = args.Parameters[2];
  3124.                         try
  3125.                         {
  3126.                             string response = TShock.Groups.RenameGroup(group, newName);
  3127.                             args.Player.SendSuccessMessage(response);
  3128.                         }
  3129.                         catch (GroupManagerException ex)
  3130.                         {
  3131.                             args.Player.SendErrorMessage(ex.Message);
  3132.                         }
  3133.                     }
  3134.                     #endregion
  3135.                     return;
  3136.                 case "del":
  3137.                     #region Delete group
  3138.                     {
  3139.                         if (args.Parameters.Count != 2)
  3140.                         {
  3141.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group del <group name>", Specifier);
  3142.                             return;
  3143.                         }
  3144.  
  3145.                         try
  3146.                         {
  3147.                             string response = TShock.Groups.DeleteGroup(args.Parameters[1]);
  3148.                             if (response.Length > 0)
  3149.                             {
  3150.                                 args.Player.SendSuccessMessage(response);
  3151.                             }
  3152.                         }
  3153.                         catch (GroupManagerException ex)
  3154.                         {
  3155.                             args.Player.SendErrorMessage(ex.ToString());
  3156.                         }
  3157.                     }
  3158.                     #endregion
  3159.                     return;
  3160.                 case "delperm":
  3161.                     #region Delete permissions
  3162.                     {
  3163.                         if (args.Parameters.Count < 3)
  3164.                         {
  3165.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group delperm <group name> <permissions...>", Specifier);
  3166.                             return;
  3167.                         }
  3168.  
  3169.                         string groupName = args.Parameters[1];
  3170.                         args.Parameters.RemoveRange(0, 2);
  3171.                         if (groupName == "*")
  3172.                         {
  3173.                             foreach (Group g in TShock.Groups)
  3174.                             {
  3175.                                 TShock.Groups.DeletePermissions(g.Name, args.Parameters);
  3176.                             }
  3177.                             args.Player.SendSuccessMessage("Modified all groups.");
  3178.                             return;
  3179.                         }
  3180.                         try
  3181.                         {
  3182.                             string response = TShock.Groups.DeletePermissions(groupName, args.Parameters);
  3183.                             if (response.Length > 0)
  3184.                             {
  3185.                                 args.Player.SendSuccessMessage(response);
  3186.                             }
  3187.                             return;
  3188.                         }
  3189.                         catch (GroupManagerException ex)
  3190.                         {
  3191.                             args.Player.SendErrorMessage(ex.ToString());
  3192.                         }
  3193.                     }
  3194.                     #endregion
  3195.                     return;
  3196.                 case "list":
  3197.                     #region List groups
  3198.                     {
  3199.                         int pageNumber;
  3200.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  3201.                             return;
  3202.                         var groupNames = from grp in TShock.Groups.groups
  3203.                                          select grp.Name;
  3204.                         PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(groupNames),
  3205.                             new PaginationTools.Settings
  3206.                             {
  3207.                                 HeaderFormat = "Groups ({0}/{1}):",
  3208.                                 FooterFormat = "Type {0}group list {{0}} for more.".SFormat(Specifier)
  3209.                             });
  3210.                     }
  3211.                     #endregion
  3212.                     return;
  3213.                 case "listperm":
  3214.                     #region List permissions
  3215.                     {
  3216.                         if (args.Parameters.Count == 1)
  3217.                         {
  3218.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group listperm <group name> [page]", Specifier);
  3219.                             return;
  3220.                         }
  3221.                         int pageNumber;
  3222.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 2, args.Player, out pageNumber))
  3223.                             return;
  3224.  
  3225.                         if (!TShock.Groups.GroupExists(args.Parameters[1]))
  3226.                         {
  3227.                             args.Player.SendErrorMessage("Invalid group.");
  3228.                             return;
  3229.                         }
  3230.                         Group grp = TShock.Groups.GetGroupByName(args.Parameters[1]);
  3231.                         List<string> permissions = grp.TotalPermissions;
  3232.  
  3233.                         PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(permissions),
  3234.                             new PaginationTools.Settings
  3235.                             {
  3236.                                 HeaderFormat = "Permissions for " + grp.Name + " ({0}/{1}):",
  3237.                                 FooterFormat = "Type {0}group listperm {1} {{0}} for more.".SFormat(Specifier, grp.Name),
  3238.                                 NothingToDisplayString = "There are currently no permissions for " + grp.Name + "."
  3239.                             });
  3240.                     }
  3241.                     #endregion
  3242.                     return;
  3243.             }
  3244.         }
  3245.         #endregion Group Management
  3246.  
  3247.         #region Item Management
  3248.  
  3249.         private static void ItemBan(CommandArgs args)
  3250.         {
  3251.             string subCmd = args.Parameters.Count == 0 ? "help" : args.Parameters[0].ToLower();
  3252.             switch (subCmd)
  3253.             {
  3254.                 case "add":
  3255.                     #region Add item
  3256.                     {
  3257.                         if (args.Parameters.Count != 2)
  3258.                         {
  3259.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}itemban add <item name>", Specifier);
  3260.                             return;
  3261.                         }
  3262.  
  3263.                         List<Item> items = TShock.Utils.GetItemByIdOrName(args.Parameters[1]);
  3264.                         if (items.Count == 0)
  3265.                         {
  3266.                             args.Player.SendErrorMessage("Invalid item.");
  3267.                         }
  3268.                         else if (items.Count > 1)
  3269.                         {
  3270.                             args.Player.SendMultipleMatchError(items.Select(i => $"{i.Name}({i.netID})"));
  3271.                         }
  3272.                         else
  3273.                         {
  3274.                             TShock.Itembans.AddNewBan(EnglishLanguage.GetItemNameById(items[0].type));
  3275.                             args.Player.SendSuccessMessage("Banned " + items[0].Name + ".");
  3276.                         }
  3277.                     }
  3278.                     #endregion
  3279.                     return;
  3280.                 case "allow":
  3281.                     #region Allow group to item
  3282.                     {
  3283.                         if (args.Parameters.Count != 3)
  3284.                         {
  3285.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}itemban allow <item name> <group name>", Specifier);
  3286.                             return;
  3287.                         }
  3288.  
  3289.                         List<Item> items = TShock.Utils.GetItemByIdOrName(args.Parameters[1]);
  3290.                         if (items.Count == 0)
  3291.                         {
  3292.                             args.Player.SendErrorMessage("Invalid item.");
  3293.                         }
  3294.                         else if (items.Count > 1)
  3295.                         {
  3296.                             args.Player.SendMultipleMatchError(items.Select(i => $"{i.Name}({i.netID})"));
  3297.                         }
  3298.                         else
  3299.                         {
  3300.                             if (!TShock.Groups.GroupExists(args.Parameters[2]))
  3301.                             {
  3302.                                 args.Player.SendErrorMessage("Invalid group.");
  3303.                                 return;
  3304.                             }
  3305.  
  3306.                             ItemBan ban = TShock.Itembans.GetItemBanByName(EnglishLanguage.GetItemNameById(items[0].type));
  3307.                             if (ban == null)
  3308.                             {
  3309.                                 args.Player.SendErrorMessage("{0} is not banned.", items[0].Name);
  3310.                                 return;
  3311.                             }
  3312.                             if (!ban.AllowedGroups.Contains(args.Parameters[2]))
  3313.                             {
  3314.                                 TShock.Itembans.AllowGroup(EnglishLanguage.GetItemNameById(items[0].type), args.Parameters[2]);
  3315.                                 args.Player.SendSuccessMessage("{0} has been allowed to use {1}.", args.Parameters[2], items[0].Name);
  3316.                             }
  3317.                             else
  3318.                             {
  3319.                                 args.Player.SendWarningMessage("{0} is already allowed to use {1}.", args.Parameters[2], items[0].Name);
  3320.                             }
  3321.                         }
  3322.                     }
  3323.                     #endregion
  3324.                     return;
  3325.                 case "del":
  3326.                     #region Delete item
  3327.                     {
  3328.                         if (args.Parameters.Count != 2)
  3329.                         {
  3330.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}itemban del <item name>", Specifier);
  3331.                             return;
  3332.                         }
  3333.  
  3334.                         List<Item> items = TShock.Utils.GetItemByIdOrName(args.Parameters[1]);
  3335.                         if (items.Count == 0)
  3336.                         {
  3337.                             args.Player.SendErrorMessage("Invalid item.");
  3338.                         }
  3339.                         else if (items.Count > 1)
  3340.                         {
  3341.                             args.Player.SendMultipleMatchError(items.Select(i => $"{i.Name}({i.netID})"));
  3342.                         }
  3343.                         else
  3344.                         {
  3345.                             TShock.Itembans.RemoveBan(EnglishLanguage.GetItemNameById(items[0].type));
  3346.                             args.Player.SendSuccessMessage("Unbanned " + items[0].Name + ".");
  3347.                         }
  3348.                     }
  3349.                     #endregion
  3350.                     return;
  3351.                 case "disallow":
  3352.                     #region Disllow group from item
  3353.                     {
  3354.                         if (args.Parameters.Count != 3)
  3355.                         {
  3356.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}itemban disallow <item name> <group name>", Specifier);
  3357.                             return;
  3358.                         }
  3359.  
  3360.                         List<Item> items = TShock.Utils.GetItemByIdOrName(args.Parameters[1]);
  3361.                         if (items.Count == 0)
  3362.                         {
  3363.                             args.Player.SendErrorMessage("Invalid item.");
  3364.                         }
  3365.                         else if (items.Count > 1)
  3366.                         {
  3367.                             args.Player.SendMultipleMatchError(items.Select(i => $"{i.Name}({i.netID})"));
  3368.                         }
  3369.                         else
  3370.                         {
  3371.                             if (!TShock.Groups.GroupExists(args.Parameters[2]))
  3372.                             {
  3373.                                 args.Player.SendErrorMessage("Invalid group.");
  3374.                                 return;
  3375.                             }
  3376.  
  3377.                             ItemBan ban = TShock.Itembans.GetItemBanByName(EnglishLanguage.GetItemNameById(items[0].type));
  3378.                             if (ban == null)
  3379.                             {
  3380.                                 args.Player.SendErrorMessage("{0} is not banned.", items[0].Name);
  3381.                                 return;
  3382.                             }
  3383.                             if (ban.AllowedGroups.Contains(args.Parameters[2]))
  3384.                             {
  3385.                                 TShock.Itembans.RemoveGroup(EnglishLanguage.GetItemNameById(items[0].type), args.Parameters[2]);
  3386.                                 args.Player.SendSuccessMessage("{0} has been disallowed to use {1}.", args.Parameters[2], items[0].Name);
  3387.                             }
  3388.                             else
  3389.                             {
  3390.                                 args.Player.SendWarningMessage("{0} is already disallowed to use {1}.", args.Parameters[2], items[0].Name);
  3391.                             }
  3392.                         }
  3393.                     }
  3394.                     #endregion
  3395.                     return;
  3396.                 case "help":
  3397.                     #region Help
  3398.                     {
  3399.                         int pageNumber;
  3400.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  3401.                             return;
  3402.  
  3403.                         var lines = new List<string>
  3404.                         {
  3405.                             "add <item> - Adds an item ban.",
  3406.                             "allow <item> <group> - Allows a group to use an item.",
  3407.                             "del <item> - Deletes an item ban.",
  3408.                             "disallow <item> <group> - Disallows a group from using an item.",
  3409.                             "list [page] - Lists all item bans."
  3410.                         };
  3411.  
  3412.                         PaginationTools.SendPage(args.Player, pageNumber, lines,
  3413.                             new PaginationTools.Settings
  3414.                             {
  3415.                                 HeaderFormat = "Item Ban Sub-Commands ({0}/{1}):",
  3416.                                 FooterFormat = "Type {0}itemban help {{0}} for more sub-commands.".SFormat(Specifier)
  3417.                             }
  3418.                         );
  3419.                     }
  3420.                     #endregion
  3421.                     return;
  3422.                 case "list":
  3423.                     #region List items
  3424.                     {
  3425.                         int pageNumber;
  3426.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  3427.                             return;
  3428.                         IEnumerable<string> itemNames = from itemBan in TShock.Itembans.ItemBans
  3429.                                                         select itemBan.Name;
  3430.                         PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(itemNames),
  3431.                             new PaginationTools.Settings
  3432.                             {
  3433.                                 HeaderFormat = "Item bans ({0}/{1}):",
  3434.                                 FooterFormat = "Type {0}itemban list {{0}} for more.".SFormat(Specifier),
  3435.                                 NothingToDisplayString = "There are currently no banned items."
  3436.                             });
  3437.                     }
  3438.                     #endregion
  3439.                     return;
  3440.             }
  3441.         }
  3442.         #endregion Item Management
  3443.  
  3444.         #region Projectile Management
  3445.  
  3446.         private static void ProjectileBan(CommandArgs args)
  3447.         {
  3448.             string subCmd = args.Parameters.Count == 0 ? "help" : args.Parameters[0].ToLower();
  3449.             switch (subCmd)
  3450.             {
  3451.                 case "add":
  3452.                     #region Add projectile
  3453.                     {
  3454.                         if (args.Parameters.Count != 2)
  3455.                         {
  3456.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}projban add <proj id>", Specifier);
  3457.                             return;
  3458.                         }
  3459.                         short id;
  3460.                         if (Int16.TryParse(args.Parameters[1], out id) && id > 0 && id < Main.maxProjectileTypes)
  3461.                         {
  3462.                             TShock.ProjectileBans.AddNewBan(id);
  3463.                             args.Player.SendSuccessMessage("Banned projectile {0}.", id);
  3464.                         }
  3465.                         else
  3466.                             args.Player.SendErrorMessage("Invalid projectile ID!");
  3467.                     }
  3468.                     #endregion
  3469.                     return;
  3470.                 case "allow":
  3471.                     #region Allow group to projectile
  3472.                     {
  3473.                         if (args.Parameters.Count != 3)
  3474.                         {
  3475.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}projban allow <id> <group>", Specifier);
  3476.                             return;
  3477.                         }
  3478.  
  3479.                         short id;
  3480.                         if (Int16.TryParse(args.Parameters[1], out id) && id > 0 && id < Main.maxProjectileTypes)
  3481.                         {
  3482.                             if (!TShock.Groups.GroupExists(args.Parameters[2]))
  3483.                             {
  3484.                                 args.Player.SendErrorMessage("Invalid group.");
  3485.                                 return;
  3486.                             }
  3487.  
  3488.                             ProjectileBan ban = TShock.ProjectileBans.GetBanById(id);
  3489.                             if (ban == null)
  3490.                             {
  3491.                                 args.Player.SendErrorMessage("Projectile {0} is not banned.", id);
  3492.                                 return;
  3493.                             }
  3494.                             if (!ban.AllowedGroups.Contains(args.Parameters[2]))
  3495.                             {
  3496.                                 TShock.ProjectileBans.AllowGroup(id, args.Parameters[2]);
  3497.                                 args.Player.SendSuccessMessage("{0} has been allowed to use projectile {1}.", args.Parameters[2], id);
  3498.                             }
  3499.                             else
  3500.                                 args.Player.SendWarningMessage("{0} is already allowed to use projectile {1}.", args.Parameters[2], id);
  3501.                         }
  3502.                         else
  3503.                             args.Player.SendErrorMessage("Invalid projectile ID!");
  3504.                     }
  3505.                     #endregion
  3506.                     return;
  3507.                 case "del":
  3508.                     #region Delete projectile
  3509.                     {
  3510.                         if (args.Parameters.Count != 2)
  3511.                         {
  3512.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}projban del <id>", Specifier);
  3513.                             return;
  3514.                         }
  3515.  
  3516.                         short id;
  3517.                         if (Int16.TryParse(args.Parameters[1], out id) && id > 0 && id < Main.maxProjectileTypes)
  3518.                         {
  3519.                             TShock.ProjectileBans.RemoveBan(id);
  3520.                             args.Player.SendSuccessMessage("Unbanned projectile {0}.", id);
  3521.                             return;
  3522.                         }
  3523.                         else
  3524.                             args.Player.SendErrorMessage("Invalid projectile ID!");
  3525.                     }
  3526.                     #endregion
  3527.                     return;
  3528.                 case "disallow":
  3529.                     #region Disallow group from projectile
  3530.                     {
  3531.                         if (args.Parameters.Count != 3)
  3532.                         {
  3533.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}projban disallow <id> <group name>", Specifier);
  3534.                             return;
  3535.                         }
  3536.  
  3537.                         short id;
  3538.                         if (Int16.TryParse(args.Parameters[1], out id) && id > 0 && id < Main.maxProjectileTypes)
  3539.                         {
  3540.                             if (!TShock.Groups.GroupExists(args.Parameters[2]))
  3541.                             {
  3542.                                 args.Player.SendErrorMessage("Invalid group.");
  3543.                                 return;
  3544.                             }
  3545.  
  3546.                             ProjectileBan ban = TShock.ProjectileBans.GetBanById(id);
  3547.                             if (ban == null)
  3548.                             {
  3549.                                 args.Player.SendErrorMessage("Projectile {0} is not banned.", id);
  3550.                                 return;
  3551.                             }
  3552.                             if (ban.AllowedGroups.Contains(args.Parameters[2]))
  3553.                             {
  3554.                                 TShock.ProjectileBans.RemoveGroup(id, args.Parameters[2]);
  3555.                                 args.Player.SendSuccessMessage("{0} has been disallowed from using projectile {1}.", args.Parameters[2], id);
  3556.                                 return;
  3557.                             }
  3558.                             else
  3559.                                 args.Player.SendWarningMessage("{0} is already prevented from using projectile {1}.", args.Parameters[2], id);
  3560.                         }
  3561.                         else
  3562.                             args.Player.SendErrorMessage("Invalid projectile ID!");
  3563.                     }
  3564.                     #endregion
  3565.                     return;
  3566.                 case "help":
  3567.                     #region Help
  3568.                     {
  3569.                         int pageNumber;
  3570.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  3571.                             return;
  3572.  
  3573.                         var lines = new List<string>
  3574.                         {
  3575.                             "add <projectile ID> - Adds a projectile ban.",
  3576.                             "allow <projectile ID> <group> - Allows a group to use a projectile.",
  3577.                             "del <projectile ID> - Deletes an projectile ban.",
  3578.                             "disallow <projectile ID> <group> - Disallows a group from using a projectile.",
  3579.                             "list [page] - Lists all projectile bans."
  3580.                         };
  3581.  
  3582.                         PaginationTools.SendPage(args.Player, pageNumber, lines,
  3583.                             new PaginationTools.Settings
  3584.                             {
  3585.                                 HeaderFormat = "Projectile Ban Sub-Commands ({0}/{1}):",
  3586.                                 FooterFormat = "Type {0}projban help {{0}} for more sub-commands.".SFormat(Specifier)
  3587.                             }
  3588.                         );
  3589.                     }
  3590.                     #endregion
  3591.                     return;
  3592.                 case "list":
  3593.                     #region List projectiles
  3594.                     {
  3595.                         int pageNumber;
  3596.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  3597.                             return;
  3598.                         IEnumerable<Int16> projectileIds = from projectileBan in TShock.ProjectileBans.ProjectileBans
  3599.                                                            select projectileBan.ID;
  3600.                         PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(projectileIds),
  3601.                             new PaginationTools.Settings
  3602.                             {
  3603.                                 HeaderFormat = "Projectile bans ({0}/{1}):",
  3604.                                 FooterFormat = "Type {0}projban list {{0}} for more.".SFormat(Specifier),
  3605.                                 NothingToDisplayString = "There are currently no banned projectiles."
  3606.                             });
  3607.                     }
  3608.                     #endregion
  3609.                     return;
  3610.             }
  3611.         }
  3612.         #endregion Projectile Management
  3613.  
  3614.         #region Tile Management
  3615.         private static void TileBan(CommandArgs args)
  3616.         {
  3617.             string subCmd = args.Parameters.Count == 0 ? "help" : args.Parameters[0].ToLower();
  3618.             switch (subCmd)
  3619.             {
  3620.                 case "add":
  3621.                     #region Add tile
  3622.                     {
  3623.                         if (args.Parameters.Count != 2)
  3624.                         {
  3625.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tileban add <tile id>", Specifier);
  3626.                             return;
  3627.                         }
  3628.                         short id;
  3629.                         if (Int16.TryParse(args.Parameters[1], out id) && id >= 0 && id < Main.maxTileSets)
  3630.                         {
  3631.                             TShock.TileBans.AddNewBan(id);
  3632.                             args.Player.SendSuccessMessage("Banned tile {0}.", id);
  3633.                         }
  3634.                         else
  3635.                             args.Player.SendErrorMessage("Invalid tile ID!");
  3636.                     }
  3637.                     #endregion
  3638.                     return;
  3639.                 case "allow":
  3640.                     #region Allow group to place tile
  3641.                     {
  3642.                         if (args.Parameters.Count != 3)
  3643.                         {
  3644.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tileban allow <id> <group>", Specifier);
  3645.                             return;
  3646.                         }
  3647.  
  3648.                         short id;
  3649.                         if (Int16.TryParse(args.Parameters[1], out id) && id >= 0 && id < Main.maxTileSets)
  3650.                         {
  3651.                             if (!TShock.Groups.GroupExists(args.Parameters[2]))
  3652.                             {
  3653.                                 args.Player.SendErrorMessage("Invalid group.");
  3654.                                 return;
  3655.                             }
  3656.  
  3657.                             TileBan ban = TShock.TileBans.GetBanById(id);
  3658.                             if (ban == null)
  3659.                             {
  3660.                                 args.Player.SendErrorMessage("Tile {0} is not banned.", id);
  3661.                                 return;
  3662.                             }
  3663.                             if (!ban.AllowedGroups.Contains(args.Parameters[2]))
  3664.                             {
  3665.                                 TShock.TileBans.AllowGroup(id, args.Parameters[2]);
  3666.                                 args.Player.SendSuccessMessage("{0} has been allowed to place tile {1}.", args.Parameters[2], id);
  3667.                             }
  3668.                             else
  3669.                                 args.Player.SendWarningMessage("{0} is already allowed to place tile {1}.", args.Parameters[2], id);
  3670.                         }
  3671.                         else
  3672.                             args.Player.SendErrorMessage("Invalid tile ID!");
  3673.                     }
  3674.                     #endregion
  3675.                     return;
  3676.                 case "del":
  3677.                     #region Delete tile ban
  3678.                     {
  3679.                         if (args.Parameters.Count != 2)
  3680.                         {
  3681.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tileban del <id>", Specifier);
  3682.                             return;
  3683.                         }
  3684.  
  3685.                         short id;
  3686.                         if (Int16.TryParse(args.Parameters[1], out id) && id >= 0 && id < Main.maxTileSets)
  3687.                         {
  3688.                             TShock.TileBans.RemoveBan(id);
  3689.                             args.Player.SendSuccessMessage("Unbanned tile {0}.", id);
  3690.                             return;
  3691.                         }
  3692.                         else
  3693.                             args.Player.SendErrorMessage("Invalid tile ID!");
  3694.                     }
  3695.                     #endregion
  3696.                     return;
  3697.                 case "disallow":
  3698.                     #region Disallow group from placing tile
  3699.                     {
  3700.                         if (args.Parameters.Count != 3)
  3701.                         {
  3702.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tileban disallow <id> <group name>", Specifier);
  3703.                             return;
  3704.                         }
  3705.  
  3706.                         short id;
  3707.                         if (Int16.TryParse(args.Parameters[1], out id) && id >= 0 && id < Main.maxTileSets)
  3708.                         {
  3709.                             if (!TShock.Groups.GroupExists(args.Parameters[2]))
  3710.                             {
  3711.                                 args.Player.SendErrorMessage("Invalid group.");
  3712.                                 return;
  3713.                             }
  3714.  
  3715.                             TileBan ban = TShock.TileBans.GetBanById(id);
  3716.                             if (ban == null)
  3717.                             {
  3718.                                 args.Player.SendErrorMessage("Tile {0} is not banned.", id);
  3719.                                 return;
  3720.                             }
  3721.                             if (ban.AllowedGroups.Contains(args.Parameters[2]))
  3722.                             {
  3723.                                 TShock.TileBans.RemoveGroup(id, args.Parameters[2]);
  3724.                                 args.Player.SendSuccessMessage("{0} has been disallowed from placing tile {1}.", args.Parameters[2], id);
  3725.                                 return;
  3726.                             }
  3727.                             else
  3728.                                 args.Player.SendWarningMessage("{0} is already prevented from placing tile {1}.", args.Parameters[2], id);
  3729.                         }
  3730.                         else
  3731.                             args.Player.SendErrorMessage("Invalid tile ID!");
  3732.                     }
  3733.                     #endregion
  3734.                     return;
  3735.                 case "help":
  3736.                     #region Help
  3737.                     {
  3738.                         int pageNumber;
  3739.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  3740.                             return;
  3741.  
  3742.                         var lines = new List<string>
  3743.                         {
  3744.                             "add <tile ID> - Adds a tile ban.",
  3745.                             "allow <tile ID> <group> - Allows a group to place a tile.",
  3746.                             "del <tile ID> - Deletes a tile ban.",
  3747.                             "disallow <tile ID> <group> - Disallows a group from place a tile.",
  3748.                             "list [page] - Lists all tile bans."
  3749.                         };
  3750.  
  3751.                         PaginationTools.SendPage(args.Player, pageNumber, lines,
  3752.                             new PaginationTools.Settings
  3753.                             {
  3754.                                 HeaderFormat = "Tile Ban Sub-Commands ({0}/{1}):",
  3755.                                 FooterFormat = "Type {0}tileban help {{0}} for more sub-commands.".SFormat(Specifier)
  3756.                             }
  3757.                         );
  3758.                     }
  3759.                     #endregion
  3760.                     return;
  3761.                 case "list":
  3762.                     #region List tile bans
  3763.                     {
  3764.                         int pageNumber;
  3765.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  3766.                             return;
  3767.                         IEnumerable<Int16> tileIds = from tileBan in TShock.TileBans.TileBans
  3768.                                                      select tileBan.ID;
  3769.                         PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(tileIds),
  3770.                             new PaginationTools.Settings
  3771.                             {
  3772.                                 HeaderFormat = "Tile bans ({0}/{1}):",
  3773.                                 FooterFormat = "Type {0}tileban list {{0}} for more.".SFormat(Specifier),
  3774.                                 NothingToDisplayString = "There are currently no banned tiles."
  3775.                             });
  3776.                     }
  3777.                     #endregion
  3778.                     return;
  3779.             }
  3780.         }
  3781.         #endregion Tile Management
  3782.  
  3783.         #region Server Config Commands
  3784.  
  3785.         private static void SetSpawn(CommandArgs args)
  3786.         {
  3787.             Main.spawnTileX = args.Player.TileX + 1;
  3788.             Main.spawnTileY = args.Player.TileY + 3;
  3789.             SaveManager.Instance.SaveWorld(false);
  3790.             args.Player.SendSuccessMessage("Spawn has now been set at your location.");
  3791.         }
  3792.  
  3793.         private static void SetDungeon(CommandArgs args)
  3794.         {
  3795.             Main.dungeonX = args.Player.TileX + 1;
  3796.             Main.dungeonY = args.Player.TileY + 3;
  3797.             SaveManager.Instance.SaveWorld(false);
  3798.             args.Player.SendSuccessMessage("The dungeon's position has now been set at your location.");
  3799.         }
  3800.  
  3801.         private static void Reload(CommandArgs args)
  3802.         {
  3803.             TShock.Utils.Reload();
  3804.             Hooks.GeneralHooks.OnReloadEvent(args.Player);
  3805.  
  3806.             args.Player.SendSuccessMessage(
  3807.                 "Configuration, permissions, and regions reload complete. Some changes may require a server restart.");
  3808.         }
  3809.  
  3810.         private static void ServerPassword(CommandArgs args)
  3811.         {
  3812.             if (args.Parameters.Count != 1)
  3813.             {
  3814.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}serverpassword \"<new password>\"", Specifier);
  3815.                 return;
  3816.             }
  3817.             string passwd = args.Parameters[0];
  3818.             TShock.Config.ServerPassword = passwd;
  3819.             args.Player.SendSuccessMessage(string.Format("Server password has been changed to: {0}.", passwd));
  3820.         }
  3821.  
  3822.         private static void Save(CommandArgs args)
  3823.         {
  3824.             SaveManager.Instance.SaveWorld(false);
  3825.             foreach (TSPlayer tsply in TShock.Players.Where(tsply => tsply != null))
  3826.             {
  3827.                 tsply.SaveServerCharacter();
  3828.             }
  3829.             args.Player.SendSuccessMessage("Save succeeded.");
  3830.         }
  3831.  
  3832.         private static void Settle(CommandArgs args)
  3833.         {
  3834.             if (Liquid.panicMode)
  3835.             {
  3836.                 args.Player.SendWarningMessage("Liquids are already settling!");
  3837.                 return;
  3838.             }
  3839.             Liquid.StartPanic();
  3840.             args.Player.SendInfoMessage("Settling liquids.");
  3841.         }
  3842.  
  3843.         private static void MaxSpawns(CommandArgs args)
  3844.         {
  3845.             if (args.Parameters.Count == 0)
  3846.             {
  3847.                 args.Player.SendInfoMessage("Current maximum spawns: {0}", TShock.Config.DefaultMaximumSpawns);
  3848.                 return;
  3849.             }
  3850.  
  3851.             if (String.Equals(args.Parameters[0], "default", StringComparison.CurrentCultureIgnoreCase))
  3852.             {
  3853.                 TShock.Config.DefaultMaximumSpawns = NPC.defaultMaxSpawns = 5;
  3854.                 if (args.Silent)
  3855.                 {
  3856.                     args.Player.SendInfoMessage("Changed the maximum spawns to 5.");
  3857.                 }
  3858.                 else
  3859.                 {
  3860.                     TSPlayer.All.SendInfoMessage("{0} changed the maximum spawns to 5.", args.Player.Name);
  3861.                 }
  3862.                 return;
  3863.             }
  3864.  
  3865.             int maxSpawns = -1;
  3866.             if (!int.TryParse(args.Parameters[0], out maxSpawns) || maxSpawns < 0 || maxSpawns > Main.maxNPCs)
  3867.             {
  3868.                 args.Player.SendWarningMessage("Invalid maximum spawns!  Acceptable range is {0} to {1}", 0, Main.maxNPCs);
  3869.                 return;
  3870.             }
  3871.  
  3872.             TShock.Config.DefaultMaximumSpawns = NPC.defaultMaxSpawns = maxSpawns;
  3873.             if (args.Silent)
  3874.             {
  3875.                 args.Player.SendInfoMessage("Changed the maximum spawns to {0}.", maxSpawns);
  3876.             }
  3877.             else
  3878.             {
  3879.                 TSPlayer.All.SendInfoMessage("{0} changed the maximum spawns to {1}.", args.Player.Name, maxSpawns);
  3880.             }
  3881.         }
  3882.  
  3883.         private static void SpawnRate(CommandArgs args)
  3884.         {
  3885.             if (args.Parameters.Count == 0)
  3886.             {
  3887.                 args.Player.SendInfoMessage("Current spawn rate: {0}", TShock.Config.DefaultSpawnRate);
  3888.                 return;
  3889.             }
  3890.  
  3891.             if (String.Equals(args.Parameters[0], "default", StringComparison.CurrentCultureIgnoreCase))
  3892.             {
  3893.                 TShock.Config.DefaultSpawnRate = NPC.defaultSpawnRate = 600;
  3894.                 if (args.Silent)
  3895.                 {
  3896.                     args.Player.SendInfoMessage("Changed the spawn rate to 600.");
  3897.                 }
  3898.                 else
  3899.                 {
  3900.                     TSPlayer.All.SendInfoMessage("{0} changed the spawn rate to 600.", args.Player.Name);
  3901.                 }
  3902.                 return;
  3903.             }
  3904.  
  3905.             int spawnRate = -1;
  3906.             if (!int.TryParse(args.Parameters[0], out spawnRate) || spawnRate < 0)
  3907.             {
  3908.                 args.Player.SendWarningMessage("Invalid spawn rate!");
  3909.                 return;
  3910.             }
  3911.             TShock.Config.DefaultSpawnRate = NPC.defaultSpawnRate = spawnRate;
  3912.             if (args.Silent)
  3913.             {
  3914.                 args.Player.SendInfoMessage("Changed the spawn rate to {0}.", spawnRate);
  3915.             }
  3916.             else
  3917.             {
  3918.                 TSPlayer.All.SendInfoMessage("{0} changed the spawn rate to {1}.", args.Player.Name, spawnRate);
  3919.             }
  3920.         }
  3921.  
  3922.         #endregion Server Config Commands
  3923.  
  3924.         #region Time/PvpFun Commands
  3925.  
  3926.         private static void Time(CommandArgs args)
  3927.         {
  3928.             if (args.Parameters.Count == 0)
  3929.             {
  3930.                 double time = Main.time / 3600.0;
  3931.                 time += 4.5;
  3932.                 if (!Main.dayTime)
  3933.                     time += 15.0;
  3934.                 time = time % 24.0;
  3935.                 args.Player.SendInfoMessage("The current time is {0}:{1:D2}.", (int)Math.Floor(time), (int)Math.Floor((time % 1.0) * 60.0));
  3936.                 return;
  3937.             }
  3938.  
  3939.             switch (args.Parameters[0].ToLower())
  3940.             {
  3941.                 case "day":
  3942.                     TSPlayer.Server.SetTime(true, 0.0);
  3943.                     TSPlayer.All.SendInfoMessage("{0} set the time to 4:30.", args.Player.Name);
  3944.                     break;
  3945.                 case "night":
  3946.                     TSPlayer.Server.SetTime(false, 0.0);
  3947.                     TSPlayer.All.SendInfoMessage("{0} set the time to 19:30.", args.Player.Name);
  3948.                     break;
  3949.                 case "noon":
  3950.                     TSPlayer.Server.SetTime(true, 27000.0);
  3951.                     TSPlayer.All.SendInfoMessage("{0} set the time to 12:00.", args.Player.Name);
  3952.                     break;
  3953.                 case "midnight":
  3954.                     TSPlayer.Server.SetTime(false, 16200.0);
  3955.                     TSPlayer.All.SendInfoMessage("{0} set the time to 0:00.", args.Player.Name);
  3956.                     break;
  3957.                 default:
  3958.                     string[] array = args.Parameters[0].Split(':');
  3959.                     if (array.Length != 2)
  3960.                     {
  3961.                         args.Player.SendErrorMessage("Invalid time string! Proper format: hh:mm, in 24-hour time.");
  3962.                         return;
  3963.                     }
  3964.  
  3965.                     int hours;
  3966.                     int minutes;
  3967.                     if (!int.TryParse(array[0], out hours) || hours < 0 || hours > 23
  3968.                         || !int.TryParse(array[1], out minutes) || minutes < 0 || minutes > 59)
  3969.                     {
  3970.                         args.Player.SendErrorMessage("Invalid time string! Proper format: hh:mm, in 24-hour time.");
  3971.                         return;
  3972.                     }
  3973.  
  3974.                     decimal time = hours + (minutes / 60.0m);
  3975.                     time -= 4.50m;
  3976.                     if (time < 0.00m)
  3977.                         time += 24.00m;
  3978.  
  3979.                     if (time >= 15.00m)
  3980.                     {
  3981.                         TSPlayer.Server.SetTime(false, (double)((time - 15.00m) * 3600.0m));
  3982.                     }
  3983.                     else
  3984.                     {
  3985.                         TSPlayer.Server.SetTime(true, (double)(time * 3600.0m));
  3986.                     }
  3987.                     TSPlayer.All.SendInfoMessage("{0} set the time to {1}:{2:D2}.", args.Player.Name, hours, minutes);
  3988.                     break;
  3989.             }
  3990.         }
  3991.  
  3992.         private static void Sandstorm(CommandArgs args)
  3993.         {
  3994.             if (args.Parameters.Count < 1)
  3995.             {
  3996.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}sandstorm <stop/start>", Specifier);
  3997.                 return;
  3998.             }
  3999.  
  4000.             switch (args.Parameters[0].ToLowerInvariant())
  4001.             {
  4002.                 case "start":
  4003.                     Terraria.GameContent.Events.Sandstorm.StartSandstorm();
  4004.                     TSPlayer.All.SendInfoMessage("{0} started a sandstorm.", args.Player.Name);
  4005.                     break;
  4006.                 case "stop":
  4007.                     Terraria.GameContent.Events.Sandstorm.StopSandstorm();
  4008.                     TSPlayer.All.SendInfoMessage("{0} stopped the sandstorm.", args.Player.Name);
  4009.                     break;
  4010.                 default:
  4011.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}sandstorm <stop/start>", Specifier);
  4012.                     break;
  4013.             }
  4014.         }
  4015.  
  4016.         private static void Rain(CommandArgs args)
  4017.         {
  4018.             if (args.Parameters.Count < 1 || args.Parameters.Count > 2)
  4019.             {
  4020.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}rain [slime] <stop/start>", Specifier);
  4021.                 return;
  4022.             }
  4023.  
  4024.             int switchIndex = 0;
  4025.             if (args.Parameters.Count == 2 && args.Parameters[0].ToLowerInvariant() == "slime")
  4026.             {
  4027.                 switchIndex = 1;
  4028.             }
  4029.  
  4030.             switch (args.Parameters[switchIndex].ToLower())
  4031.             {
  4032.                 case "start":
  4033.                     if (switchIndex == 1)
  4034.                     {
  4035.                         Main.StartSlimeRain(false);
  4036.                         TSPlayer.All.SendData(PacketTypes.WorldInfo);
  4037.                         TSPlayer.All.SendInfoMessage("{0} caused it to rain slime.", args.Player.Name);
  4038.                     }
  4039.                     else
  4040.                     {
  4041.                         Main.StartRain();
  4042.                         TSPlayer.All.SendData(PacketTypes.WorldInfo);
  4043.                         TSPlayer.All.SendInfoMessage("{0} caused it to rain.", args.Player.Name);
  4044.                     }
  4045.                     break;
  4046.                 case "stop":
  4047.                     if (switchIndex == 1)
  4048.                     {
  4049.                         Main.StopSlimeRain(false);
  4050.                         TSPlayer.All.SendData(PacketTypes.WorldInfo);
  4051.                         TSPlayer.All.SendInfoMessage("{0} ended the slimey downpour.", args.Player.Name);
  4052.                     }
  4053.                     else
  4054.                     {
  4055.                         Main.StopRain();
  4056.                         TSPlayer.All.SendData(PacketTypes.WorldInfo);
  4057.                         TSPlayer.All.SendInfoMessage("{0} ended the downpour.", args.Player.Name);
  4058.                     }
  4059.                     break;
  4060.                 default:
  4061.                     args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}rain [slime] <stop/start>", Specifier);
  4062.                     break;
  4063.  
  4064.             }
  4065.         }
  4066.  
  4067.         private static void Slap(CommandArgs args)
  4068.         {
  4069.             if (args.Parameters.Count < 1 || args.Parameters.Count > 2)
  4070.             {
  4071.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}slap <player> [damage]", Specifier);
  4072.                 return;
  4073.             }
  4074.             if (args.Parameters[0].Length == 0)
  4075.             {
  4076.                 args.Player.SendErrorMessage("Invalid player!");
  4077.                 return;
  4078.             }
  4079.  
  4080.             string plStr = args.Parameters[0];
  4081.             var players = TSPlayer.FindByNameOrID(plStr);
  4082.             if (players.Count == 0)
  4083.             {
  4084.                 args.Player.SendErrorMessage("Invalid player!");
  4085.             }
  4086.             else if (players.Count > 1)
  4087.             {
  4088.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  4089.             }
  4090.             else
  4091.             {
  4092.                 var plr = players[0];
  4093.                 int damage = 5;
  4094.                 if (args.Parameters.Count == 2)
  4095.                 {
  4096.                     int.TryParse(args.Parameters[1], out damage);
  4097.                 }
  4098.                 if (!args.Player.HasPermission(Permissions.kill))
  4099.                 {
  4100.                     damage = TShock.Utils.Clamp(damage, 15, 0);
  4101.                 }
  4102.                 plr.DamagePlayer(damage);
  4103.                 TSPlayer.All.SendInfoMessage("{0} slapped {1} for {2} damage.", args.Player.Name, plr.Name, damage);
  4104.                 TShock.Log.Info("{0} slapped {1} for {2} damage.", args.Player.Name, plr.Name, damage);
  4105.             }
  4106.         }
  4107.  
  4108.         private static void Wind(CommandArgs args)
  4109.         {
  4110.             if (args.Parameters.Count != 1)
  4111.             {
  4112.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}wind <speed>", Specifier);
  4113.                 return;
  4114.             }
  4115.  
  4116.             int speed;
  4117.             if (!int.TryParse(args.Parameters[0], out speed) || speed * 100 < 0)
  4118.             {
  4119.                 args.Player.SendErrorMessage("Invalid wind speed!");
  4120.                 return;
  4121.             }
  4122.  
  4123.             Main.windSpeed = speed;
  4124.             Main.windSpeedSet = speed;
  4125.             Main.windSpeedSpeed = 0f;
  4126.             TSPlayer.All.SendData(PacketTypes.WorldInfo);
  4127.             TSPlayer.All.SendInfoMessage("{0} changed the wind speed to {1}.", args.Player.Name, speed);
  4128.         }
  4129.  
  4130.         #endregion Time/PvpFun Commands
  4131.  
  4132.         #region Region Commands
  4133.  
  4134.         private static void Region(CommandArgs args)
  4135.         {
  4136.             string cmd = "help";
  4137.             if (args.Parameters.Count > 0)
  4138.             {
  4139.                 cmd = args.Parameters[0].ToLower();
  4140.             }
  4141.             switch (cmd)
  4142.             {
  4143.                 case "name":
  4144.                     {
  4145.                         {
  4146.                             args.Player.SendInfoMessage("Hit a block to get the name of the region");
  4147.                             args.Player.AwaitingName = true;
  4148.                             args.Player.AwaitingNameParameters = args.Parameters.Skip(1).ToArray();
  4149.                         }
  4150.                         break;
  4151.                     }
  4152.                 case "set":
  4153.                     {
  4154.                         int choice = 0;
  4155.                         if (args.Parameters.Count == 2 &&
  4156.                             int.TryParse(args.Parameters[1], out choice) &&
  4157.                             choice >= 1 && choice <= 2)
  4158.                         {
  4159.                             args.Player.SendInfoMessage("Hit a block to Set Point " + choice);
  4160.                             args.Player.AwaitingTempPoint = choice;
  4161.                         }
  4162.                         else
  4163.                         {
  4164.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /region set <1/2>");
  4165.                         }
  4166.                         break;
  4167.                     }
  4168.                 case "define":
  4169.                     {
  4170.                         if (args.Parameters.Count > 1)
  4171.                         {
  4172.                             if (!args.Player.TempPoints.Any(p => p == Point.Zero))
  4173.                             {
  4174.                                 string regionName = String.Join(" ", args.Parameters.GetRange(1, args.Parameters.Count - 1));
  4175.                                 var x = Math.Min(args.Player.TempPoints[0].X, args.Player.TempPoints[1].X);
  4176.                                 var y = Math.Min(args.Player.TempPoints[0].Y, args.Player.TempPoints[1].Y);
  4177.                                 var width = Math.Abs(args.Player.TempPoints[0].X - args.Player.TempPoints[1].X);
  4178.                                 var height = Math.Abs(args.Player.TempPoints[0].Y - args.Player.TempPoints[1].Y);
  4179.  
  4180.                                 if (TShock.Regions.AddRegion(x, y, width, height, regionName, args.Player.Account.Name,
  4181.                                                              Main.worldID.ToString()))
  4182.                                 {
  4183.                                     args.Player.TempPoints[0] = Point.Zero;
  4184.                                     args.Player.TempPoints[1] = Point.Zero;
  4185.                                     args.Player.SendInfoMessage("Set region " + regionName);
  4186.                                 }
  4187.                                 else
  4188.                                 {
  4189.                                     args.Player.SendErrorMessage("Region " + regionName + " already exists");
  4190.                                 }
  4191.                             }
  4192.                             else
  4193.                             {
  4194.                                 args.Player.SendErrorMessage("Points not set up yet");
  4195.                             }
  4196.                         }
  4197.                         else
  4198.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region define <name>", Specifier);
  4199.                         break;
  4200.                     }
  4201.                 case "protect":
  4202.                     {
  4203.                         if (args.Parameters.Count == 3)
  4204.                         {
  4205.                             string regionName = args.Parameters[1];
  4206.                             if (args.Parameters[2].ToLower() == "true")
  4207.                             {
  4208.                                 if (TShock.Regions.SetRegionState(regionName, true))
  4209.                                     args.Player.SendInfoMessage("Protected region " + regionName);
  4210.                                 else
  4211.                                     args.Player.SendErrorMessage("Could not find specified region");
  4212.                             }
  4213.                             else if (args.Parameters[2].ToLower() == "false")
  4214.                             {
  4215.                                 if (TShock.Regions.SetRegionState(regionName, false))
  4216.                                     args.Player.SendInfoMessage("Unprotected region " + regionName);
  4217.                                 else
  4218.                                     args.Player.SendErrorMessage("Could not find specified region");
  4219.                             }
  4220.                             else
  4221.                                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region protect <name> <true/false>", Specifier);
  4222.                         }
  4223.                         else
  4224.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /region protect <name> <true/false>", Specifier);
  4225.                         break;
  4226.                     }
  4227.                 case "delete":
  4228.                     {
  4229.                         if (args.Parameters.Count > 1)
  4230.                         {
  4231.                             string regionName = String.Join(" ", args.Parameters.GetRange(1, args.Parameters.Count - 1));
  4232.                             if (TShock.Regions.DeleteRegion(regionName))
  4233.                             {
  4234.                                 args.Player.SendInfoMessage("Deleted region \"{0}\".", regionName);
  4235.                             }
  4236.                             else
  4237.                                 args.Player.SendErrorMessage("Could not find the specified region!");
  4238.                         }
  4239.                         else
  4240.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region delete <name>", Specifier);
  4241.                         break;
  4242.                     }
  4243.                 case "clear":
  4244.                     {
  4245.                         args.Player.TempPoints[0] = Point.Zero;
  4246.                         args.Player.TempPoints[1] = Point.Zero;
  4247.                         args.Player.SendInfoMessage("Cleared temporary points.");
  4248.                         args.Player.AwaitingTempPoint = 0;
  4249.                         break;
  4250.                     }
  4251.                 case "allow":
  4252.                     {
  4253.                         if (args.Parameters.Count > 2)
  4254.                         {
  4255.                             string playerName = args.Parameters[1];
  4256.                             string regionName = "";
  4257.  
  4258.                             for (int i = 2; i < args.Parameters.Count; i++)
  4259.                             {
  4260.                                 if (regionName == "")
  4261.                                 {
  4262.                                     regionName = args.Parameters[2];
  4263.                                 }
  4264.                                 else
  4265.                                 {
  4266.                                     regionName = regionName + " " + args.Parameters[i];
  4267.                                 }
  4268.                             }
  4269.                             if (TShock.UserAccounts.GetUserAccountByName(playerName) != null)
  4270.                             {
  4271.                                 if (TShock.Regions.AddNewUser(regionName, playerName))
  4272.                                 {
  4273.                                     args.Player.SendInfoMessage("Added user " + playerName + " to " + regionName);
  4274.                                 }
  4275.                                 else
  4276.                                     args.Player.SendErrorMessage("Region " + regionName + " not found");
  4277.                             }
  4278.                             else
  4279.                             {
  4280.                                 args.Player.SendErrorMessage("Player " + playerName + " not found");
  4281.                             }
  4282.                         }
  4283.                         else
  4284.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region allow <name> <region>", Specifier);
  4285.                         break;
  4286.                     }
  4287.                 case "remove":
  4288.                     if (args.Parameters.Count > 2)
  4289.                     {
  4290.                         string playerName = args.Parameters[1];
  4291.                         string regionName = "";
  4292.  
  4293.                         for (int i = 2; i < args.Parameters.Count; i++)
  4294.                         {
  4295.                             if (regionName == "")
  4296.                             {
  4297.                                 regionName = args.Parameters[2];
  4298.                             }
  4299.                             else
  4300.                             {
  4301.                                 regionName = regionName + " " + args.Parameters[i];
  4302.                             }
  4303.                         }
  4304.                         if (TShock.UserAccounts.GetUserAccountByName(playerName) != null)
  4305.                         {
  4306.                             if (TShock.Regions.RemoveUser(regionName, playerName))
  4307.                             {
  4308.                                 args.Player.SendInfoMessage("Removed user " + playerName + " from " + regionName);
  4309.                             }
  4310.                             else
  4311.                                 args.Player.SendErrorMessage("Region " + regionName + " not found");
  4312.                         }
  4313.                         else
  4314.                         {
  4315.                             args.Player.SendErrorMessage("Player " + playerName + " not found");
  4316.                         }
  4317.                     }
  4318.                     else
  4319.                         args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region remove <name> <region>", Specifier);
  4320.                     break;
  4321.                 case "allowg":
  4322.                     {
  4323.                         if (args.Parameters.Count > 2)
  4324.                         {
  4325.                             string group = args.Parameters[1];
  4326.                             string regionName = "";
  4327.  
  4328.                             for (int i = 2; i < args.Parameters.Count; i++)
  4329.                             {
  4330.                                 if (regionName == "")
  4331.                                 {
  4332.                                     regionName = args.Parameters[2];
  4333.                                 }
  4334.                                 else
  4335.                                 {
  4336.                                     regionName = regionName + " " + args.Parameters[i];
  4337.                                 }
  4338.                             }
  4339.                             if (TShock.Groups.GroupExists(group))
  4340.                             {
  4341.                                 if (TShock.Regions.AllowGroup(regionName, group))
  4342.                                 {
  4343.                                     args.Player.SendInfoMessage("Added group " + group + " to " + regionName);
  4344.                                 }
  4345.                                 else
  4346.                                     args.Player.SendErrorMessage("Region " + regionName + " not found");
  4347.                             }
  4348.                             else
  4349.                             {
  4350.                                 args.Player.SendErrorMessage("Group " + group + " not found");
  4351.                             }
  4352.                         }
  4353.                         else
  4354.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region allowg <group> <region>", Specifier);
  4355.                         break;
  4356.                     }
  4357.                 case "removeg":
  4358.                     if (args.Parameters.Count > 2)
  4359.                     {
  4360.                         string group = args.Parameters[1];
  4361.                         string regionName = "";
  4362.  
  4363.                         for (int i = 2; i < args.Parameters.Count; i++)
  4364.                         {
  4365.                             if (regionName == "")
  4366.                             {
  4367.                                 regionName = args.Parameters[2];
  4368.                             }
  4369.                             else
  4370.                             {
  4371.                                 regionName = regionName + " " + args.Parameters[i];
  4372.                             }
  4373.                         }
  4374.                         if (TShock.Groups.GroupExists(group))
  4375.                         {
  4376.                             if (TShock.Regions.RemoveGroup(regionName, group))
  4377.                             {
  4378.                                 args.Player.SendInfoMessage("Removed group " + group + " from " + regionName);
  4379.                             }
  4380.                             else
  4381.                                 args.Player.SendErrorMessage("Region " + regionName + " not found");
  4382.                         }
  4383.                         else
  4384.                         {
  4385.                             args.Player.SendErrorMessage("Group " + group + " not found");
  4386.                         }
  4387.                     }
  4388.                     else
  4389.                         args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region removeg <group> <region>", Specifier);
  4390.                     break;
  4391.                 case "list":
  4392.                     {
  4393.                         int pageNumber;
  4394.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
  4395.                             return;
  4396.  
  4397.                         IEnumerable<string> regionNames = from region in TShock.Regions.Regions
  4398.                                                           where region.WorldID == Main.worldID.ToString()
  4399.                                                           select region.Name;
  4400.                         PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(regionNames),
  4401.                             new PaginationTools.Settings
  4402.                             {
  4403.                                 HeaderFormat = "Regions ({0}/{1}):",
  4404.                                 FooterFormat = "Type {0}region list {{0}} for more.".SFormat(Specifier),
  4405.                                 NothingToDisplayString = "There are currently no regions defined."
  4406.                             });
  4407.                         break;
  4408.                     }
  4409.                 case "info":
  4410.                     {
  4411.                         if (args.Parameters.Count == 1 || args.Parameters.Count > 4)
  4412.                         {
  4413.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region info <region> [-d] [page]", Specifier);
  4414.                             break;
  4415.                         }
  4416.  
  4417.                         string regionName = args.Parameters[1];
  4418.                         bool displayBoundaries = args.Parameters.Skip(2).Any(
  4419.                             p => p.Equals("-d", StringComparison.InvariantCultureIgnoreCase)
  4420.                         );
  4421.  
  4422.                         Region region = TShock.Regions.GetRegionByName(regionName);
  4423.                         if (region == null)
  4424.                         {
  4425.                             args.Player.SendErrorMessage("Region \"{0}\" does not exist.", regionName);
  4426.                             break;
  4427.                         }
  4428.  
  4429.                         int pageNumberIndex = displayBoundaries ? 3 : 2;
  4430.                         int pageNumber;
  4431.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, pageNumberIndex, args.Player, out pageNumber))
  4432.                             break;
  4433.  
  4434.                         List<string> lines = new List<string>
  4435.                         {
  4436.                             string.Format("X: {0}; Y: {1}; W: {2}; H: {3}, Z: {4}", region.Area.X, region.Area.Y, region.Area.Width, region.Area.Height, region.Z),
  4437.                             string.Concat("Owner: ", region.Owner),
  4438.                             string.Concat("Protected: ", region.DisableBuild.ToString()),
  4439.                         };
  4440.  
  4441.                         if (region.AllowedIDs.Count > 0)
  4442.                         {
  4443.                             IEnumerable<string> sharedUsersSelector = region.AllowedIDs.Select(userId =>
  4444.                             {
  4445.                                 UserAccount account = TShock.UserAccounts.GetUserAccountByID(userId);
  4446.                                 if (account != null)
  4447.                                     return account.Name;
  4448.  
  4449.                                 return string.Concat("{ID: ", userId, "}");
  4450.                             });
  4451.                             List<string> extraLines = PaginationTools.BuildLinesFromTerms(sharedUsersSelector.Distinct());
  4452.                             extraLines[0] = "Shared with: " + extraLines[0];
  4453.                             lines.AddRange(extraLines);
  4454.                         }
  4455.                         else
  4456.                         {
  4457.                             lines.Add("Region is not shared with any users.");
  4458.                         }
  4459.  
  4460.                         if (region.AllowedGroups.Count > 0)
  4461.                         {
  4462.                             List<string> extraLines = PaginationTools.BuildLinesFromTerms(region.AllowedGroups.Distinct());
  4463.                             extraLines[0] = "Shared with groups: " + extraLines[0];
  4464.                             lines.AddRange(extraLines);
  4465.                         }
  4466.                         else
  4467.                         {
  4468.                             lines.Add("Region is not shared with any groups.");
  4469.                         }
  4470.  
  4471.                         PaginationTools.SendPage(
  4472.                             args.Player, pageNumber, lines, new PaginationTools.Settings
  4473.                             {
  4474.                                 HeaderFormat = string.Format("Information About Region \"{0}\" ({{0}}/{{1}}):", region.Name),
  4475.                                 FooterFormat = string.Format("Type {0}region info {1} {{0}} for more information.", Specifier, regionName)
  4476.                             }
  4477.                         );
  4478.  
  4479.                         if (displayBoundaries)
  4480.                         {
  4481.                             Rectangle regionArea = region.Area;
  4482.                             foreach (Point boundaryPoint in Utils.Instance.EnumerateRegionBoundaries(regionArea))
  4483.                             {
  4484.                                 // Preferring dotted lines as those should easily be distinguishable from actual wires.
  4485.                                 if ((boundaryPoint.X + boundaryPoint.Y & 1) == 0)
  4486.                                 {
  4487.                                     // Could be improved by sending raw tile data to the client instead but not really
  4488.                                     // worth the effort as chances are very low that overwriting the wire for a few
  4489.                                     // nanoseconds will cause much trouble.
  4490.                                     ITile tile = Main.tile[boundaryPoint.X, boundaryPoint.Y];
  4491.                                     bool oldWireState = tile.wire();
  4492.                                     tile.wire(true);
  4493.  
  4494.                                     try
  4495.                                     {
  4496.                                         args.Player.SendTileSquare(boundaryPoint.X, boundaryPoint.Y, 1);
  4497.                                     }
  4498.                                     finally
  4499.                                     {
  4500.                                         tile.wire(oldWireState);
  4501.                                     }
  4502.                                 }
  4503.                             }
  4504.  
  4505.                             Timer boundaryHideTimer = null;
  4506.                             boundaryHideTimer = new Timer((state) =>
  4507.                             {
  4508.                                 foreach (Point boundaryPoint in Utils.Instance.EnumerateRegionBoundaries(regionArea))
  4509.                                     if ((boundaryPoint.X + boundaryPoint.Y & 1) == 0)
  4510.                                         args.Player.SendTileSquare(boundaryPoint.X, boundaryPoint.Y, 1);
  4511.  
  4512.                                 Debug.Assert(boundaryHideTimer != null);
  4513.                                 boundaryHideTimer.Dispose();
  4514.                             },
  4515.                                 null, 5000, Timeout.Infinite
  4516.                             );
  4517.                         }
  4518.  
  4519.                         break;
  4520.                     }
  4521.                 case "z":
  4522.                     {
  4523.                         if (args.Parameters.Count == 3)
  4524.                         {
  4525.                             string regionName = args.Parameters[1];
  4526.                             int z = 0;
  4527.                             if (int.TryParse(args.Parameters[2], out z))
  4528.                             {
  4529.                                 if (TShock.Regions.SetZ(regionName, z))
  4530.                                     args.Player.SendInfoMessage("Region's z is now " + z);
  4531.                                 else
  4532.                                     args.Player.SendErrorMessage("Could not find specified region");
  4533.                             }
  4534.                             else
  4535.                                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region z <name> <#>", Specifier);
  4536.                         }
  4537.                         else
  4538.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region z <name> <#>", Specifier);
  4539.                         break;
  4540.                     }
  4541.                 case "resize":
  4542.                 case "expand":
  4543.                     {
  4544.                         if (args.Parameters.Count == 4)
  4545.                         {
  4546.                             int direction;
  4547.                             switch (args.Parameters[2])
  4548.                             {
  4549.                                 case "u":
  4550.                                 case "up":
  4551.                                     {
  4552.                                         direction = 0;
  4553.                                         break;
  4554.                                     }
  4555.                                 case "r":
  4556.                                 case "right":
  4557.                                     {
  4558.                                         direction = 1;
  4559.                                         break;
  4560.                                     }
  4561.                                 case "d":
  4562.                                 case "down":
  4563.                                     {
  4564.                                         direction = 2;
  4565.                                         break;
  4566.                                     }
  4567.                                 case "l":
  4568.                                 case "left":
  4569.                                     {
  4570.                                         direction = 3;
  4571.                                         break;
  4572.                                     }
  4573.                                 default:
  4574.                                     {
  4575.                                         direction = -1;
  4576.                                         break;
  4577.                                     }
  4578.                             }
  4579.                             int addAmount;
  4580.                             int.TryParse(args.Parameters[3], out addAmount);
  4581.                             if (TShock.Regions.ResizeRegion(args.Parameters[1], addAmount, direction))
  4582.                             {
  4583.                                 args.Player.SendInfoMessage("Region Resized Successfully!");
  4584.                                 TShock.Regions.Reload();
  4585.                             }
  4586.                             else
  4587.                                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region resize <region> <u/d/l/r> <amount>", Specifier);
  4588.                         }
  4589.                         else
  4590.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region resize <region> <u/d/l/r> <amount>", Specifier);
  4591.                         break;
  4592.                     }
  4593.                 case "rename":
  4594.                     {
  4595.                         if (args.Parameters.Count != 3)
  4596.                         {
  4597.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region rename <region> <new name>", Specifier);
  4598.                             break;
  4599.                         }
  4600.                         else
  4601.                         {
  4602.                             string oldName = args.Parameters[1];
  4603.                             string newName = args.Parameters[2];
  4604.  
  4605.                             if (oldName == newName)
  4606.                             {
  4607.                                 args.Player.SendErrorMessage("Error: both names are the same.");
  4608.                                 break;
  4609.                             }
  4610.  
  4611.                             Region oldRegion = TShock.Regions.GetRegionByName(oldName);
  4612.  
  4613.                             if (oldRegion == null)
  4614.                             {
  4615.                                 args.Player.SendErrorMessage("Invalid region \"{0}\".", oldName);
  4616.                                 break;
  4617.                             }
  4618.  
  4619.                             Region newRegion = TShock.Regions.GetRegionByName(newName);
  4620.  
  4621.                             if (newRegion != null)
  4622.                             {
  4623.                                 args.Player.SendErrorMessage("Region \"{0}\" already exists.", newName);
  4624.                                 break;
  4625.                             }
  4626.                            
  4627.                             if(TShock.Regions.RenameRegion(oldName, newName))
  4628.                             {
  4629.                                 args.Player.SendInfoMessage("Region renamed successfully!");
  4630.                             }
  4631.                             else
  4632.                             {
  4633.                                 args.Player.SendErrorMessage("Failed to rename the region.");
  4634.                             }
  4635.                         }
  4636.                         break;
  4637.                     }
  4638.                 case "tp":
  4639.                     {
  4640.                         if (!args.Player.HasPermission(Permissions.tp))
  4641.                         {
  4642.                             args.Player.SendErrorMessage("You don't have the necessary permission to do that.");
  4643.                             break;
  4644.                         }
  4645.                         if (args.Parameters.Count <= 1)
  4646.                         {
  4647.                             args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}region tp <region>.", Specifier);
  4648.                             break;
  4649.                         }
  4650.  
  4651.                         string regionName = string.Join(" ", args.Parameters.Skip(1));
  4652.                         Region region = TShock.Regions.GetRegionByName(regionName);
  4653.                         if (region == null)
  4654.                         {
  4655.                             args.Player.SendErrorMessage("Region \"{0}\" does not exist.", regionName);
  4656.                             break;
  4657.                         }
  4658.  
  4659.                         args.Player.Teleport(region.Area.Center.X * 16, region.Area.Center.Y * 16);
  4660.                         break;
  4661.                     }
  4662.                 case "help":
  4663.                 default:
  4664.                     {
  4665.                         int pageNumber;
  4666.                         int pageParamIndex = 0;
  4667.                         if (args.Parameters.Count > 1)
  4668.                             pageParamIndex = 1;
  4669.                         if (!PaginationTools.TryParsePageNumber(args.Parameters, pageParamIndex, args.Player, out pageNumber))
  4670.                             return;
  4671.  
  4672.                         List<string> lines = new List<string> {
  4673.                           "set <1/2> - Sets the temporary region points.",
  4674.                           "clear - Clears the temporary region points.",
  4675.                           "define <name> - Defines the region with the given name.",
  4676.                           "delete <name> - Deletes the given region.",
  4677.                           "name [-u][-z][-p] - Shows the name of the region at the given point.",
  4678.                           "rename <region> <new name> - Renames the given region.",
  4679.                           "list - Lists all regions.",
  4680.                           "resize <region> <u/d/l/r> <amount> - Resizes a region.",
  4681.                           "allow <user> <region> - Allows a user to a region.",
  4682.                           "remove <user> <region> - Removes a user from a region.",
  4683.                           "allowg <group> <region> - Allows a user group to a region.",
  4684.                           "removeg <group> <region> - Removes a user group from a region.",
  4685.                           "info <region> [-d] - Displays several information about the given region.",
  4686.                           "protect <name> <true/false> - Sets whether the tiles inside the region are protected or not.",
  4687.                           "z <name> <#> - Sets the z-order of the region.",
  4688.                         };
  4689.                         if (args.Player.HasPermission(Permissions.tp))
  4690.                             lines.Add("tp <region> - Teleports you to the given region's center.");
  4691.  
  4692.                         PaginationTools.SendPage(
  4693.                           args.Player, pageNumber, lines,
  4694.                           new PaginationTools.Settings
  4695.                           {
  4696.                               HeaderFormat = "Available Region Sub-Commands ({0}/{1}):",
  4697.                               FooterFormat = "Type {0}region {{0}} for more sub-commands.".SFormat(Specifier)
  4698.                           }
  4699.                         );
  4700.                         break;
  4701.                     }
  4702.             }
  4703.         }
  4704.  
  4705.         #endregion Region Commands
  4706.  
  4707.         #region World Protection Commands
  4708.  
  4709.         private static void ToggleAntiBuild(CommandArgs args)
  4710.         {
  4711.             TShock.Config.DisableBuild = !TShock.Config.DisableBuild;
  4712.             TSPlayer.All.SendSuccessMessage(string.Format("Anti-build is now {0}.", (TShock.Config.DisableBuild ? "on" : "off")));
  4713.         }
  4714.  
  4715.         private static void ProtectSpawn(CommandArgs args)
  4716.         {
  4717.             TShock.Config.SpawnProtection = !TShock.Config.SpawnProtection;
  4718.             TSPlayer.All.SendSuccessMessage(string.Format("Spawn is now {0}.", (TShock.Config.SpawnProtection ? "protected" : "open")));
  4719.         }
  4720.  
  4721.         #endregion World Protection Commands
  4722.  
  4723.         #region General Commands
  4724.  
  4725.         private static void Help(CommandArgs args)
  4726.         {
  4727.             if (args.Parameters.Count > 1)
  4728.             {
  4729.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}help <command/page>", Specifier);
  4730.                 return;
  4731.             }
  4732.  
  4733.             int pageNumber;
  4734.             if (args.Parameters.Count == 0 || int.TryParse(args.Parameters[0], out pageNumber))
  4735.             {
  4736.                 if (!PaginationTools.TryParsePageNumber(args.Parameters, 0, args.Player, out pageNumber))
  4737.                 {
  4738.                     return;
  4739.                 }
  4740.  
  4741.                 IEnumerable<string> cmdNames = from cmd in ChatCommands
  4742.                                                where cmd.CanRun(args.Player) && (cmd.Name != "auth" || TShock.SetupToken != 0)
  4743.                                                select Specifier + cmd.Name;
  4744.  
  4745.                 PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(cmdNames),
  4746.                     new PaginationTools.Settings
  4747.                     {
  4748.                         HeaderFormat = "Commands ({0}/{1}):",
  4749.                         FooterFormat = "Type {0}help {{0}} for more.".SFormat(Specifier)
  4750.                     });
  4751.             }
  4752.             else
  4753.             {
  4754.                 string commandName = args.Parameters[0].ToLower();
  4755.                 if (commandName.StartsWith(Specifier))
  4756.                 {
  4757.                     commandName = commandName.Substring(1);
  4758.                 }
  4759.  
  4760.                 Command command = ChatCommands.Find(c => c.Names.Contains(commandName));
  4761.                 if (command == null)
  4762.                 {
  4763.                     args.Player.SendErrorMessage("Invalid command.");
  4764.                     return;
  4765.                 }
  4766.                 if (!command.CanRun(args.Player))
  4767.                 {
  4768.                     args.Player.SendErrorMessage("You do not have access to this command.");
  4769.                     return;
  4770.                 }
  4771.  
  4772.                 args.Player.SendSuccessMessage("{0}{1} help: ", Specifier, command.Name);
  4773.                 if (command.HelpDesc == null)
  4774.                 {
  4775.                     args.Player.SendInfoMessage(command.HelpText);
  4776.                     return;
  4777.                 }
  4778.                 foreach (string line in command.HelpDesc)
  4779.                 {
  4780.                     args.Player.SendInfoMessage(line);
  4781.                 }
  4782.             }
  4783.         }
  4784.  
  4785.         private static void GetVersion(CommandArgs args)
  4786.         {
  4787.             args.Player.SendInfoMessage("TShock: {0} ({1}).", TShock.VersionNum, TShock.VersionCodename);
  4788.         }
  4789.  
  4790.         private static void ListConnectedPlayers(CommandArgs args)
  4791.         {
  4792.             bool invalidUsage = (args.Parameters.Count > 2);
  4793.  
  4794.             bool displayIdsRequested = false;
  4795.             int pageNumber = 1;
  4796.             if (!invalidUsage)
  4797.             {
  4798.                 foreach (string parameter in args.Parameters)
  4799.                 {
  4800.                     if (parameter.Equals("-i", StringComparison.InvariantCultureIgnoreCase))
  4801.                     {
  4802.                         displayIdsRequested = true;
  4803.                         continue;
  4804.                     }
  4805.  
  4806.                     if (!int.TryParse(parameter, out pageNumber))
  4807.                     {
  4808.                         invalidUsage = true;
  4809.                         break;
  4810.                     }
  4811.                 }
  4812.             }
  4813.             if (invalidUsage)
  4814.             {
  4815.                 args.Player.SendErrorMessage("Invalid usage, proper usage: {0}who [-i] [pagenumber]", Specifier);
  4816.                 return;
  4817.             }
  4818.             if (displayIdsRequested && !args.Player.HasPermission(Permissions.seeids))
  4819.             {
  4820.                 args.Player.SendErrorMessage("You don't have the required permission to list player ids.");
  4821.                 return;
  4822.             }
  4823.  
  4824.             args.Player.SendSuccessMessage("Online Players ({0}/{1})", TShock.Utils.GetActivePlayerCount(), TShock.Config.MaxSlots);
  4825.  
  4826.             var players = new List<string>();
  4827.  
  4828.             foreach (TSPlayer ply in TShock.Players)
  4829.             {
  4830.                 if (ply != null && ply.Active)
  4831.                 {
  4832.                     if (displayIdsRequested)
  4833.                     {
  4834.                         players.Add(String.Format("{0} (ID: {1}{2})", ply.Name, ply.Index, ply.Account != null ? ", ID: " + ply.Account.ID : ""));
  4835.                     }
  4836.                     else
  4837.                     {
  4838.                         players.Add(ply.Name);
  4839.                     }
  4840.                 }
  4841.             }
  4842.  
  4843.             PaginationTools.SendPage(
  4844.                 args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(players),
  4845.                 new PaginationTools.Settings
  4846.                 {
  4847.                     IncludeHeader = false,
  4848.                     FooterFormat = string.Format("Type {0}who {1}{{0}} for more.", Specifier, displayIdsRequested ? "-i " : string.Empty)
  4849.                 }
  4850.             );
  4851.         }
  4852.  
  4853.         private static void SetupToken(CommandArgs args)
  4854.         {
  4855.             if (TShock.SetupToken == 0)
  4856.             {
  4857.                 if (args.Player.Group.Name == new SuperAdminGroup().Name)
  4858.                     args.Player.SendInfoMessage("The initial setup system is already disabled.");
  4859.                 else
  4860.                 {
  4861.                     args.Player.SendWarningMessage("The initial setup system is disabled. This incident has been logged.");
  4862.                     TShock.Log.Warn("{0} attempted to use the initial setup system even though it's disabled.", args.Player.IP);
  4863.                     return;
  4864.                 }
  4865.             }
  4866.  
  4867.             // If the user account is already a superadmin (permanent), disable the system
  4868.             if (args.Player.IsLoggedIn && args.Player.tempGroup == null)
  4869.             {
  4870.                 args.Player.SendSuccessMessage("Your new account has been verified, and the {0}setup system has been turned off.", Specifier);
  4871.                 args.Player.SendSuccessMessage("You can always use the {0}user command to manage players.", Specifier);
  4872.                 args.Player.SendSuccessMessage("The setup system will remain disabled as long as a superadmin exists (even if you delete setup.lock).");
  4873.                 args.Player.SendSuccessMessage("Share your server, talk with other admins, and more on our forums -- https://tshock.co/");
  4874.                 args.Player.SendSuccessMessage("Thank you for using TShock for Terraria!");
  4875.                 FileTools.CreateFile(Path.Combine(TShock.SavePath, "setup.lock"));
  4876.                 File.Delete(Path.Combine(TShock.SavePath, "setup-code.txt"));
  4877.                 TShock.SetupToken = 0;
  4878.                 return;
  4879.             }
  4880.  
  4881.             if (args.Parameters.Count == 0)
  4882.             {
  4883.                 args.Player.SendErrorMessage("You must provide a setup code!");
  4884.                 return;
  4885.             }
  4886.  
  4887.             int givenCode;
  4888.             if (!Int32.TryParse(args.Parameters[0], out givenCode) || givenCode != TShock.SetupToken)
  4889.             {
  4890.                 args.Player.SendErrorMessage("Incorrect setup code. This incident has been logged.");
  4891.                 TShock.Log.Warn(args.Player.IP + " attempted to use an incorrect setup code.");
  4892.                 return;
  4893.             }
  4894.  
  4895.             if (args.Player.Group.Name != "superadmin")
  4896.                 args.Player.tempGroup = new SuperAdminGroup();
  4897.  
  4898.             args.Player.SendInfoMessage("Temporary system access has been given to you, so you can run one command.");
  4899.             args.Player.SendInfoMessage("Please use the following to create a permanent account for you.");
  4900.             args.Player.SendInfoMessage("{0}user add <username> <password> owner", Specifier);
  4901.             args.Player.SendInfoMessage("Creates: <username> with the password <password> as part of the owner group.");
  4902.             args.Player.SendInfoMessage("Please use {0}login <username> <password> after this process.", Specifier);
  4903.             args.Player.SendInfoMessage("If you understand, please {0}login <username> <password> now, and then type {0}setup.", Specifier);
  4904.             return;
  4905.         }
  4906.  
  4907.         private static void ThirdPerson(CommandArgs args)
  4908.         {
  4909.             if (args.Parameters.Count == 0)
  4910.             {
  4911.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}me <text>", Specifier);
  4912.                 return;
  4913.             }
  4914.             if (args.Player.mute)
  4915.                 args.Player.SendErrorMessage("You are muted.");
  4916.             else
  4917.                 TSPlayer.All.SendMessage(string.Format("*{0} {1}", args.Player.Name, String.Join(" ", args.Parameters)), 205, 133, 63);
  4918.         }
  4919.  
  4920.         private static void PartyChat(CommandArgs args)
  4921.         {
  4922.             if (args.Parameters.Count == 0)
  4923.             {
  4924.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}p <team chat text>", Specifier);
  4925.                 return;
  4926.             }
  4927.             int playerTeam = args.Player.Team;
  4928.  
  4929.             if (args.Player.mute)
  4930.                 args.Player.SendErrorMessage("You are muted.");
  4931.             else if (playerTeam != 0)
  4932.             {
  4933.                 string msg = string.Format("<{0}> {1}", args.Player.Name, String.Join(" ", args.Parameters));
  4934.                 foreach (TSPlayer player in TShock.Players)
  4935.                 {
  4936.                     if (player != null && player.Active && player.Team == playerTeam)
  4937.                         player.SendMessage(msg, Main.teamColor[playerTeam].R, Main.teamColor[playerTeam].G, Main.teamColor[playerTeam].B);
  4938.                 }
  4939.             }
  4940.             else
  4941.                 args.Player.SendErrorMessage("You are not in a party!");
  4942.         }
  4943.  
  4944.         private static void Mute(CommandArgs args)
  4945.         {
  4946.             if (args.Parameters.Count < 1)
  4947.             {
  4948.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}mute <player> [reason]", Specifier);
  4949.                 return;
  4950.             }
  4951.  
  4952.             var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
  4953.             if (players.Count == 0)
  4954.             {
  4955.                 args.Player.SendErrorMessage("Invalid player!");
  4956.             }
  4957.             else if (players.Count > 1)
  4958.             {
  4959.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  4960.             }
  4961.             else if (players[0].HasPermission(Permissions.mute))
  4962.             {
  4963.                 args.Player.SendErrorMessage("You cannot mute this player.");
  4964.             }
  4965.             else if (players[0].mute)
  4966.             {
  4967.                 var plr = players[0];
  4968.                 plr.mute = false;
  4969.                 TSPlayer.All.SendInfoMessage("{0} has been unmuted by {1}.", plr.Name, args.Player.Name);
  4970.             }
  4971.             else
  4972.             {
  4973.                 string reason = "No reason specified.";
  4974.                 if (args.Parameters.Count > 1)
  4975.                     reason = String.Join(" ", args.Parameters.ToArray(), 1, args.Parameters.Count - 1);
  4976.                 var plr = players[0];
  4977.                 plr.mute = true;
  4978.                 TSPlayer.All.SendInfoMessage("{0} has been muted by {1} for {2}.", plr.Name, args.Player.Name, reason);
  4979.             }
  4980.         }
  4981.  
  4982.         private static void Motd(CommandArgs args)
  4983.         {
  4984.             args.Player.SendFileTextAsMessage(FileTools.MotdPath);
  4985.         }
  4986.  
  4987.         private static void Rules(CommandArgs args)
  4988.         {
  4989.             args.Player.SendFileTextAsMessage(FileTools.RulesPath);
  4990.         }
  4991.  
  4992.         private static void Whisper(CommandArgs args)
  4993.         {
  4994.             if (args.Parameters.Count < 2)
  4995.             {
  4996.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}whisper <player> <text>", Specifier);
  4997.                 return;
  4998.             }
  4999.  
  5000.             var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
  5001.             if (players.Count == 0)
  5002.             {
  5003.                 args.Player.SendErrorMessage("Invalid player!");
  5004.             }
  5005.             else if (players.Count > 1)
  5006.             {
  5007.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  5008.             }
  5009.             else if (args.Player.mute)
  5010.             {
  5011.                 args.Player.SendErrorMessage("You are muted.");
  5012.             }
  5013.             else
  5014.             {
  5015.                 var plr = players[0];
  5016.                 var msg = string.Join(" ", args.Parameters.ToArray(), 1, args.Parameters.Count - 1);
  5017.                 plr.SendMessage(String.Format("<From {0}> {1}", args.Player.Name, msg), Color.MediumPurple);
  5018.                 args.Player.SendMessage(String.Format("<To {0}> {1}", plr.Name, msg), Color.MediumPurple);
  5019.                 plr.LastWhisper = args.Player;
  5020.                 args.Player.LastWhisper = plr;
  5021.             }
  5022.         }
  5023.  
  5024.         private static void Reply(CommandArgs args)
  5025.         {
  5026.             if (args.Player.mute)
  5027.             {
  5028.                 args.Player.SendErrorMessage("You are muted.");
  5029.             }
  5030.             else if (args.Player.LastWhisper != null)
  5031.             {
  5032.                 var msg = string.Join(" ", args.Parameters);
  5033.                 args.Player.LastWhisper.SendMessage(String.Format("<From {0}> {1}", args.Player.Name, msg), Color.MediumPurple);
  5034.                 args.Player.SendMessage(String.Format("<To {0}> {1}", args.Player.LastWhisper.Name, msg), Color.MediumPurple);
  5035.             }
  5036.             else
  5037.             {
  5038.                 args.Player.SendErrorMessage("You haven't previously received any whispers. Please use {0}whisper to whisper to other people.", Specifier);
  5039.             }
  5040.         }
  5041.  
  5042.         private static void Annoy(CommandArgs args)
  5043.         {
  5044.             if (args.Parameters.Count != 2)
  5045.             {
  5046.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}annoy <player> <seconds to annoy>", Specifier);
  5047.                 return;
  5048.             }
  5049.             int annoy = 5;
  5050.             int.TryParse(args.Parameters[1], out annoy);
  5051.  
  5052.             var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
  5053.             if (players.Count == 0)
  5054.                 args.Player.SendErrorMessage("Invalid player!");
  5055.             else if (players.Count > 1)
  5056.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  5057.             else
  5058.             {
  5059.                 var ply = players[0];
  5060.                 args.Player.SendSuccessMessage("Annoying " + ply.Name + " for " + annoy + " seconds.");
  5061.                 (new Thread(ply.Whoopie)).Start(annoy);
  5062.             }
  5063.         }
  5064.  
  5065.         private static void Confuse(CommandArgs args)
  5066.         {
  5067.             if (args.Parameters.Count != 1)
  5068.             {
  5069.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}confuse <player>", Specifier);
  5070.                 return;
  5071.             }
  5072.             var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
  5073.             if (players.Count == 0)
  5074.                 args.Player.SendErrorMessage("Invalid player!");
  5075.             else if (players.Count > 1)
  5076.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  5077.             else
  5078.             {
  5079.                 var ply = players[0];
  5080.                 ply.Confused = !ply.Confused;
  5081.                 args.Player.SendSuccessMessage("{0} is {1} confused.", ply.Name, ply.Confused ? "now" : "no longer");
  5082.             }
  5083.         }
  5084.  
  5085.         private static void Rocket(CommandArgs args)
  5086.         {
  5087.             if (args.Parameters.Count != 1)
  5088.             {
  5089.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}rocket <player>", Specifier);
  5090.                 return;
  5091.             }
  5092.             var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
  5093.             if (players.Count == 0)
  5094.                 args.Player.SendErrorMessage("Invalid player!");
  5095.             else if (players.Count > 1)
  5096.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  5097.             else
  5098.             {
  5099.                 var ply = players[0];
  5100.  
  5101.                 if (ply.IsLoggedIn && Main.ServerSideCharacter)
  5102.                 {
  5103.                     ply.TPlayer.velocity.Y = -50;
  5104.                     TSPlayer.All.SendData(PacketTypes.PlayerUpdate, "", ply.Index);
  5105.                     args.Player.SendSuccessMessage("Rocketed {0}.", ply.Name);
  5106.                 }
  5107.                 else
  5108.                 {
  5109.                     args.Player.SendErrorMessage("Failed to rocket player: Not logged in or not SSC mode.");
  5110.                 }
  5111.             }
  5112.         }
  5113.  
  5114.         private static void FireWork(CommandArgs args)
  5115.         {
  5116.             if (args.Parameters.Count < 1)
  5117.             {
  5118.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}firework <player> [red|green|blue|yellow]", Specifier);
  5119.                 return;
  5120.             }
  5121.             var players = TSPlayer.FindByNameOrID(args.Parameters[0]);
  5122.             if (players.Count == 0)
  5123.                 args.Player.SendErrorMessage("Invalid player!");
  5124.             else if (players.Count > 1)
  5125.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  5126.             else
  5127.             {
  5128.                 int type = 167;
  5129.                 if (args.Parameters.Count > 1)
  5130.                 {
  5131.                     if (args.Parameters[1].ToLower() == "green")
  5132.                         type = 168;
  5133.                     else if (args.Parameters[1].ToLower() == "blue")
  5134.                         type = 169;
  5135.                     else if (args.Parameters[1].ToLower() == "yellow")
  5136.                         type = 170;
  5137.                 }
  5138.                 var ply = players[0];
  5139.                 int p = Projectile.NewProjectile(ply.TPlayer.position.X, ply.TPlayer.position.Y - 64f, 0f, -8f, type, 0, (float)0);
  5140.                 Main.projectile[p].Kill();
  5141.                 args.Player.SendSuccessMessage("Launched Firework on {0}.", ply.Name);
  5142.             }
  5143.         }
  5144.  
  5145.         private static void Aliases(CommandArgs args)
  5146.         {
  5147.             if (args.Parameters.Count < 1)
  5148.             {
  5149.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}aliases <command or alias>", Specifier);
  5150.                 return;
  5151.             }
  5152.  
  5153.             string givenCommandName = string.Join(" ", args.Parameters);
  5154.             if (string.IsNullOrWhiteSpace(givenCommandName))
  5155.             {
  5156.                 args.Player.SendErrorMessage("Please enter a proper command name or alias.");
  5157.                 return;
  5158.             }
  5159.  
  5160.             string commandName;
  5161.             if (givenCommandName[0] == Specifier[0])
  5162.                 commandName = givenCommandName.Substring(1);
  5163.             else
  5164.                 commandName = givenCommandName;
  5165.  
  5166.             bool didMatch = false;
  5167.             foreach (Command matchingCommand in ChatCommands.Where(cmd => cmd.Names.IndexOf(commandName) != -1))
  5168.             {
  5169.                 if (matchingCommand.Names.Count > 1)
  5170.                     args.Player.SendInfoMessage(
  5171.                         "Aliases of {0}{1}: {0}{2}", Specifier, matchingCommand.Name, string.Join(", {0}".SFormat(Specifier), matchingCommand.Names.Skip(1)));
  5172.                 else
  5173.                     args.Player.SendInfoMessage("{0}{1} defines no aliases.", Specifier, matchingCommand.Name);
  5174.  
  5175.                 didMatch = true;
  5176.             }
  5177.  
  5178.             if (!didMatch)
  5179.                 args.Player.SendErrorMessage("No command or command alias matching \"{0}\" found.", givenCommandName);
  5180.         }
  5181.  
  5182.         private static void CreateDumps(CommandArgs args)
  5183.         {
  5184.             TShock.Utils.DumpPermissionMatrix("PermissionMatrix.txt");
  5185.             TShock.Utils.Dump(false);
  5186.             args.Player.SendSuccessMessage("Your reference dumps have been created in the server folder.");
  5187.             return;
  5188.         }
  5189.  
  5190.         #endregion General Commands
  5191.  
  5192.         #region Cheat Commands
  5193.  
  5194.         private static void Clear(CommandArgs args)
  5195.         {
  5196.             if (args.Parameters.Count != 1 && args.Parameters.Count != 2)
  5197.             {
  5198.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}clear <item/npc/projectile> [radius]", Specifier);
  5199.                 return;
  5200.             }
  5201.  
  5202.             int radius = 50;
  5203.             if (args.Parameters.Count == 2)
  5204.             {
  5205.                 if (!int.TryParse(args.Parameters[1], out radius) || radius <= 0)
  5206.                 {
  5207.                     args.Player.SendErrorMessage("Invalid radius.");
  5208.                     return;
  5209.                 }
  5210.             }
  5211.  
  5212.             switch (args.Parameters[0].ToLower())
  5213.             {
  5214.                 case "item":
  5215.                 case "items":
  5216.                     {
  5217.                         int cleared = 0;
  5218.                         for (int i = 0; i < Main.maxItems; i++)
  5219.                         {
  5220.                             float dX = Main.item[i].position.X - args.Player.X;
  5221.                             float dY = Main.item[i].position.Y - args.Player.Y;
  5222.  
  5223.                             if (Main.item[i].active && dX * dX + dY * dY <= radius * radius * 256f)
  5224.                             {
  5225.                                 Main.item[i].active = false;
  5226.                                 TSPlayer.All.SendData(PacketTypes.ItemDrop, "", i);
  5227.                                 cleared++;
  5228.                             }
  5229.                         }
  5230.                         args.Player.SendSuccessMessage("Deleted {0} items within a radius of {1}.", cleared, radius);
  5231.                     }
  5232.                     break;
  5233.                 case "npc":
  5234.                 case "npcs":
  5235.                     {
  5236.                         int cleared = 0;
  5237.                         for (int i = 0; i < Main.maxNPCs; i++)
  5238.                         {
  5239.                             float dX = Main.npc[i].position.X - args.Player.X;
  5240.                             float dY = Main.npc[i].position.Y - args.Player.Y;
  5241.  
  5242.                             if (Main.npc[i].active && dX * dX + dY * dY <= radius * radius * 256f)
  5243.                             {
  5244.                                 Main.npc[i].active = false;
  5245.                                 Main.npc[i].type = 0;
  5246.                                 TSPlayer.All.SendData(PacketTypes.NpcUpdate, "", i);
  5247.                                 cleared++;
  5248.                             }
  5249.                         }
  5250.                         args.Player.SendSuccessMessage("Deleted {0} NPCs within a radius of {1}.", cleared, radius);
  5251.                     }
  5252.                     break;
  5253.                 case "proj":
  5254.                 case "projectile":
  5255.                 case "projectiles":
  5256.                     {
  5257.                         int cleared = 0;
  5258.                         for (int i = 0; i < Main.maxProjectiles; i++)
  5259.                         {
  5260.                             float dX = Main.projectile[i].position.X - args.Player.X;
  5261.                             float dY = Main.projectile[i].position.Y - args.Player.Y;
  5262.  
  5263.                             if (Main.projectile[i].active && dX * dX + dY * dY <= radius * radius * 256f)
  5264.                             {
  5265.                                 Main.projectile[i].active = false;
  5266.                                 Main.projectile[i].type = 0;
  5267.                                 TSPlayer.All.SendData(PacketTypes.ProjectileNew, "", i);
  5268.                                 cleared++;
  5269.                             }
  5270.                         }
  5271.                         args.Player.SendSuccessMessage("Deleted {0} projectiles within a radius of {1}.", cleared, radius);
  5272.                     }
  5273.                     break;
  5274.                 default:
  5275.                     args.Player.SendErrorMessage("Invalid clear option!");
  5276.                     break;
  5277.             }
  5278.         }
  5279.  
  5280.         private static void Kill(CommandArgs args)
  5281.         {
  5282.             if (args.Parameters.Count < 1)
  5283.             {
  5284.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}kill <player>", Specifier);
  5285.                 return;
  5286.             }
  5287.  
  5288.             string plStr = String.Join(" ", args.Parameters);
  5289.             var players = TSPlayer.FindByNameOrID(plStr);
  5290.             if (players.Count == 0)
  5291.             {
  5292.                 args.Player.SendErrorMessage("Invalid player!");
  5293.             }
  5294.             else if (players.Count > 1)
  5295.             {
  5296.                 args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  5297.             }
  5298.             else
  5299.             {
  5300.                 var plr = players[0];
  5301.                 plr.KillPlayer();
  5302.                 args.Player.SendSuccessMessage(string.Format("You just killed {0}!", plr.Name));
  5303.                 plr.SendErrorMessage("{0} just killed you!", args.Player.Name);
  5304.             }
  5305.         }
  5306.  
  5307.         private static void Butcher(CommandArgs args)
  5308.         {
  5309.             if (args.Parameters.Count > 1)
  5310.             {
  5311.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}butcher [mob type]", Specifier);
  5312.                 return;
  5313.             }
  5314.  
  5315.             int npcId = 0;
  5316.  
  5317.             if (args.Parameters.Count == 1)
  5318.             {
  5319.                 var npcs = TShock.Utils.GetNPCByIdOrName(args.Parameters[0]);
  5320.                 if (npcs.Count == 0)
  5321.                 {
  5322.                     args.Player.SendErrorMessage("Invalid mob type!");
  5323.                     return;
  5324.                 }
  5325.                 else if (npcs.Count > 1)
  5326.                 {
  5327.                     args.Player.SendMultipleMatchError(npcs.Select(n => $"{n.FullName}({n.type})"));
  5328.                     return;
  5329.                 }
  5330.                 else
  5331.                 {
  5332.                     npcId = npcs[0].netID;
  5333.                 }
  5334.             }
  5335.  
  5336.             int kills = 0;
  5337.             for (int i = 0; i < Main.npc.Length; i++)
  5338.             {
  5339.                 if (Main.npc[i].active && ((npcId == 0 && !Main.npc[i].townNPC && Main.npc[i].netID != NPCID.TargetDummy) || Main.npc[i].netID == npcId))
  5340.                 {
  5341.                     TSPlayer.Server.StrikeNPC(i, (int)(Main.npc[i].life + (Main.npc[i].defense * 0.6)), 0, 0);
  5342.                     kills++;
  5343.                 }
  5344.             }
  5345.             TSPlayer.All.SendInfoMessage("{0} butchered {1} NPCs.", args.Player.Name, kills);
  5346.         }
  5347.  
  5348.         private static void Item(CommandArgs args)
  5349.         {
  5350.             if (args.Parameters.Count < 1)
  5351.             {
  5352.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}item <item name/id> [item amount] [prefix id/name]", Specifier);
  5353.                 return;
  5354.             }
  5355.  
  5356.             int amountParamIndex = -1;
  5357.             int itemAmount = 0;
  5358.             for (int i = 1; i < args.Parameters.Count; i++)
  5359.             {
  5360.                 if (int.TryParse(args.Parameters[i], out itemAmount))
  5361.                 {
  5362.                     amountParamIndex = i;
  5363.                     break;
  5364.                 }
  5365.             }
  5366.  
  5367.             string itemNameOrId;
  5368.             if (amountParamIndex == -1)
  5369.                 itemNameOrId = string.Join(" ", args.Parameters);
  5370.             else
  5371.                 itemNameOrId = string.Join(" ", args.Parameters.Take(amountParamIndex));
  5372.  
  5373.             Item item;
  5374.             List<Item> matchedItems = TShock.Utils.GetItemByIdOrName(itemNameOrId);
  5375.             if (matchedItems.Count == 0)
  5376.             {
  5377.                 args.Player.SendErrorMessage("Invalid item type!");
  5378.                 return;
  5379.             }
  5380.             else if (matchedItems.Count > 1)
  5381.             {
  5382.                 args.Player.SendMultipleMatchError(matchedItems.Select(i => $"{i.Name}({i.netID})"));
  5383.                 return;
  5384.             }
  5385.             else
  5386.             {
  5387.                 item = matchedItems[0];
  5388.             }
  5389.             if (item.type < 1 && item.type >= Main.maxItemTypes)
  5390.             {
  5391.                 args.Player.SendErrorMessage("The item type {0} is invalid.", itemNameOrId);
  5392.                 return;
  5393.             }
  5394.  
  5395.             int prefixId = 0;
  5396.             if (amountParamIndex != -1 && args.Parameters.Count > amountParamIndex + 1)
  5397.             {
  5398.                 string prefixidOrName = args.Parameters[amountParamIndex + 1];
  5399.                 var prefixIds = TShock.Utils.GetPrefixByIdOrName(prefixidOrName);
  5400.  
  5401.                 if (item.accessory && prefixIds.Contains(PrefixID.Quick))
  5402.                 {
  5403.                     prefixIds.Remove(PrefixID.Quick);
  5404.                     prefixIds.Remove(PrefixID.Quick2);
  5405.                     prefixIds.Add(PrefixID.Quick2);
  5406.                 }
  5407.                 else if (!item.accessory && prefixIds.Contains(PrefixID.Quick))
  5408.                     prefixIds.Remove(PrefixID.Quick2);
  5409.  
  5410.                 if (prefixIds.Count > 1)
  5411.                 {
  5412.                     args.Player.SendMultipleMatchError(prefixIds.Select(p => p.ToString()));
  5413.                     return;
  5414.                 }
  5415.                 else if (prefixIds.Count == 0)
  5416.                 {
  5417.                     args.Player.SendErrorMessage("No prefix matched \"{0}\".", prefixidOrName);
  5418.                     return;
  5419.                 }
  5420.                 else
  5421.                 {
  5422.                     prefixId = prefixIds[0];
  5423.                 }
  5424.             }
  5425.  
  5426.             if (args.Player.InventorySlotAvailable || (item.type > 70 && item.type < 75) || item.ammo > 0 || item.type == 58 || item.type == 184)
  5427.             {
  5428.                 if (itemAmount == 0 || itemAmount > item.maxStack)
  5429.                     itemAmount = item.maxStack;
  5430.  
  5431.                 if (args.Player.GiveItemCheck(item.type, EnglishLanguage.GetItemNameById(item.type), itemAmount, prefixId))
  5432.                 {
  5433.                     item.prefix = (byte)prefixId;
  5434.                     args.Player.SendSuccessMessage("Gave {0} {1}(s).", itemAmount, item.AffixName());
  5435.                 }
  5436.                 else
  5437.                 {
  5438.                     args.Player.SendErrorMessage("You cannot spawn banned items.");
  5439.                 }
  5440.             }
  5441.             else
  5442.             {
  5443.                 args.Player.SendErrorMessage("Your inventory seems full.");
  5444.             }
  5445.         }
  5446.  
  5447.         private static void RenameNPC(CommandArgs args)
  5448.         {
  5449.             if (args.Parameters.Count != 2)
  5450.             {
  5451.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}renameNPC <guide, nurse, etc.> <newname>", Specifier);
  5452.                 return;
  5453.             }
  5454.             int npcId = 0;
  5455.             if (args.Parameters.Count == 2)
  5456.             {
  5457.                 List<NPC> npcs = TShock.Utils.GetNPCByIdOrName(args.Parameters[0]);
  5458.                 if (npcs.Count == 0)
  5459.                 {
  5460.                     args.Player.SendErrorMessage("Invalid mob type!");
  5461.                     return;
  5462.                 }
  5463.                 else if (npcs.Count > 1)
  5464.                 {
  5465.                     args.Player.SendMultipleMatchError(npcs.Select(n => $"{n.FullName}({n.type})"));
  5466.                     return;
  5467.                 }
  5468.                 else if (args.Parameters[1].Length > 200)
  5469.                 {
  5470.                     args.Player.SendErrorMessage("New name is too large!");
  5471.                     return;
  5472.                 }
  5473.                 else
  5474.                 {
  5475.                     npcId = npcs[0].netID;
  5476.                 }
  5477.             }
  5478.             int done = 0;
  5479.             for (int i = 0; i < Main.npc.Length; i++)
  5480.             {
  5481.                 if (Main.npc[i].active && ((npcId == 0 && !Main.npc[i].townNPC) || (Main.npc[i].netID == npcId && Main.npc[i].townNPC)))
  5482.                 {
  5483.                     Main.npc[i].GivenName = args.Parameters[1];
  5484.                     NetMessage.SendData(56, -1, -1, NetworkText.FromLiteral(args.Parameters[1]), i, 0f, 0f, 0f, 0);
  5485.                     done++;
  5486.                 }
  5487.             }
  5488.             if (done > 0)
  5489.             {
  5490.                 TSPlayer.All.SendInfoMessage("{0} renamed the {1}.", args.Player.Name, args.Parameters[0]);
  5491.             }
  5492.             else
  5493.             {
  5494.                 args.Player.SendErrorMessage("Could not rename {0}!", args.Parameters[0]);
  5495.             }
  5496.         }
  5497.  
  5498.         private static void Give(CommandArgs args)
  5499.         {
  5500.             if (args.Parameters.Count < 2)
  5501.             {
  5502.                 args.Player.SendErrorMessage(
  5503.                     "Invalid syntax! Proper syntax: {0}give <item type/id> <player> [item amount] [prefix id/name]", Specifier);
  5504.                 return;
  5505.             }
  5506.             if (args.Parameters[0].Length == 0)
  5507.             {
  5508.                 args.Player.SendErrorMessage("Missing item name/id.");
  5509.                 return;
  5510.             }
  5511.             if (args.Parameters[1].Length == 0)
  5512.             {
  5513.                 args.Player.SendErrorMessage("Missing player name.");
  5514.                 return;
  5515.             }
  5516.             int itemAmount = 0;
  5517.             int prefix = 0;
  5518.             var items = TShock.Utils.GetItemByIdOrName(args.Parameters[0]);
  5519.             args.Parameters.RemoveAt(0);
  5520.             string plStr = args.Parameters[0];
  5521.             args.Parameters.RemoveAt(0);
  5522.             if (args.Parameters.Count == 1)
  5523.                 int.TryParse(args.Parameters[0], out itemAmount);
  5524.             if (items.Count == 0)
  5525.             {
  5526.                 args.Player.SendErrorMessage("Invalid item type!");
  5527.             }
  5528.             else if (items.Count > 1)
  5529.             {
  5530.                 args.Player.SendMultipleMatchError(items.Select(i => $"{i.Name}({i.netID})"));
  5531.             }
  5532.             else
  5533.             {
  5534.                 var item = items[0];
  5535.  
  5536.                 if (args.Parameters.Count == 2)
  5537.                 {
  5538.                     int.TryParse(args.Parameters[0], out itemAmount);
  5539.                     var prefixIds = TShock.Utils.GetPrefixByIdOrName(args.Parameters[1]);
  5540.                     if (item.accessory && prefixIds.Contains(PrefixID.Quick))
  5541.                     {
  5542.                         prefixIds.Remove(PrefixID.Quick);
  5543.                         prefixIds.Remove(PrefixID.Quick2);
  5544.                         prefixIds.Add(PrefixID.Quick2);
  5545.                     }
  5546.                     else if (!item.accessory && prefixIds.Contains(PrefixID.Quick))
  5547.                         prefixIds.Remove(PrefixID.Quick2);
  5548.                     if (prefixIds.Count == 1)
  5549.                         prefix = prefixIds[0];
  5550.                 }
  5551.  
  5552.                 if (item.type >= 1 && item.type < Main.maxItemTypes)
  5553.                 {
  5554.                     var players = TSPlayer.FindByNameOrID(plStr);
  5555.                     if (players.Count == 0)
  5556.                     {
  5557.                         args.Player.SendErrorMessage("Invalid player!");
  5558.                     }
  5559.                     else if (players.Count > 1)
  5560.                     {
  5561.                         args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  5562.                     }
  5563.                     else
  5564.                     {
  5565.                         var plr = players[0];
  5566.                         if (plr.InventorySlotAvailable || (item.type > 70 && item.type < 75) || item.ammo > 0 || item.type == 58 || item.type == 184)
  5567.                         {
  5568.                             if (itemAmount == 0 || itemAmount > item.maxStack)
  5569.                                 itemAmount = item.maxStack;
  5570.                             if (plr.GiveItemCheck(item.type, EnglishLanguage.GetItemNameById(item.type), itemAmount, prefix))
  5571.                             {
  5572.                                 args.Player.SendSuccessMessage(string.Format("Gave {0} {1} {2}(s).", plr.Name, itemAmount, item.Name));
  5573.                                 plr.SendSuccessMessage(string.Format("{0} gave you {1} {2}(s).", args.Player.Name, itemAmount, item.Name));
  5574.                             }
  5575.                             else
  5576.                             {
  5577.                                 args.Player.SendErrorMessage("You cannot spawn banned items.");
  5578.                             }
  5579.  
  5580.                         }
  5581.                         else
  5582.                         {
  5583.                             args.Player.SendErrorMessage("Player does not have free slots!");
  5584.                         }
  5585.                     }
  5586.                 }
  5587.                 else
  5588.                 {
  5589.                     args.Player.SendErrorMessage("Invalid item type!");
  5590.                 }
  5591.             }
  5592.         }
  5593.  
  5594.         private static void Heal(CommandArgs args)
  5595.         {
  5596.             TSPlayer playerToHeal;
  5597.             if (args.Parameters.Count > 0)
  5598.             {
  5599.                 string plStr = String.Join(" ", args.Parameters);
  5600.                 var players = TSPlayer.FindByNameOrID(plStr);
  5601.                 if (players.Count == 0)
  5602.                 {
  5603.                     args.Player.SendErrorMessage("Invalid player!");
  5604.                     return;
  5605.                 }
  5606.                 else if (players.Count > 1)
  5607.                 {
  5608.                     args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  5609.                     return;
  5610.                 }
  5611.                 else
  5612.                 {
  5613.                     playerToHeal = players[0];
  5614.                 }
  5615.             }
  5616.             else if (!args.Player.RealPlayer)
  5617.             {
  5618.                 args.Player.SendErrorMessage("You can't heal yourself!");
  5619.                 return;
  5620.             }
  5621.             else
  5622.             {
  5623.                 playerToHeal = args.Player;
  5624.             }
  5625.  
  5626.             playerToHeal.Heal();
  5627.             if (playerToHeal == args.Player)
  5628.             {
  5629.                 args.Player.SendSuccessMessage("You just got healed!");
  5630.             }
  5631.             else
  5632.             {
  5633.                 args.Player.SendSuccessMessage(string.Format("You just healed {0}", playerToHeal.Name));
  5634.                 playerToHeal.SendSuccessMessage(string.Format("{0} just healed you!", args.Player.Name));
  5635.             }
  5636.         }
  5637.  
  5638.         private static void Buff(CommandArgs args)
  5639.         {
  5640.             if (args.Parameters.Count < 1 || args.Parameters.Count > 2)
  5641.             {
  5642.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}buff <buff id/name> [time(seconds)]", Specifier);
  5643.                 return;
  5644.             }
  5645.             int id = 0;
  5646.             int time = 60;
  5647.             if (!int.TryParse(args.Parameters[0], out id))
  5648.             {
  5649.                 var found = TShock.Utils.GetBuffByName(args.Parameters[0]);
  5650.                 if (found.Count == 0)
  5651.                 {
  5652.                     args.Player.SendErrorMessage("Invalid buff name!");
  5653.                     return;
  5654.                 }
  5655.                 else if (found.Count > 1)
  5656.                 {
  5657.                     args.Player.SendMultipleMatchError(found.Select(f => Lang.GetBuffName(f)));
  5658.                     return;
  5659.                 }
  5660.                 id = found[0];
  5661.             }
  5662.             if (args.Parameters.Count == 2)
  5663.                 int.TryParse(args.Parameters[1], out time);
  5664.             if (id > 0 && id < Main.maxBuffTypes)
  5665.             {
  5666.                 if (time < 0 || time > short.MaxValue)
  5667.                     time = 60;
  5668.                 args.Player.SetBuff(id, time * 60);
  5669.                 args.Player.SendSuccessMessage(string.Format("You have buffed yourself with {0}({1}) for {2} seconds!",
  5670.                                                       TShock.Utils.GetBuffName(id), TShock.Utils.GetBuffDescription(id), (time)));
  5671.             }
  5672.             else
  5673.                 args.Player.SendErrorMessage("Invalid buff ID!");
  5674.         }
  5675.  
  5676.         private static void GBuff(CommandArgs args)
  5677.         {
  5678.             if (args.Parameters.Count < 2 || args.Parameters.Count > 3)
  5679.             {
  5680.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}gbuff <player> <buff id/name> [time(seconds)]", Specifier);
  5681.                 return;
  5682.             }
  5683.             int id = 0;
  5684.             int time = 60;
  5685.             var foundplr = TSPlayer.FindByNameOrID(args.Parameters[0]);
  5686.             if (foundplr.Count == 0)
  5687.             {
  5688.                 args.Player.SendErrorMessage("Invalid player!");
  5689.                 return;
  5690.             }
  5691.             else if (foundplr.Count > 1)
  5692.             {
  5693.                 args.Player.SendMultipleMatchError(foundplr.Select(p => p.Name));
  5694.                 return;
  5695.             }
  5696.             else
  5697.             {
  5698.                 if (!int.TryParse(args.Parameters[1], out id))
  5699.                 {
  5700.                     var found = TShock.Utils.GetBuffByName(args.Parameters[1]);
  5701.                     if (found.Count == 0)
  5702.                     {
  5703.                         args.Player.SendErrorMessage("Invalid buff name!");
  5704.                         return;
  5705.                     }
  5706.                     else if (found.Count > 1)
  5707.                     {
  5708.                         args.Player.SendMultipleMatchError(found.Select(b => Lang.GetBuffName(b)));
  5709.                         return;
  5710.                     }
  5711.                     id = found[0];
  5712.                 }
  5713.                 if (args.Parameters.Count == 3)
  5714.                     int.TryParse(args.Parameters[2], out time);
  5715.                 if (id > 0 && id < Main.maxBuffTypes)
  5716.                 {
  5717.                     if (time < 0 || time > short.MaxValue)
  5718.                         time = 60;
  5719.                     foundplr[0].SetBuff(id, time * 60);
  5720.                     args.Player.SendSuccessMessage(string.Format("You have buffed {0} with {1}({2}) for {3} seconds!",
  5721.                                                           foundplr[0].Name, TShock.Utils.GetBuffName(id),
  5722.                                                           TShock.Utils.GetBuffDescription(id), (time)));
  5723.                     foundplr[0].SendSuccessMessage(string.Format("{0} has buffed you with {1}({2}) for {3} seconds!",
  5724.                                                           args.Player.Name, TShock.Utils.GetBuffName(id),
  5725.                                                           TShock.Utils.GetBuffDescription(id), (time)));
  5726.                 }
  5727.                 else
  5728.                     args.Player.SendErrorMessage("Invalid buff ID!");
  5729.             }
  5730.         }
  5731.  
  5732.         private static void Grow(CommandArgs args)
  5733.         {
  5734.             if (args.Parameters.Count != 1)
  5735.             {
  5736.                 args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}grow <tree/epictree/mushroom/cactus/herb>", Specifier);
  5737.                 return;
  5738.             }
  5739.             var name = "Fail";
  5740.             var x = args.Player.TileX;
  5741.             var y = args.Player.TileY + 3;
  5742.  
  5743.             if (!TShock.Regions.CanBuild(x, y, args.Player))
  5744.             {
  5745.                 args.Player.SendErrorMessage("You're not allowed to change tiles here!");
  5746.                 return;
  5747.             }
  5748.  
  5749.             switch (args.Parameters[0].ToLower())
  5750.             {
  5751.                 case "tree":
  5752.                     for (int i = x - 1; i < x + 2; i++)
  5753.                     {
  5754.                         Main.tile[i, y].active(true);
  5755.                         Main.tile[i, y].type = 2;
  5756.                         Main.tile[i, y].wall = 0;
  5757.                     }
  5758.                     Main.tile[x, y - 1].wall = 0;
  5759.                     WorldGen.GrowTree(x, y);
  5760.                     name = "Tree";
  5761.                     break;
  5762.                 case "epictree":
  5763.                     for (int i = x - 1; i < x + 2; i++)
  5764.                     {
  5765.                         Main.tile[i, y].active(true);
  5766.                         Main.tile[i, y].type = 2;
  5767.                         Main.tile[i, y].wall = 0;
  5768.                     }
  5769.                     Main.tile[x, y - 1].wall = 0;
  5770.                     Main.tile[x, y - 1].liquid = 0;
  5771.                     Main.tile[x, y - 1].active(true);
  5772.                     WorldGen.GrowEpicTree(x, y);
  5773.                     name = "Epic Tree";
  5774.                     break;
  5775.                 case "mushroom":
  5776.                     for (int i = x - 1; i < x + 2; i++)
  5777.                     {
  5778.                         Main.tile[i, y].active(true);
  5779.                         Main.tile[i, y].type = 70;
  5780.                         Main.tile[i, y].wall = 0;
  5781.                     }
  5782.                     Main.tile[x, y - 1].wall = 0;
  5783.                     WorldGen.GrowShroom(x, y);
  5784.                     name = "Mushroom";
  5785.                     break;
  5786.                 case "cactus":
  5787.                     Main.tile[x, y].type = 53;
  5788.                     WorldGen.GrowCactus(x, y);
  5789.                     name = "Cactus";
  5790.                     break;
  5791.                 case "herb":
  5792.                     Main.tile[x, y].active(true);
  5793.                     Main.tile[x, y].frameX = 36;
  5794.                     Main.tile[x, y].type = 83;
  5795.                     WorldGen.GrowAlch(x, y);
  5796.                     name = "Herb";
  5797.                     break;
  5798.                 default:
  5799.                     args.Player.SendErrorMessage("Unknown plant!");
  5800.                     return;
  5801.             }
  5802.             args.Player.SendTileSquare(x, y);
  5803.             args.Player.SendSuccessMessage("Tried to grow a " + name + ".");
  5804.         }
  5805.  
  5806.         private static void ToggleGodMode(CommandArgs args)
  5807.         {
  5808.             TSPlayer playerToGod;
  5809.             if (args.Parameters.Count > 0)
  5810.             {
  5811.                 if (!args.Player.HasPermission(Permissions.godmodeother))
  5812.                 {
  5813.                     args.Player.SendErrorMessage("You do not have permission to god mode another player!");
  5814.                     return;
  5815.                 }
  5816.                 string plStr = String.Join(" ", args.Parameters);
  5817.                 var players = TSPlayer.FindByNameOrID(plStr);
  5818.                 if (players.Count == 0)
  5819.                 {
  5820.                     args.Player.SendErrorMessage("Invalid player!");
  5821.                     return;
  5822.                 }
  5823.                 else if (players.Count > 1)
  5824.                 {
  5825.                     args.Player.SendMultipleMatchError(players.Select(p => p.Name));
  5826.                     return;
  5827.                 }
  5828.                 else
  5829.                 {
  5830.                     playerToGod = players[0];
  5831.                 }
  5832.             }
  5833.             else if (!args.Player.RealPlayer)
  5834.             {
  5835.                 args.Player.SendErrorMessage("You can't god mode a non player!");
  5836.                 return;
  5837.             }
  5838.             else
  5839.             {
  5840.                 playerToGod = args.Player;
  5841.             }
  5842.  
  5843.             playerToGod.GodMode = !playerToGod.GodMode;
  5844.  
  5845.             if (playerToGod == args.Player)
  5846.             {
  5847.                 args.Player.SendSuccessMessage(string.Format("You are {0} in god mode.", args.Player.GodMode ? "now" : "no longer"));
  5848.             }
  5849.             else
  5850.             {
  5851.                 args.Player.SendSuccessMessage(string.Format("{0} is {1} in god mode.", playerToGod.Name, playerToGod.GodMode ? "now" : "no longer"));
  5852.                 playerToGod.SendSuccessMessage(string.Format("You are {0} in god mode.", playerToGod.GodMode ? "now" : "no longer"));
  5853.             }
  5854.         }
  5855.  
  5856.         #endregion Cheat Comamnds
  5857.     }
  5858. }
Add Comment
Please, Sign In to add comment