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

FREE MyPlayerBuilds : 2.2.0 2.2.0

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("MyPlayerBuilds", "Danevi/RustFlash", "2.2.0")]
    [Description("Allows players to save and load their own builds")]
    class MyPlayerBuilds : RustPlugin
    {
        [PluginReference]
        private Plugin CopyPaste;

        #region Configuration
        private Configuration config;

        private class Configuration
        {
            [JsonProperty("Maximum bases per player")]
            public int MaxBasesPerPlayer = 10;
            
            [JsonProperty("Save cooldown in seconds")]
            public float SaveCooldown = 60f;
            
            [JsonProperty("Enable automatic backups")]
            public bool EnableBackups = true;
            
            [JsonProperty("Debug mode")]
            public bool DebugMode = false;
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null)
                {
                    LoadDefaultConfig();
                }
            }
            catch
            {
                PrintWarning("Creating new configuration file");
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void LoadDefaultConfig()
        {
            config = new Configuration();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(config);
        }
        #endregion

        #region Stored Data
        private StoredData storedData;
        private Dictionary<ulong, DateTime> cooldowns = new Dictionary<ulong, DateTime>();
        private Dictionary<ulong, string> pendingSaves = new Dictionary<ulong, string>();

        private class StoredData
        {
            public Dictionary<ulong, PlayerData> players = new Dictionary<ulong, PlayerData>();
        }

        private class PlayerData
        {
            public List<string> savedBases = new List<string>();
            public DateTime lastSave = DateTime.MinValue;
        }

        private void LoadData()
        {
            try
            {
                storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>("MyPlayerBuilds");
            }
            catch
            {
                storedData = new StoredData();
            }
        }

        private void SaveData()
        {
            Interface.Oxide.DataFileSystem.WriteObject("MyPlayerBuilds", storedData);
        }
        #endregion

        #region Initialization
        void Init()
        {
            permission.RegisterPermission("myplayerbuilds.use", this);
            permission.RegisterPermission("myplayerbuilds.admin", this);
            
            LoadData();
            
            cmd.AddChatCommand("save", this, SaveCommand);
            cmd.AddChatCommand("load", this, LoadCommand);
            cmd.AddChatCommand("listbases", this, ListCommand);
            cmd.AddChatCommand("deletebase", this, DeleteCommand);
        }

        void OnServerInitialized()
        {
            if (CopyPaste == null)
            {
                PrintError("CopyPaste plugin is required but not loaded!");
                Interface.Oxide.UnloadPlugin(Name);
            }
        }

        void Unload()
        {
            SaveData();
        }
        #endregion

        #region Hook Methods
        void OnCopyFinished(object rawData, string filename, object player, object sourcePos)
        {
            DebugLog($"OnCopyFinished called for file: {filename}");
            
            if (player is Oxide.Core.Libraries.Covalence.IPlayer iPlayer && 
                iPlayer.Object is BasePlayer basePlayer)
            {
                if (pendingSaves.ContainsKey(basePlayer.userID) && 
                    pendingSaves[basePlayer.userID] == filename)
                {
                    DebugLog($"Processing pending save for {basePlayer.displayName}: {filename}");
                    timer.Once(1f, () => ProcessSaveFromHook(basePlayer, filename));
                }
            }
        }
        #endregion

        #region Helper Methods
        private void DebugLog(string message)
        {
            if (config.DebugMode)
            {
                Puts($"[DEBUG] {message}");
            }
        }

        private void ProcessSaveFromHook(BasePlayer player, string baseName)
        {
            try
            {
                string sourceFile = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}copypaste{Path.DirectorySeparatorChar}{baseName}.json";
                string playerDir = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}MyPlayerBuilds{Path.DirectorySeparatorChar}{player.userID}";
                string destFile = $"{playerDir}{Path.DirectorySeparatorChar}{baseName}.json";

                DebugLog($"ProcessSaveFromHook: Source={sourceFile}, Dest={destFile}");
                DebugLog($"Source exists: {System.IO.File.Exists(sourceFile)}");

                if (!System.IO.File.Exists(sourceFile))
                {
                    SendReply(player, lang.GetMessage("SaveError", this, player.UserIDString), "CopyPaste file not found");
                    return;
                }

                if (!System.IO.Directory.Exists(playerDir))
                    System.IO.Directory.CreateDirectory(playerDir);

                if (System.IO.File.Exists(destFile) && config.EnableBackups)
                {
                    string backupFile = $"{playerDir}{Path.DirectorySeparatorChar}{baseName}_backup_{DateTime.Now:yyyyMMddHHmmss}.json";
                    System.IO.File.Copy(destFile, backupFile);
                    DebugLog($"Created backup: {backupFile}");
                }

                System.IO.File.Copy(sourceFile, destFile, true);
                DebugLog($"Copied file to: {destFile}");

                if (!storedData.players.ContainsKey(player.userID))
                    storedData.players[player.userID] = new PlayerData();
                
                var playerData = storedData.players[player.userID];
                if (!playerData.savedBases.Contains(baseName))
                    playerData.savedBases.Add(baseName);
                
                playerData.lastSave = DateTime.Now;
                SaveData();

                pendingSaves.Remove(player.userID);

                SendReply(player, lang.GetMessage("SaveSuccess", this, player.UserIDString), baseName);
                DebugLog($"Successfully saved base: {baseName}");
                
                Interface.CallHook("OnPlayerBuildSaved", player, baseName);
            }
            catch (Exception ex)
            {
                DebugLog($"ProcessSaveFromHook error: {ex}");
                SendReply(player, lang.GetMessage("SaveError", this, player.UserIDString), ex.Message);
                pendingSaves.Remove(player.userID);
            }
        }
        #endregion

        #region Localization
        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["NoPermission"] = "You don't have permission to use this command.",
                ["ProvideName"] = "Usage: /{0} <name>",
                ["InvalidName"] = "Invalid name. Use only letters, numbers, - and _",
                ["OnCooldown"] = "Please wait {0} seconds before saving again.",
                ["MaxBases"] = "You have reached the maximum of {0} bases.",
                ["SaveSuccess"] = "Base '{0}' saved successfully!",
                ["SaveError"] = "Error saving base: {0}",
                ["LoadSuccess"] = "Base '{0}' loaded successfully!",
                ["LoadError"] = "Error loading base: {0}",
                ["BaseNotFound"] = "Base '{0}' not found.",
                ["NoBases"] = "You have no saved bases.",
                ["BasesList"] = "Your saved bases:\n{0}",
                ["DeleteSuccess"] = "Base '{0}' deleted successfully.",
                ["NoCopyPaste"] = "CopyPaste plugin is not loaded!",
                ["LookAtGround"] = "You need to look at the ground where you want to load your base!",
                ["LookAtBuilding"] = "You need to look at a building block (foundation, wall, etc.) to save!",
                ["SaveInProgress"] = "Saving base '{0}', please wait...",
                ["LoadInProgress"] = "Loading base '{0}', please wait..."
            }, this);

            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["NoPermission"] = "Du hast keine Berechtigung für diesen Befehl.",
                ["ProvideName"] = "Verwendung: /{0} <Name>",
                ["InvalidName"] = "Ungültiger Name. Nur Buchstaben, Zahlen, - und _ erlaubt.",
                ["OnCooldown"] = "Bitte warte {0} Sekunden vor dem nächsten Speichern.",
                ["MaxBases"] = "Du hast das Maximum von {0} Basen erreicht.",
                ["SaveSuccess"] = "Basis '{0}' erfolgreich gespeichert!",
                ["SaveError"] = "Fehler beim Speichern: {0}",
                ["LoadSuccess"] = "Basis '{0}' erfolgreich geladen!",
                ["LoadError"] = "Fehler beim Laden: {0}",
                ["BaseNotFound"] = "Basis '{0}' nicht gefunden.",
                ["NoBases"] = "Du hast keine gespeicherten Basen.",
                ["BasesList"] = "Deine gespeicherten Basen:\n{0}",
                ["DeleteSuccess"] = "Basis '{0}' erfolgreich gelöscht.",
                ["NoCopyPaste"] = "CopyPaste Plugin ist nicht geladen!",
                ["LookAtGround"] = "Du musst auf den Boden schauen, wo du deine Basis laden möchtest!",
                ["LookAtBuilding"] = "Du musst auf einen Baublock (Foundation, Wand, etc.) schauen zum Speichern!",
                ["SaveInProgress"] = "Speichere Basis '{0}', bitte warten...",
                ["LoadInProgress"] = "Lade Basis '{0}', bitte warten..."
            }, this, "de");
        }
        #endregion

        #region Commands
        void SaveCommand(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, "myplayerbuilds.use"))
            {
                SendReply(player, lang.GetMessage("NoPermission", this, player.UserIDString));
                return;
            }

            if (CopyPaste == null || !CopyPaste.IsLoaded)
            {
                SendReply(player, lang.GetMessage("NoCopyPaste", this, player.UserIDString));
                return;
            }

            if (args.Length == 0)
            {
                SendReply(player, lang.GetMessage("ProvideName", this, player.UserIDString), "save");
                return;
            }

            string baseName = args[0];
            
            if (!System.Text.RegularExpressions.Regex.IsMatch(baseName, @"^[a-zA-Z0-9_-]+$"))
            {
                SendReply(player, lang.GetMessage("InvalidName", this, player.UserIDString));
                return;
            }

            if (cooldowns.ContainsKey(player.userID))
            {
                var timeSince = DateTime.Now - cooldowns[player.userID];
                if (timeSince.TotalSeconds < config.SaveCooldown)
                {
                    var remaining = config.SaveCooldown - timeSince.TotalSeconds;
                    SendReply(player, lang.GetMessage("OnCooldown", this, player.UserIDString), 
                        Math.Ceiling(remaining).ToString());
                    return;
                }
            }

            if (!storedData.players.ContainsKey(player.userID))
                storedData.players[player.userID] = new PlayerData();
            
            var playerData = storedData.players[player.userID];
            if (playerData.savedBases.Count >= config.MaxBasesPerPlayer)
            {
                SendReply(player, lang.GetMessage("MaxBases", this, player.UserIDString), 
                    config.MaxBasesPerPlayer.ToString());
                return;
            }

            RaycastHit hit;
            if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 100f))
            {
                SendReply(player, lang.GetMessage("LookAtBuilding", this, player.UserIDString));
                return;
            }

            var entity = hit.GetEntity();
            if (entity == null || entity.PrefabName == null || 
                (!entity.PrefabName.Contains("foundation") && !entity.PrefabName.Contains("wall") && 
                 !entity.PrefabName.Contains("floor") && !entity.PrefabName.Contains("roof")))
            {
                SendReply(player, lang.GetMessage("LookAtBuilding", this, player.UserIDString));
                return;
            }

            DebugLog($"Player {player.displayName} saving base '{baseName}', looking at {entity.PrefabName}");

            pendingSaves[player.userID] = baseName;
            
            SendReply(player, lang.GetMessage("SaveInProgress", this, player.UserIDString), baseName);

            var cmdArgs = new List<string> { baseName };
            cmdArgs.AddRange(args.Skip(1));
            
            DebugLog($"Executing copy command with args: {string.Join(" ", cmdArgs)}");
            
            rust.RunClientCommand(player, "copy", cmdArgs.ToArray());
            
            cooldowns[player.userID] = DateTime.Now;
            
            timer.Once(10f, () => 
            {
                if (pendingSaves.ContainsKey(player.userID) && pendingSaves[player.userID] == baseName)
                {
                    pendingSaves.Remove(player.userID);
                    SendReply(player, lang.GetMessage("SaveError", this, player.UserIDString), "Save timeout - try looking directly at a foundation block");
                }
            });
        }

        void LoadCommand(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, "myplayerbuilds.use"))
            {
                SendReply(player, lang.GetMessage("NoPermission", this, player.UserIDString));
                return;
            }

            if (CopyPaste == null || !CopyPaste.IsLoaded)
            {
                SendReply(player, lang.GetMessage("NoCopyPaste", this, player.UserIDString));
                return;
            }

            if (args.Length == 0)
            {
                SendReply(player, lang.GetMessage("ProvideName", this, player.UserIDString), "load");
                return;
            }

            string baseName = args[0];
            string playerFile = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}MyPlayerBuilds{Path.DirectorySeparatorChar}{player.userID}{Path.DirectorySeparatorChar}{baseName}.json";

            if (!System.IO.File.Exists(playerFile))
            {
                SendReply(player, lang.GetMessage("BaseNotFound", this, player.UserIDString), baseName);
                return;
            }

            RaycastHit hit;
            if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f))
            {
                SendReply(player, lang.GetMessage("LookAtGround", this, player.UserIDString));
                return;
            }

            DebugLog($"Loading base '{baseName}' for player {player.displayName}");

            try
            {
                string tempFile = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}copypaste{Path.DirectorySeparatorChar}{baseName}.json";
                
                DebugLog($"Copying {playerFile} to {tempFile}");
                System.IO.File.Copy(playerFile, tempFile, true);

                if (!System.IO.File.Exists(tempFile))
                {
                    SendReply(player, lang.GetMessage("LoadError", this, player.UserIDString), "Failed to prepare file for loading");
                    return;
                }

                SendReply(player, lang.GetMessage("LoadInProgress", this, player.UserIDString), baseName);

                var pasteArgs = new List<string> { baseName };
                
                if (args.Length > 1)
                    pasteArgs.AddRange(args.Skip(1));
                else
                    pasteArgs.AddRange(new[] { "autoheight", "true" }); 

                DebugLog($"Executing paste command with args: {string.Join(" ", pasteArgs)}");

                rust.RunClientCommand(player, "paste", pasteArgs.ToArray());

                timer.Once(2f, () => 
                {
                    SendReply(player, lang.GetMessage("LoadSuccess", this, player.UserIDString), baseName);
                    Interface.CallHook("OnPlayerBuildLoaded", player, baseName);
                });

                timer.Once(5f, () =>
                {
                    try
                    {
                        if (System.IO.File.Exists(tempFile))
                        {
                            System.IO.File.Delete(tempFile);
                            DebugLog($"Cleaned up temp file: {tempFile}");
                        }
                    }
                    catch (Exception ex)
                    {
                        DebugLog($"Error cleaning up temp file: {ex.Message}");
                    }
                });
            }
            catch (Exception ex)
            {
                DebugLog($"LoadCommand error: {ex}");
                SendReply(player, lang.GetMessage("LoadError", this, player.UserIDString), ex.Message);
            }
        }

        void ListCommand(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, "myplayerbuilds.use"))
            {
                SendReply(player, lang.GetMessage("NoPermission", this, player.UserIDString));
                return;
            }

            if (!storedData.players.ContainsKey(player.userID) || 
                storedData.players[player.userID].savedBases.Count == 0)
            {
                SendReply(player, lang.GetMessage("NoBases", this, player.UserIDString));
                return;
            }

            var bases = string.Join("\n", storedData.players[player.userID].savedBases.Select(b => $"• {b}"));
            SendReply(player, lang.GetMessage("BasesList", this, player.UserIDString), bases);
        }

        void DeleteCommand(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, "myplayerbuilds.use"))
            {
                SendReply(player, lang.GetMessage("NoPermission", this, player.UserIDString));
                return;
            }

            if (args.Length == 0)
            {
                SendReply(player, lang.GetMessage("ProvideName", this, player.UserIDString), "deletebase");
                return;
            }

            string baseName = args[0];
            string playerFile = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}MyPlayerBuilds{Path.DirectorySeparatorChar}{player.userID}{Path.DirectorySeparatorChar}{baseName}.json";

            if (!System.IO.File.Exists(playerFile))
            {
                SendReply(player, lang.GetMessage("BaseNotFound", this, player.UserIDString), baseName);
                return;
            }

            try
            {
                System.IO.File.Delete(playerFile);
                
                if (storedData.players.ContainsKey(player.userID))
                {
                    storedData.players[player.userID].savedBases.Remove(baseName);
                    SaveData();
                }

                SendReply(player, lang.GetMessage("DeleteSuccess", this, player.UserIDString), baseName);
                Interface.CallHook("OnPlayerBuildDeleted", player, baseName);
            }
            catch (Exception ex)
            {
                SendReply(player, lang.GetMessage("SaveError", this, player.UserIDString), ex.Message);
            }
        }
        #endregion

        #region Console Commands (for debugging)
        [ConsoleCommand("myplayerbuilds.debug")]
        void DebugCommand(ConsoleSystem.Arg arg)
        {
            if (!arg.IsAdmin)
                return;

            if (arg.Args == null || arg.Args.Length == 0)
            {
                arg.ReplyWith("Usage: myplayerbuilds.debug <on/off>");
                return;
            }

            switch (arg.Args[0].ToLower())
            {
                case "on":
                    config.DebugMode = true;
                    SaveConfig();
                    arg.ReplyWith("Debug mode enabled");
                    break;
                case "off":
                    config.DebugMode = false;
                    SaveConfig();
                    arg.ReplyWith("Debug mode disabled");
                    break;
                default:
                    arg.ReplyWith("Usage: myplayerbuilds.debug <on/off>");
                    break;
            }
        }

        [ConsoleCommand("myplayerbuilds.testload")]
        void TestLoadCommand(ConsoleSystem.Arg arg)
        {
            if (!arg.IsAdmin)
                return;

            if (arg.Args == null || arg.Args.Length < 2)
            {
                arg.ReplyWith("Usage: myplayerbuilds.testload <playerId> <baseName>");
                return;
            }

            if (!ulong.TryParse(arg.Args[0], out ulong playerId))
            {
                arg.ReplyWith("Invalid player ID");
                return;
            }

            string baseName = arg.Args[1];
            string playerFile = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}MyPlayerBuilds{Path.DirectorySeparatorChar}{playerId}{Path.DirectorySeparatorChar}{baseName}.json";

            arg.ReplyWith($"Checking file: {playerFile}");
            arg.ReplyWith($"File exists: {System.IO.File.Exists(playerFile)}");

            if (System.IO.File.Exists(playerFile))
            {
                try
                {
                    var content = System.IO.File.ReadAllText(playerFile);
                    arg.ReplyWith($"File size: {content.Length} characters");
                    arg.ReplyWith($"Content preview: {content.Substring(0, Math.Min(200, content.Length))}...");
                }
                catch (Exception ex)
                {
                    arg.ReplyWith($"Error reading file: {ex.Message}");
                }
            }

            string copyPasteFile = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}copypaste{Path.DirectorySeparatorChar}{baseName}.json";
            arg.ReplyWith($"CopyPaste file exists: {System.IO.File.Exists(copyPasteFile)}");
        }

        [ConsoleCommand("myplayerbuilds.listsaves")]
        void ListSavesCommand(ConsoleSystem.Arg arg)
        {
            if (!arg.IsAdmin)
                return;

            if (arg.Args == null || arg.Args.Length < 1)
            {
                arg.ReplyWith("Usage: myplayerbuilds.listsaves <playerId>");
                return;
            }

            if (!ulong.TryParse(arg.Args[0], out ulong playerId))
            {
                arg.ReplyWith("Invalid player ID");
                return;
            }

            string playerDir = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}MyPlayerBuilds{Path.DirectorySeparatorChar}{playerId}";
            
            if (!System.IO.Directory.Exists(playerDir))
            {
                arg.ReplyWith("Player has no saved bases directory");
                return;
            }

            var files = System.IO.Directory.GetFiles(playerDir, "*.json")
                .Select(f => Path.GetFileNameWithoutExtension(f))
                .ToArray();

            arg.ReplyWith($"Found {files.Length} base files: {string.Join(", ", files)}");
        }
        #endregion

        #region API
        private bool SavePlayerBuild(ulong userId, string baseName, string buildData)
        {
            try
            {
                string playerDir = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}MyPlayerBuilds{Path.DirectorySeparatorChar}{userId}";
                if (!System.IO.Directory.Exists(playerDir))
                    System.IO.Directory.CreateDirectory(playerDir);
                
                string destFile = $"{playerDir}{Path.DirectorySeparatorChar}{baseName}.json";
                System.IO.File.WriteAllText(destFile, buildData);
                
                if (!storedData.players.ContainsKey(userId))
                    storedData.players[userId] = new PlayerData();
                
                if (!storedData.players[userId].savedBases.Contains(baseName))
                    storedData.players[userId].savedBases.Add(baseName);
                
                SaveData();
                return true;
            }
            catch
            {
                return false;
            }
        }

        private string GetPlayerBuild(ulong userId, string baseName)
        {
            try
            {
                string playerFile = $"{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}MyPlayerBuilds{Path.DirectorySeparatorChar}{userId}{Path.DirectorySeparatorChar}{baseName}.json";
                if (!System.IO.File.Exists(playerFile))
                    return null;
                
                return System.IO.File.ReadAllText(playerFile);
            }
            catch
            {
                return null;
            }
        }

        private List<string> GetPlayerBuilds(ulong userId)
        {
            if (!storedData.players.ContainsKey(userId))
                return new List<string>();
            
            return storedData.players[userId].savedBases;
        }
        #endregion
    }
}

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