Beispiel #1
0
    private void SpawnNewPlayer(Network.Connection connection)
    {
        BasePlayer.SpawnPoint spawnPoint = ServerMgr.FindSpawnPoint();
        BasePlayer            player     = GameManager.server.CreateEntity("assets/prefabs/player/player.prefab", spawnPoint.pos, spawnPoint.rot, true).ToPlayer();

        if (Interface.CallHook("OnPlayerSpawn", player) != null)
        {
            return;
        }
        player.health                = 0f;
        player.lifestate             = BaseCombatEntity.LifeState.Dead;
        player.ResetLifeStateOnSpawn = false;
        player.limitNetworking       = true;
        player.Spawn();
        player.limitNetworking = false;
        player.PlayerInit(connection);
        if (SleepingBag.FindForPlayer(player.userID, true).Length != 0 || player.hasPreviousLife)
        {
            player.SendRespawnOptions();
        }
        else
        {
            player.Respawn();
        }
        DebugEx.Log(string.Concat(new object[] { player.net.connection.ToString(), " joined [", player.net.connection.os, "/", player.net.connection.ownerid, "]" }), StackTraceLogType.None);
    }
Beispiel #2
0
        void cmdChatRemoveHome(BasePlayer player, string command, string[] args)
        {
            if (args.Length != 1)
            {
                SendReply(player, Messages["removehomeArgsError"]);
                return;
            }
            if (!homes.ContainsKey(player.userID))
            {
                SendReply(player, Messages["homesmissing"]);
                return;
            }
            var name        = args[0];
            var playerHomes = homes[player.userID];

            if (!playerHomes.ContainsKey(name))
            {
                SendReply(player, Messages["homenotexist"]);
                return;
            }
            foreach (var sleepingBag in SleepingBag.FindForPlayer(player.userID, true))
            {
                if (Vector3.Distance(sleepingBag.transform.position, playerHomes[name]) < 1)
                {
                    sleepingBag.Kill();
                    break;
                }
            }
            playerHomes.Remove(name);
            SendReply(player, Messages["removehomesuccess"]);
        }
Beispiel #3
0
    public static bool SpawnPlayer(BasePlayer player, uint sleepingBag)
    {
        SleepingBag[] sleepingBagArray = SleepingBag.FindForPlayer(player.userID, true);
        SleepingBag   sleepingBag1     = sleepingBagArray.FirstOrDefault <SleepingBag>((SleepingBag x) => {
            if (x.deployerUserID != player.userID || x.net.ID != sleepingBag)
            {
                return(false);
            }
            return(x.unlockTime < UnityEngine.Time.realtimeSinceStartup);
        });

        if (sleepingBag1 == null)
        {
            return(false);
        }
        Vector3    vector3     = sleepingBag1.transform.position + sleepingBag1.spawnOffset;
        Quaternion quaternion  = sleepingBag1.transform.rotation;
        Quaternion quaternion1 = Quaternion.Euler(0f, quaternion.eulerAngles.y, 0f);

        player.RespawnAt(vector3, quaternion1);
        SleepingBag[] sleepingBagArray1 = sleepingBagArray;
        for (int i = 0; i < (int)sleepingBagArray1.Length; i++)
        {
            SleepingBag sleepingBag2 = sleepingBagArray1[i];
            if (Vector3.Distance(vector3, sleepingBag2.transform.position) <= ConVar.Server.respawnresetrange)
            {
                sleepingBag2.unlockTime = UnityEngine.Time.realtimeSinceStartup + sleepingBag2.secondsBetweenReuses;
            }
        }
        return(true);
    }
    private void SpawnNewPlayer(Network.Connection connection)
    {
        BasePlayer.SpawnPoint spawnPoint = ServerMgr.FindSpawnPoint();
        BasePlayer            player     = GameManager.server.CreateEntity("assets/prefabs/player/player.prefab", spawnPoint.pos, spawnPoint.rot, true).ToPlayer();

        if (Interface.CallHook("OnPlayerSpawn", (object)player) != null)
        {
            return;
        }
        player.health                = 0.0f;
        player.lifestate             = BaseCombatEntity.LifeState.Dead;
        player.ResetLifeStateOnSpawn = false;
        player.limitNetworking       = true;
        player.Spawn();
        player.limitNetworking = false;
        player.PlayerInit(connection);
        if (SleepingBag.FindForPlayer(player.userID, true).Length == 0 && !player.hasPreviousLife)
        {
            player.Respawn();
        }
        else
        {
            player.SendRespawnOptions();
        }
        DebugEx.Log((object)(((object)player.net.get_connection()).ToString() + " joined [" + (object)player.net.get_connection().os + "/" + (object)(ulong)player.net.get_connection().ownerid + "]"), (StackTraceLogType)0);
    }
        private void PushOut(BasePlayer kickedPlayer)
        {
            var  bags         = SleepingBag.FindForPlayer(kickedPlayer.userID, true);
            bool willTeleport = bags.Length > 0 && TeleportBeforeKick;
            int  kickseconds  = KickWaitSeconds;

            if (willTeleport)
            {
                kickedPlayer.ChatMessage(
                    GetMessage("TeleportKickNotice", kickedPlayer.UserIDString).Replace("{kickseconds}", KickWaitSeconds.ToString())
                    );
            }
            else
            {
                kickedPlayer.ChatMessage(
                    GetMessage("KickNotice", kickedPlayer.UserIDString).Replace("{kickseconds}", KickWaitSeconds.ToString())
                    );
            }

            timer.Once(kickseconds, () =>
            {
                kickedPlayer.ChatMessage(GetMessage("KickNow", kickedPlayer.UserIDString));
                if (willTeleport)
                {
                    var destinationBag = bags[UnityEngine.Random.Range(0, bags.Length)];
                    kickedPlayer.Teleport(destinationBag.ServerPosition);
                }
                kickedPlayer.Kick(
                    GetMessage("KickedForPriorPlayer", kickedPlayer.UserIDString)
                    );
            });
        }
Beispiel #6
0
    public static bool SpawnPlayer(BasePlayer player, uint sleepingBag)
    {
        SleepingBag[] forPlayer    = SleepingBag.FindForPlayer(player.userID, true);
        SleepingBag   sleepingBag1 = ((IEnumerable <SleepingBag>)forPlayer).FirstOrDefault <SleepingBag>((Func <SleepingBag, bool>)(x =>
        {
            if ((long)x.deployerUserID == (long)player.userID && x.net.ID == (int)sleepingBag)
            {
                return((double)x.unlockTime < (double)Time.get_realtimeSinceStartup());
            }
            return(false);
        }));

        if (Object.op_Equality((Object)sleepingBag1, (Object)null))
        {
            return(false);
        }
        Vector3    position  = Vector3.op_Addition(((Component)sleepingBag1).get_transform().get_position(), sleepingBag1.spawnOffset);
        Quaternion rotation1 = ((Component)sleepingBag1).get_transform().get_rotation();
        Quaternion rotation2 = Quaternion.Euler(0.0f, (float)((Quaternion) ref rotation1).get_eulerAngles().y, 0.0f);

        player.RespawnAt(position, rotation2);
        foreach (SleepingBag sleepingBag2 in forPlayer)
        {
            if ((double)Vector3.Distance(position, ((Component)sleepingBag2).get_transform().get_position()) <= (double)Server.respawnresetrange)
            {
                sleepingBag2.unlockTime = Time.get_realtimeSinceStartup() + sleepingBag2.secondsBetweenReuses;
            }
        }
        return(true);
    }
 private void ResetSpawnTargets(BasePlayer player)
 {
     SleepingBag[] bags = SleepingBag.FindForPlayer(player.userID, true);
     foreach (SleepingBag bag in bags)
     {
         bag.unlockTime = 0f;
     }
 }
 private void ResetSpawnTargets(BasePlayer player)
 {
     SleepingBag[] bags = SleepingBag.FindForPlayer(player.userID, true);
     foreach (var entry in bags)
     {
         BagCooldown.SetValue(entry, 0);
     }
 }
 void RemoveSleepingBagsFor(ulong player_id)
 {
     if (Config.Get <bool>("RemoveMurdererBags"))
     {
         foreach (SleepingBag bag in SleepingBag.FindForPlayer(player_id, true))
         {
             bag.Kill();
         }
     }
 }
Beispiel #10
0
        List <Dictionary <string, object> > FindSleepingBags(string userid)
        {
            var   bags    = new List <Dictionary <string, object> >();
            ulong ulongid = ulong.Parse(userid);

            foreach (SleepingBag bag in SleepingBag.FindForPlayer(ulongid, true))
            {
                bags.Add(GetSleepingBagData(bag));
            }

            return(bags);
        }
Beispiel #11
0
        private bool playerSpawnedAtSleepingBag(BasePlayer player)
        {
            SleepingBag[] sleepingBags = SleepingBag.FindForPlayer(player.userID, false);
            foreach (SleepingBag sleepingBag in sleepingBags)
            {
                if (sleepingBag.transform.position.Equals(player.transform.position))
                {
                    return(true);
                }
            }

            return(false);
        }
 public void SetCooldowns(BasePlayer player, Vector3 deathPosition)
 {
     foreach (var bag in SleepingBag.FindForPlayer(player.userID, true))
     {
         float distance       = bag.Distance2D(player);
         float targetCooldown = Mathf.Clamp(Settings.MaxCooldown - distance / Settings.CooldownPerDistance, 0, Settings.MaxCooldown);
         //Puts($"Target cooldown for distance {distance} is {targetCooldown}");
         if (bag.unlockSeconds < targetCooldown)
         {
             SetBagCooldown(bag, targetCooldown);
         }
     }
 }
Beispiel #13
0
        bool CheckMaxBags(ulong userid)
        {
            int count = 0;

            foreach (SleepingBag bag in SleepingBag.FindForPlayer(userid, true))
            {
                count++;
            }

            if (count > MaximumBags)
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
Beispiel #14
0
    public static bool DestroyBag(BasePlayer player, uint sleepingBag)
    {
        SleepingBag forPlayer = SleepingBag.FindForPlayer(player.userID, sleepingBag, false);

        if (Object.op_Equality((Object)forPlayer, (Object)null))
        {
            return(false);
        }
        if (forPlayer.canBePublic)
        {
            forPlayer.SetPublic(true);
            forPlayer.deployerUserID = 0UL;
        }
        else
        {
            forPlayer.Kill(BaseNetworkable.DestroyMode.None);
        }
        player.SendRespawnOptions();
        return(true);
    }
Beispiel #15
0
    public static bool DestroyBag(BasePlayer player, uint sleepingBag)
    {
        SleepingBag sleepingBag1 = SleepingBag.FindForPlayer(player.userID, sleepingBag, false);

        if (sleepingBag1 == null)
        {
            return(false);
        }
        if (!sleepingBag1.canBePublic)
        {
            sleepingBag1.Kill(BaseNetworkable.DestroyMode.None);
        }
        else
        {
            sleepingBag1.SetPublic(true);
            sleepingBag1.deployerUserID = (ulong)0;
        }
        player.SendRespawnOptions();
        return(true);
    }
Beispiel #16
0
        string Find(BasePlayer player, string[] args)
        {
            string returnstring = string.Empty;

            if (!hasPermission(player, findPermission, findAuthlevel))
            {
                return(GetMsg("You don't have the permission to use this command.", player));
            }

            if (args == null || args.Length == 0)
            {
                return(GetMsg("ListCommands", player));
            }
            var puserid = player == null ? 0L : player.userID;

            switch (args[0].ToLower())
            {
            case "player":
            case "bag":
            case "cupboard":
            case "building":
                if (args.Length == 1)
                {
                    return(GetMsg("You need to select a target player.", player));
                }
                var f = FindPlayerID(args[1], player);
                if (!(f is ulong))
                {
                    return(f.ToString());
                }
                ulong targetID = (ulong)f;
                var   d        = GetPlayerInfo(targetID);
                returnstring = d.ToString() + ":\n\r";
                switch (args[0].ToLower())
                {
                case "player":
                    var p = FindPosition(targetID);
                    if (p == null)
                    {
                        returnstring += GetMsg("This player doesn't have a position", player);
                    }
                    else
                    {
                        d.AddFind("Position", (Vector3)p, string.Empty);
                    }
                    break;

                case "bag":
                    var bs = SleepingBag.FindForPlayer(targetID, true).ToList();
                    if (bs.Count == 0)
                    {
                        returnstring += GetMsg("This player doesn't have any bags", player);
                    }
                    foreach (var b in bs)
                    {
                        d.AddFind(b.ShortPrefabName, b.transform.position, b.niceName);
                    }
                    break;

                case "cupboard":
                    var cs = Resources.FindObjectsOfTypeAll <BuildingPrivlidge>().Where(x => x.authorizedPlayers.Any((ProtoBuf.PlayerNameID z) => z.userid == targetID)).ToList();
                    if (cs.Count == 0)
                    {
                        returnstring += GetMsg("This player doesn't have any cupboard privileges", player);
                    }
                    foreach (var c in cs)
                    {
                        d.AddFind("Tool Cupboard", c.transform.position, string.Empty);
                    }
                    break;

                case "building":
                    var bb = Resources.FindObjectsOfTypeAll <BuildingBlock>().Where(x => x.OwnerID == targetID).ToList();
                    if (bb.Count == 0)
                    {
                        returnstring += GetMsg("This player hasn't built anything yet", player);
                    }
                    var dic = new Dictionary <uint, Dictionary <string, object> >();
                    foreach (var b in bb)
                    {
                        if (!dic.ContainsKey(b.buildingID))
                        {
                            dic.Add(b.buildingID, new Dictionary <string, object>
                            {
                                { "pos", b.transform.position },
                                { "num", 0 }
                            });
                        }
                        dic[b.buildingID]["num"] = (int)dic[b.buildingID]["num"] + 1;
                    }
                    foreach (var c in dic)
                    {
                        d.AddFind("Building", (Vector3)c.Value["pos"], c.Value["num"].ToString());
                    }
                    break;

                default:
                    break;
                }
                for (int i = 0; i < d.Data.Count; i++)
                {
                    returnstring += i.ToString() + " - " + d.Data[i].ToString() + "\n\r";
                }
                if (cachedFinder.ContainsKey(puserid))
                {
                    cachedFinder[puserid].Data.Clear();
                    cachedFinder[puserid] = null;
                    cachedFinder.Remove(puserid);
                }
                cachedFinder.Add(puserid, d);
                break;

            case "item":
                if (args.Length < 3)
                {
                    return(GetMsg("usage: /find item ITEMNAME MINAMOUNT optional:STEAMID.", player));
                }
                var   pu       = GetPlayerInfo(puserid);
                var   itemname = args[1].ToLower();
                ulong ownerid  = 0L;
                if (args.Length > 3)
                {
                    ulong.TryParse(args[3], out ownerid);
                }
                var itemamount = 0;
                if (!(int.TryParse(args[2], out itemamount)))
                {
                    return(GetMsg("usage: /find item ITEMNAME MINAMOUNT optional:STEAMID.", player));
                }
                ItemDefinition item = null;
                for (int i = 0; i < ItemManager.itemList.Count; i++)
                {
                    if (ItemManager.itemList[i].displayName.english.ToLower() == itemname)
                    {
                        item = ItemManager.itemList[i];
                        break;
                    }
                }
                if (item == null)
                {
                    return(GetMsg("You didn't use a valid item name.", player));
                }
                foreach (StorageContainer sc in Resources.FindObjectsOfTypeAll <StorageContainer>())
                {
                    ItemContainer inventory = sc.inventory;
                    if (inventory == null)
                    {
                        continue;
                    }
                    List <Item> list   = inventory.itemList.FindAll((Item x) => x.info.itemid == item.itemid);
                    int         amount = 0;
                    if (amount < itemamount)
                    {
                        continue;
                    }
                    pu.AddFind("Box", sc.transform.position, amount.ToString());
                }
                foreach (BasePlayer bp in Resources.FindObjectsOfTypeAll <BasePlayer>())
                {
                    PlayerInventory inventory = player.inventory;
                    if (inventory == null)
                    {
                        continue;
                    }
                    int amount = inventory.GetAmount(item.itemid);
                    if (amount < itemamount)
                    {
                        continue;
                    }
                    Dictionary <string, object> scdata = new Dictionary <string, object>();
                    pu.AddFind(string.Format("{0} {1}", player.userID.ToString(), player.displayName), bp.transform.position, amount.ToString());
                }
                for (int i = 0; i < pu.Data.Count; i++)
                {
                    returnstring += i.ToString() + " - " + pu.Data[i].ToString() + "\n\r";
                }
                if (cachedFinder.ContainsKey(puserid))
                {
                    cachedFinder[puserid].Data.Clear();
                    cachedFinder[puserid] = null;
                    cachedFinder.Remove(puserid);
                }
                cachedFinder.Add(puserid, pu);
                break;

            case "tp":
                if (player == null)
                {
                    return(GetMsg("You are using the console, you can't tp!", player));
                }
                if (!cachedFinder.ContainsKey(puserid))
                {
                    return(GetMsg("You didn't find anything yet", player));
                }
                if (args.Length == 1)
                {
                    return(GetMsg("You need to select a target findid.", player));
                }
                var fp = cachedFinder[puserid];
                var id = 0;
                int.TryParse(args[1], out id);
                if (id >= fp.Data.Count)
                {
                    return(GetMsg("This id is out of range.", player));
                }

                var data = cachedFinder[puserid].Data[id];
                player.MovePosition(data.Pos);
                player.ClientRPCPlayer(null, player, "ForcePositionTo", data.Pos);
                returnstring += data.ToString();
                break;

            default:
                returnstring += GetMsg("ListCommands", player);
                break;
            }



            return(returnstring);
        }
Beispiel #17
0
 public virtual SleepingBag[] FindSleepingBagsForPlayer(ulong playerID, bool ignoreTimers)
 {
     return(SleepingBag.FindForPlayer(playerID, ignoreTimers));
 }
Beispiel #18
0
        // command delegator
        void CommandDelegator(BasePlayer player, string command, string[] args)
        {
            if (!hasPermission(player, PermCanUse))
            {
                return;
            }

            string message = "InvalidCommand";

            // assume args[0] is the command (beyond /bed)
            if (args != null && args.Length > 0)
            {
                command = args[0];
            }
            // shift arguments
            if (args != null)
            {
                if (args.Length > 1)
                {
                    args = args.Skip(1).ToArray();
                }
                else
                {
                    args = new string[] { }
                };
            }
            object[] opts = new object[] { command };
            if (Enum.IsDefined(typeof(Command), command))
            {
                Command cmd = (Command)Enum.Parse(typeof(Command), command);
                if ((!hasPermission(player, PermCanSharePublic) && (cmd == Command.share || cmd == Command.unshare)) ||
                    (!hasPermission(player, PermCanSharePrivate) && (cmd == Command.sharewith || cmd == Command.unsharewith)))
                {
                    return;
                }
                switch (cmd)
                {
                case Command.clear:
                    if (hasPermission(player, PermCanClear))
                    {
                        HandleClear(out message, out opts);
                    }
                    else
                    {
                        message = "NoClearPerm";
                    }
                    break;

                case Command.share:
                    HandleShare(player, true, null, out message, out opts);
                    break;

                case Command.sharewith:
                    if (args == null && args.Length == 0)
                    {
                        message = "InvalidArguments";
                        break;
                    }
                    HandleShare(player, true, args, out message, out opts);
                    break;

                case Command.status:
                    HandleStatus(player, out message, out opts);
                    break;

                case Command.unshare:
                    HandleShare(player, false, null, out message, out opts);
                    break;

                case Command.unsharewith:
                    if (args == null && args.Length == 0)
                    {
                        message = "InvalidArguments";
                        break;
                    }
                    HandleShare(player, false, args, out message, out opts);
                    break;

                default:
                    break;
                }
            }
            else
            {
                ShowCommands(out message, out opts);
            }
            if (message != null && message != "")
            {
                SendMessage(player, message, opts);
            }
        }

        // handle sharing/unsharing
        void HandleShare(BasePlayer player, bool share, string[] args, out string message, out object[] opts)
        {
            message = "ShareSuccess";
            opts    = new object[] { "bag", share ? "shared" : "unshared" };

            bool          with    = args != null;
            bool          all     = false;
            List <ulong>  players = new List <ulong>();
            List <string> names   = new List <string>();

            if (with)
            {
                foreach (string s in args)
                {
                    if (!share && s == "all")
                    {
                        all = true;
                        break;
                    }
                    BasePlayer p = rust.FindPlayer(s);
                    if (p == null)
                    {
                        names.Add(s);
                        continue;
                    }
                    players.Add(p.userID);
                }
            }

            if (with && !all && (players.Count == 0 || players.Count != args.Length))
            {
                message = "PlayersNotFound";
                opts    = new object[] { string.Join(", ", names.ToArray()) };
                return;
            }

            object entity;

            if (GetRaycastTarget(player, out entity) && entity is SleepingBag)
            {
                SleepingBag bag = entity as SleepingBag;
                if (bag.ShortPrefabName == BedPrefabName)
                {
                    opts[0] = "bed";
                }
                if (bag.deployerUserID != player.userID && !isAdmin(player))
                {
                    message = "CannotShareOther";
                    opts[1] = "share";
                    return;
                }
                else
                {
                    if (share)
                    {
                        bag.secondsBetweenReuses = 0f;
                        data.AddOrUpdateBag(bag.net.ID, bag.deployerUserID, players);
                        playerNameCache[player.userID] = player.displayName;
                    }
                    else
                    {
                        if (!data.RemoveOrUpdateBag(bag.net.ID, players, all))
                        {
                            message = "NotShared";
                        }
                    }
                }
            }
            else
            {
                message = "NoBag";
                opts    = new object[] { };
            }
        }

        // handle checking status of a bed/bag
        void HandleStatus(BasePlayer player, out string message, out object[] opts)
        {
            message = "Status";
            opts    = new object[] { "bag", "unshared" };
            object entity;

            if (GetRaycastTarget(player, out entity) && entity is SleepingBag)
            {
                SleepingBag bag = entity as SleepingBag;
                if (bag.ShortPrefabName == BedPrefabName)
                {
                    opts[0] = "bed";
                }
                SharedBagInfo i = data.sharedBags.FirstOrDefault(s => s.bagId == bag.net.ID);
                if (i != null)
                {
                    opts[1] = "shared " + (i.isPublic ? " (public)" : " (private)");
                }
            }
            else
            {
                message = "NoBag";
                opts    = new object[] { };
            }
        }

        // handle clearing shared bag/beds
        void HandleClear(out string message, out object[] opts)
        {
            message = "ClearSuccess";
            opts    = new object[] { data.sharedBags.Count };
            data.sharedBags.Clear();
            SaveData();
        }

        #endregion

        #region Hooks

        // on player death, wait for 1s then rebuild respawnInformation including shared beds/bags
        object OnPlayerDie(BasePlayer player, HitInfo hitinfo)
        {
            if (!data.HasSharedBags() || hasPermission(player, PermNoShare, false))
            {
                return(null);
            }
            // after 1 second, send player updated respawn info
            timer.Once(1f, () => {
                using (RespawnInformation respawnInformation = Pool.Get <RespawnInformation>())
                {
                    respawnInformation.spawnOptions = Pool.Get <List <RespawnInformation.SpawnOptions> >();
                    SleepingBag[] sleepingBagArray  = SleepingBag.FindForPlayer(player.userID, true);
                    for (int i = 0; i < (int)sleepingBagArray.Length; i++)
                    {
                        SleepingBag sleepingBag = sleepingBagArray[i];
                        if (data.sharedBags.Count(s => s.bagId == sleepingBag.net.ID) > 0 || dummyBags.ContainsKey(sleepingBag.net.ID))
                        {
                            continue;
                        }
                        RespawnInformation.SpawnOptions d = Pool.Get <RespawnInformation.SpawnOptions>();
                        d.id            = sleepingBag.net.ID;
                        d.name          = sleepingBag.niceName;
                        d.type          = RespawnInformation.SpawnOptions.RespawnType.SleepingBag;
                        d.unlockSeconds = sleepingBag.unlockSeconds;
                        respawnInformation.spawnOptions.Add(d);
                    }
                    respawnInformation.previousLife = SingletonComponent <ServerMgr> .Instance.persistance.GetLastLifeStory(player.userID);
                    respawnInformation.fadeIn       = (respawnInformation.previousLife == null ? false : respawnInformation.previousLife.timeDied > (Epoch.Current - 5));
                    player.ClientRPCPlayer(null, player, "OnRespawnInformation", respawnInformation);
                }
            });
            // after 6 seconds, build/display shared bag/bed UI
            if (data.HasSharedBags())
            {
                timer.Once(6f, () => ShowGUI(player));
            }
            return(null);
        }

        // on respawn, destroy dummy bags and gui
        object OnPlayerRespawn(BasePlayer player)
        {
            DestroyDummyBags(player);
            DestroyGUI(player);
            return(null);
        }

        #endregion

        #region GUI

        // find or create a PlayerUI
        PlayerUI FindPlayerUI(BasePlayer player)
        {
            PlayerUI ui = playerUIs.FirstOrDefault(u => u.UserId == player.userID);

            if (ui == null)
            {
                playerUIs.Add(ui = new PlayerUI(player));
            }
            return(ui);
        }

        void ShowGUI(BasePlayer player)
        {
            PlayerUI ui      = FindPlayerUI(player);
            bool     dirty   = false;
            int      counter = 0;

            foreach (SharedBagInfo entry in data.sharedBags.Where(i => i.isPublic || i.sharedWith.Contains(player.userID)))
            {
                SleepingBag sleepingBag = SleepingBag.FindForPlayer(entry.owner, entry.bagId, true);
                if (sleepingBag == null)
                {
                    dirty = true;                     // no longer a valid shared bag
                    continue;
                }
                uint bagId;
                if (SpawnDummyBag(sleepingBag, player, out bagId))
                {
                    string messageName = "SharedBagNameText";
                    if (!entry.isPublic)
                    {
                        messageName = "SharedBagNameTextPrivate";
                    }
                    string bagName = string.Format(GetMessage(messageName, player.UserIDString), new object[] { sleepingBag.niceName, GetPlayerName(sleepingBag.deployerUserID) });
                    CreateRespawnButton(ui, bagId, bagName, counter++);
                }
            }
            // save changes to shared mappings
            if (dirty)
            {
                ValidateSharedBags();
            }
        }

        void RefreshUI(PlayerUI ui, uint bagId)
        {
            BagUIInfo bag = ui.FindBagInfo(bagId);

            if (bag == null || !ui.IsPanelOpen(bagId))
            {
                return;
            }

            CreateRespawnButton(ui, bag.id, bag.name, bag.index);
        }

        // build respawn button
        void CreateRespawnButton(PlayerUI ui, uint bagId, string bagName, int index)
        {
            BagUIInfo bag = new BagUIInfo()
            {
                id    = bagId,
                index = index,
                name  = bagName
            };
            // set up button position
            float xPosMin = data.ui.screenMarginX;
            float yPosMin = data.ui.screenMarginY + ((data.ui.verticalSpacer + data.ui.buttonHeight) * index);
            float xPosMax = xPosMin + data.ui.buttonWidth;
            float yPosMax = yPosMin + data.ui.buttonHeight;

            Vector2 buttonAnchorMin = new Vector2(xPosMin, yPosMin);
            Vector2 buttonAnchorMax = new Vector2(xPosMax, yPosMax);

            // set up icon layout
            float iconXMin = data.ui.iconPaddingX - data.ui.iconWidth;
            float iconYMin = data.ui.iconPaddingY;
            float iconXMax = data.ui.iconPanelWidth - data.ui.iconWidth;
            float iconYMax = 1f - iconYMin;

            Vector2 iconPosMin = new Vector2(iconXMin, iconYMin);
            Vector2 iconPosMax = new Vector2(iconXMax, iconYMax);

            // set up text layout
            float spawnTextYMin = data.ui.spawnTextPaddingY;

            Vector2 spawnTextPosMin = new Vector2(iconXMax, spawnTextYMin);
            Vector2 spawnTextPosMax = Vector2.one;

            float bagNameTextYMin = data.ui.bagNameTextPaddingY;

            Vector2 bagNameTextPosMin = new Vector2(iconXMax, bagNameTextYMin);
            Vector2 bagNameTextPosMax = new Vector2(1f, spawnTextYMin);

            string headerText = GetMessage("SharedHeaderText", ui.Player?.UserIDString);

            // build GUI elements

            string elementName = UIElementPrefix + bagId;
            string message     = ui.Message(bagId);

            CuiElementContainer container = UI.CreateElementContainer(elementName, data.ui.buttonColor, buttonAnchorMin, buttonAnchorMax);

            UI.CreateLabel(ref container, elementName, headerText, 16, spawnTextPosMin, spawnTextPosMax, TextAnchor.MiddleLeft);
            UI.CreateLabel(ref container, elementName, bagName, 12, bagNameTextPosMin, bagNameTextPosMax, TextAnchor.UpperLeft, "RobotoCondensed-Regular.ttf");
            UI.CreateImage(ref container, elementName, data.ui.bagIcon, data.ui.bagIconColor, iconPosMin, iconPosMax);
            UI.CreateButton(ref container, elementName, new Color(1f, 1f, 1f, 0.05f), string.Empty, 1, Vector2.zero, Vector2.one, ui.BuildCommand(GUIRespawnCommand, bagId));

            ui.CreateUI(bag, container);
            if (!string.IsNullOrEmpty(message))
            {
                OverlayMessageUI(ui, bagId, message, buttonAnchorMin, buttonAnchorMax);
            }
        }

        // create a message overlay on a button
        void OverlayMessageUI(PlayerUI ui, uint id, string message, Vector2 aMin, Vector2 aMax, float displayTime = 5f)
        {
            string panelName = UIElementPrefix + id + "_Message";
            CuiElementContainer container = UI.CreateElementContainer(panelName, new Color(0.7f, 0.3f, 0.3f, 0.9f), aMin, aMax, fadeIn: 0f);

            UI.CreateLabel(ref container, panelName, $"<color=white>{message}</color>", 14, Vector2.zero, Vector2.one, fadeIn: 0f);
            ui.CreateMessageUI(id, container);
            timer.In(displayTime, () => ui.DestroyMessageUI(id));
        }

        // destroy a player's GUI elements
        void DestroyGUI(BasePlayer player, bool kill = false)
        {
            if (kill)
            {
                FindPlayerUI(player).Kill();
            }
            else
            {
                FindPlayerUI(player).DestroyUI();
            }
        }

        // destroy all player GUI elements
        void DestroyAllGUI()
        {
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                DestroyGUI(player, true);
            }
        }

        #endregion

        #region Helper Procedures

        // spawn a dummy bag at location of shared bag/bed to be used as a respawn point
        bool SpawnDummyBag(SleepingBag bag, BasePlayer player, out uint bagId)
        {
            bagId = 0;
            BaseEntity entity = GameManager.server.CreateEntity(bag.PrefabName, bag.transform.position, bag.transform.rotation, false);

            entity.limitNetworking = true;
            entity.Spawn();
            if (entity != null && entity is SleepingBag)
            {
                SleepingBag newBag = entity as SleepingBag;
                newBag.model.enabled        = false;
                newBag.deployerUserID       = player.userID;
                newBag.secondsBetweenReuses = 0f;
                bagId            = newBag.net.ID;
                dummyBags[bagId] = player.userID;
                return(true);
            }
            return(false);
        }

        // Destroy all dummy bags for a player
        void DestroyDummyBags(BasePlayer player)
        {
            uint[] bags = dummyBags.Where(x => x.Value == player.userID).Select(pair => pair.Key).ToArray();
            if (bags == null || bags.Length == 0)
            {
                return;
            }
            foreach (uint bagId in bags)
            {
                SleepingBag.DestroyBag(player, bagId);
            }
        }

        // Destroy all dummy bags
        void DestroyAllDummyBags()
        {
            foreach (KeyValuePair <uint, ulong> entry in dummyBags)
            {
                SleepingBag bag = SleepingBag.FindForPlayer(entry.Value, entry.Key, true);
                if (bag != null)
                {
                    bag.Kill(BaseNetworkable.DestroyMode.None);
                }
            }
            dummyBags.Clear();
        }

        // validate shared bag list
        void ValidateSharedBags()
        {
            if (!data.HasSharedBags())
            {
                return;
            }
            List <uint> toRemove = new List <uint>();

            // check each bag in the shared bags list and remove any invalid bags
            foreach (SharedBagInfo entry in data.sharedBags)
            {
                SleepingBag sleepingBag = SleepingBag.FindForPlayer(entry.owner, entry.bagId, true);
                if (sleepingBag == null)
                {
                    toRemove.Add(entry.bagId);                     // no longer a valid shared bag
                }
            }

            if (data.sharedBags.RemoveWhere(i => toRemove.Contains(i.bagId)) > 0)
            {
                Puts(GetMessage("Prefix") + string.Format(GetMessage("ValidateStats"), new object[] { toRemove.Count }));
                SaveData();
            }
        }

        // handle raycast from player
        bool GetRaycastTarget(BasePlayer player, out object closestEntity)
        {
            closestEntity = false;

            RaycastHit hit;

            if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 5f))
            {
                return(false);
            }

            closestEntity = hit.GetEntity();
            return(true);
        }

        // get a player name (using cache if possible)
        string GetPlayerName(ulong userID)
        {
            if (playerNameCache.ContainsKey(userID))
            {
                return(playerNameCache[userID]);
            }
            else
            {
                BasePlayer player = BasePlayer.FindByID(userID);
                if (player == null)
                {
                    player = BasePlayer.FindSleeping(userID);
                }

                if (player != null)
                {
                    playerNameCache[userID] = player.displayName;
                    return(player.displayName);
                }
            }
            return("unknown");
        }