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

FREE ItemOwnership : 1.0.57 1.0.57

Нет прав для скачивания
C#:
#region Software License
/*

Copyright (c) 2023 Lurch/.lurch.  SignalWriter@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/
#endregion Software License

#region Release notes
// Originally Intended to be used with Metaphysics plugin, but that has gone the way of the Dodo bird
//
//
//  VER      Date         Dev           Notes                                                                       
// =======  ==========  ===========     ================================================================================================================
//
// 1.0.57   09/22/2023      Lurch       Added debugs on all hooks for testing strange problem
//
// 1.0.56   09/22/2023      Lurch       Removed '.json' from default data store name (resulted in name.json.json)
//                                      Fixed chat commands -- difference between RustPlugin and CovalencePlugin (ChatCommand -vs- Command)  
//
// 1.0.55   09/22/2023      Lurch           Fixed io.reload to actually perform a true reload.
//
//                                          Added two new chat commands: (Must have admin perm)
//                                          io.debug   which is a Debug mode toggle --> in chat /io.debug  will toggle debug on and off
//                                          io.changeowner or io.co  which will change EVERY item a specified player owns to another player id.
//                                          Takes TWO arguments - first is the FROM player's Steam ID or name (accepts one or the other)
//                                          Second argument (optional) is to TO player name or Steam ID.
//
//                                          Examples:
//
//                                          io.co 76561197970521596 Thagmeister
//                                          The above command will change everything Lurch owns to Thagmeister
//
//                                          io.co Thagmeister
//                                          The above command will change everything Thatgmeister owns to player id ZERO (no owner)
//
// 1.0.54   08/21/2023      Lurch       Cleaned out some old plugin refs (no longer needed) and wrote documentation for public distribution.
//
// 1.0.53   08/08/2023      Lurch       Patched the CanEquipItem() hook as it was still using old methods.
//
// 1.0.52   08/02/2023      Lurch       Added CanMoveItem() check so that items don't get droppped to ground if not owned
//
// 1.0.50   07/31/2023      Lurch       Changed 1.0.47's update to use OnItemPickup hook and added OnItemAddedToContainer hook
//
// 1.0.47   07/30/2023      Lurch       Added inventory rejection. An unusual exploit was discovered by @Beee & @Iron - this fix addresses that.
//
// 1.0.46   07/14/2023      Lurch       Cleanup for distro to UMOD 
//
// 0.0.45   05/02/2023      Beee        Converted all Item.UID's to ULONG from UINT per May 4th FP update
//
// 0.0.44   04/06/2023      Lurch       Attempt to subdue NRE when CanWearItem() is called with possible null iplayer object
//
// 0.0.43   04/04/2023      Lurch       Quickly added user messages for inability to Wear/Equip items - TODO: Make the messages configurable
//
// 0.0.42   03/27/2023      Lurch       Removed some debug spam
//
// 0.0.41   03/26/2023      Lurch       Attempts to prevent NRE's when XDQuest reloads                            
//
// 0.0.40   03/23/2023      Lurch       Added timer to call TimedEvaluation() every 15 seconds to remove any orphaned items
//                                      caught by watching deleted items in the OnItemRemove() hook.
//
// 0.0.39   03/21/2023      Lurch       Fixed NRE if no items in store & trying to add new item 
//
// 0.0.38   03/20/2023      Lurch       Updated IsItemOwner() to return bool? - null if no registered owner or owner is NPC or not in registry
//
// 0.0.36   03/19/2023      Lurch       Added try-catch trap for duplicate ItemUID's (Does Facepunch re-use Itemuid's ?????)
//
// 0.0.35   03/19/2023      Lurch       Refactored list storage - stop using Merge() function - now directly to/from persisted storage.
//                                      Too many items being lost somehow, so we don't cache / trust anything in memory.
//
// 0.0.32   03/18/2023      Lurch       Fix NRE for IsItemOwner() API call & add [HookMethod()] attribute
//
// 0.0.31   03/18/2023      Lurch       Added IsItemOwner() API callable method at request of @beee
//                                      Added if player.IsNpc check before adding item to list
//
// 0.0.30   03/16/2023      Lurch       Added bypass permission and checking in both CanWear and CanEquip hooks
//
// 0.0.29   03/16/2023      Lurch       Inserted Admin permission - wrapped /ioload with perms.
//                                      Cleaned up some spammy Puts statements.
//
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endregion Release notes

#region Namespaces

using System;
using Newtonsoft.Json;
using Oxide.Core;
using Facepunch.Extend;
using Oxide.Core.Libraries.Covalence;

using System.Collections.Generic;
using System.Linq;

using Oxide.Core.Plugins;
using Oxide.Game.Rust.Libraries;


#endregion Namespaces

namespace Oxide.Plugins
{
    [Info("ItemOwnership", "Lurch", "1.0.57")]
    [Description("Prevents special items from being used by other players unless items are in the shareable items list.")]
    internal class ItemOwnership : RustPlugin
    {
        #region Vars
        #region Readonly and constants

        private const bool en = true; // use of English language
        private const string VERSION = "1.0.57";
        private const string _permissionAdmin = "itemownership.admin";
        private const string _permissionBypass = "itemownership.bypass";
        private const ulong _baseSteamID = 76561197960265728L; 
        private readonly object writeLock = new object(); // v1.0.55

        #endregion Readonly and constants

        private bool _Debug_ = true;
        private bool _lookupInitialized = false;
        private Timer _itemReconcilliationTimer;
        private List<SpecialItem> _SI;
        private Configuration _config;
        private List<ulong> _deletedItemIDs;
        private Dictionary<ulong, ExtendedItem> itemLookup; 

        public enum UpdateMode { Add, ChangeOwner, Delete };

        #endregion Vars

        #region Config

        public class Configuration
        {
            [JsonProperty("VERSION: Plugin Version (Read only)")]
            public string Version { get { return VERSION; } }

            [JsonProperty("DEBUG: If true, enables debug mode messages to server console window")]
            public bool DEBUG = false;

            [JsonProperty("Name of the storage file of items used by this plugin")]
            public string ItemStoreFilename = "ItemOwnerItemStore";

            [JsonProperty("Frequency of check (in secs) to remove deleted items from storage.")]
            public int ItemReconcileTimer = 15;

            [JsonProperty("List of Rust Item ID's that are shareable.")]
            public List<int> ShareableItemIds = new List<int>()
            {
                1104520648,
                1488979457
            }; // Chainsaw, Jackhammer
        }

        protected override void LoadDefaultConfig() => _config = new Configuration();
        protected override void LoadConfig()
        {
            try
            {
                base.LoadConfig();
                _config = Config.ReadObject<Configuration>();

                if (null == _config)
                    throw new JsonException();
                _Debug_ = _config.DEBUG;

                // Check current Plugin version against stored config version
                if (VERSION != _config.Version)
                {
                    // Just re-save config to persist existing & add new fields
                    PrintWarning("Updating configuration to newer version.");
                    SaveConfig();
                }
            }
            catch (Exception ex)
            {
                PrintError($"Problem loading Configuration - creating default config \r\n--> {ex}");
                LoadDefaultConfig();
                SaveConfig();
            }
        }
        protected override void SaveConfig()
        {
            if (null == _config)
            {
                _config = new Configuration();
                PrintWarning("No stored configuration found - creating new from defaults.");
            }
            try
            {
                Config.WriteObject(_config);
            }
            catch (Exception ex)
            {
                PrintError($"ERROR :: Unable to save configuration! \r\n-->{ex}");
            }
        }


        #endregion Config

        #region Chat Commands

        [ChatCommand("io.ver")] // Users
        private void CMDVersion(BasePlayer player, string command, string[] args)
        {
            //BasePlayer player = iplayer.Object as BasePlayer;
            SendMessage(player, $"ItemOwnership version {VERSION} by Lurch.");
        }

        [ChatCommand("io.reload")] // Admins
        private void CMDReload(BasePlayer player, string command, string[] args)
        {
            if (HasPerm(player.IPlayer, _permissionAdmin))
            {
                //BasePlayer player = iplayer.Object as BasePlayer;
                SendMessage(player, $"Reloading item store");

                var tmp = GetAllPersistedSpecialItems("CMDReload()");
                int count = 0;
                if (null != tmp)
                {
                    _SI = null;
                    _SI = tmp;
                    count = tmp.Count;
                }

                SendMessage(player, $"Reloaded item store: [{count}] item(s) in memory.");

                if (count > 0 && _Debug_)
                {
                    foreach (var item in tmp)
                        SendMessage(player, $"ID:[{item.ItemUID}] Owned by:[{item.OwnerSteamID}]");
                }
            }
            else
                SendMessage(player, "No permission.");
        }

        [ChatCommand("io.changeowner")]
        private void CMDChangeOwner(BasePlayer player, string command, string[] args)
        {
            if (HasPerm(player.IPlayer, _permissionAdmin))
            {
                int newID = 0;
                if(null != args && args.Length > 0)
                {
                    //check if first arg is SteamID
                    var id = args[0];
                    ulong toPlayer = _baseSteamID;
                    ulong fromPlayer = 0UL;
                    BasePlayer temp = null;

                    if (id == "!" || id == "0")
                        fromPlayer = _baseSteamID;
                    else
                    {
                        temp = GetPlayerByNameOrID(id);
                        fromPlayer = temp.userID;
                    }

                    if (fromPlayer > 0UL)
                    {
                        if (args.Length > 1)
                        {
                            temp = GetPlayerByNameOrID(args[1]);

                            if (null != temp)
                                toPlayer = temp.userID;
                            else
                            {
                                SendMessage(player, $"Invalid 'TO' entry - unable to match [{id}] to an existing player.");
                                return;
                            }
                        }

                        if(null != _SI)
                        {
                            int changes = 0;
                            foreach(var item in _SI)
                            {
                                if (item.OwnerSteamID == fromPlayer)
                                {
                                    item.SetNewOwner(toPlayer);
                                    ++changes;
                                    if(_Debug_)
                                        SendMessage(player, $"Item:[{item.Name}] changed to owner:{toPlayer}");
                                }
                            }
                            if (changes > 0)
                            {
                                var ss = PersistSpecialItems("CMDChangeOwner()");

                                if (ss)
                                {
                                    var newSI = GetAllPersistedSpecialItems("CMDChangeOwner()");
                                    if (newSI != null && newSI.Count > 0)
                                    {
                                        _SI = newSI;

                                        // Invalidate cached lookup structure
                                        itemLookup = null;
                                        InitLookup();
                                        SendMessage(player, $"{(changes > 1 ? $"{changes} Items were" : $"One item was")} changed from owner:[{fromPlayer}] to: [{toPlayer}]");
                                    }
                                    else
                                        SendMessage(player, $"WARNING: Failed to read new ItemOwnerStore.");
                                }
                                else
                                    SendMessage(player, $"No changes made - failed to write new ItemOwnerStore.");
                            }
                            else
                                SendMessage(player, $"No items found to change! Player is a pauper!");
                        }
                        else
                            SendMessage(player, $"Store appears to be empty - no changes made.");
                    }
                    else
                        SendMessage(player, $"Invalid 'FROM' entry - unable to match [{id}] to an existing player.");
                }

                //SystemMessage(iplayer, $"Changing ownershiip of ALL items belonging to player: [{}] to {()}");
            }
            else
                SendMessage(player, "No permission.");
        }

        [ChatCommand("io.debug")]
        private void CMDDebugToggle(BasePlayer player, string command, string[] args)
        {
            if (HasPerm(player.IPlayer, _permissionAdmin))
            {
                _Debug_ = !_Debug_;
                SendMessage(player, $"{(_Debug_ ? "Debug is now on":"Debug is now off")}");
            }
            else
                SendMessage(player, $"No permission.");
        }

        [ChatCommand("io.co")]
        private void CMDChangeOwnershort(BasePlayer player, string command, string[] args)
        {
            CMDChangeOwner(player, command, args);
        }

        [ChatCommand("io.ver")]
        private void CMDVersionshort(BasePlayer player, string commnand, string[] args)
        {
            //CMDVersion(iplayer, commnand, args);
            SendMessage(player, $"ItemOwnership version {VERSION} by Lurch.");
        }

        #endregion Chat Commands

        #region OxideHooks

        private void Loaded()
        {
            LoadConfig();

            //
            // Perms
            permission.RegisterPermission(_permissionAdmin, this);
            permission.RegisterPermission(_permissionBypass, this);

        }

        private void Unload()
        {
            if (null != _itemReconcilliationTimer)
            {
                _itemReconcilliationTimer.Destroy();
                _itemReconcilliationTimer = null;
            }

            if(null != _SI)
                _SI.Clear();

            if (null != _deletedItemIDs)
                _deletedItemIDs.Clear();

            if(null != itemLookup)
                itemLookup.Clear();


        }

        private void OnItemAddedToContainer(ItemContainer container, Item item)
        {
            if (null == item || null == container) return;
            int pos = 1;
            try
            {
                BasePlayer player = container?.playerOwner ?? null;
                if (null == player || player.IsNpc) return;
                pos = 4;
                // If it's not personal inventory we dont care
                if (container == player.inventory.containerBelt || container == player.inventory.containerMain || container == player.inventory.containerWear)
                {
                    pos = 6;
                    if (!HasPerm(player.UserIDString, _permissionBypass))
                    {
                        pos = 8;
                        if (null != itemLookup && _lookupInitialized)
                        {
                            pos = 10;
                            if (itemLookup.ContainsKey(item.uid.Value))
                            {
                                pos = 12;
                                if (!itemLookup[item.uid.Value].IsShareable)
                                {
                                    pos = 14;
                                    if (itemLookup[item.uid.Value].OwnerSteamID == player.userID)
                                        return;

                                    pos = 16;
                                    SendReply(player, "This item does not belong to you...dropping it!");
                                    pos = 18;
                                    item.Drop(player.GetDropPosition(), player.GetDropVelocity());
                                    pos = 20;
                                    container.MarkDirty();
                                }
                            }
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                PrintWarning($"OnItemAddedToContainer()::Error at {pos} -->\r\n{ex}");
            }

        }

        private object OnItemPickup(Item item, BasePlayer player) // 7/31/2023 Update 1.0.50
        {
            if (null == item || null == player) return null;
            int pos = 1;
            try
            {
                if (!HasPerm(player.UserIDString, _permissionBypass))
                {
                    pos = 3;
                    if (null != itemLookup && _lookupInitialized)
                    {
                        pos = 5;
                        if (itemLookup.ContainsKey(item.uid.Value))
                        {
                            pos = 7;
                            if (!itemLookup[item.uid.Value].IsShareable)
                            {
                                pos = 9;
                                if (itemLookup[item.uid.Value].OwnerSteamID == player.userID)
                                    return null;

                                pos = 11;
                                SendReply(player, "This item does not belong to you.");
                                return false;
                            }
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                PrintWarning($"OnItemPickup()::Error at {pos} -->\r\n{ex}");
            }

            return null;
        }

        private bool CanEquipItem(PlayerInventory inventory, Item item, int targetPos)
        {
            if (null != inventory && null != item)
            {
                int pos = 1;
                try
                {
                    pos = 3;
                    BasePlayer player = inventory.containerMain.playerOwner;
                    if (null == player || player.IsNpc) return true;

                    pos = 7;
                    if ((bool)inventory.baseEntity?.IsNpc)
                        return true;

                    pos = 9;
                    if (!HasPerm(player.UserIDString, _permissionBypass))
                    {
                        if (null != itemLookup && _lookupInitialized)
                        {
                            pos = 11;
                            if (itemLookup.ContainsKey(item.uid.Value))
                            {
                                pos = 13;
                                if (!itemLookup[item.uid.Value].IsShareable)
                                {
                                    pos = 15;
                                    if (itemLookup[item.uid.Value].OwnerSteamID == player.userID)
                                        return true;

                                    pos = 17;
                                    //SystemMessage(player.IPlayer, $"This item belongs to someone else.");
                                    SendReply(player, "This item belongs to someone else.");
                                    pos = 19;
                                    item.Drop(player.GetDropPosition(), player.GetDropVelocity());
                                    return false;
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    PrintWarning($"CanEquipItem()::Error at {pos} -->\r\n{ex}");
                }
            }
            
            return true;
        }

        private object CanWearItem(PlayerInventory inventory, Item item, int targetSlot)
        {
            if (null != inventory && null != item)
            {
                if (null != inventory?.baseEntity)
                {
                    var playerId = inventory.baseEntity.userID;
                    var player = inventory.containerWear.GetOwnerPlayer();

                    if (0UL != playerId && null != player && !player.IsNpc)
                    {
                        if (!HasPerm(player.UserIDString, _permissionBypass))
                        {
                            if (null != itemLookup && _lookupInitialized)
                            {
                                if (itemLookup.ContainsKey(item.uid.Value))
                                {
                                    if (!itemLookup[item.uid.Value].IsShareable)
                                    {
                                        if (itemLookup[item.uid.Value].OwnerSteamID == playerId)
                                            return null;

                                        SendReply(player, "This item belongs to someone else.");
                                        return false;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            return null;
        }

        private object CanMoveItem(Item item, PlayerInventory playerLoot, ItemContainerId targetContainer, int targetSlot, int amount)
        {
            // Version 1.0.52
            if (null == item || null == playerLoot) return null;
            if (0 == item.uid.Value) return null;

            var player = playerLoot.baseEntity as BasePlayer;
            if (null != player && !player.IsNpc)
            {
                if (!HasPerm(player.UserIDString, _permissionBypass))
                {
                    if (null != itemLookup && _lookupInitialized)
                    {
                        if (itemLookup.ContainsKey(item.uid.Value))
                        {
                            if (!itemLookup[item.uid.Value].IsShareable)
                            {
                                if (itemLookup[item.uid.Value].OwnerSteamID == player.userID)
                                    return null;

                                SendReply(player, "This item belongs to someone else.");
                                return false;
                            }
                        }
                    }
                }
            }

            return null;

        }

        private void OnServerInitialized()
        {
            if (_Debug_) Puts("ServerInit");

            _SI = GetAllPersistedSpecialItems("ServerInit()");
            _lookupInitialized = InitLookup();


            //
            // Init persisted items
            itemLookup = new Dictionary<ulong, ExtendedItem>();
            //_SI = new List<SpecialItem>();

            //
            // Initialize fast lookup dict
            _lookupInitialized = InitLookup();

            //
            // Deleted Item ID watch
            _deletedItemIDs = new List<ulong>();

            //
            // Set Debug
            _Debug_ = _config.DEBUG;

            // Kick off repeat checks 
            if(null != _itemReconcilliationTimer)
            {
                _itemReconcilliationTimer.Destroy();
                _itemReconcilliationTimer = null;
            }

            _itemReconcilliationTimer = timer.Every(_config.ItemReconcileTimer, TimedEvaluations);
        }

        /// <summary>
        /// Called when items are deleted in-world
        /// </summary>
        /// <param name="item"></param>
        private void OnItemRemove(Item item)
        {
            // We just add the ID's in here and handle
            // every  _config.ItemReconcileTimer seconds
            if (null != item)
            {
                if(null == _deletedItemIDs)
                    _deletedItemIDs= new List<ulong>();

                _deletedItemIDs.Add(item.uid.Value);
            }
        }

        #endregion OxideHooks

        #region Operations

        private void TimedEvaluations()
        {
            int count = 0;
            var killSet = new List<ulong>(_deletedItemIDs);

            if (null != killSet && killSet.Count > 0)
            {
                _deletedItemIDs.Clear();
                _SI = GetAllPersistedSpecialItems("TimedEvaluations()");

                foreach (var killID in killSet)
                {
                    var kill = _SI.Where(x => x.ItemUID == killID).FirstOrDefault();
                    if (null != kill)
                    {
                        _SI.Remove(kill);
                        ++count;
                    }
                }

                if (count > 0)
                {
                    PersistSpecialItems("TimedEvaluations()");
                    if (_Debug_) Puts($"Deleted Item Reconcilliation: [{count}] items were removed from ItemStore.");
                }
            }
        }

        private bool PersistSpecialItems(string caller = "Self()")
        {
            var retval = true;

            if (null != _SI && _SI.Count > 0)
            {
                try
                {
                    lock (writeLock)
                    {
                        var store = new ItemStore();
                        store.InitializeStore(_SI);
                        SaveData<ItemStore>(store, _config.ItemStoreFilename);
                        if (_Debug_) Puts($"Successfully stored [{_SI.Count}] items to ItemStore file:[{_config.ItemStoreFilename}]");
                    }
                }
                catch (Exception ex)
                {
                    PrintError($"Failed to save ItemStore from caller:{caller} --> {ex}");
                    retval = false;
                }
            }
            else
            {
                PrintWarning("SpecialItems store is empty...");
                //var delStore = new ItemStore();
                //SaveData<ItemStore>(delStore, _config.ItemStoreFilename);
            }

            return retval;
        }

        private List<SpecialItem> GetAllPersistedSpecialItems(string caller = "Self()")
        {
            //if (_Debug_) Puts($"GetAllPersistedSpecialItems() called by {caller}");

            try
            {
                if (null == _config)
                    LoadConfig();

                var storeItems = new ItemStore();
                LoadData<ItemStore>(ref storeItems, _config.ItemStoreFilename);
                var storedItemData = storeItems.GetPersistedItems();

                return (storedItemData != null ? storedItemData : new List<SpecialItem>());

            }
            catch (Exception ex)
            {
                PrintError($"Failed to load special items from item store ->{ex}");
            }

            return null;
        }

        public void ModifyItemList(BasePlayer player, Item item, UpdateMode mode)
        {
            if (null != item) // player can be null in some instances like destroy item
            {
                switch (mode)
                {
                    case UpdateMode.Add:
                        AddItem(player, item);
                        break;

                    case UpdateMode.ChangeOwner:
                        ChangeOwner(player, item);
                        break;

                    case UpdateMode.Delete:
                        RemoveItem(player, item);
                        break;
                }
            }
        }

        private void AddItem(BasePlayer player, Item item)
        {
            if (null == player) return;
            if (player.IsNpc) return;
            if (null == item) return;

            try
            {
                // Check for dupe
                if (itemLookup.ContainsKey(item.uid.Value))
                    return;

                var pid = player.userID;
                //
                // Create new tracking item
                var myItem = new SpecialItem();
                myItem.ItemUID = item.uid.Value;
                myItem.OwnerSteamID = pid;
                myItem.CreatorID = pid;
                // Check shared items list
                myItem.IsShareable = _config.ShareableItemIds.Contains(item.info.itemid);
                //
                // Read master list from disk & add new item to it
                _SI = GetAllPersistedSpecialItems("AddItem()");
                _SI.Add(myItem);
                //
                // Create fast lookup table entry
                if (_lookupInitialized)
                    AddItemToLookup(player, item);
                else
                    _lookupInitialized = InitLookup();

                // Write it all back out
                var ss = PersistSpecialItems("AddItem()"); // send back to disk

                if(_Debug_) Puts($"{(ss ? "Sucessfully" : "WARNING!! unsuccessfully")} stored item:{item.uid.Value} belonging to player:{player.displayName} - {pid}");
            }
            catch(Exception ex)
            {
                PrintError($"Adding item error -->\r\n {ex}");
            }
        }

        private void RemoveItem(BasePlayer player, Item item)
        {
            if (null == item) return;

            if (itemLookup.ContainsKey(item.uid.Value))
            {
                // 
                // Get master list
                _SI = GetAllPersistedSpecialItems("RemoveItem()");

                var si = _SI.Where(x => x.ItemUID == item.uid.Value).FirstOrDefault();
                if (null != si)
                {
                    _SI.Remove(si);
                    if(!_lookupInitialized)
                        _lookupInitialized = InitLookup();

                    itemLookup.Remove(item.uid.Value);
                    //
                    // Save snapshot to disk
                    PersistSpecialItems("RemoveItem()");
                }
            }
        }

        public bool ChangeOwner(BasePlayer player, Item item)
        {
            if (null == player || null == item) return false;

            _SI = GetAllPersistedSpecialItems("ChangeOwner()");
            var si = _SI.Where(x => x.ItemUID == item.uid.Value).FirstOrDefault();
            if (null != si)
            {
                si.SetNewOwner(player.userID);

                if (!_lookupInitialized)
                    _lookupInitialized = InitLookup();

                if (null != itemLookup && itemLookup.ContainsKey(item.uid.Value))
                {
                    var it = itemLookup[item.uid.Value];
                    it.OwnerSteamID = player.userID;
                }
                else
                {
                    PrintWarning($"Missing item:{item.uid.Value} Name:{item.name} from fast lookup - added.");
                    AddItemToLookup(player, item);
                }

                //
                // Save snapshot to disk
                PersistSpecialItems("ChangeOwner()");
                return true;
            }
            else
            {
                AddItem(player, item);
                return true;
            }
        }

        private bool InitLookup()
        {
            int dupes = 0;
            if (null == itemLookup)
                itemLookup = new Dictionary<ulong, ExtendedItem>();

            if (null == _SI) return false;

            foreach (var item in _SI)
            {
                var extItem = new ExtendedItem();
                extItem.IsShareable = item.IsShareable;
                extItem.OwnerSteamID = item.OwnerSteamID;
                try
                {
                    // Does FP re-use item.uid's between sessions???? (Not yet anyway)
                    itemLookup.Add(item.ItemUID, extItem);
                }
                catch 
                {
                    ++dupes; 
                    //var myItem = itemLookup[item.ItemUID];
                    //PrintError($"Caught duplicate item key! New:{item.ItemUID}:{item.Name} Owner:{item.OwnerSteamID} Existing item:{item.ItemUID} Owner:{myItem.OwnerSteamID}");
                }
            }

            if (dupes > 0)
                PrintWarning($"Caught [{dupes}] duplicate item entries in ItemStore upon init() - these items were ignored.");

            return true;
        }

        private bool AddItemToLookup(BasePlayer player, Item item)
        {
            if(null == player || null == item) return false;

            //
            // Create fast lookup table entry
            var extItem = new ExtendedItem();
            if (null != _config)
            {
                //
                // Check to see if new item is listed as a shareable item
                if (_config.ShareableItemIds.Contains(item.info.itemid))
                    extItem.IsShareable = true;

                extItem.OwnerSteamID = player.userID;

                //
                // Add new item to fast lookup
                if (!itemLookup.ContainsKey(item.uid.Value))
                {
                    itemLookup.Add(item.uid.Value, extItem);
                    return true;
                }
                else
                    PrintError($"ItemID:{item.uid.Value} was already in fast lookup table");
            }

            return false;
        }

        #endregion Operations

        #region PluginAPI

        /// <summary>
        /// Callable API to add/remove item from list
        /// </summary>
        /// <param name="player"></param>
        /// <param name="item"></param>
        /// <param name="action"></param>
        [HookMethod("UpdateItemList")]
        public void UpdateItemList(BasePlayer player, Item item, int action)
        {
            var mode = (UpdateMode)action;
            ModifyItemList(player, item, mode);
        }

        /// <summary>
        /// Callable API to determine if item is owned by player
        /// </summary>
        /// <param name="player"></param>
        /// <param name="item"></param>
        /// <returns>True if item is owned by player, null if no owner/npc owner/item doesnt exist in registry, false otherwise</returns>
        [HookMethod("IsItemOwner")]
        public bool? IsItemOwner(BasePlayer player, Item item)
        {
            if(null == itemLookup)
            {
                GetAllPersistedSpecialItems("API Call to IsItemOwner()");
                InitLookup();
            }

            if (itemLookup.ContainsKey(item.uid.Value))
            {
                // NPC or no owner registered
                if (itemLookup[item.uid.Value].OwnerSteamID < _baseSteamID)
                    return null;

                return itemLookup[item.uid.Value].OwnerSteamID == player.userID;
            }

            return null;
        }

        #endregion PluginAPI

        #region Utils

        private bool HasPerm(string playerID, string perm) => permission.UserHasPermission(playerID, perm);
        private bool HasPerm(IPlayer player, string perm) => player.HasPermission(perm);
        private static void LoadData<T>(ref T data, string filename = null) =>
            data = Interface.Oxide.DataFileSystem.ReadObject<T>(filename ?? nameof(ItemOwnership));

        private static void SaveData<T>(T data, string filename = null) =>
            Interface.Oxide.DataFileSystem.WriteObject(filename ?? nameof(ItemOwnership), data);

        private void SystemMessage(IPlayer player, string textOrLang, params object[] args)
        {
            if (null != player)
            {
                if (player.IsConnected)
                {
                    //string message = GetLang(textOrLang, player.Id, args);
                    //player.Reply(message != textOrLang ? message : textOrLang);
                    player.Reply(textOrLang);
                }
                else
                    PrintWarning($"Player response (offline): {textOrLang}");
            }
        }

        private static BasePlayer GetPlayerBySteamID(ulong steamID)
        {
            var plyr = BasePlayer.activePlayerList.Where(x => x != null && x.userID == steamID).FirstOrDefault();
            return plyr ?? null;
        }

        private static BasePlayer GetPlayerByName(string name)
        {
            BasePlayer foundPlayer = null;

            foreach (var player in BasePlayer.activePlayerList)
            {
                if (player.displayName.Equals(name, StringComparison.OrdinalIgnoreCase)
                   || player.UserIDString.Contains(name, StringComparison.OrdinalIgnoreCase)
                   || player.displayName.Contains(name, StringComparison.OrdinalIgnoreCase))
                {
                    foundPlayer = player;
                }
            }
            return foundPlayer;
        }

        private static BasePlayer GetPlayerByNameOrID(string nameOrID)
        {
            BasePlayer myPlayer = null;
            if (nameOrID.IsSteamId())
                myPlayer = GetPlayerBySteamID(ToSteamID(nameOrID));
            else
                myPlayer = GetPlayerByName(nameOrID);

            return myPlayer;
        }

        private void SendMessage(BasePlayer player, string msg)
        {
            SendReply(player, $"{msg}");
        }

        private static ulong ToSteamID(string id)
        {
            ulong val = 0UL;
            if(ulong.TryParse(id, out val))
            {
                return val;
            }

            return 0;
        }
        #endregion Utils

        #region Classes

        [Serializable]
        public class ItemStore
        {
            [JsonProperty]
            public string Version
            { get; set; } = VERSION;

            [JsonProperty]
            public bool IsValid
            { get; private set; }

            [JsonProperty]
            public bool EmptyData
            { get; private set; } = true;

            [JsonProperty]
            public int RawRecordCount
            { get; private set; }

            [JsonProperty]
            public int RecCountOnDeserialize
            { get; private set; }

            [JsonProperty]
            public HashSet<SpecialItem> SpecialItems
            { get; private set; }

            public void InitializeStore(List<SpecialItem> specialItems)
            {
                if (null != specialItems && specialItems.Count > 0)
                {
                    this.RawRecordCount = specialItems.Count;
                    this.SpecialItems = new HashSet<SpecialItem>(specialItems);
                    this.EmptyData = (null == this.SpecialItems ? true : false);
                    this.IsValid = true;
                }
            }

            public List<SpecialItem> GetPersistedItems()
            {
                if (null != this.SpecialItems)
                {
                    var si = new List<SpecialItem>(SpecialItems);

                    if(null == si || si.Count < 1)
                    {
                        // Try a different deserialization approach.
                        si = new List<SpecialItem>();
                        
                        foreach(var record in SpecialItems)
                        {
                            var rec = new SpecialItem();
                            rec.IsShareable = record.IsShareable;
                            rec.IsValid = record.IsValid;
                            rec.CreatorID = record.CreatorID;
                            rec.ItemUID = record.ItemUID;
                            rec.SkinID = record.SkinID;
                            rec.RustShortName = record.RustShortName;
                            rec.Name = record.Name;
                            rec.OwnerSteamID = record.OwnerSteamID;
                            rec.AltDeserialize = true;
                            
                            si.Add(rec);
                        }
                    }

                    this.EmptyData = (null == si ? false : true);
                    return si;
                }

                return null;
            }

            public ItemStore()
            {

            }
        }

        [Serializable]
        public class SpecialItem
        {
            private ulong _ownerId = 0UL;

            [JsonProperty]
            public bool IsValid
            { get; set; } 

            [JsonProperty]
            public ulong OwnerSteamID
            {
                get {  return _ownerId; }
                set
                {
                    _ownerId = value;
                    this.IsValid = true;
                }
            }

            [JsonProperty]
            public ulong CreatorID
            { get; set; }

            [JsonProperty]
            public string Name
            { get; set; }

            [JsonProperty]
            public string RustShortName
            { get; set; }

            [JsonProperty]
            public ulong SkinID
            { get; set; } = 0UL;

            [JsonProperty]
            public ulong ItemUID
            { get; set; } = 0UL;

            [JsonProperty]
            public bool IsShareable
            { get; set; }

            [JsonProperty]
            public bool AltDeserialize
            { get; set; } = false;

            public SpecialItem()
            {

            }

            public void SetNewOwner(ulong steamID)
            {
                OwnerSteamID = steamID;
            }
        }

        [Serializable]
        public class ExtendedItem
        {
            public ulong OwnerSteamID
            { get; set; }
            public bool IsShareable
            { get; set; }

            public ExtendedItem() { }
        }

        #endregion Classes
    }
}

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