using System; using Newtonsoft.Json; using UnityEngine; namespace Oxide.Plugins { [Info("DeepSeaWarning", "Uzumi & NordicRust", "1.0.0")] [Description("Broadcasts chat and toast warnings when Deep Sea opens (brief navmesh-related lag).")] internal class DeepSeaWarning : RustPlugin { private const string UpdateCheckUrl = "https://nordicrust.eu/plugin-version.php?slug=deepseawarning"; private const string UpdatePageUrl = "https://nordicrust.eu/plugins"; private bool _updateCheckScheduled; private void Init() { ScheduleUpdateCheck(); } private void ScheduleUpdateCheck() { if (_updateCheckScheduled) return; _updateCheckScheduled = true; timer.Once(8f, CheckForPluginUpdate); } private void CheckForPluginUpdate() { webrequest.Enqueue(UpdateCheckUrl, null, (code, response) => { if (code != 200 || string.IsNullOrWhiteSpace(response)) return; string latestVersion = null; string updateUrl = UpdatePageUrl; var lines = response.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { var idx = line.IndexOf('='); if (idx <= 0) continue; var key = line.Substring(0, idx).Trim(); var value = line.Substring(idx + 1).Trim(); if (key.Equals("latest_version", StringComparison.OrdinalIgnoreCase)) latestVersion = value; else if (key.Equals("download_url", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(value)) updateUrl = value; } if (string.IsNullOrEmpty(latestVersion)) return; var currentVersion = Version.ToString(); if (IsRemoteVersionNewer(currentVersion, latestVersion)) { PrintWarning($"Update available for {Name}: current {currentVersion}, latest {latestVersion}. Download: {updateUrl}"); } }, this); } private static bool IsRemoteVersionNewer(string currentVersion, string remoteVersion) { var current = ParseLooseVersion(currentVersion); var remote = ParseLooseVersion(remoteVersion); return remote.CompareTo(current) > 0; } private static System.Version ParseLooseVersion(string value) { if (string.IsNullOrWhiteSpace(value)) return new System.Version(0, 0, 0); var core = value.Trim(); var dash = core.IndexOf('-'); if (dash >= 0) core = core.Substring(0, dash); var plus = core.IndexOf('+'); if (plus >= 0) core = core.Substring(0, plus); System.Version parsed; return System.Version.TryParse(core, out parsed) ? parsed : new System.Version(0, 0, 0); } #region Configuration private PluginConfig _config; private sealed class PluginConfig { [JsonProperty("Enabled")] public bool Enabled = true; [JsonProperty("Chat icon SteamID (optional)")] public string ChatIconSteamId = ""; [JsonProperty("Toast duration seconds")] public float ToastSeconds = 8f; [JsonProperty("Toast type (0=Info, 1=Error, 2=Warning)")] public int ToastType = 2; [JsonProperty("Announce: Deep Sea opening (start)")] public bool AnnounceOnOpen = true; [JsonProperty("Announce: Deep Sea opened (done)")] public bool AnnounceOnOpened = false; [JsonProperty("Announce: Deep Sea closing (start)")] public bool AnnounceOnClose = false; [JsonProperty("Announce: Deep Sea closed (done)")] public bool AnnounceOnClosed = false; [JsonProperty("Open message (chat + toast)")] public string MessageOpen = "Deep Sea is opening now. Expect server lag for a moment."; [JsonProperty("Opened message (chat + toast)")] public string MessageOpened = "Deep Sea finished opening."; [JsonProperty("Close message (chat + toast)")] public string MessageClose = "Deep Sea is closing now."; [JsonProperty("Closed message (chat + toast)")] public string MessageClosed = "Deep Sea finished closing."; [JsonProperty("Minimum seconds between announcements (per event)")] public float CooldownSeconds = 60f; } protected override void LoadConfig() { base.LoadConfig(); try { _config = Config.ReadObject(); if (_config == null) throw new Exception("Config returned null"); } catch (Exception e) { PrintWarning($"Config error, loading defaults. {e.Message}"); LoadDefaultConfig(); } SaveConfig(); } protected override void LoadDefaultConfig() { _config = new PluginConfig(); SaveConfig(); } private new void SaveConfig() => Config.WriteObject(_config, true); #endregion #region State private DateTime _lastOpenAnnounceUtc = DateTime.MinValue; private DateTime _lastOpenedAnnounceUtc = DateTime.MinValue; private DateTime _lastCloseAnnounceUtc = DateTime.MinValue; private DateTime _lastClosedAnnounceUtc = DateTime.MinValue; #endregion #region Hooks private void OnDeepSeaOpen(DeepSeaManager local1) { if (!IsEnabled() || !_config.AnnounceOnOpen) return; if (!TryPassCooldown(ref _lastOpenAnnounceUtc)) return; BroadcastChatAndToast(_config.MessageOpen); } private void OnDeepSeaOpened(DeepSeaManager local1) { if (!IsEnabled() || !_config.AnnounceOnOpened) return; if (!TryPassCooldown(ref _lastOpenedAnnounceUtc)) return; BroadcastChatAndToast(_config.MessageOpened); } private void OnDeepSeaClose(DeepSeaManager local1) { if (!IsEnabled() || !_config.AnnounceOnClose) return; if (!TryPassCooldown(ref _lastCloseAnnounceUtc)) return; BroadcastChatAndToast(_config.MessageClose); } private void OnDeepSeaClosed(DeepSeaManager local1) { if (!IsEnabled() || !_config.AnnounceOnClosed) return; if (!TryPassCooldown(ref _lastClosedAnnounceUtc)) return; BroadcastChatAndToast(_config.MessageClosed); } private void Unload() { foreach (var player in BasePlayer.activePlayerList) player?.SendConsoleCommand("gametip.hidegametip"); } #endregion #region Messaging private bool IsEnabled() => _config != null && _config.Enabled; private bool TryPassCooldown(ref DateTime lastUtc) { float cd = Mathf.Clamp(_config?.CooldownSeconds ?? 0f, 0f, 3600f); if (cd <= 0f) { lastUtc = DateTime.UtcNow; return true; } var now = DateTime.UtcNow; if ((now - lastUtc).TotalSeconds < cd) return false; lastUtc = now; return true; } private void BroadcastChatAndToast(string message) { if (string.IsNullOrWhiteSpace(message)) return; foreach (var player in BasePlayer.activePlayerList) { if (player == null || !player.IsConnected) continue; SendChat(player, message); ShowToast(player, message); } } private void SendChat(BasePlayer player, string message) { if (player == null) return; if (!string.IsNullOrEmpty(_config.ChatIconSteamId) && ulong.TryParse(_config.ChatIconSteamId, out _)) { rust.SendChatMessage(player, string.Empty, message, _config.ChatIconSteamId); return; } SendReply(player, message); } private void ShowToast(BasePlayer player, string text) { if (player == null || string.IsNullOrEmpty(text) || _config == null) return; float seconds = Mathf.Clamp(_config.ToastSeconds, 1f, 60f); int type = Mathf.Clamp(_config.ToastType, 0, 2); player.SendConsoleCommand("gametip.hidegametip"); player.SendConsoleCommand("gametip.showtoast", type, text); timer.Once(seconds, () => player?.SendConsoleCommand("gametip.hidegametip")); } #endregion } }