From c440096ef7e5c55ee960b4162063708f8c9e909e Mon Sep 17 00:00:00 2001 From: Shaun Prince Date: Fri, 8 Oct 2021 16:00:31 -0700 Subject: [PATCH] v1.7.0 --- defaults/oxide/config/HumanNPC.json | 14 +- defaults/oxide/config/MonumentsRecycler.json | 2 +- defaults/oxide/config/PathFinding.json | 3 + defaults/oxide/plugins/BradleyGuards.cs | 399 +++-- defaults/oxide/plugins/CopyPaste.cs | 1651 ++++++++++-------- defaults/oxide/plugins/EntityCleanup.cs | 705 ++++---- defaults/oxide/plugins/MonumentsRecycler.cs | 710 ++++---- defaults/oxide/plugins/PathFinding.cs | 574 ++++++ 8 files changed, 2389 insertions(+), 1669 deletions(-) create mode 100644 defaults/oxide/config/PathFinding.json create mode 100644 defaults/oxide/plugins/PathFinding.cs diff --git a/defaults/oxide/config/HumanNPC.json b/defaults/oxide/config/HumanNPC.json index 26048bfb..d487d071 100644 --- a/defaults/oxide/config/HumanNPC.json +++ b/defaults/oxide/config/HumanNPC.json @@ -1,4 +1,12 @@ -{ - "Chat": "{0}: ", - "Place Npc in teams": true +{ + "Settings": { + "Chat": "{0}: ", + "NpcInTeams": false, + "NpcUseRadius": 5.0 + }, + "Version": { + "Major": 0, + "Minor": 3, + "Patch": 52 + } } \ No newline at end of file diff --git a/defaults/oxide/config/MonumentsRecycler.json b/defaults/oxide/config/MonumentsRecycler.json index a0af486e..80edb2d5 100644 --- a/defaults/oxide/config/MonumentsRecycler.json +++ b/defaults/oxide/config/MonumentsRecycler.json @@ -6,7 +6,7 @@ "Large Oil Rig - Recycler Position - Level 6": true, "Small Oil Rig - Recycler Position - Level 3": true, "Small Oil Rig - Recycler Position - Level 4": true, - "Dome - Recycler": false, + "Dome - Recycler": true, "Fishing Village - Recycler - Large": true, "Fishing Village - Recycler - Small A": true, "Fishing Village - Recycler - Small B": true diff --git a/defaults/oxide/config/PathFinding.json b/defaults/oxide/config/PathFinding.json new file mode 100644 index 00000000..c5a759a5 --- /dev/null +++ b/defaults/oxide/config/PathFinding.json @@ -0,0 +1,3 @@ +{ + "Max Depth": 5000 +} \ No newline at end of file diff --git a/defaults/oxide/plugins/BradleyGuards.cs b/defaults/oxide/plugins/BradleyGuards.cs index 5c3f25cf..479de80a 100644 --- a/defaults/oxide/plugins/BradleyGuards.cs +++ b/defaults/oxide/plugins/BradleyGuards.cs @@ -1,7 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System; -using Rust; using Oxide.Core.Plugins; using Oxide.Core; using UnityEngine; @@ -11,16 +10,16 @@ namespace Oxide.Plugins { - [Info("Bradley Guards", "Bazz3l", "1.3.6")] - [Description("Calls reinforcements when bradley is destroyed at launch site.")] + [Info("Bradley Guards", "Bazz3l", "1.3.7")] + [Description("Call in armed reinforcements when bradley is destroyed at launch site.")] public class BradleyGuards : RustPlugin { [PluginReference] Plugin Kits; - + #region Fields - private const string Ch47Prefab = "assets/prefabs/npc/ch47/ch47scientists.entity.prefab"; - private const string LandingName = "BradleyLandingZone"; + private const string CH47_PREFAB = "assets/prefabs/npc/ch47/ch47scientists.entity.prefab"; + private const string LANDING_NAME = "BradleyLandingZone"; private readonly HashSet _npcs = new HashSet(); private CH47HelicopterAIController _chinook; @@ -37,7 +36,7 @@ public class BradleyGuards : RustPlugin #region Config - protected override void LoadDefaultConfig() => _config = GetDefaultConfig(); + protected override void LoadDefaultConfig() => _config = PluginConfig.DefaultConfig(); protected override void LoadConfig() { @@ -51,48 +50,21 @@ protected override void LoadConfig() { throw new JsonException(); } + + if (_config.ToDictionary().Keys + .SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) return; } catch { - LoadDefaultConfig(); + PrintWarning("Loaded default config, please check your configuration file for errors."); - SaveConfig(); - - PrintWarning("Loaded default configuration file."); + LoadDefaultConfig(); } } protected override void SaveConfig() => Config.WriteObject(_config, true); - private PluginConfig GetDefaultConfig() - { - return new PluginConfig - { - ChatIcon = 0, - APCHealth = 1000f, - APCCrates = 4, - NPCAmount = 6, - InstantCrates = true, - GuardSettings = new List { - new GuardSetting - { - Name = "Heavy Gunner", - Health = 300f, - MaxRoamRadius = 80f, - MaxAggressionRange = 200f, - }, - new GuardSetting - { - Name = "Light Gunner", - Health = 200f, - MaxRoamRadius = 80f, - MaxAggressionRange = 150f, - } - } - }; - } - - private class PluginConfig + class PluginConfig { [JsonProperty(PropertyName = "ChatIcon (chat icon SteamID64)")] public ulong ChatIcon; @@ -108,12 +80,49 @@ private class PluginConfig [JsonProperty(PropertyName = "InstantCrates (unlock crates when guards are eliminated)")] public bool InstantCrates; + + [JsonProperty(PropertyName = "DisableChinookDamage (should chinook be able to take damage)")] + public bool DisableChinookDamage; [JsonProperty(PropertyName = "GuardSettings (create different types of guards must contain atleast 1)")] public List GuardSettings; + + public static PluginConfig DefaultConfig() + { + return new PluginConfig + { + ChatIcon = 0, + APCHealth = 1000f, + APCCrates = 4, + NPCAmount = 6, + InstantCrates = true, + GuardSettings = new List { + new GuardSetting + { + Name = "Heavy Gunner", + Health = 300f, + MaxRoamRadius = 80f, + MaxAggressionRange = 200f, + }, + new GuardSetting + { + Name = "Light Gunner", + Health = 200f, + MaxRoamRadius = 80f, + MaxAggressionRange = 150f, + } + } + }; + } + + public string ToJson() => + JsonConvert.SerializeObject(this); + + public Dictionary ToDictionary() => + JsonConvert.DeserializeObject>(ToJson()); } - private class GuardSetting + class GuardSetting { [JsonProperty(PropertyName = "Name (custom display name)")] public string Name; @@ -144,90 +153,88 @@ private class GuardSetting protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { - {"EventStart", "Bradley Gaurds: Tank commander sent for reinforcements, fight for your life."}, - {"EventEnded", "Bradley Gaurds: Reinforcements have been eliminated, loot up fast."}, + {"EventStart", "Bradley Guards: Tank commander sent for reinforcements, fight for your life."}, + {"EventEnded", "Bradley Guards: Reinforcements have been eliminated, loot up fast."}, }, this); } - private void OnServerInitialized() => GetLandingPoint(); + void OnServerInitialized() => + GetLandingPoint(); - private void Unload() => CleanUp(); + void Unload() => + CleanUp(); - private void OnEntitySpawned(BradleyAPC bradley) => OnAPCSpawned(bradley); + void OnEntitySpawned(BradleyAPC bradley) => + OnAPCSpawned(bradley); - private void OnEntityDeath(BradleyAPC bradley, HitInfo info) => OnAPCDeath(bradley); + void OnEntityDeath(BradleyAPC bradley, HitInfo info) => + OnAPCDeath(bradley); - private void OnEntityDeath(NPCPlayerApex npc, HitInfo info) => OnNPCDeath(npc); + void OnEntityDeath(NPCPlayerApex npc, HitInfo info) => + OnNPCDeath(npc); - private void OnEntityKill(NPCPlayerApex npc) => OnNPCDeath(npc); + void OnEntityKill(NPCPlayerApex npc) => + OnNPCDeath(npc); + + object OnHelicopterAttacked(CH47HelicopterAIController heli, HitInfo info) => + OnCH47Attacked(heli, info); - private void OnFireBallDamage(FireBall fire, NPCPlayerApex npc, HitInfo info) + void OnFireBallDamage(FireBall fireball, NPCPlayerApex npc, HitInfo info) { - if (!(_npcs.Contains(npc) && info.Initiator is FireBall)) - { - return; - } - - info.damageTypes = new DamageTypeList(); + if (!(_npcs.Contains(npc) && info.Initiator is FireBall)) return; + info.DoHitEffects = false; info.damageTypes.ScaleAll(0f); } - private void OnEntityDismounted(BaseMountable mountable, NPCPlayerApex npc) + void OnEntityDismounted(BaseMountable mountable, NPCPlayerApex npc) { - if (!_npcs.Contains(npc)) - { - return; - } - - npc.SetFact(NPCPlayerApex.Facts.IsMounted, (byte) 0, true, true); - npc.SetFact(NPCPlayerApex.Facts.WantsToDismount, (byte) 0, true, true); - npc.SetFact(NPCPlayerApex.Facts.CanNotWieldWeapon, (byte) 0, true, true); + if (!_npcs.Contains(npc)) return; + npc.Resume(); + npc.modelState.mounted = false; + npc.modelState.onground = true; + npc.SetFact(NPCPlayerApex.Facts.IsMounted, 0, true, true); + npc.SetFact(NPCPlayerApex.Facts.CanNotWieldWeapon, 0, true, true); + npc.InvokeRandomized(new Action(npc.RadioChatter), npc.RadioEffectRepeatRange.x, npc.RadioEffectRepeatRange.x, npc.RadioEffectRepeatRange.y - npc.RadioEffectRepeatRange.x); } #endregion #region Core - private void SpawnEvent() + void SpawnEvent() { - _chinook = GameManager.server.CreateEntity(Ch47Prefab, _chinookPosition, Quaternion.identity) as CH47HelicopterAIController; + _chinook = GameManager.server.CreateEntity(CH47_PREFAB, _chinookPosition, Quaternion.identity) as CH47HelicopterAIController; _chinook.Spawn(); _chinook.SetLandingTarget(_landingPosition); _chinook.SetMinHoverHeight(1.5f); _chinook.CancelInvoke(new Action(_chinook.SpawnScientists)); - _chinook.GetOrAddComponent(); + _chinook.GetOrAddComponent(); for (int i = 0; i < _config.NPCAmount - 1; i++) { - SpawnScientist(_chinook, _config.GuardSettings.GetRandom(), _chinook.transform.position + _chinook.transform.forward * 10f, _bradleyPosition); + SpawnScientist(_config.GuardSettings.GetRandom(), _chinook.transform.position + _chinook.transform.forward * 10f, _bradleyPosition); } for (int j = 0; j < 1; j++) { - SpawnScientist(_chinook, _config.GuardSettings.GetRandom(), _chinook.transform.position - _chinook.transform.forward * 15f, _bradleyPosition); + SpawnScientist(_config.GuardSettings.GetRandom(), _chinook.transform.position - _chinook.transform.forward * 15f, _bradleyPosition); } MessageAll("EventStart"); } - private void SpawnScientist(CH47HelicopterAIController chinook, GuardSetting settings, Vector3 position, Vector3 eventPos) + void SpawnScientist(GuardSetting settings, Vector3 position, Vector3 eventPos) { - NPCPlayerApex npc = GameManager.server.CreateEntity(chinook.scientistPrefab.resourcePath, position, Quaternion.identity) as NPCPlayerApex; - - if (npc == null) - { - return; - } + NPCPlayerApex npc = GameManager.server.CreateEntity(_chinook.scientistPrefab.resourcePath, position, Quaternion.identity) as NPCPlayerApex; + if (npc == null) return; npc.Spawn(); - npc.Mount((BaseMountable) chinook); - + npc.Mount(_chinook); npc.RadioEffect = new GameObjectRef(); npc.DeathEffect = new GameObjectRef(); npc.displayName = settings.Name; - npc.startHealth = settings.Health; npc.damageScale = settings.DamageScale; npc.Stats.VisionRange = settings.MaxAggressionRange + 3f; npc.Stats.DeaggroRange = settings.MaxAggressionRange + 2f; @@ -237,45 +244,41 @@ private void SpawnScientist(CH47HelicopterAIController chinook, GuardSetting set npc.Stats.Hostility = 1f; npc.Stats.Defensiveness = 1f; npc.Stats.OnlyAggroMarkedTargets = true; + npc.startHealth = settings.Health; npc.InitializeHealth(settings.Health, settings.Health); npc.InitFacts(); - (npc as Scientist).LootPanelName = settings.Name; + RenameNPC(npc as Scientist, settings.Name); + + npc.GetOrAddComponent() + ?.SetDestination(GetRandomPoint(eventPos, 6f)); _npcs.Add(npc); - npc.Invoke(() => { - GiveKit(npc, settings.KitEnabled, settings.KitName); + npc.Invoke(() => GiveKit(npc, settings.KitEnabled, settings.KitName), 2f); + } - npc.gameObject.AddComponent().SetDestination(GetRandomPoint(eventPos, 6f)); - }, 2f); + void RenameNPC(Scientist npc, string name) + { + npc.displayName = name; + npc.LootPanelName = name; } - private void GiveKit(NPCPlayerApex npc, bool kitEnabled, string kitName) + void GiveKit(NPCPlayerApex npc, bool kitEnabled, string kitName) { if (kitEnabled && !string.IsNullOrEmpty(kitName)) { npc.inventory.Strip(); - Interface.Oxide.CallHook("GiveKit", npc, kitName); - return; } ItemManager.CreateByName("scientistsuit_heavy", 1, 0)?.MoveToContainer(npc.inventory.containerWear); } - private void OnNPCDeath(NPCPlayerApex npc) + void OnNPCDeath(NPCPlayerApex npc) { - if (!_npcs.Remove(npc)) - { - return; - } - - if (_npcs.Count > 0) - { - return; - } + if (!_npcs.Remove(npc) || _npcs.Count > 0) return; if (_config.InstantCrates) { @@ -286,15 +289,12 @@ private void OnNPCDeath(NPCPlayerApex npc) MessageAll("EventEnded"); } - private void OnAPCSpawned(BradleyAPC bradley) + void OnAPCSpawned(BradleyAPC bradley) { Vector3 position = bradley.transform.position; - if (!IsInBounds(position)) - { - return; - } - + if (!IsInBounds(position)) return; + bradley.maxCratesToSpawn = _config.APCCrates; bradley._maxHealth = bradley._health = _config.APCHealth; bradley.health = bradley._maxHealth; @@ -302,26 +302,27 @@ private void OnAPCSpawned(BradleyAPC bradley) ClearGuards(); } - private void OnAPCDeath(BradleyAPC bradley) + void OnAPCDeath(BradleyAPC bradley) { - if (bradley == null || bradley.IsDestroyed) - { - return; - } + if (bradley == null || bradley.IsDestroyed) return; Vector3 position = bradley.transform.position; - if (!IsInBounds(position)) - { - return; - } + if (!IsInBounds(position)) return; _bradleyPosition = position; SpawnEvent(); } - private void RemoveFlames() + object OnCH47Attacked(CH47HelicopterAIController heli, HitInfo info) + { + if (heli == null || !_config.DisableChinookDamage) return null; + if (heli == _chinook) return true; + return null; + } + + void RemoveFlames() { List entities = Pool.GetList(); @@ -329,15 +330,16 @@ private void RemoveFlames() foreach (FireBall fireball in entities) { - if (!(fireball.IsValid() && !fireball.IsDestroyed)) continue; - - NextFrame(() => fireball.Kill()); + if (fireball.IsValid() && !fireball.IsDestroyed) + { + fireball.Kill(); + } } Pool.FreeList(ref entities); } - - private void UnlockCrates() + + void UnlockCrates() { List entities = Pool.GetList(); @@ -362,34 +364,29 @@ private void UnlockCrates() Pool.FreeList(ref entities); } - private void CreateLandingZone() + void CreateLandingZone() { - _landingZone = new GameObject(LandingName) { - layer = 16, - transform = { - position = _landingPosition, - rotation = _landingRotation - } - }.AddComponent(); + GameObject gameObject = new GameObject(LANDING_NAME); + gameObject.transform.SetPositionAndRotation(_landingPosition, _landingRotation); + _landingZone = gameObject.AddComponent(); } - private void CleanUp() + void CleanUp() { ClearGuards(); ClearZones(); } - - private void ClearZones() + + void ClearZones() { - if (_landingZone != null) - { - UnityEngine.Object.Destroy(_landingZone.gameObject); - } - + if (_landingZone == null) return; + + UnityEngine.Object.Destroy(_landingZone.gameObject); + _landingZone = null; } - private void ClearGuards() + void ClearGuards() { for (int i = 0; i < _npcs.Count; i++) { @@ -404,7 +401,7 @@ private void ClearGuards() _npcs.Clear(); } - private void GetLandingPoint() + void GetLandingPoint() { foreach (MonumentInfo monument in UnityEngine.Object.FindObjectsOfType()) { @@ -413,135 +410,131 @@ private void GetLandingPoint() SetLandingPoint(monument); } } - - private void SetLandingPoint(MonumentInfo monument) + + void SetLandingPoint(MonumentInfo monument) { _monumentPosition = monument.transform.position; - + _landingRotation = monument.transform.rotation; - _landingPosition = monument.transform.position + monument.transform.right * 125f; + _landingPosition = _monumentPosition + monument.transform.right * 125f; _landingPosition.y = TerrainMeta.HeightMap.GetHeight(_landingPosition); - _chinookPosition = monument.transform.position + -monument.transform.right * 250f; + _chinookPosition = _monumentPosition + -monument.transform.right * 250f; _chinookPosition.y += 150f; _hasLaunch = true; CreateLandingZone(); } - - private bool IsInBounds(Vector3 position) - { - return _hasLaunch && Vector3.Distance(_monumentPosition, position) <= 300f; - } + bool IsInBounds(Vector3 position) => _hasLaunch && Vector3.Distance(_monumentPosition, position) <= 300f; + #endregion #region Component - - private class CustomNavigation : MonoBehaviour + + class NPCNavigationComponent : MonoBehaviour { - private NPCPlayerApex _npc; - private Vector3 _targetPoint; + NPCPlayerApex _npc; + Vector3 _destination; private void Awake() { _npc = gameObject.GetComponent(); - InvokeRepeating(nameof(Relocate), 0f, 5f); + InvokeRepeating(nameof(DoMove), 5f, 5f); } private void OnDestroy() { CancelInvoke(); - + if (_npc.IsValid() && !_npc.IsDestroyed) { _npc.Kill(); } } - public void SetDestination(Vector3 position) + public void SetDestination(Vector3 destination) { - _targetPoint = position; + _destination = destination; } - private void Relocate() + private void DoMove() { - if (_npc == null || _npc.IsDestroyed) - { - return; - } + if (!(_npc.IsValid() && !_npc.IsDestroyed)) return; + + if (_npc.isMounted) return; - if (_npc.isMounted) + if (IsOutOfRange() && _npc.AttackTarget != null) { - return; + _npc.AttackTarget = null; } - if (!(_npc.AttackTarget == null || IsOutOfBounds())) - { - return; - } - - if (_npc.IsStuck) + if (_npc.AttackTarget != null) { - DoWarp(); + _npc.NeverMove = false; } - if (_npc.GetNavAgent == null || !_npc.GetNavAgent.isOnNavMesh) - { - _npc.finalDestination = _targetPoint; - } - else + if (_npc.AttackTarget == null) { - _npc.GetNavAgent.SetDestination(_targetPoint); - _npc.IsDormant = false; - } + _npc.NeverMove = true; - _npc.IsStopped = false; - _npc.Destination = _targetPoint; - } + if (_npc.IsStuck) + { + Warp(); + } - private bool IsOutOfBounds() - { - return _npc.AttackTarget != null && Vector3.Distance(transform.position, _targetPoint) > _npc.Stats.MaxRoamRange; + if (_npc.GetNavAgent == null || !_npc.GetNavAgent.isOnNavMesh) + { + _npc.finalDestination = _destination; + } + else + { + _npc.GetNavAgent.SetDestination(_destination); + } + + _npc.IsStopped = false; + _npc.Destination = _destination; + } } - private void DoWarp() + private void Warp() { _npc.Pause(); - _npc.ServerPosition = _targetPoint; - _npc.GetNavAgent.Warp(_targetPoint); + _npc.ServerPosition = _destination; + _npc.GetNavAgent.Warp(_destination); _npc.stuckDuration = 0f; _npc.IsStuck = false; _npc.Resume(); } + + private bool IsOutOfRange() => + Vector3.Distance(transform.position, _destination) > _npc.Stats.MaxRoamRange; } - private class CustomCh47 : MonoBehaviour + class CH47NavigationComponent : MonoBehaviour { - private CH47HelicopterAIController _chinook; + CH47HelicopterAIController _chinook; - private void Awake() + void Awake() { _chinook = GetComponent(); InvokeRepeating(nameof(CheckDropped), 5f, 5f); } - private void OnDestroy() + void OnDestroy() { CancelInvoke(); - - _chinook.Invoke(new Action(_chinook.DelayedKill), 10f); + + if (_chinook.IsValid() && !_chinook.IsDestroyed) + _chinook.Invoke(_chinook.DelayedKill, 10f); } - private void CheckDropped() + void CheckDropped() { - if (_chinook == null || _chinook.IsDestroyed || _chinook.HasAnyPassengers()) - { - return; - } + if (_chinook.HasAnyPassengers()) return; Destroy(this); } @@ -551,16 +544,16 @@ private void CheckDropped() #region Helpers - private string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args); + string Lang(string key, string id = null, params object[] args) => + string.Format(lang.GetMessage(key, this, id), args); - private void MessageAll(string key) => Server.Broadcast(Lang(key, null), _config.ChatIcon); + void MessageAll(string key) => + Server.Broadcast(Lang(key, null), _config.ChatIcon); - private Vector3 GetRandomPoint(Vector3 position, float radius) + Vector3 GetRandomPoint(Vector3 position, float radius) { Vector3 pos = position + UnityEngine.Random.onUnitSphere * radius; - pos.y = TerrainMeta.HeightMap.GetHeight(pos); - return pos; } diff --git a/defaults/oxide/plugins/CopyPaste.cs b/defaults/oxide/plugins/CopyPaste.cs index 8a0248b6..e722d081 100644 --- a/defaults/oxide/plugins/CopyPaste.cs +++ b/defaults/oxide/plugins/CopyPaste.cs @@ -12,10 +12,14 @@ using Facepunch; using Newtonsoft.Json; using Oxide.Core; +using Oxide.Core.Libraries.Covalence; +using Oxide.Game.Rust.Libraries.Covalence; using ProtoBuf; using UnityEngine; using Graphics = System.Drawing.Graphics; +// ReSharper disable SpecifyACultureInStringConversionExplicitly + /* * CREDITS * @@ -24,15 +28,20 @@ * bsdinis - Wire fix * nivex - Ownership option, sign fix * DezLife - CCTV fix + * Wulf - Skipping 4.1.24 :D * */ namespace Oxide.Plugins { - [Info("Copy Paste", "misticos", "4.1.27")] // Wulf skipped 24 :( + [Info("Copy Paste", "misticos", "4.1.31")] [Description("Copy and paste buildings to save them or move them")] - public class CopyPaste : RustPlugin + public class CopyPaste : CovalencePlugin { + // ReSharper disable once Unity.IncorrectMonoBehaviourInstantiation + private readonly Item _emptyItem = new Item { info = new ItemDefinition() }; + private readonly IPlayer _consolePlayer = new RustConsolePlayer(); + private int _copyLayer = LayerMask.GetMask("Construction", "Prevent Building", "Construction Trigger", "Trigger", "Deployed", "Default", "Ragdoll"), @@ -46,7 +55,6 @@ public class CopyPaste : RustPlugin _pastePermission = "copypaste.paste", _pastebackPermission = "copypaste.pasteback", _undoPermission = "copypaste.undo", - _serverId = "Server", _subDirectory = "copypaste/"; private Dictionary>> _lastPastes = @@ -55,28 +63,28 @@ public class CopyPaste : RustPlugin private Dictionary _signSizes = new Dictionary { //{"spinner.wheel.deployed", new SignSize(512, 512)}, - {"sign.pictureframe.landscape", new SignSize(256, 128)}, - {"sign.pictureframe.tall", new SignSize(128, 512)}, - {"sign.pictureframe.portrait", new SignSize(128, 256)}, - {"sign.pictureframe.xxl", new SignSize(1024, 512)}, - {"sign.pictureframe.xl", new SignSize(512, 512)}, - {"sign.small.wood", new SignSize(128, 64)}, - {"sign.medium.wood", new SignSize(256, 128)}, - {"sign.large.wood", new SignSize(256, 128)}, - {"sign.huge.wood", new SignSize(512, 128)}, - {"sign.hanging.banner.large", new SignSize(64, 256)}, - {"sign.pole.banner.large", new SignSize(64, 256)}, - {"sign.post.single", new SignSize(128, 64)}, - {"sign.post.double", new SignSize(256, 256)}, - {"sign.post.town", new SignSize(256, 128)}, - {"sign.post.town.roof", new SignSize(256, 128)}, - {"sign.hanging", new SignSize(128, 256)}, - {"sign.hanging.ornate", new SignSize(256, 128)}, - {"sign.neon.xl.animated", new SignSize(250, 250)}, - {"sign.neon.xl", new SignSize(250, 250)}, - {"sign.neon.125x215.animated", new SignSize(215, 125)}, - {"sign.neon.125x215", new SignSize(215, 125)}, - {"sign.neon.125x125", new SignSize(125, 125)}, + { "sign.pictureframe.landscape", new SignSize(256, 128) }, + { "sign.pictureframe.tall", new SignSize(128, 512) }, + { "sign.pictureframe.portrait", new SignSize(128, 256) }, + { "sign.pictureframe.xxl", new SignSize(1024, 512) }, + { "sign.pictureframe.xl", new SignSize(512, 512) }, + { "sign.small.wood", new SignSize(128, 64) }, + { "sign.medium.wood", new SignSize(256, 128) }, + { "sign.large.wood", new SignSize(256, 128) }, + { "sign.huge.wood", new SignSize(512, 128) }, + { "sign.hanging.banner.large", new SignSize(64, 256) }, + { "sign.pole.banner.large", new SignSize(64, 256) }, + { "sign.post.single", new SignSize(128, 64) }, + { "sign.post.double", new SignSize(256, 256) }, + { "sign.post.town", new SignSize(256, 128) }, + { "sign.post.town.roof", new SignSize(256, 128) }, + { "sign.hanging", new SignSize(128, 256) }, + { "sign.hanging.ornate", new SignSize(256, 128) }, + { "sign.neon.xl.animated", new SignSize(250, 250) }, + { "sign.neon.xl", new SignSize(250, 250) }, + { "sign.neon.125x215.animated", new SignSize(215, 125) }, + { "sign.neon.125x215", new SignSize(215, 125) }, + { "sign.neon.125x125", new SignSize(125, 125) }, }; private List _checkSlots = new List @@ -249,10 +257,9 @@ private void OnServerInitialized() private object TryCopyFromSteamId(ulong userId, string filename, string[] args, Action callback = null) { - var player = BasePlayer.FindByID(userId); - + var player = players.FindPlayerById(userId.ToString())?.Object as BasePlayer; if (player == null) - return Lang("NOT_FOUND_PLAYER", userId.ToString()); + return Lang("NOT_FOUND_PLAYER"); RaycastHit hit; @@ -260,29 +267,29 @@ private object TryCopyFromSteamId(ulong userId, string filename, string[] args, return Lang("NO_ENTITY_RAY", player.UserIDString); return TryCopy(hit.point, hit.GetEntity().GetNetworkRotation().eulerAngles, filename, - DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), args, player, callback); + DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), args, player.IPlayer, callback); } private object TryPasteFromSteamId(ulong userId, string filename, string[] args, Action callback = null) { - var player = BasePlayer.FindByID(userId); - + var player = players.FindPlayerById(userId.ToString())?.Object as BasePlayer; if (player == null) - return Lang("NOT_FOUND_PLAYER", player.UserIDString); + return Lang("NOT_FOUND_PLAYER"); RaycastHit hit; if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f, _rayPaste)) return Lang("NO_ENTITY_RAY", player.UserIDString); - return TryPaste(hit.point, filename, player, DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), + return TryPaste(hit.point, filename, player.IPlayer, + DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), args, callback: callback); } private object TryPasteFromVector3(Vector3 pos, float rotationCorrection, string filename, string[] args, Action callback = null) { - return TryPaste(pos, filename, null, rotationCorrection, args, callback: callback); + return TryPaste(pos, filename, _consolePlayer, rotationCorrection, args, callback: callback); } #endregion @@ -293,7 +300,7 @@ private object CheckCollision(HashSet> entities, Vect { foreach (var entityobj in entities) { - if (Physics.CheckSphere((Vector3) entityobj["position"], radius, _copyLayer)) + if (Physics.CheckSphere((Vector3)entityobj["position"], radius, _copyLayer)) return Lang("BLOCKING_PASTE"); } @@ -304,59 +311,38 @@ private bool CheckPlaced(string prefabname, Vector3 pos, Quaternion rot) { const float maxDiff = 0.01f; - var ents = new List(); - Vis.Entities(pos, maxDiff, ents); - - foreach (var ent in ents) + var ents = Pool.GetList(); + try { - if (ent.PrefabName != prefabname) - continue; - - if (Vector3.Distance(ent.transform.position, pos) > maxDiff) - { - continue; - } + Vis.Entities(pos, maxDiff, ents); - if (Vector3.Distance(ent.transform.rotation.eulerAngles, rot.eulerAngles) > maxDiff) + foreach (var ent in ents) { - continue; - } - - return true; - } - - return false; - } - - private object CmdPasteBack(BasePlayer player, string[] args) - { - var userIdString = player == null ? _serverId : player.UserIDString; - - if (args.Length < 1) - return Lang("SYNTAX_PASTEBACK", userIdString); - - var success = TryPasteBack(args[0], player, args.Skip(1).ToArray()); - - if (success is string) - return (string) success; - - return true; - } + if (ent.PrefabName != prefabname) + continue; - private object CmdUndo(string userIdString, string[] args) - { - var player = BasePlayer.Find(userIdString); - if (!_lastPastes.ContainsKey(userIdString)) - return Lang("NO_PASTED_STRUCTURE", userIdString); + if (Vector3.Distance(ent.transform.position, pos) > maxDiff) + { + continue; + } - var entities = new HashSet(_lastPastes[userIdString].Pop().ToList()); + if (Vector3.Distance(ent.transform.rotation.eulerAngles, rot.eulerAngles) > maxDiff) + { + continue; + } - UndoLoop(entities, player); + return true; + } - return true; + return false; + } + finally + { + Pool.FreeList(ref ents); + } } - private void UndoLoop(HashSet entities, BasePlayer player, int count = 0) + private void UndoLoop(HashSet entities, IPlayer player, int count = 0) { foreach (var storageContainer in entities.OfType().Where(x => !x.IsDestroyed)) { @@ -378,6 +364,12 @@ private void UndoLoop(HashSet entities, BasePlayer player, int count ore.CleanupBonus(); } + var io = p as IOEntity; + if (io != null) + { + io.ClearConnections(); + } + if (p != null && !p.IsDestroyed) p.Kill(); }); @@ -385,10 +377,7 @@ private void UndoLoop(HashSet entities, BasePlayer player, int count // If it gets stuck in infinite loop break the loop. if (count != 0 && entities.Count != 0 && entities.Count == count) { - if (player != null) - SendReply(player, "Undo cancelled because of infinite loop."); - else - Puts("Undo cancelled because of infinite loop."); + player.Reply("Undo cancelled because of infinite loop."); return; } @@ -396,18 +385,15 @@ private void UndoLoop(HashSet entities, BasePlayer player, int count NextTick(() => UndoLoop(entities, player, entities.Count)); else { - if (player != null) - SendReply(player, Lang("UNDO_SUCCESS", player.UserIDString)); - else - Puts(Lang("UNDO_SUCCESS")); + player.Reply(Lang("UNDO_SUCCESS", player.Id)); - if (_lastPastes[player?.UserIDString ?? _serverId].Count == 0) - _lastPastes.Remove(player?.UserIDString ?? _serverId); + if (_lastPastes.ContainsKey(player.Id) && _lastPastes[player.Id].Count == 0) + _lastPastes.Remove(player.Id); } } private void Copy(Vector3 sourcePos, Vector3 sourceRot, string filename, float rotationCorrection, - CopyMechanics copyMechanics, float range, bool saveTree, bool saveShare, bool eachToEach, BasePlayer player, + CopyMechanics copyMechanics, float range, bool saveTree, bool saveShare, bool eachToEach, IPlayer player, Action callback) { var currentLayer = _copyLayer; @@ -428,13 +414,13 @@ private void Copy(Vector3 sourcePos, Vector3 sourceRot, string filename, float r SourcePos = sourcePos, SourceRot = sourceRot, Player = player, + BasePlayer = player.Object as BasePlayer, Callback = callback }; copyData.CheckFrom.Push(sourcePos); NextTick(() => CopyLoop(copyData)); - ; } // Main loop for copy, will fetch all the data needed. Is called every tick untill copy is done (can't find any entities) @@ -452,33 +438,43 @@ private void CopyLoop(CopyData copyData) break; var list = Pool.GetList(); - Vis.Entities(checkFrom.Pop(), copyData.Range, list, copyData.CurrentLayer); - - foreach (var entity in list) + try { - if (!houseList.Add(entity)) - continue; + Vis.Entities(checkFrom.Pop(), copyData.Range, list, copyData.CurrentLayer); - if (copyMechanics == CopyMechanics.Building) + foreach (var entity in list) { - var buildingBlock = entity.GetComponentInParent(); + if (!houseList.Add(entity)) + continue; - if (buildingBlock != null) + if (copyMechanics == CopyMechanics.Building) { - if (buildingId == 0) - buildingId = buildingBlock.buildingID; + var buildingBlock = entity.GetComponentInParent(); - if (buildingId != buildingBlock.buildingID) - continue; + if (buildingBlock != null) + { + if (buildingId == 0) + buildingId = buildingBlock.buildingID; + + if (buildingId != buildingBlock.buildingID) + continue; + } } - } - if (copyData.EachToEach) - checkFrom.Push(entity.transform.position); - if (entity.GetComponent() != null) - continue; - copyData.RawData.Add(EntityData(entity, entity.transform.position, - entity.transform.rotation.eulerAngles / 57.29578f, copyData)); + var transform = entity.transform; + if (copyData.EachToEach) + checkFrom.Push(transform.position); + + if (entity.GetComponent() != null) + continue; + + copyData.RawData.Add(EntityData(entity, transform.position, + transform.rotation.eulerAngles / 57.29578f, copyData)); + } + } + finally + { + Pool.FreeList(ref list); } copyData.BuildingId = buildingId; @@ -502,25 +498,25 @@ private void CopyLoop(CopyData copyData) { "position", new Dictionary { - {"x", sourcePos.x.ToString()}, - {"y", sourcePos.y.ToString()}, - {"z", sourcePos.z.ToString()} + { "x", sourcePos.x.ToString() }, + { "y", sourcePos.y.ToString() }, + { "z", sourcePos.z.ToString() } } }, - {"rotationy", copyData.SourceRot.y.ToString()}, - {"rotationdiff", copyData.RotCor.ToString()} + { "rotationy", copyData.SourceRot.y.ToString() }, + { "rotationdiff", copyData.RotCor.ToString() } }; datafile["entities"] = copyData.RawData; datafile["protocol"] = new Dictionary { - {"items", 2}, - {"version", Version} + { "items", 2 }, + { "version", Version } }; Interface.Oxide.DataFileSystem.SaveDatafile(path); - SendReply(copyData.Player, Lang("COPY_SUCCESS", copyData.Player.UserIDString, copyData.Filename)); + copyData.Player.Reply(Lang("COPY_SUCCESS", copyData.Player.Id, copyData.Filename)); copyData.Callback?.Invoke(); @@ -530,7 +526,7 @@ private void CopyLoop(CopyData copyData) private float DegreeToRadian(float angle) { - return (float) (Math.PI * angle / 180.0f); + return (float)(Math.PI * angle / 180.0f); } private Dictionary EntityData(BaseEntity entity, Vector3 entPos, Vector3 entRot, @@ -542,28 +538,44 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, var data = new Dictionary { - {"prefabname", entity.PrefabName}, - {"skinid", entity.skinID}, - {"flags", TryCopyFlags(entity)}, + { "prefabname", entity.PrefabName }, + { "skinid", entity.skinID }, + { "flags", TryCopyFlags(entity) }, { "pos", new Dictionary { - {"x", normalizedPos.x.ToString()}, - {"y", normalizedPos.y.ToString()}, - {"z", normalizedPos.z.ToString()} + { "x", normalizedPos.x.ToString() }, + { "y", normalizedPos.y.ToString() }, + { "z", normalizedPos.z.ToString() } } }, { "rot", new Dictionary { - {"x", entRot.x.ToString()}, - {"y", entRot.y.ToString()}, - {"z", entRot.z.ToString()} + { "x", entRot.x.ToString() }, + { "y", entRot.y.ToString() }, + { "z", entRot.z.ToString() } } }, - {"ownerid", entity.OwnerID} + { "ownerid", entity.OwnerID } }; + var growableEntity = entity as GrowableEntity; + if (growableEntity != null) + { + var genes = GrowableGeneEncoding.EncodeGenesToInt(growableEntity.Genes); + if (genes > 0) + { + data.Add("genes", genes); + } + + var perent = growableEntity.GetParentEntity(); + if (perent != null) + { + data.Add("hasParent", true); + } + } + TryCopySlots(entity, data, copyData.SaveShare); var buildingblock = entity as BuildingBlock; @@ -582,12 +594,13 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { var itemdata = new Dictionary { - {"condition", item.condition.ToString()}, - {"id", item.info.itemid}, - {"amount", item.amount}, - {"skinid", item.skin}, - {"position", item.position}, - {"blueprintTarget", item.blueprintTarget} + { "condition", item.condition.ToString() }, + { "id", item.info.itemid }, + { "amount", item.amount }, + { "skinid", item.skin }, + { "position", item.position }, + { "blueprintTarget", item.blueprintTarget }, + { "dataInt", item.instanceData?.dataInt ?? 0 } }; if (!string.IsNullOrEmpty(item.text)) @@ -607,7 +620,7 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { itemdata.Add("magazine", new Dictionary { - {magazine.ammoType.itemid.ToString(), magazine.contents} + { magazine.ammoType.itemid.ToString(), magazine.contents } }); } } @@ -621,8 +634,8 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { contents.Add(new Dictionary { - {"id", itemContains.info.itemid}, - {"amount", itemContains.amount} + { "id", itemContains.info.itemid }, + { "amount", itemContains.amount } }); } @@ -644,12 +657,13 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { var itemdata = new Dictionary { - {"condition", item.condition.ToString()}, - {"id", item.info.itemid}, - {"amount", item.amount}, - {"skinid", item.skin}, - {"position", item.position}, - {"blueprintTarget", item.blueprintTarget} + { "condition", item.condition.ToString() }, + { "id", item.info.itemid }, + { "amount", item.amount }, + { "skinid", item.skin }, + { "position", item.position }, + { "blueprintTarget", item.blueprintTarget }, + { "dataInt", item.instanceData?.dataInt ?? 0 } }; if (!string.IsNullOrEmpty(item.text)) @@ -669,7 +683,7 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { itemdata.Add("magazine", new Dictionary { - {magazine.ammoType.itemid.ToString(), magazine.contents} + { magazine.ammoType.itemid.ToString(), magazine.contents } }); } } @@ -683,8 +697,8 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { contents.Add(new Dictionary { - {"id", itemContains.info.itemid}, - {"amount", itemContains.amount} + { "id", itemContains.info.itemid }, + { "amount", itemContains.amount } }); } @@ -702,12 +716,12 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { data.Add("sign", new Dictionary { - {"locked", sign.IsLocked()} + { "locked", sign.IsLocked() } }); - var signData = (Dictionary) data["sign"]; + var signData = (Dictionary)data["sign"]; - for (int num = 0; num < sign.textureIDs.Length; num++) + for (var num = 0; num < sign.textureIDs.Length; num++) { var textureId = sign.textureIDs[num]; if (textureId == 0) @@ -723,6 +737,13 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, signData["amount"] = sign.textureIDs.Length; } + var lights = entity as AdvancedChristmasLights; + if (lights != null) + { + data.Add("points", lights.points.Select(x => new { x.normal, x.point })); + data.Add("animationStyle", lights.animationStyle); + } + if (copyData.SaveShare) { var sleepingBag = entity as SleepingBag; @@ -731,9 +752,9 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { data.Add("sleepingbag", new Dictionary { - {"niceName", sleepingBag.niceName}, - {"deployerUserID", sleepingBag.deployerUserID}, - {"isPublic", sleepingBag.IsPublic()} + { "niceName", sleepingBag.niceName }, + { "deployerUserID", sleepingBag.deployerUserID }, + { "isPublic", sleepingBag.IsPublic() } }); } @@ -743,7 +764,7 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { data.Add("cupboard", new Dictionary { - {"authorizedPlayers", cupboard.authorizedPlayers.Select(y => y.userid).ToList()} + { "authorizedPlayers", cupboard.authorizedPlayers.Select(y => y.userid).ToList() } }); } @@ -753,7 +774,7 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { data.Add("autoturret", new Dictionary { - {"authorizedPlayers", autoTurret.authorizedPlayers.Select(p => p.userid).ToList()} + { "authorizedPlayers", autoTurret.authorizedPlayers.Select(p => p.userid).ToList() } }); } } @@ -763,9 +784,9 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { data.Add("cctv", new Dictionary { - {"yaw", cctvRc.yawAmount}, - {"pitch", cctvRc.pitchAmount}, - {"rcIdentifier", cctvRc.rcIdentifier} + { "yaw", cctvRc.yawAmount }, + { "pitch", cctvRc.pitchAmount }, + { "rcIdentifier", cctvRc.rcIdentifier } }); } @@ -779,21 +800,21 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { sellOrders.Add(new Dictionary { - {"itemToSellID", vendItem.itemToSellID}, - {"itemToSellAmount", vendItem.itemToSellAmount}, - {"currencyID", vendItem.currencyID}, - {"currencyAmountPerItem", vendItem.currencyAmountPerItem}, - {"inStock", vendItem.inStock}, - {"currencyIsBP", vendItem.currencyIsBP}, - {"itemToSellIsBP", vendItem.itemToSellIsBP} + { "itemToSellID", vendItem.itemToSellID }, + { "itemToSellAmount", vendItem.itemToSellAmount }, + { "currencyID", vendItem.currencyID }, + { "currencyAmountPerItem", vendItem.currencyAmountPerItem }, + { "inStock", vendItem.inStock }, + { "currencyIsBP", vendItem.currencyIsBP }, + { "itemToSellIsBP", vendItem.itemToSellIsBP } }); } data.Add("vendingmachine", new Dictionary { - {"shopName", vendingMachine.shopName}, - {"isBroadcasting", vendingMachine.IsBroadcasting()}, - {"sellOrders", sellOrders} + { "shopName", vendingMachine.shopName }, + { "isBroadcasting", vendingMachine.IsBroadcasting() }, + { "sellOrders", sellOrders } }); } @@ -804,10 +825,10 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, var ioData = new Dictionary(); var inputs = ioEntity.inputs.Select(input => new Dictionary { - {"connectedID", input.connectedTo.entityRef.uid}, - {"connectedToSlot", input.connectedToSlot}, - {"niceName", input.niceName}, - {"type", (int) input.type} + { "connectedID", input.connectedTo.entityRef.uid }, + { "connectedToSlot", input.connectedToSlot }, + { "niceName", input.niceName }, + { "type", (int)input.type } }) .Cast() .ToList(); @@ -819,11 +840,11 @@ private Dictionary EntityData(BaseEntity entity, Vector3 entPos, { var ioConnection = new Dictionary { - {"connectedID", output.connectedTo.entityRef.uid}, - {"connectedToSlot", output.connectedToSlot}, - {"niceName", output.niceName}, - {"type", (int) output.type}, - {"linePoints", output.linePoints?.ToList() ?? new List()} + { "connectedID", output.connectedTo.entityRef.uid }, + { "connectedToSlot", output.connectedToSlot }, + { "niceName", output.niceName }, + { "type", (int)output.type }, + { "linePoints", output.linePoints?.ToList() ?? new List() } }; outputs.Add(ioConnection); @@ -867,13 +888,14 @@ private object FindBestHeight(HashSet> entities, Vect foreach (var entity in entities) { - if (((string) entity["prefabname"]).Contains("/foundation/")) + var prefab = (string)entity["prefabname"]; + if (prefab.Contains("/foundation/") || prefab.Contains("/foundation.triangle/")) { - var foundHeight = GetGround((Vector3) entity["position"]); + var foundHeight = GetGround((Vector3)entity["position"]); if (foundHeight != null) { - var height = (Vector3) foundHeight; + var height = (Vector3)foundHeight; if (height.y > maxHeight) maxHeight = height.y; @@ -938,9 +960,9 @@ private int GetItemId(int itemId) return itemId; } - private bool HasAccess(BasePlayer player, string permName) + private bool HasAccess(IPlayer player, string permName) { - return player.IsAdmin || permission.UserHasPermission(player.UserIDString, permName); + return player.IsAdmin || player.HasPermission(permName); } private byte[] ImageResize(byte[] imageBytes, int width, int height) @@ -963,10 +985,10 @@ private string Lang(string key, string userId = null, params object[] args) => private Vector3 NormalizePosition(Vector3 initialPos, Vector3 currentPos, float diffRot) { var transformedPos = currentPos - initialPos; - var newX = transformedPos.x * (float) Math.Cos(-diffRot) + - transformedPos.z * (float) Math.Sin(-diffRot); - var newZ = transformedPos.z * (float) Math.Cos(-diffRot) - - transformedPos.x * (float) Math.Sin(-diffRot); + var newX = transformedPos.x * (float)Math.Cos(-diffRot) + + transformedPos.z * (float)Math.Sin(-diffRot); + var newZ = transformedPos.z * (float)Math.Cos(-diffRot) - + transformedPos.x * (float)Math.Sin(-diffRot); transformedPos.x = newX; transformedPos.z = newZ; @@ -975,13 +997,9 @@ private Vector3 NormalizePosition(Vector3 initialPos, Vector3 currentPos, float } private void Paste(ICollection> entities, Dictionary protocol, - bool ownership, Vector3 startPos, BasePlayer player, bool stability, float rotationCorrection, + bool ownership, Vector3 startPos, IPlayer player, bool stability, float rotationCorrection, float heightAdj, bool auth, Action callback, string filename) { - - var ioEntities = new Dictionary>(); - uint buildingId = 0; - //Settings var isItemReplace = !protocol.ContainsKey("items"); @@ -995,6 +1013,7 @@ private void Paste(ICollection> entities, Dictionary< IsItemReplace = isItemReplace, Entities = entities, Player = player, + BasePlayer = player.Object as BasePlayer, QuaternionRotation = quaternionRotation, StartPos = startPos, Stability = stability, @@ -1011,17 +1030,16 @@ private void PasteLoop(PasteData pasteData) { var entities = pasteData.Entities; var todo = entities.Take(_config.PasteBatchSize).ToArray(); - var player = pasteData.Player; foreach (var data in todo) { entities.Remove(data); - var prefabname = (string) data["prefabname"]; + var prefabname = (string)data["prefabname"]; var skinid = ulong.Parse(data["skinid"].ToString()); - var pos = (Vector3) data["position"]; - var rot = (Quaternion) data["rotation"]; + var pos = (Vector3)data["position"]; + var rot = (Quaternion)data["rotation"]; - var ownerId = player?.userID ?? 0; + var ownerId = pasteData.BasePlayer?.userID ?? 0; if (data.ContainsKey("ownerid")) { ownerId = Convert.ToUInt64(data["ownerid"]); @@ -1042,11 +1060,12 @@ private void PasteLoop(PasteData pasteData) if (entity == null) continue; - entity.transform.position = pos; - entity.transform.rotation = rot; + var transform = entity.transform; + transform.position = pos; + transform.rotation = rot; - if (player != null) - entity.SendMessage("SetDeployedBy", player, SendMessageOptions.DontRequireReceiver); + if (pasteData.BasePlayer != null) + entity.SendMessage("SetDeployedBy", pasteData.BasePlayer, SendMessageOptions.DontRequireReceiver); if (pasteData.Ownership) entity.OwnerID = ownerId; @@ -1056,7 +1075,7 @@ private void PasteLoop(PasteData pasteData) if (buildingBlock != null) { buildingBlock.blockDefinition = PrefabAttribute.server.Find(buildingBlock.prefabID); - buildingBlock.SetGrade((BuildingGrade.Enum) data["grade"]); + buildingBlock.SetGrade((BuildingGrade.Enum)data["grade"]); if (!pasteData.Stability) buildingBlock.grounded = true; } @@ -1095,7 +1114,14 @@ private void PasteLoop(PasteData pasteData) var box = entity as StorageContainer; if (box != null) { - box.inventory.Clear(); + if (box.inventory == null) + { + box.inventory = new ItemContainer(); + box.inventory.ServerInitialize(null, box.inventorySlots); + box.inventory.GiveUID(); + box.inventory.entityOwner = box; + } + else box.inventory.Clear(); var items = new List(); @@ -1109,6 +1135,45 @@ private void PasteLoop(PasteData pasteData) var itemamount = Convert.ToInt32(item["amount"]); var itemskin = ulong.Parse(item["skinid"].ToString()); var itemcondition = Convert.ToSingle(item["condition"]); + var dataInt = 0; + if (item.ContainsKey("dataInt")) + { + dataInt = Convert.ToInt32(item["dataInt"]); + } + + var growableEntity = entity as GrowableEntity; + if (growableEntity != null) + { + if (data.ContainsKey("genes")) + { + var genesData = (int)data["genes"]; + + if (genesData > 0) + { + GrowableGeneEncoding.DecodeIntToGenes(genesData, growableEntity.Genes); + } + } + + if (data.ContainsKey("hasParent")) + { + var isParented = (bool)data["hasParent"]; + + if (isParented) + { + RaycastHit hitInfo; + + if (Physics.Raycast(growableEntity.transform.position, Vector3.down, out hitInfo, + .5f, Rust.Layers.DefaultDeployVolumeCheck)) + { + var parentEntity = hitInfo.GetEntity(); + if (parentEntity != null) + { + growableEntity.SetParent(parentEntity, true); + } + } + } + } + } if (pasteData.IsItemReplace) itemid = GetItemId(itemid); @@ -1132,6 +1197,15 @@ private void PasteLoop(PasteData pasteData) i.blueprintTarget = blueprintTarget; } + if (dataInt > 0) + { + i.instanceData = new ProtoBuf.Item.InstanceData() + { + ShouldPool = false, + dataInt = dataInt + }; + } + if (item.ContainsKey("magazine")) { var heldent = i.GetHeldEntity(); @@ -1198,8 +1272,9 @@ private void PasteLoop(PasteData pasteData) .Select(Convert.ToUInt64).ToList(); } - if (player != null && !authorizedPlayers.Contains(player.userID) && pasteData.Auth) - authorizedPlayers.Add(player.userID); + if (pasteData.BasePlayer != null && !authorizedPlayers.Contains(pasteData.BasePlayer.userID) && + pasteData.Auth) + authorizedPlayers.Add(pasteData.BasePlayer.userID); foreach (var userId in authorizedPlayers) { @@ -1216,7 +1291,12 @@ private void PasteLoop(PasteData pasteData) var containerIo = entity as ContainerIOEntity; if (containerIo != null) { - containerIo.inventory.Clear(); + if (containerIo.inventory == null) + { + containerIo.CreateInventory(true); + containerIo.OnInventoryFirstCreated(containerIo.inventory); + } + else containerIo.inventory.Clear(); var items = new List(); @@ -1230,6 +1310,11 @@ private void PasteLoop(PasteData pasteData) var itemamount = Convert.ToInt32(itemJson["amount"]); var itemskin = ulong.Parse(itemJson["skinid"].ToString()); var itemcondition = Convert.ToSingle(itemJson["condition"]); + var dataInt = 0; + if (itemJson.ContainsKey("dataInt")) + { + dataInt = Convert.ToInt32(itemJson["dataInt"]); + } if (pasteData.IsItemReplace) itemid = GetItemId(itemid); @@ -1253,6 +1338,15 @@ private void PasteLoop(PasteData pasteData) item.blueprintTarget = blueprintTarget; } + if (dataInt > 0) + { + item.instanceData = new ProtoBuf.Item.InstanceData() + { + ShouldPool = false, + dataInt = dataInt + }; + } + if (itemJson.ContainsKey("magazine")) { var heldent = item.GetHeldEntity(); @@ -1323,7 +1417,7 @@ private void PasteLoop(PasteData pasteData) int amount; if (int.TryParse(signData["amount"].ToString(), out amount)) { - for (int num = 0; num < amount; num++) + for (var num = 0; num < amount; num++) { if (signData.ContainsKey($"texture{num}")) { @@ -1347,6 +1441,24 @@ private void PasteLoop(PasteData pasteData) sign.SendNetworkUpdate(); } + var lights = entity as AdvancedChristmasLights; + if (lights != null) + { + if (data.ContainsKey("points")) + { + foreach (Dictionary point in data["points"] as List) + { + lights.points.Add(new AdvancedChristmasLights.pointEntry + { normal = (Vector3)point["normal"], point = (Vector3)point["point"] }); + } + } + + if (data.ContainsKey("animationStyle")) + { + lights.animationStyle = (AdvancedChristmasLights.AnimationType)data["animationStyle"]; + } + } + var sleepingBag = entity as SleepingBag; if (sleepingBag != null && data.ContainsKey("sleepingbag")) { @@ -1369,8 +1481,9 @@ private void PasteLoop(PasteData pasteData) .ToList(); } - if (player != null && !authorizedPlayers.Contains(player.userID) && pasteData.Auth) - authorizedPlayers.Add(player.userID); + if (pasteData.BasePlayer != null && !authorizedPlayers.Contains(pasteData.BasePlayer.userID) && + pasteData.Auth) + authorizedPlayers.Add(pasteData.BasePlayer.userID); foreach (var userId in authorizedPlayers) { @@ -1387,7 +1500,7 @@ private void PasteLoop(PasteData pasteData) var cctvRc = entity as CCTV_RC; if (cctvRc != null && data.ContainsKey("cctv")) { - var cctv = (Dictionary) data["cctv"]; + var cctv = (Dictionary)data["cctv"]; cctvRc.yawAmount = Convert.ToSingle(cctv["yaw"]); cctvRc.pitchAmount = Convert.ToSingle(cctv["pitch"]); cctvRc.rcIdentifier = cctv["rcIdentifier"].ToString(); @@ -1455,7 +1568,7 @@ private void PasteLoop(PasteData pasteData) ioData.Add("entity", ioEntity); ioData.Add("newId", ioEntity.net.ID); - object oldIdObject = 0; + object oldIdObject; if (ioData.TryGetValue("oldID", out oldIdObject)) { var oldId = Convert.ToUInt32(oldIdObject); @@ -1555,9 +1668,6 @@ private void PasteLoop(PasteData pasteData) ioEntity.inputs[index] = new IOEntity.IOSlot(); var ioConnection = pasteData.IoEntities[oldId]; - - object temp; - if (ioConnection.ContainsKey("newId")) { ioEntity.inputs[index].connectedTo.entityRef.uid = @@ -1603,7 +1713,7 @@ private void PasteLoop(PasteData pasteData) ioOutput.niceName = output["niceName"] as string; - ioOutput.type = (IOEntity.IOType) Convert.ToInt32(output["type"]); + ioOutput.type = (IOEntity.IOType)Convert.ToInt32(output["type"]); } if (output.ContainsKey("linePoints")) @@ -1639,22 +1749,15 @@ private void PasteLoop(PasteData pasteData) entity.UpdateStability(); } - if (player != null) - { - SendReply(player, Lang("PASTE_SUCCESS", player.UserIDString)); + pasteData.Player.Reply(Lang("PASTE_SUCCESS", pasteData.Player.Id)); #if DEBUG - SendReply(player, $"Stopwatch took: {pasteData.Sw.Elapsed.TotalMilliseconds} ms"); + pasteData.Player.Reply($"Stopwatch took: {pasteData.Sw.Elapsed.TotalMilliseconds} ms"); #endif - } - else - { - Puts(Lang("PASTE_SUCCESS")); - } - if (!_lastPastes.ContainsKey(player?.UserIDString ?? _serverId)) - _lastPastes[player?.UserIDString ?? _serverId] = new Stack>(); + if (!_lastPastes.ContainsKey(pasteData.Player.Id)) + _lastPastes[pasteData.Player.Id] = new Stack>(); - _lastPastes[player?.UserIDString ?? _serverId].Push(pasteData.PastedEntities); + _lastPastes[pasteData.Player.Id].Push(pasteData.PastedEntities); pasteData.Callback?.Invoke(); @@ -1669,43 +1772,41 @@ private HashSet> PreLoadData(List entities, V var quaternionRotation = Quaternion.EulerRotation(eulerRotation); var preloaddata = new HashSet>(); - foreach (var entity in entities) + foreach (Dictionary entity in entities) { - var data = entity as Dictionary; - - if (!deployables && !data.ContainsKey("grade")) + if (!deployables && !entity.ContainsKey("grade")) continue; - var pos = (Dictionary) data["pos"]; - var rot = (Dictionary) data["rot"]; + var pos = (Dictionary)entity["pos"]; + var rot = (Dictionary)entity["rot"]; - data.Add("position", + entity.Add("position", quaternionRotation * new Vector3(Convert.ToSingle(pos["x"]), Convert.ToSingle(pos["y"]), Convert.ToSingle(pos["z"])) + startPos); - data.Add("rotation", + entity.Add("rotation", Quaternion.EulerRotation(eulerRotation + new Vector3(Convert.ToSingle(rot["x"]), Convert.ToSingle(rot["y"]), Convert.ToSingle(rot["z"])))); - if (!inventories && data.ContainsKey("items")) - data["items"] = new List(); + if (!inventories && entity.ContainsKey("items")) + entity["items"] = new List(); - if (!vending && data["prefabname"].ToString().Contains("vendingmachine")) - data.Remove("vendingmachine"); + if (!vending && entity["prefabname"].ToString().Contains("vendingmachine")) + entity.Remove("vendingmachine"); - preloaddata.Add(data); + preloaddata.Add(entity); } return preloaddata; } private object TryCopy(Vector3 sourcePos, Vector3 sourceRot, string filename, float rotationCorrection, - string[] args, BasePlayer player, Action callback) + string[] args, IPlayer player, Action callback) { bool saveShare = _config.Copy.Share, saveTree = _config.Copy.Tree, eachToEach = _config.Copy.EachToEach; var copyMechanics = CopyMechanics.Proximity; var radius = _config.Copy.Radius; - for (var i = 0;; i = i + 2) + for (var i = 0;; i += 2) { if (i >= args.Length) break; @@ -1789,8 +1890,8 @@ private void TryCopySlots(BaseEntity ent, IDictionary housedata, var codedata = new Dictionary { - {"prefabname", slotEntity.PrefabName}, - {"flags", TryCopyFlags(ent)} + { "prefabname", slotEntity.PrefabName }, + { "flags", TryCopyFlags(ent) } }; if (slotEntity.GetComponent()) @@ -1841,10 +1942,10 @@ private Dictionary TryCopyFlags(BaseEntity entity) return flags; } - private object TryPaste(Vector3 startPos, string filename, BasePlayer player, float rotationCorrection, + private object TryPaste(Vector3 startPos, string filename, IPlayer player, float rotationCorrection, string[] args, bool autoHeight = true, Action callback = null) { - var userId = player?.UserIDString; + var userId = player?.Id; var path = _subDirectory + filename; @@ -1864,7 +1965,7 @@ private object TryPaste(Vector3 startPos, string filename, BasePlayer player, fl stability = _config.Paste.Stability, ownership = _config.Paste.EntityOwner; - for (var i = 0;; i = i + 2) + for (var i = 0;; i += 2) { if (i >= args.Length) break; @@ -1940,6 +2041,16 @@ private object TryPaste(Vector3 startPos, string filename, BasePlayer player, fl break; + case "position": + startPos = args[valueIndex].ToVector3(); + break; + + case "rotation": + if (!float.TryParse(args[valueIndex], out rotationCorrection)) + return Lang("SYNTAX_FLOAT", userId, param); + + break; + default: return Lang("SYNTAX_PASTE_OR_PASTEBACK", userId); } @@ -1957,11 +2068,11 @@ private object TryPaste(Vector3 startPos, string filename, BasePlayer player, fl if (bestHeight is string) return bestHeight; - heightAdj += (float) bestHeight - startPos.y; + heightAdj += (float)bestHeight - startPos.y; foreach (var entity in preloadData) { - var pos = (Vector3) entity["position"]; + var pos = (Vector3)entity["position"]; pos.y += heightAdj; entity["position"] = pos; @@ -1999,14 +2110,13 @@ private List TryPasteSlots(BaseEntity ent, Dictionary; - var slotEntity = GameManager.server.CreateEntity((string) slotData["prefabname"], Vector3.zero); - + var slotEntity = GameManager.server.CreateEntity((string)slotData?["prefabname"], Vector3.zero); if (slotEntity == null) continue; slotEntity.gameObject.Identity(); slotEntity.SetParent(ent, slotName); - slotEntity.OnDeployed(ent, null); + slotEntity.OnDeployed(ent, null, _emptyItem); slotEntity.Spawn(); ent.SetSlot(slot, slotEntity); @@ -2018,7 +2128,7 @@ private List TryPasteSlots(BaseEntity ent, Dictionary()) { - var code = (string) slotData["code"]; + var code = (string)slotData["code"]; if (!string.IsNullOrEmpty(code)) { @@ -2026,12 +2136,12 @@ private List TryPasteSlots(BaseEntity ent, Dictionary) + foreach (var userId in (List)slotData["whitelistPlayers"]) { codeLock.whitelistPlayers.Add(Convert.ToUInt64(userId)); } @@ -2039,14 +2149,14 @@ private List TryPasteSlots(BaseEntity ent, Dictionary) + foreach (var userId in (List)slotData["guestPlayers"]) { codeLock.guestPlayers.Add(Convert.ToUInt64(userId)); } @@ -2078,82 +2188,82 @@ private List TryPasteSlots(BaseEntity ent, Dictionary; - var pos = defaultdata["position"] as Dictionary; - var rotationCorrection = Convert.ToSingle(defaultdata["rotationdiff"]); - var startPos = new Vector3(Convert.ToSingle(pos["x"]), Convert.ToSingle(pos["y"]), - Convert.ToSingle(pos["z"])); + var pos = defaultdata?["position"] as Dictionary; + var rotationCorrection = Convert.ToSingle(defaultdata?["rotationdiff"]); + var startPos = new Vector3(Convert.ToSingle(pos?["x"]), Convert.ToSingle(pos?["y"]), + Convert.ToSingle(pos?["z"])); - return TryPaste(startPos, filename, player, rotationCorrection, args, autoHeight: false); + return TryPaste(startPos, filename, player, rotationCorrection, args, false); } - //Сhat commands - - [ChatCommand("copy")] - private void CmdChatCopy(BasePlayer player, string command, string[] args) + [Command("copy")] + private void CmdCopy(IPlayer player, string command, string[] args) { if (!HasAccess(player, _copyPermission)) { - SendReply(player, Lang("NO_ACCESS", player.UserIDString)); + player.Reply(Lang("NO_ACCESS", player.Id)); return; } if (args.Length < 1) { - SendReply(player, Lang("SYNTAX_COPY", player.UserIDString)); + player.Reply(Lang("SYNTAX_COPY", player.Id)); return; } + var basePlayer = player.Object as BasePlayer; var savename = args[0]; - var success = TryCopyFromSteamId(player.userID, savename, args.Skip(1).ToArray()); + var success = + TryCopyFromSteamId(basePlayer == null ? 0ul : basePlayer.userID, savename, + args.Skip(1).ToArray()) as string; - if (success is string) - { - SendReply(player, (string) success); - } + if (!string.IsNullOrEmpty(success)) + player.Reply(success); } - [ChatCommand("paste")] - private void CmdChatPaste(BasePlayer player, string command, string[] args) + [Command("paste")] + private void CmdPaste(IPlayer player, string command, string[] args) { if (!HasAccess(player, _pastePermission)) { - SendReply(player, Lang("NO_ACCESS", player.UserIDString)); + player.Reply(Lang("NO_ACCESS", player.Id)); return; } if (args.Length < 1) { - SendReply(player, Lang("SYNTAX_PASTE_OR_PASTEBACK", player.UserIDString)); + player.Reply(Lang("SYNTAX_PASTE_OR_PASTEBACK", player.Id)); return; } - var success = TryPasteFromSteamId(player.userID, args[0], args.Skip(1).ToArray()); + var basePlayer = player.Object as BasePlayer; + var success = + TryPasteFromSteamId(basePlayer == null ? 0ul : basePlayer.userID, args[0], + args.Skip(1).ToArray()) as string; - if (success is string) - { - SendReply(player, (string) success); - } + if (!string.IsNullOrEmpty(success)) + player.Reply(success); } - [ChatCommand("copylist")] - private void CmdChatList(BasePlayer player, string command, string[] args) + [Command("copylist")] + private void CmdList(IPlayer player, string command, string[] args) { if (!HasAccess(player, _listPermission)) { - SendReply(player, Lang("NO_ACCESS", player.UserIDString)); + player.Reply(Lang("NO_ACCESS", player.Id)); return; } @@ -2168,461 +2278,449 @@ private void CmdChatList(BasePlayer player, string command, string[] args) fileList.Add(justfile); } - SendReply(player, Lang("AVAILABLE_STRUCTURES", player.UserIDString)); - SendReply(player, string.Join(", ", fileList.ToArray())); + player.Reply(Lang("AVAILABLE_STRUCTURES", player.Id)); + player.Reply(string.Join(", ", fileList.ToArray())); } - [ChatCommand("pasteback")] - private void CmdChatPasteBack(BasePlayer player, string command, string[] args) + [Command("pasteback")] + private void CmdPasteBack(IPlayer player, string command, string[] args) { if (!HasAccess(player, _pastebackPermission)) { - SendReply(player, Lang("NO_ACCESS", player.UserIDString)); + player.Reply(Lang("NO_ACCESS", player.Id)); return; } - var result = CmdPasteBack(player, args); - - if (result is string) - SendReply(player, (string) result); - } - - [ChatCommand("undo")] - private void CmdChatUndo(BasePlayer player, string command, string[] args) - { - if (!HasAccess(player, _undoPermission)) + if (args.Length < 1) { - SendReply(player, Lang("NO_ACCESS", player.UserIDString)); + player.Reply(Lang("SYNTAX_PASTEBACK", player.Id)); return; } - CmdUndo(player.UserIDString, args); + var success = TryPasteBack(args[0], player, args.Skip(1).ToArray()) as string; + if (!string.IsNullOrEmpty(success)) + player.Reply(success); } - //Console commands [From Server] - - [ConsoleCommand("pasteback")] - private void CmdConsolePasteBack(ConsoleSystem.Arg arg) + [Command("undo")] + private void CmdUndo(IPlayer player, string command, string[] args) { - if (!arg.IsAdmin) + if (!HasAccess(player, _undoPermission)) + { + player.Reply(Lang("NO_ACCESS", player.Id)); return; + } - var result = CmdPasteBack(arg.Player(), arg.Args); - - if (result is string) - SendReply(arg, (string) result); - } - - [ConsoleCommand("undo")] - private void CmdConsoleUndo(ConsoleSystem.Arg arg) - { - if (!arg.IsAdmin) + if (!_lastPastes.ContainsKey(player.Id)) + { + player.Reply(Lang("NO_PASTED_STRUCTURE", player.Id)); return; + } - var player = arg.Player(); + var entities = new HashSet(_lastPastes[player.Id].Pop().ToList()); - CmdUndo(player == null ? _serverId : player.UserIDString, arg.Args); + UndoLoop(entities, player); } //Replace between old ItemID to new ItemID private static readonly Dictionary ReplaceItemId = new Dictionary { - {-1461508848, 1545779598}, - {2115555558, 588596902}, - {-533875561, 785728077}, - {1621541165, 51984655}, - {-422893115, -1691396643}, - {815896488, -1211166256}, - {805088543, -1321651331}, - {449771810, 605467368}, - {1152393492, 1712070256}, - {1578894260, -742865266}, - {1436532208, 1638322904}, - {542276424, -1841918730}, - {1594947829, -17123659}, - {-1035059994, -1685290200}, - {1818890814, -1036635990}, - {1819281075, -727717969}, - {1685058759, -1432674913}, - {93029210, 1548091822}, - {-1565095136, 352130972}, - {-1775362679, 215754713}, - {-1775249157, 14241751}, - {-1280058093, -1023065463}, - {-420273765, -1234735557}, - {563023711, -2139580305}, - {790921853, -262590403}, - {-337261910, -2072273936}, - {498312426, -1950721390}, - {504904386, 1655650836}, - {-1221200300, -559599960}, - {510887968, 15388698}, - {-814689390, 866889860}, - {1024486167, 1382263453}, - {2021568998, 609049394}, - {97329, 1099314009}, - {1046072789, -582782051}, - {97409, -1273339005}, - {-1480119738, -1262185308}, - {1611480185, 1931713481}, - {-1386464949, 1553078977}, - {93832698, 1776460938}, - {-1063412582, -586342290}, - {-1887162396, -996920608}, - {-55660037, 1588298435}, - {919780768, 1711033574}, - {-365801095, 1719978075}, - {68998734, 613961768}, - {-853695669, 1443579727}, - {271534758, 833533164}, - {-770311783, -180129657}, - {-1192532973, 1424075905}, - {-307490664, 1525520776}, - {707427396, 602741290}, - {707432758, -761829530}, - {-2079677721, 1783512007}, - {-1342405573, -1316706473}, - {-139769801, 1946219319}, - {-1043746011, -700591459}, - {2080339268, 1655979682}, - {-171664558, -1941646328}, - {1050986417, -1557377697}, - {-1693683664, 1789825282}, - {523409530, 1121925526}, - {1300054961, 634478325}, - {-2095387015, 1142993169}, - {1428021640, 1104520648}, - {94623429, 1534542921}, - {1436001773, -1938052175}, - {1711323399, 1973684065}, - {1734319168, -1848736516}, - {-1658459025, -1440987069}, - {-726947205, -751151717}, - {-341443994, 363467698}, - {1540879296, 2009734114}, - {94756378, -858312878}, - {3059095, 204391461}, - {3059624, 1367190888}, - {2045107609, -778875547}, - {583366917, 998894949}, - {2123300234, 1965232394}, - {1983936587, -321733511}, - {1257201758, -97956382}, - {-1144743963, 296519935}, - {-1144542967, -113413047}, - {-1144334585, -2022172587}, - {1066729526, -1101924344}, - {-1598790097, 1390353317}, - {-933236257, 1221063409}, - {-1575287163, -1336109173}, - {-2104481870, -2067472972}, - {-1571725662, 1353298668}, - {1456441506, 1729120840}, - {1200628767, -1112793865}, - {-778796102, 1409529282}, - {1526866730, 674734128}, - {1925723260, -1519126340}, - {1891056868, 1401987718}, - {1295154089, -1878475007}, - {498591726, 1248356124}, - {1755466030, -592016202}, - {726730162, 798638114}, - {-1034048911, -1018587433}, - {252529905, 274502203}, - {471582113, -1065444793}, - {-1138648591, 16333305}, - {305916740, 649305914}, - {305916742, 649305916}, - {305916744, 649305918}, - {1908328648, -1535621066}, - {-2078972355, 1668129151}, - {-533484654, 989925924}, - {1571660245, 1569882109}, - {1045869440, -1215753368}, - {1985408483, 528668503}, - {97513422, 304481038}, - {1496470781, -196667575}, - {1229879204, 952603248}, - {-1722829188, 936496778}, - {1849912854, 1948067030}, - {-1266285051, 1413014235}, - {-1749787215, -1000573653}, - {28178745, -946369541}, - {-505639592, -1999722522}, - {1598149413, -1992717673}, - {-1779401418, -691113464}, - {-57285700, -335089230}, - {98228420, 479143914}, - {1422845239, 999690781}, - {277631078, -1819763926}, - {115739308, 1366282552}, - {-522149009, -690276911}, - {3175989, -1899491405}, - {718197703, -746030907}, - {384204160, 1840822026}, - {-1308622549, 143803535}, - {-217113639, -2124352573}, - {-1580059655, -265876753}, - {-1832205789, 1070894649}, - {305916741, 649305917}, - {936777834, 3222790}, - {-1224598842, 200773292}, - {-1976561211, -1506397857}, - {-1406876421, 1675639563}, - {-1397343301, -23994173}, - {1260209393, 850280505}, - {-1035315940, 1877339384}, - {-1381682752, 1714496074}, - {696727039, -1022661119}, - {-2128719593, -803263829}, - {-1178289187, -1903165497}, - {1351172108, 1181207482}, - {-450738836, -1539025626}, - {-966287254, -324675402}, - {340009023, 671063303}, - {124310981, -1478212975}, - {1501403549, -2094954543}, - {698310895, -1252059217}, - {523855532, 1266491000}, - {2045246801, -886280491}, - {583506109, -237809779}, - {-148163128, 794356786}, - {-132588262, -1773144852}, - {-1666761111, 196700171}, - {-465236267, 442289265}, - {-1211618504, 1751045826}, - {2133577942, -1982036270}, - {-1014825244, -682687162}, - {-991829475, 1536610005}, - {-642008142, -1709878924}, - {661790782, 1272768630}, - {-1440143841, -1780802565}, - {569119686, 1746956556}, - {1404466285, -1102429027}, - {-1616887133, -48090175}, - {-1167640370, -1163532624}, - {-1284735799, 1242482355}, - {-1278649848, -1824943010}, - {776005741, 1814288539}, - {108061910, -316250604}, - {255101535, -1663759755}, - {-51678842, 1658229558}, - {-789202811, 254522515}, - {516382256, -132516482}, - {50834473, 1381010055}, - {-975723312, 1159991980}, - {1908195100, -850982208}, - {-1097452776, -110921842}, - {146685185, -1469578201}, - {-1716193401, -1812555177}, - {193190034, -2069578888}, - {371156815, -852563019}, - {3343606, -1966748496}, - {825308669, -1137865085}, - {830965940, -586784898}, - {1662628660, -163828118}, - {1662628661, -163828117}, - {1662628662, -163828112}, - {-1832205788, 1070894648}, - {-1832205786, 1070894646}, - {1625090418, 181590376}, - {-1269800768, -874975042}, - {429648208, -1190096326}, - {-1832205787, 1070894647}, - {-1832205785, 1070894645}, - {107868, 696029452}, - {997973965, -2012470695}, - {-46188931, -702051347}, - {-46848560, -194953424}, - {-2066726403, -989755543}, - {-2043730634, 1873897110}, - {1325935999, -1520560807}, - {-225234813, -78533081}, - {-202239044, -1509851560}, - {-322501005, 1422530437}, - {-1851058636, 1917703890}, - {-1828062867, -1162759543}, - {-1966381470, -1130350864}, - {968732481, 1391703481}, - {991728250, -242084766}, - {-253819519, 621915341}, - {-1714986849, 1827479659}, - {-1691991080, 813023040}, - {179448791, -395377963}, - {431617507, -1167031859}, - {688032252, 69511070}, - {-1059362949, -4031221}, - {1265861812, 1110385766}, - {374890416, 317398316}, - {1567404401, 1882709339}, - {-1057402571, 95950017}, - {-758925787, -1130709577}, - {-1411620422, 1052926200}, - {88869913, -542577259}, - {-2094080303, 1318558775}, - {843418712, -1962971928}, - {-1569356508, -1405508498}, - {-1569280852, 1478091698}, - {449769971, 1953903201}, - {590532217, -2097376851}, - {3387378, 1414245162}, - {1767561705, 1992974553}, - {106433500, 237239288}, - {-1334615971, -1778159885}, - {-135651869, 1722154847}, - {-1595790889, 1850456855}, - {-459156023, -1695367501}, - {106434956, -1779183908}, - {-578028723, -1302129395}, - {-586116979, 286193827}, - {-1379225193, -75944661}, - {-930579334, 649912614}, - {548699316, 818877484}, - {142147109, 1581210395}, - {148953073, 1903654061}, - {102672084, 980333378}, - {640562379, -1651220691}, - {-1732316031, -1622660759}, - {-2130280721, 756517185}, - {-1725510067, -722241321}, - {1974032895, -1673693549}, - {-225085592, -567909622}, - {509654999, 1898094925}, - {466113771, -1511285251}, - {2033918259, 1373971859}, - {2069925558, -1736356576}, - {-1026117678, 803222026}, - {1987447227, -1861522751}, - {540154065, -544317637}, - {1939428458, 176787552}, - {-288010497, -2002277461}, - {-847065290, 1199391518}, - {3506021, 963906841}, - {649603450, 442886268}, - {3506418, 1414245522}, - {569935070, -1104881824}, - {113284, -1985799200}, - {1916127949, -277057363}, - {-1775234707, -1978999529}, - {-388967316, 1326180354}, - {2007564590, -575483084}, - {-1705696613, 177226991}, - {670655301, -253079493}, - {1148128486, -1958316066}, - {-141135377, 567235583}, - {109266897, -932201673}, - {-527558546, 2087678962}, - {-1745053053, -904863145}, - {1223860752, 573926264}, - {-419069863, 1234880403}, - {-1617374968, -1994909036}, - {2057749608, 1950721418}, - {24576628, -2025184684}, - {-1659202509, 1608640313}, - {2107229499, -1549739227}, - {191795897, -765183617}, - {-1009492144, 795371088}, - {2077983581, -1367281941}, - {378365037, 352499047}, - {-529054135, -1199897169}, - {-529054134, -1199897172}, - {486166145, -1023374709}, - {1628490888, 23352662}, - {1498516223, 1205607945}, - {-632459882, -1647846966}, - {-626812403, -845557339}, - {385802761, -1370759135}, - {2117976603, 121049755}, - {1338515426, -996185386}, - {-1455694274, 98508942}, - {1579245182, 2070189026}, - {-587434450, 1521286012}, - {-163742043, 1542290441}, - {-1224714193, -1832422579}, - {644359987, 826309791}, - {-1962514734, -143132326}, - {-705305612, 1153652756}, - {-357728804, -1819233322}, - {-698499648, -1138208076}, - {1213686767, -1850571427}, - {386382445, -855748505}, - {1859976884, 553887414}, - {960793436, 996293980}, - {1001265731, 2048317869}, - {1253290621, -1754948969}, - {470729623, -1293296287}, - {1051155022, -369760990}, - {865679437, -1878764039}, - {927253046, -1039528932}, - {109552593, 1796682209}, - {-2092529553, 1230323789}, - {691633666, -363689972}, - {-2055888649, 1629293099}, - {621575320, -41440462}, - {-2118132208, 1602646136}, - {-1127699509, 1540934679}, - {-685265909, -92759291}, - {552706886, -1100422738}, - {1835797460, -1021495308}, - {-892259869, 642482233}, - {-1623330855, -465682601}, - {-1616524891, 1668858301}, - {789892804, 171931394}, - {-1289478934, -1583967946}, - {-892070738, -2099697608}, - {-891243783, -1581843485}, - {889398893, -1157596551}, - {-1625468793, 1397052267}, - {1293049486, 1975934948}, - {1369769822, 559147458}, - {586484018, 1079279582}, - {110115790, 593465182}, - {1490499512, 1523195708}, - {3552619, 2019042823}, - {1471284746, 73681876}, - {456448245, -1758372725}, - {110547964, 795236088}, - {1588977225, -1667224349}, - {918540912, -209869746}, - {-471874147, 1686524871}, - {205978836, 1723747470}, - {-1044400758, -129230242}, - {-2073307447, -1331212963}, - {435230680, 2106561762}, - {-864578046, 223891266}, - {1660607208, 935692442}, - {260214178, -1478445584}, - {-1847536522, 198438816}, - {-496055048, -967648160}, - {-1792066367, 99588025}, - {562888306, -956706906}, - {-427925529, -1429456799}, - {995306285, 1451568081}, - {-378017204, -1117626326}, - {447918618, -148794216}, - {313836902, 1516985844}, - {1175970190, -796583652}, - {525244071, -148229307}, - {-1021702157, -819720157}, - {-402507101, 671706427}, - {-1556671423, -1183726687}, - {61936445, -1614955425}, - {112903447, -1779180711}, - {1817873886, -1100168350}, - {1824679850, -132247350}, - {-1628526499, -1863559151}, - {547302405, -119235651}, - {1840561315, 2114754781}, - {-460592212, -1379835144}, - {3655341, -151838493}, - {1554697726, 418081930}, - {-1883959124, 832133926}, - {-481416622, 1524187186}, - {-481416621, -41896755}, - {-481416620, -1607980696}, - {-1151126752, 1058261682}, - {-1926458555, 794443127} + { -1461508848, 1545779598 }, + { 2115555558, 588596902 }, + { -533875561, 785728077 }, + { 1621541165, 51984655 }, + { -422893115, -1691396643 }, + { 815896488, -1211166256 }, + { 805088543, -1321651331 }, + { 449771810, 605467368 }, + { 1152393492, 1712070256 }, + { 1578894260, -742865266 }, + { 1436532208, 1638322904 }, + { 542276424, -1841918730 }, + { 1594947829, -17123659 }, + { -1035059994, -1685290200 }, + { 1818890814, -1036635990 }, + { 1819281075, -727717969 }, + { 1685058759, -1432674913 }, + { 93029210, 1548091822 }, + { -1565095136, 352130972 }, + { -1775362679, 215754713 }, + { -1775249157, 14241751 }, + { -1280058093, -1023065463 }, + { -420273765, -1234735557 }, + { 563023711, -2139580305 }, + { 790921853, -262590403 }, + { -337261910, -2072273936 }, + { 498312426, -1950721390 }, + { 504904386, 1655650836 }, + { -1221200300, -559599960 }, + { 510887968, 15388698 }, + { -814689390, 866889860 }, + { 1024486167, 1382263453 }, + { 2021568998, 609049394 }, + { 97329, 1099314009 }, + { 1046072789, -582782051 }, + { 97409, -1273339005 }, + { -1480119738, -1262185308 }, + { 1611480185, 1931713481 }, + { -1386464949, 1553078977 }, + { 93832698, 1776460938 }, + { -1063412582, -586342290 }, + { -1887162396, -996920608 }, + { -55660037, 1588298435 }, + { 919780768, 1711033574 }, + { -365801095, 1719978075 }, + { 68998734, 613961768 }, + { -853695669, 1443579727 }, + { 271534758, 833533164 }, + { -770311783, -180129657 }, + { -1192532973, 1424075905 }, + { -307490664, 1525520776 }, + { 707427396, 602741290 }, + { 707432758, -761829530 }, + { -2079677721, 1783512007 }, + { -1342405573, -1316706473 }, + { -139769801, 1946219319 }, + { -1043746011, -700591459 }, + { 2080339268, 1655979682 }, + { -171664558, -1941646328 }, + { 1050986417, -1557377697 }, + { -1693683664, 1789825282 }, + { 523409530, 1121925526 }, + { 1300054961, 634478325 }, + { -2095387015, 1142993169 }, + { 1428021640, 1104520648 }, + { 94623429, 1534542921 }, + { 1436001773, -1938052175 }, + { 1711323399, 1973684065 }, + { 1734319168, -1848736516 }, + { -1658459025, -1440987069 }, + { -726947205, -751151717 }, + { -341443994, 363467698 }, + { 1540879296, 2009734114 }, + { 94756378, -858312878 }, + { 3059095, 204391461 }, + { 3059624, 1367190888 }, + { 2045107609, -778875547 }, + { 583366917, 998894949 }, + { 2123300234, 1965232394 }, + { 1983936587, -321733511 }, + { 1257201758, -97956382 }, + { -1144743963, 296519935 }, + { -1144542967, -113413047 }, + { -1144334585, -2022172587 }, + { 1066729526, -1101924344 }, + { -1598790097, 1390353317 }, + { -933236257, 1221063409 }, + { -1575287163, -1336109173 }, + { -2104481870, -2067472972 }, + { -1571725662, 1353298668 }, + { 1456441506, 1729120840 }, + { 1200628767, -1112793865 }, + { -778796102, 1409529282 }, + { 1526866730, 674734128 }, + { 1925723260, -1519126340 }, + { 1891056868, 1401987718 }, + { 1295154089, -1878475007 }, + { 498591726, 1248356124 }, + { 1755466030, -592016202 }, + { 726730162, 798638114 }, + { -1034048911, -1018587433 }, + { 252529905, 274502203 }, + { 471582113, -1065444793 }, + { -1138648591, 16333305 }, + { 305916740, 649305914 }, + { 305916742, 649305916 }, + { 305916744, 649305918 }, + { 1908328648, -1535621066 }, + { -2078972355, 1668129151 }, + { -533484654, 989925924 }, + { 1571660245, 1569882109 }, + { 1045869440, -1215753368 }, + { 1985408483, 528668503 }, + { 97513422, 304481038 }, + { 1496470781, -196667575 }, + { 1229879204, 952603248 }, + { -1722829188, 936496778 }, + { 1849912854, 1948067030 }, + { -1266285051, 1413014235 }, + { -1749787215, -1000573653 }, + { 28178745, -946369541 }, + { -505639592, -1999722522 }, + { 1598149413, -1992717673 }, + { -1779401418, -691113464 }, + { -57285700, -335089230 }, + { 98228420, 479143914 }, + { 1422845239, 999690781 }, + { 277631078, -1819763926 }, + { 115739308, 1366282552 }, + { -522149009, -690276911 }, + { 3175989, -1899491405 }, + { 718197703, -746030907 }, + { 384204160, 1840822026 }, + { -1308622549, 143803535 }, + { -217113639, -2124352573 }, + { -1580059655, -265876753 }, + { -1832205789, 1070894649 }, + { 305916741, 649305917 }, + { 936777834, 3222790 }, + { -1224598842, 200773292 }, + { -1976561211, -1506397857 }, + { -1406876421, 1675639563 }, + { -1397343301, -23994173 }, + { 1260209393, 850280505 }, + { -1035315940, 1877339384 }, + { -1381682752, 1714496074 }, + { 696727039, -1022661119 }, + { -2128719593, -803263829 }, + { -1178289187, -1903165497 }, + { 1351172108, 1181207482 }, + { -450738836, -1539025626 }, + { -966287254, -324675402 }, + { 340009023, 671063303 }, + { 124310981, -1478212975 }, + { 1501403549, -2094954543 }, + { 698310895, -1252059217 }, + { 523855532, 1266491000 }, + { 2045246801, -886280491 }, + { 583506109, -237809779 }, + { -148163128, 794356786 }, + { -132588262, -1773144852 }, + { -1666761111, 196700171 }, + { -465236267, 442289265 }, + { -1211618504, 1751045826 }, + { 2133577942, -1982036270 }, + { -1014825244, -682687162 }, + { -991829475, 1536610005 }, + { -642008142, -1709878924 }, + { 661790782, 1272768630 }, + { -1440143841, -1780802565 }, + { 569119686, 1746956556 }, + { 1404466285, -1102429027 }, + { -1616887133, -48090175 }, + { -1167640370, -1163532624 }, + { -1284735799, 1242482355 }, + { -1278649848, -1824943010 }, + { 776005741, 1814288539 }, + { 108061910, -316250604 }, + { 255101535, -1663759755 }, + { -51678842, 1658229558 }, + { -789202811, 254522515 }, + { 516382256, -132516482 }, + { 50834473, 1381010055 }, + { -975723312, 1159991980 }, + { 1908195100, -850982208 }, + { -1097452776, -110921842 }, + { 146685185, -1469578201 }, + { -1716193401, -1812555177 }, + { 193190034, -2069578888 }, + { 371156815, -852563019 }, + { 3343606, -1966748496 }, + { 825308669, -1137865085 }, + { 830965940, -586784898 }, + { 1662628660, -163828118 }, + { 1662628661, -163828117 }, + { 1662628662, -163828112 }, + { -1832205788, 1070894648 }, + { -1832205786, 1070894646 }, + { 1625090418, 181590376 }, + { -1269800768, -874975042 }, + { 429648208, -1190096326 }, + { -1832205787, 1070894647 }, + { -1832205785, 1070894645 }, + { 107868, 696029452 }, + { 997973965, -2012470695 }, + { -46188931, -702051347 }, + { -46848560, -194953424 }, + { -2066726403, -989755543 }, + { -2043730634, 1873897110 }, + { 1325935999, -1520560807 }, + { -225234813, -78533081 }, + { -202239044, -1509851560 }, + { -322501005, 1422530437 }, + { -1851058636, 1917703890 }, + { -1828062867, -1162759543 }, + { -1966381470, -1130350864 }, + { 968732481, 1391703481 }, + { 991728250, -242084766 }, + { -253819519, 621915341 }, + { -1714986849, 1827479659 }, + { -1691991080, 813023040 }, + { 179448791, -395377963 }, + { 431617507, -1167031859 }, + { 688032252, 69511070 }, + { -1059362949, -4031221 }, + { 1265861812, 1110385766 }, + { 374890416, 317398316 }, + { 1567404401, 1882709339 }, + { -1057402571, 95950017 }, + { -758925787, -1130709577 }, + { -1411620422, 1052926200 }, + { 88869913, -542577259 }, + { -2094080303, 1318558775 }, + { 843418712, -1962971928 }, + { -1569356508, -1405508498 }, + { -1569280852, 1478091698 }, + { 449769971, 1953903201 }, + { 590532217, -2097376851 }, + { 3387378, 1414245162 }, + { 1767561705, 1992974553 }, + { 106433500, 237239288 }, + { -1334615971, -1778159885 }, + { -135651869, 1722154847 }, + { -1595790889, 1850456855 }, + { -459156023, -1695367501 }, + { 106434956, -1779183908 }, + { -578028723, -1302129395 }, + { -586116979, 286193827 }, + { -1379225193, -75944661 }, + { -930579334, 649912614 }, + { 548699316, 818877484 }, + { 142147109, 1581210395 }, + { 148953073, 1903654061 }, + { 102672084, 980333378 }, + { 640562379, -1651220691 }, + { -1732316031, -1622660759 }, + { -2130280721, 756517185 }, + { -1725510067, -722241321 }, + { 1974032895, -1673693549 }, + { -225085592, -567909622 }, + { 509654999, 1898094925 }, + { 466113771, -1511285251 }, + { 2033918259, 1373971859 }, + { 2069925558, -1736356576 }, + { -1026117678, 803222026 }, + { 1987447227, -1861522751 }, + { 540154065, -544317637 }, + { 1939428458, 176787552 }, + { -288010497, -2002277461 }, + { -847065290, 1199391518 }, + { 3506021, 963906841 }, + { 649603450, 442886268 }, + { 3506418, 1414245522 }, + { 569935070, -1104881824 }, + { 113284, -1985799200 }, + { 1916127949, -277057363 }, + { -1775234707, -1978999529 }, + { -388967316, 1326180354 }, + { 2007564590, -575483084 }, + { -1705696613, 177226991 }, + { 670655301, -253079493 }, + { 1148128486, -1958316066 }, + { -141135377, 567235583 }, + { 109266897, -932201673 }, + { -527558546, 2087678962 }, + { -1745053053, -904863145 }, + { 1223860752, 573926264 }, + { -419069863, 1234880403 }, + { -1617374968, -1994909036 }, + { 2057749608, 1950721418 }, + { 24576628, -2025184684 }, + { -1659202509, 1608640313 }, + { 2107229499, -1549739227 }, + { 191795897, -765183617 }, + { -1009492144, 795371088 }, + { 2077983581, -1367281941 }, + { 378365037, 352499047 }, + { -529054135, -1199897169 }, + { -529054134, -1199897172 }, + { 486166145, -1023374709 }, + { 1628490888, 23352662 }, + { 1498516223, 1205607945 }, + { -632459882, -1647846966 }, + { -626812403, -845557339 }, + { 385802761, -1370759135 }, + { 2117976603, 121049755 }, + { 1338515426, -996185386 }, + { -1455694274, 98508942 }, + { 1579245182, 2070189026 }, + { -587434450, 1521286012 }, + { -163742043, 1542290441 }, + { -1224714193, -1832422579 }, + { 644359987, 826309791 }, + { -1962514734, -143132326 }, + { -705305612, 1153652756 }, + { -357728804, -1819233322 }, + { -698499648, -1138208076 }, + { 1213686767, -1850571427 }, + { 386382445, -855748505 }, + { 1859976884, 553887414 }, + { 960793436, 996293980 }, + { 1001265731, 2048317869 }, + { 1253290621, -1754948969 }, + { 470729623, -1293296287 }, + { 1051155022, -369760990 }, + { 865679437, -1878764039 }, + { 927253046, -1039528932 }, + { 109552593, 1796682209 }, + { -2092529553, 1230323789 }, + { 691633666, -363689972 }, + { -2055888649, 1629293099 }, + { 621575320, -41440462 }, + { -2118132208, 1602646136 }, + { -1127699509, 1540934679 }, + { -685265909, -92759291 }, + { 552706886, -1100422738 }, + { 1835797460, -1021495308 }, + { -892259869, 642482233 }, + { -1623330855, -465682601 }, + { -1616524891, 1668858301 }, + { 789892804, 171931394 }, + { -1289478934, -1583967946 }, + { -892070738, -2099697608 }, + { -891243783, -1581843485 }, + { 889398893, -1157596551 }, + { -1625468793, 1397052267 }, + { 1293049486, 1975934948 }, + { 1369769822, 559147458 }, + { 586484018, 1079279582 }, + { 110115790, 593465182 }, + { 1490499512, 1523195708 }, + { 3552619, 2019042823 }, + { 1471284746, 73681876 }, + { 456448245, -1758372725 }, + { 110547964, 795236088 }, + { 1588977225, -1667224349 }, + { 918540912, -209869746 }, + { -471874147, 1686524871 }, + { 205978836, 1723747470 }, + { -1044400758, -129230242 }, + { -2073307447, -1331212963 }, + { 435230680, 2106561762 }, + { -864578046, 223891266 }, + { 1660607208, 935692442 }, + { 260214178, -1478445584 }, + { -1847536522, 198438816 }, + { -496055048, -967648160 }, + { -1792066367, 99588025 }, + { 562888306, -956706906 }, + { -427925529, -1429456799 }, + { 995306285, 1451568081 }, + { -378017204, -1117626326 }, + { 447918618, -148794216 }, + { 313836902, 1516985844 }, + { 1175970190, -796583652 }, + { 525244071, -148229307 }, + { -1021702157, -819720157 }, + { -402507101, 671706427 }, + { -1556671423, -1183726687 }, + { 61936445, -1614955425 }, + { 112903447, -1779180711 }, + { 1817873886, -1100168350 }, + { 1824679850, -132247350 }, + { -1628526499, -1863559151 }, + { 547302405, -119235651 }, + { 1840561315, 2114754781 }, + { -460592212, -1379835144 }, + { 3655341, -151838493 }, + { 1554697726, 418081930 }, + { -1883959124, 832133926 }, + { -481416622, 1524187186 }, + { -481416621, -41896755 }, + { -481416620, -1607980696 }, + { -1151126752, 1058261682 }, + { -1926458555, 794443127 } }; //Languages phrases @@ -2633,25 +2731,25 @@ private void CmdConsoleUndo(ConsoleSystem.Arg arg) { "FILE_NOT_EXISTS", new Dictionary { - {"en", "File does not exist"}, - {"ru", "Файл не существует"}, - {"nl", "Bestand bestaat niet."} + { "en", "File does not exist" }, + { "ru", "Файл не существует" }, + { "nl", "Bestand bestaat niet." } } }, { "FILE_BROKEN", new Dictionary { - {"en", "Something went wrong during pasting because of a error in the file."}, - {"ru", "Файл поврежден, вставка невозможна"}, - {"nl", "Er is iets misgegaan tijdens het plakken door een beschadigd bestand."} + { "en", "Something went wrong during pasting because of a error in the file." }, + { "ru", "Файл поврежден, вставка невозможна" }, + { "nl", "Er is iets misgegaan tijdens het plakken door een beschadigd bestand." } } }, { "NO_ACCESS", new Dictionary { - {"en", "You don't have the permissions to use this command"}, - {"ru", "У вас нет прав доступа к данной команде"}, - {"nl", "U heeft geen toestemming/permissie om dit commando te gebruiken."} + { "en", "You don't have the permissions to use this command" }, + { "ru", "У вас нет прав доступа к данной команде" }, + { "nl", "U heeft geen toestemming/permissie om dit commando te gebruiken." } } }, { @@ -2663,7 +2761,9 @@ private void CmdConsoleUndo(ConsoleSystem.Arg arg) "vending - Information and sellings in vending machine\n" + "stability - Wether or not to disable stability\n" + "deployables - Wether or not to copy deployables\n" + - "auth - Wether or not to copy lock and cupboard whitelists" + "auth - Wether or not to copy lock and cupboard whitelists\n" + + "position - Override position\n" + + "rotation - Override rotation" }, { "ru", "Синтаксис: /pasteback <Название Объекта> <опция значение>\n" + @@ -2691,7 +2791,9 @@ private void CmdConsoleUndo(ConsoleSystem.Arg arg) "deployables true/false - false to remove deployables\n" + "inventories true/false - false to ignore inventories\n" + "vending - Information and sellings in vending machine\n" + - "stability - Wether or not to disable stability on the building" + "stability - Wether or not to disable stability on the building\n" + + "position - Override position\n" + + "rotation - Override rotation" }, { "ru", "Синтаксис: /paste or /pasteback <Название Объекта> <опция значение>\n" + @@ -2716,17 +2818,17 @@ private void CmdConsoleUndo(ConsoleSystem.Arg arg) { "PASTEBACK_SUCCESS", new Dictionary { - {"en", "You've successfully placed back the structure"}, - {"ru", "Постройка успешно вставлена на старое место"}, - {"nl", "Het gebouw is succesvol teruggeplaatst."} + { "en", "You've successfully placed back the structure" }, + { "ru", "Постройка успешно вставлена на старое место" }, + { "nl", "Het gebouw is succesvol teruggeplaatst." } } }, { "PASTE_SUCCESS", new Dictionary { - {"en", "You've successfully pasted the structure"}, - {"ru", "Постройка успешно вставлена"}, - {"nl", "Het gebouw is succesvol geplaatst."} + { "en", "You've successfully pasted the structure" }, + { "ru", "Постройка успешно вставлена" }, + { "nl", "Het gebouw is succesvol geplaatst." } } }, { @@ -2758,99 +2860,111 @@ private void CmdConsoleUndo(ConsoleSystem.Arg arg) { "NO_ENTITY_RAY", new Dictionary { - {"en", "Couldn't ray something valid in front of you"}, - {"ru", "Не удалось найти какой-либо объект перед вами"}, - {"nl", "U kijkt niet naar een geschikt object om een kopie op te starten."} + { "en", "Couldn't ray something valid in front of you" }, + { "ru", "Не удалось найти какой-либо объект перед вами" }, + { "nl", "U kijkt niet naar een geschikt object om een kopie op te starten." } } }, { "COPY_SUCCESS", new Dictionary { - {"en", "The structure was successfully copied as {0}"}, - {"ru", "Постройка успешно скопирована под названием: {0}"}, - {"nl", "Gebouw is succesvol gekopieërd"} + { "en", "The structure was successfully copied as {0}" }, + { "ru", "Постройка успешно скопирована под названием: {0}" }, + { "nl", "Gebouw is succesvol gekopieërd" } } }, { "NO_PASTED_STRUCTURE", new Dictionary { - {"en", "You must paste structure before undoing it"}, - {"ru", "Вы должны вставить постройку перед тем, как отменить действие"}, - {"nl", "U moet eerst een gebouw terugplaatsen alvorens deze ongedaan gemaakt kan worden (duhh)"} + { "en", "You must paste structure before undoing it" }, + { "ru", "Вы должны вставить постройку перед тем, как отменить действие" }, + { + "nl", + "U moet eerst een gebouw terugplaatsen alvorens deze ongedaan gemaakt kan worden (duhh)" + } } }, { "UNDO_SUCCESS", new Dictionary { - {"en", "You've successfully undid what you pasted"}, - {"ru", "Вы успешно снесли вставленную постройку"}, - {"nl", "Laatse geplaatste gebouw is succesvol ongedaan gemaakt."} + { "en", "You've successfully undid what you pasted" }, + { "ru", "Вы успешно снесли вставленную постройку" }, + { "nl", "Laatse geplaatste gebouw is succesvol ongedaan gemaakt." } } }, { "NOT_FOUND_PLAYER", new Dictionary { - {"en", "Couldn't find the player"}, - {"ru", "Не удалось найти игрока"}, - {"nl", "Speler niet gevonden."} + { "en", "Couldn't find the player" }, + { "ru", "Не удалось найти игрока" }, + { "nl", "Speler niet gevonden." } } }, { "SYNTAX_BOOL", new Dictionary { - {"en", "Option {0} must be true/false"}, - {"ru", "Опция {0} принимает значения true/false"}, - {"nl", "Optie {0} moet true of false zijn"} + { "en", "Option {0} must be true/false" }, + { "ru", "Опция {0} принимает значения true/false" }, + { "nl", "Optie {0} moet true of false zijn" } + } + }, + { + "SYNTAX_FLOAT", new Dictionary + { + { "en", "Option {0} must be a decimal" }, + { "ru", "Опция {0} принимает только числовые значения с точкой" }, + { "nl", "Optie {0} moet een decimal zijn" } } }, { "SYNTAX_HEIGHT", new Dictionary { - {"en", "Option height must be a number"}, - {"ru", "Опция height принимает только числовые значения"}, - {"nl", "De optie height accepteert alleen nummers"} + { "en", "Option height must be a number" }, + { "ru", "Опция height принимает только числовые значения" }, + { "nl", "De optie height accepteert alleen nummers" } } }, { "SYNTAX_BLOCKCOLLISION", new Dictionary { - {"en", "Option blockcollision must be a number, 0 will deactivate the option"}, + { "en", "Option blockcollision must be a number, 0 will deactivate the option" }, { "ru", "Опция blockcollision принимает только числовые значения, 0 позволяет отключить проверку" }, - {"nl", "Optie blockcollision accepteert alleen nummers, 0 schakelt deze functionaliteit uit"} + { "nl", "Optie blockcollision accepteert alleen nummers, 0 schakelt deze functionaliteit uit" } } }, { "SYNTAX_RADIUS", new Dictionary { - {"en", "Option radius must be a number"}, - {"ru", "Опция radius принимает только числовые значения"}, - {"nl", "Optie height accepteert alleen nummers"} + { "en", "Option radius must be a number" }, + { "ru", "Опция radius принимает только числовые значения" }, + { "nl", "Optie height accepteert alleen nummers" } } }, { "BLOCKING_PASTE", new Dictionary { - {"en", "Something is blocking the paste"}, - {"ru", "Что-то препятствует вставке"}, - {"nl", "Iets blokkeert het plaatsen van dit gebouw"} + { "en", "Something is blocking the paste" }, + { "ru", "Что-то препятствует вставке" }, + { "nl", "Iets blokkeert het plaatsen van dit gebouw" } } }, { "AVAILABLE_STRUCTURES", new Dictionary { - {"ru", "Доступные постройки:"}, - {"en", "Available structures:"}, - {"nl", "Beschikbare bestanden om te plaatsen zijn:"} + { "ru", "Доступные постройки:" }, + { "en", "Available structures:" }, + { "nl", "Beschikbare bestanden om te plaatsen zijn:" } } } }; public class CopyData { - public BasePlayer Player; + public IPlayer Player; + public BasePlayer BasePlayer; public Stack CheckFrom = new Stack(); public HashSet HouseList = new HashSet(); public List RawData = new List(); @@ -2882,7 +2996,8 @@ public class PasteData public Dictionary> IoEntities = new Dictionary>(); - public BasePlayer Player; + public IPlayer Player; + public BasePlayer BasePlayer; public List StabilityEntities = new List(); public Quaternion QuaternionRotation; public Action Callback; diff --git a/defaults/oxide/plugins/EntityCleanup.cs b/defaults/oxide/plugins/EntityCleanup.cs index c1ca953d..585def85 100644 --- a/defaults/oxide/plugins/EntityCleanup.cs +++ b/defaults/oxide/plugins/EntityCleanup.cs @@ -1,339 +1,366 @@ -//#define DEBUG //This line enables debug output -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Newtonsoft.Json; -using UnityEngine; -using Arg = ConsoleSystem.Arg; - -namespace Oxide.Plugins -{ - [Info("Entity Cleanup", "2CHEVSKII", "3.0.3")] - [Description("Easy way to cleanup your server from unnecessary entities")] - public class EntityCleanup : RustPlugin - { - #region Fields - - - private PluginSettings Settings { get; set; } - private const string PERMISSION = "entitycleanup.use"; - - private HashSet Deployables { get; } = new HashSet(); - private Timer ScheduleTimer { get; set; } - - - #endregion - - #region Config - - - private class PluginSettings - { - [JsonProperty(PropertyName = "Scheduled cleanup seconds (x <= 0 to disable)")] - internal int ScheduledCleanup { get; set; } - [JsonProperty(PropertyName = "Scheduled cleanup building blocks")] - internal bool ScheduledCleanupBuildings { get; set; } - [JsonProperty(PropertyName = "Scheduled cleanup deployables")] - internal bool ScheduledCleanupDeployables { get; set; } - [JsonProperty(PropertyName = "Scheduled cleanup outside cupboard range")] - internal bool ScheduledCleanupOutsideCupRange { get; set; } - [JsonProperty(PropertyName = "Scheduled cleanup entities with hp less than specified (x = [0.0-1.0])")] - internal float ScheduledCleanupDamaged { get; set; } - } - - protected override void LoadConfig() - { - base.LoadConfig(); - try - { - Settings = Config.ReadObject(); - if(Settings == null) - throw new JsonException("Can't read config..."); - else - Puts("Configuration loaded..."); - } - catch - { - LoadDefaultConfig(); - } - } - - protected override void LoadDefaultConfig() - { - Config.Clear(); - Settings = GetDefaultSettings(); - SaveConfig(); - PrintWarning("Default configuration created..."); - } - - protected override void SaveConfig() => Config.WriteObject(Settings, true); - - private PluginSettings GetDefaultSettings() => new PluginSettings { - ScheduledCleanup = 3600, - ScheduledCleanupBuildings = true, - ScheduledCleanupDeployables = true, - ScheduledCleanupOutsideCupRange = true, - ScheduledCleanupDamaged = 0f - }; - - - #endregion - - #region LangAPI - - - private const string mhelp = "Help message"; - private const string mnoperm = "No permissions"; - private const string mannounce = "Announcement"; - - private Dictionary DefaultMessages_EN { get; } = new Dictionary - { - { mhelp, "Usage: cleanup () (all)" }, - { mnoperm, "You have no access to that command" }, - { mannounce, "Server is cleaning up {0} entities..." } - }; - - protected override void LoadDefaultMessages() => lang.RegisterMessages(DefaultMessages_EN, this, "en"); - - private string GetReply(BasePlayer player, string message, params object[] args) => string.Format(lang.GetMessage(message, this, player?.UserIDString), args); - - - #endregion - - #region Hooks - - - private void Init() - { - permission.RegisterPermission(PERMISSION, this); - permission.GrantGroupPermission("admin", PERMISSION, this); - cmd.AddConsoleCommand("cleanup", this, delegate (Arg arg) - { - return CommandHandler(arg); - }); - InitDeployables(); - } - - private void OnServerInitialized() - { - if(Settings.ScheduledCleanup > 0) - StartScheduleTimer(); - } - - - #endregion - - #region Commands - - - private bool CommandHandler(Arg arg) - { - BasePlayer player = arg.Player(); - - if(player && !permission.UserHasPermission(player.UserIDString, PERMISSION)) - arg.ReplyWith(GetReply(player, mnoperm)); - else if(!arg.HasArgs()) - ScheduledCleanup(); - else - { - switch(arg.Args.Length) - { - case 1: - switch(arg.Args[0].ToLower()) - { - case "all": - ServerMgr.Instance.StartCoroutine(CollectData(new List())); - break; - default: - ServerMgr.Instance.StartCoroutine(CollectData(new List(), false, arg.Args[0])); - break; - } - break; - case 2: - switch(arg.Args[0].ToLower()) - { - case "all": - if(arg.Args[1].ToLower() == "all") - ServerMgr.Instance.StartCoroutine(CollectData(new List(), true)); - else - arg.ReplyWith(GetReply(player, mhelp)); - break; - default: - if(arg.Args[1].ToLower() == "all") - ServerMgr.Instance.StartCoroutine(CollectData(new List(), true, arg.Args[1])); - else - arg.ReplyWith(GetReply(player, mhelp)); - break; - } - break; - default: - arg.ReplyWith(GetReply(player, mhelp)); - break; - } - } - return true; - } - - - #endregion - - #region Core - - - private IEnumerator CollectData(List entities, bool all = false, string name = null) - { - IEnumerator enumerator = BaseNetworkable.serverEntities.GetEnumerator(); -#if(DEBUG) - Puts("Started data collect"); -#endif - while(enumerator.MoveNext()) - { - BaseEntity baseEntity = enumerator.Current as BaseEntity; - - var parentEntity = baseEntity?.GetParentEntity(); - - while(parentEntity != null && !parentEntity.IsDestroyed) - { - baseEntity = parentEntity; - parentEntity = baseEntity?.GetParentEntity(); - } - - if(baseEntity == null) - { -#if(DEBUG) - Puts("Skipped not a baseEntity"); -#endif - yield return new WaitForEndOfFrame(); - continue; - } - - if(baseEntity.OwnerID == 0) - { - #if(DEBUG) - Puts("Skipped baseEntity without ownerid"); - #endif - yield return new WaitForEndOfFrame(); - - continue; - } - - if(baseEntity.GetBuildingPrivilege() != null && !all && (baseEntity.Health() / baseEntity.MaxHealth()) > Settings.ScheduledCleanupDamaged) - { -#if(DEBUG) - Puts("Skipped BE with BP or HP"); -#endif - yield return new WaitForEndOfFrame(); - continue; - } - - if((name == null || name.ToLower() == "buildings") && baseEntity is StabilityEntity) - { -#if(DEBUG) - Puts("Added building block"); -#endif - entities.Add(baseEntity); - yield return new WaitForEndOfFrame(); - continue; - } - - if(((name == null || name.ToLower() == "deployables") && Deployables.Contains(baseEntity.gameObject.name)) - || (name != null && baseEntity.gameObject.name.Contains(name, CompareOptions.IgnoreCase))) - { -#if(DEBUG) - Puts("Added deployable"); -#endif - entities.Add(baseEntity); - yield return new WaitForEndOfFrame(); - continue; - } - } - - if(entities.Count < 1) - { -#if(DEBUG) - Puts("Attempting to clean, but nothing to be cleaned"); -#endif - yield break; - } - - ServerMgr.Instance.StartCoroutine(Cleanup(entities)); - } - - private IEnumerator Cleanup(List entities) - { - Server.Broadcast(GetReply(null, mannounce, entities.Count)); - - for(int i = 0; i < entities.Count; i++) - { - if(!entities[i].IsDestroyed) - { - entities[i].Kill(BaseNetworkable.DestroyMode.None); - yield return new WaitForSeconds(0.05f); - } - } -#if(DEBUG) - Puts($"Cleanup finished, {entities.Count} entities cleaned."); -#endif - } - - - #endregion - - #region Utility - - - private void InitDeployables() - { - IEnumerable deps = from def in ItemManager.GetItemDefinitions() - where def.GetComponent() != null - select def.GetComponent(); - - Puts($"Found {deps.Count()} deployables definitions"); - - foreach(ItemModDeployable dep in deps) - if(!Deployables.Contains(dep.entityPrefab.resourcePath)) - Deployables.Add(dep.entityPrefab.resourcePath); - } - - private void StartScheduleTimer() - { - if(ScheduleTimer != null && !ScheduleTimer.Destroyed) - ScheduleTimer.Destroy(); - ScheduleTimer = timer.Once(Settings.ScheduledCleanup, () => ScheduledCleanup()); - } - - private void ScheduledCleanup() - { -#if(DEBUG) - Puts("Scheduled CU triggered"); -#endif - if(Settings.ScheduledCleanupBuildings && Settings.ScheduledCleanupDeployables) - { -#if(DEBUG) - Puts("Scheduled CU all"); -#endif - ServerMgr.Instance.StartCoroutine(CollectData(new List())); - } - else if(Settings.ScheduledCleanupBuildings) - { -#if(DEBUG) - Puts("Scheduled CU building"); -#endif - ServerMgr.Instance.StartCoroutine(CollectData(new List(), false, "buildings")); - } - else if(Settings.ScheduledCleanupDeployables) - { -#if(DEBUG) - Puts("Scheduled CU deployable"); -#endif - ServerMgr.Instance.StartCoroutine(CollectData(new List(), false, "deployables")); - if(Settings.ScheduledCleanup > 0) - StartScheduleTimer(); - } - } - - - #endregion - } -} \ No newline at end of file +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using Facepunch; + +using Oxide.Core.Libraries.Covalence; + +using UnityEngine; + +namespace Oxide.Plugins +{ + [Info("Entity Cleanup", "2CHEVSKII", "4.0.0")] + [Description("Easy way to cleanup your server from unnecessary entities.")] + public class EntityCleanup : CovalencePlugin + { + const string PERMISSION_USE = "entitycleanup.use"; + + const string M_PREFIX = "Chat prefix", + M_NO_PERMISSION = "No permission", + M_INVALID_USAGE = "Invalid usage", + M_CLEANUP_STARTED = "Cleanup started", + M_CLEANUP_FINISHED = "Cleanup finished", + M_CLEANUP_RUNNING = "Cleanup running"; + + PluginSettings settings; + CleanupHandler handler; + + #region Command handler + + void CommandHandler(IPlayer player, string command, string[] args) + { + if (!player.HasPermission(PERMISSION_USE)) + { + Message(player, M_NO_PERMISSION); + return; + } + + switch (args.Length) + { + case 0: + if (!handler.TryStartCleanup()) + { + Message(player, M_CLEANUP_RUNNING); + } + + return; + default: + Message(player, M_INVALID_USAGE); + break; + } + } + + #endregion + + #region Oxide hooks + + void Init() + { + permission.RegisterPermission(PERMISSION_USE, this); + AddCovalenceCommand("entitycleanup", "CommandHandler"); + } + + void OnServerInitialized() + { + handler = ServerMgr.Instance.gameObject.AddComponent(); + handler.Init(this); + } + + void Unload() + { + handler.Shutdown(); + } + + #endregion + + #region LangAPI + + protected override void LoadDefaultMessages() + { + lang.RegisterMessages( + new Dictionary { + [M_PREFIX] = "[Entity Cleanup] ", + [M_NO_PERMISSION] = "You have no access to this command", + [M_INVALID_USAGE] = "Invalid command usage", + [M_CLEANUP_STARTED] = "Cleaning up old server entities...", + [M_CLEANUP_FINISHED] = "Cleanup completed, purged {0} old entities", + [M_CLEANUP_RUNNING] = "Cleanup is already running, wait until it completes" + }, + this + ); + } + + string GetMessage(IPlayer player, string langKey) + { + return lang.GetMessage(langKey, this, player.Id); + } + + void Message(IPlayer player, string langKey, params object[] args) + { + var prefix = GetMessage(player, M_PREFIX); + var format = GetMessage(player, langKey); + var message = string.Format(format, args); + + player.Message(prefix + message); + } + + void Announce(string langKey, params object[] args) + { + foreach (IPlayer player in players.Connected) + { + Message(player, langKey, args); + } + } + + #endregion + + #region Configuration load + + protected override void LoadDefaultConfig() + { + settings = PluginSettings.Default; + SaveConfig(); + } + + protected override void LoadConfig() + { + base.LoadConfig(); + + try + { + settings = Config.ReadObject(); + + if (settings == null) + { + throw new Exception("Configuration is null"); + } + } + catch (Exception e) + { + LogError("Could not read configuration file:\n{0}", e.Message); + LoadDefaultConfig(); + } + } + + protected override void SaveConfig() + { + Config.WriteObject(settings); + } + + #endregion + + #region Nested types + + #region CleanupHandler + + class CleanupHandler : MonoBehaviour + { + PluginSettings settings; + List entityList; + Coroutine cleanupRoutine; + HashSet deployables; + + bool IsCleanupRunning => cleanupRoutine != null; + + event Action OnCleanupStarted; + event Action OnCleanupComplete; + + public void Init(EntityCleanup plugin) + { + settings = plugin.settings; + + OnCleanupStarted = () => + { + plugin.Announce(M_CLEANUP_STARTED); + }; + + OnCleanupComplete = purgedEntityCount => + { + plugin.Announce(M_CLEANUP_FINISHED, purgedEntityCount); + }; + + entityList = Pool.GetList(); + deployables = Pool.Get>(); + + var modDeployables = from itemDef in ItemManager.GetItemDefinitions() + group itemDef by itemDef.GetComponent() + into deps where deps.Key != null select deps.Key; + + foreach (var prefab in from depl in modDeployables select depl.entityPrefab.resourcePath) + { + deployables.Add(prefab); + } + } + + public void Shutdown() + { + CancelInvoke(); + + if (IsCleanupRunning) + { + StopCoroutine(cleanupRoutine); + cleanupRoutine = null; + } + + Pool.FreeList(ref entityList); + deployables.Clear(); + Pool.Free(ref deployables); + Destroy(this); + } + + public bool TryStartCleanup() + { + if (IsCleanupRunning) + { + return false; + } + + StartCleanup(); + InitializeTimedCleanup(); + + return true; + } + + #region Unity messages + + void Start() + { + InitializeTimedCleanup(); + } + + #endregion + + void InitializeTimedCleanup() + { + CancelInvoke(nameof(StartCleanup)); + + if (settings.Interval > 0) + { + InvokeRepeating(nameof(StartCleanup), settings.Interval, settings.Interval); + } + } + + void StartCleanup() + { + if (IsCleanupRunning) + { + return; + } + + cleanupRoutine = StartCoroutine(Cleanup()); + } + + IEnumerator Cleanup() + { + entityList.AddRange(BaseNetworkable.serverEntities); + int cleanedCount = 0; + + OnCleanupStarted(); + + for (int i = 0; i < entityList.Count; i++) + { + var entity = entityList[i] as BaseEntity; + + if (entity && IsCleanupCandidate(entity)) + { + entity.Kill(BaseNetworkable.DestroyMode.Gib); + cleanedCount++; + } + + yield return new WaitForEndOfFrame(); + } + + OnCleanupComplete(cleanedCount); + + cleanupRoutine = null; + } + + bool IsCleanupCandidate(BaseEntity entity) + { + if (entity.parentEntity.IsSet()) + { + return false; + } + + if (entity.OwnerID == 0ul) + { + return false; + } + + if ( + settings.Whitelist.Contains(entity.ShortPrefabName) || + settings.Whitelist.Contains(entity.PrefabName) + ) + { + return false; + } + + if (entity is StabilityEntity && settings.CleanupBuildings || + deployables.Contains(entity.gameObject.name) && settings.CleanupDeployables) + { + BuildingPrivlidge buildingPrivilege = entity.GetBuildingPrivilege(); + bool hasBuildingPrivilege = buildingPrivilege != null; + + if (hasBuildingPrivilege) + { + if (!settings.RemoveInsidePrivilege) + { + return false; + } + + if (settings.CheckOwnerIdPrivilegeAuthorized && buildingPrivilege.IsAuthed(entity.OwnerID)) + { + return false; + } + + return settings.InsideHealthFractionTheshold == 0f || entity.Health() / entity.MaxHealth() < settings.InsideHealthFractionTheshold; + } + + if (!settings.RemoveOutsidePrivilege) + { + return false; + } + + return settings.OutsideHealthFractionTheshold == 0f || entity.Health() / entity.MaxHealth() < settings.OutsideHealthFractionTheshold;; + } + + return false; + } + } + + #endregion + + #region PluginSettings + + class PluginSettings + { + public static PluginSettings Default => new PluginSettings { + Interval = 3600, + CleanupBuildings = true, + CleanupDeployables = true, + RemoveOutsidePrivilege = true, + OutsideHealthFractionTheshold = 0.2f, + RemoveInsidePrivilege = false, + InsideHealthFractionTheshold = 0f, + Whitelist = Array.Empty(), + CheckOwnerIdPrivilegeAuthorized = true + }; + + public int Interval { get; set; } + public bool CleanupBuildings { get; set; } + public bool CleanupDeployables { get; set; } + public bool RemoveOutsidePrivilege { get; set; } + public float OutsideHealthFractionTheshold { get; set; } + public bool RemoveInsidePrivilege { get; set; } + public float InsideHealthFractionTheshold { get; set; } + public string[] Whitelist { get; set; } + public bool CheckOwnerIdPrivilegeAuthorized { get; set; } + } + + #endregion + + #endregion + } +} diff --git a/defaults/oxide/plugins/MonumentsRecycler.cs b/defaults/oxide/plugins/MonumentsRecycler.cs index a3a7a4ff..03aadccc 100644 --- a/defaults/oxide/plugins/MonumentsRecycler.cs +++ b/defaults/oxide/plugins/MonumentsRecycler.cs @@ -1,356 +1,356 @@ -using UnityEngine; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using Rust; - -namespace Oxide.Plugins -{ - [Info("Monuments Recycler", "Dana", "0.2.4")] - [Description("Adds recyclers to monuments including the cargo ship.")] - internal class MonumentsRecycler : RustPlugin - { - private bool _serverInitialized = false; - private const string RecyclerPrefab = "assets/bundled/prefabs/static/recycler_static.prefab"; - private const string SmallOilRigKey = "oil_rig_small"; - private const string LargeOilRigKey = "large_oil_rig"; - private const string DomeKey = "dome_monument_name"; - const string FishingVillageLargePrefab = "assets/bundled/prefabs/autospawn/monument/fishing_village/fishing_village_a.prefab"; - const string FishingVillageSmallBPrefab = "assets/bundled/prefabs/autospawn/monument/fishing_village/fishing_village_b.prefab"; - const string FishingVillageSmallAPrefab = "assets/bundled/prefabs/autospawn/monument/fishing_village/fishing_village_c.prefab"; - - private List _recyclers = new List(); - private SpawnData _domeSpawnData = new SpawnData(new Vector3(19.9f, 32.23f, 16.57f), new Vector3(0, 235, 0)); - private readonly Dictionary _smallOilRigRecyclerPositions = new Dictionary - { - {"3", new SpawnData(new Vector3(21.01f, 22.5f, -30.8f),new Vector3(0, 180, 0) )}, - {"4",new SpawnData(new Vector3(32.1f, 27f, -34.5f),new Vector3(0, 270, 0)) } - }; - private readonly Dictionary _largeOilRigRecyclerPositions = new Dictionary - { - {"4", new SpawnData(new Vector3(20.57f, 27.1f, -44.52f),new Vector3(0, 0, 0) )}, - {"6",new SpawnData(new Vector3(-13.6f, 36.1f, -3.4f),new Vector3(0, 180, 0)) } - }; - private readonly Dictionary _fishingVillageRecyclerPositions = new Dictionary - { - {"smallB", new SpawnData(new Vector3(-21f,0.3f,6.5f),new Vector3(0f,130f,0f)) }, - {"smallA", new SpawnData(new Vector3(-8.5f,2f,16.06067f),new Vector3(0,90f,0)) }, - {"large", new SpawnData(new Vector3(-20.7f,0.2f,-9.6f), new Vector3(0,45f,0)) } - }; - private Configuration _config; - - public class Configuration - { - [JsonProperty(PropertyName = "Cargo Ship - Recycler Position - Front")] - public bool RecyclerPositionFront = true; - - [JsonProperty(PropertyName = "Cargo Ship - Recycler Position - Back")] - public bool RecyclerPositionBack = true; - - [JsonProperty(PropertyName = "Cargo Ship - Recycler Position - Bottom")] - public bool RecyclerPositionBottom = true; - - [JsonProperty(PropertyName = "Large Oil Rig - Recycler Position - Level 4")] - public bool LargeOilRigRecyclerPositionLevel4 = true; - - [JsonProperty(PropertyName = "Large Oil Rig - Recycler Position - Level 6")] - public bool LargeOilRigRecyclerPositionLevel6 = true; - - [JsonProperty(PropertyName = "Small Oil Rig - Recycler Position - Level 3")] - public bool SmallOilRigRecyclerPositionLevel3 = true; - - [JsonProperty(PropertyName = "Small Oil Rig - Recycler Position - Level 4")] - public bool SmallOilRigRecyclerPositionLevel4 = true; - - [JsonProperty(PropertyName = "Fishing Village - Recycler - Large")] - public bool FishingVillageRecyclerLarge { get; set; } = true; - - [JsonProperty(PropertyName = "Fishing Village - Recycler - Small A")] - public bool FishingVillageRecyclerSmallA { get; set; } = true; - - [JsonProperty(PropertyName = "Fishing Village - Recycler - Small B")] - public bool FishingVillageRecyclerSmallB { get; set; } = true; - - [JsonProperty(PropertyName = "Dome - Recycler")] - public bool DomeRecycler = true; - } - - protected override void LoadConfig() - { - base.LoadConfig(); - try - { - _config = Config.ReadObject(); - //if (_config != null && _serverInitialized) - //{ - // ShowRecyclers(); - //} - } - catch - { - PrintError("Loading default config! Error loading your config, it's corrupt!"); - LoadDefaultConfig(); - } - - if (_config == null) - { - LoadDefaultConfig(); - } - - SaveConfig(); - var pluginEnabled = _config.RecyclerPositionFront || _config.RecyclerPositionBack || _config.RecyclerPositionBottom || - _config.LargeOilRigRecyclerPositionLevel4 || _config.LargeOilRigRecyclerPositionLevel6 || - _config.SmallOilRigRecyclerPositionLevel3 || _config.SmallOilRigRecyclerPositionLevel4 || - _config.DomeRecycler || _config.FishingVillageRecyclerLarge || _config.FishingVillageRecyclerSmallA - || _config.FishingVillageRecyclerSmallB; - if (!pluginEnabled) - { - PrintWarning("No Recycler Position Found"); - Unsubscribe(nameof(OnEntitySpawned)); - } - } - - protected override void LoadDefaultConfig() => _config = new Configuration(); - - protected override void SaveConfig() => Config.WriteObject(_config); - - Tuple GetCargoFrontPosition(Vector3 position, Quaternion rotation) => Tuple.Create(new Vector3(position.x - .2172264f, position.y + 9.574753f, position.z + 79.05922f), new Quaternion(rotation.x, rotation.y + 180, rotation.z, rotation.w)); - - Tuple GetCargoBackPosition(Vector3 position, Quaternion rotation) => Tuple.Create(new Vector3(position.x + .2407f, position.y + 9.5f, position.z - 36.87305f), rotation); - - Tuple GetCargoBottomPosition(Vector3 position, Quaternion rotation) => Tuple.Create(new Vector3(position.x, position.y + .57767f, position.z + 10f), new Quaternion(rotation.x, rotation.y + 180, rotation.z, rotation.w)); - - private void OnEntitySpawned(CargoShip ship) - { - if (ship == null) return; - Vector3 pos = ship.transform.position; - Quaternion rot = new Quaternion(); - Timer thisTimer = null; - - thisTimer = timer.Every(0.01f, () => - { - if (ship == null || ship.IsDestroyed) - { - thisTimer.Destroy(); - return; - } - - ship.transform.position = pos; - ship.transform.rotation = rot; - }); - - timer.Once(3, () => - { - if (ship == null || ship.IsDestroyed) - { - return; - } - - if (_config.RecyclerPositionFront) - { - Vector3 position = ship.transform.position; - Quaternion rotation = ship.transform.rotation; - SpawnRecycler(ship, GetCargoFrontPosition(position, rotation)); - } - - if (_config.RecyclerPositionBack) - { - Vector3 position = ship.transform.position; - Quaternion rotation = ship.transform.rotation; - SpawnRecycler(ship, GetCargoBackPosition(position, rotation)); - } - - if (_config.RecyclerPositionBottom) - { - Vector3 position = ship.transform.position; - Quaternion rotation = ship.transform.rotation; - SpawnRecycler(ship, GetCargoBottomPosition(position, rotation)); - } - - thisTimer.Destroy(); - }); - } - - private void OnServerInitialized() - { - _serverInitialized = true; - ShowRecyclers(); - } - private void ShowRecyclers() - { - var oilRigEnabled = _config.LargeOilRigRecyclerPositionLevel4 || _config.LargeOilRigRecyclerPositionLevel6 || - _config.SmallOilRigRecyclerPositionLevel3 || _config.SmallOilRigRecyclerPositionLevel4; - var fishingVillageEnabled = _config.FishingVillageRecyclerLarge || _config.FishingVillageRecyclerSmallA || _config.FishingVillageRecyclerSmallB; - if (oilRigEnabled) - { - - SpawnData spawnData; - var monuments = TerrainMeta.Path.Monuments?.Where(x => x.shouldDisplayOnMap && x.displayPhrase.english.Contains("Oil Rig")).ToList() ?? new List(); - foreach (var monument in monuments) - { - if (monument.displayPhrase?.token == SmallOilRigKey) - { - if (_config.SmallOilRigRecyclerPositionLevel3) - { - if (_smallOilRigRecyclerPositions.TryGetValue("3", out spawnData)) - { - SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation); - } - } - if (_config.SmallOilRigRecyclerPositionLevel4) - { - if (_smallOilRigRecyclerPositions.TryGetValue("4", out spawnData)) - { - SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation); - } - } - } - else if (monument.displayPhrase?.token == LargeOilRigKey) - { - if (_config.LargeOilRigRecyclerPositionLevel4) - { - if (_largeOilRigRecyclerPositions.TryGetValue("4", out spawnData)) - { - SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation); - } - } - if (_config.LargeOilRigRecyclerPositionLevel6) - { - if (_largeOilRigRecyclerPositions.TryGetValue("6", out spawnData)) - { - SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation); - } - } - } - } - } - - if (_config.DomeRecycler) - { - var monuments = TerrainMeta.Path.Monuments?.Where(x => x.shouldDisplayOnMap && x.displayPhrase.english.Contains("Dome")).ToList() ?? new List(); - foreach (var monument in monuments) - { - if (monument.displayPhrase?.token == DomeKey) - { - SpawnRecycler(monument.transform, _domeSpawnData.Position, _domeSpawnData.Rotation); - } - } - } - if (fishingVillageEnabled) - { - SpawnData spawnData; - var monuments = TerrainMeta.Path.Monuments?.Where(x => x.shouldDisplayOnMap && x.displayPhrase.english.ToLower().Contains("fishing")).ToList() ?? new List(); - foreach (var monument in monuments) - { - if (monument.name == FishingVillageLargePrefab) - { - if (_config.FishingVillageRecyclerLarge) - { - if (_fishingVillageRecyclerPositions.TryGetValue("large", out spawnData)) - { - SpawnFishingVillageRecycler(monument.transform, spawnData.Position, spawnData.Rotation); - } - } - } - else if (monument.name == FishingVillageSmallAPrefab) - { - if (_config.FishingVillageRecyclerSmallA) - { - if (_fishingVillageRecyclerPositions.TryGetValue("smallA", out spawnData)) - { - SpawnFishingVillageRecycler(monument.transform, spawnData.Position, spawnData.Rotation); - } - } - } - else if (monument.name == FishingVillageSmallBPrefab) - { - if (_config.FishingVillageRecyclerSmallB) - { - if (_fishingVillageRecyclerPositions.TryGetValue("smallB", out spawnData)) - { - SpawnFishingVillageRecycler(monument.transform, spawnData.Position, spawnData.Rotation); - } - } - } - } - } - } - - private void SpawnFishingVillageRecycler(Transform objTransform, Vector3 spawnOffset, Vector3 rotationOffset) - { - - var mtx = objTransform.localToWorldMatrix; - var finalPos = mtx.MultiplyPoint3x4(spawnOffset); - var rotation = mtx.rotation * Quaternion.Euler(rotationOffset); - var entity = GameManager.server.CreateEntity(RecyclerPrefab, finalPos, rotation); - if (entity != null) - { - entity.Spawn(); - _recyclers.Add(entity); - } - } - private void SpawnRecycler(Transform objTransform, Vector3 spawnOffset, Vector3 rotationOffset) - { - - var mtx = objTransform.localToWorldMatrix; - var finalPos = mtx.MultiplyPoint3x4(spawnOffset); - var oilRot = mtx.rotation * Quaternion.Euler(rotationOffset); - if (!GamePhysics.CheckSphere(finalPos, .1f, Layers.Server.Deployed, QueryTriggerInteraction.Ignore)) - { - var entity = GameManager.server.CreateEntity(RecyclerPrefab, finalPos, oilRot); - if (entity != null) - { - entity.Spawn(); - _recyclers.Add(entity); - } - } - } - private void SpawnRecycler(CargoShip ship, Tuple selectedTransform) - { - BaseEntity rec = GameManager.server.CreateEntity(RecyclerPrefab, selectedTransform.Item1, selectedTransform.Item2, true); - if (rec != null) - { - rec.enableSaving = true; - rec.Spawn(); - rec.SetParent(ship, true, true); - Rigidbody comp = rec.GetComponent(); - if (comp) - { - comp.isKinematic = true; - } - - _recyclers.Add(rec); - } - else - { - Puts("Unable to create RecyclerPrefab for Front position!"); - } - } - - private void Unload() - { - - foreach (var recycler in _recyclers) - { - if (recycler is Recycler) - { - ((Recycler)recycler).DropItems(); - } - recycler.Kill(); - } - } - - private class SpawnData - { - public SpawnData(Vector3 position, Vector3 rotation) - { - Position = position; - Rotation = rotation; - } - - public Vector3 Position { get; set; } - public Vector3 Rotation { get; set; } - } - } +using UnityEngine; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using Rust; + +namespace Oxide.Plugins +{ + [Info("Monuments Recycler", "Dana", "0.2.5")] + [Description("Adds recyclers to monuments including the cargo ship.")] + internal class MonumentsRecycler : RustPlugin + { + private bool _serverInitialized = false; + private const string RecyclerPrefab = "assets/bundled/prefabs/static/recycler_static.prefab"; + private const string SmallOilRigKey = "oil_rig_small"; + private const string LargeOilRigKey = "large_oil_rig"; + private const string DomeKey = "dome_monument_name"; + const string FishingVillageLargePrefab = "assets/bundled/prefabs/autospawn/monument/fishing_village/fishing_village_a.prefab"; + const string FishingVillageSmallBPrefab = "assets/bundled/prefabs/autospawn/monument/fishing_village/fishing_village_b.prefab"; + const string FishingVillageSmallAPrefab = "assets/bundled/prefabs/autospawn/monument/fishing_village/fishing_village_c.prefab"; + + private List _recyclers = new List(); + private SpawnData _domeSpawnData = new SpawnData(new Vector3(19.9f, 37.8f, 16.57f), new Vector3(0, 235, 0)); + private readonly Dictionary _smallOilRigRecyclerPositions = new Dictionary + { + {"3", new SpawnData(new Vector3(21.01f, 22.5f, -30.8f),new Vector3(0, 180, 0) )}, + {"4",new SpawnData(new Vector3(32.1f, 27f, -34.5f),new Vector3(0, 270, 0)) } + }; + private readonly Dictionary _largeOilRigRecyclerPositions = new Dictionary + { + {"4", new SpawnData(new Vector3(20.57f, 27.1f, -44.52f),new Vector3(0, 0, 0) )}, + {"6",new SpawnData(new Vector3(-13.6f, 36.1f, -3.4f),new Vector3(0, 180, 0)) } + }; + private readonly Dictionary _fishingVillageRecyclerPositions = new Dictionary + { + {"smallB", new SpawnData(new Vector3(-21f,0.3f,6.5f),new Vector3(0f,130f,0f)) }, + {"smallA", new SpawnData(new Vector3(-8.5f,2f,16.06067f),new Vector3(0,90f,0)) }, + {"large", new SpawnData(new Vector3(-20.7f,0.2f,-9.6f), new Vector3(0,45f,0)) } + }; + private Configuration _config; + + public class Configuration + { + [JsonProperty(PropertyName = "Cargo Ship - Recycler Position - Front")] + public bool RecyclerPositionFront = true; + + [JsonProperty(PropertyName = "Cargo Ship - Recycler Position - Back")] + public bool RecyclerPositionBack = true; + + [JsonProperty(PropertyName = "Cargo Ship - Recycler Position - Bottom")] + public bool RecyclerPositionBottom = true; + + [JsonProperty(PropertyName = "Large Oil Rig - Recycler Position - Level 4")] + public bool LargeOilRigRecyclerPositionLevel4 = true; + + [JsonProperty(PropertyName = "Large Oil Rig - Recycler Position - Level 6")] + public bool LargeOilRigRecyclerPositionLevel6 = true; + + [JsonProperty(PropertyName = "Small Oil Rig - Recycler Position - Level 3")] + public bool SmallOilRigRecyclerPositionLevel3 = true; + + [JsonProperty(PropertyName = "Small Oil Rig - Recycler Position - Level 4")] + public bool SmallOilRigRecyclerPositionLevel4 = true; + + [JsonProperty(PropertyName = "Fishing Village - Recycler - Large")] + public bool FishingVillageRecyclerLarge { get; set; } = true; + + [JsonProperty(PropertyName = "Fishing Village - Recycler - Small A")] + public bool FishingVillageRecyclerSmallA { get; set; } = true; + + [JsonProperty(PropertyName = "Fishing Village - Recycler - Small B")] + public bool FishingVillageRecyclerSmallB { get; set; } = true; + + [JsonProperty(PropertyName = "Dome - Recycler")] + public bool DomeRecycler = true; + } + + protected override void LoadConfig() + { + base.LoadConfig(); + try + { + _config = Config.ReadObject(); + //if (_config != null && _serverInitialized) + //{ + // ShowRecyclers(); + //} + } + catch + { + PrintError("Loading default config! Error loading your config, it's corrupt!"); + LoadDefaultConfig(); + } + + if (_config == null) + { + LoadDefaultConfig(); + } + + SaveConfig(); + var pluginEnabled = _config.RecyclerPositionFront || _config.RecyclerPositionBack || _config.RecyclerPositionBottom || + _config.LargeOilRigRecyclerPositionLevel4 || _config.LargeOilRigRecyclerPositionLevel6 || + _config.SmallOilRigRecyclerPositionLevel3 || _config.SmallOilRigRecyclerPositionLevel4 || + _config.DomeRecycler || _config.FishingVillageRecyclerLarge || _config.FishingVillageRecyclerSmallA + || _config.FishingVillageRecyclerSmallB; + if (!pluginEnabled) + { + PrintWarning("No Recycler Position Found"); + Unsubscribe(nameof(OnEntitySpawned)); + } + } + + protected override void LoadDefaultConfig() => _config = new Configuration(); + + protected override void SaveConfig() => Config.WriteObject(_config); + + Tuple GetCargoFrontPosition(Vector3 position, Quaternion rotation) => Tuple.Create(new Vector3(position.x - .2172264f, position.y + 9.574753f, position.z + 79.05922f), new Quaternion(rotation.x, rotation.y + 180, rotation.z, rotation.w)); + + Tuple GetCargoBackPosition(Vector3 position, Quaternion rotation) => Tuple.Create(new Vector3(position.x + .2407f, position.y + 9.5f, position.z - 36.87305f), rotation); + + Tuple GetCargoBottomPosition(Vector3 position, Quaternion rotation) => Tuple.Create(new Vector3(position.x, position.y + .57767f, position.z + 10f), new Quaternion(rotation.x, rotation.y + 180, rotation.z, rotation.w)); + + private void OnEntitySpawned(CargoShip ship) + { + if (ship == null) return; + Vector3 pos = ship.transform.position; + Quaternion rot = new Quaternion(); + Timer thisTimer = null; + + thisTimer = timer.Every(0.01f, () => + { + if (ship == null || ship.IsDestroyed) + { + thisTimer.Destroy(); + return; + } + + ship.transform.position = pos; + ship.transform.rotation = rot; + }); + + timer.Once(3, () => + { + if (ship == null || ship.IsDestroyed) + { + return; + } + + if (_config.RecyclerPositionFront) + { + Vector3 position = ship.transform.position; + Quaternion rotation = ship.transform.rotation; + SpawnRecycler(ship, GetCargoFrontPosition(position, rotation)); + } + + if (_config.RecyclerPositionBack) + { + Vector3 position = ship.transform.position; + Quaternion rotation = ship.transform.rotation; + SpawnRecycler(ship, GetCargoBackPosition(position, rotation)); + } + + if (_config.RecyclerPositionBottom) + { + Vector3 position = ship.transform.position; + Quaternion rotation = ship.transform.rotation; + SpawnRecycler(ship, GetCargoBottomPosition(position, rotation)); + } + + thisTimer.Destroy(); + }); + } + + private void OnServerInitialized() + { + _serverInitialized = true; + ShowRecyclers(); + } + private void ShowRecyclers() + { + var oilRigEnabled = _config.LargeOilRigRecyclerPositionLevel4 || _config.LargeOilRigRecyclerPositionLevel6 || + _config.SmallOilRigRecyclerPositionLevel3 || _config.SmallOilRigRecyclerPositionLevel4; + var fishingVillageEnabled = _config.FishingVillageRecyclerLarge || _config.FishingVillageRecyclerSmallA || _config.FishingVillageRecyclerSmallB; + if (oilRigEnabled) + { + + SpawnData spawnData; + var monuments = TerrainMeta.Path.Monuments?.Where(x => x.shouldDisplayOnMap && x.displayPhrase.english.Contains("Oil Rig")).ToList() ?? new List(); + foreach (var monument in monuments) + { + if (monument.displayPhrase?.token == SmallOilRigKey) + { + if (_config.SmallOilRigRecyclerPositionLevel3) + { + if (_smallOilRigRecyclerPositions.TryGetValue("3", out spawnData)) + { + SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation); + } + } + if (_config.SmallOilRigRecyclerPositionLevel4) + { + if (_smallOilRigRecyclerPositions.TryGetValue("4", out spawnData)) + { + SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation); + } + } + } + else if (monument.displayPhrase?.token == LargeOilRigKey) + { + if (_config.LargeOilRigRecyclerPositionLevel4) + { + if (_largeOilRigRecyclerPositions.TryGetValue("4", out spawnData)) + { + SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation); + } + } + if (_config.LargeOilRigRecyclerPositionLevel6) + { + if (_largeOilRigRecyclerPositions.TryGetValue("6", out spawnData)) + { + SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation); + } + } + } + } + } + + if (_config.DomeRecycler) + { + var monuments = TerrainMeta.Path.Monuments?.Where(x => x.shouldDisplayOnMap && x.displayPhrase.english.Contains("Dome")).ToList() ?? new List(); + foreach (var monument in monuments) + { + if (monument.displayPhrase?.token == DomeKey) + { + SpawnRecycler(monument.transform, _domeSpawnData.Position, _domeSpawnData.Rotation); + } + } + } + if (fishingVillageEnabled) + { + SpawnData spawnData; + var monuments = TerrainMeta.Path.Monuments?.Where(x => x.shouldDisplayOnMap && x.displayPhrase.english.ToLower().Contains("fishing")).ToList() ?? new List(); + foreach (var monument in monuments) + { + if (monument.name == FishingVillageLargePrefab) + { + if (_config.FishingVillageRecyclerLarge) + { + if (_fishingVillageRecyclerPositions.TryGetValue("large", out spawnData)) + { + SpawnFishingVillageRecycler(monument.transform, spawnData.Position, spawnData.Rotation); + } + } + } + else if (monument.name == FishingVillageSmallAPrefab) + { + if (_config.FishingVillageRecyclerSmallA) + { + if (_fishingVillageRecyclerPositions.TryGetValue("smallA", out spawnData)) + { + SpawnFishingVillageRecycler(monument.transform, spawnData.Position, spawnData.Rotation); + } + } + } + else if (monument.name == FishingVillageSmallBPrefab) + { + if (_config.FishingVillageRecyclerSmallB) + { + if (_fishingVillageRecyclerPositions.TryGetValue("smallB", out spawnData)) + { + SpawnFishingVillageRecycler(monument.transform, spawnData.Position, spawnData.Rotation); + } + } + } + } + } + } + + private void SpawnFishingVillageRecycler(Transform objTransform, Vector3 spawnOffset, Vector3 rotationOffset) + { + + var mtx = objTransform.localToWorldMatrix; + var finalPos = mtx.MultiplyPoint3x4(spawnOffset); + var rotation = mtx.rotation * Quaternion.Euler(rotationOffset); + var entity = GameManager.server.CreateEntity(RecyclerPrefab, finalPos, rotation); + if (entity != null) + { + entity.Spawn(); + _recyclers.Add(entity); + } + } + private void SpawnRecycler(Transform objTransform, Vector3 spawnOffset, Vector3 rotationOffset) + { + + var mtx = objTransform.localToWorldMatrix; + var finalPos = mtx.MultiplyPoint3x4(spawnOffset); + var oilRot = mtx.rotation * Quaternion.Euler(rotationOffset); + if (!GamePhysics.CheckSphere(finalPos, .1f, Layers.Server.Deployed, QueryTriggerInteraction.Ignore)) + { + var entity = GameManager.server.CreateEntity(RecyclerPrefab, finalPos, oilRot); + if (entity != null) + { + entity.Spawn(); + _recyclers.Add(entity); + } + } + } + private void SpawnRecycler(CargoShip ship, Tuple selectedTransform) + { + BaseEntity rec = GameManager.server.CreateEntity(RecyclerPrefab, selectedTransform.Item1, selectedTransform.Item2, true); + if (rec != null) + { + rec.enableSaving = true; + rec.Spawn(); + rec.SetParent(ship, true, true); + Rigidbody comp = rec.GetComponent(); + if (comp) + { + comp.isKinematic = true; + } + + _recyclers.Add(rec); + } + else + { + Puts("Unable to create RecyclerPrefab for Front position!"); + } + } + + private void Unload() + { + + foreach (var recycler in _recyclers) + { + if (recycler is Recycler) + { + ((Recycler)recycler).DropItems(); + } + recycler.Kill(); + } + } + + private class SpawnData + { + public SpawnData(Vector3 position, Vector3 rotation) + { + Position = position; + Rotation = rotation; + } + + public Vector3 Position { get; set; } + public Vector3 Rotation { get; set; } + } + } } \ No newline at end of file diff --git a/defaults/oxide/plugins/PathFinding.cs b/defaults/oxide/plugins/PathFinding.cs new file mode 100644 index 00000000..775acef0 --- /dev/null +++ b/defaults/oxide/plugins/PathFinding.cs @@ -0,0 +1,574 @@ +using System; +using System.Collections.Generic; +using Oxide.Core.Plugins; +using UnityEngine; +using static UnityEngine.Vector3; + +namespace Oxide.Plugins +{ + [Info("PathFinding", "Reneb / Nogrod", "1.1.3", ResourceId = 868)] + [Description("Path finding API, used by other plugins only")] + public class PathFinding : RustPlugin + { + private static readonly Vector3 Up = up; + public sealed class PathFinder + { + private static readonly sbyte[,] Direction = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}, {1, -1}, {1, 1}, {-1, 1}, {-1, -1}}; + public static readonly Vector3 EyesPosition; + public static readonly uint Size; + public static PriorityQueue OpenList; + public static PathFindNode[,] Grid; + + static PathFinder() + { + EyesPosition = new Vector3(0f, 1.6f, 0f); + OpenList = new PriorityQueue(MaxDepth * Direction.GetLength(0)); // 8 directions for each node + Size = World.Size; + Grid = new PathFindNode[Size, Size]; + //Interface.Oxide.LogInfo("Queue: {0} Grid: {1} Size: {2}", OpenList.MaxSize, Grid.Length, Size); + } + + public List FindPath(Vector3 sourcePos, Vector3 targetPos) + { + //Interface.Oxide.LogInfo("Queue: {0} Grid: {1}", OpenList.MaxSize, Grid.Length); + var closedList = new HashSet(); + + var targetNode = new PathFindNode(targetPos); + if (targetNode.X < 0 || targetNode.X >= Size || targetNode.Z < 0 || targetNode.Z >= Size) return null; + Grid[targetNode.X, targetNode.Z] = targetNode; + + var startNode = new PathFindNode(sourcePos); + if (startNode.X < 0 || startNode.X >= Size || startNode.Z < 0 || startNode.Z >= Size) return null; + Grid[startNode.X, startNode.Z] = startNode; + OpenList.Enqueue(startNode); + //PathFindNode closestNode = null; + + while (OpenList.Count > 0) + { + var currentNode = OpenList.Dequeue(); + if (currentNode == targetNode) + { + Clear(); + return RetracePath(startNode, targetNode); + } + closedList.Add(currentNode); + for (var i = 0; i < 8; i++) + { + var dirX = Direction[i, 0]; + var dirZ = Direction[i, 1]; + var x = currentNode.X + dirX; + var z = currentNode.Z + dirZ; + if (x < 0 || x >= Size || z < 0 || z >= Size) continue; + var neighbour = FindPathNodeOrCreate(x, z, currentNode.Position.y); + //Interface.Oxide.LogInfo("Checking neighbour: {0} {1} {2} {3} {4}", x, z, neighbour.Position, closedList.Contains(neighbour), neighbour.Walkable); + if (!neighbour.Walkable) continue; + var newGScore = currentNode.G + GetDistance(currentNode, neighbour); + if (newGScore >= neighbour.G && closedList.Contains(neighbour)) continue; + if (newGScore < neighbour.G || !OpenList.Contains(neighbour)) + { + //foreach (var player in BasePlayer.activePlayerList) + // player.SendConsoleCommand("ddraw.sphere", 30f, Color.black, neighbour.Position, .25f); + neighbour.G = newGScore; + neighbour.H = GetDistance(neighbour, targetNode); + neighbour.F = newGScore + neighbour.H; + neighbour.Parent = currentNode; + if (!OpenList.Contains(neighbour)) + OpenList.Enqueue(neighbour); + else + OpenList.Update(neighbour); + //if (closestNode == null || newGScore < closestNode.G) + // closestNode = neighbour; + } + } + if (closedList.Count > MaxDepth) + { + //Interface.Oxide.LogWarning("[PathFinding] Hit MaxDepth!"); + break; + } + } + Clear(); + //if (closestNode != null) + // return RetracePath(startNode, closestNode); + return null; + } + + private static void Clear() + { + OpenList.Clear(); + Array.Clear(Grid, 0, Grid.Length); + } + + private static List RetracePath(PathFindNode startNode, PathFindNode endNode) + { + var path = new List(); + while (endNode != startNode) + { + path.Add(endNode.Position); + endNode = endNode.Parent; + } + path.Reverse(); + //path.RemoveAt(0); + return path; + } + + private static int GetDistance(PathFindNode nodeA, PathFindNode nodeB) + { + var dstX = Math.Abs(nodeA.X - nodeB.X); + var dstZ = Math.Abs(nodeA.Z - nodeB.Z); + var dstY = Math.Abs(nodeA.Position.y - nodeB.Position.y); + + if (dstX > dstZ) + return 14*dstZ + 10*(dstX - dstZ) + (int)(10*dstY); + return 14*dstX + 10*(dstZ - dstX) + (int)(10*dstY); + } + + private static PathFindNode FindPathNodeOrCreate(int x, int z, float y) + { + var node = Grid[x, z]; + if (node != null) return node; + var halfGrid = Size/2f; + var groundPos = new Vector3(x - halfGrid, y, z - halfGrid); + //groundPos.y = TerrainMeta.HeightMap.GetHeight(groundPos); + FindRawGroundPosition(groundPos, out groundPos); + Grid[x, z] = node = new PathFindNode(groundPos); + return node; + } + } + + public sealed class PriorityQueue + { + private readonly PathFindNode[] nodes; + private int numNodes; + + public PriorityQueue(int maxNodes) + { + numNodes = 0; + nodes = new PathFindNode[maxNodes + 1]; + } + + public int Count => numNodes; + + public int MaxSize => nodes.Length - 1; + + public void Clear() + { + Array.Clear(nodes, 1, numNodes); + numNodes = 0; + } + + public bool Contains(PathFindNode node) + { + return nodes[node.QueueIndex] == node; + } + + public void Update(PathFindNode node) + { + SortUp(node); + } + + public void Enqueue(PathFindNode node) + { + nodes[++numNodes] = node; + node.QueueIndex = numNodes; + SortUp(node); + } + + private void Swap(PathFindNode node1, PathFindNode node2) + { + nodes[node1.QueueIndex] = node2; + nodes[node2.QueueIndex] = node1; + + var temp = node1.QueueIndex; + node1.QueueIndex = node2.QueueIndex; + node2.QueueIndex = temp; + } + + private void SortUp(PathFindNode node) + { + var parent = node.QueueIndex/2; + while (parent > 0) + { + var parentNode = nodes[parent]; + if (CompareTo(parentNode, node) >= 0) + break; + + Swap(node, parentNode); + + parent = node.QueueIndex/2; + } + } + + private void SortDown(PathFindNode node) + { + var finalQueueIndex = node.QueueIndex; + while (true) + { + var newParent = node; + var childLeftIndex = 2*finalQueueIndex; + + if (childLeftIndex > numNodes) + { + node.QueueIndex = finalQueueIndex; + nodes[finalQueueIndex] = node; + break; + } + + var childLeft = nodes[childLeftIndex]; + if (CompareTo(childLeft, newParent) >= 0) + { + newParent = childLeft; + } + + var childRightIndex = childLeftIndex + 1; + if (childRightIndex <= numNodes) + { + var childRight = nodes[childRightIndex]; + if (CompareTo(childRight, newParent) >= 0) + { + newParent = childRight; + } + } + + if (newParent != node) + { + nodes[finalQueueIndex] = newParent; + + var temp = newParent.QueueIndex; + newParent.QueueIndex = finalQueueIndex; + finalQueueIndex = temp; + } + else + { + node.QueueIndex = finalQueueIndex; + nodes[finalQueueIndex] = node; + break; + } + } + } + + public PathFindNode Dequeue() + { + var node = nodes[1]; + Remove(node); + return node; + } + + public void Remove(PathFindNode node) + { + if (node.QueueIndex == numNodes) + { + nodes[numNodes--] = null; + return; + } + + var formerLastNode = nodes[numNodes]; + Swap(node, formerLastNode); + nodes[numNodes--] = null; + + var parentIndex = formerLastNode.QueueIndex/2; + var parentNode = nodes[parentIndex]; + + if (parentIndex > 0 && CompareTo(formerLastNode, parentNode) >= 0) + SortUp(formerLastNode); + else + SortDown(formerLastNode); + } + + private static int CompareTo(PathFindNode node, PathFindNode other) + { + if (node.F == other.F) + { + if (node.H == other.H) + return 0; + if (node.H > other.H) + return -1; + return 1; + } + if (node.F > other.F) + return -1; + return 1; + } + } + + public sealed class PathFindNode + { + public readonly int X; + public readonly int Z; + public int QueueIndex; + public float H; + public float G; + public float F; + public PathFindNode Parent; + public Vector3 Position; + public bool Walkable; + + public PathFindNode(Vector3 position) + { + Position = position; + X = (int) Math.Round(position.x + PathFinder.Size/2f); + Z = (int) Math.Round(position.z + PathFinder.Size/2f); + Walkable = !Physics.CheckSphere(position + PathFinder.EyesPosition, .801f, blockLayer); + } + + public override int GetHashCode() + { + return X << 16 | Z; + } + } + + public static bool FindRawGroundPosition(Vector3 sourcePos, out Vector3 groundPos) + { + groundPos = sourcePos; + RaycastHit hitinfo; + if (Physics.Raycast(sourcePos + Up, down, out hitinfo, 50, groundLayer)) + { + groundPos.y = Math.Max(hitinfo.point.y, TerrainMeta.HeightMap.GetHeight(groundPos)); + return true; + } + if (Physics.Raycast(sourcePos - Up, Up, out hitinfo, 1.5f, groundLayer)) + { + groundPos.y = Math.Max(hitinfo.point.y, TerrainMeta.HeightMap.GetHeight(groundPos)); + return true; + } + return false; + } + + private class PathFollower : MonoBehaviour + { + public List Paths = new List(); + public float secondsTaken; + public float secondsToTake; + public float waypointDone; + public float speed; + public Vector3 StartPos; + public Vector3 EndPos; + public Vector3 nextPos; + public BaseEntity entity; + public BasePlayer player; + + private void Awake() + { + entity = GetComponent(); + player = GetComponent() ?? player; + speed = 4f; + } + + private void Move() + { + if (secondsTaken == 0f) FindNextWaypoint(); + Execute_Move(); + if (waypointDone >= 1f) secondsTaken = 0f; + } + + private void Execute_Move() + { + if (StartPos == EndPos) return; + secondsTaken += Time.deltaTime; + waypointDone = Mathf.InverseLerp(0f, secondsToTake, secondsTaken); + nextPos = Lerp(StartPos, EndPos, waypointDone); + entity.transform.position = nextPos; + player?.ClientRPCPlayer(null, player, "ForcePositionTo", nextPos); + } + + private void FindNextWaypoint() + { + if (Paths.Count == 0) + { + StartPos = EndPos = zero; + enabled = false; + return; + } + SetMovementPoint(Paths[0], 4f); + } + + public void SetMovementPoint(Vector3 endpos, float s) + { + StartPos = entity.transform.position; + if (endpos != StartPos) + { + EndPos = endpos; + secondsToTake = Distance(EndPos, StartPos)/s; + entity.transform.rotation = Quaternion.LookRotation(EndPos - StartPos); + if (player != null) SetViewAngle(player, entity.transform.rotation); + secondsTaken = 0f; + waypointDone = 0f; + } + Paths.RemoveAt(0); + } + + private void SetViewAngle(BasePlayer player, Quaternion ViewAngles) + { + player.viewAngles = ViewAngles.eulerAngles; + player.SendNetworkUpdate(); + } + + private void FixedUpdate() + { + Move(); + } + } + + public static Vector3 jumpPosition = new Vector3(0f, 1f, 0f); + public static int groundLayer; + public static int blockLayer; + private static int MaxDepth = 5000; + + protected override void LoadDefaultConfig() + { + } + + private void CheckCfg(string Key, ref T var) + { + if (Config[Key] is T) + var = (T) Config[Key]; + else + Config[Key] = var; + } + + private void Init() + { + CheckCfg("Max Depth", ref MaxDepth); + SaveConfig(); + } + + ///////////////////////////////////////////// + /// OXIDE HOOKS + ///////////////////////////////////////////// + private void OnServerInitialized() + { + groundLayer = LayerMask.GetMask("Terrain", "World", "Construction", "Deployed", "Default"); + blockLayer = LayerMask.GetMask("World", "Construction", "Tree", "Deployed", "Default"); + + timer.Once(30f, ResetPathFollowers); + } + + private void Unload() + { + var objects = UnityEngine.Object.FindObjectsOfType(); + if (objects != null) + foreach (var gameObj in objects) + UnityEngine.Object.Destroy(gameObj); + PathFinder.OpenList = null; + PathFinder.Grid = null; + } + + ///////////////////////////////////////////// + /// Outside Plugin Calls + ///////////////////////////////////////////// + private bool FindAndFollowPath(BaseEntity entity, Vector3 sourcePosition, Vector3 targetPosition) + { + //var curtime = Time.realtimeSinceStartup; + var bestPath = FindBestPath(sourcePosition, targetPosition); + //Debug.Log((Time.realtimeSinceStartup - curtime).ToString()); + if (bestPath == null) return false; + FollowPath(entity, bestPath); + return true; + } + + private void FollowPath(BaseEntity entity, List pathpoints) + { + var pathfollower = entity.GetComponent() ?? entity.gameObject.AddComponent(); + pathfollower.Paths = pathpoints; + pathfollower.enabled = true; + } + + [HookMethod("FindBestPath")] + public List FindBestPath(Vector3 sourcePosition, Vector3 targetPosition) + { + return FindLinePath(sourcePosition, targetPosition) ?? FindPath(sourcePosition, targetPosition); + } + + public List Go(Vector3 source, Vector3 target) + { + return FindLinePath(source, target) ?? FindPath(source, target); + } + + private List FindPath(Vector3 sourcePosition, Vector3 targetPosition) + { + //Puts("FindPath: {0} {1}", sourcePosition, targetPosition); + return new PathFinder().FindPath(sourcePosition, targetPosition); + } + + private List FindLinePath(Vector3 sourcePosition, Vector3 targetPosition) + { + var distance = (int) Math.Ceiling(Distance(sourcePosition, targetPosition)); + if (distance <= 0) return null; + var straightPath = new List(new Vector3[distance]) {[distance - 1] = targetPosition}; + var currentPos = Lerp(sourcePosition, targetPosition, 1f / distance); + Vector3 groundPosition; + if (!FindRawGroundPosition(currentPos, out groundPosition)) return null; + if (Distance(groundPosition, sourcePosition) > 2) return null; + if (Physics.Linecast(sourcePosition + jumpPosition, groundPosition + jumpPosition, blockLayer)) return null; + straightPath[0] = groundPosition; + for (var i = 1; i < distance - 1; i++) + { + currentPos = Lerp(sourcePosition, targetPosition, (i + 1f)/distance); + if (!FindRawGroundPosition(currentPos, out groundPosition)) return null; + if (Distance(groundPosition, straightPath[i - 1]) > 2) return null; + if (Physics.Linecast(straightPath[i - 1] + jumpPosition, groundPosition + jumpPosition, blockLayer)) return null; + straightPath[i] = groundPosition; + } + if (Physics.Linecast((distance == 1 ? sourcePosition : straightPath[distance - 2]) + jumpPosition, targetPosition + jumpPosition, blockLayer)) return null; + return straightPath; + } + + ///////////////////////////////////////////// + /// Reset part of the plugin + ///////////////////////////////////////////// + private void ResetPathFollowers() + { + var objects = UnityEngine.Object.FindObjectsOfType(); + foreach (var gameObj in objects) + if (gameObj.Paths.Count == 0) + UnityEngine.Object.Destroy(gameObj); + } + + ///////////////////////////////////////////// + /// Debug Command + ///////////////////////////////////////////// + [ChatCommand("path")] + private void cmdChatPath(BasePlayer player, string command, string[] args) + { + if (player.net.connection.authLevel < 1) return; + Quaternion currentRot; + if (!TryGetPlayerView(player, out currentRot)) return; + object closestEnt; + Vector3 closestHitpoint; + if (!TryGetClosestRayPoint(player.transform.position, currentRot, out closestEnt, out closestHitpoint)) return; + + FindAndFollowPath(player, player.transform.position, closestHitpoint); + } + + private bool TryGetPlayerView(BasePlayer player, out Quaternion viewAngle) + { + viewAngle = new Quaternion(0f, 0f, 0f, 0f); + if (player.serverInput.current == null) return false; + viewAngle = Quaternion.Euler(player.serverInput.current.aimAngles); + return true; + } + + private bool TryGetClosestRayPoint(Vector3 sourcePos, Quaternion sourceDir, out object closestEnt, out Vector3 closestHitpoint) + { + var sourceEye = sourcePos + PathFinder.EyesPosition; + var ray = new Ray(sourceEye, sourceDir*forward); + + var hits = Physics.RaycastAll(ray); + var closestdist = 999999f; + closestHitpoint = sourcePos; + closestEnt = false; + for (var i = 0; i < hits.Length; i++) + { + var hit = hits[i]; + if (hit.collider.GetComponentInParent() == null && hit.distance < closestdist) + { + closestdist = hit.distance; + closestEnt = hit.collider; + closestHitpoint = hit.point; + } + } + + if (closestEnt is bool) return false; + return true; + } + } +}