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

FREE DayNightWeatherController : 0.7.0 0.7.0

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

namespace Oxide.Plugins
{
    [Info("DayNightWeatherController", "SeesAll", "0.7.0")]
    [Description("Controls day/night cycles and weather with smart runtime strategies.")]
    public class DayNightWeatherController : RustPlugin
    {
        private PluginConfig config;
        private RuntimeStrategy runtimeStrategy = RuntimeStrategy.TrueWorldControl;
        private Timer worldControlTimer;
        private Timer weatherVerifyTimer;

        private bool originalTimeStateCaptured;
        private bool originalProgressTime;
        private bool originalUseTimeCurve;
        private float originalDayLengthInMinutes;

        private CyclePhase currentPhase = CyclePhase.Unknown;
        private DateTime phaseStartedUtc;
        private bool observedFirstPhaseBoundary;
        private double lastCompletedDaySeconds = -1d;
        private double lastCompletedNightSeconds = -1d;

        private const string DefaultAdminOverridePermission = "daynightweathercontroller.adminoverride";
        private const float WorldControlIntervalSeconds = 1f;
        private const float WeatherVerifyIntervalSeconds = 180f;

        private enum RuntimeStrategy
        {
            TrueWorldControl,
            NoControl
        }

        private enum CyclePhase
        {
            Unknown,
            Day,
            Night
        }

        private class PluginConfig
        {
            [JsonProperty(PropertyName = "EnvironmentControl")]
            public EnvironmentControlSettings EnvironmentControl = new EnvironmentControlSettings();

            [JsonProperty(PropertyName = "TimeControl")]
            public TimeControlSettings TimeControl = new TimeControlSettings();

            [JsonProperty(PropertyName = "WeatherControl")]
            public WeatherControlSettings WeatherControl = new WeatherControlSettings();

            [JsonProperty(PropertyName = "Permissions")]
            public PermissionSettings Permissions = new PermissionSettings();
        }

        private class EnvironmentControlSettings
        {
            [JsonProperty(PropertyName = "Enabled")]
            public bool Enabled = true;
        }

        private class TimeControlSettings
        {
            [JsonProperty(PropertyName = "Mode (1=DAY, 2=NIGHT, 3=CUSTOM, 4=VANILLA)")]
            public int Mode = 1;

            [JsonProperty(PropertyName = "DayLengthMinutes (Custom Only)")]
            public int DayLengthMinutes = 55;

            [JsonProperty(PropertyName = "NightLengthMinutes (Custom Only)")]
            public int NightLengthMinutes = 5;

            [JsonProperty(PropertyName = "NightStartsAtHour (Custom Only, includes dusk)")]
            public float NightStartsAtHour = 18.5f;

            [JsonProperty(PropertyName = "DayStartsAtHour (Custom Only, includes dawn)")]
            public float DayStartsAtHour = 8f;

            [JsonProperty(PropertyName = "LockedHour (DAY/NIGHT Only, Recommended Day=12.0, Night=0.0)")]
            public float LockedHour = 12f;
        }

        private class WeatherControlSettings
        {
            [JsonProperty(PropertyName = "Mode (Clear, Overcast, RainHeavy, RainMild, Storm, Fog, Vanilla)")]
            public string Mode = "Clear";
        }

        private class PermissionSettings
        {
            [JsonProperty(PropertyName = "UseAdminOverridePermission")]
            public string UseAdminOverridePermission = DefaultAdminOverridePermission;
        }

        protected override void LoadDefaultConfig()
        {
            config = CreateDefaultConfig();
            SaveConfig();
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();

            try
            {
                var raw = Config.ReadObject<Dictionary<string, object>>();
                config = MigrateConfig(raw);
                SaveConfig();
            }
            catch (Exception ex)
            {
                PrintWarning($"Failed to load config, regenerating default config. Reason: {ex.Message}");
                config = CreateDefaultConfig();
                SaveConfig();
            }
        }

        protected override void SaveConfig() => Config.WriteObject(config, true);

        private PluginConfig CreateDefaultConfig()
        {
            return new PluginConfig();
        }

        private PluginConfig MigrateConfig(Dictionary<string, object> raw)
        {
            var migrated = CreateDefaultConfig();

            if (raw == null)
                return migrated;

            var environment = GetMap(raw, "EnvironmentControl");
            if (environment != null)
                migrated.EnvironmentControl.Enabled = GetBool(environment, "Enabled", migrated.EnvironmentControl.Enabled);

            var time = GetMap(raw, "TimeControl");
            if (time != null)
            {
                migrated.TimeControl.Mode =
                    GetInt(time, "Mode (1=DAY, 2=NIGHT, 3=CUSTOM, 4=VANILLA)",
                    GetInt(time, "Mode", migrated.TimeControl.Mode));

                migrated.TimeControl.DayLengthMinutes =
                    GetInt(time, "DayLengthMinutes (Custom Only)",
                    GetInt(time, "DayLengthMinutes", migrated.TimeControl.DayLengthMinutes));

                migrated.TimeControl.NightLengthMinutes =
                    GetInt(time, "NightLengthMinutes (Custom Only)",
                    GetInt(time, "NightLengthMinutes", migrated.TimeControl.NightLengthMinutes));

                migrated.TimeControl.NightStartsAtHour =
                    GetFloat(time, "NightStartsAtHour (Custom Only, includes dusk)",
                    GetFloat(time, "NightStartsAtHour", migrated.TimeControl.NightStartsAtHour));

                migrated.TimeControl.DayStartsAtHour =
                    GetFloat(time, "DayStartsAtHour (Custom Only, includes dawn)",
                    GetFloat(time, "DayStartsAtHour", migrated.TimeControl.DayStartsAtHour));

                migrated.TimeControl.LockedHour =
                    GetFloat(time, "LockedHour (DAY/NIGHT Only, Recommended Day=12.0, Night=0.0)",
                    GetFloat(time, "LockedHour", migrated.TimeControl.LockedHour));
            }

            var weather = GetMap(raw, "WeatherControl");
            if (weather != null)
            {
                migrated.WeatherControl.Mode =
                    GetString(weather, "Mode (Clear, Overcast, RainHeavy, RainMild, Storm, Fog, Vanilla)",
                    GetString(weather, "Mode (Clear, Overcast, Rain, Storm, Fog, Vanilla)",
                    GetString(weather, "Mode (Clear, Rain, Storm, Fog, Overcast, Vanilla)",
                    GetString(weather, "Mode", migrated.WeatherControl.Mode))));
            }

            var permissionsMap = GetMap(raw, "Permissions");
            if (permissionsMap != null)
            {
                migrated.Permissions.UseAdminOverridePermission =
                    GetString(permissionsMap, "UseAdminOverridePermission", migrated.Permissions.UseAdminOverridePermission);
            }

            NormalizeConfig(migrated);
            return migrated;
        }

        private void NormalizeConfig(PluginConfig cfg)
        {
            if (cfg.TimeControl.Mode < 1 || cfg.TimeControl.Mode > 4)
                cfg.TimeControl.Mode = 1;

            if (cfg.TimeControl.DayLengthMinutes < 1)
                cfg.TimeControl.DayLengthMinutes = 1;

            if (cfg.TimeControl.NightLengthMinutes < 1)
                cfg.TimeControl.NightLengthMinutes = 1;

            cfg.TimeControl.NightStartsAtHour = NormalizeHour(cfg.TimeControl.NightStartsAtHour);
            cfg.TimeControl.DayStartsAtHour = NormalizeHour(cfg.TimeControl.DayStartsAtHour);

            if (Math.Abs(cfg.TimeControl.NightStartsAtHour - cfg.TimeControl.DayStartsAtHour) < 0.01f)
            {
                cfg.TimeControl.NightStartsAtHour = 18.5f;
                cfg.TimeControl.DayStartsAtHour = 8f;
            }

            if (cfg.TimeControl.LockedHour < 0f)
                cfg.TimeControl.LockedHour = 0f;

            if (cfg.TimeControl.LockedHour > 24f)
                cfg.TimeControl.LockedHour = 24f;

            cfg.WeatherControl.Mode = NormalizeWeatherMode(cfg.WeatherControl.Mode);

            if (string.IsNullOrWhiteSpace(cfg.Permissions.UseAdminOverridePermission))
                cfg.Permissions.UseAdminOverridePermission = DefaultAdminOverridePermission;
        }

        private string NormalizeWeatherMode(string input)
        {
            if (string.IsNullOrWhiteSpace(input))
                return "Clear";

            string mode = input.Trim();

            if (mode.Equals("clear", StringComparison.OrdinalIgnoreCase)) return "Clear";
            if (mode.Equals("overcast", StringComparison.OrdinalIgnoreCase)) return "Overcast";
            if (mode.Equals("rain", StringComparison.OrdinalIgnoreCase)) return "RainHeavy";
            if (mode.Equals("rainheavy", StringComparison.OrdinalIgnoreCase)) return "RainHeavy";
            if (mode.Equals("rainmild", StringComparison.OrdinalIgnoreCase)) return "RainMild";
            if (mode.Equals("storm", StringComparison.OrdinalIgnoreCase)) return "Storm";
            if (mode.Equals("fog", StringComparison.OrdinalIgnoreCase)) return "Fog";
            if (mode.Equals("vanilla", StringComparison.OrdinalIgnoreCase)) return "Vanilla";

            PrintWarning($"Unknown weather mode '{input}', defaulting to Clear.");
            return "Clear";
        }

        private float NormalizeHour(float hour)
        {
            hour %= 24f;
            return hour < 0f ? hour + 24f : hour;
        }

        private float ForwardHourSpan(float fromHour, float toHour)
        {
            float span = NormalizeHour(toHour) - NormalizeHour(fromHour);
            return span <= 0f ? span + 24f : span;
        }

        private Dictionary<string, object> GetMap(Dictionary<string, object> root, string key)
        {
            object value;
            if (!root.TryGetValue(key, out value) || value == null)
                return null;

            var dict = value as Dictionary<string, object>;
            if (dict != null)
                return dict;

            var jObject = value as Newtonsoft.Json.Linq.JObject;
            return jObject != null ? jObject.ToObject<Dictionary<string, object>>() : null;
        }

        private bool GetBool(Dictionary<string, object> map, string key, bool fallback)
        {
            object value;
            if (!map.TryGetValue(key, out value) || value == null)
                return fallback;

            try { return Convert.ToBoolean(value); }
            catch { return fallback; }
        }

        private int GetInt(Dictionary<string, object> map, string key, int fallback)
        {
            object value;
            if (!map.TryGetValue(key, out value) || value == null)
                return fallback;

            try { return Convert.ToInt32(value); }
            catch { return fallback; }
        }

        private float GetFloat(Dictionary<string, object> map, string key, float fallback)
        {
            object value;
            if (!map.TryGetValue(key, out value) || value == null)
                return fallback;

            try { return Convert.ToSingle(value); }
            catch { return fallback; }
        }

        private string GetString(Dictionary<string, object> map, string key, string fallback)
        {
            object value;
            if (!map.TryGetValue(key, out value) || value == null)
                return fallback;

            return Convert.ToString(value);
        }

        private string GetTimeModeName(int mode)
        {
            switch (mode)
            {
                case 1: return "DAY";
                case 2: return "NIGHT";
                case 3: return "CUSTOM";
                case 4: return "VANILLA";
                default: return $"UNKNOWN({mode})";
            }
        }

        private bool IsForcedWeather()
        {
            return !string.Equals(config.WeatherControl.Mode, "Vanilla", StringComparison.OrdinalIgnoreCase);
        }

        private bool HasValidSky()
        {
            return TOD_Sky.Instance != null && TOD_Sky.Instance.Components != null && TOD_Sky.Instance.Components.Time != null;
        }

        private void Init()
        {
            config = config ?? CreateDefaultConfig();
            NormalizeConfig(config);
            RegisterConfiguredPermission();
        }

        private void OnServerInitialized()
        {
            CaptureOriginalTimeState();
            ResolveRuntimeStrategy();

            if (runtimeStrategy == RuntimeStrategy.TrueWorldControl)
            {
                InitializePhaseTracking();
                ApplyWorldControlState();
                StartWorldControlTimer();
            }

            if (IsForcedWeather())
            {
                ApplyConfiguredWeather(true);
                StartWeatherVerifyTimer();
            }

            LogRuntimeSummary();
        }

        private void Unload()
        {
            worldControlTimer?.Destroy();
            weatherVerifyTimer?.Destroy();

            RestoreOriginalTimeState();

            if (IsForcedWeather())
                ConsoleSystem.Run(ConsoleSystem.Option.Server, "weather.reset");
        }

        private void ResolveRuntimeStrategy()
        {
            worldControlTimer?.Destroy();

            if (!config.EnvironmentControl.Enabled)
            {
                runtimeStrategy = RuntimeStrategy.NoControl;
                return;
            }

            if (config.TimeControl.Mode == 4)
            {
                runtimeStrategy = RuntimeStrategy.NoControl;
                return;
            }

            runtimeStrategy = RuntimeStrategy.TrueWorldControl;
        }

        private void LogRuntimeSummary()
        {
            Puts("[DayNightWeatherController]");
            Puts($"Strategy: {runtimeStrategy}");
            Puts($"Time Mode: {GetTimeModeName(config.TimeControl.Mode)}");
            Puts($"Weather Mode: {config.WeatherControl.Mode}");

            if (config.TimeControl.Mode == 3)
            {
                Puts($"Visible Night Window: {config.TimeControl.NightStartsAtHour:F2} to {config.TimeControl.DayStartsAtHour:F2}");
                Puts($"Configured Durations: Day {config.TimeControl.DayLengthMinutes}m, Night {config.TimeControl.NightLengthMinutes}m");
            }
        }

        private void StartWorldControlTimer()
        {
            worldControlTimer?.Destroy();
            worldControlTimer = timer.Every(WorldControlIntervalSeconds, ApplyWorldControlState);
        }

        private void ApplyWorldControlState()
        {
            if (runtimeStrategy != RuntimeStrategy.TrueWorldControl || !HasValidSky())
                return;

            var timeComponent = TOD_Sky.Instance.Components.Time;

            if (config.TimeControl.Mode == 4)
                return;

            timeComponent.UseTimeCurve = false;

            switch (config.TimeControl.Mode)
            {
                case 1:
                    timeComponent.ProgressTime = false;
                    TOD_Sky.Instance.Cycle.Hour = config.TimeControl.LockedHour;
                    ConVar.Env.time = config.TimeControl.LockedHour;
                    break;

                case 2:
                    timeComponent.ProgressTime = false;
                    TOD_Sky.Instance.Cycle.Hour = 0f;
                    ConVar.Env.time = 0f;
                    break;

                case 3:
                    ApplyCustomCycleState(timeComponent);
                    break;
            }
        }

        private void ApplyCustomCycleState(TOD_Time timeComponent)
        {
            timeComponent.ProgressTime = true;

            float currentHour = TOD_Sky.Instance.Cycle.Hour;
            CyclePhase phase = IsConfiguredNight(currentHour) ? CyclePhase.Night : CyclePhase.Day;
            UpdatePhaseTracking(phase);

            float nightSpan = ForwardHourSpan(config.TimeControl.NightStartsAtHour, config.TimeControl.DayStartsAtHour);
            float daySpan = Math.Max(0.1f, 24f - nightSpan);

            if (phase == CyclePhase.Day)
            {
                timeComponent.DayLengthInMinutes = config.TimeControl.DayLengthMinutes * (24f / daySpan);
            }
            else
            {
                timeComponent.DayLengthInMinutes = config.TimeControl.NightLengthMinutes * (24f / nightSpan);
            }
        }

        private bool IsConfiguredNight(float hour)
        {
            float start = config.TimeControl.NightStartsAtHour;
            float end = config.TimeControl.DayStartsAtHour;
            hour = NormalizeHour(hour);

            return start < end
                ? hour >= start && hour < end
                : hour >= start || hour < end;
        }

        private void InitializePhaseTracking()
        {
            if (!HasValidSky() || config.TimeControl.Mode != 3)
                return;

            currentPhase = IsConfiguredNight(TOD_Sky.Instance.Cycle.Hour) ? CyclePhase.Night : CyclePhase.Day;
            phaseStartedUtc = DateTime.UtcNow;
            observedFirstPhaseBoundary = false;
        }

        private void UpdatePhaseTracking(CyclePhase phase)
        {
            if (phase == currentPhase)
                return;

            DateTime now = DateTime.UtcNow;
            double elapsedSeconds = phaseStartedUtc == default(DateTime)
                ? 0d
                : (now - phaseStartedUtc).TotalSeconds;

            if (observedFirstPhaseBoundary && currentPhase != CyclePhase.Unknown)
            {
                if (currentPhase == CyclePhase.Day)
                    lastCompletedDaySeconds = elapsedSeconds;
                else if (currentPhase == CyclePhase.Night)
                    lastCompletedNightSeconds = elapsedSeconds;

                Puts($"Completed {currentPhase} phase in {FormatDuration(elapsedSeconds)}.");
            }
            else
            {
                observedFirstPhaseBoundary = true;
            }

            currentPhase = phase;
            phaseStartedUtc = now;
            Puts($"Entered {currentPhase} phase at server hour {TOD_Sky.Instance.Cycle.Hour:F2}.");
        }

        private void CaptureOriginalTimeState()
        {
            if (originalTimeStateCaptured || !HasValidSky())
                return;

            var timeComponent = TOD_Sky.Instance.Components.Time;
            originalProgressTime = timeComponent.ProgressTime;
            originalUseTimeCurve = timeComponent.UseTimeCurve;
            originalDayLengthInMinutes = timeComponent.DayLengthInMinutes;
            originalTimeStateCaptured = true;
        }

        private void RestoreOriginalTimeState()
        {
            if (!originalTimeStateCaptured || !HasValidSky())
                return;

            var timeComponent = TOD_Sky.Instance.Components.Time;
            timeComponent.ProgressTime = originalProgressTime;
            timeComponent.UseTimeCurve = originalUseTimeCurve;
            timeComponent.DayLengthInMinutes = originalDayLengthInMinutes;
        }

        private string FormatDuration(double totalSeconds)
        {
            if (totalSeconds < 0d)
                return "not measured yet";

            TimeSpan duration = TimeSpan.FromSeconds(totalSeconds);
            return duration.TotalHours >= 1d
                ? $"{(int)duration.TotalHours}h {duration.Minutes}m {duration.Seconds}s"
                : $"{duration.Minutes}m {duration.Seconds}s";
        }

        private string BuildStatusMessage()
        {
            float serverHour = HasValidSky() ? TOD_Sky.Instance.Cycle.Hour : -1f;
            string phase = currentPhase.ToString();
            string elapsed = phaseStartedUtc == default(DateTime)
                ? "not available"
                : FormatDuration((DateTime.UtcNow - phaseStartedUtc).TotalSeconds);

            string effectiveCycle = HasValidSky()
                ? TOD_Sky.Instance.Components.Time.DayLengthInMinutes.ToString("F2") + " minutes per 24 game hours"
                : "not available";

            return
                $"RuntimeStrategy: {runtimeStrategy}\n" +
                $"TimeMode: {GetTimeModeName(config.TimeControl.Mode)}\n" +
                $"WeatherMode: {config.WeatherControl.Mode}\n" +
                $"ServerHour: {serverHour:F2}\n" +
                $"CurrentPhase: {phase} (elapsed since observed boundary/load: {elapsed})\n" +
                $"VisibleNightWindow: {config.TimeControl.NightStartsAtHour:F2} to {config.TimeControl.DayStartsAtHour:F2}\n" +
                $"ConfiguredDurations: Day {config.TimeControl.DayLengthMinutes}m, Night {config.TimeControl.NightLengthMinutes}m\n" +
                $"LastCompletedDay: {FormatDuration(lastCompletedDaySeconds)}\n" +
                $"LastCompletedNight: {FormatDuration(lastCompletedNightSeconds)}\n" +
                $"EffectiveCycleRate: {effectiveCycle}";
        }

        private void StartWeatherVerifyTimer()
        {
            weatherVerifyTimer?.Destroy();
            weatherVerifyTimer = timer.Every(WeatherVerifyIntervalSeconds, () =>
            {
                if (IsForcedWeather())
                    ApplyConfiguredWeather(false);
            });
        }

        private void ApplyConfiguredWeather(bool forceVanillaReset)
        {
            string mode = config.WeatherControl.Mode ?? "Clear";

            if (string.Equals(mode, "Vanilla", StringComparison.OrdinalIgnoreCase))
            {
                if (forceVanillaReset)
                    ConsoleSystem.Run(ConsoleSystem.Option.Server, "weather.reset");
                return;
            }

            ConsoleSystem.Run(ConsoleSystem.Option.Server, $"weather.load {mode}");
        }

        private void RegisterConfiguredPermission()
        {
            string perm = config != null && !string.IsNullOrWhiteSpace(config.Permissions.UseAdminOverridePermission)
                ? config.Permissions.UseAdminOverridePermission
                : DefaultAdminOverridePermission;

            permission.RegisterPermission(perm, this);
        }

        private bool HasAdminOverrideAccess(BasePlayer player)
        {
            if (player == null)
                return false;

            string perm = config.Permissions.UseAdminOverridePermission;
            if (string.IsNullOrWhiteSpace(perm))
                perm = DefaultAdminOverridePermission;

            return permission.UserHasPermission(player.UserIDString, perm);
        }

        private bool RequireAdminOverrideAccess(BasePlayer player)
        {
            if (HasAdminOverrideAccess(player))
                return true;

            SendReply(player, "You do not have permission to use this command.");
            return false;
        }

        [ChatCommand("envstatus")]
        private void CmdEnvStatus(BasePlayer player, string command, string[] args)
        {
            if (!RequireAdminOverrideAccess(player))
                return;

            SendReply(player, BuildStatusMessage());
        }

        [ConsoleCommand("dnwc.status")]
        private void CmdConsoleStatus(ConsoleSystem.Arg arg)
        {
            if (arg.Connection != null && arg.Connection.authLevel < 2)
            {
                arg.ReplyWith("You do not have permission to use this command.");
                return;
            }

            arg.ReplyWith(BuildStatusMessage());
        }
    }
}

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