TGAM123

NTeleportation plugin update

Dec 23rd, 2018
229
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 144.32 KB | None | 0 0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.Linq;
  6. using Facepunch;
  7. using Newtonsoft.Json;
  8. using Newtonsoft.Json.Converters;
  9. using Newtonsoft.Json.Linq;
  10. using Oxide.Core;
  11. using Oxide.Core.Configuration;
  12. using Oxide.Core.Plugins;
  13. using Oxide.Game.Rust;
  14. using Rust;
  15. using UnityEngine;
  16.  
  17. namespace Oxide.Plugins
  18. {
  19.     [Info("NTeleportation", "RFC1920", "1.0.34", ResourceId = 1832)]
  20.     class NTeleportation : RustPlugin
  21.     {
  22.         private const string NewLine = "\n";
  23.         private const string ConfigDefaultPermVip = "nteleportation.vip";
  24.         private const string PermDeleteHome = "nteleportation.deletehome";
  25.         private const string PermHomeHomes = "nteleportation.homehomes";
  26.         private const string PermImportHomes = "nteleportation.importhomes";
  27.         private const string PermRadiusHome = "nteleportation.radiushome";
  28.         private const string PermTp = "nteleportation.tp";
  29.         private const string PermTpB = "nteleportation.tpb";
  30.         private const string PermTpConsole = "nteleportation.tpconsole";
  31.         private const string PermTpHome = "nteleportation.tphome";
  32.         private const string PermTpTown = "nteleportation.tptown";
  33.         private const string PermTpN = "nteleportation.tpn";
  34.         private const string PermTpL = "nteleportation.tpl";
  35.         private const string PermTpRemove = "nteleportation.tpremove";
  36.         private const string PermTpSave = "nteleportation.tpsave";
  37.         private const string PermWipeHomes = "nteleportation.wipehomes";
  38.         private const string PermCraftHome = "nteleportation.crafthome";
  39.         private const string PermCraftTown = "nteleportation.crafttown";
  40.         private const string PermCraftTpR = "nteleportation.crafttpr";
  41.         private DynamicConfigFile dataAdmin;
  42.         private DynamicConfigFile dataHome;
  43.         private DynamicConfigFile dataTPR;
  44.         private DynamicConfigFile dataTown;
  45.         private Dictionary<ulong, AdminData> Admin;
  46.         private Dictionary<ulong, HomeData> Home;
  47.         private Dictionary<ulong, TeleportData> TPR;
  48.         private Dictionary<ulong, TeleportData> Town;
  49.         private bool changedAdmin;
  50.         private bool changedHome;
  51.         private bool changedTPR;
  52.         private bool changedTown;
  53.         private ConfigData configData;
  54.         private float boundary;
  55.         private readonly int triggerLayer = LayerMask.GetMask("Trigger");
  56.         private readonly int groundLayer = LayerMask.GetMask("Terrain", "World");
  57.         private readonly int buildingLayer = LayerMask.GetMask("Terrain", "World", "Construction", "Deployed");
  58.         private readonly int blockLayer = LayerMask.GetMask("Construction");
  59.         private readonly Dictionary<ulong, TeleportTimer> TeleportTimers = new Dictionary<ulong, TeleportTimer>();
  60.         private readonly Dictionary<ulong, Timer> PendingRequests = new Dictionary<ulong, Timer>();
  61.         private readonly Dictionary<ulong, BasePlayer> PlayersRequests = new Dictionary<ulong, BasePlayer>();
  62.         private readonly Dictionary<int, string> ReverseBlockedItems = new Dictionary<int, string>();
  63.         private readonly HashSet<ulong> teleporting = new HashSet<ulong>();
  64.  
  65.         [PluginReference]
  66.         private Plugin Clans, Economics, ServerRewards, Friends, RustIO;
  67.  
  68.         class ConfigData
  69.         {
  70.             public SettingsData Settings { get; set; }
  71.             public AdminSettingsData Admin { get; set; }
  72.             public HomesSettingsData Home { get; set; }
  73.             public TPRData TPR { get; set; }
  74.             public TownData Town { get; set; }
  75.             public VersionNumber Version { get; set; }
  76.         }
  77.  
  78.         class SettingsData
  79.         {
  80.             public string ChatName { get; set; }
  81.             public bool HomesEnabled { get; set; }
  82.             public bool TPREnabled { get; set; }
  83.             public bool TownEnabled { get; set; }
  84.             public bool InterruptTPOnHurt { get; set; }
  85.             public Dictionary<string, string> BlockedItems { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  86.             public string BypassCMD { get; set; }
  87.             public bool UseEconomics { get; set; }
  88.             public bool UseServerRewards { get; set; }
  89.         }
  90.  
  91.         class AdminSettingsData
  92.         {
  93.             public bool AnnounceTeleportToTarget { get; set; }
  94.             public bool UseableByModerators { get; set; }
  95.             public int LocationRadius { get; set; }
  96.             public int TeleportNearDefaultDistance { get; set; }
  97.         }
  98.  
  99.         class HomesSettingsData
  100.         {
  101.             public int HomesLimit { get; set; }
  102.             public Dictionary<string, int> VIPHomesLimits { get; set; }
  103.             public int Cooldown { get; set; }
  104.             public int Countdown { get; set; }
  105.             public int DailyLimit { get; set; }
  106.             public Dictionary<string, int> VIPDailyLimits { get; set; }
  107.             public Dictionary<string, int> VIPCooldowns { get; set; }
  108.             public Dictionary<string, int> VIPCountdowns { get; set; }
  109.             public int LocationRadius { get; set; }
  110.             public bool ForceOnTopOfFoundation { get; set; }
  111.             public bool CheckFoundationForOwner { get; set; }
  112.             public bool UseFriends { get; set; }
  113.             public bool UsableOutOfBuildingBlocked { get; set; }
  114.             public bool UsableIntoBuildingBlocked { get; set; }
  115.             public bool CupOwnerAllowOnBuildingBlocked { get; set; }
  116.             public bool AllowIceberg { get; set; }
  117.             public bool AllowCave { get; set; }
  118.             public bool AllowCraft { get; set; }
  119.             public bool AllowAboveFoundation { get; set; }
  120.             public bool CheckValidOnList { get; set; }
  121.             public int Pay { get; set; }
  122.             public int Bypass { get; set; }
  123.         }
  124.  
  125.         class TPRData
  126.         {
  127.             public int Cooldown { get; set; }
  128.             public int Countdown { get; set; }
  129.             public int DailyLimit { get; set; }
  130.             public Dictionary<string, int> VIPDailyLimits { get; set; }
  131.             public Dictionary<string, int> VIPCooldowns { get; set; }
  132.             public Dictionary<string, int> VIPCountdowns { get; set; }
  133.             public int RequestDuration { get; set; }
  134.             public bool BlockTPAOnCeiling { get; set; }
  135.             public bool UsableOutOfBuildingBlocked { get; set; }
  136.             public bool UsableIntoBuildingBlocked { get; set; }
  137.             public bool CupOwnerAllowOnBuildingBlocked { get; set; }
  138.             public bool AllowCraft { get; set; }
  139.             public int Pay { get; set; }
  140.             public int Bypass { get; set; }
  141.         }
  142.  
  143.         class TownData
  144.         {
  145.             public int Cooldown { get; set; }
  146.             public int Countdown { get; set; }
  147.             public int DailyLimit { get; set; }
  148.             public Dictionary<string, int> VIPDailyLimits { get; set; }
  149.             public Dictionary<string, int> VIPCooldowns { get; set; }
  150.             public Dictionary<string, int> VIPCountdowns { get; set; }
  151.             public Vector3 Location { get; set; }
  152.             public bool UsableOutOfBuildingBlocked { get; set; }
  153.             public bool AllowCraft { get; set; }
  154.             public int Pay { get; set; }
  155.             public int Bypass { get; set; }
  156.         }
  157.  
  158.         class AdminData
  159.         {
  160.             [JsonProperty("pl")]
  161.             public Vector3 PreviousLocation { get; set; }
  162.  
  163.             [JsonProperty("l")]
  164.             public Dictionary<string, Vector3> Locations { get; set; } = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);
  165.         }
  166.  
  167.         class HomeData
  168.         {
  169.             [JsonProperty("l")]
  170.             public Dictionary<string, Vector3> Locations { get; set; } = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);
  171.  
  172.             [JsonProperty("t")]
  173.             public TeleportData Teleports { get; set; } = new TeleportData();
  174.         }
  175.  
  176.         class TeleportData
  177.         {
  178.             [JsonProperty("a")]
  179.             public int Amount { get; set; }
  180.  
  181.             [JsonProperty("d")]
  182.             public string Date { get; set; }
  183.  
  184.             [JsonProperty("t")]
  185.             public int Timestamp { get; set; }
  186.         }
  187.  
  188.         class TeleportTimer
  189.         {
  190.             public Timer Timer { get; set; }
  191.             public BasePlayer OriginPlayer { get; set; }
  192.             public BasePlayer TargetPlayer { get; set; }
  193.         }
  194.  
  195.         protected override void LoadDefaultConfig()
  196.         {
  197.             Config.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
  198.             Config.Settings.Converters = new JsonConverter[] { new UnityVector3Converter() };
  199.             Config.WriteObject(new ConfigData
  200.             {
  201.                 Settings = new SettingsData
  202.                 {
  203.                     ChatName = "<color=red>Teleportation</color>: ",
  204.                     HomesEnabled = true,
  205.                     TPREnabled = true,
  206.                     TownEnabled = true,
  207.                     InterruptTPOnHurt = true,
  208.                     BypassCMD = "pay",
  209.                     UseEconomics = false,
  210.                     UseServerRewards = false
  211.                 },
  212.                 Admin = new AdminSettingsData
  213.                 {
  214.                     AnnounceTeleportToTarget = false,
  215.                     UseableByModerators = true,
  216.                     LocationRadius = 25,
  217.                     TeleportNearDefaultDistance = 30
  218.                 },
  219.                 Home = new HomesSettingsData
  220.                 {
  221.                     HomesLimit = 2,
  222.                     VIPHomesLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
  223.                     Cooldown = 600,
  224.                     Countdown = 15,
  225.                     DailyLimit = 5,
  226.                     VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
  227.                     VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
  228.                     VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
  229.                     LocationRadius = 25,
  230.                     ForceOnTopOfFoundation = true,
  231.                     CheckFoundationForOwner = true,
  232.                     UseFriends = true,
  233.                     AllowAboveFoundation = true,
  234.                     CheckValidOnList = false,
  235.                     CupOwnerAllowOnBuildingBlocked = true
  236.                 },
  237.                 TPR = new TPRData
  238.                 {
  239.                     Cooldown = 600,
  240.                     Countdown = 15,
  241.                     DailyLimit = 5,
  242.                     VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
  243.                     VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
  244.                     VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
  245.                     RequestDuration = 30,
  246.                     BlockTPAOnCeiling = true,
  247.                     CupOwnerAllowOnBuildingBlocked = true
  248.                 },
  249.                 Town = new TownData
  250.                 {
  251.                     Cooldown = 600,
  252.                     Countdown = 15,
  253.                     DailyLimit = 5,
  254.                     VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
  255.                     VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } },
  256.                     VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } }
  257.                 },
  258.                 Version = Version
  259.             }, true);
  260.         }
  261.  
  262.         private void Init()
  263.         {
  264.             lang.RegisterMessages(new Dictionary<string, string>
  265.             {
  266.                 {"AdminTP", "You teleported to {0}!"},
  267.                 {"AdminTPTarget", "{0} teleported to you!"},
  268.                 {"AdminTPPlayers", "You teleported {0} to {1}!"},
  269.                 {"AdminTPPlayer", "{0} teleported you to {1}!"},
  270.                 {"AdminTPPlayerTarget", "{0} teleported {1} to you!"},
  271.                 {"AdminTPCoordinates", "You teleported to {0}!"},
  272.                 {"AdminTPTargetCoordinates", "You teleported {0} to {1}!"},
  273.                 {"AdminTPOutOfBounds", "You tried to teleport to a set of coordinates outside the map boundaries!"},
  274.                 {"AdminTPBoundaries", "X and Z values need to be between -{0} and {0} while the Y value needs to be between -100 and 2000!"},
  275.                 {"AdminTPLocation", "You teleported to {0}!"},
  276.                 {"AdminTPLocationSave", "You have saved the current location!"},
  277.                 {"AdminTPLocationRemove", "You have removed the location {0}!"},
  278.                 {"AdminLocationList", "The following locations are available:"},
  279.                 {"AdminLocationListEmpty", "You haven't saved any locations!"},
  280.                 {"AdminTPBack", "You've teleported back to your previous location!"},
  281.                 {"AdminTPBackSave", "Your previous location has been saved, use /tpb to teleport back!"},
  282.                 {"AdminTPTargetCoordinatesTarget", "{0} teleported you to {1}!"},
  283.                 {"AdminTPConsoleTP", "You were teleported to {0}"},
  284.                 {"AdminTPConsoleTPPlayer", "You were teleported to {0}"},
  285.                 {"AdminTPConsoleTPPlayerTarget", "{0} was teleported to you!"},
  286.                 {"HomeTP", "You teleported to your home '{0}'!"},
  287.                 {"HomeAdminTP", "You teleported to {0}'s home '{1}'!"},
  288.                 {"HomeSave", "You have saved the current location as your home!"},
  289.                 {"HomeNoFoundation", "You can only use a home location on a foundation!"},
  290.                 {"HomeFoundationNotOwned", "You can't use home on someone else's house."},
  291.                 {"HomeFoundationUnderneathFoundation", "You can't use home on a foundation that is underneath another foundation."},
  292.                 {"HomeFoundationNotFriendsOwned", "You or a friend need to own the house to use home!"},
  293.                 {"HomeRemovedInvalid", "Your home '{0}' was removed because not on a foundation or not owned!"},
  294.                 {"HomeRemovedInsideBlock", "Your home '{0}' was removed because inside a foundation!"},
  295.                 {"HomeRemove", "You have removed your home {0}!"},
  296.                 {"HomeDelete", "You have removed {0}'s home '{1}'!"},
  297.                 {"HomeList", "The following homes are available:"},
  298.                 {"HomeListEmpty", "You haven't saved any homes!"},
  299.                 {"HomeMaxLocations", "Unable to set your home here, you have reached the maximum of {0} homes!"},
  300.                 {"HomeQuota", "You have set {0} of the maximum {1} homes!"},
  301.                 {"HomeTPStarted", "Teleporting to your home {0} in {1} seconds!"},
  302.                 {"PayToHome", "Standard payment of {0} applies to all home teleports!"},
  303.                 {"PayToTown", "Standard payment of {0} applies to all town teleports!"},
  304.                 {"PayToTPR", "Standard payment of {0} applies to all tprs!"},
  305.                 {"HomeTPCooldown", "Your teleport is currently on cooldown. You'll have to wait {0} for your next teleport."},
  306.                 {"HomeTPCooldownBypass", "Your teleport was currently on cooldown. You chose to bypass that by paying {0} from your balance."},
  307.                 {"HomeTPCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
  308.                 {"HomeTPCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
  309.                 {"HomeTPCooldownBypassP2", "Type /home NAME {0}." },
  310.                 {"HomeTPLimitReached", "You have reached the daily limit of {0} teleports today!"},
  311.                 {"HomeTPAmount", "You have {0} home teleports left today!"},
  312.                 {"HomesListWiped", "You have wiped all the saved home locations!"},
  313.                 {"HomeTPBuildingBlocked", "You can't set your home if you are not allowed to build in this zone!"},
  314.                 {"HomeTPSwimming", "You can't set your home while swimming!"},
  315.                 {"HomeTPCrafting", "You can't set your home while crafting!"},
  316.                 {"Request", "You've requested a teleport to {0}!"},
  317.                 {"RequestTarget", "{0} requested to be teleported to you! Use '/tpa' to accept!"},
  318.                 {"PendingRequest", "You already have a request pending, cancel that request or wait until it gets accepted or times out!"},
  319.                 {"PendingRequestTarget", "The player you wish to teleport to already has a pending request, try again later!"},
  320.                 {"NoPendingRequest", "You have no pending teleport request!"},
  321.                 {"AcceptOnRoof", "You can't accept a teleport while you're on a ceiling, get to ground level!"},
  322.                 {"Accept", "{0} has accepted your teleport request! Teleporting in {1} seconds!"},
  323.                 {"AcceptTarget", "You've accepted the teleport request of {0}!"},
  324.                 {"NotAllowed", "You are not allowed to use this command!"},
  325.                 {"Success", "You teleported to {0}!"},
  326.                 {"SuccessTarget", "{0} teleported to you!"},
  327.                 {"Cancelled", "Your teleport request to {0} was cancelled!"},
  328.                 {"CancelledTarget", "{0} teleport request was cancelled!"},
  329.                 {"TPCancelled", "Your teleport was cancelled!"},
  330.                 {"TPCancelledTarget", "{0} cancelled teleport!"},
  331.                 {"TPYouCancelledTarget", "You cancelled {0} teleport!"},
  332.                 {"TimedOut", "{0} did not answer your request in time!"},
  333.                 {"TimedOutTarget", "You did not answer {0}'s teleport request in time!"},
  334.                 {"TargetDisconnected", "{0} has disconnected, your teleport was cancelled!"},
  335.                 {"TPRCooldown", "Your teleport requests are currently on cooldown. You'll have to wait {0} to send your next teleport request."},
  336.                 {"TPRCooldownBypass", "Your teleport request was on cooldown. You chose to bypass that by paying {0} from your balance."},
  337.                 {"TPRCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
  338.                 {"TPRCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
  339.                 {"TPMoney", "{0} deducted from your account!"},
  340.                 {"TPNoMoney", "You do not have {0} in any account!"},
  341.                 {"TPRCooldownBypassP2", "Type /tpr {0}." },
  342.                 {"TPRLimitReached", "You have reached the daily limit of {0} teleport requests today!"},
  343.                 {"TPRAmount", "You have {0} teleport requests left today!"},
  344.                 {"TPRTarget", "Your target is currently not available!"},
  345.                 {"TPDead", "You can't teleport while being dead!"},
  346.                 {"TPWounded", "You can't teleport while being wounded!"},
  347.                 {"TPMounted", "You can't teleport while seated!"},
  348.                 {"TPBuildingBlocked", "You can't teleport while in a building blocked zone!"},
  349.                 {"TPTargetBuildingBlocked", "You can't teleport in a building blocked zone!"},
  350.                 {"TPTargetInsideBlock", "You can't teleport into a foundation!"},
  351.                 {"TPSwimming", "You can't teleport while swimming!"},
  352.                 {"TPCargoShip", "You can't teleport from the cargo ship!"},
  353.                 {"TPHotAirBalloon", "You can't teleport to or from a hot air balloon!"},
  354.                 {"TPSafeZone", "You can't teleport from a safezone!"},
  355.                 {"TPCrafting", "You can't teleport while crafting!"},
  356.                 {"TPBlockedItem", "You can't teleport while carrying: {0}!"},
  357.                 {"TownTP", "You teleported to town!"},
  358.                 {"TownTPNotSet", "Town is currently not set!"},
  359.                 {"TownTPLocation", "You have set the town location set to {0}!"},
  360.                 {"TownTPStarted", "Teleporting to town in {0} seconds!"},
  361.                 {"TownTPCooldown", "Your teleport is currently on cooldown. You'll have to wait {0} for your next teleport."},
  362.                 {"TownTPCooldownBypass", "Your teleport request was on cooldown. You chose to bypass that by paying {0} from your balance."},
  363.                 {"TownTPCooldownBypassF", "Your teleport is currently on cooldown. You do not have sufficient funds - {0} - to bypass."},
  364.                 {"TownTPCooldownBypassP", "You may choose to pay {0} to bypass this cooldown." },
  365.                 {"TownTPCooldownBypassP2", "Type /town {0}." },
  366.                 {"TownTPLimitReached", "You have reached the daily limit of {0} teleports today!"},
  367.                 {"TownTPAmount", "You have {0} town teleports left today!"},
  368.                 {"Interrupted", "Your teleport was interrupted!"},
  369.                 {"InterruptedTarget", "{0}'s teleport was interrupted!"},
  370.                 {"Unlimited", "Unlimited"},
  371.                 {
  372.                     "TPInfoGeneral", string.Join(NewLine, new[]
  373.                     {
  374.                         "Please specify the module you want to view the info of.",
  375.                         "The available modules are: ",
  376.                     })
  377.                 },
  378.                 {
  379.                     "TPHelpGeneral", string.Join(NewLine, new[]
  380.                     {
  381.                         "/tpinfo - Shows limits and cooldowns.",
  382.                         "Please specify the module you want to view the help of.",
  383.                         "The available modules are: ",
  384.                     })
  385.                 },
  386.                 {
  387.                     "TPHelpadmintp", string.Join(NewLine, new[]
  388.                     {
  389.                         "As an admin you have access to the following commands:",
  390.                         "/tp \"targetplayer\" - Teleports yourself to the target player.",
  391.                         "/tp \"player\" \"targetplayer\" - Teleports the player to the target player.",
  392.                         "/tp x y z - Teleports you to the set of coordinates.",
  393.                         "/tpl - Shows a list of saved locations.",
  394.                         "/tpl \"location name\" - Teleports you to a saved location.",
  395.                         "/tpsave \"location name\" - Saves your current position as the location name.",
  396.                         "/tpremove \"location name\" - Removes the location from your saved list.",
  397.                         "/tpb - Teleports you back to the place where you were before teleporting.",
  398.                         "/home radius \"radius\" - Find all homes in radius.",
  399.                         "/home delete \"player name|id\" \"home name\" - Remove a home from a player.",
  400.                         "/home tp \"player name|id\" \"name\" - Teleports you to the home location with the name 'name' from the player.",
  401.                         "/home homes \"player name|id\" - Shows you a list of all homes from the player."
  402.                     })
  403.                 },
  404.                 {
  405.                     "TPHelphome", string.Join(NewLine, new[]
  406.                     {
  407.                         "With the following commands you can set your home location to teleport back to:",
  408.                         "/home add \"name\" - Saves your current position as the location name.",
  409.                         "/home list - Shows you a list of all the locations you have saved.",
  410.                         "/home remove \"name\" - Removes the location of your saved homes.",
  411.                         "/home \"name\" - Teleports you to the home location."
  412.                     })
  413.                 },
  414.                 {
  415.                     "TPHelptpr", string.Join(NewLine, new[]
  416.                     {
  417.                         "With these commands you can request to be teleported to a player or accept someone else's request:",
  418.                         "/tpr \"player name\" - Sends a teleport request to the player.",
  419.                         "/tpa - Accepts an incoming teleport request.",
  420.                         "/tpc - Cancel teleport or request."
  421.                     })
  422.                 },
  423.                 {
  424.                     "TPSettingsGeneral", string.Join(NewLine, new[]
  425.                     {
  426.                         "Please specify the module you want to view the settings of. ",
  427.                         "The available modules are:",
  428.                     })
  429.                 },
  430.                 {
  431.                     "TPSettingshome", string.Join(NewLine, new[]
  432.                     {
  433.                         "Home System has the current settings enabled:",
  434.                         "Time between teleports: {0}",
  435.                         "Daily amount of teleports: {1}",
  436.                         "Amount of saved Home locations: {2}"
  437.                     })
  438.                 },
  439.                 {
  440.                     "TPSettingstpr", string.Join(NewLine, new[]
  441.                     {
  442.                         "TPR System has the current settings enabled:",
  443.                         "Time between teleports: {0}",
  444.                         "Daily amount of teleports: {1}"
  445.                     })
  446.                 },
  447.                 {
  448.                     "TPSettingstown", string.Join(NewLine, new[]
  449.                     {
  450.                         "Town System has the current settings enabled:",
  451.                         "Time between teleports: {0}",
  452.                         "Daily amount of teleports: {1}"
  453.                     })
  454.                 },
  455.                 {"PlayerNotFound", "The specified player couldn't be found please try again!"},
  456.                 {"MultiplePlayers", "Found multiple players: {0}"},
  457.                 {"CantTeleportToSelf", "You can't teleport to yourself!"},
  458.                 {"CantTeleportPlayerToSelf", "You can't teleport a player to himself!"},
  459.                 {"TeleportPending", "You can't initiate another teleport while you have a teleport pending!"},
  460.                 {"TeleportPendingTarget", "You can't request a teleport to someone who's about to teleport!"},
  461.                 {"LocationExists", "A location with this name already exists at {0}!"},
  462.                 {"LocationExistsNearby", "A location with the name {0} already exists near this position!"},
  463.                 {"LocationNotFound", "Couldn't find a location with that name!"},
  464.                 {"NoPreviousLocationSaved", "No previous location saved!"},
  465.                 {"HomeExists", "You have already saved a home location by this name!"},
  466.                 {"HomeExistsNearby", "A home location with the name {0} already exists near this position!"},
  467.                 {"HomeNotFound", "Couldn't find your home with that name!"},
  468.                 {"InvalidCoordinates", "The coordinates you've entered are invalid!"},
  469.                 {"InvalidHelpModule", "Invalid module supplied!"},
  470.                 {"InvalidCharacter", "You have used an invalid character, please limit yourself to the letters a to z and numbers."},
  471.                 {
  472.                     "SyntaxCommandTP", string.Join(NewLine, new[]
  473.                     {
  474.                         "A Syntax Error Occurred!",
  475.                         "You can only use the /tp command as follows:",
  476.                         "/tp \"targetplayer\" - Teleports yourself to the target player.",
  477.                         "/tp \"player\" \"targetplayer\" - Teleports the player to the target player.",
  478.                         "/tp x y z - Teleports you to the set of coordinates.",
  479.                         "/tp \"player\" x y z - Teleports the player to the set of coordinates."
  480.                     })
  481.                 },
  482.                 {
  483.                     "SyntaxCommandTPL", string.Join(NewLine, new[]
  484.                     {
  485.                         "A Syntax Error Occurred!",
  486.                         "You can only use the /tpl command as follows:",
  487.                         "/tpl - Shows a list of saved locations.",
  488.                         "/tpl \"location name\" - Teleports you to a saved location."
  489.                     })
  490.                 },
  491.                 {
  492.                     "SyntaxCommandTPSave", string.Join(NewLine, new[]
  493.                     {
  494.                         "A Syntax Error Occurred!",
  495.                         "You can only use the /tpsave command as follows:",
  496.                         "/tpsave \"location name\" - Saves your current position as 'location name'."
  497.                     })
  498.                 },
  499.                 {
  500.                     "SyntaxCommandTPRemove", string.Join(NewLine, new[]
  501.                     {
  502.                         "A Syntax Error Occurred!",
  503.                         "You can only use the /tpremove command as follows:",
  504.                         "/tpremove \"location name\" - Removes the location with the name 'location name'."
  505.                     })
  506.                 },
  507.                 {
  508.                     "SyntaxCommandTPN", string.Join(NewLine, new[]
  509.                     {
  510.                         "A Syntax Error Occurred!",
  511.                         "You can only use the /tpn command as follows:",
  512.                         "/tpn \"targetplayer\" - Teleports yourself the default distance behind the target player.",
  513.                         "/tpn \"targetplayer\" \"distance\" - Teleports you the specified distance behind the target player."
  514.                     })
  515.                 },
  516.                 {
  517.                     "SyntaxCommandSetHome", string.Join(NewLine, new[]
  518.                     {
  519.                         "A Syntax Error Occurred!",
  520.                         "You can only use the /home add command as follows:",
  521.                         "/home add \"name\" - Saves the current location as your home with the name 'name'."
  522.                     })
  523.                 },
  524.                 {
  525.                     "SyntaxCommandRemoveHome", string.Join(NewLine, new[]
  526.                     {
  527.                         "A Syntax Error Occurred!",
  528.                         "You can only use the /home remove command as follows:",
  529.                         "/home remove \"name\" - Removes the home location with the name 'name'."
  530.                     })
  531.                 },
  532.                 {
  533.                     "SyntaxCommandHome", string.Join(NewLine, new[]
  534.                     {
  535.                         "A Syntax Error Occurred!",
  536.                         "You can only use the /home command as follows:",
  537.                         "/home \"name\" - Teleports yourself to your home with the name 'name'.",
  538.                         "/home \"name\" pay - Teleports yourself to your home with the name 'name', avoiding cooldown by paying for it.",
  539.                         "/home add \"name\" - Saves the current location as your home with the name 'name'.",
  540.                         "/home list - Shows you a list of all your saved home locations.",
  541.                         "/home remove \"name\" - Removes the home location with the name 'name'."
  542.                     })
  543.                 },
  544.                 {
  545.                     "SyntaxCommandHomeAdmin", string.Join(NewLine, new[]
  546.                     {
  547.                         "/home radius \"radius\" - Shows you a list of all homes in radius(10).",
  548.                         "/home delete \"player name|id\" \"name\" - Removes the home location with the name 'name' from the player.",
  549.                         "/home tp \"player name|id\" \"name\" - Teleports you to the home location with the name 'name' from the player.",
  550.                         "/home homes \"player name|id\" - Shows you a list of all homes from the player."
  551.                     })
  552.                 },
  553.                 {
  554.                     "SyntaxCommandTown", string.Join(NewLine, new[]
  555.                     {
  556.                         "A Syntax Error Occurred!",
  557.                         "You can only use the /town command as follows:",
  558.                         "/town - Teleports yourself to town.",
  559.                         "/town pay - Teleports yourself to town, paying the penalty."
  560.                     })
  561.                 },
  562.                 {
  563.                     "SyntaxCommandTownAdmin", string.Join(NewLine, new[]
  564.                     {
  565.                         "/town set - Saves the current location as town.",
  566.                     })
  567.                 },
  568.                 {
  569.                     "SyntaxCommandHomeDelete", string.Join(NewLine, new[]
  570.                     {
  571.                         "A Syntax Error Occurred!",
  572.                         "You can only use the /home delete command as follows:",
  573.                         "/home delete \"player name|id\" \"name\" - Removes the home location with the name 'name' from the player."
  574.                     })
  575.                 },
  576.                 {
  577.                     "SyntaxCommandHomeAdminTP", string.Join(NewLine, new[]
  578.                     {
  579.                         "A Syntax Error Occurred!",
  580.                         "You can only use the /home tp command as follows:",
  581.                         "/home tp \"player name|id\" \"name\" - Teleports you to the home location with the name 'name' from the player."
  582.                     })
  583.                 },
  584.                 {
  585.                     "SyntaxCommandHomeHomes", string.Join(NewLine, new[]
  586.                     {
  587.                         "A Syntax Error Occurred!",
  588.                         "You can only use the /home homes command as follows:",
  589.                         "/home homes \"player name|id\" - Shows you a list of all homes from the player."
  590.                     })
  591.                 },
  592.                 {
  593.                     "SyntaxCommandListHomes", string.Join(NewLine, new[]
  594.                     {
  595.                         "A Syntax Error Occurred!",
  596.                         "You can only use the /home list command as follows:",
  597.                         "/home list - Shows you a list of all your saved home locations."
  598.                     })
  599.                 },
  600.                 {
  601.                     "SyntaxCommandTPR", string.Join(NewLine, new[]
  602.                     {
  603.                         "A Syntax Error Occurred!",
  604.                         "You can only use the /tpr command as follows:",
  605.                         "/tpr \"player name\" - Sends out a teleport request to 'player name'."
  606.                     })
  607.                 },
  608.                 {
  609.                     "SyntaxCommandTPA", string.Join(NewLine, new[]
  610.                     {
  611.                         "A Syntax Error Occurred!",
  612.                         "You can only use the /tpa command as follows:",
  613.                         "/tpa - Accepts an incoming teleport request."
  614.                     })
  615.                 },
  616.                 {
  617.                     "SyntaxCommandTPC", string.Join(NewLine, new[]
  618.                     {
  619.                         "A Syntax Error Occurred!",
  620.                         "You can only use the /tpc command as follows:",
  621.                         "/tpc - Cancels an teleport request."
  622.                     })
  623.                 },
  624.                 {
  625.                     "SyntaxConsoleCommandToPos", string.Join(NewLine, new[]
  626.                     {
  627.                         "A Syntax Error Occurred!",
  628.                         "You can only use the teleport.topos console command as follows:",
  629.                         " > teleport.topos \"player\" x y z"
  630.                     })
  631.                 },
  632.                 {
  633.                     "SyntaxConsoleCommandToPlayer", string.Join(NewLine, new[]
  634.                     {
  635.                         "A Syntax Error Occurred!",
  636.                         "You can only use the teleport.toplayer console command as follows:",
  637.                         " > teleport.toplayer \"player\" \"target player\""
  638.                     })
  639.                 },
  640.                 {"LogTeleport", "{0} teleported to {1}."},
  641.                 {"LogTeleportPlayer", "{0} teleported {1} to {2}."},
  642.                 {"LogTeleportBack", "{0} teleported back to previous location."}
  643.             }, this);
  644.             Config.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
  645.             Config.Settings.Converters = new JsonConverter[] { new UnityVector3Converter() };
  646.             try
  647.             {
  648.                 configData = Config.ReadObject<ConfigData>();
  649.             }
  650.             catch (Exception)
  651.             {
  652.                 Puts("Corrupt config, loading default...");
  653.                 LoadDefaultConfig();
  654.             }
  655.             if (!(configData.Version == Version))
  656.             {
  657.                 if (configData.Home.VIPHomesLimits == null)
  658.                 {
  659.                     configData.Home.VIPHomesLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  660.                     configData.Home.VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  661.                     configData.Home.VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  662.                     configData.TPR.VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  663.                     configData.TPR.VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  664.                     configData.Town.VIPDailyLimits = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  665.                     configData.Town.VIPCooldowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  666.                 }
  667.                 if (configData.Home.VIPCountdowns == null)
  668.                 {
  669.                     configData.Home.VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  670.                     configData.TPR.VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  671.                     configData.Town.VIPCountdowns = new Dictionary<string, int> { { ConfigDefaultPermVip, 5 } };
  672.                 }
  673.                 if (configData.Version <= new VersionNumber(1, 0, 4))
  674.                     configData.Home.AllowAboveFoundation = true;
  675.                 if (configData.Version < new VersionNumber(1, 0, 14))
  676.                 {
  677.                     configData.Home.UsableIntoBuildingBlocked = true;
  678.                     configData.TPR.UsableIntoBuildingBlocked = true;
  679.                 }
  680.                 configData.Version = Version;
  681.                 Config.WriteObject(configData, true);
  682.             }
  683.             dataAdmin = GetFile(nameof(NTeleportation) + "Admin");
  684.             Admin = dataAdmin.ReadObject<Dictionary<ulong, AdminData>>();
  685.             dataHome = GetFile(nameof(NTeleportation) + "Home");
  686.             Home = dataHome.ReadObject<Dictionary<ulong, HomeData>>();
  687.             dataTPR = GetFile(nameof(NTeleportation) + "TPR");
  688.             TPR = dataTPR.ReadObject<Dictionary<ulong, TeleportData>>();
  689.             dataTown = GetFile(nameof(NTeleportation) + "Town");
  690.             Town = dataTown.ReadObject<Dictionary<ulong, TeleportData>>();
  691.             cmd.AddConsoleCommand("teleport.toplayer", this, ccmdTeleport);
  692.             cmd.AddConsoleCommand("teleport.topos", this, ccmdTeleport);
  693.             permission.RegisterPermission(PermDeleteHome, this);
  694.             permission.RegisterPermission(PermHomeHomes, this);
  695.             permission.RegisterPermission(PermImportHomes, this);
  696.             permission.RegisterPermission(PermRadiusHome, this);
  697.             permission.RegisterPermission(PermTp, this);
  698.             permission.RegisterPermission(PermTpB, this);
  699.             permission.RegisterPermission(PermTpConsole, this);
  700.             permission.RegisterPermission(PermTpHome, this);
  701.             permission.RegisterPermission(PermTpTown, this);
  702.             permission.RegisterPermission(PermTpN, this);
  703.             permission.RegisterPermission(PermTpL, this);
  704.             permission.RegisterPermission(PermTpRemove, this);
  705.             permission.RegisterPermission(PermTpSave, this);
  706.             permission.RegisterPermission(PermWipeHomes, this);
  707.             permission.RegisterPermission(PermCraftHome, this);
  708.             permission.RegisterPermission(PermCraftTown, this);
  709.             permission.RegisterPermission(PermCraftTpR, this);
  710.             foreach (var key in configData.Home.VIPCooldowns.Keys)
  711.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  712.             foreach (var key in configData.Home.VIPCountdowns.Keys)
  713.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  714.             foreach (var key in configData.Home.VIPDailyLimits.Keys)
  715.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  716.             foreach (var key in configData.Home.VIPHomesLimits.Keys)
  717.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  718.             foreach (var key in configData.TPR.VIPCooldowns.Keys)
  719.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  720.             foreach (var key in configData.TPR.VIPCountdowns.Keys)
  721.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  722.             foreach (var key in configData.TPR.VIPDailyLimits.Keys)
  723.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  724.             foreach (var key in configData.Town.VIPCooldowns.Keys)
  725.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  726.             foreach (var key in configData.Town.VIPCountdowns.Keys)
  727.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  728.             foreach (var key in configData.Town.VIPDailyLimits.Keys)
  729.                 if (!permission.PermissionExists(key, this)) permission.RegisterPermission(key, this);
  730.         }
  731.  
  732.         private DynamicConfigFile GetFile(string name)
  733.         {
  734.             var file = Interface.Oxide.DataFileSystem.GetFile(name);
  735.             file.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
  736.             file.Settings.Converters = new JsonConverter[] { new UnityVector3Converter(), new CustomComparerDictionaryCreationConverter<string>(StringComparer.OrdinalIgnoreCase) };
  737.             return file;
  738.         }
  739.  
  740.         void OnServerInitialized()
  741.         {
  742.             boundary = TerrainMeta.Size.x / 2;
  743.             CheckPerms(configData.Home.VIPHomesLimits);
  744.             CheckPerms(configData.Home.VIPDailyLimits);
  745.             CheckPerms(configData.Home.VIPCooldowns);
  746.             CheckPerms(configData.TPR.VIPDailyLimits);
  747.             CheckPerms(configData.TPR.VIPCooldowns);
  748.             CheckPerms(configData.Town.VIPDailyLimits);
  749.             CheckPerms(configData.Town.VIPCooldowns);
  750.             foreach (var item in configData.Settings.BlockedItems)
  751.             {
  752.                 var definition = ItemManager.FindItemDefinition(item.Key);
  753.                 if (definition == null)
  754.                 {
  755.                     Puts("Blocked item not found: {0}", item.Key);
  756.                     continue;
  757.                 }
  758.                 ReverseBlockedItems[definition.itemid] = item.Value;
  759.             }
  760.         }
  761.  
  762.         void OnServerSave()
  763.         {
  764.             SaveTeleportsAdmin();
  765.             SaveTeleportsHome();
  766.             SaveTeleportsTPR();
  767.             SaveTeleportsTown();
  768.         }
  769.  
  770.         void OnServerShutdown() => OnServerSave();
  771.  
  772.         void Unload() => OnServerSave();
  773.  
  774.         void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitinfo)
  775.         {
  776.             var player = entity.ToPlayer();
  777.             if (player == null || hitinfo == null) return;
  778.             if (hitinfo.damageTypes.Has(DamageType.Fall) && teleporting.Contains(player.userID))
  779.             {
  780.                 hitinfo.damageTypes = new DamageTypeList();
  781.                 teleporting.Remove(player.userID);
  782.             }
  783.             TeleportTimer teleportTimer;
  784.             if (!TeleportTimers.TryGetValue(player.userID, out teleportTimer)) return;
  785.             NextTick(() =>
  786.             {
  787.                 if (hitinfo.damageTypes.Total() <= 0) return;
  788.                 PrintMsgL(teleportTimer.OriginPlayer, "Interrupted");
  789.                 if (teleportTimer.TargetPlayer != null)
  790.                     PrintMsgL(teleportTimer.TargetPlayer, "InterruptedTarget", teleportTimer.OriginPlayer.displayName);
  791.                 teleportTimer.Timer.Destroy();
  792.                 TeleportTimers.Remove(player.userID);
  793.             });
  794.         }
  795.  
  796.         void OnPlayerSleepEnded(BasePlayer player)
  797.         {
  798.             if (teleporting.Contains(player.userID))
  799.                 timer.Once(3, () => { teleporting.Remove(player.userID); });
  800.         }
  801.  
  802.         void OnPlayerDisconnected(BasePlayer player)
  803.         {
  804.             Timer reqTimer;
  805.             if (PendingRequests.TryGetValue(player.userID, out reqTimer))
  806.             {
  807.                 var originPlayer = PlayersRequests[player.userID];
  808.                 PrintMsgL(originPlayer, "RequestTargetOff");
  809.                 reqTimer.Destroy();
  810.                 PendingRequests.Remove(player.userID);
  811.                 PlayersRequests.Remove(player.userID);
  812.                 PlayersRequests.Remove(originPlayer.userID);
  813.             }
  814.             TeleportTimer teleportTimer;
  815.             if (TeleportTimers.TryGetValue(player.userID, out teleportTimer))
  816.             {
  817.                 teleportTimer.Timer.Destroy();
  818.                 TeleportTimers.Remove(player.userID);
  819.             }
  820.             teleporting.Remove(player.userID);
  821.         }
  822.  
  823.         private void SaveTeleportsAdmin()
  824.         {
  825.             if (Admin == null || !changedAdmin) return;
  826.             dataAdmin.WriteObject(Admin);
  827.             changedAdmin = false;
  828.         }
  829.  
  830.         private void SaveTeleportsHome()
  831.         {
  832.             if (Home == null || !changedHome) return;
  833.             dataHome.WriteObject(Home);
  834.             changedHome = false;
  835.         }
  836.  
  837.         private void SaveTeleportsTPR()
  838.         {
  839.             if (TPR == null || !changedTPR) return;
  840.             dataTPR.WriteObject(TPR);
  841.             changedTPR = false;
  842.         }
  843.  
  844.         private void SaveTeleportsTown()
  845.         {
  846.             if (Town == null || !changedTown) return;
  847.             dataTown.WriteObject(Town);
  848.             changedTown = false;
  849.         }
  850.  
  851.         private void SaveLocation(BasePlayer player)
  852.         {
  853.             if (!IsAllowed(player, PermTpB)) return;
  854.             AdminData adminData;
  855.             if (!Admin.TryGetValue(player.userID, out adminData))
  856.                 Admin[player.userID] = adminData = new AdminData();
  857.             adminData.PreviousLocation = player.transform.position;
  858.             changedAdmin = true;
  859.             PrintMsgL(player, "AdminTPBackSave");
  860.         }
  861.  
  862.         [ChatCommand("tp")]
  863.         private void cmdChatTeleport(BasePlayer player, string command, string[] args)
  864.         {
  865.             if (!IsAllowedMsg(player, PermTp)) return;
  866.             BasePlayer target;
  867.             float x, y, z;
  868.             switch (args.Length)
  869.             {
  870.                 case 1:
  871.                     target = FindPlayersSingle(args[0], player);
  872.                     if (target == null) return;
  873.                     if (target == player)
  874.                     {
  875.                         PrintMsgL(player, "CantTeleportToSelf");
  876.                         return;
  877.                     }
  878.                     TeleportToPlayer(player, target);
  879.                     PrintMsgL(player, "AdminTP", target.displayName);
  880.                     Puts(_("LogTeleport", null, player.displayName, target.displayName));
  881.                     if (configData.Admin.AnnounceTeleportToTarget)
  882.                         PrintMsgL(target, "AdminTPTarget", player.displayName);
  883.                     break;
  884.                 case 2:
  885.                     var origin = FindPlayersSingle(args[0], player);
  886.                     if (origin == null) return;
  887.                     target = FindPlayersSingle(args[1], player);
  888.                     if (target == null) return;
  889.                     if (target == origin)
  890.                     {
  891.                         PrintMsgL(player, "CantTeleportPlayerToSelf");
  892.                         return;
  893.                     }
  894.                     TeleportToPlayer(origin, target);
  895.                     PrintMsgL(player, "AdminTPPlayers", origin.displayName, target.displayName);
  896.                     PrintMsgL(origin, "AdminTPPlayer", player.displayName, target.displayName);
  897.                     PrintMsgL(target, "AdminTPPlayerTarget", player.displayName, origin.displayName);
  898.                     Puts(_("LogTeleportPlayer", null, player.displayName, origin.displayName, target.displayName));
  899.                     break;
  900.                 case 3:
  901.                     if (!float.TryParse(args[0], out x) || !float.TryParse(args[1], out y) || !float.TryParse(args[2], out z))
  902.                     {
  903.                         PrintMsgL(player, "InvalidCoordinates");
  904.                         return;
  905.                     }
  906.                     if (!CheckBoundaries(x, y, z))
  907.                     {
  908.                         PrintMsgL(player, "AdminTPOutOfBounds");
  909.                         PrintMsgL(player, "AdminTPBoundaries", boundary);
  910.                         return;
  911.                     }
  912.                     TeleportToPosition(player, x, y, z);
  913.                     PrintMsgL(player, "AdminTPCoordinates", player.transform.position);
  914.                     Puts(_("LogTeleport", null, player.displayName, player.transform.position));
  915.                     break;
  916.                 case 4:
  917.                     target = FindPlayersSingle(args[0], player);
  918.                     if (target == null) return;
  919.                     if (!float.TryParse(args[0], out x) || !float.TryParse(args[1], out y) || !float.TryParse(args[2], out z))
  920.                     {
  921.                         PrintMsgL(player, "InvalidCoordinates");
  922.                         return;
  923.                     }
  924.                     if (!CheckBoundaries(x, y, z))
  925.                     {
  926.                         PrintMsgL(player, "AdminTPOutOfBounds");
  927.                         PrintMsgL(player, "AdminTPBoundaries", boundary);
  928.                         return;
  929.                     }
  930.                     TeleportToPosition(target, x, y, z);
  931.                     if (player == target)
  932.                     {
  933.                         PrintMsgL(player, "AdminTPCoordinates", player.transform.position);
  934.                         Puts(_("LogTeleport", null, player.displayName, player.transform.position));
  935.                     }
  936.                     else
  937.                     {
  938.                         PrintMsgL(player, "AdminTPTargetCoordinates", target.displayName, player.transform.position);
  939.                         PrintMsgL(target, "AdminTPTargetCoordinatesTarget", player.displayName, player.transform.position);
  940.                         Puts(_("LogTeleportPlayer", null, player.displayName, target.displayName, player.transform.position));
  941.                     }
  942.                     break;
  943.                 default:
  944.                     PrintMsgL(player, "SyntaxCommandTP");
  945.                     break;
  946.             }
  947.         }
  948.  
  949.         [ChatCommand("tpn")]
  950.         private void cmdChatTeleportNear(BasePlayer player, string command, string[] args)
  951.         {
  952.             if (!IsAllowedMsg(player, PermTpN)) return;
  953.             switch (args.Length)
  954.             {
  955.                 case 1:
  956.                 case 2:
  957.                     var target = FindPlayersSingle(args[0], player);
  958.                     if (target == null) return;
  959.                     if (target == player)
  960.                     {
  961.                         PrintMsgL(player, "CantTeleportToSelf");
  962.                         return;
  963.                     }
  964.                     int distance;
  965.                     if (args.Length != 2 || !int.TryParse(args[1], out distance))
  966.                         distance = configData.Admin.TeleportNearDefaultDistance;
  967.                     float x = UnityEngine.Random.Range(-distance, distance);
  968.                     var z = (float)System.Math.Sqrt(System.Math.Pow(distance, 2) - System.Math.Pow(x, 2));
  969.                     var destination = target.transform.position;
  970.                     destination.x = destination.x - x;
  971.                     destination.z = destination.z - z;
  972.                     Teleport(player, GetGroundBuilding(destination));
  973.                     PrintMsgL(player, "AdminTP", target.displayName);
  974.                     Puts(_("LogTeleport", null, player.displayName, target.displayName));
  975.                     if (configData.Admin.AnnounceTeleportToTarget)
  976.                         PrintMsgL(target, "AdminTPTarget", player.displayName);
  977.                     break;
  978.                 default:
  979.                     PrintMsgL(player, "SyntaxCommandTPN");
  980.                     break;
  981.             }
  982.         }
  983.  
  984.         [ChatCommand("tpl")]
  985.         private void cmdChatTeleportLocation(BasePlayer player, string command, string[] args)
  986.         {
  987.             if (!IsAllowedMsg(player, PermTpL)) return;
  988.             AdminData adminData;
  989.             if (!Admin.TryGetValue(player.userID, out adminData) || adminData.Locations.Count <= 0)
  990.             {
  991.                 PrintMsgL(player, "AdminLocationListEmpty");
  992.                 return;
  993.             }
  994.             switch (args.Length)
  995.             {
  996.                 case 0:
  997.                     PrintMsgL(player, "AdminLocationList");
  998.                     foreach (var location in adminData.Locations)
  999.                         PrintMsgL(player, $"{location.Key} {location.Value}");
  1000.                     break;
  1001.                 case 1:
  1002.                     Vector3 loc;
  1003.                     if (!adminData.Locations.TryGetValue(args[0], out loc))
  1004.                     {
  1005.                         PrintMsgL(player, "LocationNotFound");
  1006.                         return;
  1007.                     }
  1008.                     Teleport(player, loc);
  1009.                     PrintMsgL(player, "AdminTPLocation", args[0]);
  1010.                     break;
  1011.                 default:
  1012.                     PrintMsgL(player, "SyntaxCommandTPL");
  1013.                     break;
  1014.             }
  1015.         }
  1016.  
  1017.         [ChatCommand("tpsave")]
  1018.         private void cmdChatSaveTeleportLocation(BasePlayer player, string command, string[] args)
  1019.         {
  1020.             if (!IsAllowedMsg(player, PermTpSave)) return;
  1021.             if (args.Length != 1)
  1022.             {
  1023.                 PrintMsgL(player, "SyntaxCommandTPSave");
  1024.                 return;
  1025.             }
  1026.             AdminData adminData;
  1027.             if (!Admin.TryGetValue(player.userID, out adminData))
  1028.                 Admin[player.userID] = adminData = new AdminData();
  1029.             Vector3 location;
  1030.             if (adminData.Locations.TryGetValue(args[0], out location))
  1031.             {
  1032.                 PrintMsgL(player, "LocationExists", location);
  1033.                 return;
  1034.             }
  1035.             var positionCoordinates = player.transform.position;
  1036.             foreach (var loc in adminData.Locations)
  1037.             {
  1038.                 if (Vector3.Distance(positionCoordinates, loc.Value) < configData.Admin.LocationRadius)
  1039.                 {
  1040.                     PrintMsgL(player, "LocationExistsNearby", loc.Key);
  1041.                     return;
  1042.                 }
  1043.             }
  1044.             adminData.Locations[args[0]] = positionCoordinates;
  1045.             PrintMsgL(player, "AdminTPLocationSave");
  1046.             changedAdmin = true;
  1047.         }
  1048.  
  1049.         [ChatCommand("tpremove")]
  1050.         private void cmdChatRemoveTeleportLocation(BasePlayer player, string command, string[] args)
  1051.         {
  1052.             if (!IsAllowedMsg(player, PermTpRemove)) return;
  1053.             if (args.Length != 1)
  1054.             {
  1055.                 PrintMsgL(player, "SyntaxCommandTPRemove");
  1056.                 return;
  1057.             }
  1058.             AdminData adminData;
  1059.             if (!Admin.TryGetValue(player.userID, out adminData) || adminData.Locations.Count <= 0)
  1060.             {
  1061.                 PrintMsgL(player, "AdminLocationListEmpty");
  1062.                 return;
  1063.             }
  1064.             if (adminData.Locations.Remove(args[0]))
  1065.             {
  1066.                 PrintMsgL(player, "AdminTPLocationRemove", args[0]);
  1067.                 changedAdmin = true;
  1068.                 return;
  1069.             }
  1070.             PrintMsgL(player, "LocationNotFound");
  1071.         }
  1072.  
  1073.         [ChatCommand("tpb")]
  1074.         private void cmdChatTeleportBack(BasePlayer player, string command, string[] args)
  1075.         {
  1076.             if (!IsAllowedMsg(player, PermTpB)) return;
  1077.             if (args.Length != 0)
  1078.             {
  1079.                 PrintMsgL(player, "SyntaxCommandTPB");
  1080.                 return;
  1081.             }
  1082.             AdminData adminData;
  1083.             if (!Admin.TryGetValue(player.userID, out adminData) || adminData.PreviousLocation == default(Vector3))
  1084.             {
  1085.                 PrintMsgL(player, "NoPreviousLocationSaved");
  1086.                 return;
  1087.             }
  1088.             Teleport(player, adminData.PreviousLocation);
  1089.             adminData.PreviousLocation = default(Vector3);
  1090.             changedAdmin = true;
  1091.             PrintMsgL(player, "AdminTPBack");
  1092.             Puts(_("LogTeleportBack", null, player.displayName));
  1093.         }
  1094.  
  1095.         [ChatCommand("sethome")]
  1096.         private void cmdChatSetHome(BasePlayer player, string command, string[] args)
  1097.         {
  1098.             if (!configData.Settings.HomesEnabled) return;
  1099.             if (args.Length != 1)
  1100.             {
  1101.                 PrintMsgL(player, "SyntaxCommandSetHome");
  1102.                 return;
  1103.             }
  1104.             var err = CheckPlayer(player, false, CanCraftHome(player));
  1105.             if (err != null)
  1106.             {
  1107.                 PrintMsgL(player, $"Home{err}");
  1108.                 return;
  1109.             }
  1110.             if (!player.CanBuild())
  1111.             {
  1112.                 PrintMsgL(player, "HomeTPBuildingBlocked");
  1113.                 return;
  1114.             }
  1115.             if (!args[0].All(char.IsLetterOrDigit))
  1116.             {
  1117.                 PrintMsgL(player, "InvalidCharacter");
  1118.                 return;
  1119.             }
  1120.             HomeData homeData;
  1121.             if (!Home.TryGetValue(player.userID, out homeData))
  1122.                 Home[player.userID] = homeData = new HomeData();
  1123.             var limit = GetHigher(player, configData.Home.VIPHomesLimits, configData.Home.HomesLimit);
  1124.             if (homeData.Locations.Count >= limit)
  1125.             {
  1126.                 PrintMsgL(player, "HomeMaxLocations", limit);
  1127.                 return;
  1128.             }
  1129.             Vector3 location;
  1130.             if (homeData.Locations.TryGetValue(args[0], out location))
  1131.             {
  1132.                 PrintMsgL(player, "HomeExists", location);
  1133.                 return;
  1134.             }
  1135.             var positionCoordinates = player.transform.position;
  1136.             foreach (var loc in homeData.Locations)
  1137.             {
  1138.                 if (Vector3.Distance(positionCoordinates, loc.Value) < configData.Home.LocationRadius)
  1139.                 {
  1140.                     PrintMsgL(player, "HomeExistsNearby", loc.Key);
  1141.                     return;
  1142.                 }
  1143.             }
  1144.             err = CanPlayerTeleport(player);
  1145.             if (err != null)
  1146.             {
  1147.                 SendReply(player, err);
  1148.                 return;
  1149.             }
  1150.  
  1151.             if (player.IsAdmin)
  1152.                 player.SendConsoleCommand("ddraw.sphere", 60f, Color.blue, GetGround(positionCoordinates), 2.5f);
  1153.  
  1154.             err = CheckFoundation(player.userID, positionCoordinates);
  1155.             if (err != null)
  1156.             {
  1157.                 PrintMsgL(player, err);
  1158.                 return;
  1159.             }
  1160.             err = CheckInsideBlock(positionCoordinates);
  1161.             if (err != null)
  1162.             {
  1163.                 PrintMsgL(player, err);
  1164.                 return;
  1165.             }
  1166.             homeData.Locations[args[0]] = positionCoordinates;
  1167.             changedHome = true;
  1168.             PrintMsgL(player, "HomeSave");
  1169.             PrintMsgL(player, "HomeQuota", homeData.Locations.Count, limit);
  1170.         }
  1171.  
  1172.         [ChatCommand("removehome")]
  1173.         private void cmdChatRemoveHome(BasePlayer player, string command, string[] args)
  1174.         {
  1175.             if (!configData.Settings.HomesEnabled) return;
  1176.             if (args.Length != 1)
  1177.             {
  1178.                 PrintMsgL(player, "SyntaxCommandRemoveHome");
  1179.                 return;
  1180.             }
  1181.             HomeData homeData;
  1182.             if (!Home.TryGetValue(player.userID, out homeData) || homeData.Locations.Count <= 0)
  1183.             {
  1184.                 PrintMsgL(player, "HomeListEmpty");
  1185.                 return;
  1186.             }
  1187.             if (homeData.Locations.Remove(args[0]))
  1188.             {
  1189.                 changedHome = true;
  1190.                 PrintMsgL(player, "HomeRemove", args[0]);
  1191.             }
  1192.             else
  1193.                 PrintMsgL(player, "HomeNotFound");
  1194.         }
  1195.  
  1196.         [ChatCommand("home")]
  1197.         private void cmdChatHome(BasePlayer player, string command, string[] args)
  1198.         {
  1199.             if (!configData.Settings.HomesEnabled) return;
  1200.             if (args.Length == 0)
  1201.             {
  1202.                 PrintMsgL(player, "SyntaxCommandHome");
  1203.                 if (IsAllowed(player)) PrintMsgL(player, "SyntaxCommandHomeAdmin");
  1204.                 return;
  1205.             }
  1206.             switch (args[0].ToLower())
  1207.             {
  1208.                 case "add":
  1209.                     cmdChatSetHome(player, command, args.Skip(1).ToArray());
  1210.                     break;
  1211.                 case "list":
  1212.                     cmdChatListHome(player, command, args.Skip(1).ToArray());
  1213.                     break;
  1214.                 case "remove":
  1215.                     cmdChatRemoveHome(player, command, args.Skip(1).ToArray());
  1216.                     break;
  1217.                 case "radius":
  1218.                     cmdChatHomeRadius(player, command, args.Skip(1).ToArray());
  1219.                     break;
  1220.                 case "delete":
  1221.                     cmdChatHomeDelete(player, command, args.Skip(1).ToArray());
  1222.                     break;
  1223.                 case "tp":
  1224.                     cmdChatHomeAdminTP(player, command, args.Skip(1).ToArray());
  1225.                     break;
  1226.                 case "homes":
  1227.                     cmdChatHomeHomes(player, command, args.Skip(1).ToArray());
  1228.                     break;
  1229.                 case "wipe":
  1230.                     cmdChatWipeHomes(player, command, args.Skip(1).ToArray());
  1231.                     break;
  1232.                 default:
  1233.                     cmdChatHomeTP(player, command, args);
  1234.                     break;
  1235.             }
  1236.         }
  1237.  
  1238.         [ChatCommand("radiushome")]
  1239.         private void cmdChatHomeRadius(BasePlayer player, string command, string[] args)
  1240.         {
  1241.             if (!IsAllowedMsg(player, PermRadiusHome)) return;
  1242.             float radius;
  1243.             if (args.Length != 1 || !float.TryParse(args[0], out radius)) radius = 10;
  1244.             var found = false;
  1245.             foreach (var homeData in Home)
  1246.             {
  1247.                 var toRemove = new List<string>();
  1248.                 var target = RustCore.FindPlayerById(homeData.Key)?.displayName ?? homeData.Key.ToString();
  1249.                 foreach (var location in homeData.Value.Locations)
  1250.                 {
  1251.                     if (Vector3.Distance(player.transform.position, location.Value) <= radius)
  1252.                     {
  1253.                         if (CheckFoundation(homeData.Key, location.Value) != null)
  1254.                         {
  1255.                             toRemove.Add(location.Key);
  1256.                             continue;
  1257.                         }
  1258.                         var entity = GetFoundationOwned(location.Value, homeData.Key);
  1259.                         if (entity == null) continue;
  1260.                         player.SendConsoleCommand("ddraw.text", 30f, Color.blue, entity.CenterPoint() + new Vector3(0, .5f), $"<size=20>{target} - {location.Key} {location.Value}</size>");
  1261.                         DrawBox(player, entity.CenterPoint(), entity.transform.rotation, entity.bounds.size);
  1262.                         PrintMsg(player, $"{target} - {location.Key} {location.Value}");
  1263.                         found = true;
  1264.                     }
  1265.                 }
  1266.                 foreach (var loc in toRemove)
  1267.                 {
  1268.                     homeData.Value.Locations.Remove(loc);
  1269.                     changedHome = true;
  1270.                 }
  1271.             }
  1272.             if (!found)
  1273.                 PrintMsgL(player, "HomeNoFound");
  1274.         }
  1275.  
  1276.         [ChatCommand("deletehome")]
  1277.         private void cmdChatHomeDelete(BasePlayer player, string command, string[] args)
  1278.         {
  1279.             if (!IsAllowedMsg(player, PermDeleteHome)) return;
  1280.             if (args.Length != 2)
  1281.             {
  1282.                 PrintMsgL(player, "SyntaxCommandHomeDelete");
  1283.                 return;
  1284.             }
  1285.             var userId = FindPlayersSingleId(args[0], player);
  1286.             if (userId <= 0) return;
  1287.             HomeData targetHome;
  1288.             if (!Home.TryGetValue(userId, out targetHome) || !targetHome.Locations.Remove(args[1]))
  1289.             {
  1290.                 PrintMsgL(player, "HomeNotFound");
  1291.                 return;
  1292.             }
  1293.             changedHome = true;
  1294.             PrintMsgL(player, "HomeDelete", args[0], args[1]);
  1295.         }
  1296.  
  1297.         [ChatCommand("tphome")]
  1298.         private void cmdChatHomeAdminTP(BasePlayer player, string command, string[] args)
  1299.         {
  1300.             if (!IsAllowedMsg(player, PermTpHome)) return;
  1301.             if (args.Length != 2)
  1302.             {
  1303.                 PrintMsgL(player, "SyntaxCommandHomeAdminTP");
  1304.                 return;
  1305.             }
  1306.             var userId = FindPlayersSingleId(args[0], player);
  1307.             if (userId <= 0) return;
  1308.             HomeData targetHome;
  1309.             Vector3 location;
  1310.             if (!Home.TryGetValue(userId, out targetHome) || !targetHome.Locations.TryGetValue(args[1], out location))
  1311.             {
  1312.                 PrintMsgL(player, "HomeNotFound");
  1313.                 return;
  1314.             }
  1315.             Teleport(player, location);
  1316.             PrintMsgL(player, "HomeAdminTP", args[0], args[1]);
  1317.         }
  1318.  
  1319.         // Check that plugins are available and enabled for CheckEconomy()
  1320.         private bool UseEconomy()
  1321.         {
  1322.             if((configData.Settings.UseEconomics && Economics) ||
  1323.                 (configData.Settings.UseServerRewards && ServerRewards))
  1324.             {
  1325.                 return true;
  1326.             }
  1327.             return false;
  1328.         }
  1329.  
  1330.         // Check balance on multiple plugins and optionally withdraw money from the player
  1331.         private bool CheckEconomy(BasePlayer player, double bypass, bool withdraw = false)
  1332.         {
  1333.             double balance = 0;
  1334.             bool foundmoney = false;
  1335.  
  1336.             // Check Economics first.  If not in use or balance low, check ServerRewards below
  1337.             if(configData.Settings.UseEconomics && Economics)
  1338.             {
  1339.                 balance = (double)Economics?.CallHook("Balance", player.UserIDString);
  1340.                 if(balance >= bypass)
  1341.                 {
  1342.                     foundmoney = true;
  1343.                     if(withdraw == true)
  1344.                     {
  1345.                         var w = (bool)Economics?.CallHook("Withdraw", player.userID, bypass);
  1346.                         return w;
  1347.                     }
  1348.                 }
  1349.             }
  1350.  
  1351.             // No money via Economics, or plugin not in use.  Try ServerRewards.
  1352.             if(configData.Settings.UseServerRewards && ServerRewards)
  1353.             {
  1354.                 object bal = ServerRewards?.Call("CheckPoints", player.userID);
  1355.                 balance = Convert.ToDouble(bal);
  1356.                 if(balance >= bypass && foundmoney == false)
  1357.                 {
  1358.                     foundmoney = true;
  1359.                     if(withdraw == true)
  1360.                     {
  1361.                         var w = (bool)ServerRewards?.Call("TakePoints", player.userID, (int)bypass);
  1362.                         return w;
  1363.                     }
  1364.                 }
  1365.             }
  1366.  
  1367.             // Just checking balance without withdrawal - did we find anything?
  1368.             if(foundmoney == true)
  1369.             {
  1370.                 return true;
  1371.             }
  1372.             return false;
  1373.         }
  1374.  
  1375.         private void cmdChatHomeTP(BasePlayer player, string command, string[] args)
  1376.         {
  1377.             if (!configData.Settings.HomesEnabled) return;
  1378.             if (args.Length < 1)
  1379.             {
  1380.                 PrintMsgL(player, "SyntaxCommandHome");
  1381.                 return;
  1382.             }
  1383.             var err = CheckPlayer(player, configData.Home.UsableOutOfBuildingBlocked, CanCraftHome(player));
  1384.             if (err != null)
  1385.             {
  1386.                 PrintMsgL(player, err);
  1387.                 return;
  1388.             }
  1389.             HomeData homeData;
  1390.             if (!Home.TryGetValue(player.userID, out homeData) || homeData.Locations.Count <= 0)
  1391.             {
  1392.                 PrintMsgL(player, "HomeListEmpty");
  1393.                 return;
  1394.             }
  1395.             Vector3 location;
  1396.             if (!homeData.Locations.TryGetValue(args[0], out location))
  1397.             {
  1398.                 PrintMsgL(player, "HomeNotFound");
  1399.                 return;
  1400.             }
  1401.             err = CheckFoundation(player.userID, location) ?? CheckTargetLocation(player, location, configData.Home.UsableIntoBuildingBlocked, configData.Home.CupOwnerAllowOnBuildingBlocked);
  1402.             if (err != null)
  1403.             {
  1404.                 PrintMsgL(player, "HomeRemovedInvalid", args[0]);
  1405.                 homeData.Locations.Remove(args[0]);
  1406.                 changedHome = true;
  1407.                 return;
  1408.             }
  1409.             err = CheckInsideBlock(location);
  1410.             if (err != null)
  1411.             {
  1412.                 PrintMsgL(player, "HomeRemovedInsideBlock", args[0]);
  1413.                 homeData.Locations.Remove(args[0]);
  1414.                 changedHome = true;
  1415.                 return;
  1416.             }
  1417.             var timestamp = Facepunch.Math.Epoch.Current;
  1418.             var currentDate = DateTime.Now.ToString("d");
  1419.             if (homeData.Teleports.Date != currentDate)
  1420.             {
  1421.                 homeData.Teleports.Amount = 0;
  1422.                 homeData.Teleports.Date = currentDate;
  1423.             }
  1424.             var cooldown = GetLower(player, configData.Home.VIPCooldowns, configData.Home.Cooldown);
  1425.  
  1426.             if (cooldown > 0 && timestamp - homeData.Teleports.Timestamp < cooldown)
  1427.             {
  1428.                 var cmdSent = "";
  1429.                 bool foundmoney = CheckEconomy(player, configData.Home.Bypass);
  1430.                 try
  1431.                 {
  1432.                     cmdSent = args[1].ToLower();
  1433.                 }
  1434.                 catch {}
  1435.  
  1436.                 bool payalso = false;
  1437.                 if(configData.Home.Pay > 0)
  1438.                 {
  1439.                     payalso = true;
  1440.                 }
  1441.                 if((configData.Settings.BypassCMD != null) && (cmdSent == configData.Settings.BypassCMD.ToLower()))
  1442.                 {
  1443.                     if(foundmoney == true)
  1444.                     {
  1445.                         CheckEconomy(player, configData.Home.Bypass, true);
  1446.                         PrintMsgL(player, "HomeTPCooldownBypass", configData.Home.Bypass);
  1447.                         if(payalso)
  1448.                         {
  1449.                             PrintMsgL(player, "PayToHome", configData.Home.Pay);
  1450.                         }
  1451.                     }
  1452.                     else
  1453.                     {
  1454.                         PrintMsgL(player, "HomeTPCooldownBypassF", configData.Home.Bypass);
  1455.                         return;
  1456.                     }
  1457.                 }
  1458.                 else if(UseEconomy())
  1459.                 {
  1460.                     var remain = cooldown - (timestamp - homeData.Teleports.Timestamp);
  1461.                     PrintMsgL(player, "HomeTPCooldown", FormatTime(remain));
  1462.                     if(configData.Home.Bypass > 0 && configData.Settings.BypassCMD != null)
  1463.                     {
  1464.                         PrintMsgL(player, "HomeTPCooldownBypassP", configData.Home.Bypass);
  1465.                         PrintMsgL(player, "HomeTPCooldownBypassP2", configData.Settings.BypassCMD);
  1466.                     }
  1467.                     return;
  1468.                 }
  1469.                 else
  1470.                 {
  1471.                     var remain = cooldown - (timestamp - homeData.Teleports.Timestamp);
  1472.                     PrintMsgL(player, "HomeTPCooldown", FormatTime(remain));
  1473.                     return;
  1474.                 }
  1475.             }
  1476.             var limit = GetHigher(player, configData.Home.VIPDailyLimits, configData.Home.DailyLimit);
  1477.             if (limit > 0 && homeData.Teleports.Amount >= limit)
  1478.             {
  1479.                 PrintMsgL(player, "HomeTPLimitReached", limit);
  1480.                 return;
  1481.             }
  1482.             if (TeleportTimers.ContainsKey(player.userID))
  1483.             {
  1484.                 PrintMsgL(player, "TeleportPending");
  1485.                 return;
  1486.             }
  1487.             err = CanPlayerTeleport(player);
  1488.             if (err != null)
  1489.             {
  1490.                 SendReply(player, err);
  1491.                 return;
  1492.             }
  1493.             err = CheckItems(player);
  1494.             if (err != null)
  1495.             {
  1496.                 PrintMsgL(player, "TPBlockedItem", err);
  1497.                 return;
  1498.             }
  1499.  
  1500.             var countdown = GetLower(player, configData.Home.VIPCountdowns, configData.Home.Countdown);
  1501.             TeleportTimers[player.userID] = new TeleportTimer
  1502.             {
  1503.                 OriginPlayer = player,
  1504.                 Timer = timer.Once(countdown, () =>
  1505.                 {
  1506.                     err = CheckPlayer(player, configData.Home.UsableOutOfBuildingBlocked, CanCraftHome(player));
  1507.                     if (err != null)
  1508.                     {
  1509.                         PrintMsgL(player, "Interrupted");
  1510.                         PrintMsgL(player, err);
  1511.                         TeleportTimers.Remove(player.userID);
  1512.                         return;
  1513.                     }
  1514.                     err = CanPlayerTeleport(player);
  1515.                     if (err != null)
  1516.                     {
  1517.                         PrintMsgL(player, "Interrupted");
  1518.                         SendReply(player, err);
  1519.                         TeleportTimers.Remove(player.userID);
  1520.                         return;
  1521.                     }
  1522.                     err = CheckItems(player);
  1523.                     if (err != null)
  1524.                     {
  1525.                         PrintMsgL(player, "Interrupted");
  1526.                         PrintMsgL(player, "TPBlockedItem", err);
  1527.                         TeleportTimers.Remove(player.userID);
  1528.                         return;
  1529.                     }
  1530.                     err = CheckFoundation(player.userID, location) ?? CheckTargetLocation(player, location, configData.Home.UsableIntoBuildingBlocked, configData.Home.CupOwnerAllowOnBuildingBlocked);
  1531.                     if (err != null)
  1532.                     {
  1533.                         PrintMsgL(player, "HomeRemovedInvalid", args[0]);
  1534.                         homeData.Locations.Remove(args[0]);
  1535.                         changedHome = true;
  1536.                         return;
  1537.                     }
  1538.                     err = CheckInsideBlock(location);
  1539.                     if (err != null)
  1540.                     {
  1541.                         PrintMsgL(player, "HomeRemovedInsideBlock", args[0]);
  1542.                         homeData.Locations.Remove(args[0]);
  1543.                         changedHome = true;
  1544.                         return;
  1545.                     }
  1546.                     if(UseEconomy())
  1547.                     {
  1548.                         if (configData.Home.Pay > 0 && !CheckEconomy(player, configData.Home.Pay))
  1549.                         {
  1550.                             PrintMsgL(player, "Interrupted");
  1551.                             PrintMsgL(player, "TPNoMoney", configData.Home.Pay);
  1552.  
  1553.                             TeleportTimers.Remove(player.userID);
  1554.                             return;
  1555.                         }
  1556.                         else if(configData.Home.Pay > 0)
  1557.                         {
  1558.                             var w = CheckEconomy(player, (double)configData.Home.Pay, true);
  1559.                             PrintMsgL(player, "TPMoney", (double)configData.Home.Pay);
  1560.                         }
  1561.                     }
  1562.                     Teleport(player, location);
  1563.                     homeData.Teleports.Amount++;
  1564.                     homeData.Teleports.Timestamp = timestamp;
  1565.                     changedHome = true;
  1566.                     PrintMsgL(player, "HomeTP", args[0]);
  1567.                     if (limit > 0) PrintMsgL(player, "HomeTPAmount", limit - homeData.Teleports.Amount);
  1568.                     TeleportTimers.Remove(player.userID);
  1569.                 })
  1570.             };
  1571.             PrintMsgL(player, "HomeTPStarted", args[0], countdown);
  1572.         }
  1573.  
  1574.         [ChatCommand("listhomes")]
  1575.         private void cmdChatListHome(BasePlayer player, string command, string[] args)
  1576.         {
  1577.             if (!configData.Settings.HomesEnabled) return;
  1578.             if (args.Length != 0)
  1579.             {
  1580.                 PrintMsgL(player, "SyntaxCommandListHomes");
  1581.                 return;
  1582.             }
  1583.             HomeData homeData;
  1584.             if (!Home.TryGetValue(player.userID, out homeData) || homeData.Locations.Count <= 0)
  1585.             {
  1586.                 PrintMsgL(player, "HomeListEmpty");
  1587.                 return;
  1588.             }
  1589.             PrintMsgL(player, "HomeList");
  1590.             if (configData.Home.CheckValidOnList)
  1591.             {
  1592.                 var toRemove = new List<string>();
  1593.                 foreach (var location in homeData.Locations)
  1594.                 {
  1595.                     var err = CheckFoundation(player.userID, location.Value);
  1596.                     if (err != null)
  1597.                     {
  1598.                         toRemove.Add(location.Key);
  1599.                         continue;
  1600.                     }
  1601.                     PrintMsgL(player, $"{location.Key} {location.Value}");
  1602.                 }
  1603.                 foreach (var loc in toRemove)
  1604.                 {
  1605.                     PrintMsgL(player, "HomeRemovedInvalid", loc);
  1606.                     homeData.Locations.Remove(loc);
  1607.                     changedHome = true;
  1608.                 }
  1609.                 return;
  1610.             }
  1611.             foreach (var location in homeData.Locations)
  1612.                 PrintMsgL(player, $"{location.Key} {location.Value}");
  1613.         }
  1614.  
  1615.         [ChatCommand("homehomes")]
  1616.         private void cmdChatHomeHomes(BasePlayer player, string command, string[] args)
  1617.         {
  1618.             if (!IsAllowedMsg(player, PermHomeHomes)) return;
  1619.             if (args.Length != 1)
  1620.             {
  1621.                 PrintMsgL(player, "SyntaxCommandHomeHomes");
  1622.                 return;
  1623.             }
  1624.             var userId = FindPlayersSingleId(args[0], player);
  1625.             if (userId <= 0) return;
  1626.             HomeData homeData;
  1627.             if (!Home.TryGetValue(userId, out homeData) || homeData.Locations.Count <= 0)
  1628.             {
  1629.                 PrintMsgL(player, "HomeListEmpty");
  1630.                 return;
  1631.             }
  1632.             PrintMsgL(player, "HomeList");
  1633.             var toRemove = new List<string>();
  1634.             foreach (var location in homeData.Locations)
  1635.             {
  1636.                 var err = CheckFoundation(userId, location.Value);
  1637.                 if (err != null)
  1638.                 {
  1639.                     toRemove.Add(location.Key);
  1640.                     continue;
  1641.                 }
  1642.                 PrintMsgL(player, $"{location.Key} {location.Value}");
  1643.             }
  1644.             foreach (var loc in toRemove)
  1645.             {
  1646.                 PrintMsgL(player, "HomeRemovedInvalid", loc);
  1647.                 homeData.Locations.Remove(loc);
  1648.                 changedHome = true;
  1649.             }
  1650.         }
  1651.  
  1652.         [ChatCommand("tpr")]
  1653.         private void cmdChatTeleportRequest(BasePlayer player, string command, string[] args)
  1654.         {
  1655.             if (!configData.Settings.TPREnabled) return;
  1656.             if (args.Length != 1)
  1657.             {
  1658.                 PrintMsgL(player, "SyntaxCommandTPR");
  1659.                 return;
  1660.             }
  1661.             var targets = FindPlayersOnline(args[0]);
  1662.             if (targets.Count <= 0)
  1663.             {
  1664.                 PrintMsgL(player, "PlayerNotFound");
  1665.                 return;
  1666.             }
  1667.             if (targets.Count > 1)
  1668.             {
  1669.                 PrintMsgL(player, "MultiplePlayers", string.Join(", ", targets.ConvertAll(p => p.displayName).ToArray()));
  1670.                 return;
  1671.             }
  1672.             var target = targets[0];
  1673.             if (target == player)
  1674.             {
  1675.                 PrintMsgL(player, "CantTeleportToSelf");
  1676.                 return;
  1677.             }
  1678.             var err = CheckPlayer(player, configData.TPR.UsableOutOfBuildingBlocked, CanCraftTPR(player));
  1679.             if (err != null)
  1680.             {
  1681.                 PrintMsgL(player, err);
  1682.                 return;
  1683.             }
  1684.             err = CheckTargetLocation(target, target.transform.position, configData.TPR.UsableIntoBuildingBlocked, configData.TPR.CupOwnerAllowOnBuildingBlocked);
  1685.             if (err != null)
  1686.             {
  1687.                 PrintMsgL(player, err);
  1688.                 return;
  1689.             }
  1690.             var timestamp = Facepunch.Math.Epoch.Current;
  1691.             var currentDate = DateTime.Now.ToString("d");
  1692.             TeleportData tprData;
  1693.             if (!TPR.TryGetValue(player.userID, out tprData))
  1694.                 TPR[player.userID] = tprData = new TeleportData();
  1695.             if (tprData.Date != currentDate)
  1696.             {
  1697.                 tprData.Amount = 0;
  1698.                 tprData.Date = currentDate;
  1699.             }
  1700.  
  1701.             var cooldown = GetLower(player, configData.TPR.VIPCooldowns, configData.TPR.Cooldown);
  1702.             if (cooldown > 0 && timestamp - tprData.Timestamp < cooldown)
  1703.             {
  1704.                 var cmdSent = "";
  1705.                 bool foundmoney = CheckEconomy(player, configData.TPR.Bypass);
  1706.                 try
  1707.                 {
  1708.                     cmdSent = args[1].ToLower();
  1709.                 }
  1710.                 catch {}
  1711.  
  1712.                 bool payalso = false;
  1713.                 if(configData.TPR.Pay > 0)
  1714.                 {
  1715.                     payalso = true;
  1716.                 }
  1717.                 if((configData.Settings.BypassCMD != null) && (cmdSent == configData.Settings.BypassCMD.ToLower()))
  1718.                 {
  1719.                     if(foundmoney == true)
  1720.                     {
  1721.                         CheckEconomy(player, configData.TPR.Bypass, true);
  1722.                         PrintMsgL(player, "TPRCooldownBypass", configData.TPR.Bypass);
  1723.                         if(payalso)
  1724.                         {
  1725.                             PrintMsgL(player, "PayToTPR", configData.TPR.Pay);
  1726.                         }
  1727.                     }
  1728.                     else
  1729.                     {
  1730.                         PrintMsgL(player, "TPRCooldownBypassF", configData.TPR.Bypass);
  1731.                         return;
  1732.                     }
  1733.                 }
  1734.                 else if(UseEconomy())
  1735.                 {
  1736.                     var remain = cooldown - (timestamp - tprData.Timestamp);
  1737.                     PrintMsgL(player, "TPRCooldown", FormatTime(remain));
  1738.                     if(configData.TPR.Bypass > 0 && configData.Settings.BypassCMD != null)
  1739.                     {
  1740.                         PrintMsgL(player, "TPRCooldownBypassP", configData.TPR.Bypass);
  1741.                         PrintMsgL(player, "TPRCooldownBypassP2", configData.Settings.BypassCMD);
  1742.                     }
  1743.                     return;
  1744.                 }
  1745.                 else
  1746.                 {
  1747.                     var remain = cooldown - (timestamp - tprData.Timestamp);
  1748.                     PrintMsgL(player, "TPRCooldown", FormatTime(remain));
  1749.                     return;
  1750.                 }
  1751.             }
  1752.             var limit = GetHigher(player, configData.TPR.VIPDailyLimits, configData.TPR.DailyLimit);
  1753.             if (limit > 0 && tprData.Amount >= limit)
  1754.             {
  1755.                 PrintMsgL(player, "TPRLimitReached", limit);
  1756.                 return;
  1757.             }
  1758.             if (TeleportTimers.ContainsKey(player.userID))
  1759.             {
  1760.                 PrintMsgL(player, "TeleportPending");
  1761.                 return;
  1762.             }
  1763.             if (TeleportTimers.ContainsKey(target.userID))
  1764.             {
  1765.                 PrintMsgL(player, "TeleportPendingTarget");
  1766.                 return;
  1767.             }
  1768.             if (PlayersRequests.ContainsKey(player.userID))
  1769.             {
  1770.                 PrintMsgL(player, "PendingRequest");
  1771.                 return;
  1772.             }
  1773.             if (PlayersRequests.ContainsKey(target.userID))
  1774.             {
  1775.                 PrintMsgL(player, "PendingRequestTarget");
  1776.                 return;
  1777.             }
  1778.             err = CanPlayerTeleport(player);
  1779.             if (err != null)
  1780.             {
  1781.                 SendReply(player, err);
  1782.                 return;
  1783.             }
  1784.             err = CanPlayerTeleport(target);
  1785.             if (err != null)
  1786.             {
  1787.                 PrintMsgL(player, "TPRTarget");
  1788.                 return;
  1789.             }
  1790.             err = CheckItems(player);
  1791.             if (err != null)
  1792.             {
  1793.                 PrintMsgL(player, "TPBlockedItem", err);
  1794.                 return;
  1795.             }
  1796.             if(UseEconomy())
  1797.             {
  1798.                 if (configData.TPR.Pay > 0 && !CheckEconomy(player, configData.TPR.Pay))
  1799.                 {
  1800.                     PrintMsgL(player, "TPNoMoney", configData.TPR.Pay);
  1801.                     return;
  1802.                 }
  1803.                 else if(configData.TPR.Pay > 0)
  1804.                 {
  1805.                     var w = CheckEconomy(player, (double)configData.TPR.Pay, true);
  1806.                     PrintMsgL(player, "TPMoney", (double)configData.TPR.Pay);
  1807.                 }
  1808.             }
  1809.             PlayersRequests[player.userID] = target;
  1810.             PlayersRequests[target.userID] = player;
  1811.             PendingRequests[target.userID] = timer.Once(configData.TPR.RequestDuration, () => { RequestTimedOut(player, target); });
  1812.             PrintMsgL(player, "Request", target.displayName);
  1813.             PrintMsgL(target, "RequestTarget", player.displayName);
  1814.         }
  1815.  
  1816.         [ChatCommand("tpa")]
  1817.         private void cmdChatTeleportAccept(BasePlayer player, string command, string[] args)
  1818.         {
  1819.             if (!configData.Settings.TPREnabled) return;
  1820.             if (args.Length != 0)
  1821.             {
  1822.                 PrintMsgL(player, "SyntaxCommandTPA");
  1823.                 return;
  1824.             }
  1825.             Timer reqTimer;
  1826.             if (!PendingRequests.TryGetValue(player.userID, out reqTimer))
  1827.             {
  1828.                 PrintMsgL(player, "NoPendingRequest");
  1829.                 return;
  1830.             }
  1831.             var err = CheckPlayer(player, false, CanCraftTPR(player));
  1832.             if (err != null)
  1833.             {
  1834.                 PrintMsgL(player, err);
  1835.                 return;
  1836.             }
  1837.             err = CanPlayerTeleport(player);
  1838.             if (err != null)
  1839.             {
  1840.                 SendReply(player, err);
  1841.                 return;
  1842.             }
  1843.             var originPlayer = PlayersRequests[player.userID];
  1844.             err = CheckTargetLocation(originPlayer, player.transform.position, configData.TPR.UsableIntoBuildingBlocked, configData.TPR.CupOwnerAllowOnBuildingBlocked);
  1845.             if (err != null)
  1846.             {
  1847.                 SendReply(player, err);
  1848.                 return;
  1849.             }
  1850.             if (configData.TPR.BlockTPAOnCeiling)
  1851.             {
  1852.                 var position = GetGround(player.transform.position);
  1853.                 if (Vector3.Distance(position, player.transform.position) > 2)
  1854.                 {
  1855.                     RaycastHit hitInfo;
  1856.                     BaseEntity entity = null;
  1857.                     if (Physics.SphereCast(player.transform.position, .5f, Vector3.down, out hitInfo, 5, blockLayer))
  1858.                         entity = hitInfo.GetEntity();
  1859.                     if (entity != null && !entity.PrefabName.Contains("foundation"))
  1860.                     {
  1861.                         PrintMsgL(player, "AcceptOnRoof");
  1862.                         return;
  1863.                     }
  1864.                 }
  1865.             }
  1866.             var countdown = GetLower(originPlayer, configData.TPR.VIPCountdowns, configData.TPR.Countdown);
  1867.             PrintMsgL(originPlayer, "Accept", player.displayName, countdown);
  1868.             PrintMsgL(player, "AcceptTarget", originPlayer.displayName);
  1869.             var timestamp = Facepunch.Math.Epoch.Current;
  1870.             TeleportTimers[originPlayer.userID] = new TeleportTimer
  1871.             {
  1872.                 OriginPlayer = originPlayer,
  1873.                 TargetPlayer = player,
  1874.                 Timer = timer.Once(countdown, () =>
  1875.                 {
  1876.                     err = CheckPlayer(originPlayer, configData.TPR.UsableOutOfBuildingBlocked, CanCraftTPR(originPlayer)) ?? CheckPlayer(player, false, CanCraftTPR(player));
  1877.                     if (err != null)
  1878.                     {
  1879.                         PrintMsgL(player, "InterruptedTarget", originPlayer.displayName);
  1880.                         PrintMsgL(originPlayer, "Interrupted");
  1881.                         PrintMsgL(originPlayer, err);
  1882.                         TeleportTimers.Remove(originPlayer.userID);
  1883.                         return;
  1884.                     }
  1885.                     err = CheckTargetLocation(originPlayer, player.transform.position, configData.TPR.UsableIntoBuildingBlocked, configData.TPR.CupOwnerAllowOnBuildingBlocked);
  1886.                     if (err != null)
  1887.                     {
  1888.                         SendReply(player, err);
  1889.                         PrintMsgL(originPlayer, "Interrupted");
  1890.                         SendReply(originPlayer, err);
  1891.                         TeleportTimers.Remove(originPlayer.userID);
  1892.                         return;
  1893.                     }
  1894.                     err = CanPlayerTeleport(originPlayer) ?? CanPlayerTeleport(player);
  1895.                     if (err != null)
  1896.                     {
  1897.                         SendReply(player, err);
  1898.                         PrintMsgL(originPlayer, "Interrupted");
  1899.                         SendReply(originPlayer, err);
  1900.                         TeleportTimers.Remove(originPlayer.userID);
  1901.                         return;
  1902.                     }
  1903.                     err = CheckItems(originPlayer);
  1904.                     if (err != null)
  1905.                     {
  1906.                         PrintMsgL(player, "InterruptedTarget", originPlayer.displayName);
  1907.                         PrintMsgL(originPlayer, "Interrupted");
  1908.                         PrintMsgL(originPlayer, "TPBlockedItem", err);
  1909.                         TeleportTimers.Remove(originPlayer.userID);
  1910.                         return;
  1911.                     }
  1912.                     if(UseEconomy())
  1913.                     {
  1914.                         if (configData.TPR.Pay > 0 && !CheckEconomy(player, configData.TPR.Pay))
  1915.                         {
  1916.                             PrintMsgL(player, "InterruptedTarget", originPlayer.displayName);
  1917.                             PrintMsgL(originPlayer, "TPNoMoney", configData.TPR.Pay);
  1918.                             TeleportTimers.Remove(originPlayer.userID);
  1919.                             return;
  1920.                         }
  1921.                         else if (configData.TPR.Pay > 0)
  1922.                         {
  1923.                             CheckEconomy(player, configData.TPR.Pay, true);
  1924.                             PrintMsgL(player, "TPMoney", (double)configData.TPR.Pay);
  1925.                         }
  1926.                     }
  1927.                     Teleport(originPlayer, CheckPosition(player.transform.position));
  1928.                     var tprData = TPR[originPlayer.userID];
  1929.                     tprData.Amount++;
  1930.                     tprData.Timestamp = timestamp;
  1931.                     changedTPR = true;
  1932.                     PrintMsgL(player, "SuccessTarget", originPlayer.displayName);
  1933.                     PrintMsgL(originPlayer, "Success", player.displayName);
  1934.                     var limit = GetHigher(player, configData.TPR.VIPDailyLimits, configData.TPR.DailyLimit);
  1935.                     if (limit > 0) PrintMsgL(originPlayer, "TPRAmount", limit - tprData.Amount);
  1936.                     TeleportTimers.Remove(originPlayer.userID);
  1937.                 })
  1938.             };
  1939.             reqTimer.Destroy();
  1940.             PendingRequests.Remove(player.userID);
  1941.             PlayersRequests.Remove(player.userID);
  1942.             PlayersRequests.Remove(originPlayer.userID);
  1943.         }
  1944.  
  1945.         [ChatCommand("wipehomes")]
  1946.         private void cmdChatWipeHomes(BasePlayer player, string command, string[] args)
  1947.         {
  1948.             if (!IsAllowedMsg(player, PermWipeHomes)) return;
  1949.             Home.Clear();
  1950.             changedHome = true;
  1951.             PrintMsgL(player, "HomesListWiped");
  1952.         }
  1953.  
  1954.         [ChatCommand("tphelp")]
  1955.         private void cmdChatTeleportHelp(BasePlayer player, string command, string[] args)
  1956.         {
  1957.             if (!configData.Settings.HomesEnabled && !configData.Settings.TPREnabled && !IsAllowedMsg(player)) return;
  1958.             if (args.Length == 1)
  1959.             {
  1960.                 var key = $"TPHelp{args[0].ToLower()}";
  1961.                 var msg = _(key, player);
  1962.                 if (key.Equals(msg))
  1963.                     PrintMsgL(player, "InvalidHelpModule");
  1964.                 else
  1965.                     PrintMsg(player, msg);
  1966.             }
  1967.             else
  1968.             {
  1969.                 var msg = _("TPHelpGeneral", player);
  1970.                 if (IsAllowed(player))
  1971.                     msg += NewLine + "/tphelp AdminTP";
  1972.                 if (configData.Settings.HomesEnabled)
  1973.                     msg += NewLine + "/tphelp Home";
  1974.                 if (configData.Settings.TPREnabled)
  1975.                     msg += NewLine + "/tphelp TPR";
  1976.                 PrintMsg(player, msg);
  1977.             }
  1978.         }
  1979.  
  1980.         [ChatCommand("tpinfo")]
  1981.         private void cmdChatTeleportInfo(BasePlayer player, string command, string[] args)
  1982.         {
  1983.             if (!configData.Settings.HomesEnabled && !configData.Settings.TPREnabled && !configData.Settings.TownEnabled) return;
  1984.             if (args.Length == 1)
  1985.             {
  1986.                 var module = args[0].ToLower();
  1987.                 var msg = _($"TPSettings{module}", player);
  1988.                 var timestamp = Facepunch.Math.Epoch.Current;
  1989.                 var currentDate = DateTime.Now.ToString("d");
  1990.                 TeleportData teleportData;
  1991.                 int limit;
  1992.                 int cooldown;
  1993.                 switch (module)
  1994.                 {
  1995.                     case "home":
  1996.                         limit = GetHigher(player, configData.Home.VIPDailyLimits, configData.Home.DailyLimit);
  1997.                         cooldown = GetLower(player, configData.Home.VIPCooldowns, configData.Home.Cooldown);
  1998.                         PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player), GetHigher(player, configData.Home.VIPHomesLimits, configData.Home.HomesLimit)));
  1999.                         HomeData homeData;
  2000.                         if (!Home.TryGetValue(player.userID, out homeData))
  2001.                             Home[player.userID] = homeData = new HomeData();
  2002.                         if (homeData.Teleports.Date != currentDate)
  2003.                         {
  2004.                             homeData.Teleports.Amount = 0;
  2005.                             homeData.Teleports.Date = currentDate;
  2006.                         }
  2007.                         if (limit > 0) PrintMsgL(player, "HomeTPAmount", limit - homeData.Teleports.Amount);
  2008.                         if (cooldown > 0 && timestamp - homeData.Teleports.Timestamp < cooldown)
  2009.                         {
  2010.                             var remain = cooldown - (timestamp - homeData.Teleports.Timestamp);
  2011.                             PrintMsgL(player, "HomeTPCooldown", FormatTime(remain));
  2012.                         }
  2013.                         break;
  2014.                     case "tpr":
  2015.                         limit = GetHigher(player, configData.TPR.VIPDailyLimits, configData.TPR.DailyLimit);
  2016.                         cooldown = GetLower(player, configData.TPR.VIPCooldowns, configData.TPR.Cooldown);
  2017.                         PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player)));
  2018.                         if (!TPR.TryGetValue(player.userID, out teleportData))
  2019.                             TPR[player.userID] = teleportData = new TeleportData();
  2020.                         if (teleportData.Date != currentDate)
  2021.                         {
  2022.                             teleportData.Amount = 0;
  2023.                             teleportData.Date = currentDate;
  2024.                         }
  2025.                         if (limit > 0) PrintMsgL(player, "TPRAmount", limit - teleportData.Amount);
  2026.                         if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
  2027.                         {
  2028.                             var remain = cooldown - (timestamp - teleportData.Timestamp);
  2029.                             PrintMsgL(player, "TPRCooldown", FormatTime(remain));
  2030.                         }
  2031.                         break;
  2032.                     case "town":
  2033.                         limit = GetHigher(player, configData.Town.VIPDailyLimits, configData.Town.DailyLimit);
  2034.                         cooldown = GetLower(player, configData.Town.VIPCooldowns, configData.Town.Cooldown);
  2035.                         PrintMsg(player, string.Format(msg, FormatTime(cooldown), limit > 0 ? limit.ToString() : _("Unlimited", player)));
  2036.                         if (!Town.TryGetValue(player.userID, out teleportData))
  2037.                             Town[player.userID] = teleportData = new TeleportData();
  2038.                         if (teleportData.Date != currentDate)
  2039.                         {
  2040.                             teleportData.Amount = 0;
  2041.                             teleportData.Date = currentDate;
  2042.                         }
  2043.                         if (limit > 0) PrintMsgL(player, "TownTPAmount", limit - teleportData.Amount);
  2044.                         if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
  2045.                         {
  2046.                             var remain = cooldown - (timestamp - teleportData.Timestamp);
  2047.                             PrintMsgL(player, "TownTPCooldown", FormatTime(remain));
  2048.                             PrintMsgL(player, "TownTPCooldownBypassP", configData.Town.Bypass);
  2049.                             PrintMsgL(player, "TownTPCooldownBypassP2", configData.Settings.BypassCMD);
  2050.                         }
  2051.                         break;
  2052.                     default:
  2053.                         PrintMsgL(player, "InvalidHelpModule");
  2054.                         break;
  2055.                 }
  2056.             }
  2057.             else
  2058.             {
  2059.                 var msg = _("TPInfoGeneral", player);
  2060.                 if (configData.Settings.HomesEnabled)
  2061.                     msg += NewLine + "/tpinfo Home";
  2062.                 if (configData.Settings.TPREnabled)
  2063.                     msg += NewLine + "/tpinfo TPR";
  2064.                 if (configData.Settings.TownEnabled)
  2065.                     msg += NewLine + "/tpinfo Town";
  2066.                 PrintMsgL(player, msg);
  2067.             }
  2068.         }
  2069.  
  2070.         [ChatCommand("tpc")]
  2071.         private void cmdChatTeleportCancel(BasePlayer player, string command, string[] args)
  2072.         {
  2073.             if (!configData.Settings.TPREnabled) return;
  2074.             if (args.Length != 0)
  2075.             {
  2076.                 PrintMsgL(player, "SyntaxCommandTPC");
  2077.                 return;
  2078.             }
  2079.             TeleportTimer teleportTimer;
  2080.             if (TeleportTimers.TryGetValue(player.userID, out teleportTimer))
  2081.             {
  2082.                 teleportTimer.Timer?.Destroy();
  2083.                 PrintMsgL(player, "TPCancelled");
  2084.                 PrintMsgL(teleportTimer.TargetPlayer, "TPCancelledTarget", player.displayName);
  2085.                 TeleportTimers.Remove(player.userID);
  2086.                 return;
  2087.             }
  2088.             foreach (var keyValuePair in TeleportTimers)
  2089.             {
  2090.                 if (keyValuePair.Value.TargetPlayer != player) continue;
  2091.                 keyValuePair.Value.Timer?.Destroy();
  2092.                 PrintMsgL(keyValuePair.Value.OriginPlayer, "TPCancelledTarget", player.displayName);
  2093.                 PrintMsgL(player, "TPYouCancelledTarget", keyValuePair.Value.OriginPlayer.displayName);
  2094.                 TeleportTimers.Remove(keyValuePair.Key);
  2095.                 return;
  2096.             }
  2097.             BasePlayer target;
  2098.             if (!PlayersRequests.TryGetValue(player.userID, out target))
  2099.             {
  2100.                 PrintMsgL(player, "NoPendingRequest");
  2101.                 return;
  2102.             }
  2103.             Timer reqTimer;
  2104.             if (PendingRequests.TryGetValue(player.userID, out reqTimer))
  2105.             {
  2106.                 reqTimer.Destroy();
  2107.                 PendingRequests.Remove(player.userID);
  2108.             }
  2109.             else if (PendingRequests.TryGetValue(target.userID, out reqTimer))
  2110.             {
  2111.                 reqTimer.Destroy();
  2112.                 PendingRequests.Remove(target.userID);
  2113.                 var temp = player;
  2114.                 player = target;
  2115.                 target = temp;
  2116.             }
  2117.             PlayersRequests.Remove(target.userID);
  2118.             PlayersRequests.Remove(player.userID);
  2119.             PrintMsgL(player, "Cancelled", target.displayName);
  2120.             PrintMsgL(target, "CancelledTarget", player.displayName);
  2121.         }
  2122.  
  2123.         [ChatCommand("town")]
  2124.         private void cmdChatTown(BasePlayer player, string command, string[] args)
  2125.         {
  2126.             if (!IsAllowedMsg(player, PermTpTown)) return;
  2127.  
  2128.             if (args.Length == 1 && IsAllowed(player) && args[0].ToLower().Equals("set"))
  2129.             {
  2130.                 configData.Town.Location = player.transform.position;
  2131.                 Config.WriteObject(configData, true);
  2132.                 PrintMsgL(player, "TownTPLocation", configData.Town.Location);
  2133.                 return;
  2134.             }
  2135.             if (args.Length == 1 && (args[0].ToLower() != configData.Settings.BypassCMD.ToLower()))
  2136.             {
  2137.                 PrintMsgL(player, "SyntaxCommandTown");
  2138.                 if (IsAllowed(player)) PrintMsgL(player, "SyntaxCommandTownAdmin");
  2139.                 return;
  2140.             }
  2141.  
  2142.             if (configData.Town.Location == default(Vector3))
  2143.             {
  2144.                 PrintMsgL(player, "TownTPNotSet");
  2145.                 return;
  2146.             }
  2147.             var err = CheckPlayer(player, configData.Town.UsableOutOfBuildingBlocked, CanCraftTown(player));
  2148.             if (err != null)
  2149.             {
  2150.                 PrintMsgL(player, err);
  2151.                 return;
  2152.             }
  2153.             TeleportData teleportData;
  2154.             if (!Town.TryGetValue(player.userID, out teleportData))
  2155.                 Town[player.userID] = teleportData = new TeleportData();
  2156.             var timestamp = Facepunch.Math.Epoch.Current;
  2157.             var currentDate = DateTime.Now.ToString("d");
  2158.             if (teleportData.Date != currentDate)
  2159.             {
  2160.                 teleportData.Amount = 0;
  2161.                 teleportData.Date = currentDate;
  2162.             }
  2163.             var cooldown = GetLower(player, configData.Town.VIPCooldowns, configData.Town.Cooldown);
  2164.             if (cooldown > 0 && timestamp - teleportData.Timestamp < cooldown)
  2165.             {
  2166.                 var cmdSent = "";
  2167.                 bool foundmoney = CheckEconomy(player, configData.Town.Bypass);
  2168.                 try
  2169.                 {
  2170.                     cmdSent = args[0].ToLower();
  2171.                 }
  2172.                 catch {}
  2173.  
  2174.                 bool payalso = false;
  2175.                 if(configData.Town.Pay > 0)
  2176.                 {
  2177.                     payalso = true;
  2178.                 }
  2179.                 if((configData.Settings.BypassCMD != null) && (cmdSent == configData.Settings.BypassCMD.ToLower()))
  2180.                 {
  2181.                     if(foundmoney == true)
  2182.                     {
  2183.                         CheckEconomy(player, configData.Town.Bypass, true);
  2184.                         PrintMsgL(player, "TownTPCooldownBypass", configData.Town.Bypass);
  2185.                         if(payalso)
  2186.                         {
  2187.                             PrintMsgL(player, "PayToTown", configData.Town.Pay);
  2188.                         }
  2189.                     }
  2190.                     else
  2191.                     {
  2192.                         PrintMsgL(player, "TownTPCooldownBypassF", configData.Town.Bypass);
  2193.                         return;
  2194.                     }
  2195.                 }
  2196.                 else if(UseEconomy())
  2197.                 {
  2198.                     var remain = cooldown - (timestamp - teleportData.Timestamp);
  2199.                     PrintMsgL(player, "TownTPCooldown", FormatTime(remain));
  2200.                     if(configData.Town.Bypass > 0 && configData.Settings.BypassCMD != null)
  2201.                     {
  2202.                         PrintMsgL(player, "TownTPCooldownBypassP", configData.Town.Bypass);
  2203.                         PrintMsgL(player, "TownTPCooldownBypassP2", configData.Settings.BypassCMD);
  2204.                     }
  2205.                     return;
  2206.                 }
  2207.                 else
  2208.                 {
  2209.                     var remain = cooldown - (timestamp - teleportData.Timestamp);
  2210.                     PrintMsgL(player, "TownTPCooldown", FormatTime(remain));
  2211.                     return;
  2212.                 }
  2213.             }
  2214.  
  2215.             var limit = GetHigher(player, configData.Town.VIPDailyLimits, configData.Town.DailyLimit);
  2216.             if (limit > 0 && teleportData.Amount >= limit)
  2217.             {
  2218.                 PrintMsgL(player, "TownTPLimitReached", limit);
  2219.                 return;
  2220.             }
  2221.             if (TeleportTimers.ContainsKey(player.userID))
  2222.             {
  2223.                 PrintMsgL(player, "TeleportPending");
  2224.                 return;
  2225.             }
  2226.             err = CanPlayerTeleport(player);
  2227.             if (err != null)
  2228.             {
  2229.                 SendReply(player, err);
  2230.                 return;
  2231.             }
  2232.             err = CheckItems(player);
  2233.             if (err != null)
  2234.             {
  2235.                 PrintMsgL(player, "TPBlockedItem", err);
  2236.                 return;
  2237.             }
  2238.  
  2239.             var countdown = GetLower(player, configData.Town.VIPCountdowns, configData.Town.Countdown);
  2240.             TeleportTimers[player.userID] = new TeleportTimer
  2241.             {
  2242.                 OriginPlayer = player,
  2243.                 Timer = timer.Once(countdown, () =>
  2244.                 {
  2245.                     err = CheckPlayer(player, configData.Town.UsableOutOfBuildingBlocked, CanCraftTown(player));
  2246.                     if (err != null)
  2247.                     {
  2248.                         PrintMsgL(player, "Interrupted");
  2249.                         PrintMsgL(player, err);
  2250.                         TeleportTimers.Remove(player.userID);
  2251.                         return;
  2252.                     }
  2253.                     err = CanPlayerTeleport(player);
  2254.                     if (err != null)
  2255.                     {
  2256.                         PrintMsgL(player, "Interrupted");
  2257.                         SendReply(player, err);
  2258.                         TeleportTimers.Remove(player.userID);
  2259.                         return;
  2260.                     }
  2261.                     err = CheckItems(player);
  2262.                     if (err != null)
  2263.                     {
  2264.                         PrintMsgL(player, "Interrupted");
  2265.                         PrintMsgL(player, "TPBlockedItem", err);
  2266.                         TeleportTimers.Remove(player.userID);
  2267.                         return;
  2268.                     }
  2269.                     if(UseEconomy())
  2270.                     {
  2271.                         if(configData.Town.Pay > 0 && ! CheckEconomy(player, configData.Town.Pay))
  2272.                         {
  2273.                             PrintMsgL(player, "Interrupted");
  2274.                             PrintMsgL(player, "TPNoMoney", configData.Town.Pay);
  2275.                             TeleportTimers.Remove(player.userID);
  2276.                             return;
  2277.                         }
  2278.                         else if (configData.Town.Pay > 0)
  2279.                         {
  2280.                             CheckEconomy(player, configData.Town.Pay, true);
  2281.                             PrintMsgL(player, "TPMoney", (double)configData.Town.Pay);
  2282.                         }
  2283.                     }
  2284.                     Teleport(player, configData.Town.Location);
  2285.                     teleportData.Amount++;
  2286.                     teleportData.Timestamp = timestamp;
  2287.  
  2288.                     changedTown = true;
  2289.                     PrintMsgL(player, "TownTP");
  2290.                     if (limit > 0) PrintMsgL(player, "TownTPAmount", limit - teleportData.Amount);
  2291.                     TeleportTimers.Remove(player.userID);
  2292.                 })
  2293.             };
  2294.             PrintMsgL(player, "TownTPStarted", countdown);
  2295.         }
  2296.  
  2297.         private bool ccmdTeleport(ConsoleSystem.Arg arg)
  2298.         {
  2299.             if (arg.Player() != null && !IsAllowedMsg(arg.Player(), PermTpConsole)) return false;
  2300.             HashSet<BasePlayer> players;
  2301.             switch (arg.cmd.FullName)
  2302.             {
  2303.                 case "teleport.topos":
  2304.                     if (!arg.HasArgs(4))
  2305.                     {
  2306.                         arg.ReplyWith(_("SyntaxConsoleCommandToPos", arg.Player()));
  2307.                         return false;
  2308.                     }
  2309.                     players = FindPlayers(arg.GetString(0));
  2310.                     if (players.Count <= 0)
  2311.                     {
  2312.                         arg.ReplyWith(_("PlayerNotFound", arg.Player()));
  2313.                         return false;
  2314.                     }
  2315.                     if (players.Count > 1)
  2316.                     {
  2317.                         arg.ReplyWith(_("MultiplePlayers", arg.Player(), string.Join(", ", players.Select(p => p.displayName).ToArray())));
  2318.                         return false;
  2319.                     }
  2320.                     var targetPlayer = players.First();
  2321.                     var x = arg.GetFloat(1, -10000);
  2322.                     var y = arg.GetFloat(2, -10000);
  2323.                     var z = arg.GetFloat(3, -10000);
  2324.                     if (!CheckBoundaries(x, y, z))
  2325.                     {
  2326.                         arg.ReplyWith(_("AdminTPOutOfBounds", arg.Player()) + Environment.NewLine + _("AdminTPBoundaries", arg.Player(), boundary));
  2327.                         return false;
  2328.                     }
  2329.                     TeleportToPosition(targetPlayer, x, y, z);
  2330.                     PrintMsgL(targetPlayer, "AdminTPConsoleTP", targetPlayer.transform.position);
  2331.                     arg.ReplyWith(_("AdminTPTargetCoordinates", arg.Player(), targetPlayer.displayName, targetPlayer.transform.position));
  2332.                     Puts(_("LogTeleportPlayer", null, arg.Player()?.displayName, targetPlayer.displayName, targetPlayer.transform.position));
  2333.                     break;
  2334.                 case "teleport.toplayer":
  2335.                     if (!arg.HasArgs(2))
  2336.                     {
  2337.                         arg.ReplyWith(_("SyntaxConsoleCommandToPlayer", arg.Player()));
  2338.                         return false;
  2339.                     }
  2340.                     players = FindPlayers(arg.GetString(0));
  2341.                     if (players.Count <= 0)
  2342.                     {
  2343.                         arg.ReplyWith(_("PlayerNotFound", arg.Player()));
  2344.                         return false;
  2345.                     }
  2346.                     if (players.Count > 1)
  2347.                     {
  2348.                         arg.ReplyWith(_("MultiplePlayers", arg.Player(), string.Join(", ", players.Select(p => p.displayName).ToArray())));
  2349.                         return false;
  2350.                     }
  2351.                     var originPlayer = players.First();
  2352.                     players = FindPlayers(arg.GetString(1));
  2353.                     if (players.Count <= 0)
  2354.                     {
  2355.                         arg.ReplyWith(_("PlayerNotFound", arg.Player()));
  2356.                         return false;
  2357.                     }
  2358.                     if (players.Count > 1)
  2359.                     {
  2360.                         arg.ReplyWith(_("MultiplePlayers", arg.Player(), string.Join(", ", players.Select(p => p.displayName).ToArray())));
  2361.                         return false;
  2362.                     }
  2363.                     targetPlayer = players.First();
  2364.                     if (targetPlayer == originPlayer)
  2365.                     {
  2366.                         arg.ReplyWith(_("CantTeleportPlayerToSelf", arg.Player()));
  2367.                         return false;
  2368.                     }
  2369.                     TeleportToPlayer(originPlayer, targetPlayer);
  2370.                     arg.ReplyWith(_("AdminTPPlayers", arg.Player(), originPlayer.displayName, targetPlayer.displayName));
  2371.                     PrintMsgL(originPlayer, "AdminTPConsoleTPPlayer", targetPlayer.displayName);
  2372.                     PrintMsgL(targetPlayer, "AdminTPConsoleTPPlayerTarget", originPlayer.displayName);
  2373.                     Puts(_("LogTeleportPlayer", null, arg.Player()?.displayName, originPlayer.displayName, targetPlayer.displayName));
  2374.                     break;
  2375.             }
  2376.             return false;
  2377.         }
  2378.  
  2379.         [ConsoleCommand("teleport.importhomes")]
  2380.         private bool ccmdImportHomes(ConsoleSystem.Arg arg)
  2381.         {
  2382.             if (arg.Player() != null && !IsAllowedMsg(arg.Player(), PermImportHomes))
  2383.             {
  2384.                 arg.ReplyWith("Not allowed.");
  2385.                 return false;
  2386.             }
  2387.             var datafile = Interface.Oxide.DataFileSystem.GetFile("m-Teleportation");
  2388.             if (!datafile.Exists())
  2389.             {
  2390.                 arg.ReplyWith("No m-Teleportation.json exists.");
  2391.                 return false;
  2392.             }
  2393.             datafile.Load();
  2394.             var allHomeData = datafile["HomeData"] as Dictionary<string, object>;
  2395.             if (allHomeData == null)
  2396.             {
  2397.                 arg.ReplyWith("Empty HomeData.");
  2398.                 return false;
  2399.             }
  2400.             var count = 0;
  2401.             foreach (var kvp in allHomeData)
  2402.             {
  2403.                 var homeDataOld = kvp.Value as Dictionary<string, object>;
  2404.                 if (homeDataOld == null) continue;
  2405.                 if (!homeDataOld.ContainsKey("HomeLocations")) continue;
  2406.                 var homeList = homeDataOld["HomeLocations"] as Dictionary<string, object>;
  2407.                 if (homeList == null) continue;
  2408.                 var userId = Convert.ToUInt64(kvp.Key);
  2409.                 HomeData homeData;
  2410.                 if (!Home.TryGetValue(userId, out homeData))
  2411.                     Home[userId] = homeData = new HomeData();
  2412.                 foreach (var kvp2 in homeList)
  2413.                 {
  2414.                     var positionData = kvp2.Value as Dictionary<string, object>;
  2415.                     if (positionData == null) continue;
  2416.                     if (!positionData.ContainsKey("x") || !positionData.ContainsKey("y") || !positionData.ContainsKey("z")) continue;
  2417.                     var position = new Vector3(Convert.ToSingle(positionData["x"]), Convert.ToSingle(positionData["y"]), Convert.ToSingle(positionData["z"]));
  2418.                     homeData.Locations[kvp2.Key] = position;
  2419.                     changedHome = true;
  2420.                     count++;
  2421.                 }
  2422.             }
  2423.             arg.ReplyWith(string.Format("Imported {0} homes.", count));
  2424.             return false;
  2425.         }
  2426.  
  2427.         private void RequestTimedOut(BasePlayer player, BasePlayer target)
  2428.         {
  2429.             PlayersRequests.Remove(player.userID);
  2430.             PlayersRequests.Remove(target.userID);
  2431.             PendingRequests.Remove(target.userID);
  2432.             PrintMsgL(player, "TimedOut", target.displayName);
  2433.             PrintMsgL(target, "TimedOutTarget", player.displayName);
  2434.         }
  2435.  
  2436.         #region Util
  2437.  
  2438.         private string FormatTime(long seconds)
  2439.         {
  2440.             var timespan = TimeSpan.FromSeconds(seconds);
  2441.             return string.Format(timespan.TotalHours >= 1 ? "{2:00}:{0:00}:{1:00}" : "{0:00}:{1:00}", timespan.Minutes, timespan.Seconds, System.Math.Floor(timespan.TotalHours));
  2442.         }
  2443.  
  2444.         private double ConvertToRadians(double angle)
  2445.         {
  2446.             return System.Math.PI / 180 * angle;
  2447.         }
  2448.  
  2449.         #region Teleport
  2450.  
  2451.         public void TeleportToPlayer(BasePlayer player, BasePlayer target) => Teleport(player, target.transform.position);
  2452.  
  2453.         public void TeleportToPosition(BasePlayer player, float x, float y, float z) => Teleport(player, new Vector3(x, y, z));
  2454.  
  2455.         public void Teleport(BasePlayer player, Vector3 position)
  2456.         {
  2457.             SaveLocation(player);
  2458.             teleporting.Add(player.userID);
  2459.             if (player.net?.connection != null)
  2460.                 player.ClientRPCPlayer(null, player, "StartLoading");
  2461.             StartSleeping(player);
  2462.             player.MovePosition(position);
  2463.             if (player.net?.connection != null)
  2464.                 player.ClientRPCPlayer(null, player, "ForcePositionTo", position);
  2465.             if (player.net?.connection != null)
  2466.                 player.SetPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot, true);
  2467.             player.UpdateNetworkGroup();
  2468.             //player.UpdatePlayerCollider(true, false);
  2469.             player.SendNetworkUpdateImmediate(false);
  2470.             if (player.net?.connection == null) return;
  2471.             //TODO temporary for potential rust bug
  2472.             try
  2473.             {
  2474.                 player.ClearEntityQueue(null);
  2475.             }
  2476.             catch
  2477.             {
  2478.             }
  2479.             player.SendFullSnapshot();
  2480.         }
  2481.  
  2482.         private void StartSleeping(BasePlayer player)
  2483.         {
  2484.             if (player.IsSleeping())
  2485.                 return;
  2486.             player.SetPlayerFlag(BasePlayer.PlayerFlags.Sleeping, true);
  2487.             if (!BasePlayer.sleepingPlayerList.Contains(player))
  2488.                 BasePlayer.sleepingPlayerList.Add(player);
  2489.             player.CancelInvoke("InventoryUpdate");
  2490.             //player.inventory.crafting.CancelAll(true);
  2491.             //player.UpdatePlayerCollider(true, false);
  2492.         }
  2493.  
  2494.         #endregion
  2495.  
  2496.         #region Checks
  2497.  
  2498.         private Vector3 CheckPosition(Vector3 position)
  2499.         {
  2500.             var hits = Physics.OverlapSphere(position, 2, blockLayer);
  2501.             var distance = 5f;
  2502.             BuildingBlock buildingBlock = null;
  2503.             for (var i = 0; i < hits.Length; i++)
  2504.             {
  2505.                 var block = hits[i].GetComponentInParent<BuildingBlock>();
  2506.                 if (block == null) continue;
  2507.                 var prefab = block.PrefabName;
  2508.                 if (!prefab.Contains("foundation", CompareOptions.OrdinalIgnoreCase) && !prefab.Contains("floor", CompareOptions.OrdinalIgnoreCase) && !prefab.Contains("pillar", CompareOptions.OrdinalIgnoreCase)) continue;
  2509.                 if (!(Vector3.Distance(block.transform.position, position) < distance)) continue;
  2510.                 buildingBlock = block;
  2511.                 distance = Vector3.Distance(block.transform.position, position);
  2512.             }
  2513.             if (buildingBlock == null) return position;
  2514.             var blockRotation = buildingBlock.transform.rotation.eulerAngles.y;
  2515.             var angles = new[] { 360 - blockRotation, 180 - blockRotation };
  2516.             var location = default(Vector3);
  2517.             const double r = 1.9;
  2518.             var locationDistance = 100f;
  2519.             for (var i = 0; i < angles.Length; i++)
  2520.             {
  2521.                 var radians = ConvertToRadians(angles[i]);
  2522.                 var newX = r * System.Math.Cos(radians);
  2523.                 var newZ = r * System.Math.Sin(radians);
  2524.                 var newLoc = new Vector3((float)(buildingBlock.transform.position.x + newX), buildingBlock.transform.position.y + .2f, (float)(buildingBlock.transform.position.z + newZ));
  2525.                 if (Vector3.Distance(position, newLoc) < locationDistance)
  2526.                 {
  2527.                     location = newLoc;
  2528.                     locationDistance = Vector3.Distance(position, newLoc);
  2529.                 }
  2530.             }
  2531.             return location;
  2532.         }
  2533.  
  2534.         private string CanPlayerTeleport(BasePlayer player)
  2535.         {
  2536.             return Interface.Oxide.CallHook("CanTeleport", player) as string;
  2537.         }
  2538.  
  2539.         private bool CanCraftHome(BasePlayer player)
  2540.         {
  2541.             return configData.Home.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftHome);
  2542.         }
  2543.  
  2544.         private bool CanCraftTown(BasePlayer player)
  2545.         {
  2546.             return configData.Town.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftTown);
  2547.         }
  2548.  
  2549.         private bool CanCraftTPR(BasePlayer player)
  2550.         {
  2551.             return configData.TPR.AllowCraft || permission.UserHasPermission(player.UserIDString, PermCraftTpR);
  2552.         }
  2553.  
  2554.         private string CheckPlayer(BasePlayer player, bool build = false, bool craft = false)
  2555.         {
  2556.             var onship = player.GetComponentInParent<CargoShip>();
  2557.             var onballoon = player.GetComponentInParent<HotAirBalloon>();
  2558.             if (player.isMounted)
  2559.                 return "TPMounted";
  2560.             if (!player.IsAlive())
  2561.                 return "TPDead";
  2562.             if (player.IsWounded())
  2563.                 return "TPWounded";
  2564.             if (!build && !player.CanBuild())
  2565.                 return "TPBuildingBlocked";
  2566.             if (player.IsSwimming())
  2567.                 return "TPSwimming";
  2568.             if (onship)
  2569.                 return "TPCargoShip";
  2570.             if (onballoon)
  2571.                 return "TPHotAirBalloon";
  2572.             if (player.InSafeZone())
  2573.                 return "TPSafeZone";
  2574.             if (!craft && player.inventory.crafting.queue.Count > 0)
  2575.                 return "TPCrafting";
  2576.             return null;
  2577.         }
  2578.  
  2579.         private string CheckTargetLocation(BasePlayer player, Vector3 targetLocation, bool build, bool owner)
  2580.         {
  2581.             var colliders = Pool.GetList<Collider>();
  2582.             Vis.Colliders(targetLocation, 0.1f, colliders, triggerLayer);
  2583.             var cups = false;
  2584.             foreach (var collider in colliders)
  2585.             {
  2586.                 var cup = collider.GetComponentInParent<BuildingPrivlidge>();
  2587.                 if (cup == null) continue;
  2588.                 cups = true;
  2589.                 if (owner && player.userID == cup.OwnerID)
  2590.                 {
  2591.                     Pool.FreeList(ref colliders);
  2592.                     return null;
  2593.                 }
  2594.                 if (cup.IsAuthed(player))
  2595.                 {
  2596.                     Pool.FreeList(ref colliders);
  2597.                     return null;
  2598.                 }
  2599.             }
  2600.             Pool.FreeList(ref colliders);
  2601.             return cups && !build ? "TPTargetBuildingBlocked" : null;
  2602.         }
  2603.  
  2604.         private string CheckInsideBlock(Vector3 targetLocation)
  2605.         {
  2606.             List<BuildingBlock> blocks = Pool.GetList<BuildingBlock>();
  2607.             Vis.Entities(targetLocation + new Vector3(0, 0.25f), 0.1f, blocks, blockLayer);
  2608.             bool inside = blocks.Count > 0;
  2609.             Pool.FreeList(ref blocks);
  2610.  
  2611.             return inside ? "TPTargetInsideBlock" : null;
  2612.         }
  2613.  
  2614.         private string CheckItems(BasePlayer player)
  2615.         {
  2616.             foreach (var blockedItem in ReverseBlockedItems)
  2617.             {
  2618.                 if (player.inventory.containerMain.GetAmount(blockedItem.Key, true) > 0)
  2619.                     return blockedItem.Value;
  2620.                 if (player.inventory.containerBelt.GetAmount(blockedItem.Key, true) > 0)
  2621.                     return blockedItem.Value;
  2622.                 if (player.inventory.containerWear.GetAmount(blockedItem.Key, true) > 0)
  2623.                     return blockedItem.Value;
  2624.             }
  2625.             return null;
  2626.         }
  2627.  
  2628.         private string CheckFoundation(ulong userID, Vector3 position)
  2629.         {
  2630.             if (UnderneathFoundation(position))
  2631.             {
  2632.                 return "HomeFoundationUnderneathFoundation";
  2633.             }
  2634.  
  2635.             if (!configData.Home.ForceOnTopOfFoundation) return null;
  2636.  
  2637.             var entities = GetFoundation(position);
  2638.             if (entities.Count == 0)
  2639.                 return "HomeNoFoundation";
  2640.  
  2641.             if (!configData.Home.CheckFoundationForOwner) return null;
  2642.             for (var i = 0; i < entities.Count; i++)
  2643.                 if (entities[i].OwnerID == userID) return null;
  2644.             if (!configData.Home.UseFriends)
  2645.                 return "HomeFoundationNotOwned";
  2646.  
  2647.             var moderator = (bool)(Clans?.CallHook("IsModerator", userID) ?? false);
  2648.             var userIdString = userID.ToString();
  2649.             for (var i = 0; i < entities.Count; i++)
  2650.             {
  2651.                 var entity = entities[i];
  2652.                 if ((bool)(Friends?.CallHook("HasFriend", entity.OwnerID, userID) ?? false) || (bool)(Clans?.CallHook("HasFriend", entity.OwnerID, userID) ?? false) && moderator || (bool)(RustIO?.CallHook("HasFriend", entity.OwnerID.ToString(), userIdString) ?? false))
  2653.                     return null;
  2654.             }
  2655.  
  2656.             return "HomeFoundationNotFriendsOwned";
  2657.         }
  2658.  
  2659.         private BuildingBlock GetFoundationOwned(Vector3 position, ulong userID)
  2660.         {
  2661.             var entities = GetFoundation(position);
  2662.             if (entities.Count == 0)
  2663.                 return null;
  2664.             if (!configData.Home.CheckFoundationForOwner) return entities[0];
  2665.             for (var i = 0; i < entities.Count; i++)
  2666.                 if (entities[i].OwnerID == userID) return entities[i];
  2667.             if (!configData.Home.UseFriends)
  2668.                 return null;
  2669.             var moderator = (bool)(Clans?.CallHook("IsModerator", userID) ?? false);
  2670.             var userIdString = userID.ToString();
  2671.             for (var i = 0; i < entities.Count; i++)
  2672.             {
  2673.                 var entity = entities[i];
  2674.                 if ((bool)(Friends?.CallHook("HasFriend", entity.OwnerID, userID) ?? false) || (bool)(Clans?.CallHook("HasFriend", entity.OwnerID, userID) ?? false) && moderator || (bool)(RustIO?.CallHook("HasFriend", entity.OwnerID.ToString(), userIdString) ?? false))
  2675.                     return entity;
  2676.             }
  2677.             return null;
  2678.         }
  2679.  
  2680.         private List<BuildingBlock> GetFoundation(Vector3 positionCoordinates)
  2681.         {
  2682.             var position = GetGround(positionCoordinates);
  2683.             var entities = new List<BuildingBlock>();
  2684.             var hits = Pool.GetList<BuildingBlock>();
  2685.             Vis.Entities(position, 2.5f, hits, buildingLayer);
  2686.             for (var i = 0; i < hits.Count; i++)
  2687.             {
  2688.                 var entity = hits[i];
  2689.                 if (!entity.PrefabName.Contains("foundation") || positionCoordinates.y < entity.WorldSpaceBounds().ToBounds().max.y) continue;
  2690.                 entities.Add(entity);
  2691.             }
  2692.             Pool.FreeList(ref hits);
  2693.             return entities;
  2694.         }
  2695.  
  2696.         private bool CheckBoundaries(float x, float y, float z)
  2697.         {
  2698.             return x <= boundary && x >= -boundary && y < 2000 && y >= -100 && z <= boundary && z >= -boundary;
  2699.         }
  2700.  
  2701.         private Vector3 GetGround(Vector3 sourcePos)
  2702.         {
  2703.             if (!configData.Home.AllowAboveFoundation) return sourcePos;
  2704.             var newPos = sourcePos;
  2705.             newPos.y = TerrainMeta.HeightMap.GetHeight(newPos);
  2706.             sourcePos.y += .5f;
  2707.             RaycastHit hitinfo;
  2708.             var done = false;
  2709.             if (Physics.SphereCast(sourcePos, .1f, Vector3.down, out hitinfo, 250, groundLayer))
  2710.             {
  2711.                 if ((configData.Home.AllowIceberg && hitinfo.collider.name.Contains("iceberg")) || (configData.Home.AllowCave && hitinfo.collider.name.Contains("cave_")))
  2712.                 {
  2713.                     sourcePos.y = hitinfo.point.y;
  2714.                     done = true;
  2715.                 }
  2716.                 else
  2717.                 {
  2718.                     var mesh = hitinfo.collider.GetComponentInChildren<MeshCollider>();
  2719.                     if (mesh != null && mesh.sharedMesh.name.Contains("rock_"))
  2720.                     {
  2721.                         sourcePos.y = hitinfo.point.y;
  2722.                         done = true;
  2723.                     }
  2724.                 }
  2725.             }
  2726.             if (!configData.Home.AllowCave && Physics.SphereCast(sourcePos, .1f, Vector3.up, out hitinfo, 250, groundLayer) && hitinfo.collider.name.Contains("rock_"))
  2727.             {
  2728.                 sourcePos.y = newPos.y - 10;
  2729.                 done = true;
  2730.             }
  2731.             return done ? sourcePos : newPos;
  2732.         }
  2733.  
  2734.         private Vector3 GetGroundBuilding(Vector3 sourcePos)
  2735.         {
  2736.             sourcePos.y = TerrainMeta.HeightMap.GetHeight(sourcePos);
  2737.             RaycastHit hitinfo;
  2738.             if (Physics.Raycast(sourcePos, Vector3.down, out hitinfo, buildingLayer))
  2739.             {
  2740.                 sourcePos.y = System.Math.Max(hitinfo.point.y, sourcePos.y);
  2741.                 return sourcePos;
  2742.             }
  2743.             if (Physics.Raycast(sourcePos, Vector3.up, out hitinfo, buildingLayer))
  2744.                 sourcePos.y = System.Math.Max(hitinfo.point.y, sourcePos.y);
  2745.             return sourcePos;
  2746.         }
  2747.  
  2748.         private bool UnderneathFoundation(Vector3 position)
  2749.         {
  2750.             RaycastHit hit;
  2751.             if (Physics.Raycast(new Ray(position, Vector3.up), out hit, 10, buildingLayer))
  2752.             {
  2753.                 if (hit.GetCollider().name.Contains("foundation"))
  2754.                 {
  2755.                     return true;
  2756.                 }
  2757.             }
  2758.  
  2759.             return false;
  2760.         }
  2761.  
  2762.         private bool IsAllowed(BasePlayer player, string perm = null)
  2763.         {
  2764.             var playerAuthLevel = player.net?.connection?.authLevel;
  2765.             var requiredAuthLevel = configData.Admin.UseableByModerators ? 1 : 2;
  2766.             if (playerAuthLevel >= requiredAuthLevel) return true;
  2767.             return !string.IsNullOrEmpty(perm) && permission.UserHasPermission(player.UserIDString, perm);
  2768.         }
  2769.  
  2770.         private bool IsAllowedMsg(BasePlayer player, string perm = null)
  2771.         {
  2772.             if (IsAllowed(player, perm)) return true;
  2773.             PrintMsg(player, "NotAllowed");
  2774.             return false;
  2775.         }
  2776.  
  2777.         private int GetHigher(BasePlayer player, Dictionary<string, int> limits, int limit)
  2778.         {
  2779.             foreach (var l in limits)
  2780.             {
  2781.                 if (permission.UserHasPermission(player.UserIDString, l.Key) && l.Value > limit)
  2782.                     limit = l.Value;
  2783.             }
  2784.             return limit;
  2785.         }
  2786.  
  2787.         private int GetLower(BasePlayer player, Dictionary<string, int> times, int time)
  2788.         {
  2789.             foreach (var l in times)
  2790.             {
  2791.                 if (permission.UserHasPermission(player.UserIDString, l.Key) && l.Value < time)
  2792.                     time = l.Value;
  2793.             }
  2794.             return time;
  2795.         }
  2796.  
  2797.         private void CheckPerms(Dictionary<string, int> limits)
  2798.         {
  2799.             foreach (var limit in limits)
  2800.             {
  2801.                 if (!permission.PermissionExists(limit.Key))
  2802.                     permission.RegisterPermission(limit.Key, this);
  2803.             }
  2804.         }
  2805.  
  2806.         #endregion
  2807.  
  2808.         #region Message
  2809.  
  2810.         private string _(string msgId, BasePlayer player, params object[] args)
  2811.         {
  2812.             var msg = lang.GetMessage(msgId, this, player?.UserIDString);
  2813.             return args.Length > 0 ? string.Format(msg, args) : msg;
  2814.         }
  2815.  
  2816.         private void PrintMsgL(BasePlayer player, string msgId, params object[] args)
  2817.         {
  2818.             if (player == null) return;
  2819.             PrintMsg(player, _(msgId, player, args));
  2820.         }
  2821.  
  2822.         private void PrintMsg(BasePlayer player, string msg)
  2823.         {
  2824.             if (player == null) return;
  2825.             SendReply(player, $"{configData.Settings.ChatName}{msg}");
  2826.         }
  2827.  
  2828.         #endregion
  2829.  
  2830.         #region DrawBox
  2831.  
  2832.         private static void DrawBox(BasePlayer player, Vector3 center, Quaternion rotation, Vector3 size)
  2833.         {
  2834.             size = size / 2;
  2835.             var point1 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y + size.y, center.z + size.z), center, rotation);
  2836.             var point2 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y - size.y, center.z + size.z), center, rotation);
  2837.             var point3 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y + size.y, center.z - size.z), center, rotation);
  2838.             var point4 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y - size.y, center.z - size.z), center, rotation);
  2839.             var point5 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y + size.y, center.z + size.z), center, rotation);
  2840.             var point6 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y - size.y, center.z + size.z), center, rotation);
  2841.             var point7 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y + size.y, center.z - size.z), center, rotation);
  2842.             var point8 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y - size.y, center.z - size.z), center, rotation);
  2843.  
  2844.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point1, point2);
  2845.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point1, point3);
  2846.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point1, point5);
  2847.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point4, point2);
  2848.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point4, point3);
  2849.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point4, point8);
  2850.  
  2851.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point5, point6);
  2852.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point5, point7);
  2853.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point6, point2);
  2854.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point8, point6);
  2855.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point8, point7);
  2856.             player.SendConsoleCommand("ddraw.line", 30f, Color.blue, point7, point3);
  2857.         }
  2858.  
  2859.         private static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion rotation)
  2860.         {
  2861.             return rotation * (point - pivot) + pivot;
  2862.         }
  2863.  
  2864.         #endregion
  2865.  
  2866.         #region FindPlayer
  2867.  
  2868.         private ulong FindPlayersSingleId(string nameOrIdOrIp, BasePlayer player)
  2869.         {
  2870.             var targets = FindPlayers(nameOrIdOrIp);
  2871.             if (targets.Count > 1)
  2872.             {
  2873.                 PrintMsgL(player, "MultiplePlayers", string.Join(", ", targets.Select(p => p.displayName).ToArray()));
  2874.                 return 0;
  2875.             }
  2876.             ulong userId;
  2877.             if (targets.Count <= 0)
  2878.             {
  2879.                 if (ulong.TryParse(nameOrIdOrIp, out userId)) return userId;
  2880.                 PrintMsgL(player, "PlayerNotFound");
  2881.                 return 0;
  2882.             }
  2883.             else
  2884.                 userId = targets.First().userID;
  2885.             return userId;
  2886.         }
  2887.  
  2888.         private BasePlayer FindPlayersSingle(string nameOrIdOrIp, BasePlayer player)
  2889.         {
  2890.             var targets = FindPlayers(nameOrIdOrIp);
  2891.             if (targets.Count <= 0)
  2892.             {
  2893.                 PrintMsgL(player, "PlayerNotFound");
  2894.                 return null;
  2895.             }
  2896.             if (targets.Count > 1)
  2897.             {
  2898.                 PrintMsgL(player, "MultiplePlayers", string.Join(", ", targets.Select(p => p.displayName).ToArray()));
  2899.                 return null;
  2900.             }
  2901.             return targets.First();
  2902.         }
  2903.  
  2904.         private static HashSet<BasePlayer> FindPlayers(string nameOrIdOrIp)
  2905.         {
  2906.             var players = new HashSet<BasePlayer>();
  2907.             if (string.IsNullOrEmpty(nameOrIdOrIp)) return players;
  2908.             foreach (var activePlayer in BasePlayer.activePlayerList)
  2909.             {
  2910.                 if (activePlayer.UserIDString.Equals(nameOrIdOrIp))
  2911.                     players.Add(activePlayer);
  2912.                 else if (!string.IsNullOrEmpty(activePlayer.displayName) && activePlayer.displayName.ToLower().IndexOf(nameOrIdOrIp.ToLower()) != -1)
  2913.                     players.Add(activePlayer);
  2914.                 else if (activePlayer.net?.connection != null && activePlayer.net.connection.ipaddress.Equals(nameOrIdOrIp))
  2915.                     players.Add(activePlayer);
  2916.             }
  2917.             foreach (var sleepingPlayer in BasePlayer.sleepingPlayerList)
  2918.             {
  2919.                 if (sleepingPlayer.UserIDString.Equals(nameOrIdOrIp))
  2920.                     players.Add(sleepingPlayer);
  2921.                 else if (!string.IsNullOrEmpty(sleepingPlayer.displayName) && sleepingPlayer.displayName.ToLower().IndexOf(nameOrIdOrIp.ToLower()) != -1)
  2922.                     players.Add(sleepingPlayer);
  2923.             }
  2924.             return players;
  2925.         }
  2926.  
  2927.         private static List<BasePlayer> FindPlayersOnline(string nameOrIdOrIp)
  2928.         {
  2929.             var players = new List<BasePlayer>();
  2930.             if (string.IsNullOrEmpty(nameOrIdOrIp)) return players;
  2931.             foreach (var activePlayer in BasePlayer.activePlayerList)
  2932.             {
  2933.                 if (activePlayer.UserIDString.Equals(nameOrIdOrIp))
  2934.                     players.Add(activePlayer);
  2935.                 else if (!string.IsNullOrEmpty(activePlayer.displayName) && activePlayer.displayName.Contains(nameOrIdOrIp, CompareOptions.IgnoreCase))
  2936.                     players.Add(activePlayer);
  2937.                 else if (activePlayer.net?.connection != null && activePlayer.net.connection.ipaddress.Equals(nameOrIdOrIp))
  2938.                     players.Add(activePlayer);
  2939.             }
  2940.             return players;
  2941.         }
  2942.  
  2943.         #endregion
  2944.  
  2945.         #endregion
  2946.  
  2947.         #region API
  2948.  
  2949.         private Dictionary<string, Vector3> GetHomes(object playerObj)
  2950.         {
  2951.             if (playerObj == null) return null;
  2952.             if (playerObj is string)
  2953.                 playerObj = Convert.ToUInt64(playerObj);
  2954.             if (!(playerObj is ulong))
  2955.                 throw new ArgumentException("playerObj");
  2956.             var playerId = (ulong)playerObj;
  2957.             HomeData homeData;
  2958.             if (!Home.TryGetValue(playerId, out homeData) || homeData.Locations.Count == 0)
  2959.                 return null;
  2960.             return homeData.Locations;
  2961.         }
  2962.  
  2963.         private int GetLimitRemaining(BasePlayer player, string type)
  2964.         {
  2965.             if (player == null || string.IsNullOrEmpty(type)) return 0;
  2966.             var currentDate = DateTime.Now.ToString("d");
  2967.             int limit;
  2968.             var remaining = -1;
  2969.             switch (type.ToLower())
  2970.             {
  2971.                 case "home":
  2972.                     limit = GetHigher(player, configData.Home.VIPDailyLimits, configData.Home.DailyLimit);
  2973.                     HomeData homeData;
  2974.                     if (!Home.TryGetValue(player.userID, out homeData))
  2975.                         Home[player.userID] = homeData = new HomeData();
  2976.                     if (homeData.Teleports.Date != currentDate)
  2977.                     {
  2978.                         homeData.Teleports.Amount = 0;
  2979.                         homeData.Teleports.Date = currentDate;
  2980.                     }
  2981.                     if (limit > 0)
  2982.                         remaining = limit - homeData.Teleports.Amount;
  2983.                     break;
  2984.                 case "town":
  2985.                     limit = GetHigher(player, configData.Town.VIPDailyLimits, configData.Town.DailyLimit);
  2986.                     TeleportData townData;
  2987.                     if (!Town.TryGetValue(player.userID, out townData))
  2988.                         Town[player.userID] = townData = new TeleportData();
  2989.                     if (townData.Date != currentDate)
  2990.                     {
  2991.                         townData.Amount = 0;
  2992.                         townData.Date = currentDate;
  2993.                     }
  2994.                     if (limit > 0)
  2995.                         remaining = limit - townData.Amount;
  2996.                     break;
  2997.                 case "tpr":
  2998.                     limit = GetHigher(player, configData.TPR.VIPDailyLimits, configData.TPR.DailyLimit);
  2999.                     TeleportData tprData;
  3000.                     if (!TPR.TryGetValue(player.userID, out tprData))
  3001.                         TPR[player.userID] = tprData = new TeleportData();
  3002.                     if (tprData.Date != currentDate)
  3003.                     {
  3004.                         tprData.Amount = 0;
  3005.                         tprData.Date = currentDate;
  3006.                     }
  3007.                     if (limit > 0)
  3008.                         remaining = limit - tprData.Amount;
  3009.                     break;
  3010.             }
  3011.             return remaining;
  3012.         }
  3013.  
  3014.         private int GetCooldownRemaining(BasePlayer player, string type)
  3015.         {
  3016.             if (player == null || string.IsNullOrEmpty(type)) return 0;
  3017.             var currentDate = DateTime.Now.ToString("d");
  3018.             var timestamp = Facepunch.Math.Epoch.Current;
  3019.             int cooldown;
  3020.             var remaining = -1;
  3021.             switch (type.ToLower())
  3022.             {
  3023.                 case "home":
  3024.                     cooldown = GetLower(player, configData.Home.VIPCooldowns, configData.Home.Cooldown);
  3025.                     HomeData homeData;
  3026.                     if (!Home.TryGetValue(player.userID, out homeData))
  3027.                         Home[player.userID] = homeData = new HomeData();
  3028.                     if (homeData.Teleports.Date != currentDate)
  3029.                     {
  3030.                         homeData.Teleports.Amount = 0;
  3031.                         homeData.Teleports.Date = currentDate;
  3032.                     }
  3033.                     if (cooldown > 0 && timestamp - homeData.Teleports.Timestamp < cooldown)
  3034.                         remaining = cooldown - (timestamp - homeData.Teleports.Timestamp);
  3035.                     break;
  3036.                 case "town":
  3037.                     cooldown = GetLower(player, configData.Town.VIPCooldowns, configData.Town.Cooldown);
  3038.                     TeleportData townData;
  3039.                     if (!Town.TryGetValue(player.userID, out townData))
  3040.                         Town[player.userID] = townData = new TeleportData();
  3041.                     if (townData.Date != currentDate)
  3042.                     {
  3043.                         townData.Amount = 0;
  3044.                         townData.Date = currentDate;
  3045.                     }
  3046.                     if (cooldown > 0 && timestamp - townData.Timestamp < cooldown)
  3047.                         remaining = cooldown - (timestamp - townData.Timestamp);
  3048.                     break;
  3049.                 case "tpr":
  3050.                     cooldown = GetLower(player, configData.TPR.VIPCooldowns, configData.TPR.Cooldown);
  3051.                     TeleportData tprData;
  3052.                     if (!TPR.TryGetValue(player.userID, out tprData))
  3053.                         TPR[player.userID] = tprData = new TeleportData();
  3054.                     if (tprData.Date != currentDate)
  3055.                     {
  3056.                         tprData.Amount = 0;
  3057.                         tprData.Date = currentDate;
  3058.                     }
  3059.                     if (cooldown > 0 && timestamp - tprData.Timestamp < cooldown)
  3060.                         remaining = cooldown - (timestamp - tprData.Timestamp);
  3061.                     break;
  3062.             }
  3063.             return remaining;
  3064.         }
  3065.  
  3066.         #endregion
  3067.  
  3068.         private class UnityVector3Converter : JsonConverter
  3069.         {
  3070.             public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  3071.             {
  3072.                 var vector = (Vector3)value;
  3073.                 writer.WriteValue($"{vector.x} {vector.y} {vector.z}");
  3074.             }
  3075.  
  3076.             public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  3077.             {
  3078.                 if (reader.TokenType == JsonToken.String)
  3079.                 {
  3080.                     var values = reader.Value.ToString().Trim().Split(' ');
  3081.                     return new Vector3(Convert.ToSingle(values[0]), Convert.ToSingle(values[1]), Convert.ToSingle(values[2]));
  3082.                 }
  3083.                 var o = JObject.Load(reader);
  3084.                 return new Vector3(Convert.ToSingle(o["x"]), Convert.ToSingle(o["y"]), Convert.ToSingle(o["z"]));
  3085.             }
  3086.  
  3087.             public override bool CanConvert(Type objectType)
  3088.             {
  3089.                 return objectType == typeof(Vector3);
  3090.             }
  3091.         }
  3092.  
  3093.         private class CustomComparerDictionaryCreationConverter<T> : CustomCreationConverter<IDictionary>
  3094.         {
  3095.             private readonly IEqualityComparer<T> comparer;
  3096.  
  3097.             public CustomComparerDictionaryCreationConverter(IEqualityComparer<T> comparer)
  3098.             {
  3099.                 if (comparer == null)
  3100.                     throw new ArgumentNullException(nameof(comparer));
  3101.                 this.comparer = comparer;
  3102.             }
  3103.  
  3104.             public override bool CanConvert(Type objectType)
  3105.             {
  3106.                 return HasCompatibleInterface(objectType) && HasCompatibleConstructor(objectType);
  3107.             }
  3108.  
  3109.             private static bool HasCompatibleInterface(Type objectType)
  3110.             {
  3111.                 return objectType.GetInterfaces().Where(i => HasGenericTypeDefinition(i, typeof(IDictionary<,>))).Any(i => typeof(T).IsAssignableFrom(i.GetGenericArguments().First()));
  3112.             }
  3113.  
  3114.             private static bool HasGenericTypeDefinition(Type objectType, Type typeDefinition)
  3115.             {
  3116.                 return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeDefinition;
  3117.             }
  3118.  
  3119.             private static bool HasCompatibleConstructor(Type objectType)
  3120.             {
  3121.                 return objectType.GetConstructor(new[] { typeof(IEqualityComparer<T>) }) != null;
  3122.             }
  3123.  
  3124.             public override IDictionary Create(Type objectType)
  3125.             {
  3126.                 return Activator.CreateInstance(objectType, comparer) as IDictionary;
  3127.             }
  3128.         }
  3129.     }
  3130. }
Advertisement
Add Comment
Please, Sign In to add comment