PageRenderTime 37ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 1ms

/fCraft/Commands/ModerationCommands.cs

https://github.com/EpicClowny/LegendCraft
C# | 3299 lines | 2850 code | 362 blank | 87 comment | 625 complexity | 0e6de37676c89544b295c56f0c87daf7 MD5 | raw file
  1. // Copyright 2009-2012 Matvei Stefarov <me@matvei.org>
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Net;
  6. using fCraft.Events;
  7. using JetBrains.Annotations;
  8. using System.IO;
  9. namespace fCraft {
  10. /// <summary>
  11. /// Most commands for server moderation - kick, ban, rank change, etc - are here.
  12. /// </summary>
  13. static class ModerationCommands {
  14. const string BanCommonHelp = "Ban information can be viewed with &H/BanInfo";
  15. internal static void Init() {
  16. CdBan.Help += BanCommonHelp;
  17. CdBanIP.Help += BanCommonHelp;
  18. CdBanAll.Help += BanCommonHelp;
  19. CdUnban.Help += BanCommonHelp;
  20. CdUnbanIP.Help += BanCommonHelp;
  21. CdUnbanAll.Help += BanCommonHelp;
  22. CommandManager.RegisterCommand( CdBan );
  23. CommandManager.RegisterCommand( CdBanIP );
  24. CommandManager.RegisterCommand( CdUnban );
  25. CommandManager.RegisterCommand( CdUnbanIP );
  26. CommandManager.RegisterCommand( CdUnbanAll );
  27. CommandManager.RegisterCommand( CdBanEx );
  28. CommandManager.RegisterCommand( CdKick );
  29. CommandManager.RegisterCommand( CdRank );
  30. CommandManager.RegisterCommand( CdHide );
  31. CommandManager.RegisterCommand( CdUnhide );
  32. CommandManager.RegisterCommand( CdSetSpawn );
  33. CommandManager.RegisterCommand( CdFreeze );
  34. CommandManager.RegisterCommand( CdUnfreeze );
  35. CommandManager.RegisterCommand( CdTP );
  36. CommandManager.RegisterCommand( CdBring );
  37. CommandManager.RegisterCommand( CdWorldBring );
  38. CommandManager.RegisterCommand( CdBringAll );
  39. CommandManager.RegisterCommand( CdPatrol );
  40. CommandManager.RegisterCommand( CdSpecPatrol );
  41. CommandManager.RegisterCommand( CdMute );
  42. CommandManager.RegisterCommand( CdUnmute );
  43. CommandManager.RegisterCommand( CdSpectate );
  44. CommandManager.RegisterCommand( CdUnspectate );
  45. CommandManager.RegisterCommand( CdSlap );
  46. CommandManager.RegisterCommand( CdTPZone );
  47. CommandManager.RegisterCommand( CdBasscannon );
  48. CommandManager.RegisterCommand( CdKill );
  49. CommandManager.RegisterCommand( CdTempBan );
  50. CommandManager.RegisterCommand( CdWarn );
  51. CommandManager.RegisterCommand( CdUnWarn );
  52. CommandManager.RegisterCommand( CdDisconnect );
  53. CommandManager.RegisterCommand( CdImpersonate );
  54. CommandManager.RegisterCommand( CdImmortal );
  55. CommandManager.RegisterCommand( CdTitle );
  56. CommandManager.RegisterCommand( CdMuteAll );
  57. CommandManager.RegisterCommand( CdAssassinate );
  58. CommandManager.RegisterCommand( CdPunch );
  59. CommandManager.RegisterCommand( CdBanAll );
  60. CommandManager.RegisterCommand( CdEconomy );
  61. CommandManager.RegisterCommand( CdPay );
  62. CommandManager.RegisterCommand(CdBanGrief);
  63. CommandManager.RegisterCommand(CdStealthKick);
  64. CommandManager.RegisterCommand(CdBeatDown);
  65. CommandManager.RegisterCommand(CdLastCommand);
  66. CommandManager.RegisterCommand(CdFreezeBring);
  67. CommandManager.RegisterCommand(CdTempRank);
  68. CommandManager.RegisterCommand(CdSetClickDistance);
  69. CommandManager.RegisterCommand(CdAutoRank);
  70. CommandManager.RegisterCommand(CdAnnounce);
  71. CommandManager.RegisterCommand(CdForceHold);
  72. CommandManager.RegisterCommand(CdGetBlock);
  73. //CommandManager.RegisterCommand(CdTPA);
  74. }
  75. #region LegendCraft
  76. /* Copyright (c) <2012-2014> <LeChosenOne, DingusBungus>
  77. Permission is hereby granted, free of charge, to any person obtaining a copy
  78. of this software and associated documentation files (the "Software"), to deal
  79. in the Software without restriction, including without limitation the rights
  80. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  81. copies of the Software, and to permit persons to whom the Software is
  82. furnished to do so, subject to the following conditions:
  83. The above copyright notice and this permission notice shall be included in
  84. all copies or substantial portions of the Software.
  85. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  86. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  87. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  88. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  89. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  90. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  91. THE SOFTWARE.*/
  92. static readonly CommandDescriptor CdGetBlock = new CommandDescriptor
  93. {
  94. Name = "GetBlock",
  95. IsConsoleSafe = true,
  96. Category = CommandCategory.Moderation,
  97. Permissions = new[] { Permission.Spectate },
  98. Help = "&SReturns the block that the player is currently holding.",
  99. Usage = "&S/GetBlock [player]",
  100. Handler = GetBlockHandler
  101. };
  102. private static void GetBlockHandler(Player player, Command cmd)
  103. {
  104. string target = cmd.Next();
  105. if (String.IsNullOrEmpty(target))
  106. {
  107. CdGetBlock.PrintUsage(player);
  108. return;
  109. }
  110. Player targetPlayer = Server.FindPlayerOrPrintMatches(player, target, false, true);
  111. if (targetPlayer == null)
  112. {
  113. return;
  114. }
  115. player.Message("{0} is currently holding {1}.", targetPlayer.Name, targetPlayer.Info.HeldBlock.ToString() );
  116. }
  117. static readonly CommandDescriptor CdForceHold = new CommandDescriptor
  118. {
  119. Name = "ForceHold",
  120. IsConsoleSafe = false,
  121. Category = CommandCategory.Moderation,
  122. Permissions = new[] { Permission.Spectate },
  123. Help = "&SForces a player to hold a certain block. Only use when needed!",
  124. Usage = "&S/ForceHold [player] [block]",
  125. Handler = FHHandler
  126. };
  127. private static void FHHandler(Player player, Command cmd)
  128. {
  129. if (!Heartbeat.ClassiCube() || !player.ClassiCube)
  130. {
  131. player.Message("This is a ClassiCube only command!");
  132. return;
  133. }
  134. string target = cmd.Next();
  135. if (String.IsNullOrEmpty(target))
  136. {
  137. CdForceHold.PrintUsage(player);
  138. return;
  139. }
  140. Player p = Server.FindPlayerOrPrintMatches(player, target, false, true);
  141. if (p == null)
  142. {
  143. return;
  144. }
  145. string blockStr = cmd.Next();
  146. if (String.IsNullOrEmpty(blockStr))
  147. {
  148. CdForceHold.PrintUsage(player);
  149. return;
  150. }
  151. //format blockname to be "Stone" instead of "STONE" or "stone"
  152. blockStr = blockStr.Substring(0, 1).ToUpper() + blockStr.Substring(1).ToLower();
  153. Block block;
  154. try
  155. {
  156. block = (Block)Enum.Parse(typeof(Block), blockStr);
  157. }
  158. catch
  159. {
  160. player.Message("Sorry, that was not a valid block name!");
  161. return;
  162. }
  163. p.Send(PacketWriter.MakeHoldThis((byte)block, false));
  164. }
  165. static readonly CommandDescriptor CdAnnounce = new CommandDescriptor //todo: make this work lol
  166. {
  167. Name = "Announce",
  168. IsConsoleSafe = true,
  169. Category = CommandCategory.Moderation,
  170. Permissions = new[] { Permission.Say },
  171. Help = "&SAnnounces a message at the top of every player's screen on a specified world. To send a server-wide announcement, use /Announce all [message].",
  172. Usage = "&S/Announce [world] [message]",
  173. Handler = AnnounceHandler
  174. };
  175. private static void AnnounceHandler(Player player, Command cmd)
  176. {
  177. if (!Heartbeat.ClassiCube() || !player.ClassiCube)
  178. {
  179. player.Message("This is a ClassiCube only command!");
  180. return;
  181. }
  182. Player[] targetPlayers;
  183. string world = cmd.Next();
  184. if(string.IsNullOrEmpty(world))
  185. {
  186. CdAnnounce.PrintUsage(player);
  187. return;
  188. }
  189. if (world == "all")
  190. {
  191. targetPlayers = Server.Players;
  192. }
  193. else
  194. {
  195. World[] targetWorlds = WorldManager.FindWorlds(player, world);
  196. if (targetWorlds.Length > 1)
  197. {
  198. player.MessageManyMatches("world", targetWorlds);
  199. return;
  200. }
  201. else if (targetWorlds.Length == 1)
  202. {
  203. targetPlayers = targetWorlds[0].Players;
  204. }
  205. else
  206. {
  207. player.Message("No worlds found matching {0}! Please check your spelling.", world);
  208. return;
  209. }
  210. }
  211. string message = cmd.Next();
  212. if (string.IsNullOrEmpty(message))
  213. {
  214. CdAnnounce.PrintUsage(player);
  215. return;
  216. }
  217. Packet packet = PacketWriter.MakeSpecialMessage(100, message);
  218. foreach (Player p in targetPlayers)
  219. {
  220. p.Send(packet);
  221. }
  222. }
  223. static readonly CommandDescriptor CdAutoRank = new CommandDescriptor
  224. {
  225. Name = "AutoRank",
  226. IsConsoleSafe = true,
  227. Category = CommandCategory.Moderation,
  228. Permissions = new[] { Permission.EditPlayerDB },
  229. Help = "&SManagement commands for running Autorank. Type '/help AutoRank [option]' for details.",
  230. Usage = "&S/AutoRank [enable/disable/force/exempt/unexempt]",
  231. HelpSections = new Dictionary<string, string>{
  232. { "enable", "&H/AutoRank enable\n&S" +
  233. "Enables the autorank system on the server." },
  234. { "disable", "&H/AutoRank disable\n&S" +
  235. "Disables the autorank system on the server." },
  236. { "force", "&H/AutoRank force\n&S" +
  237. "Forces the server to check all online players if they are due for an autorank. Works even if autorank is disabled."},
  238. { "exempt", "&H/AutoRank exempt [player]\n&S" +
  239. "Exempts a player from autorank. Even if the player is due for an autorank promotion or demotion, they will not be ranked."},
  240. { "unexempt", "&H/AutoRank unexempt [player]\n&S" +
  241. "Lifts the exemption on the target player. Target player can now be autoranked." },
  242. },
  243. Handler = AutoRankHandler
  244. };
  245. private static void AutoRankHandler(Player player, Command cmd)
  246. {
  247. string option = cmd.Next();
  248. if (string.IsNullOrEmpty(option))
  249. {
  250. CdAutoRank.PrintUsage(player);
  251. return;
  252. }
  253. switch (option)
  254. {
  255. case "enable":
  256. if (Server.AutoRankEnabled)
  257. {
  258. player.Message("AutoRank is already enabled!");
  259. break;
  260. }
  261. Server.AutoRankEnabled = true;
  262. player.Message("AutoRank is now enabled on {0}!", ConfigKey.ServerName.GetString());
  263. break;
  264. case "disable":
  265. if (!Server.AutoRankEnabled)
  266. {
  267. player.Message("AutoRank is already disabled!");
  268. break;
  269. }
  270. Server.AutoRankEnabled = false;
  271. player.Message("AutoRank is now disabled on {0}!", ConfigKey.ServerName.GetString());
  272. break;
  273. case "force":
  274. player.Message("Checking for online players to force autorank...");
  275. //refresh xml incase host updated autorank script, and then check each player
  276. AutoRank.AutoRankManager.Load();
  277. foreach (Player p in Server.Players)
  278. {
  279. AutoRank.AutoRankManager.Check(p);
  280. }
  281. player.Message("AutoRank force check finished.");
  282. break;
  283. case "exempt":
  284. string target = cmd.Next();
  285. if (string.IsNullOrEmpty(target))
  286. {
  287. CdAutoRank.PrintUsage(player);
  288. break;
  289. }
  290. Player targetPlayer = Server.FindPlayerOrPrintMatches(player, target, false, true);
  291. if (targetPlayer == null)
  292. {
  293. return;
  294. }
  295. targetPlayer.IsAutoRankExempt = true;
  296. player.Message("{0} is now exempt from autorank rank changes.", targetPlayer.Name);
  297. break;
  298. case "unexempt":
  299. string target_ = cmd.Next();
  300. if (string.IsNullOrEmpty(target_))
  301. {
  302. CdAutoRank.PrintUsage(player);
  303. break;
  304. }
  305. Player targetPlayer_ = Server.FindPlayerOrPrintMatches(player, target_, false, true);
  306. if (targetPlayer_ == null)
  307. {
  308. return;
  309. }
  310. targetPlayer_.IsAutoRankExempt = true;
  311. player.Message("{0} is now unexempt from autorank rank changes.", targetPlayer_.Name);
  312. break;
  313. default:
  314. CdAutoRank.PrintUsage(player);
  315. break;
  316. }
  317. }
  318. static readonly CommandDescriptor CdSetClickDistance = new CommandDescriptor
  319. {
  320. Name = "SetClickDistance",
  321. Aliases = new[] { "setclick", "clickdistance", "click", "scd" },
  322. IsConsoleSafe = false,
  323. Category = CommandCategory.Moderation,
  324. Permissions = new[] { Permission.Promote },
  325. Help = "&SSets the clicking distance of a player. NOTE: Block distances are approximate (+/- 1)",
  326. Usage = "&S/SetClick (number of blocks)",
  327. Handler = SetClickHandler
  328. };
  329. private static void SetClickHandler(Player player, Command cmd)
  330. {
  331. string targetName = cmd.Next();
  332. if (String.IsNullOrEmpty(targetName))
  333. {
  334. CdSetClickDistance.PrintUsage(player);
  335. return;
  336. }
  337. Player target = Server.FindPlayerOrPrintMatches(player, targetName, false, true);
  338. if (target == null)
  339. {
  340. player.MessageNoPlayer(targetName);
  341. return;
  342. }
  343. if (!target.ClassiCube)
  344. {
  345. player.Message("Click distance can only be changed for people playing on ClassiCube");
  346. return;
  347. }
  348. string number = cmd.Next();
  349. if (number == "normal")
  350. {
  351. target.Send(PacketWriter.MakeSetClickDistance(160));
  352. player.Message("Player {0}&s's click distance was set to normal.", target.ClassyName);
  353. target.hasCustomClickDistance = false;
  354. return;
  355. }
  356. int distance;
  357. if (String.IsNullOrEmpty(number) || !Int32.TryParse(number, out distance))
  358. {
  359. CdSetClickDistance.PrintUsage(player);
  360. return;
  361. }
  362. if (distance > 20 || distance < 1)
  363. {
  364. player.Message("Accepted values are between 1 and 20!");
  365. return;
  366. }
  367. int adjustedDistance = (32 * distance);
  368. target.Send(PacketWriter.MakeSetClickDistance(adjustedDistance));
  369. target.hasCustomClickDistance = true;
  370. player.Message("Player {0}&s's click distance was set to {1} blocks.", target.ClassyName, distance);
  371. }
  372. static readonly CommandDescriptor CdTempRank = new CommandDescriptor
  373. {
  374. Name = "TempRank",
  375. Aliases = new[] { "tRank"},
  376. IsConsoleSafe = true,
  377. Category = CommandCategory.Moderation,
  378. Permissions = new[] { Permission.Promote },
  379. Help = "&SRanks a player for a limited amount of time. Tempranks are reset upon server restart or shutdown.",
  380. Usage = "&S/TempRank [player] [rank] [duration] [reason]",
  381. Handler = TempRankHandler
  382. };
  383. static void TempRankHandler(Player player, Command cmd)
  384. {
  385. string target = cmd.Next();
  386. string rank = cmd.Next();
  387. string rankTime = cmd.Next();
  388. TimeSpan rankDuration;
  389. string reason = cmd.Next();
  390. if(String.IsNullOrEmpty(target))
  391. {
  392. player.Message("&SPlease input a player's name to temprank.");
  393. return;
  394. }
  395. if (String.IsNullOrEmpty(rank))
  396. {
  397. player.Message("&SPlease input your desired rank.");
  398. return;
  399. }
  400. if (String.IsNullOrEmpty(rankTime))
  401. {
  402. player.Message("&SPlease input the duration of the temprank.");
  403. return;
  404. }
  405. if (String.IsNullOrEmpty(reason))
  406. {
  407. player.Message("&SPlease input the reason for the temprank.");
  408. return;
  409. }
  410. if (!rankTime.TryParseMiniTimespan(out rankDuration) || rankDuration <= TimeSpan.Zero)
  411. {
  412. player.Message("&SThe time must be an integer greater than 0!");
  413. return;
  414. }
  415. Rank targetRank = RankManager.FindRank(rank);
  416. if (targetRank == null)
  417. {
  418. player.MessageNoRank(rank);
  419. return;
  420. }
  421. PlayerInfo targetInfo = PlayerDB.FindPlayerInfoExact(target);
  422. if (targetInfo == null)
  423. {
  424. player.Message("&SThe player, {0}&S could not be found. Please check the spelling of the player.", target);
  425. }
  426. targetInfo.ChangeRank(player, targetRank, "TempRank(" + rankTime + "): " + reason , true, true, false);
  427. targetInfo.isTempRanked = true;
  428. targetInfo.tempRankTime = rankDuration;
  429. }
  430. static readonly CommandDescriptor CdTPA = new CommandDescriptor
  431. {
  432. Name = "TPA",
  433. Aliases = new[] { "TeleportAsk"},
  434. IsConsoleSafe = false,
  435. Category = CommandCategory.Moderation,
  436. Permissions = new[] { Permission.TPA },
  437. Help = "&SAsks a player permission to teleport to them before teleporting.",
  438. Usage = "&S/Tpa player",
  439. Handler = TPAHandler
  440. };
  441. static void TPAHandler(Player player, Command cmd)
  442. {
  443. string name = cmd.Next();
  444. Player player_ = player;
  445. if (String.IsNullOrEmpty(name))
  446. {
  447. CdTPA.PrintUsage(player);
  448. return;
  449. }
  450. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  451. if (target == null)
  452. {
  453. return;
  454. }
  455. else
  456. {
  457. if (!cmd.IsConfirmed)
  458. {
  459. target.Confirm(cmd, player.ClassyName + "&S would like to teleport to you.");
  460. }
  461. else
  462. {
  463. //Big error here. Both of the following messages are sent to the target, and the target teleports towards itself.
  464. player.Message("&STeleporting you to {0}...", name);
  465. target.ParseMessage("/tp " + name, false, false);
  466. return;
  467. }
  468. }
  469. }
  470. static readonly CommandDescriptor CdFreezeBring = new CommandDescriptor
  471. {
  472. Name = "FreezeBring",
  473. Aliases = new[] { "fBring" , "fSummon", "fb" },
  474. IsConsoleSafe = false,
  475. Category = CommandCategory.Moderation,
  476. Permissions = new[] { Permission.Freeze },
  477. Help = "&SFreezes and summons a player to your location. Usefull for griefers.",
  478. Usage = "&S/FreezeBring player",
  479. Handler = FBHandler
  480. };
  481. static void FBHandler(Player player, Command cmd)
  482. {
  483. string name = cmd.Next();
  484. if (String.IsNullOrEmpty(name))
  485. {
  486. CdFreezeBring.PrintUsage(player);
  487. return;
  488. }
  489. else
  490. {
  491. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  492. if (target == null)
  493. {
  494. return;
  495. }
  496. else
  497. {
  498. player.ParseMessage("/summon " + name, false, false); //to lazy to change target's coords, so ill just summon
  499. target.Info.IsFrozen = true;
  500. target.Message("&SYou have been frozen by {0}", player.ClassyName);
  501. return;
  502. }
  503. }
  504. }
  505. static readonly CommandDescriptor CdLastCommand = new CommandDescriptor
  506. {
  507. Name = "LastCommand",
  508. Aliases = new[] { "LastCmd", "last" },
  509. IsConsoleSafe = true,
  510. Category = CommandCategory.Moderation,
  511. Permissions = new[] { Permission.Spectate },
  512. Help = "Checks the last command used by a player. Leave the player parameter blank for your last command.",
  513. Usage = "/LastCommand (player)",
  514. Handler = LastCommandHandler
  515. };
  516. static void LastCommandHandler(Player player, Command cmd) //allows the user to see the last command a target player has used
  517. {
  518. string target = cmd.Next();
  519. if (String.IsNullOrEmpty(target))
  520. {
  521. if (player.LastCommand == null)
  522. {
  523. player.Message("You do not have a last command");
  524. CdLastCommand.PrintUsage(player);
  525. return;
  526. }
  527. string sub = player.LastCommand.ToString().Split('"')[1].Split('"')[0];
  528. player.Message("Your last command used was: " + sub);
  529. return;
  530. }
  531. Player targetName = Server.FindPlayerOrPrintMatches(player, target, false, true);
  532. if (targetName == null)
  533. {
  534. player.Message("No player found matching " + target);
  535. return;
  536. }
  537. else
  538. {
  539. string sub = targetName.LastCommand.ToString().Split('"')[1].Split('"')[0];
  540. player.Message(targetName.Name + "'s last command was " + sub);
  541. return;
  542. }
  543. }
  544. static readonly CommandDescriptor CdBeatDown = new CommandDescriptor
  545. {
  546. Name = "Beatdown",
  547. IsConsoleSafe = true,
  548. NotRepeatable = true,
  549. Aliases = new[] { "ground", "bd", "pummel" },
  550. Category = CommandCategory.Moderation | CommandCategory.Fun,
  551. Permissions = new[] { Permission.Basscannon },
  552. Help = "Pummels a player into the ground. " +
  553. "Available items are: dog, hammer, sack, pistol, curb and soap.",
  554. Usage = "/Beatdown <playername> [item]",
  555. Handler = BeatDownHandler
  556. };
  557. static void BeatDownHandler(Player player, Command cmd) //a more brutal /slap cmd, sends the player underground to the bottom of the world
  558. {
  559. string name = cmd.Next();
  560. string item = cmd.Next();
  561. if (name == null)
  562. {
  563. player.Message("Please enter a name");
  564. return;
  565. }
  566. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  567. if (target == null) return;
  568. if (target.Immortal)
  569. {
  570. player.Message("&SYou failed to beatdown {0}&S, they are immortal", target.ClassyName);
  571. return;
  572. }
  573. if (target == player)
  574. {
  575. player.Message("&sYou can't pummel yourself.... What's wrong with you???");
  576. return;
  577. }
  578. double time = (DateTime.Now - player.Info.LastUsedBeatDown).TotalSeconds;
  579. double timeLeft = Math.Round(20 - time);
  580. if (time < 20)
  581. {
  582. player.Message("&WYou can use /BeatDown again in " + timeLeft + " seconds.");
  583. return;
  584. }
  585. string aMessage;
  586. if (player.Can(Permission.Basscannon, target.Info.Rank))
  587. {
  588. Position pummeled = new Position(target.Position.X, target.Position.Y, (target.World.Map.Bounds.ZMin) * 32);
  589. target.previousLocation = target.Position;
  590. target.previousWorld = null;
  591. target.TeleportTo(pummeled);
  592. if (string.IsNullOrEmpty(item))
  593. {
  594. Server.Players.CanSee(target).Union(target).Message("{0} &Swas pummeled into the ground by {1}", target.ClassyName, player.ClassyName);
  595. target.Message("Do &a/Spawn&s to get back above ground.");
  596. IRC.PlayerSomethingMessage(player, "beat down", target, null);
  597. player.Info.LastUsedBeatDown = DateTime.Now;
  598. return;
  599. }
  600. else if (item.ToLower() == "hammer")
  601. aMessage = String.Format("{1}&S Beat Down {0}&S with a Hammer", target.ClassyName, player.ClassyName);
  602. else if (item.ToLower() == "sack")
  603. aMessage = String.Format("{1}&s pummeled {0}&S with a Sack of Potatoes", target.ClassyName, player.ClassyName);
  604. else if (item.ToLower() == "soap")
  605. aMessage = String.Format("{1}&S pummeled {0}&s with Socks Full of Soap", target.ClassyName, player.ClassyName);
  606. else if (item.ToLower() == "pistol")
  607. aMessage = String.Format("{1}&S Pistol-Whipped {0}", target.ClassyName, player.ClassyName);
  608. else if (item.ToLower() == "curb")
  609. aMessage = String.Format("{1}&S Curb-Stomped {0}", target.ClassyName, player.ClassyName);
  610. else if (item.ToLower() == "dog")
  611. aMessage = String.Format("{1}&S Beat Down {0}&S like a dog", target.ClassyName, player.ClassyName);
  612. else
  613. {
  614. Server.Players.CanSee(target).Union(target).Message("{0} &Swas pummeled into the ground by {1}", target.ClassyName, player.ClassyName);
  615. target.Message("Do &a/Spawn&s to get back above ground.");
  616. IRC.PlayerSomethingMessage(player, "beat down", target, null);
  617. player.Info.LastUsedBeatDown = DateTime.Now;
  618. return;
  619. }
  620. Server.Players.CanSee(target).Union(target).Message(aMessage);
  621. target.Message("Do &a/Spawn&s to get back above ground.");
  622. IRC.PlayerSomethingMessage(player, "beat down", target, null);
  623. player.Info.LastUsedBeatDown = DateTime.Now;
  624. return;
  625. }
  626. else
  627. {
  628. player.Message("&sYou can only Beat Down players ranked {0}&S or lower",
  629. player.Info.Rank.GetLimit(Permission.Basscannon).ClassyName);
  630. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName);
  631. }
  632. }
  633. static readonly CommandDescriptor CdStealthKick = new CommandDescriptor
  634. {
  635. Name = "StealthKick",
  636. Aliases = new[] { "sk", "stealthk" },
  637. Category = CommandCategory.Moderation,
  638. IsConsoleSafe = true,
  639. Permissions = new[] { Permission.Gtfo },
  640. Usage = "/StealthKick (playername)",
  641. Help = "&SKicks a player stealthily. The kick will say the player disconnected and will not save to the playerDB.",
  642. Handler = StealthKickHandler
  643. };
  644. internal static void StealthKickHandler(Player player, Command cmd)
  645. {
  646. string name = cmd.Next();
  647. if (name == null)
  648. {
  649. player.Message("Please enter a name");
  650. return;
  651. }
  652. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  653. if (target == null) return;
  654. if (target == player)
  655. {
  656. player.Message("You cannot StealthKick yourself.");
  657. return;
  658. }
  659. if (cmd.HasNext)
  660. {
  661. player.Message("A reason does not need to be specified when using StealthKick.");
  662. }
  663. if (player.Can(Permission.Gtfo, target.Info.Rank))
  664. {
  665. Player targetPlayer = target;
  666. target.Send(PacketWriter.MakeDisconnect("You've lost connection to the server."));
  667. }
  668. else
  669. {
  670. player.Message("You can only StealthKick players ranked {0}&S or lower",
  671. player.Info.Rank.GetLimit(Permission.Gtfo).ClassyName);
  672. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName);
  673. }
  674. }
  675. static CommandDescriptor CdBanGrief = new CommandDescriptor
  676. {
  677. Name = "BanGrief",
  678. Category = CommandCategory.Moderation,
  679. Aliases = new[] { "Bg" },
  680. IsConsoleSafe = true,
  681. Permissions = new[] { Permission.Ban },
  682. NotRepeatable = true,
  683. Usage = "/BanGrief Playername",
  684. Help = "Bans the player with the reason \"Grief, appeal at <website>\"",
  685. Handler = BanGriefHandler
  686. };
  687. static void BanGriefHandler(Player player, Command cmd)
  688. {
  689. string targetName = cmd.Next();
  690. if (targetName == null)
  691. {
  692. CdBanGrief.PrintUsage(player);
  693. return;
  694. }
  695. PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches(player, targetName);
  696. if (target == null) return;
  697. string reason = "Grief, appeal at " + ConfigKey.WebsiteURL.GetString();
  698. try
  699. {
  700. Player targetPlayer = target.PlayerObject;
  701. target.Ban(player, reason, true, true);
  702. WarnIfOtherPlayersOnIP(player, target, targetPlayer);
  703. }
  704. catch (PlayerOpException ex)
  705. {
  706. player.Message(ex.MessageColored);
  707. if (ex.ErrorCode == PlayerOpExceptionCode.ReasonRequired)
  708. {
  709. FreezeIfAllowed(player, target);
  710. }
  711. }
  712. }
  713. #region Economy
  714. static readonly CommandDescriptor CdPay = new CommandDescriptor
  715. {
  716. Name = "Pay",
  717. Aliases = new[] { "Purchase" },
  718. Category = CommandCategory.Moderation,
  719. IsConsoleSafe = false,
  720. Permissions = new[] { Permission.Economy },
  721. Usage = "/pay player amount",
  722. Help = "&SUsed to pay a certain player an amount of bits.",
  723. Handler = PayHandler
  724. };
  725. static void PayHandler(Player player, Command cmd)
  726. {
  727. string targetName = cmd.Next();
  728. string amount = cmd.Next();
  729. int amountnum;
  730. //lotsa idiot proofing in this one ^.^
  731. if (targetName == null)
  732. {
  733. player.Message("&ePlease type in a player's name to pay bits towards.");
  734. return;
  735. }
  736. Player target = Server.FindPlayerOrPrintMatches(player, targetName, false, true);
  737. if (target == player)
  738. {
  739. player.Message("You can't pay yourself >.> Doesn't work like that.");
  740. return;
  741. }
  742. if (target == null)
  743. {
  744. return;
  745. }
  746. else
  747. {
  748. if (!int.TryParse(amount, out amountnum))
  749. {
  750. player.Message("&eThe amount must be a positive whole number!");
  751. return;
  752. }
  753. if (amountnum < 1)
  754. {
  755. player.Message("&eThe amount must be a positive whole number!");
  756. return;
  757. }
  758. if (cmd.IsConfirmed)
  759. {
  760. if (amountnum > player.Info.Money)
  761. {
  762. player.Message("You don't have that many bits!");
  763. return;
  764. }
  765. else
  766. {
  767. //show him da monai
  768. int pNewMoney = player.Info.Money - amountnum;
  769. int tNewMoney = target.Info.Money + amountnum;
  770. player.Message("&eYou have paid &C{1}&e to {0}&e.", target.ClassyName, amountnum);
  771. target.Message("&e{0} &ehas paid you {1} &ebit(s).", player.ClassyName, amountnum);
  772. Server.Players.Except(target).Except(player).Message("&e{0} &ewas paid {1} &ebit(s) from {2}&e.", target.ClassyName, amountnum, player.ClassyName);
  773. player.Info.Money = pNewMoney;
  774. target.Info.Money = tNewMoney;
  775. return;
  776. }
  777. }
  778. else
  779. {
  780. player.Confirm(cmd, "&eAre you sure you want to pay {0}&e {1} &ebits?", target.ClassyName, amountnum);
  781. return;
  782. }
  783. }
  784. }
  785. static readonly CommandDescriptor CdEconomy = new CommandDescriptor
  786. {
  787. Name = "Economy",
  788. Aliases = new[] { "Money", "Econ" },
  789. Category = CommandCategory.Moderation,
  790. IsConsoleSafe = false,
  791. Permissions = new[] { Permission.Economy },
  792. Usage = "/Economy [give/take/show/pay] [playername] [pay/give/take: amount]",
  793. Help = "&SEconomy commands for LegendCraft. Show will show you the amount of money a player has" +
  794. "and give/take will give or take bits from or to a player. WARNING, give and take will change your server's inflation.",
  795. Handler = EconomyHandler
  796. };
  797. static void EconomyHandler(Player player, Command cmd)
  798. {
  799. try
  800. {
  801. string option = cmd.Next();
  802. string targetName = cmd.Next();
  803. string amount = cmd.Next();
  804. int amountnum;
  805. if (option == null)
  806. {
  807. CdEconomy.PrintUsage(player);
  808. }
  809. if (option == "give")
  810. {
  811. if (!player.Can(Permission.ManageEconomy))
  812. {
  813. player.Message("You do not have the required permisions to use that command!");
  814. return;
  815. }
  816. Player target = Server.FindPlayerOrPrintMatches(player, targetName, false, true);
  817. if (targetName == null)
  818. {
  819. player.Message("&ePlease type in a player's name to give bits towards.");
  820. return;
  821. }
  822. if (target == null)
  823. {
  824. return;
  825. }
  826. else
  827. {
  828. if (!int.TryParse(amount, out amountnum))
  829. {
  830. player.Message("&ePlease select from a whole number.");
  831. return;
  832. }
  833. if (cmd.IsConfirmed)
  834. {
  835. //actually give the player the money
  836. int tNewMoney = target.Info.Money + amountnum;
  837. player.Message("&eYou have given {0} &C{1} &ebit(s).", target.ClassyName, amountnum);
  838. target.Message("&e{0} &ehas given you {1} &ebit(s).", player.ClassyName, amountnum);
  839. Server.Players.Except(target).Except(player).Message("&e{0} &ewas given {1} &ebit(s) from {2}&e.", target.ClassyName, amountnum, player.ClassyName);
  840. target.Info.Money = tNewMoney;
  841. return;
  842. }
  843. else
  844. {
  845. player.Confirm(cmd, "&eAre you sure you want to give {0} &C{1} &ebits?&s", target.ClassyName, amountnum);
  846. return;
  847. }
  848. }
  849. }
  850. if (option == "take")
  851. {
  852. if (!player.Can(Permission.ManageEconomy))
  853. {
  854. player.Message("You do not have the required permisions to use that command!");
  855. return;
  856. }
  857. Player target = Server.FindPlayerOrPrintMatches(player, targetName, false, true);
  858. if (targetName == null)
  859. {
  860. player.Message("&ePlease type in a player's name to take bits away from.");
  861. return;
  862. }
  863. if (target == null)
  864. {
  865. return;
  866. }
  867. else
  868. {
  869. if (!int.TryParse(amount, out amountnum))
  870. {
  871. player.Message("&eThe amount must be a number!");
  872. return;
  873. }
  874. if (cmd.IsConfirmed)
  875. {
  876. if (amountnum > target.Info.Money)
  877. {
  878. player.Message("{0}&e doesn't have that many bits!", target.ClassyName);
  879. return;
  880. }
  881. else
  882. {
  883. //actually give the player the money
  884. int tNewMoney = target.Info.Money - amountnum;
  885. player.Message("&eYou have taken &c{1}&e from {0}.", target.ClassyName, amountnum);
  886. target.Message("&e{0} &ehas taken {1} &ebit(s) from you.", player.ClassyName, amountnum);
  887. Server.Players.Except(target).Except(player).Message("&e{0} &etook {1} &ebit(s) from {2}&e.", player.ClassyName, amountnum, target.ClassyName);
  888. target.Info.Money = tNewMoney;
  889. return;
  890. }
  891. }
  892. else
  893. {
  894. player.Confirm(cmd, "&eAre you sure you want to take &c{1} &ebits from {0}&e?&s", target.ClassyName, amountnum);
  895. return;
  896. }
  897. }
  898. }
  899. if (option == "pay")
  900. {
  901. //lotsa idiot proofing in this one ^.^
  902. if (targetName == null)
  903. {
  904. player.Message("&ePlease type in a player's name to pay bits towards.");
  905. return;
  906. }
  907. Player target = Server.FindPlayerOrPrintMatches(player, targetName, false, true);
  908. if (target == player)
  909. {
  910. player.Message("You can't pay yourself >.> Doesn't work like that.");
  911. return;
  912. }
  913. if (target == null)
  914. {
  915. return;
  916. }
  917. else
  918. {
  919. if (!int.TryParse(amount, out amountnum))
  920. {
  921. player.Message("&eThe amount must be a positive whole number!");
  922. return;
  923. }
  924. if (amountnum < 1)
  925. {
  926. player.Message("&eThe amount must be a positive whole number!");
  927. return;
  928. }
  929. if (cmd.IsConfirmed)
  930. {
  931. if (amountnum > player.Info.Money)
  932. {
  933. player.Message("You don't have that many bits!");
  934. return;
  935. }
  936. else
  937. {
  938. //show him da monai
  939. int pNewMoney = player.Info.Money - amountnum;
  940. int tNewMoney = target.Info.Money + amountnum;
  941. player.Message("&eYou have paid &C{1}&e to {0}&e.", target.ClassyName, amountnum);
  942. target.Message("&e{0} &ehas paid you {1} &ebit(s).", player.ClassyName, amountnum);
  943. Server.Players.Except(target).Except(player).Message("&e{0} &ewas paid {1} &ebit(s) from {2}&e.", target.ClassyName, amountnum, player.ClassyName);
  944. player.Info.Money = pNewMoney;
  945. target.Info.Money = tNewMoney;
  946. return;
  947. }
  948. }
  949. else
  950. {
  951. player.Confirm(cmd, "&eAre you sure you want to pay {0}&e {1} &ebits?&s", target.ClassyName, amountnum);
  952. return;
  953. }
  954. }
  955. }
  956. else if (option == "show")
  957. {
  958. Player target = Server.FindPlayerOrPrintMatches(player, targetName, false, true);
  959. if (targetName == null)
  960. {
  961. player.Message("&ePlease type in a player's name to see how many bits they have.");
  962. return;
  963. }
  964. if (target == null)
  965. {
  966. return;
  967. }
  968. else
  969. {
  970. //actually show how much money that person has
  971. player.Message("&e{0}&e has &C{1}&e bits currently!", target.ClassyName, target.Info.Money);
  972. }
  973. }
  974. else
  975. {
  976. player.Message("&eValid choices are '/economy take', '/economy give', '/economy show' and '/econ pay'.");
  977. return;
  978. }
  979. }
  980. catch (ArgumentNullException)
  981. {
  982. CdEconomy.PrintUsage(player);
  983. }
  984. }
  985. #endregion
  986. static readonly CommandDescriptor CdBanAll = new CommandDescriptor
  987. {
  988. Name = "BanAll",
  989. Category = CommandCategory.Moderation,
  990. IsConsoleSafe = true,
  991. Permissions = new[] { Permission.Ban, Permission.BanIP, Permission.BanAll },
  992. Usage = "/BanAll PlayerName|IPAddress [Reason]",
  993. Help = "&SBans the player's name, IP, and all other names associated with the IP. " +
  994. "UndoAll's the playername. If player is not online, " +
  995. "the last known IP associated with the name is used. " +
  996. "You can also type in the IP address directly. " +
  997. "Any text after PlayerName/IP will be saved as a memo. ",
  998. Handler = BanAllHandler
  999. };
  1000. static void BanAllHandler(Player player, Command cmd)
  1001. {
  1002. string targetNameOrIP = cmd.Next();
  1003. if (targetNameOrIP == null)
  1004. {
  1005. CdBanAll.PrintUsage(player);
  1006. return;
  1007. }
  1008. string reason = cmd.NextAll();
  1009. IPAddress targetAddress;
  1010. if (Server.IsIP(targetNameOrIP) && IPAddress.TryParse(targetNameOrIP, out targetAddress))
  1011. {
  1012. try
  1013. {
  1014. targetAddress.BanAll(player, reason, true, true);
  1015. }
  1016. catch (PlayerOpException ex)
  1017. {
  1018. player.Message(ex.MessageColored);
  1019. }
  1020. }
  1021. else
  1022. {
  1023. PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches(player, targetNameOrIP);
  1024. if (target == null) return;
  1025. try
  1026. {
  1027. if (target.LastIP.Equals(IPAddress.Any) || target.LastIP.Equals(IPAddress.None))
  1028. {
  1029. target.Ban(player, reason, true, true);
  1030. }
  1031. else
  1032. {
  1033. target.BanAll(player, reason, true, true);
  1034. BuildingCommands.UndoAllHandler(player, new Command("/UndoAll " + target.Name));
  1035. }
  1036. }
  1037. catch (PlayerOpException ex)
  1038. {
  1039. player.Message(ex.MessageColored);
  1040. if (ex.ErrorCode == PlayerOpExceptionCode.ReasonRequired)
  1041. {
  1042. FreezeIfAllowed(player, target);
  1043. }
  1044. }
  1045. }
  1046. }
  1047. static readonly CommandDescriptor CdAssassinate = new CommandDescriptor
  1048. {
  1049. Name = "Assasinate",
  1050. Category = CommandCategory.Fun | CommandCategory.Fun,
  1051. Aliases = new[] { "Snipe", "Assassinate" },
  1052. IsConsoleSafe = true,
  1053. Permissions = new[] { Permission.Kill },
  1054. Help = "Silently kills a player.",
  1055. NotRepeatable = true,
  1056. Usage = "/assassinate playername",
  1057. Handler = AssassinateHandler
  1058. };
  1059. internal static void AssassinateHandler(Player player, Command cmd)
  1060. {
  1061. string name = cmd.Next();
  1062. if (name == null)
  1063. {
  1064. player.Message("Please enter a name.");
  1065. return;
  1066. }
  1067. if (!player.Info.IsHidden)
  1068. {
  1069. player.Message("You can only assassinate while hidden silly head.");
  1070. return;
  1071. }
  1072. else
  1073. {
  1074. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  1075. if (target == null) return;
  1076. if (target.Immortal)
  1077. {
  1078. player.Message("&SYou can't assassinate {0}&S, they're immortal.", target.ClassyName);
  1079. return;
  1080. }
  1081. if (player.Can(Permission.Kill, target.Info.Rank))
  1082. {
  1083. target.TeleportTo(player.World.Map.Spawn);
  1084. target.Message("&8You were just assassinated!");
  1085. player.Message("&8Successfully assassinated {0}.", target.ClassyName);
  1086. }
  1087. if (target == player)
  1088. {
  1089. Server.Message("{0} killed itself in confusion!", player.ClassyName);
  1090. return;
  1091. }
  1092. if (target == null)
  1093. {
  1094. player.Message("Please enter a victim's name.");
  1095. return;
  1096. }
  1097. else
  1098. {
  1099. if (player.Can(Permission.Kill, target.Info.Rank))
  1100. {
  1101. target.TeleportTo(player.World.Map.Spawn);
  1102. }
  1103. else
  1104. {
  1105. player.Message("You can only assassinate players ranked {0}&S or lower.",
  1106. player.Info.Rank.GetLimit(Permission.Kill).ClassyName);
  1107. player.Message("{0}&S is ranked {1}.", target.ClassyName, target.Info.Rank.ClassyName);
  1108. }
  1109. }
  1110. }
  1111. }
  1112. static readonly CommandDescriptor CdPunch = new CommandDescriptor
  1113. {
  1114. Name = "Punch",
  1115. Aliases = new string[] { "Pu" },
  1116. Category = CommandCategory.Chat | CommandCategory.Fun,
  1117. Permissions = new[] { Permission.Brofist },
  1118. IsConsoleSafe = true,
  1119. Help = "&aPunches &Sa player. " +
  1120. "Availble items are: groin, stomach, tits, and knockout.\n" +
  1121. "NOTE: Items are optional.",
  1122. Usage = "/Punch playerName item",
  1123. Handler = PunchHandler
  1124. };
  1125. static void PunchHandler(Player player, Command cmd)
  1126. {
  1127. string name = cmd.Next();
  1128. string item = cmd.Next();
  1129. if (name == null)
  1130. {
  1131. player.Message("Please enter a name");
  1132. return;
  1133. }
  1134. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  1135. if (target == null) return;
  1136. if (target.Immortal)
  1137. {
  1138. player.Message("&SYou failed to &aPunch {0}&S, they are immortal", target.ClassyName);
  1139. return;
  1140. }
  1141. if (target == player)
  1142. {
  1143. player.Message("&sYou can't &aPunch &syourself, Weirdo!");
  1144. return;
  1145. }
  1146. double time = (DateTime.Now - player.Info.LastUsedSlap).TotalSeconds;
  1147. if (time < 10)
  1148. {
  1149. player.Message("&WYou can use /Punch again in " + Math.Round(10 - time) + " seconds.");
  1150. return;
  1151. }
  1152. string aMessage;
  1153. if (player.Can(Permission.Slap, target.Info.Rank))
  1154. {
  1155. Position slap = new Position(target.Position.X, target.Position.Y, (target.World.Map.Bounds.ZMax) * 32);
  1156. target.TeleportTo(slap);
  1157. if (string.IsNullOrEmpty(item))
  1158. {
  1159. Server.Players.CanSee(target).Union(target).Message("{0} &Swas &aPunched &Sin the &cFace &Sby {1}", target.ClassyName, player.ClassyName);
  1160. IRC.PlayerSomethingMessage(player, "punched", target, null);
  1161. player.Info.LastUsedSlap = DateTime.Now;
  1162. return;
  1163. }
  1164. else if (item.ToLower() == "groin")
  1165. aMessage = String.Format("{0} &Swas &aPunched &Sin the &cGroin &Sby {1}", target.ClassyName, player.ClassyName);
  1166. else if (item.ToLower() == "stomach")
  1167. aMessage = String.Format("{0} &Swas &aPunched &Sin the &cStomach &Sby {1}", target.ClassyName, player.ClassyName);
  1168. else if (item.ToLower() == "tits")
  1169. aMessage = String.Format("{0} &Swas &aPunched &Sright in the &cTits &Sby {1}", target.ClassyName, player.ClassyName);
  1170. else if (item.ToLower() == "knockout")
  1171. aMessage = String.Format("{0} &Swas &cKnocked Out &Sby {1}", target.ClassyName, player.ClassyName);
  1172. else
  1173. {
  1174. Server.Players.CanSee(target).Union(target).Message("{0} &Swas &aPunched &Sin the &cFace &Sby {1}", target.ClassyName, player.ClassyName);
  1175. IRC.PlayerSomethingMessage(player, "punched", target, null);
  1176. player.Info.LastUsedSlap = DateTime.Now;
  1177. return;
  1178. }
  1179. Server.Players.CanSee(target).Union(target).Message(aMessage);
  1180. IRC.PlayerSomethingMessage(player, "punched", target, null);
  1181. player.Info.LastUsedSlap = DateTime.Now;
  1182. return;
  1183. }
  1184. else
  1185. {
  1186. player.Message("&sYou can only Punch players ranked {0}&S or lower",
  1187. player.Info.Rank.GetLimit(Permission.Slap).ClassyName);
  1188. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName);
  1189. }
  1190. }
  1191. static readonly CommandDescriptor CdMuteAll = new CommandDescriptor
  1192. {
  1193. Name = "MuteAll",
  1194. Aliases = new[] { "quiet" },
  1195. Category = CommandCategory.Moderation,
  1196. IsConsoleSafe = true,
  1197. Permissions = new[] { Permission.MuteAll },
  1198. Usage = "/Muteall [duration] [s/h/d/w]",
  1199. Help = "Mutes all chat feed for the specified time. 30 seconds is the time limit. To unmuteall, type /muteall 1s",
  1200. Handler = MuteAllHandler
  1201. };
  1202. internal static void MuteAllHandler(Player player, Command cmd)
  1203. {
  1204. string timeString = cmd.Next();
  1205. TimeSpan duration;
  1206. // validate command parameters
  1207. if (String.IsNullOrEmpty(timeString) ||
  1208. !timeString.TryParseMiniTimespan(out duration) || duration <= TimeSpan.Zero)
  1209. {
  1210. player.Message("&H/Muteall Duration");
  1211. return;
  1212. }
  1213. TimeSpan MaxMuteDuration = TimeSpan.FromSeconds(30);
  1214. // check if given time exceeds maximum (30 seconds)
  1215. if (duration > MaxMuteDuration)
  1216. {
  1217. player.Message("Maximum mute duration is {0}.", MaxMuteDuration.ToMiniString());
  1218. duration = MaxMuteDuration;
  1219. } //check to keep limit under
  1220. foreach (Player target in Server.Players.Where(p => p.World != null && p != player))
  1221. {
  1222. // actually mute
  1223. try
  1224. {
  1225. target.Info.Mute(player, duration, false, true);
  1226. }
  1227. catch (PlayerOpException ex)
  1228. {
  1229. player.Message(ex.MessageColored);
  1230. }
  1231. }
  1232. string message = cmd.Next();
  1233. if (message == null)
  1234. {
  1235. Server.Message(" {0}&W has muted the chat feed for {1}&W seconds!", player.ClassyName, timeString);
  1236. }
  1237. }
  1238. #endregion
  1239. #region 800Craft
  1240. //Copyright (C) <2012> <Jon Baker, Glenn MariĂŤn and Lao Tszy>
  1241. //This program is free software: you can redistribute it and/or modify
  1242. //it under the terms of the GNU General Public License as published by
  1243. //the Free Software Foundation, either version 3 of the License, or
  1244. //(at your option) any later version.
  1245. //This program is distributed in the hope that it will be useful,
  1246. //but WITHOUT ANY WARRANTY; without even the implied warranty of
  1247. //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1248. //GNU General Public License for more details.
  1249. //You should have received a copy of the GNU General Public License
  1250. //along with this program. If not, see <http://www.gnu.org/licenses/>.
  1251. public static List<string> BassText = new List<string>();
  1252. static readonly CommandDescriptor CdTitle = new CommandDescriptor
  1253. {
  1254. Name = "Title",
  1255. Category = CommandCategory.Moderation,
  1256. IsConsoleSafe = true,
  1257. Permissions = new[] { Permission.EditPlayerDB },
  1258. Usage = "/Title <Playername> <Title>",
  1259. Help = "&SChanges or sets a player's title.",
  1260. Handler = TitleHandler
  1261. };
  1262. static void TitleHandler(Player player, Command cmd)
  1263. {
  1264. string targetName = cmd.Next();
  1265. string titleName = cmd.NextAll();
  1266. if (string.IsNullOrEmpty(targetName))
  1267. {
  1268. CdTitle.PrintUsage(player);
  1269. return;
  1270. }
  1271. PlayerInfo info = PlayerDB.FindPlayerInfoOrPrintMatches(player, targetName);
  1272. if (info == null) return;
  1273. string oldTitle = info.TitleName;
  1274. if (titleName.Length == 0) titleName = null;
  1275. if (titleName == info.TitleName)
  1276. {
  1277. if (titleName == null)
  1278. {
  1279. player.Message("Title: Title for {0} is not set.",
  1280. info.Name);
  1281. }
  1282. else
  1283. {
  1284. player.Message("Title: Title for {0} is already set to \"{1}&S\"",
  1285. info.Name,
  1286. titleName);
  1287. }
  1288. return;
  1289. }
  1290. //check the title, is it a title?
  1291. if (titleName != null)
  1292. {
  1293. string StripT = Color.StripColors(titleName);
  1294. if (!StripT.StartsWith("[") && !StripT.EndsWith("]"))
  1295. {
  1296. titleName = info.Rank.Color + "[" + titleName + info.Rank.Color + "] ";
  1297. }
  1298. }
  1299. info.TitleName = titleName;
  1300. if (oldTitle == null)
  1301. {
  1302. player.Message("Title: Title for {0} set to \"{1}&S\"",
  1303. info.Name,
  1304. titleName);
  1305. }
  1306. else if (titleName == null)
  1307. {
  1308. player.Message("Title: Title for {0} was reset (was \"{1}&S\")",
  1309. info.Name,
  1310. oldTitle);
  1311. }
  1312. else
  1313. {
  1314. player.Message("Title: Title for {0} changed from \"{1}&S\" to \"{2}&S\"",
  1315. info.Name,
  1316. oldTitle,
  1317. titleName);
  1318. }
  1319. }
  1320. static readonly CommandDescriptor CdImmortal = new CommandDescriptor
  1321. {
  1322. Name = "Immortal",
  1323. Aliases = new [] { "Invincible", "God" },
  1324. Category = CommandCategory.Moderation,
  1325. IsConsoleSafe = true,
  1326. Permissions = new[] { Permission.Immortal },
  1327. Help = "Stops death by all things.",
  1328. NotRepeatable = true,
  1329. Usage = "/Immortal",
  1330. Handler = ImmortalHandler
  1331. };
  1332. internal static void ImmortalHandler(Player player, Command cmd)
  1333. {
  1334. if (player.Immortal){
  1335. player.Immortal = false;
  1336. Server.Players.Message("{0}&S is no longer Immortal", player.ClassyName);
  1337. return;
  1338. }
  1339. player.Immortal = true;
  1340. Server.Players.Message("{0}&S is now Immortal", player.ClassyName);
  1341. }
  1342. static readonly CommandDescriptor CdKill = new CommandDescriptor
  1343. {
  1344. Name = "Kill",
  1345. Category = CommandCategory.Moderation | CommandCategory.Fun,
  1346. Aliases = new[] { "Slay" },
  1347. IsConsoleSafe = false,
  1348. Permissions = new[] { Permission.Kill },
  1349. Help = "Kills a player.",
  1350. NotRepeatable = true,
  1351. Usage = "/Kill playername",
  1352. Handler = KillHandler
  1353. };
  1354. internal static void KillHandler(Player player, Command cmd)
  1355. {
  1356. string name = cmd.Next();
  1357. string reason = cmd.NextAll();
  1358. if (name == null)
  1359. {
  1360. player.Message("Please enter a name");
  1361. return;
  1362. }
  1363. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  1364. if (target == null) return;
  1365. if (target.Immortal)
  1366. {
  1367. player.Message("&SYou failed to kill {0}&S, they are immortal", target.ClassyName);
  1368. return;
  1369. }
  1370. double time = (DateTime.Now - player.Info.LastUsedKill).TotalSeconds;
  1371. if (time < 10)
  1372. {
  1373. player.Message("&WYou can use /Kill again in " + Math.Round(10 - time) + " seconds.");
  1374. return;
  1375. }
  1376. if (target == null)
  1377. {
  1378. player.Message("You need to enter a player name to Kill");
  1379. return;
  1380. }
  1381. else
  1382. {
  1383. if (target == player)
  1384. {
  1385. player.TeleportTo(player.World.Map.Spawn);
  1386. player.Info.LastUsedKill = DateTime.Now;
  1387. Server.Players.CanSee(target).Message("{0}&C killed itself in confusion!", player);
  1388. return;
  1389. }
  1390. if (player.Can(Permission.Kill, target.Info.Rank) && reason.Length < 1)
  1391. {
  1392. target.TeleportTo(player.World.Map.Spawn);
  1393. player.Info.LastUsedKill = DateTime.Now;
  1394. Server.Players.CanSee(target).Message("{0}&C was &4Killed&C by {1}", target.ClassyName, player.ClassyName);
  1395. return;
  1396. }
  1397. else if (player.Can(Permission.Kill, target.Info.Rank) && reason != null)
  1398. {
  1399. target.TeleportTo(player.World.Map.Spawn);
  1400. player.Info.LastUsedKill = DateTime.Now;
  1401. Server.Players.CanSee(target).Message("{0}&C was &4Killed&C by {1}&c: {2}", target.ClassyName, player.ClassyName, reason);
  1402. }
  1403. else
  1404. {
  1405. player.Message("You can only Kill players ranked {0}&S or lower",
  1406. player.Info.Rank.GetLimit(Permission.Kill).ClassyName);
  1407. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName);
  1408. }
  1409. }
  1410. }
  1411. static readonly CommandDescriptor CdSlap = new CommandDescriptor
  1412. {
  1413. Name = "Slap",
  1414. IsConsoleSafe = true,
  1415. NotRepeatable = true,
  1416. Aliases = new[] { "Sky" },
  1417. Category = CommandCategory.Moderation | CommandCategory.Fun,
  1418. Permissions = new[] { Permission.Slap },
  1419. Help = "Slaps a player to the sky. " +
  1420. "Available items are: bakingtray, fish, bitchslap, shoe, fryingpan, ho, noodle, and ass.",
  1421. Usage = "/Slap <playername> [item]",
  1422. Handler = Slap
  1423. };
  1424. static void Slap(Player player, Command cmd)
  1425. {
  1426. string name = cmd.Next();
  1427. string item = cmd.Next();
  1428. if (name == null){
  1429. player.Message("Please enter a name");
  1430. return;
  1431. }
  1432. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  1433. if (target == null) return;
  1434. if (target.Immortal){
  1435. player.Message("&SYou failed to slap {0}&S, they are immortal", target.ClassyName);
  1436. return;
  1437. }
  1438. if (target == player){
  1439. player.Message("&sYou can't slap yourself.... What's wrong with you???");
  1440. return;
  1441. }
  1442. double time = (DateTime.Now - player.Info.LastUsedSlap).TotalSeconds;
  1443. if (time < 10){
  1444. player.Message("&WYou can use /Slap again in " + Math.Round(10 - time) + " seconds.");
  1445. return;
  1446. }
  1447. string aMessage;
  1448. if (player.Can(Permission.Slap, target.Info.Rank)){
  1449. Position slap = new Position(target.Position.X, target.Position.Y, (target.World.Map.Bounds.ZMax) * 32);
  1450. target.previousLocation = target.Position;
  1451. target.previousWorld = null;
  1452. target.TeleportTo(slap);
  1453. if (string.IsNullOrEmpty(item)){
  1454. Server.Players.CanSee(target).Union(target).Message("{0} &Swas slapped sky high by {1}", target.ClassyName, player.ClassyName);
  1455. IRC.PlayerSomethingMessage(player, "slapped", target, null);
  1456. player.Info.LastUsedSlap = DateTime.Now;
  1457. return;
  1458. }
  1459. else if (item.ToLower() == "bakingtray")
  1460. aMessage = String.Format("{0} &Swas slapped by {1}&S with a Baking Tray", target.ClassyName, player.ClassyName);
  1461. else if (item.ToLower() == "fish")
  1462. aMessage = String.Format("{0} &Swas slapped by {1}&S with a Giant Fish", target.ClassyName, player.ClassyName);
  1463. else if (item.ToLower() == "bitchslap")
  1464. aMessage = String.Format("{0} &Swas bitch-slapped by {1}", target.ClassyName, player.ClassyName);
  1465. else if (item.ToLower() == "shoe")
  1466. aMessage = String.Format("{0} &Swas slapped by {1}&S with a Shoe", target.ClassyName, player.ClassyName);
  1467. else if (item.ToLower() == "ass")
  1468. aMessage = String.Format("{0} &Swas slapped on the &0Ass&s by {1}", target.ClassyName, player.ClassyName);
  1469. else if (item.ToLower() == "fryingpan")
  1470. aMessage = String.Format("{0} &Swas slapped by {1}&S with a Frying Pan", target.ClassyName, player.ClassyName);
  1471. else if (item.ToLower() == "noodle")
  1472. aMessage = String.Format("{0} &Swas slapped by {1}&S with a Wet Noodle", target.ClassyName, player.ClassyName);
  1473. else if (item.ToLower() == "ho")
  1474. aMessage = String.Format("{1} &Sslapped {0}&S like a Pimp slaps a Ho", target.ClassyName, player.ClassyName);
  1475. else{
  1476. Server.Players.CanSee(target).Union(target).Message("{0} &Swas slapped sky high by {1}", target.ClassyName, player.ClassyName);
  1477. IRC.PlayerSomethingMessage(player, "slapped", target, null);
  1478. player.Info.LastUsedSlap = DateTime.Now;
  1479. return;
  1480. }
  1481. Server.Players.CanSee(target).Union(target).Message(aMessage);
  1482. IRC.PlayerSomethingMessage(player, "slapped", target, null);
  1483. player.Info.LastUsedSlap = DateTime.Now;
  1484. return;
  1485. }else{
  1486. player.Message("&sYou can only Slap players ranked {0}&S or lower",
  1487. player.Info.Rank.GetLimit(Permission.Slap).ClassyName);
  1488. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName);
  1489. }
  1490. }
  1491. static readonly CommandDescriptor CdTPZone = new CommandDescriptor
  1492. {
  1493. Name = "Tpzone",
  1494. IsConsoleSafe = false,
  1495. Aliases = new[] { "tpz", "zonetp" },
  1496. Category = CommandCategory.World | CommandCategory.Zone,
  1497. Permissions = new[] { Permission.Teleport },
  1498. Help = "Teleports you to the centre of a Zone listed in /Zones.",
  1499. Usage = "/tpzone ZoneName",
  1500. Handler = TPZone
  1501. };
  1502. static void TPZone(Player player, Command cmd)
  1503. {
  1504. string zoneName = cmd.Next();
  1505. if (zoneName == null){
  1506. player.Message("No zone name specified. See &W/Help tpzone");
  1507. return;
  1508. }else{
  1509. Zone zone = player.World.Map.Zones.Find(zoneName);
  1510. if (zone == null){
  1511. player.MessageNoZone(zoneName);
  1512. return;
  1513. }
  1514. Position zPos = new Position((((zone.Bounds.XMin + zone.Bounds.XMax) / 2) * 32),
  1515. (((zone.Bounds.YMin + zone.Bounds.YMax) / 2) * 32),
  1516. (((zone.Bounds.ZMin + zone.Bounds.ZMax) / 2) + 2) * 32);
  1517. player.TeleportTo((zPos));
  1518. player.Message("&WTeleporting you to zone " + zone.ClassyName);
  1519. }
  1520. }
  1521. static readonly CommandDescriptor CdImpersonate = new CommandDescriptor
  1522. {
  1523. Name = "Impersonate",
  1524. Category = CommandCategory.Moderation | CommandCategory.Fun,
  1525. IsConsoleSafe = true,
  1526. Permissions = new[] { Permission.EditPlayerDB },
  1527. Help = "&SChanges to players skin to a desired name. " +
  1528. "If no playername is given, all changes are reverted. " +
  1529. "Note: The name above your head changes too",
  1530. Usage = "/Impersonate PlayerName",
  1531. Handler = ImpersonateHandler
  1532. };
  1533. static void ImpersonateHandler(Player player, Command cmd)
  1534. {
  1535. //entityChanged should be set to true for the skin update to happen in real time
  1536. string iName = cmd.Next();
  1537. if (iName == null && player.iName == null)
  1538. {
  1539. CdImpersonate.PrintUsage(player);
  1540. return;
  1541. }
  1542. if (iName == null)
  1543. {
  1544. player.iName = null;
  1545. player.entityChanged = true;
  1546. player.Info.tempDisplayedName = null;
  1547. player.Message("&SAll changes have been removed and your skin has been updated");
  1548. return;
  1549. }
  1550. //ignore isvalidname for percent codes to work
  1551. if (player.iName == null)
  1552. {
  1553. player.Message("&SYour name has changed from '" + player.Info.Rank.Color + player.Name + "&S' to '" + iName + "&S'");
  1554. }
  1555. if (player.iName != null)
  1556. {
  1557. player.Message("&SYour name has changed from '" + player.iName + "&S' to '" + iName + "&S'");
  1558. }
  1559. PlayerInfo targetInfo = PlayerDB.FindPlayerInfoExact(iName);
  1560. try
  1561. {
  1562. player.iName = targetInfo.Rank.Color + targetInfo.Name;
  1563. if (targetInfo.DisplayedName != null)
  1564. player.Info.tempDisplayedName = targetInfo.DisplayedName;
  1565. else
  1566. player.Info.tempDisplayedName = targetInfo.Rank.Color + targetInfo.Name;
  1567. }
  1568. catch
  1569. {
  1570. player.iName = RankManager.LowestRank.Color + iName;
  1571. player.Info.tempDisplayedName = RankManager.LowestRank.Color + iName;
  1572. }
  1573. player.entityChanged = true;
  1574. }
  1575. static readonly CommandDescriptor CdTempBan = new CommandDescriptor
  1576. {
  1577. Name = "Tempban",
  1578. Category = CommandCategory.Moderation,
  1579. IsConsoleSafe = true,
  1580. Aliases = new[] { "tban" },
  1581. Permissions = new[] { Permission.TempBan },
  1582. Help = "Bans a player for a selected amount of time. Example: 10s | 10m | 10h ",
  1583. Usage = "/Tempban Player Duration",
  1584. Handler = Tempban
  1585. };
  1586. static void Tempban(Player player, Command cmd)
  1587. {
  1588. string targetName = cmd.Next();
  1589. string timeString = cmd.Next();
  1590. TimeSpan duration;
  1591. try{
  1592. if (String.IsNullOrEmpty(targetName) || String.IsNullOrEmpty(timeString) ||
  1593. !timeString.TryParseMiniTimespan(out duration) || duration <= TimeSpan.Zero)
  1594. {
  1595. CdTempBan.PrintUsage(player);
  1596. return;
  1597. }
  1598. }catch (OverflowException){
  1599. player.Message("TempBan: Given duration is too long.");
  1600. return;
  1601. }
  1602. // find the target
  1603. PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches(player, targetName);
  1604. if (target == null){
  1605. player.MessageNoPlayer(targetName);
  1606. return;
  1607. };
  1608. if (target.Name == player.Name){
  1609. player.Message("Trying to T-Ban yourself? Fail!");
  1610. return;
  1611. }
  1612. // check permissions
  1613. if (!player.Can(Permission.BanIP, target.Rank)){
  1614. player.Message("You can only Temp-Ban players ranked {0}&S or lower.",
  1615. player.Info.Rank.GetLimit(Permission.BanIP).ClassyName);
  1616. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Rank.ClassyName);
  1617. return;
  1618. }
  1619. // do the banning
  1620. if (target.Tempban(player.Name, duration)){
  1621. string reason = cmd.NextAll();
  1622. try{
  1623. Player targetPlayer = target.PlayerObject;
  1624. target.Ban(player, "You were Banned for " + timeString, false, true);
  1625. Server.TempBans.Add(targetPlayer);
  1626. }
  1627. catch (PlayerOpException ex){
  1628. player.Message(ex.MessageColored);
  1629. }
  1630. Scheduler.NewTask(t => Untempban(player, target)).RunOnce(duration);
  1631. Server.Message("&SPlayer {0}&S was Banned by {1}&S for {2}",
  1632. target.ClassyName, player.ClassyName, duration.ToMiniString());
  1633. if (reason.Length > 0) Server.Message("&Wreason: {0}", reason);
  1634. Logger.Log(LogType.UserActivity, "Player {0} was Banned by {1} for {2}",
  1635. target.Name, player.Name, duration.ToMiniString());
  1636. }else{
  1637. player.Message("Player {0}&S is already Banned by {1}&S for {2:0} more.",
  1638. target.ClassyName,
  1639. target.BannedBy,
  1640. target.BannedUntil.Subtract(DateTime.UtcNow).ToMiniString());
  1641. }
  1642. }
  1643. public static void Untempban(Player player, PlayerInfo target)
  1644. {
  1645. if (!target.IsBanned) return;
  1646. else
  1647. target.Unban(player, "Tempban Expired", true, true);
  1648. }
  1649. static readonly CommandDescriptor CdBasscannon = new CommandDescriptor
  1650. {
  1651. Name = "Basscannon",
  1652. Category = CommandCategory.Moderation | CommandCategory.Fun,
  1653. IsConsoleSafe = true,
  1654. Aliases = new[] { "bc" },
  1655. IsHidden = false,
  1656. Permissions = new[] { Permission.Basscannon },
  1657. Usage = "Let the Basscannon 'Kick' it!",
  1658. Help = "A classy way to kick players from the server",
  1659. Handler = Basscannon
  1660. };
  1661. internal static void Basscannon(Player player, Command cmd)
  1662. {
  1663. string name = cmd.Next();
  1664. string reason = cmd.NextAll();
  1665. if (name == null)
  1666. {
  1667. player.Message("Please enter a player name to use the basscannon on.");
  1668. return;
  1669. }
  1670. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  1671. if (target == null)
  1672. {
  1673. return;
  1674. }
  1675. if (ConfigKey.RequireKickReason.Enabled() && String.IsNullOrEmpty(reason))
  1676. {
  1677. player.Message("&WPlease specify a reason: &W/Basscannon PlayerName Reason");
  1678. // freeze the target player to prevent further damage
  1679. return;
  1680. }
  1681. if (player.Can(Permission.Kick, target.Info.Rank))
  1682. {
  1683. target.Info.IsHidden = false;
  1684. try
  1685. {
  1686. Player targetPlayer = target;
  1687. target.BassKick(player, reason, LeaveReason.Kick, true, true, true);
  1688. if (BassText.Count < 1){
  1689. BassText.Add("Flux Pavillion does not approve of your behavior");
  1690. BassText.Add("Let the Basscannon KICK IT!");
  1691. BassText.Add("WUB WUB WUB WUB WUB WUB!");
  1692. BassText.Add("Basscannon, Basscannon, Basscannon, Basscannon!");
  1693. BassText.Add("Pow pow POW!!!");
  1694. }
  1695. string line = BassText[new Random().Next(0, BassText.Count)].Trim();
  1696. if (line.Length == 0) return;
  1697. Server.Message("&9{0}", line);
  1698. }
  1699. catch (PlayerOpException ex)
  1700. {
  1701. player.Message(ex.MessageColored);
  1702. if (ex.ErrorCode == PlayerOpExceptionCode.ReasonRequired)
  1703. return;
  1704. }
  1705. }
  1706. else
  1707. {
  1708. player.Message("You can only use /Basscannon on players ranked {0}&S or lower",
  1709. player.Info.Rank.GetLimit(Permission.Kick).ClassyName);
  1710. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName);
  1711. }
  1712. }
  1713. static readonly CommandDescriptor CdWarn = new CommandDescriptor
  1714. {
  1715. Name = "Warn",
  1716. Category = CommandCategory.Moderation,
  1717. IsConsoleSafe = true,
  1718. NotRepeatable = true,
  1719. Permissions = new[] { Permission.Warn },
  1720. Help = "&SWarns a player and puts a black star next to their name for 20 minutes. During the 20 minutes, if they are warned again, they will get kicked.",
  1721. Usage = "/Warn playername",
  1722. Handler = Warn
  1723. };
  1724. internal static void Warn(Player player, Command cmd)
  1725. {
  1726. string name = cmd.Next();
  1727. if (name == null)
  1728. {
  1729. player.Message("No player specified.");
  1730. return;
  1731. }
  1732. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  1733. if (target == null)
  1734. {
  1735. player.MessageNoPlayer(name);
  1736. return;
  1737. }
  1738. if (player.Can(Permission.Warn, target.Info.Rank))
  1739. {
  1740. target.Info.IsHidden = false;
  1741. if (target.Info.Warn(player.Name))
  1742. {
  1743. Server.Message("{0}&S has been warned by {1}",
  1744. target.ClassyName, player.ClassyName);
  1745. Scheduler.NewTask(t => target.Info.UnWarn()).RunOnce(TimeSpan.FromMinutes(15));
  1746. }
  1747. else
  1748. {
  1749. try
  1750. {
  1751. Player targetPlayer = target;
  1752. target.Kick(player, "Auto Kick (2 warnings or more)", LeaveReason.Kick, true, true, true);
  1753. }
  1754. catch (PlayerOpException ex)
  1755. {
  1756. player.Message(ex.MessageColored);
  1757. if (ex.ErrorCode == PlayerOpExceptionCode.ReasonRequired)
  1758. return;
  1759. }
  1760. }
  1761. }
  1762. else
  1763. {
  1764. player.Message("You can only warn players ranked {0}&S or lower",
  1765. player.Info.Rank.GetLimit(Permission.Warn).ClassyName);
  1766. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName);
  1767. }
  1768. }
  1769. static readonly CommandDescriptor CdUnWarn = new CommandDescriptor
  1770. {
  1771. Name = "Unwarn",
  1772. Aliases = new string[] { "uw" },
  1773. Category = CommandCategory.Moderation,
  1774. IsConsoleSafe = true,
  1775. Permissions = new[] { Permission.Warn },
  1776. Usage = "/Unwarn PlayerName",
  1777. Help = "&SUnwarns a player",
  1778. Handler = UnWarn
  1779. };
  1780. internal static void UnWarn(Player player, Command cmd)
  1781. {
  1782. string name = cmd.Next();
  1783. if (name == null)
  1784. {
  1785. player.Message("No player specified.");
  1786. return;
  1787. }
  1788. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  1789. if (target == null)
  1790. {
  1791. player.MessageNoPlayer(name);
  1792. return;
  1793. }
  1794. if (player.Can(Permission.Warn, target.Info.Rank))
  1795. {
  1796. if (target.Info.UnWarn())
  1797. {
  1798. Server.Message("{0}&S had their warning removed by {1}.", target.ClassyName, player.ClassyName);
  1799. }
  1800. else
  1801. {
  1802. player.Message("{0}&S does not have a warning.", target.ClassyName);
  1803. }
  1804. }
  1805. else
  1806. {
  1807. player.Message("You can only unwarn players ranked {0}&S or lower",
  1808. player.Info.Rank.GetLimit(Permission.Warn).ClassyName);
  1809. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName);
  1810. }
  1811. }
  1812. static readonly CommandDescriptor CdDisconnect = new CommandDescriptor
  1813. {
  1814. Name = "Disconnect",
  1815. Category = CommandCategory.Moderation,
  1816. IsConsoleSafe = true,
  1817. Aliases = new[] { "gtfo" },
  1818. IsHidden = false,
  1819. Permissions = new[] { Permission.Gtfo },
  1820. Usage = "/disconnect playername",
  1821. Help = "Get rid of those annoying people without saving to PlayerDB",
  1822. Handler = dc
  1823. };
  1824. internal static void dc(Player player, Command cmd)
  1825. {
  1826. string name = cmd.Next();
  1827. if (name == null)
  1828. {
  1829. player.Message("Please enter a name");
  1830. return;
  1831. }
  1832. Player target = Server.FindPlayerOrPrintMatches(player, name, false, true);
  1833. if (target == null) return;
  1834. if (player.Can(Permission.Gtfo, target.Info.Rank))
  1835. {
  1836. try
  1837. {
  1838. Player targetPlayer = target;
  1839. target.Kick(player, "Manually disconnected by " + player.Name, LeaveReason.Kick, false, true, false);
  1840. Server.Players.Message("{0} &Swas manually disconnected by {1}", target.ClassyName, player.ClassyName);
  1841. }
  1842. catch (PlayerOpException ex)
  1843. {
  1844. player.Message(ex.MessageColored);
  1845. if (ex.ErrorCode == PlayerOpExceptionCode.ReasonRequired)
  1846. return;
  1847. }
  1848. }
  1849. else
  1850. {
  1851. player.Message("You can only Disconnect players ranked {0}&S or lower",
  1852. player.Info.Rank.GetLimit(Permission.Gtfo).ClassyName);
  1853. player.Message("{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName);
  1854. }
  1855. }
  1856. #endregion
  1857. #region Ban / Unban
  1858. static readonly CommandDescriptor CdBan = new CommandDescriptor {
  1859. Name = "Ban",
  1860. Category = CommandCategory.Moderation,
  1861. IsConsoleSafe = true,
  1862. Permissions = new[] { Permission.Ban },
  1863. Usage = "/Ban PlayerName [Reason]",
  1864. Help = "&SBans a specified player by name. Note: Does NOT ban IP. " +
  1865. "Any text after the player name will be saved as a ban reason. ",
  1866. Handler = BanHandler
  1867. };
  1868. static void BanHandler( Player player, Command cmd ) {
  1869. string targetName = cmd.Next();
  1870. if( targetName == null ) {
  1871. CdBan.PrintUsage( player );
  1872. return;
  1873. }
  1874. PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches( player, targetName );
  1875. if( target == null ) return;
  1876. string reason = cmd.NextAll();
  1877. try {
  1878. Player targetPlayer = target.PlayerObject;
  1879. target.Ban( player, reason, true, true );
  1880. WarnIfOtherPlayersOnIP( player, target, targetPlayer );
  1881. } catch( PlayerOpException ex ) {
  1882. player.Message( ex.MessageColored );
  1883. if( ex.ErrorCode == PlayerOpExceptionCode.ReasonRequired ) {
  1884. FreezeIfAllowed( player, target );
  1885. }
  1886. }
  1887. }
  1888. static readonly CommandDescriptor CdBanIP = new CommandDescriptor {
  1889. Name = "BanIP",
  1890. Category = CommandCategory.Moderation,
  1891. IsConsoleSafe = true,
  1892. Permissions = new[] { Permission.Ban, Permission.BanIP },
  1893. Usage = "/BanIP PlayerName|IPAddress [Reason]",
  1894. Help = "&SBans the player's name and IP. If player is not online, last known IP associated with the name is used. " +
  1895. "You can also type in the IP address directly. " +
  1896. "Any text after PlayerName/IP will be saved as a memo. ",
  1897. Handler = BanIPHandler
  1898. };
  1899. static void BanIPHandler( Player player, Command cmd ) {
  1900. string targetNameOrIP = cmd.Next();
  1901. if( targetNameOrIP == null ) {
  1902. CdBanIP.PrintUsage( player );
  1903. return;
  1904. }
  1905. string reason = cmd.NextAll();
  1906. IPAddress targetAddress;
  1907. if( Server.IsIP( targetNameOrIP ) && IPAddress.TryParse( targetNameOrIP, out targetAddress ) ) {
  1908. try {
  1909. targetAddress.BanIP( player, reason, true, true );
  1910. } catch( PlayerOpException ex ) {
  1911. player.Message( ex.MessageColored );
  1912. }
  1913. } else {
  1914. PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches( player, targetNameOrIP );
  1915. if( target == null ) return;
  1916. try {
  1917. if( target.LastIP.Equals( IPAddress.Any ) || target.LastIP.Equals( IPAddress.None ) ) {
  1918. target.Ban( player, reason, true, true );
  1919. } else {
  1920. target.BanIP( player, reason, true, true );
  1921. }
  1922. } catch( PlayerOpException ex ) {
  1923. player.Message( ex.MessageColored );
  1924. if( ex.ErrorCode == PlayerOpExceptionCode.ReasonRequired ) {
  1925. FreezeIfAllowed( player, target );
  1926. }
  1927. }
  1928. }
  1929. }
  1930. static readonly CommandDescriptor CdUnban = new CommandDescriptor {
  1931. Name = "Unban",
  1932. Category = CommandCategory.Moderation,
  1933. IsConsoleSafe = true,
  1934. Permissions = new[] { Permission.Ban },
  1935. Usage = "/Unban PlayerName [Reason]",
  1936. Help = "&SRemoves ban for a specified player. Does NOT remove associated IP bans. " +
  1937. "Any text after the player name will be saved as a memo. ",
  1938. Handler = UnbanHandler
  1939. };
  1940. static void UnbanHandler( Player player, Command cmd ) {
  1941. string targetName = cmd.Next();
  1942. if( targetName == null ) {
  1943. CdUnban.PrintUsage( player );
  1944. return;
  1945. }
  1946. PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches( player, targetName );
  1947. if( target == null ) return;
  1948. string reason = cmd.NextAll();
  1949. try {
  1950. target.Unban( player, reason, true, true );
  1951. } catch( PlayerOpException ex ) {
  1952. player.Message( ex.MessageColored );
  1953. }
  1954. }
  1955. static readonly CommandDescriptor CdUnbanIP = new CommandDescriptor {
  1956. Name = "UnbanIP",
  1957. Category = CommandCategory.Moderation,
  1958. IsConsoleSafe = true,
  1959. Permissions = new[] { Permission.Ban, Permission.BanIP },
  1960. Usage = "/UnbanIP PlayerName|IPaddress [Reason]",
  1961. Help = "&SRemoves ban for a specified player's name and last known IP. " +
  1962. "You can also type in the IP address directly. " +
  1963. "Any text after the player name will be saved as a memo. ",
  1964. Handler = UnbanIPHandler
  1965. };
  1966. static void UnbanIPHandler( Player player, Command cmd ) {
  1967. string targetNameOrIP = cmd.Next();
  1968. if( targetNameOrIP == null ) {
  1969. CdUnbanIP.PrintUsage( player );
  1970. return;
  1971. }
  1972. string reason = cmd.NextAll();
  1973. try {
  1974. IPAddress targetAddress;
  1975. if( Server.IsIP( targetNameOrIP ) && IPAddress.TryParse( targetNameOrIP, out targetAddress ) ) {
  1976. targetAddress.UnbanIP( player, reason, true, true );
  1977. } else {
  1978. PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches( player, targetNameOrIP );
  1979. if( target == null ) return;
  1980. if( target.LastIP.Equals( IPAddress.Any ) || target.LastIP.Equals( IPAddress.None ) ) {
  1981. target.Unban( player, reason, true, true );
  1982. } else {
  1983. target.UnbanIP( player, reason, true, true );
  1984. }
  1985. }
  1986. } catch( PlayerOpException ex ) {
  1987. player.Message( ex.MessageColored );
  1988. }
  1989. }
  1990. static readonly CommandDescriptor CdUnbanAll = new CommandDescriptor {
  1991. Name = "UnbanAll",
  1992. Category = CommandCategory.Moderation,
  1993. IsConsoleSafe = true,
  1994. Permissions = new[] { Permission.Ban, Permission.BanIP, Permission.BanAll },
  1995. Usage = "/UnbanAll PlayerName|IPaddress [Reason]",
  1996. Help = "&SRemoves ban for a specified player's name, last known IP, and all other names associated with the IP. " +
  1997. "You can also type in the IP address directly. " +
  1998. "Any text after the player name will be saved as a memo. ",
  1999. Handler = UnbanAllHandler
  2000. };
  2001. static void UnbanAllHandler( Player player, Command cmd ) {
  2002. string targetNameOrIP = cmd.Next();
  2003. if( targetNameOrIP == null ) {
  2004. CdUnbanAll.PrintUsage( player );
  2005. return;
  2006. }
  2007. string reason = cmd.NextAll();
  2008. try {
  2009. IPAddress targetAddress;
  2010. if( Server.IsIP( targetNameOrIP ) && IPAddress.TryParse( targetNameOrIP, out targetAddress ) ) {
  2011. targetAddress.UnbanAll( player, reason, true, true );
  2012. } else {
  2013. PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches( player, targetNameOrIP );
  2014. if( target == null ) return;
  2015. if( target.LastIP.Equals( IPAddress.Any ) || target.LastIP.Equals( IPAddress.None ) ) {
  2016. target.Unban( player, reason, true, true );
  2017. } else {
  2018. target.UnbanAll( player, reason, true, true );
  2019. }
  2020. }
  2021. } catch( PlayerOpException ex ) {
  2022. player.Message( ex.MessageColored );
  2023. }
  2024. }
  2025. static readonly CommandDescriptor CdBanEx = new CommandDescriptor {
  2026. Name = "BanEx",
  2027. Category = CommandCategory.Moderation,
  2028. IsConsoleSafe = true,
  2029. Permissions = new[] { Permission.Ban, Permission.BanIP },
  2030. Usage = "/BanEx +PlayerName&S or &H/BanEx -PlayerName",
  2031. Help = "&SAdds or removes an IP-ban exemption for an account. " +
  2032. "Exempt accounts can log in from any IP, including banned ones.",
  2033. Handler = BanExHandler
  2034. };
  2035. static void BanExHandler( Player player, Command cmd ) {
  2036. string playerName = cmd.Next();
  2037. if( playerName == null || playerName.Length < 2 || (playerName[0] != '-' && playerName[0] != '+') ) {
  2038. CdBanEx.PrintUsage( player );
  2039. return;
  2040. }
  2041. bool addExemption = (playerName[0] == '+');
  2042. string targetName = playerName.Substring( 1 );
  2043. PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches( player, targetName );
  2044. if( target == null ) return;
  2045. switch( target.BanStatus ) {
  2046. case BanStatus.Banned:
  2047. if( addExemption ) {
  2048. player.Message( "Player {0}&S is currently banned. Unban before adding an exemption.",
  2049. target.ClassyName );
  2050. } else {
  2051. player.Message( "Player {0}&S is already banned. There is no exemption to remove.",
  2052. target.ClassyName );
  2053. }
  2054. break;
  2055. case BanStatus.IPBanExempt:
  2056. if( addExemption ) {
  2057. player.Message( "IP-Ban exemption already exists for player {0}", target.ClassyName );
  2058. } else {
  2059. player.Message( "IP-Ban exemption removed for player {0}",
  2060. target.ClassyName );
  2061. target.BanStatus = BanStatus.NotBanned;
  2062. }
  2063. break;
  2064. case BanStatus.NotBanned:
  2065. if( addExemption ) {
  2066. player.Message( "IP-Ban exemption added for player {0}",
  2067. target.ClassyName );
  2068. target.BanStatus = BanStatus.IPBanExempt;
  2069. } else {
  2070. player.Message( "No IP-Ban exemption exists for player {0}",
  2071. target.ClassyName );
  2072. }
  2073. break;
  2074. }
  2075. }
  2076. #endregion
  2077. #region Kick
  2078. static readonly CommandDescriptor CdKick = new CommandDescriptor {
  2079. Name = "Kick",
  2080. Aliases = new[] { "k" },
  2081. Category = CommandCategory.Moderation,
  2082. IsConsoleSafe = true,
  2083. Permissions = new[] { Permission.Kick },
  2084. Usage = "/Kick PlayerName [Reason]",
  2085. Help = "Kicks the specified player from the server. " +
  2086. "Optional kick reason/message is shown to the kicked player and logged.",
  2087. Handler = KickHandler
  2088. };
  2089. static void KickHandler( Player player, Command cmd ) {
  2090. string name = cmd.Next();
  2091. if( name == null ) {
  2092. player.Message( "Usage: &H/Kick PlayerName [Message]" );
  2093. return;
  2094. }
  2095. // find the target
  2096. Player target = Server.FindPlayerOrPrintMatches( player, name, false, true );
  2097. if( target == null ) return;
  2098. string reason = cmd.NextAll();
  2099. DateTime previousKickDate = target.Info.LastKickDate;
  2100. string previousKickedBy = target.Info.LastKickByClassy;
  2101. string previousKickReason = target.Info.LastKickReason;
  2102. // do the kick
  2103. try {
  2104. Player targetPlayer = target;
  2105. target.Kick( player, reason, LeaveReason.Kick, true, true, true );
  2106. WarnIfOtherPlayersOnIP( player, target.Info, targetPlayer );
  2107. } catch( PlayerOpException ex ) {
  2108. player.Message( ex.MessageColored );
  2109. if( ex.ErrorCode == PlayerOpExceptionCode.ReasonRequired ) {
  2110. FreezeIfAllowed( player, target.Info );
  2111. }
  2112. return;
  2113. }
  2114. // warn player if target has been kicked before
  2115. if( target.Info.TimesKicked > 1 ) {
  2116. player.Message( "Warning: {0}&S has been kicked {1} times before.",
  2117. target.ClassyName, target.Info.TimesKicked - 1 );
  2118. if( previousKickDate != DateTime.MinValue ) {
  2119. player.Message( "Most recent kick was {0} ago, by {1}",
  2120. DateTime.UtcNow.Subtract( previousKickDate ).ToMiniString(),
  2121. previousKickedBy );
  2122. }
  2123. if( !String.IsNullOrEmpty( previousKickReason ) ) {
  2124. player.Message( "Most recent kick reason was: {0}",
  2125. previousKickReason );
  2126. }
  2127. }
  2128. }
  2129. #endregion
  2130. #region Changing Rank (Promotion / Demotion)
  2131. static readonly CommandDescriptor CdRank = new CommandDescriptor {
  2132. Name = "Rank",
  2133. Aliases = new[] { "user", "promote", "demote" },
  2134. Category = CommandCategory.Moderation,
  2135. Permissions = new[] { Permission.Promote, Permission.Demote },
  2136. AnyPermission = true,
  2137. IsConsoleSafe = true,
  2138. Usage = "/Rank PlayerName RankName [Reason]",
  2139. Help = "Changes the rank of a player to a specified rank. " +
  2140. "Any text specified after the RankName will be saved as a memo.",
  2141. Handler = RankHandler
  2142. };
  2143. public static void RankHandler( Player player, Command cmd ) {
  2144. string name = cmd.Next();
  2145. string newRankName = cmd.Next();
  2146. // Check arguments
  2147. if( name == null || newRankName == null ) {
  2148. CdRank.PrintUsage( player );
  2149. player.Message( "See &H/Ranks&S for list of ranks." );
  2150. return;
  2151. }
  2152. // Parse rank name
  2153. Rank newRank = RankManager.FindRank( newRankName );
  2154. if( newRank == null ) {
  2155. player.MessageNoRank( newRankName );
  2156. return;
  2157. }
  2158. // Parse player name
  2159. if( name == "-" ) {
  2160. if( player.LastUsedPlayerName != null ) {
  2161. name = player.LastUsedPlayerName;
  2162. } else {
  2163. player.Message( "Cannot repeat player name: you haven't used any names yet." );
  2164. return;
  2165. }
  2166. }
  2167. PlayerInfo targetInfo = PlayerDB.FindPlayerInfoExact( name );
  2168. if( targetInfo == null ) {
  2169. if( !player.Can( Permission.EditPlayerDB ) ) {
  2170. player.MessageNoPlayer( name );
  2171. return;
  2172. }
  2173. if( !Player.IsValidName( name ) ) {
  2174. player.MessageInvalidPlayerName( name );
  2175. CdRank.PrintUsage( player );
  2176. return;
  2177. }
  2178. if( cmd.IsConfirmed ) {
  2179. if( newRank > RankManager.DefaultRank ) {
  2180. targetInfo = PlayerDB.AddFakeEntry( name, RankChangeType.Promoted );
  2181. } else {
  2182. targetInfo = PlayerDB.AddFakeEntry( name, RankChangeType.Demoted );
  2183. }
  2184. } else {
  2185. player.Confirm( cmd,
  2186. "Warning: Player \"{0}\" is not in the database (possible typo). Type the full name or",
  2187. name );
  2188. return;
  2189. }
  2190. }
  2191. try {
  2192. player.LastUsedPlayerName = targetInfo.Name;
  2193. //reset temprank values
  2194. if (targetInfo.isTempRanked)
  2195. {
  2196. targetInfo.isTempRanked = false;
  2197. targetInfo.tempRankTime = TimeSpan.FromSeconds(0);
  2198. }
  2199. targetInfo.ChangeRank( player, newRank, cmd.NextAll(), true, true, false );
  2200. } catch( PlayerOpException ex ) {
  2201. player.Message( ex.MessageColored );
  2202. }
  2203. }
  2204. #endregion
  2205. #region Hide
  2206. static readonly CommandDescriptor CdHide = new CommandDescriptor {
  2207. Name = "Hide",
  2208. Category = CommandCategory.Moderation,
  2209. Permissions = new[] { Permission.Hide },
  2210. Usage = "/Hide [silent]",
  2211. Help = "&SEnables invisible mode. It looks to other players like you left the server, " +
  2212. "but you can still do anything - chat, build, delete, type commands - as usual. " +
  2213. "Great way to spy on griefers and scare newbies. " +
  2214. "Call &H/Unhide&S to reveal yourself.",
  2215. Handler = HideHandler
  2216. };
  2217. static void HideHandler( Player player, Command cmd ) {
  2218. if( player.Info.IsHidden ) {
  2219. player.Message( "You are already hidden." );
  2220. return;
  2221. }
  2222. string silentString = cmd.Next();
  2223. bool silent = false;
  2224. if( silentString != null ) {
  2225. silent = silentString.Equals( "silent", StringComparison.OrdinalIgnoreCase );
  2226. }
  2227. player.Info.IsHidden = true;
  2228. player.Message( "&8You are now hidden." );
  2229. // to make it look like player just logged out in /Info
  2230. player.Info.LastSeen = DateTime.UtcNow;
  2231. if( !silent ) {
  2232. if( ConfigKey.ShowConnectionMessages.Enabled() ) {
  2233. Server.Players.CantSee( player ).Message( "&SPlayer {0}&S left the server.", player.ClassyName );
  2234. }
  2235. if( ConfigKey.IRCBotAnnounceServerJoins.Enabled() ) {
  2236. IRC.PlayerDisconnectedHandler( null, new PlayerDisconnectedEventArgs( player, LeaveReason.ClientQuit, true ) );
  2237. }
  2238. }
  2239. // for aware players: notify
  2240. Server.Players.CanSee( player ).Message( "&SPlayer {0}&S is now hidden.", player.ClassyName );
  2241. Player.RaisePlayerHideChangedEvent( player );
  2242. }
  2243. static readonly CommandDescriptor CdUnhide = new CommandDescriptor {
  2244. Name = "Unhide",
  2245. Category = CommandCategory.Moderation,
  2246. Permissions = new[] { Permission.Hide },
  2247. Usage = "/Unhide [silent]",
  2248. Help = "&SDisables the &H/Hide&S invisible mode. " +
  2249. "It looks to other players like you just joined the server.",
  2250. Handler = UnhideHandler
  2251. };
  2252. static void UnhideHandler( Player player, Command cmd ) {
  2253. if( player.World == null ) PlayerOpException.ThrowNoWorld( player );
  2254. if( !player.Info.IsHidden ) {
  2255. player.Message( "You are not currently hidden." );
  2256. return;
  2257. }
  2258. bool silent = cmd.HasNext;
  2259. // for aware players: notify
  2260. Server.Players.CanSee( player ).Message( "&SPlayer {0}&S is no longer hidden.",
  2261. player.ClassyName );
  2262. player.Message( "&8You are no longer hidden." );
  2263. player.Info.IsHidden = false;
  2264. if( !silent ) {
  2265. if( ConfigKey.ShowConnectionMessages.Enabled() ) {
  2266. // ReSharper disable AssignNullToNotNullAttribute
  2267. string msg = Server.MakePlayerConnectedMessage( player, false, player.World );
  2268. // ReSharper restore AssignNullToNotNullAttribute
  2269. Server.Players.CantSee( player ).Message( msg );
  2270. }
  2271. if( ConfigKey.IRCBotAnnounceServerJoins.Enabled() ) {
  2272. IRC.PlayerReadyHandler( null, new PlayerConnectedEventArgs( player, player.World ) );
  2273. }
  2274. }
  2275. Player.RaisePlayerHideChangedEvent( player );
  2276. }
  2277. #endregion
  2278. #region Set Spawn
  2279. static readonly CommandDescriptor CdSetSpawn = new CommandDescriptor {
  2280. Name = "SetSpawn",
  2281. Category = CommandCategory.Moderation | CommandCategory.World,
  2282. Permissions = new[] { Permission.SetSpawn },
  2283. Help = "&SAssigns your current location to be the spawn point of the map/world. " +
  2284. "If an optional PlayerName param is given, the spawn point of only that player is changed instead.",
  2285. Usage = "/SetSpawn [PlayerName]",
  2286. Handler = SetSpawnHandler
  2287. };
  2288. public static void SetSpawnHandler( Player player, Command cmd ) {
  2289. World playerWorld = player.World;
  2290. if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player );
  2291. string playerName = cmd.Next();
  2292. if( playerName == null ) {
  2293. Map map = player.WorldMap;
  2294. map.Spawn = player.Position;
  2295. player.TeleportTo( map.Spawn );
  2296. player.Send( PacketWriter.MakeAddEntity( 255, player.ListName, player.Position ) );
  2297. player.Message( "New spawn point saved." );
  2298. Logger.Log( LogType.UserActivity,
  2299. "{0} changed the spawned point.",
  2300. player.Name );
  2301. } else if( player.Can( Permission.Bring ) ) {
  2302. Player[] infos = playerWorld.FindPlayers( player, playerName );
  2303. if( infos.Length == 1 ) {
  2304. Player target = infos[0];
  2305. player.LastUsedPlayerName = target.Name;
  2306. if( player.Can( Permission.Bring, target.Info.Rank ) ) {
  2307. target.Send( PacketWriter.MakeAddEntity( 255, target.ListName, player.Position ) );
  2308. } else {
  2309. player.Message( "You may only set spawn of players ranked {0}&S or lower.",
  2310. player.Info.Rank.GetLimit( Permission.Bring ).ClassyName );
  2311. player.Message( "{0}&S is ranked {1}", target.ClassyName, target.Info.Rank.ClassyName );
  2312. }
  2313. } else if( infos.Length > 0 ) {
  2314. player.MessageManyMatches( "player", infos );
  2315. } else {
  2316. infos = Server.FindPlayers( player, playerName, true );
  2317. if( infos.Length > 0 ) {
  2318. player.Message( "You may only set spawn of players on the same world as you." );
  2319. } else {
  2320. player.MessageNoPlayer( playerName );
  2321. }
  2322. }
  2323. } else {
  2324. player.MessageNoAccess( CdSetSpawn );
  2325. }
  2326. }
  2327. #endregion
  2328. #region Freeze
  2329. static readonly CommandDescriptor CdFreeze = new CommandDescriptor {
  2330. Name = "Freeze",
  2331. Aliases = new[] { "f" },
  2332. Category = CommandCategory.Moderation,
  2333. IsConsoleSafe = true,
  2334. Permissions = new[] { Permission.Freeze },
  2335. Usage = "/Freeze PlayerName",
  2336. Help = "Freezes the specified player in place. " +
  2337. "This is usually effective, but not hacking-proof. " +
  2338. "To release the player, use &H/unfreeze PlayerName",
  2339. Handler = FreezeHandler
  2340. };
  2341. public static void FreezeHandler( Player player, Command cmd ) {
  2342. string name = cmd.Next();
  2343. if( name == null ) {
  2344. CdFreeze.PrintUsage( player );
  2345. return;
  2346. }
  2347. Player target = Server.FindPlayerOrPrintMatches( player, name, false, true );
  2348. if( target == null ) return;
  2349. try {
  2350. target.Info.Freeze( player, true, true );
  2351. } catch( PlayerOpException ex ) {
  2352. player.Message( ex.MessageColored );
  2353. }
  2354. }
  2355. static readonly CommandDescriptor CdUnfreeze = new CommandDescriptor {
  2356. Name = "Unfreeze",
  2357. Aliases = new[] { "uf" },
  2358. Category = CommandCategory.Moderation,
  2359. IsConsoleSafe = true,
  2360. Permissions = new[] { Permission.Freeze },
  2361. Usage = "/Unfreeze PlayerName",
  2362. Help = "Releases the player from a frozen state. See &H/Help Freeze&S for more information.",
  2363. Handler = UnfreezeHandler
  2364. };
  2365. static void UnfreezeHandler( Player player, Command cmd ) {
  2366. string name = cmd.Next();
  2367. if( name == null ) {
  2368. CdFreeze.PrintUsage( player );
  2369. return;
  2370. }
  2371. Player target = Server.FindPlayerOrPrintMatches( player, name, false, true );
  2372. if( target == null ) return;
  2373. try {
  2374. target.Info.Unfreeze( player, true, true );
  2375. } catch( PlayerOpException ex ) {
  2376. player.Message( ex.MessageColored );
  2377. }
  2378. }
  2379. #endregion
  2380. #region TP
  2381. static readonly CommandDescriptor CdTP = new CommandDescriptor {
  2382. Name = "TP",
  2383. Category = CommandCategory.Moderation,
  2384. Permissions = new[] { Permission.Teleport },
  2385. Usage = "/TP PlayerName&S or &H/TP X Y Z",
  2386. Help = "&STeleports you to a specified player's location. " +
  2387. "If coordinates are given, teleports to that location.",
  2388. Handler = TPHandler
  2389. };
  2390. static void TPHandler( Player player, Command cmd ) {
  2391. string name = cmd.Next();
  2392. if( name == null ) {
  2393. CdTP.PrintUsage( player );
  2394. return;
  2395. }
  2396. if( cmd.Next() != null ) {
  2397. cmd.Rewind();
  2398. int x, y, z;
  2399. if( cmd.NextInt( out x ) && cmd.NextInt( out y ) && cmd.NextInt( out z ) ) {
  2400. if( x <= -1024 || x >= 1024 || y <= -1024 || y >= 1024 || z <= -1024 || z >= 1024 ) {
  2401. player.Message( "Coordinates are outside the valid range!" );
  2402. } else {
  2403. player.previousLocation = player.Position;
  2404. player.previousWorld = null;
  2405. player.TeleportTo( new Position {
  2406. X = (short)(x * 32 + 16),
  2407. Y = (short)(y * 32 + 16),
  2408. Z = (short)(z * 32 + 16),
  2409. R = player.Position.R,
  2410. L = player.Position.L
  2411. } );
  2412. }
  2413. } else {
  2414. CdTP.PrintUsage( player );
  2415. }
  2416. } else {
  2417. if( name == "-" ) {
  2418. if( player.LastUsedPlayerName != null ) {
  2419. name = player.LastUsedPlayerName;
  2420. } else {
  2421. player.Message( "Cannot repeat player name: you haven't used any names yet." );
  2422. return;
  2423. }
  2424. }
  2425. Player[] matches = Server.FindPlayers( player, name, true );
  2426. if( matches.Length == 1 ) {
  2427. Player target = matches[0];
  2428. World targetWorld = target.World;
  2429. if( targetWorld == null ) PlayerOpException.ThrowNoWorld( target );
  2430. if( targetWorld == player.World ) {
  2431. player.previousLocation = player.Position;
  2432. player.previousWorld = null;
  2433. player.TeleportTo( target.Position );
  2434. } else {
  2435. switch( targetWorld.AccessSecurity.CheckDetailed( player.Info ) ) {
  2436. case SecurityCheckResult.Allowed:
  2437. case SecurityCheckResult.WhiteListed:
  2438. if( targetWorld.IsFull ) {
  2439. player.Message( "Cannot teleport to {0}&S because world {1}&S is full.",
  2440. target.ClassyName,
  2441. targetWorld.ClassyName );
  2442. return;
  2443. }
  2444. player.StopSpectating();
  2445. player.previousLocation = player.Position;
  2446. player.previousWorld = player.World;
  2447. player.JoinWorld( targetWorld, WorldChangeReason.Tp, target.Position );
  2448. break;
  2449. case SecurityCheckResult.BlackListed:
  2450. player.Message( "Cannot teleport to {0}&S because you are blacklisted on world {1}",
  2451. target.ClassyName,
  2452. targetWorld.ClassyName );
  2453. break;
  2454. case SecurityCheckResult.RankTooLow:
  2455. player.Message( "Cannot teleport to {0}&S because world {1}&S requires {2}+&S to join.",
  2456. target.ClassyName,
  2457. targetWorld.ClassyName,
  2458. targetWorld.AccessSecurity.MinRank.ClassyName );
  2459. break;
  2460. // TODO: case PermissionType.RankTooHigh:
  2461. }
  2462. }
  2463. } else if( matches.Length > 1 ) {
  2464. player.MessageManyMatches( "player", matches );
  2465. } else {
  2466. // Try to guess if player typed "/TP" instead of "/Join"
  2467. World[] worlds = WorldManager.FindWorlds( player, name );
  2468. if( worlds.Length == 1 ) {
  2469. player.LastUsedWorldName = worlds[0].Name;
  2470. player.StopSpectating();
  2471. player.ParseMessage( "/Join " + worlds[0].Name, false, true );
  2472. } else {
  2473. player.MessageNoPlayer( name );
  2474. }
  2475. }
  2476. }
  2477. }
  2478. #endregion
  2479. #region Bring / WorldBring / BringAll
  2480. static readonly CommandDescriptor CdBring = new CommandDescriptor {
  2481. Name = "Bring",
  2482. IsConsoleSafe = true,
  2483. Aliases = new[] { "summon", "fetch" },
  2484. Category = CommandCategory.Moderation,
  2485. Permissions = new[] { Permission.Bring },
  2486. Usage = "/Bring PlayerName [ToPlayer]",
  2487. Help = "Teleports another player to your location. " +
  2488. "If the optional second parameter is given, teleports player to another player.",
  2489. Handler = BringHandler
  2490. };
  2491. static void BringHandler( Player player, Command cmd ) {
  2492. string name = cmd.Next();
  2493. if( name == null ) {
  2494. CdBring.PrintUsage( player );
  2495. return;
  2496. }
  2497. // bringing someone to another player (instead of to self)
  2498. string toName = cmd.Next();
  2499. Player toPlayer = player;
  2500. if( toName != null ) {
  2501. toPlayer = Server.FindPlayerOrPrintMatches( player, toName, false, true );
  2502. if( toPlayer == null ) return;
  2503. } else if( toPlayer.World == null ) {
  2504. player.Message( "When used from console, /Bring requires both names to be given." );
  2505. return;
  2506. }
  2507. World world = toPlayer.World;
  2508. if( world == null ) PlayerOpException.ThrowNoWorld( toPlayer );
  2509. Player target = Server.FindPlayerOrPrintMatches( player, name, false, true );
  2510. if( target == null ) return;
  2511. if( !player.Can( Permission.Bring, target.Info.Rank ) ) {
  2512. player.Message( "You may only bring players ranked {0}&S or lower.",
  2513. player.Info.Rank.GetLimit( Permission.Bring ).ClassyName );
  2514. player.Message( "{0}&S is ranked {1}",
  2515. target.ClassyName, target.Info.Rank.ClassyName );
  2516. return;
  2517. }
  2518. if( target.World == world ) {
  2519. // teleport within the same world
  2520. target.previousLocation = target.Position;
  2521. target.previousWorld = null;
  2522. target.TeleportTo( toPlayer.Position );
  2523. target.Message("&8You were summoned by {0}", player.ClassyName);
  2524. } else {
  2525. // teleport to a different world
  2526. SecurityCheckResult check = world.AccessSecurity.CheckDetailed( target.Info );
  2527. if( check == SecurityCheckResult.RankTooHigh || check == SecurityCheckResult.RankTooLow ) {
  2528. if( player.CanJoin( world ) ) {
  2529. if( cmd.IsConfirmed ) {
  2530. BringPlayerToWorld( player, target, world, true, true );
  2531. } else {
  2532. player.Confirm( cmd,
  2533. "Player {0}&S is ranked too low to join {1}&S. Override world permissions?",
  2534. target.ClassyName,
  2535. world.ClassyName );
  2536. }
  2537. } else {
  2538. player.Message( "Neither you nor {0}&S are allowed to join world {1}",
  2539. target.ClassyName, world.ClassyName );
  2540. }
  2541. } else {
  2542. BringPlayerToWorld( player, target, world, false, true );
  2543. target.Message("&8You were summoned by {0}", player.ClassyName);
  2544. }
  2545. }
  2546. }
  2547. static readonly CommandDescriptor CdWorldBring = new CommandDescriptor {
  2548. Name = "WBring",
  2549. IsConsoleSafe = true,
  2550. Category = CommandCategory.Moderation,
  2551. Permissions = new[] { Permission.Bring },
  2552. Usage = "/WBring PlayerName WorldName",
  2553. Help = "&STeleports a player to the given world's spawn.",
  2554. Handler = WorldBringHandler
  2555. };
  2556. static void WorldBringHandler( Player player, Command cmd ) {
  2557. string playerName = cmd.Next();
  2558. string worldName = cmd.Next();
  2559. if( playerName == null || worldName == null ) {
  2560. CdWorldBring.PrintUsage( player );
  2561. return;
  2562. }
  2563. Player target = Server.FindPlayerOrPrintMatches( player, playerName, false, true );
  2564. World world = WorldManager.FindWorldOrPrintMatches( player, worldName );
  2565. if( target == null || world == null ) return;
  2566. if( target == player ) {
  2567. player.Message( "&WYou cannot &H/WBring&W yourself." );
  2568. return;
  2569. }
  2570. if( !player.Can( Permission.Bring, target.Info.Rank ) ) {
  2571. player.Message( "You may only bring players ranked {0}&S or lower.",
  2572. player.Info.Rank.GetLimit( Permission.Bring ).ClassyName );
  2573. player.Message( "{0}&S is ranked {1}",
  2574. target.ClassyName, target.Info.Rank.ClassyName );
  2575. return;
  2576. }
  2577. if( world == target.World ) {
  2578. player.Message( "Player {0}&S is already in world {1}&S. They were brought to spawn.",
  2579. target.ClassyName, world.ClassyName );
  2580. target.TeleportTo( target.WorldMap.Spawn );
  2581. return;
  2582. }
  2583. SecurityCheckResult check = world.AccessSecurity.CheckDetailed( target.Info );
  2584. if( check == SecurityCheckResult.RankTooHigh || check == SecurityCheckResult.RankTooLow ) {
  2585. if( player.CanJoin( world ) ) {
  2586. if( cmd.IsConfirmed ) {
  2587. BringPlayerToWorld( player, target, world, true, false );
  2588. } else {
  2589. player.Confirm( cmd,
  2590. "Player {0}&S is ranked too low to join {1}&S. Override world permissions?",
  2591. target.ClassyName,
  2592. world.ClassyName );
  2593. }
  2594. } else {
  2595. player.Message( "Neither you nor {0}&S are allowed to join world {1}",
  2596. target.ClassyName, world.ClassyName );
  2597. }
  2598. } else {
  2599. BringPlayerToWorld( player, target, world, false, false );
  2600. }
  2601. }
  2602. static readonly CommandDescriptor CdBringAll = new CommandDescriptor {
  2603. Name = "BringAll",
  2604. Category = CommandCategory.Moderation,
  2605. Permissions = new[] { Permission.Bring, Permission.BringAll },
  2606. Usage = "/BringAll [@Rank [@AnotherRank]] [*|World [AnotherWorld]]",
  2607. Help = "&STeleports all players from your world to you. " +
  2608. "If any world names are given, only teleports players from those worlds. " +
  2609. "If any rank names are given, only teleports players of those ranks.",
  2610. Handler = BringAllHandler
  2611. };
  2612. static void BringAllHandler( Player player, Command cmd ) {
  2613. if( player.World == null ) PlayerOpException.ThrowNoWorld( player );
  2614. List<World> targetWorlds = new List<World>();
  2615. List<Rank> targetRanks = new List<Rank>();
  2616. bool allWorlds = false;
  2617. bool allRanks = true;
  2618. // Parse the list of worlds and ranks
  2619. string arg;
  2620. while( (arg = cmd.Next()) != null ) {
  2621. if( arg.StartsWith( "@" ) ) {
  2622. Rank rank = RankManager.FindRank( arg.Substring( 1 ) );
  2623. if( rank == null ) {
  2624. player.Message( "Unknown rank: {0}", arg.Substring( 1 ) );
  2625. return;
  2626. } else {
  2627. if( player.Can( Permission.Bring, rank ) ) {
  2628. targetRanks.Add( rank );
  2629. } else {
  2630. player.Message( "&WYou are not allowed to bring players of rank {0}",
  2631. rank.ClassyName );
  2632. }
  2633. allRanks = false;
  2634. }
  2635. } else if( arg == "*" ) {
  2636. allWorlds = true;
  2637. } else {
  2638. World world = WorldManager.FindWorldOrPrintMatches( player, arg );
  2639. if( world == null ) return;
  2640. targetWorlds.Add( world );
  2641. }
  2642. }
  2643. // If no worlds were specified, use player's current world
  2644. if( !allWorlds && targetWorlds.Count == 0 ) {
  2645. targetWorlds.Add( player.World );
  2646. }
  2647. // Apply all the rank and world options
  2648. HashSet<Player> targetPlayers;
  2649. if( allRanks && allWorlds ) {
  2650. targetPlayers = new HashSet<Player>( Server.Players );
  2651. } else if( allWorlds ) {
  2652. targetPlayers = new HashSet<Player>();
  2653. foreach( Rank rank in targetRanks ) {
  2654. foreach( Player rankPlayer in Server.Players.Ranked( rank ) ) {
  2655. targetPlayers.Add( rankPlayer );
  2656. }
  2657. }
  2658. } else if( allRanks ) {
  2659. targetPlayers = new HashSet<Player>();
  2660. foreach( World world in targetWorlds ) {
  2661. foreach( Player worldPlayer in world.Players ) {
  2662. targetPlayers.Add( worldPlayer );
  2663. }
  2664. }
  2665. } else {
  2666. targetPlayers = new HashSet<Player>();
  2667. foreach( Rank rank in targetRanks ) {
  2668. foreach( World world in targetWorlds ) {
  2669. foreach( Player rankWorldPlayer in world.Players.Ranked( rank ) ) {
  2670. targetPlayers.Add( rankWorldPlayer );
  2671. }
  2672. }
  2673. }
  2674. }
  2675. Rank bringLimit = player.Info.Rank.GetLimit( Permission.Bring );
  2676. // Remove the player him/herself
  2677. targetPlayers.Remove( player );
  2678. int count = 0;
  2679. // Actually bring all the players
  2680. foreach( Player targetPlayer in targetPlayers.CanBeSeen( player )
  2681. .RankedAtMost( bringLimit ) ) {
  2682. if( targetPlayer.World == player.World ) {
  2683. // teleport within the same world
  2684. targetPlayer.TeleportTo( player.Position );
  2685. targetPlayer.Position = player.Position;
  2686. if( targetPlayer.Info.IsFrozen ) {
  2687. targetPlayer.Position = player.Position;
  2688. }
  2689. } else {
  2690. // teleport to a different world
  2691. BringPlayerToWorld( player, targetPlayer, player.World, false, true );
  2692. }
  2693. count++;
  2694. }
  2695. // Check if there's anyone to bring
  2696. if( count == 0 ) {
  2697. player.Message( "No players to bring!" );
  2698. } else {
  2699. player.Message( "Bringing {0} players...", count );
  2700. }
  2701. }
  2702. static void BringPlayerToWorld( [NotNull] Player player, [NotNull] Player target, [NotNull] World world,
  2703. bool overridePermissions, bool usePlayerPosition ) {
  2704. if( player == null ) throw new ArgumentNullException( "player" );
  2705. if( target == null ) throw new ArgumentNullException( "target" );
  2706. if( world == null ) throw new ArgumentNullException( "world" );
  2707. switch( world.AccessSecurity.CheckDetailed( target.Info ) ) {
  2708. case SecurityCheckResult.Allowed:
  2709. case SecurityCheckResult.WhiteListed:
  2710. if( world.IsFull ) {
  2711. player.Message( "Cannot bring {0}&S because world {1}&S is full.",
  2712. target.ClassyName,
  2713. world.ClassyName );
  2714. return;
  2715. }
  2716. target.StopSpectating();
  2717. if( usePlayerPosition ) {
  2718. target.JoinWorld( world, WorldChangeReason.Bring, player.Position );
  2719. } else {
  2720. target.JoinWorld( world, WorldChangeReason.Bring );
  2721. }
  2722. break;
  2723. case SecurityCheckResult.BlackListed:
  2724. player.Message( "Cannot bring {0}&S because he/she is blacklisted on world {1}",
  2725. target.ClassyName,
  2726. world.ClassyName );
  2727. break;
  2728. case SecurityCheckResult.RankTooLow:
  2729. if( overridePermissions ) {
  2730. target.StopSpectating();
  2731. if( usePlayerPosition ) {
  2732. target.JoinWorld( world, WorldChangeReason.Bring, player.Position );
  2733. } else {
  2734. target.JoinWorld( world, WorldChangeReason.Bring );
  2735. }
  2736. } else {
  2737. player.Message( "Cannot bring {0}&S because world {1}&S requires {2}+&S to join.",
  2738. target.ClassyName,
  2739. world.ClassyName,
  2740. world.AccessSecurity.MinRank.ClassyName );
  2741. }
  2742. break;
  2743. // TODO: case PermissionType.RankTooHigh:
  2744. }
  2745. }
  2746. #endregion
  2747. #region Patrol & SpecPatrol
  2748. static readonly CommandDescriptor CdPatrol = new CommandDescriptor {
  2749. Name = "Patrol",
  2750. Aliases = new[] { "pat" },
  2751. Category = CommandCategory.Moderation,
  2752. Permissions = new[] { Permission.Patrol },
  2753. Help = "Teleports you to the next player in need of checking.",
  2754. Handler = PatrolHandler
  2755. };
  2756. static void PatrolHandler( Player player, Command cmd ) {
  2757. World playerWorld = player.World;
  2758. if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player );
  2759. Player target = playerWorld.GetNextPatrolTarget( player );
  2760. if( target == null ) {
  2761. player.Message( "Patrol: No one to patrol in this world." );
  2762. return;
  2763. }
  2764. player.TeleportTo( target.Position );
  2765. player.Message( "Patrol: Teleporting to {0}", target.ClassyName );
  2766. }
  2767. static readonly CommandDescriptor CdSpecPatrol = new CommandDescriptor {
  2768. Name = "SpecPatrol",
  2769. Aliases = new[] { "spat" },
  2770. Category = CommandCategory.Moderation,
  2771. Permissions = new[] { Permission.Patrol, Permission.Spectate },
  2772. Help = "Teleports you to the next player in need of checking.",
  2773. Handler = SpecPatrolHandler
  2774. };
  2775. static void SpecPatrolHandler( Player player, Command cmd ) {
  2776. World playerWorld = player.World;
  2777. if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player );
  2778. Player target = playerWorld.GetNextPatrolTarget( player,
  2779. p => player.Can( Permission.Spectate, p.Info.Rank ),
  2780. true );
  2781. if( target == null ) {
  2782. player.Message( "Patrol: No one to spec-patrol in this world." );
  2783. return;
  2784. }
  2785. target.LastPatrolTime = DateTime.UtcNow;
  2786. player.Spectate( target );
  2787. }
  2788. #endregion
  2789. #region Mute / Unmute
  2790. static readonly TimeSpan MaxMuteDuration = TimeSpan.FromDays( 700 ); // 100w0d
  2791. static readonly CommandDescriptor CdMute = new CommandDescriptor {
  2792. Name = "Mute",
  2793. Category = CommandCategory.Moderation | CommandCategory.Chat,
  2794. IsConsoleSafe = true,
  2795. Permissions = new[] { Permission.Mute },
  2796. Help = "&SMutes a player for a specified length of time.",
  2797. Usage = "/Mute PlayerName Duration",
  2798. Handler = MuteHandler
  2799. };
  2800. static void MuteHandler( Player player, Command cmd ) {
  2801. string targetName = cmd.Next();
  2802. string timeString = cmd.Next();
  2803. TimeSpan duration;
  2804. // validate command parameters
  2805. if( String.IsNullOrEmpty( targetName ) || String.IsNullOrEmpty( timeString ) ||
  2806. !timeString.TryParseMiniTimespan( out duration ) || duration <= TimeSpan.Zero ) {
  2807. CdMute.PrintUsage( player );
  2808. return;
  2809. }
  2810. // check if given time exceeds maximum (700 days)
  2811. if( duration > MaxMuteDuration ) {
  2812. player.Message( "Maximum mute duration is {0}.", MaxMuteDuration.ToMiniString() );
  2813. duration = MaxMuteDuration;
  2814. }
  2815. // find the target
  2816. Player target = Server.FindPlayerOrPrintMatches( player, targetName, false, true );
  2817. if( target == null ) return;
  2818. // actually mute
  2819. try {
  2820. target.Info.Mute( player, duration, true, true );
  2821. } catch( PlayerOpException ex ) {
  2822. player.Message( ex.MessageColored );
  2823. }
  2824. }
  2825. static readonly CommandDescriptor CdUnmute = new CommandDescriptor {
  2826. Name = "Unmute",
  2827. Category = CommandCategory.Moderation | CommandCategory.Chat,
  2828. IsConsoleSafe = true,
  2829. Permissions = new[] { Permission.Mute },
  2830. Help = "&SUnmutes a player.",
  2831. Usage = "/Unmute PlayerName",
  2832. Handler = UnmuteHandler
  2833. };
  2834. static void UnmuteHandler( Player player, Command cmd ) {
  2835. string targetName = cmd.Next();
  2836. if( String.IsNullOrEmpty( targetName ) ) {
  2837. CdUnmute.PrintUsage( player );
  2838. return;
  2839. }
  2840. // find target
  2841. Player target = Server.FindPlayerOrPrintMatches( player, targetName, false, true );
  2842. if( target == null ) return;
  2843. try {
  2844. target.Info.Unmute( player, true, true );
  2845. } catch( PlayerOpException ex ) {
  2846. player.Message( ex.MessageColored );
  2847. }
  2848. }
  2849. #endregion
  2850. #region Spectate / Unspectate
  2851. static readonly CommandDescriptor CdSpectate = new CommandDescriptor {
  2852. Name = "Spectate",
  2853. Aliases = new[] { "follow", "spec" },
  2854. Category = CommandCategory.Moderation,
  2855. Permissions = new[] { Permission.Spectate },
  2856. Usage = "/Spectate PlayerName",
  2857. Handler = SpectateHandler
  2858. };
  2859. static void SpectateHandler( Player player, Command cmd ) {
  2860. string targetName = cmd.Next();
  2861. if( targetName == null ) {
  2862. PlayerInfo lastSpec = player.LastSpectatedPlayer;
  2863. if( lastSpec != null ) {
  2864. Player spec = player.SpectatedPlayer;
  2865. if( spec != null )
  2866. {
  2867. if (spec.World.Name != player.World.Name)
  2868. {
  2869. player.JoinWorld(spec.World, WorldChangeReason.SpectateTargetJoined);
  2870. player.Message("Joined " + spec.World.Name + " to continue spectating " + spec.ClassyName);
  2871. }
  2872. player.Message( "Now spectating {0}", spec.ClassyName );
  2873. }
  2874. else
  2875. {
  2876. player.Message( "Last spectated {0}", lastSpec.ClassyName );
  2877. }
  2878. } else {
  2879. CdSpectate.PrintUsage( player );
  2880. }
  2881. return;
  2882. }
  2883. Player target = Server.FindPlayerOrPrintMatches( player, targetName, false, true );
  2884. if( target == null ) return;
  2885. if( target == player ) {
  2886. player.Message( "You cannot spectate yourself." );
  2887. return;
  2888. }
  2889. if( !player.Can( Permission.Spectate, target.Info.Rank ) ) {
  2890. player.Message( "You may only spectate players ranked {0}&S or lower.",
  2891. player.Info.Rank.GetLimit( Permission.Spectate ).ClassyName );
  2892. player.Message( "{0}&S is ranked {1}",
  2893. target.ClassyName, target.Info.Rank.ClassyName );
  2894. return;
  2895. }
  2896. if( !player.Spectate( target ) ) {
  2897. player.Message( "Already spectating {0}", target.ClassyName );
  2898. }
  2899. }
  2900. static readonly CommandDescriptor CdUnspectate = new CommandDescriptor {
  2901. Name = "Unspectate",
  2902. Aliases = new[] { "unfollow", "unspec" },
  2903. Category = CommandCategory.Moderation,
  2904. Permissions = new[] { Permission.Spectate },
  2905. NotRepeatable = true,
  2906. Handler = UnspectateHandler
  2907. };
  2908. static void UnspectateHandler( Player player, Command cmd ) {
  2909. if( !player.StopSpectating() ) {
  2910. player.Message( "You are not currently spectating anyone." );
  2911. }
  2912. }
  2913. #endregion
  2914. // freeze target if player is allowed to do so
  2915. static void FreezeIfAllowed( Player player, PlayerInfo targetInfo ) {
  2916. if( targetInfo.IsOnline && !targetInfo.IsFrozen && player.Can( Permission.Freeze, targetInfo.Rank ) ) {
  2917. try {
  2918. targetInfo.Freeze( player, true, true );
  2919. player.Message( "Player {0}&S has been frozen while you retry.", targetInfo.ClassyName );
  2920. } catch( PlayerOpException ) { }
  2921. }
  2922. }
  2923. // warn player if others are still online from target's IP
  2924. static void WarnIfOtherPlayersOnIP( Player player, PlayerInfo targetInfo, Player except ) {
  2925. Player[] otherPlayers = Server.Players.FromIP( targetInfo.LastIP )
  2926. .Except( except )
  2927. .ToArray();
  2928. if( otherPlayers.Length > 0 ) {
  2929. player.Message( "&WWarning: Other player(s) share IP with {0}&W: {1}",
  2930. targetInfo.ClassyName,
  2931. otherPlayers.JoinToClassyString() );
  2932. }
  2933. }
  2934. }
  2935. }