Иконка ресурса

FREE RaidGuardian : 1.0.5 1.0.5

Нет прав для скачивания
C#:
using CompanionServer;
using Oxide.Core.Plugins;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Oxide.Core;
using Pool = Facepunch.Pool;
using Newtonsoft.Json;
using Oxide.Core.Libraries.Covalence;
using System.Text.RegularExpressions;
using System.Diagnostics;
namespace Oxide.Plugins {
    [Info("Raid Guardian", "Shady", "1.0.5")]
    internal class RaidGuardian : RustPlugin {
        /*
         * Copyright 2024 PRISM
         *
         * This file is part of PRISM's plugins.*
         * PRISM's plugins is free software: you can redistribute it and/or modify
         * it under the terms of the GNU General Public License as published by
         * the Free Software Foundation, either version 3 of the License, or
         * (at your option) any later version.*
         * PRISM's plugins is distributed in the hope that it will be useful,
         * but WITHOUT ANY WARRANTY; without even the implied warranty of
         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
         * GNU General Public License for more details.*
         * You should have received a copy of the GNU General Public License
         * along with PRISM's plugins.If not, see <https://www.gnu.org/licenses/>.*/
        /*
         * The below is not a license (that can be found above), but simply a notice to the reader of this code:
         * The development of PRISM has been gradual and started in 2014.* Some of the code provided in this repository may be outdated and may not be the best representation of the author's current coding practices.* Some of the code provided in this repository may have been initially developed in 2014.* Files not containing the above license are not uniquely part of PRISM's plugins and are subject to the license of the original plugin, where applicable.* No copyright infringement is ever intended and the author of PRISM's plugins will comply with any requests to remove code.* PRISM's plugins are provided as is and no functionality is guaranteed.* Many of these plugins will not work without significant changes as there are numerous instances of hard-coded functions and may rely on external API calls that are not a part of this project itself.* 
         * 
         * With those notices out of the way, I, Shady, the author of this plugin, would like you to read and acknowledge the following:
         * These plugins are part of a decade long passion project where we, PRISM, created a unique experience to all players on our Rust servers.* This was a significant part of my life.I dedicated a decade to developing and maintaining these plugins and the servers themselves.* I implore you that, should you use any of the code or plugins provided in this repository, where it belongs solely to PRISM, that you respect the time and effort put into it.* Please remember that I, a single human, invested a significant amount of time and effort into these plugins and the servers they were used on.* Please feel free to critique my code as it is written; I am aware it is not perfect.Much of it is old, and I'm capable of doing far better now.* Moreover, I ask, sincerely, that you do not use these plugins or code without acknowledging that someone dedicated themselves to this passion project.* In asking that, it is my simple desire that you do not let these plugins be used in a way that is inconsistent with my own desires and love.* Please do not use them and claim them as your own.I wish for you to use them on your server and make that server something of your own.* Something unique and beautiful.However, I ask that you do not use these plugins and claim them as your own work.* Should you make changes to them, you are, of course, the author of those changes.But, if your code is based on one of PRISM's original plugins, please do not claim it as your own.* I also request that you do not use the plugins in a way that would be detrimental to the Rust community or the community of server owners.* In doing so, I ask that you please do not monetize the functions within these plugins with any changes you should make.* Should you use any of PRISM's plugins or its code, please do not lock anything behind a paywall or in a way that is unfair to the community of Rust.* My origin is that of one where I saw, daily, very young players who only wished to enjoy a few hours of their life on a video game and in many cases had little or no expendable income.* For many people, even purchasing the game is significantly difficult.It is, in my eyes, unfair to create fun, custom content, and suggest that you care about the community while also charging for it or creating an unfair environment where players who pay are significantly advantaged compared to those who cannot afford to do so.* I cannot control what you do with this code, but I ask that you respect the spirit in which it was created:
         * That is one of sincere love, care, and appreciation for the people who played on PRISM's servers.* Our community, much like yours, is your entire server.Your people matter, just as you matter.Please remember that *everyone* deserves a fair, fun environment.* I ask that should you use these plugins, you do so with the same love and care that I put into them.I ask that you do so with the same respect, love, care, and appreciation for the people who will play on your server.*
         * One of my greatest helps over the years was MBR.He has been a wonderful person, friend, and developer; I am beyond grateful for his contributions to us, but moreover, his friendship.* Some of these plugins were authored solely by him, or with his help.Where this has occurred, it should be noted in the author field at the top of the plugin.* Worthy of note is that he is solely responsible for our top-tier Discord integration.* If you so wish to see his GitHub page, here it is linked below:
         * https://github.com/MBR-0001
         * 
         * 
         * Should you wish to support the work that I have put into this project of the past decade, please consider doing so here:
         * https://www.buymeacoffee.com/shady757
         * 
         * I neither expect nor anticipate any such donations.You are by no means obligated, it is truly a donation.It should be done out of the kindness of your heart, if you so choose.* 
         * PRISM, as a community, has not ceased and continues to exist.We have no plans of ceasing our community.We have, however, ceased our Rust servers and thus made these plugins open source.* If you ever wish to contact me, or simply join our community and become one of our beloved members, please join us here:
         * https://discord.gg/DUCnZhZ
         * 
         * To quote Rush: "There is magic at your fingers".* With love,
         * Shady and all of PRISM.Thank you for everything, always.* 
         */
        //needs to be updated to have more support for some non-buildingblock things, such as doors.perhaps check if door is connected to protected building (done)
        //little concern about decor items or furnaces/big walls.maybe can check if in tc range of protd building (done, ^ done - thankfully these things get assigned a building id lol)
        //don't stop all damage types (i.e decay, it currently stops decay) (done)
        //radius is all kinds of fucked for spheres currently at "fuck it" priority (resolved)
        //cancel start coroutine if the building is attacked, so it doesn't get protected mid-raid
        //send rust++ message when raid proteciton starts!!! (great idea imo) ("your building at X grid pos is now protected!") (done, sort of, no grid pos yet)
        //if cupboard time is < protection time, cap/clamp protection time to upkeep minutes left
        //all uses of linq are temporary because linq is BAD!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        //update: linq use reduced, but not removed entirely yet = exists only in spheres
        [PluginReference]
        private readonly Plugin Clans;
        [PluginReference]
        private readonly Plugin NoEscape;
        [PluginReference]
        private readonly Plugin WipeInfo;
        private ProtectionData _protectionData = null;
        private readonly int _constructionColl = LayerMask.GetMask(new string[] {
            "Construction", "Deployable", "Prevent Building", "Deployed"
        }
        );
        private readonly HashSet<BuildingManager.Building> _protectedBuildings = new();
        private readonly Dictionary<uint, HashSet<SphereEntity>> _buildingIdToSpheres = new();
        private readonly Dictionary<string, float> _lastWarningToast = new();
        private readonly Dictionary<uint, Coroutine> _buildingIdToCountdownCoroutine = new();
        private readonly Dictionary<uint, Coroutine> _buildingIdToEndProtectionCoroutine = new();
        private readonly Dictionary<string, float> _lastPlaytimeOnDisconnect = new();
        private readonly Dictionary<uint, Coroutine> _buildingIdToRaidBlockCoroutine = new();
        private readonly HashSet<Coroutine> _coroutines = new();
        private readonly HashSet<BuildingManager.Building> _raidBlockedBuildings = new();
        private readonly Dictionary<ulong, Action> _rustPlusNotifyAction = new();
        private readonly Regex _colorRegex = new("(<color=.+?>)", RegexOptions.Compiled);
        private readonly Regex _sizeRegex = new("(<size=.+?>)", RegexOptions.Compiled);
        private const string CHAT_STEAM_ID = "76561198865536053";
        private const string BUILD_TOO_CLOSE_PROTECTED_BUILDING = "You are attempting to build too close to a currently <i>Offline Raid Protected</i> building!";
        private const string BUILD_TOO_CLOSE_TOAST = "Too close to an <i>Offline Raid Protected</i> building!";
        private const string PROTECTED_BUILDING_TOAST = "<size=30><color=yellow>THIS BUILDING IS PROTECTED AS ITS OWNER IS OFFLINE.</color></size>";
        private readonly HashSet<string> _forbiddenTags = new(6) {
            "</color>", "</size>", "<b>", "</b>", "<i>", "</i>"
        }
        ;
        private void SpawnSpheresOnBuilding(BuildingManager.Building building, int sphereCount = 4, bool globalBroadcast = false) {
            if (building == null) throw new ArgumentNullException(nameof(building));
            var blocks = building.decayEntities;
            if (blocks == null || blocks.Count < 1) {
                PrintWarning(nameof(SpawnSpheresOnBuilding) + " called for a non-buildingblock building: " + building?.ID);
                return;
            }
            var centerPos = Vector3.zero;
            var transforms = Pool.GetList<Transform>();
            try {
                for (int i = 0; i < blocks.Count; i++)  //only checks foundations
                {
                    var block = blocks[i];
                    if (block == null || block.IsDestroyed || block.gameObject == null) continue;
                    if (!block.ShortPrefabName.Contains("foundation"))  //change to check IDs later - foundation check *may* be unnecessary, and not checking foundations may mean we can not adjust y manually? not sure
                    continue;
                    transforms.Add(block.transform);
                }
                if (transforms.Count < 1) {
                    PrintError("transforms.count < 1?!?!?!?!? on " + nameof(SpawnSpheresOnBuilding) + " building: " + building?.ID);
                    return;
                }
                centerPos = FindCenterOfTransforms(transforms);
                centerPos.y += 2f;
            }
            finally {
                Pool.FreeList(ref transforms);
            }
            //change to not use linq below.the code works, but pls no linq:
            var furthestDistance = blocks.Select(p => Vector3.Distance(p.transform.position, centerPos)).Max();
            var furthestBlock = blocks.Where(p => Vector3.Distance(p.transform.position, centerPos) == furthestDistance).FirstOrDefault();
            var furthestDist = Vector3.Distance(furthestBlock.transform.position, centerPos);
            var sphereRadius = furthestDistance * 2f + 4f;
            //7.5f - must be a few positive or else it doesn't cover entirely
            PrintWarning("set radius to furthestDistance * 2f + 4f: " + sphereRadius);
            if (_buildingIdToSpheres.TryGetValue(building.ID, out var spheres)) {
                foreach (var sphere in spheres) {
                    if (sphere == null || sphere.IsDestroyed) continue;
                    sphere.Kill();
                }
                _buildingIdToSpheres[building.ID].Clear();
            }
            else _buildingIdToSpheres[building.ID] = new HashSet<SphereEntity>();
            for (int i = 0; i < sphereCount; i++) {
                var sphere = (SphereEntity)GameManager.server.CreateEntity("assets/prefabs/visualization/sphere.prefab", centerPos, Quaternion.identity, true);
                if (sphere == null) continue;
                sphere.currentRadius = sphereRadius;
                sphere.lerpRadius = sphereRadius;
                sphere.lerpSpeed = 1000f;
                sphere.globalBroadcast = globalBroadcast;
                sphere.Spawn();
                sphere.EnableSaving(false);
                _buildingIdToSpheres[building.ID].Add(sphere);
            }
        }
        [ChatCommand("bsphere")]
        private void cmdBsphere(BasePlayer player, string command, string[] args) {
            if (!player.IsAdmin) return;
            var initialBlock = GetLookAtEntity(player, 10f) as DecayEntity;
            if (initialBlock == null) {
                SendReply(player, "You must be looking at a DecayEntity!");
                return;
            }
            //PrintWarning("initialBlock bounds: " + initialBlock.WorldSpaceBounds() + ", " + initialBlock.WorldSpaceBounds().extents.magnitude + ", " + initialBlock.WorldSpaceBounds().extents.sqrMagnitude);
            var buildingId = initialBlock.buildingID;
            var building = initialBlock.GetBuilding();
            if (_protectedBuildings.Contains(building)) {
                SendReply(player, "is already protected!!");
                return;
            }
            SpawnSpheresOnBuilding(building);
            SendReply(player, "spawned spheres");
            var prot = RaidProtectBuilding(building);
            SendReply(player, "raid protected building?: " + prot);
        }
        [ChatCommand("protected")]
        private void cmdProtected(BasePlayer player, string command, string[] args) {
            if (!player.IsAdmin) return;
            if (_protectedBuildings.Count < 1) {
                SendReply(player, "No buildings are currently protected!");
                return;
            }
            var sb = Pool.Get<StringBuilder>();
            try {
                sb.Clear().Append("Currently protected buildings:\n");
                foreach(var building in _protectedBuildings) {
                    if (BuildingManager.server.GetBuilding(building.ID) == null) {
                        PrintWarning(nameof(_protectedBuildings) + " had building with ID: " + building.ID + " but GetBuilding returned null!!!!");
                    }
                    var protInfo = _protectionData?.GetOrCreateProtectionInfo(building.ID);
                    sb.Append(building.ID).Append(" (").Append(ReadableTimeSpan(protInfo.ProtectionTime)).Append("), ");
                }
                sb.Length -= 2;
                SendReply(player, sb.ToString());
            }
            finally {
                Pool.Free(ref sb);
            }
        }
        [ChatCommand("proteccleft")]
        private void cmdHeProteccTimeLeft(BasePlayer player, string command, string[] args) {
            if (!player.IsAdmin) return;
            var initialBlock = GetLookAtEntity(player, 40f) as DecayEntity;
            if (initialBlock == null) {
                SendReply(player, "You must be looking at a DecayEntity!");
                return;
            }
            var buildingId = initialBlock.buildingID;
            var building = initialBlock.GetBuilding();
            var prot = _protectionData.GetOrCreateProtectionInfo(buildingId);
            SendReply(player, "Building " + buildingId + " has " + prot.ProtectionTime + " left.Started at: " + prot.ProtectionStartTime);
        }
        [ChatCommand("protecc")]
        private void cmdHeProtecc(BasePlayer player, string command, string[] args) {
            if (!player.IsAdmin) return;
            var initialBlock = GetLookAtEntity(player, 10f) as DecayEntity;
            if (initialBlock == null) {
                SendReply(player, "You must be looking at a DecayEntity!");
                return;
            }
            var buildingId = initialBlock.buildingID;
            var building = initialBlock.GetBuilding();
            var prot = _protectionData.GetOrCreateProtectionInfo(buildingId);
            if (prot.ProtectionTime.TotalHours < 3) prot.ProtectionTime = TimeSpan.FromHours(3);
            SendReply(player, "protecting building: " + buildingId + ", protection time: " + prot.ProtectionTime);
            var routine = StartRaidProtectionCoroutine(building, true, true);
            SendReply(player, "Started routine with init type, so should be instant");
            var protectionTime = prot.ProtectionTime;
            var wipeSpan = TimeUntilWipe();
            if (wipeSpan.TotalHours <= 24) {
                PrintWarning("wipeSpan.TotalHours <= 24, no protection!!!");
            }
            else {
                var wipeTime = GetWipeDate();
                var desiredTime = wipeTime.AddHours(-24);
                PrintWarning("wipeTime: " + wipeTime + ", 24 hours before wipe is: " + desiredTime);
                if (DateTime.Now.Add(protectionTime) >= desiredTime) {
                    PrintWarning("datetime.now + protecitontime is >= desiredTime, so we must clmap to wipe time! was: " + protectionTime);
                    protectionTime = desiredTime - DateTime.Now;
                    PrintWarning("is now (after clamp: " + protectionTime + ")");
                }
                else PrintWarning("didn't have enough protect to be >= desiredTime");
            }
            PrintWarning("final prot time: " + protectionTime);
        }
        [ChatCommand("owncheck")]
        private void cmdOwnCheck(BasePlayer player, string command, string[] args) {
            if (!player.IsAdmin) return;
            if (args.Length < 1 || !ulong.TryParse(args[0], out var ownerId)) {
                SendReply(player, "supply userID to check");
                return;
            }
            var initialBlock = GetLookAtEntity(player, 10f) as DecayEntity;
            if (initialBlock == null) {
                SendReply(player, "You must be looking at a DecayEntity!");
                return;
            }
            var buildingId = initialBlock.buildingID;
            var building = initialBlock.GetBuilding();
            if (building == null) {
                SendReply(player, "building is null!!!!!");
                return;
            }
            var isOwnedByUserIdOrClan = IsBuildingOwnedByPlayerOrClanMember(ownerId, building);
            var majorityOwn = GetMajorityOwnerFromBuilding(building);
            SendReply(player, nameof(isOwnedByUserIdOrClan) + " (" + ownerId + "): " + isOwnedByUserIdOrClan);
            SendReply(player, "actual majority owner is: " + GetMajorityOwnerFromBuilding(building));
            var clans = GetClanMembersByUserID(majorityOwn.ToString());
            if (clans == null || clans.Count < 1) {
                SendReply(player, "no clan members for: " + majorityOwn);
                return;
            }
            SendReply(player, "clan members of majority owner: " + string.Join(", ", clans));
        }
        [ChatCommand("rgcache")]
        private void cmdClanDebug(BasePlayer player, string command, string[] args) {
            if (!player.IsAdmin) return;
            if (args.Length > 1) {
                if (ulong.TryParse(args[0], out var id)) {
                    SendReply(player, args[0] + " is id, trying to find clan by userid");
                    SendReply(player, "members: " + string.Join(", ", GetClanMembersByUserID(args[0])));
                    return;
                }
                var clanMembers = GetClanMembers(args[0]);
                SendReply(player, "tried to get clan members from tag " + args[0] + ", lets see what we got: " + string.Join(", ", clanMembers));
                return;
            }
            SendReply(player, "Updating");
            UpdateClanMembersCache();
            SendReply(player, "Updated");
        }
        [ChatCommand("raidtimetest")]
        private void cmdRaidTimeTest(BasePlayer player, string command, string[] args) {
            if (!player.IsAdmin) return;
            if (args.Length < 1) {
                SendReply(player, "You must specify the seconds to add!");
                return;
            }
            if (!double.TryParse(args[0], out var seconds)) {
                SendReply(player, "not double: " + args[0]);
                return;
            }
            var initialBlock = GetLookAtEntity(player, 10f) as DecayEntity;
            if (initialBlock == null) {
                SendReply(player, "You must be looking at a DecayEntity!");
                return;
            }
            var buildingId = initialBlock.buildingID;
            var prot = _protectionData.GetOrCreateProtectionInfo(buildingId);
            prot.AddTimeToProtection(new TimeSpan(0, 0, (int)seconds));
            SendReply(player, "added seconds.protectiontime now: " + prot.ProtectionTime);
        }
#region Hooks
        private void OnClanUpdate(string tag) {
            if (string.IsNullOrWhiteSpace(tag)) throw new ArgumentNullException(nameof(tag));
            PrintWarning(nameof(OnClanUpdate) + " for " + tag + " so we are calling " + nameof(UpdateClanMembersCache));
            UpdateClanMembersCache();
            //could we have an option to update only a singular clan? of course.will we? probably.but for now, this is so fast it hardly matters.}
            private void OnClanCreate(string tag) {
                if (string.IsNullOrWhiteSpace(tag)) throw new ArgumentNullException(nameof(tag));
                PrintWarning(nameof(OnClanCreate) + " for " + tag + " so we are calling " + nameof(UpdateClanMembersCache));
                UpdateClanMembersCache();
                //could we have an option to update only a singular clan? of course.will we? probably.but for now, this is so fast it hardly matters.}
                private void OnClanDestroy(string tag) {
                    if (string.IsNullOrWhiteSpace(tag)) throw new ArgumentNullException(nameof(tag));
                    PrintWarning(nameof(OnClanDestroy) + " for " + tag + " so we are calling " + nameof(UpdateClanMembersCache));
                    UpdateClanMembersCache();
                    //could we have an option to update only a singular clan? of course.will we? probably.but for now, this is so fast it hardly matters.}
                    private void OnServerInitialized() {
                        UpdateClanMembersCache();
                        LoadData();
                        PrintWarning("looping through buildings");
                        foreach (var kvp in BuildingManager.server.buildingDictionary) {
                            var building = kvp.Value;
                            if (!_protectedBuildings.Contains(building) && IsApplicableForProtection(building)) {
                                PrintWarning("building: " + kvp.Key + " is applicable, start raid prot countdown coroutine after init");
                                //save coroutine so it can be cancelled upon connection and/or not started for each member
                                StartRaidProtectionCoroutine(building, true, true);
                            }
                        }
                        StartCoroutine(UpdateClanCacheEnumerator());
                    }
                    private void OnPlayerConnected(BasePlayer player) {
                        if (player == null) return;
                        PrintWarning(nameof(OnPlayerConnected));
                        var userId = player?.userID ?? 0;
                        var userIdString = player?.UserIDString ?? string.Empty;
                        var members = GetClanMembersByUserID(userIdString);
                        Action rustPlusAction;
                        if (members != null && members.Count > 0) {
                            for (int i = 0; i < members.Count; i++) {
                                var mem = members[i];
                                if (_rustPlusNotifyAction.TryGetValue(ulong.Parse(mem), out rustPlusAction)) ServerMgr.Instance.CancelInvoke(rustPlusAction);
                            }
                        }
                        else {
                            if (_rustPlusNotifyAction.TryGetValue(userId, out rustPlusAction)) ServerMgr.Instance.CancelInvoke(rustPlusAction);
                        }
                        foreach (var kvp in BuildingManager.server.buildingDictionary) {
                            var building = kvp.Value;
                            if (building == null) continue;
                            if (!IsBuildingOwnedByPlayerOrClanMember(player.userID, kvp.Value)) continue;
                            if (_buildingIdToEndProtectionCoroutine.TryGetValue(building.ID, out var stopRoutine)) {
                                PrintWarning("got stopRoutine!!! cancelling for: " + building.ID + "/" + player);
                                StopCoroutine(stopRoutine);
                            }
                            if (_buildingIdToCountdownCoroutine.TryGetValue(building.ID, out var coroutine) && coroutine != null) {
                                PrintWarning("found existing coroutine when player connected!! - stopping coroutine instead of starting a cancel one");
                                StopCoroutine(coroutine);
                                PrintWarning("stopped in-progress start coroutine, removing from dictionary");
                                _buildingIdToCountdownCoroutine.Remove(building.ID);
                                PrintWarning("removed from dictionary for countdown coroutine: " + building.ID);
                            }
                            else if (_protectedBuildings.Contains(building)) {
                                PrintWarning("starting end protection coroutine as there was not a start coroutine in progress, and building was protected: " + building.ID + ", majority owned by/clan member: " + player.userID);
                                StartCoroutine(StartEndingRaidProtection(building));
                            }
                        }
                    }
                    private TimeSpan GetTimeToAddToBuildingsFromDisconnect(BasePlayer player) {
                        if (player == null) throw new ArgumentNullException(nameof(player));
                        var secondsConnected = player?.net?.connection?.GetSecondsConnected() ?? 0;
                        if (secondsConnected <= 0) PrintError("failed to get seconds connected!!!!: " + player);
                        else {
                            var totalTime = (int)secondsConnected;
                            if (_lastPlaytimeOnDisconnect.TryGetValue(player.UserIDString, out var lastTime)) totalTime += (int)lastTime;
                            return new TimeSpan(totalTime < 3600 ? 3 : totalTime < 7200 ? 12 : 24, 0, 0);
                        }
                        return TimeSpan.Zero;
                    }
                    private void OnPlayerDisconnected(BasePlayer player) {
                        if (player == null) return;
                        PrintWarning(nameof(OnPlayerDisconnected) + " for: " + player);
                        var userId = player?.userID ?? 0;
                        var userIdString = player?.UserIDString ?? string.Empty;
                        try {
                            var secondsConnected = player?.net?.connection?.GetSecondsConnected() ?? 0;
                            if (secondsConnected <= 0) PrintError("failed to get seconds connected!!!!: " + player);
                            else {
                                PrintWarning("had seconds connected! time: " + secondsConnected);
                                var totalTime = (int)secondsConnected;
                                if (_lastPlaytimeOnDisconnect.TryGetValue(player.UserIDString, out var lastTime)) {
                                    PrintWarning("had a last play time, adding: " + lastTime + " to: " + totalTime);
                                    totalTime += (int)lastTime;
                                    PrintWarning("added, now: " + totalTime);
                                }
                                _lastPlaytimeOnDisconnect[player.UserIDString] = secondsConnected;
                                if (totalTime >= 3600)  //temp isadmin
                                {
                                    PrintWarning("total time was >= 3600, so now rewarding");
                                    var span = new TimeSpan(totalTime < 7200 ? 12 : 24, 0, 0);
                                    PrintWarning("span from totalTime: " + totalTime + " is: " + span);
                                    AddTimeToAllBuildings(player.userID, span);
                                }
                                else PrintWarning("totalTime was < 3600: " + totalTime + " - so no reward!");
                            }
                        }
                        catch(Exception ex) {
                            PrintError(ex.ToString());
                        }
                        ServerMgr.Instance.Invoke(() => {
                            foreach (var kvp in BuildingManager.server.buildingDictionary) {
                                var building = kvp.Value;
                                if (building == null || _protectedBuildings.Contains(building)) continue;
                                if (_buildingIdToCountdownCoroutine.TryGetValue(building.ID, out var routine) && routine != null) {
                                    PrintWarning(building.ID + " already had a countdown coroutine!!!!");
                                    continue;
                                }
                                if (!IsBuildingOwnedByPlayerOrClanMember(userId, building)) continue;
                                var prot = _protectionData?.GetOrCreateProtectionInfo(building.ID);
                                if (prot != null && prot.ProtectionTime.TotalHours < 3) prot.ProtectionTime = TimeSpan.FromHours(3);
                                if (IsApplicableForProtection(building)) {
                                    PrintWarning("building: " + kvp.Key + " is majority owned by: " + userId + " or one of their clan members and is applicable, start raid prot countdown coroutine for bid: " + building.ID);
                                    StartRaidProtectionCoroutine(building);
                                }
                            }
                        }
                        , 2f);
                        if (_rustPlusNotifyAction.TryGetValue(player.userID, out var action)) ServerMgr.Instance.CancelInvoke(action);
                        var members = GetClanMembersByUserID(userIdString);
                        var anyOtherPlayersConnected = false;
                        if (members != null && members.Count > 1) {
                            for (int i = 0; i < members.Count; i++) {
                                var p = members[i];
                                if (p == userIdString) continue;
                                var bp = BasePlayer.FindByID(ulong.Parse(p));
                                if (bp?.IsConnected ?? false) {
                                    anyOtherPlayersConnected = true;
                                    break;
                                }
                            }
                        }
                        if (anyOtherPlayersConnected) {
                            PrintWarning("user disconnected but had other clan members connected: " + player?.displayName + " (" + player?.UserIDString + ")");
                            return;
                        }
                        action = new Action(() => {
                            SendRustPlusNotificationsForProtectedBuildings(userId, members);
                        }
                        );
                        ServerMgr.Instance.Invoke(action, 620f);
                        //600 was too close? may have fired off before buildings were actually protected
                        _rustPlusNotifyAction[userId] = action;
                    }
                    private void Unload() {
                        try {
                            var copyHash = Pool.Get<HashSet<BuildingManager.Building>>();
                            try {
                                copyHash.Clear();
                                foreach (var building in _protectedBuildings) copyHash.Add(building);
                                foreach (var building in copyHash) StopRaidProtection(building);
                            }
                            finally {
                                copyHash.Clear();
                                Pool.Free(ref copyHash);
                            }
                        }
                        catch (Exception ex) {
                            PrintError(ex.ToString());
                        }
                        try {
                            //could maybe use reflection to find all ienumerators and stop coroutines, but whatever.this works for now, and is probably faster
                            foreach (var routine in _coroutines) {
                                if (routine == null) continue;
                                ServerMgr.Instance.StopCoroutine(routine);
                            }
                        }
                        catch (Exception ex) {
                            PrintError(ex.ToString());
                        }
                        try {
                            foreach (var kvp in _rustPlusNotifyAction) {
                                ServerMgr.Instance.CancelInvoke(kvp.Value);
                            }
                        }
                        catch(Exception ex) {
                            PrintError(ex.ToString());
                        }
                        SaveData();
                    }
                    private void OnLootEntityEnd(BasePlayer player, BuildingPrivlidge cupboard) {
                        if (player == null || cupboard == null) return;
                        if (cupboard.GetProtectedMinutes(true) < 1f) return;
                        var building = BuildingManager.server.GetBuilding(cupboard.buildingID);
                        if (building == null) {
                            PrintError(cupboard + " had null building on " + nameof(OnLootEntityEnd) + "!!!!");
                            return;
                        }
                        if (!IsBuildingOwnedByPlayerOrClanMember(player.userID, building)) return;
                        var buildingId = building.ID;
                        var protectionInfo = _protectionData.GetOrCreateProtectionInfo(buildingId);
                        if (protectionInfo == null) {
                            PrintError("Somehow protectionInfo is null?!");
                            return;
                        }
                        var timeToAdd = GetTimeToAddToBuildingsFromDisconnect(player);
                        var timeWouldBe = protectionInfo.ProtectionTime + timeToAdd;
                        if (timeWouldBe.TotalDays > 1) timeWouldBe = TimeSpan.FromDays(1);
                        //clamp
                        var sb = Pool.Get<StringBuilder>();
                        try {
                            var uptimeMsg = sb.Clear().Append("<color=#ff33ff>If you disconnect now, this building will be <i>offline-raid protected</i> for:</color>\n<size=20><color=#77ff33>").Append(ReadableTimeSpan(timeWouldBe)).Append("</size></color>").ToString();
                            ShowToast(player, uptimeMsg);
                            player?.Invoke(() => {
                                if (player == null || !player.IsConnected || player.IsDead()) return;
                                if (player?.inventory?.loot?.entitySource != null) return;
                                //looted again
                                var toastWarningMsg = "<color=yellow>NOTE:</color> Protection does not start until <i><color=yellow>10</color> mins</i> after disconnect\nIf a raid starts during this period, the building will <i><color=yellow>NOT</color></i> be protected!";
                                ShowToast(player, toastWarningMsg, 1);
                                player?.Invoke(() => {
                                    ShowToast(player, toastWarningMsg, 1);
                                }
                                , 1.25f);
                            }
                            , 5f);
                        }
                        finally {
                            Pool.Free(ref sb);
                        }
                        /*/
            var cupboardSpan = TimeSpan.FromSeconds(cupboard.GetProtectedSeconds());

            PrintWarning("protection time pre ")

            protectionInfo.ProtectionStartTime = Mathf.Clamp(protectionInfo.ProtectionTime, protectionInfo.ProtectionStartTime, cupboardSpan);/*/
                        //eventually, we should force protection time to be no more than cupboard upkeep time
                        //right now, it starts out that way, but if it somehow changes, it is never updated
                        //the concern is that we need to restart coroutines, which changing the start time itself does not automatically do
                    }
                    private void OnRocketLaunched(BasePlayer player, BaseEntity entity) {
                        if (player == null || entity == null) return;
                        if (!IsLookingAtProtectedBuilding(player, 250f, 12.5f)) return;
                        ShowProtectedToast(player);
                        if (!entity.IsDestroyed) entity.Kill();
                        var launcher = player?.GetHeldEntity() as BaseLauncher;
                        if (launcher == null) return;
                        var item = launcher?.GetItem();
                        var oldCond = item?.condition ?? 0f;
                        if (oldCond > 0f) {
                            launcher.Invoke(() => {
                                if (item == null) return;
                                item.condition = oldCond;
                                item.MarkDirty();
                            }
                            , 0.1f);
                        }
                        launcher.primaryMagazine.contents += 1;
                        launcher.SendNetworkUpdate();
                        NoteItemByID(player, launcher.primaryMagazine.ammoType.itemid, 1);
                    }
                    private void OnExplosiveThrown(BasePlayer player, BaseEntity entity, ThrownWeapon item) {
                        StartCoroutine(CheckExplosiveStickCoroutine(entity, player, item?.GetItem()));
                    }
                    private void OnEntityKill(DecayEntity entity) {
                        var buildingId = entity?.buildingID ?? 0;
                        if (buildingId == 0) return;
                        var building = BuildingManager.server.GetBuilding(buildingId);
                        if (building == null) {
                            PrintWarning("building was null prior to invoke?!: " + buildingId);
                            return;
                        }
                        //save and restart invoke instead of repeatedly invoking? small perf opt, maybe
                        ServerMgr.Instance.Invoke(() => {
                            var newBuilding = BuildingManager.server.GetBuilding(buildingId);
                            if (newBuilding == null) {
                                if (building != null) StopRaidProtection(building);
                                else PrintWarning("original building is now null!!");
                            }
                        }
                        , 1f);
                    }
                    private object OnSamSiteTarget(SamSite sam, BaseCombatEntity entity) {
                        if (entity == null) return null;
                        var building = sam?.GetBuilding();
                        if (building != null && _protectedBuildings.Contains(building)) return true;
                        return null;
                    }
                    private object CanBuild(Planner plan, Construction prefab, Construction.Target target) {
                        if (plan == null || prefab == null) return null;
                        var watch = Pool.Get<Stopwatch>();
                        try {
                            watch.Restart();
                            var player = plan?.GetOwnerPlayer();
                            if (player == null) return null;
                            var userId = player?.userID ?? 0;
                            var pos = player?.transform?.position ?? Vector3.zero;
                            var entities = Pool.GetList<DecayEntity>();
                            try {
                                Vis.Entities(pos, 38f, entities, Rust.Layers.Construction);
                                //this is slow, but it can't be helped.even a hashset containg decay entities is still slow - must be vector3.dist check
                                var buildings = Pool.Get<HashSet<BuildingManager.Building>>();
                                var targetDecay = target.entity as DecayEntity;
                                var targetDecayBuilding = targetDecay?.GetBuilding();
                                try {
                                    buildings.Clear();
                                    var cupboardCache = Pool.Get<Dictionary<uint, BuildingPrivlidge>>();
                                    try {
                                        cupboardCache.Clear();
                                        for (int i = 0; i < entities.Count; i++) {
                                            var entity = entities[i];
                                            var building = entity?.GetBuilding();
                                            if (building == null) continue;
                                            if (!_protectedBuildings.Contains(building)) continue;
                                            //speed opt by checking here instead of in buildings itself? it is a hashset after all
                                            if (entity.transform.position.y < pos.y && (pos.y - entity.transform.position.y) >= 10) continue;
                                            if (targetDecayBuilding == building) continue;
                                            if (!cupboardCache.TryGetValue(building.ID, out var cupboard) || (cupboard == null || cupboard.IsDestroyed))  //since we loop through a bunch of entities, we may get a lot from the same building.we don't need to call GetDominating...each time
                                            {
                                                cupboard = building?.GetDominatingBuildingPrivilege();
                                                if (cupboard != null) cupboardCache[building.ID] = cupboard;
                                                else cupboardCache.Remove(building.ID);
                                            }
                                            if (cupboard == null) continue;
                                            //cupboard.IsAuthed is slow
                                            var isAuthed = false;
                                            foreach (var ap in cupboard.authorizedPlayers) {
                                                if (ap.userid == userId) {
                                                    isAuthed = true;
                                                    break;
                                                }
                                            }
                                            if (!isAuthed) continue;
                                            buildings.Add(building);
                                        }
                                    }
                                    finally {
                                        cupboardCache?.Clear();
                                        Pool.Free(ref cupboardCache);
                                    }
                                    foreach (var building in buildings) {
                                        if (IsBuildingOwnedByPlayerOrClanMember(userId, building)) continue;
                                        SendReply(player, BUILD_TOO_CLOSE_PROTECTED_BUILDING);
                                        ShowToast(player, BUILD_TOO_CLOSE_TOAST, 1);
                                        return false;
                                    }
                                }
                                finally {
                                    buildings.Clear();
                                    Pool.Free(ref buildings);
                                }
                            }
                            finally {
                                Pool.FreeList(ref entities);
                            }
                        }
                        finally {
                            try {
                                if (watch.Elapsed.TotalMilliseconds >= 4) PrintWarning(nameof(CanBuild) + " took " + watch.Elapsed.TotalMilliseconds + " ms");
                            }
                            finally {
                                Pool.Free(ref watch);
                            }
                        }
                        return null;
                    }
                    private object OnEntityTakeDamage(DecayEntity entity, HitInfo info) {
                        if (entity == null || info == null) return null;
                        var majDmg = info?.damageTypes?.GetMajorityDamageType() ?? Rust.DamageType.Generic;
                        if (majDmg != Rust.DamageType.Explosion && majDmg != Rust.DamageType.Stab && majDmg != Rust.DamageType.Slash && majDmg != Rust.DamageType.Heat && majDmg != Rust.DamageType.Blunt && majDmg != Rust.DamageType.Bullet) return null;
                        var building = entity.GetBuilding();
                        if (building == null) {
                            var ioEnt = entity as IOEntity;
                            if (ioEnt != null) {
                                PrintWarning("building was null but ioEnt wasn't, trying to grab...");
                                for (int
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху