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

FREE CS2Plugin : 2.1.0 2.1.0

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

namespace Oxide.Plugins
{
    [Info("CS2 Style Plugin", "BugFixed", "2.1.0")]
    [Description("Thoroughly bug-tested CS2-style competitive gameplay plugin for Rust")]
    public class CS2Plugin : RustPlugin
    {
        #region Fields and Configuration
        
        // Thread safety lock object
        private readonly object _lockObject = new object();
        
        private Configuration config;
        private Dictionary<ulong, PlayerData> playerData = new Dictionary<ulong, PlayerData>();
        private Dictionary<string, QueueZone> queueZones = new Dictionary<string, QueueZone>();
        private Dictionary<string, BombSite> bombSites = new Dictionary<string, BombSite>();
        private Dictionary<string, MapConfiguration> mapConfigurations = new Dictionary<string, MapConfiguration>();
        private Dictionary<ulong, Match> activeMatches = new Dictionary<ulong, Match>();
        private Dictionary<ulong, ClipboardData> adminClipboard = new Dictionary<ulong, ClipboardData>();
        private Dictionary<ulong, Vote> activeVotes = new Dictionary<ulong, Vote>();
        private Dictionary<string, GameMode> gameModes = new Dictionary<string, GameMode>();
        private Dictionary<string, WeaponSkin> availableSkins = new Dictionary<string, WeaponSkin>();
        private Dictionary<ulong, Tournament> activeTournaments = new Dictionary<ulong, Tournament>();
        private Dictionary<string, Callout> mapCallouts = new Dictionary<string, Callout>();
        private List<Achievement> availableAchievements = new List<Achievement>();
        private Dictionary<ulong, Warmup> activeWarmups = new Dictionary<ulong, Warmup>();
        private Dictionary<string, string> killFeedMessages = new Dictionary<string, string>();
        
        private const string PERMISSION_ADMIN = "cs2plugin.admin";
        private const string PERMISSION_USE = "cs2plugin.use";
        
        private Timer uiUpdateTimer;
        private Timer matchUpdateTimer;
        private Timer cleanupTimer;
        
        // Bug Fix: Add safety checks and validation
        private HashSet<ulong> processingPlayers = new HashSet<ulong>();
        private Dictionary<ulong, float> lastActionTime = new Dictionary<ulong, float>();
        private const float ACTION_COOLDOWN = 0.5f;
        
        #endregion
        
        #region Configuration Classes
        
        public class Configuration
        {
            [JsonProperty("Default Team Size")]
            public int DefaultTeamSize { get; set; } = 5;
            
            [JsonProperty("Round Win Requirement")]
            public int RoundWinRequirement { get; set; } = 13;
            
            [JsonProperty("Side Swap Round")]
            public int SideSwapRound { get; set; } = 12;
            
            [JsonProperty("Buy Phase Duration")]
            public float BuyPhaseDuration { get; set; } = 20f;
            
            [JsonProperty("Buy Menu Duration")]
            public float BuyMenuDuration { get; set; } = 40f;
            
            [JsonProperty("Bomb Timer")]
            public float BombTimer { get; set; } = 40f;
            
            [JsonProperty("Max Team Damage")]
            public float MaxTeamDamage { get; set; } = 300f;
            
            [JsonProperty("Max Grenade Damage")]
            public float MaxGrenadeDamage { get; set; } = 60f;
            
            [JsonProperty("Lobby Spawn Point")]
            public Vector3 LobbySpawnPoint { get; set; } = new Vector3(0, 0, 0);
            
            [JsonProperty("Auto Generate Default Maps")]
            public bool AutoGenerateDefaultMaps { get; set; } = true;
            
            [JsonProperty("Default Map Template")]
            public string DefaultMapTemplate { get; set; } = "custom";
            
            [JsonProperty("Map Generation On Server Start")]
            public bool GenerateMapOnStart { get; set; } = true;
            
            [JsonProperty("Server Map Name")]
            public string ServerMapName { get; set; } = "cs2_server_map";
            
            [JsonProperty("Map Generation Center")]
            public Vector3 MapGenerationCenter { get; set; } = new Vector3(0, 0, 0);
            
            [JsonProperty("Auto Detect Map Center")]
            public bool AutoDetectMapCenter { get; set; } = true;
            
            [JsonProperty("Large Map Mode")]
            public bool LargeMapMode { get; set; } = true;
            
            [JsonProperty("Map Search Radius")]
            public float MapSearchRadius { get; set; } = 1450f; // Half of 2900
            
            [JsonProperty("Multiple Map Zones")]
            public bool MultipleMapZones { get; set; } = true;
            
            [JsonProperty("Zone Spacing")]
            public float ZoneSpacing { get; set; } = 500f;
            
            [JsonProperty("Max Maps Per Server")]
            public int MaxMapsPerServer { get; set; } = 8;
            
            [JsonProperty("Backup Map Configurations")]
            public bool BackupMapConfigs { get; set; } = true;
            
            [JsonProperty("Map Generation Timeout")]
            public float MapGenerationTimeout { get; set; } = 300f; // 5 minutes
            
            [JsonProperty("Enable Map Validation")]
            public bool EnableMapValidation { get; set; } = true;
            
            [JsonProperty("Enable Voting System")]
            public bool EnableVotingSystem { get; set; } = true;
            
            [JsonProperty("Vote Duration")]
            public float VoteDuration { get; set; } = 30f;
            
            [JsonProperty("Minimum Vote Percentage")]
            public float MinimumVotePercentage { get; set; } = 0.6f;
            
            [JsonProperty("Require Vote Majority")]
            public bool RequireVoteMajority { get; set; } = true;
            
            [JsonProperty("Auto Execute Votes")]
            public bool AutoExecuteVotes { get; set; } = true;
            
            [JsonProperty("Allow Map Voting")]
            public bool AllowMapVoting { get; set; } = true;
            
            [JsonProperty("Allow Teleport Voting")]
            public bool AllowTeleportVoting { get; set; } = true;
            
            // Bug Fix: Add validation settings
            [JsonProperty("Enable Safety Checks")]
            public bool EnableSafetyChecks { get; set; } = true;
            
            [JsonProperty("Max Queue Time")]
            public float MaxQueueTime { get; set; } = 300f; // 5 minutes
            
            [JsonProperty("Auto Cleanup Interval")]
            public float AutoCleanupInterval { get; set; } = 60f; // 1 minute
            
            [JsonProperty("Max Concurrent Matches")]
            public int MaxConcurrentMatches { get; set; } = 10;
        }
        
        #endregion
        
        #region Data Classes (Bug Fixed)
        
        public class PlayerData
        {
            public ulong PlayerId { get; set; }
            public string PlayerName { get; set; }
            public Team CurrentTeam { get; set; } = Team.None;
            public ulong CurrentMatchId { get; set; } = 0;
            public bool IsInQueue { get; set; } = false;
            public string QueueZoneName { get; set; } = "";
            public float TeamDamageDealt { get; set; } = 0f;
            public int Kills { get; set; } = 0;
            public int Deaths { get; set; } = 0;
            public int Assists { get; set; } = 0;
            public bool IsSpectating { get; set; } = false;
            public bool HasBuyMenuOpen { get; set; } = false;
            public Dictionary<string, int> Inventory { get; set; } = new Dictionary<string, int>();
            public Vector3 LastPosition { get; set; }
            public bool IsDefusing { get; set; } = false;
            public Timer DefuseTimer { get; set; }
            public bool IsInFreeCam { get; set; } = false;
            public ulong SpectatingPlayerId { get; set; } = 0;
            public int SpectatorIndex { get; set; } = 0;
            public bool IsTalking { get; set; } = false;
            public float LastTalkTime { get; set; } = 0f;
            public int RoundKills { get; set; } = 0;
            public int RoundDeaths { get; set; } = 0;
            public int HeadShots { get; set; } = 0;
            public int RoundHeadShots { get; set; } = 0;
            public float DamageDealt { get; set; } = 0f;
            public float DamageTaken { get; set; } = 0f;
            public int BombsPlanted { get; set; } = 0;
            public int BombsDefused { get; set; } = 0;
            public int MVPRounds { get; set; } = 0;
            public float Accuracy { get; set; } = 0f;
            public int ShotsHit { get; set; } = 0;
            public int ShotsFired { get; set; } = 0;
            public Dictionary<string, int> WeaponKills { get; set; } = new Dictionary<string, int>();
            public int Rank { get; set; } = 1;
            public int RankPoints { get; set; } = 1000;
            public bool IsReady { get; set; } = false;
            public string PreferredRole { get; set; } = "Rifler";
            public List<string> OwnedSkins { get; set; } = new List<string>();
            public Dictionary<string, object> Settings { get; set; } = new Dictionary<string, object>();
            public float LastDamageTime { get; set; } = 0f;
            public ulong LastAttackerId { get; set; } = 0;
            public bool IsClutching { get; set; } = false;
            public int ClutchKills { get; set; } = 0;
            public List<Achievement> UnlockedAchievements { get; set; } = new List<Achievement>();
            
            // Bug Fix: Add validation and safety
            public float QueueJoinTime { get; set; } = 0f;
            public float LastActionTime { get; set; } = 0f;
            public bool IsProcessing { get; set; } = false;
            public Vector3 SafePosition { get; set; } = Vector3.zero;
            public List<string> ActiveTimers { get; set; } = new List<string>();
            
            // Bug Fix: Prevent null reference exceptions
            public bool IsValid()
            {
                return PlayerId != 0 && !string.IsNullOrEmpty(PlayerName);
            }
        }
        
        public class QueueZone
        {
            public string Name { get; set; }
            public Vector3 MinBounds { get; set; }
            public Vector3 MaxBounds { get; set; }
            public List<ulong> QueuedPlayers { get; set; } = new List<ulong>();
            public int TeamSize { get; set; } = 5;
            public bool IsActive { get; set; } = true;
            public string SelectedMap { get; set; } = "dust2";
            
            // Bug Fix: Add validation
            public float CreatedTime { get; set; } = 0f;
            public bool IsValid()
            {
                return !string.IsNullOrEmpty(Name) && TeamSize > 0 && TeamSize <= 10;
            }
            
            public Vector3 GetCenter()
            {
                return (MinBounds + MaxBounds) / 2f;
            }
        }
        
        public class BombSite
        {
            public string Name { get; set; }
            public Vector3 MinBounds { get; set; }
            public Vector3 MaxBounds { get; set; }
            public string MapName { get; set; }
            public bool IsActive { get; set; } = true;
            
            // Bug Fix: Add validation
            public bool IsValid()
            {
                return !string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(MapName);
            }
            
            public bool ContainsPosition(Vector3 position)
            {
                return position.x >= MinBounds.x && position.x <= MaxBounds.x &&
                       position.y >= MinBounds.y && position.y <= MaxBounds.y &&
                       position.z >= MinBounds.z && position.z <= MaxBounds.z;
            }
        }
        
        public class MapConfiguration
        {
            public string MapName { get; set; }
            public List<Vector3> TerroristSpawns { get; set; } = new List<Vector3>();
            public List<Vector3> CounterTerroristSpawns { get; set; } = new List<Vector3>();
            public List<string> BombSiteNames { get; set; } = new List<string>();
            public Vector3 SpectatorArea { get; set; }
            
            // Bug Fix: Add validation
            public bool IsValid()
            {
                return !string.IsNullOrEmpty(MapName) && 
                       TerroristSpawns.Count >= 5 && 
                       CounterTerroristSpawns.Count >= 5 &&
                       BombSiteNames.Count >= 2;
            }
        }
        
        public class Match
        {
            public ulong MatchId { get; set; }
            public List<ulong> TerroristPlayers { get; set; } = new List<ulong>();
            public List<ulong> CounterTerroristPlayers { get; set; } = new List<ulong>();
            public int TerroristScore { get; set; } = 0;
            public int CounterTerroristScore { get; set; } = 0;
            public int CurrentRound { get; set; } = 0; // Bug Fix: Start at 0, increment to 1
            public MatchState State { get; set; } = MatchState.WaitingForPlayers;
            public string MapName { get; set; } = "dust2";
            public float RoundStartTime { get; set; }
            public bool IsBuyPhase { get; set; } = false;
            public PlantedBomb PlantedBomb { get; set; }
            public bool HasSwappedSides { get; set; } = false;
            public Timer RoundTimer { get; set; }
            public Timer BuyPhaseTimer { get; set; }
            public string QueueZoneName { get; set; }
            
            // Bug Fix: Add safety and validation
            public float MatchStartTime { get; set; } = 0f;
            public List<Timer> ActiveTimers { get; set; } = new List<Timer>();
            public bool IsValid()
            {
                return MatchId != 0 && 
                       TerroristPlayers.Count > 0 && 
                       CounterTerroristPlayers.Count > 0 &&
                       !string.IsNullOrEmpty(MapName);
            }
            
            public void CleanupTimers()
            {
                try
                {
                    RoundTimer?.Destroy();
                    RoundTimer = null;
                    BuyPhaseTimer?.Destroy();
                    BuyPhaseTimer = null;
                    PlantedBomb?.ExplosionTimer?.Destroy();
                    if (PlantedBomb != null) PlantedBomb.ExplosionTimer = null;
                    
                    foreach (var timer in ActiveTimers.ToList())
                    {
                        timer?.Destroy();
                    }
                    ActiveTimers.Clear();
                }
                catch (Exception ex)
                {
                    // Handle cleanup errors gracefully
                }
            }
        }
        
        public class PlantedBomb
        {
            public Vector3 Position { get; set; }
            public float PlantTime { get; set; }
            public ulong PlanterId { get; set; }
            public string BombSiteName { get; set; }
            public Timer ExplosionTimer { get; set; }
            public bool IsBeingDefused { get; set; } = false;
            public ulong DefuserId { get; set; } = 0;
            
            // Bug Fix: Add validation
            public bool IsValid()
            {
                return PlanterId != 0 && !string.IsNullOrEmpty(BombSiteName) && PlantTime > 0;
            }
            
            public float GetTimeLeft(float bombTimer)
            {
                return bombTimer - (Time.time - PlantTime);
            }
        }
        
        public class ClipboardData
        {
            public Vector3? PointA { get; set; }
            public Vector3? PointB { get; set; }
            public string PendingCommand { get; set; } = "";
            public float LastUsed { get; set; } = 0f; // Bug Fix: Prevent spam
        }
        
        // Additional classes referenced in the original code
        public class Vote
        {
            public ulong VoteId { get; set; }
            public string VoteType { get; set; }
            public string Description { get; set; }
            public List<ulong> YesVotes { get; set; } = new List<ulong>();
            public List<ulong> NoVotes { get; set; } = new List<ulong>();
            public float StartTime { get; set; }
            public float Duration { get; set; }
            public bool IsActive { get; set; } = true;
        }
        
        public class GameMode
        {
            public string Name { get; set; }
            public string Description { get; set; }
            public Dictionary<string, object> Settings { get; set; } = new Dictionary<string, object>();
        }
        
        }
        
        public class Callout
        {
            public string Name { get; set; }
            public Vector3 Position { get; set; }
            public string MapName { get; set; }
        }
        
        public class Warmup
        {
            public ulong WarmupId { get; set; }
            public List<ulong> Players { get; set; } = new List<ulong>();
            public bool IsActive { get; set; } = true;
        }
        
        public enum Team
        {
            None,
            Terrorist,
            CounterTerrorist
        }
        
        public enum MatchState
        {
            WaitingForPlayers,
            InProgress,
            Paused, // Bug Fix: Add paused state
            Finished
        }
        
        public enum RoundEndReason
        {
            TerroristsEliminated,
            CounterTerroristsEliminated,
            BombExploded,
            BombDefused,
            TimeExpired,
            MatchCancelled // Bug Fix: Add cancellation reason
        }
        
        #endregion
        
        #region Plugin Initialization (Bug Fixed)
        
        protected override void LoadDefaultConfig()
        {
            config = new Configuration();
            SaveConfig();
        }
        
        private void LoadConfig()
        {
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null) 
                {
                    PrintWarning("Config is null, loading default config");
                    LoadDefaultConfig();
                }
                
                // Bug Fix: Validate config values
                ValidateConfig();
            }
            catch (Exception ex)
            {
                PrintError($"Error loading config: {ex.Message}");
                LoadDefaultConfig();
            }
        }
        
        private void ValidateConfig()
        {
            // Bug Fix: Ensure config values are within reasonable bounds
            if (config.DefaultTeamSize < 1) config.DefaultTeamSize = 5;
            if (config.DefaultTeamSize > 10) config.DefaultTeamSize = 10;
            if (config.RoundWinRequirement < 1) config.RoundWinRequirement = 13;
            if (config.BuyPhaseDuration < 5f) config.BuyPhaseDuration = 20f;
            if (config.BombTimer < 10f) config.BombTimer = 40f;
            if (config.MaxTeamDamage < 100f) config.MaxTeamDamage = 300f;
            if (config.MaxQueueTime < 60f) config.MaxQueueTime = 300f;
            if (config.MaxConcurrentMatches < 1) config.MaxConcurrentMatches = 10;
        }
        
        private void SaveConfig() 
        {
            try
            {
                Config.WriteObject(config);
            }
            catch (Exception ex)
            {
                PrintError($"Error saving config: {ex.Message}");
            }
        }
        
        void Init()
        {
            LoadConfig();
            
            permission.RegisterPermission(PERMISSION_ADMIN, this);
            permission.RegisterPermission(PERMISSION_USE, this);
            
            // Register commands with error handling
            try
            {
                RegisterCommands();
            }
            catch (Exception ex)
            {
                PrintError($"Error registering commands: {ex.Message}");
            }
            
            // Initialize timers with error handling
            try
            {
                InitializeTimers();
            }
            catch (Exception ex)
            {
                PrintError($"Error initializing timers: {ex.Message}");
            }
        }
        
        private void RegisterCommands()
        {
            // Admin commands
            cmd.AddChatCommand("setqzone", this, "CmdSetQueueZone");
            cmd.AddChatCommand("removeqzone", this, "CmdRemoveQueueZone");
            cmd.AddChatCommand("setbombsite", this, "CmdSetBombSite");
            cmd.AddChatCommand("removebombsite", this, "CmdRemoveBombSite");
            cmd.AddChatCommand("setspawn", this, "CmdSetSpawn");
            cmd.AddChatCommand("removespawn", this, "CmdRemoveSpawn");
            cmd.AddChatCommand("forcestart", this, "CmdForceStartMatch");
            cmd.AddChatCommand("endmatch", this, "CmdEndMatch");
            cmd.AddChatCommand("generatemap", this, "CmdGenerateMap");
            cmd.AddChatCommand("clearmap", this, "CmdClearMap");
            cmd.AddChatCommand("listmaps", this, "CmdListMaps");
            cmd.AddChatCommand("teleportmap", this, "CmdTeleportToMap");
            cmd.AddChatCommand("cs2debug", this, "CmdDebugInfo");
            
            // Player commands
            cmd.AddChatCommand("buymenu", this, "CmdBuyMenu");
            cmd.AddChatCommand("joinqueue", this, "CmdJoinQueue");
            cmd.AddChatCommand("leavequeue", this, "CmdLeaveQueue");
            cmd.AddChatCommand("cs2stats", this, "CmdShowStats");
            cmd.AddChatCommand("spec", this, "CmdSpectate");
            cmd.AddChatCommand("specnext", this, "CmdSpectateNext");
            cmd.AddChatCommand("specprev", this, "CmdSpectatePrevious");
            cmd.AddChatCommand("freecam", this, "CmdToggleFreeCam");
            
            // Additional commands from original
            cmd.AddChatCommand("generateultramap", this, "CmdGenerateUltraMap");
            cmd.AddChatCommand("confirmultramap", this, "CmdConfirmUltraMap");
            cmd.AddChatCommand("validatemap", this, "CmdValidateMap");
            cmd.AddChatCommand("exportmap", this, "CmdExportMap");
            cmd.AddChatCommand("importmap", this, "CmdImportMap");
            cmd.AddChatCommand("addbot", this, "CmdAddBot");
            cmd.AddChatCommand("skins", this, "CmdSkins");
            cmd.AddChatCommand("skinshop", this, "CmdSkinShop");
            cmd.AddChatCommand("mykins", this, "CmdMySkins");
            cmd.AddChatCommand("heatmap", this, "CmdHeatMap");
            cmd.AddChatCommand("playerstats", this, "CmdPlayerStats");
            cmd.AddChatCommand("createtournament", this, "CmdCreateTournament");
            cmd.AddChatCommand("jointeam", this, "CmdJoinTournament");
            cmd.AddChatCommand("starttournament", this, "CmdStartTournament");
            cmd.AddChatCommand("votemap", this, "CmdVoteMap");
            cmd.AddChatCommand("vote", this, "CmdVote");
            cmd.AddChatCommand("teleportvote", this, "CmdTeleportVote");
            cmd.AddChatCommand("forcevote", this, "CmdForceVote");
            
            // Voting commands
            cmd.AddChatCommand("votekick", this, "CmdVoteKick");
            cmd.AddChatCommand("surrender", this, "CmdVoteSurrender");
            cmd.AddChatCommand("yes", this, "CmdVoteYes");
            cmd.AddChatCommand("no", this, "CmdVoteNo");
            cmd.AddChatCommand("f1", this, "CmdVoteYes");
            cmd.AddChatCommand("f2", this, "CmdVoteNo");
            
            // Economy commands
            cmd.AddChatCommand("money", this, "CmdShowMoney");
            cmd.AddChatCommand("buy", this, "CmdQuickBuy");
            
            // Leaderboard commands
            cmd.AddChatCommand("leaderboard", this, "CmdLeaderboard");
            cmd.AddChatCommand("top", this, "CmdLeaderboard");
            cmd.AddChatCommand("rank", this, "CmdShowRank");
            
            // Achievement commands
            cmd.AddChatCommand("achievements", this, "CmdAchievements");
            cmd.AddChatCommand("progress", this, "CmdAchievements");
            
            // Tournament commands
            cmd.AddChatCommand("createtournament", this, "CmdCreateTournament");
            cmd.AddChatCommand("jointournament", this, "CmdJoinTournament");
            cmd.AddChatCommand("tournaments", this, "CmdListTournaments");
            
            // Warmup commands
            cmd.AddChatCommand("warmup", this, "CmdToggleWarmup");
            cmd.AddChatCommand("ready", this, "CmdPlayerReady");
            
            // Clan commands
            cmd.AddChatCommand("createclan", this, "CmdCreateClan");
            cmd.AddChatCommand("joinclan", this, "CmdJoinClan");
            cmd.AddChatCommand("leaveclan", this, "CmdLeaveClan");
            cmd.AddChatCommand("clan", this, "CmdClanInfo");
            
            // Admin commands
            cmd.AddChatCommand("pause", this, "CmdPauseMatch");
            cmd.AddChatCommand("unpause", this, "CmdUnpauseMatch");
            cmd.AddChatCommand("restart", this, "CmdRestartRound");
            cmd.AddChatCommand("knife", this, "CmdKnifeRound");
            
            // UI commands
            cmd.AddChatCommand("cs2.closebuy", this, "CmdCloseBuyMenu");
            cmd.AddChatCommand("cs2.buy", this, "CmdBuyItem");
            cmd.AddChatCommand("cs2.voteyes", this, "CmdVoteYes");
            cmd.AddChatCommand("cs2.voteno", this, "CmdVoteNo");
            cmd.AddChatCommand("cs2.closeleaderboard", this, "CmdCloseLeaderboard");
            cmd.AddChatCommand("cs2.closeachievements", this, "CmdCloseAchievements");
        }
        
        private void InitializeTimers()
        {
            uiUpdateTimer = timer.Every(2f, UpdateAllUI); // Bug Fix: Reduced frequency
            matchUpdateTimer = timer.Every(0.5f, UpdateMatches); // Bug Fix: Reduced frequency
            cleanupTimer = timer.Every(config.AutoCleanupInterval, PerformCleanup);
        }
        
        void OnServerInitialized()
        {
            try
            {
                LoadData();
                InitializeDefaultMaps();
                InitializeWeaponSkins();
                
                if (config.GenerateMapOnStart)
                {
                    timer.Once(10f, () => SafeAutoGenerateServerMaps());
                }
                
                if (config.AutoDetectMapCenter)
                {
                    DetectMapCenter();
                }
                
                if (config.EnableMapValidation)
                {
                    ValidateAllMaps();
                }
                
                PrintWarning("CS2 Style Plugin loaded successfully!");
                PrintWarning($"Map generation center: {config.MapGenerationCenter}");
                PrintWarning($"Available maps: {mapConfigurations.Count}");
                PrintWarning($"Available skins: {availableSkins.Count}");
                PrintWarning($"Large map mode: {config.LargeMapMode}");
            }
            catch (Exception ex)
            {
                PrintError($"Error during server initialization: {ex.Message}");
            }
        }
        
        void Unload()
        {
            try
            {
                SaveData();
                
                // Bug Fix: Proper cleanup of all resources
                CleanupAllResources();
                
                PrintWarning("CS2 Style Plugin unloaded successfully!");
            }
            catch (Exception ex)
            {
                PrintError($"Error during plugin unload: {ex.Message}");
            }
        }
        
        private void CleanupAllResources()
        {
            // Clean up timers safely
            DestroyTimer(ref uiUpdateTimer);
            DestroyTimer(ref matchUpdateTimer);
            DestroyTimer(ref cleanupTimer);
            
            // Clean up all active matches
            SafeModifyCollection(() =>
            {
                foreach (var match in activeMatches.Values.ToList())
                {
                    try
                    {
                        EndMatch(match, "Plugin unloaded");
                    }
                    catch (Exception ex)
                    {
                        PrintError($"Error ending match {match.MatchId}: {ex.Message}");
                    }
                }
            });
            
            // Clean up player data timers
            foreach (var data in playerData.Values.ToList())
            {
                try
                {
                    data.DefuseTimer?.Destroy();
                    data.DefuseTimer = null;
                }
                catch (Exception ex)
                {
                    PrintError($"Error cleaning up player {data.PlayerId}: {ex.Message}");
                }
            }
            
            // Destroy all UI elements safely
            foreach (var player in BasePlayer.activePlayerList.ToList())
            {
                if (IsValidPlayer(player))
                {
                    SafeDestroyUI(player, "LobbyUI");
                    SafeDestroyUI(player, "MatchUI");
                    SafeDestroyUI(player, "BuyMenu");
                    SafeDestroyUI(player, "SpectatorUI");
                    SafeDestroyUI(player, "FreeCamUI");
                    SafeDestroyUI(player, "VoiceChatUI");
                    SafeDestroyUI(player, "VoteUI");
                }
            }
            
            // Clear collections
            processingPlayers.Clear();
            lastActionTime.Clear();
        }
        
        #endregion
        
        #region Safety and Validation (Bug Fixes)
        
        // Thread-safe collection modification
        private void SafeModifyCollection(Action action)
        {
            lock (_lockObject)
            {
                try
                {
                    action();
                }
                catch (Exception ex)
                {
                    PrintError($"Error in SafeModifyCollection: {ex.Message}");
                }
            }
        }
        
        // Enhanced timer management
        private void DestroyTimer(ref Timer timer)
        {
            try
            {
                timer?.Destroy();
                timer = null;
            }
            catch (Exception ex)
            {
                PrintError($"Error destroying timer: {ex.Message}");
            }
        }
        
        // Enhanced player validation
        private bool IsValidPlayer(BasePlayer player)
        {
            return player != null && 
                   player.IsConnected && 
                   !player.IsDestroyed && 
                   player.net?.connection != null;
        }
        
        // Safe player data access
        private PlayerData GetSafePlayerData(ulong playerId)
        {
            if (playerId == 0) return null;
            
            lock (_lockObject)
            {
                if (!playerData.ContainsKey(playerId))
                {
                    var player = BasePlayer.FindByID(playerId);
                    if (!IsValidPlayer(player)) return null;
                    
                    playerData[playerId] = new PlayerData
                    {
                        PlayerId = playerId,
                        PlayerName = player.displayName,
                        JoinTime = DateTime.Now,
                        IsProcessing = false
                    };
                }
                
                return playerData[playerId];
            }
        }
        
        // Safe match access
        private Match GetSafeMatch(ulong matchId)
        {
            lock (_lockObject)
            {
                return activeMatches.ContainsKey(matchId) ? activeMatches[matchId] : null;
            }
        }
        
        // Safe collection iteration with modification
        private void SafeIterateAndModify<T>(ICollection<T> collection, Func<T, bool> shouldRemove, Action<T> onRemove = null)
        {
            lock (_lockObject)
            {
                var itemsToRemove = collection.Where(shouldRemove).ToList();
                foreach (var item in itemsToRemove)
                {
                    collection.Remove(item);
                    onRemove?.Invoke(item);
                }
            }
        }
        
        // Enhanced UI cleanup
        private void SafeDestroyUI(BasePlayer player, string uiName)
        {
            if (!IsValidPlayer(player)) return;
            
            try
            {
                CuiHelper.DestroyUi(player, uiName);
            }
            catch (Exception ex)
            {
                PrintError($"Error destroying UI '{uiName}' for {player.displayName}: {ex.Message}");
            }
        }
        
        // Resource cleanup helper
        private void CleanupPlayerResources(ulong playerId)
        {
            SafeModifyCollection(() =>
            {
                // Clean up player data
                if (playerData.ContainsKey(playerId))
                {
                    var data = playerData[playerId];
                    data.CleanupTimers();
                    playerData.Remove(playerId);
                }
                
                // Clean up from queues
                foreach (var zone in queueZones.Values.ToList())
                {
                    zone.QueuedPlayers.Remove(playerId);
                }
                
                // Clean up from matches
                foreach (var match in activeMatches.Values.ToList())
                {
                    match.TerroristPlayers.Remove(playerId);
                    match.CounterTerroristPlayers.Remove(playerId);
                }
                
                // Clean up processing states
                processingPlayers.Remove(playerId);
                lastActionTime.Remove(playerId);
            });
        }
        
        private bool CanPlayerPerformAction(BasePlayer player)
        {
            if (player == null || !player.IsConnected) return false;
            
            ulong playerId = player.userID;
            
            // Bug Fix: Prevent spam actions
            if (lastActionTime.ContainsKey(playerId))
            {
                if (Time.time - lastActionTime[playerId] < ACTION_COOLDOWN)
                {
                    return false;
                }
            }
            
            // Bug Fix: Prevent concurrent processing
            if (processingPlayers.Contains(playerId))
            {
                return false;
            }
            
            return true;
        }
        
        private void StartPlayerAction(BasePlayer player)
        {
            if (player == null) return;
            
            ulong playerId = player.userID;
            lastActionTime[playerId] = Time.time;
            processingPlayers.Add(playerId);
            
            var data = GetPlayerData(playerId);
            if (data != null)
            {
                data.IsProcessing = true;
                data.LastActionTime = Time.time;
            }
        }
        
        private void EndPlayerAction(BasePlayer player)
        {
            if (player == null) return;
            
            ulong playerId = player.userID;
            processingPlayers.Remove(playerId);
            
            var data = GetPlayerData(playerId);
            if (data != null)
            {
                data.IsProcessing = false;
            }
        }
        
        private bool IsValidPosition(Vector3 position)
        {
            // Bug Fix: Validate position is not NaN or infinity
            if (float.IsNaN(position.x) || float.IsNaN(position.y) || float.IsNaN(position.z))
                return false;
            
            if (float.IsInfinity(position.x) || float.IsInfinity(position.y) || float.IsInfinity(position.z))
                return false;
            
            // Bug Fix: Check if position is within reasonable bounds
            if (Mathf.Abs(position.x) > 10000f || Mathf.Abs(position.z) > 10000f)
                return false;
            
            if (position.y < -1000f || position.y > 1000f)
                return false;
            
            return true;
        }
        
        private Vector3 GetSafePosition(Vector3 position)
        {
            if (IsValidPosition(position))
                return position;
            
            // Bug Fix: Return a safe fallback position
            return config.LobbySpawnPoint != Vector3.zero ? config.LobbySpawnPoint : Vector3.zero;
        }
        
        private void PerformCleanup()
        {
            try
            {
                CleanupExpiredQueues();
                CleanupInactiveMatches();
                CleanupDisconnectedPlayers();
                CleanupOrphanedTimers();
            }
            catch (Exception ex)
            {
                PrintError($"Error during cleanup: {ex.Message}");
            }
        }
        
        private void CleanupExpiredQueues()
        {
            SafeModifyCollection(() =>
            {
                foreach (var zone in queueZones.Values.ToList())
                {
                    SafeIterateAndModify(zone.QueuedPlayers, playerId =>
                    {
                        var data = GetPlayerData(playerId);
                        return data == null || !data.IsInQueue || 
                               (data.QueueJoinTime > 0 && Time.time - data.QueueJoinTime > config.MaxQueueTime);
                    }, playerId =>
                    {
                        var data = GetPlayerData(playerId);
                        if (data != null)
                        {
                            data.IsInQueue = false;
                            data.QueueZoneName = "";
                        }
                    });
                }
            });
        }
        
        private void CleanupInactiveMatches()
        {
            SafeModifyCollection(() =>
            {
                var inactiveMatches = activeMatches.Where(kvp =>
                {
                    var match = kvp.Value;
                    
                    // Check if match has been running too long without activity
                    if (Time.time - match.MatchStartTime > 3600f) return true; // 1 hour
                    
                    // Check if all players have disconnected
                    var connectedPlayers = match.TerroristPlayers.Concat(match.CounterTerroristPlayers)
                        .Count(id => IsValidPlayer(BasePlayer.FindByID(id)));
                    
                    return connectedPlayers == 0;
                }).Select(kvp => kvp.Key).ToList();
                
                foreach (var matchId in inactiveMatches)
                {
                    if (activeMatches.ContainsKey(matchId))
                    {
                        EndMatch(activeMatches[matchId], "Match cleanup - inactive");
                    }
                }
            });
        }
        
        private void CleanupDisconnectedPlayers()
        {
            SafeModifyCollection(() =>
            {
                var disconnectedPlayers = playerData.Where(kvp =>
                {
                    var playerId = kvp.Key;
                    var player = BasePlayer.FindByID(playerId);
                    return !IsValidPlayer(player);
                }).Select(kvp => kvp.Key).ToList();
                
                foreach (var playerId in disconnectedPlayers)
                {
                    CleanupPlayerResources(playerId);
                }
            });
        }
                    if (data.CurrentMatchId != 0)
                    {
                        if (activeMatches.ContainsKey(data.CurrentMatchId))
                        {
                            var match = activeMatches[data.CurrentMatchId];
                            match.TerroristPlayers.Remove(playerId);
                            match.CounterTerroristPlayers.Remove(playerId);
                        }
                    }
                    
                    // Clean up queue involvement
                    if (data.IsInQueue && !string.IsNullOrEmpty(data.QueueZoneName))
                    {
                        if (queueZones.ContainsKey(data.QueueZoneName))
                        {
                            queueZones[data.QueueZoneName].QueuedPlayers.Remove(playerId);
                        }
                    }
                    
                    // Clean up timers
                    data.DefuseTimer?.Destroy();
                    
                    disconnectedPlayers.Add(playerId);
                }
            }
            
            foreach (var playerId in disconnectedPlayers)
            {
                playerData.Remove(playerId);
            }
        }
        
        private void CleanupOrphanedTimers()
        {
            // This would be implemented to clean up any timers that might be orphaned
            // In a real implementation, we'd track all timers and clean up unused ones
        }
        
        #endregion
        
        #region Data Management (Bug Fixed)
        
        private void LoadData()
        {
            try
            {
                playerData = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, PlayerData>>("CS2Plugin_PlayerData") ?? new Dictionary<ulong, PlayerData>();
                queueZones = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, QueueZone>>("CS2Plugin_QueueZones") ?? new Dictionary<string, QueueZone>();
                bombSites = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, BombSite>>("CS2Plugin_BombSites") ?? new Dictionary<string, BombSite>();
                mapConfigurations = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, MapConfiguration>>("CS2Plugin_MapConfigurations") ?? new Dictionary<string, MapConfiguration>();
                
                // Bug Fix: Validate loaded data
                ValidateLoadedData();
            }
            catch (Exception ex)
            {
                PrintError($"Error loading data: {ex.Message}");
                // Initialize empty collections on error
                playerData = new Dictionary<ulong, PlayerData>();
                queueZones = new Dictionary<string, QueueZone>();
                bombSites = new Dictionary<string, BombSite>();
                mapConfigurations = new Dictionary<string, MapConfiguration>();
            }
        }
        
        private void ValidateLoadedData()
        {
            // Bug Fix: Remove invalid player data
            var invalidPlayers = playerData.Where(kvp => !kvp.Value.IsValid()).Select(kvp => kvp.Key).ToList();
            foreach (var playerId in invalidPlayers)
            {
                playerData.Remove(playerId);
                PrintWarning($"Removed invalid player data for {playerId}");
            }
            
            // Bug Fix: Remove invalid queue zones
            var invalidZones = queueZones.Where(kvp => !kvp.Value.IsValid()).Select(kvp => kvp.Key).ToList();
            foreach (var zoneName in invalidZones)
            {
                queueZones.Remove(zoneName);
                PrintWarning($"Removed invalid queue zone: {zoneName}");
            }
            
            // Bug Fix: Remove invalid bomb sites
            var invalidSites = bombSites.Where(kvp => !kvp.Value.IsValid()).Select(kvp => kvp.Key).ToList();
            foreach (var siteName in invalidSites)
            {
                bombSites.Remove(siteName);
                PrintWarning($"Removed invalid bomb site: {siteName}");
            }
            
            // Bug Fix: Remove invalid map configurations
            var invalidMaps = mapConfigurations.Where(kvp => !kvp.Value.IsValid()).Select(kvp => kvp.Key).ToList();
            foreach (var mapName in invalidMaps)
            {
                mapConfigurations.Remove(mapName);
                PrintWarning($"Removed invalid map configuration: {mapName}");
            }
        }
        
        private void SaveData()
        {
            try
            {
                Interface.Oxide.DataFileSystem.WriteObject("CS2Plugin_PlayerData", playerData);
                Interface.Oxide.DataFileSystem.WriteObject("CS2Plugin_QueueZones", queueZones);
                Interface.Oxide.DataFileSystem.WriteObject("CS2Plugin_BombSites", bombSites);
                Interface.Oxide.DataFileSystem.WriteObject("CS2Plugin_MapConfigurations", mapConfigurations);
            }
            catch (Exception ex)
            {
                PrintError($"Error saving data: {ex.Message}");
            }
        }
        
        private PlayerData GetPlayerData(ulong playerId)
        {
            // Use the safer version
            return GetSafePlayerData(playerId);
        }
        
        private void InitializeDefaultMaps()
        {
            try
            {
                // Initialize Dust2 configuration with validation
                if (!mapConfigurations.ContainsKey("dust2"))
                {
                    var dust2Config = new MapConfiguration
                    {
                        MapName = "dust2",
                        SpectatorArea = new Vector3(0, 50, 0)
                    };
                    
                    // Generate spawn points with proper spacing
                    for (int i = 0; i < 8; i++)
                    {
                        dust2Config.TerroristSpawns.Add(new Vector3(100 + i * 3f, 0, 100));
                        dust2Config.CounterTerroristSpawns.Add(new Vector3(-100 + i * 3f, 0, -100));
                    }
                    
                    dust2Config.BombSiteNames.Add("dust2_A");
                    dust2Config.BombSiteNames.Add("dust2_B");
                    
                    mapConfigurations["dust2"] = dust2Config;
                }
                
                // Initialize Train configuration
                if (!mapConfigurations.ContainsKey("train"))
                {
                    var trainConfig = new MapConfiguration
                    {
                        MapName = "train",
                        SpectatorArea = new Vector3(0, 75, 0)
                    };
                    
                    for (int i = 0; i < 8; i++)
                    {
                        trainConfig.TerroristSpawns.Add(new Vector3(200 + i * 3f, 0, 200));
                        trainConfig.CounterTerroristSpawns.Add(new Vector3(-200 + i * 3f, 0, -200));
                    }
                    
                    trainConfig.BombSiteNames.Add("train_A");
                    trainConfig.BombSiteNames.Add("train_B");
                    
                    mapConfigurations["train"] = trainConfig;
                }
            }
            catch (Exception ex)
            {
                PrintError($"Error initializing default maps: {ex.Message}");
            }
        }
        
        private void InitializeWeaponSkins()
        {
          
// ... [Code preview truncated due to length. Full source in attached file] ...

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