using System.Collections.Generic; using System; using UnityEngine; using Oxide.Core; using Oxide.Game.Rust.Cui; using Network; using System.Linq; using Oxide.Core.Libraries.Covalence; namespace Oxide.Plugins { [Info("Timers", "Von", "1.1.0")] [Description("Just a simple timer plugin")] class Timers : RustPlugin { private const string PERMISSION_USE = "timers.use"; private string timerUI = ""; private DateTime? endTime = null; private string timerText = ""; private HashSet playersWithUI = new HashSet(); private HashSet targetSteamIds = new HashSet(); private HashSet targetGroups = new HashSet(); private Configuration config; class Configuration { public string BackgroundColor = "#CC000000"; public string TextColor = "#FFFFFF"; public bool EnableRestartTimer = true; public bool TransparentBackground = false; public int FontSize = 14; } protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) throw new Exception(); } catch { LoadDefaultConfig(); } } protected override void LoadDefaultConfig() { config = new Configuration(); SaveConfig(); } protected override void SaveConfig() => Config.WriteObject(config); void Init() { permission.RegisterPermission(PERMISSION_USE, this); cmd.AddConsoleCommand("timer", this, nameof(RconTimer)); cmd.AddConsoleCommand("timer.start", this, nameof(ConsoleStart)); cmd.AddConsoleCommand("timer.stop", this, nameof(ConsoleStop)); } void OnServerInitialized() { cmd.AddChatCommand("timer", this, "CmdTimer"); } void OnServerShutdown() { if (config.EnableRestartTimer && !endTime.HasValue) { StartTimer(300, "Server Restarting"); } } void Unload() { foreach (BasePlayer player in BasePlayer.activePlayerList) { DestroyUI(player); } } void OnServerCommand(ConsoleSystem.Arg arg) { if (!config.EnableRestartTimer) return; string command = arg.cmd?.Name?.ToLower(); if (command == "restart" && arg.HasArgs(1)) { if (int.TryParse(arg.Args[0], out int seconds)) { StartTimer(seconds, "Server Restarting"); } } } void ConsoleStart(ConsoleSystem.Arg arg) { if (!arg.HasArgs(2)) { arg.ReplyWith("Usage: timer.start [@group1 @group2 steamid1 steamid2 ...]"); return; } if (!int.TryParse(arg.Args[0], out int seconds)) { arg.ReplyWith("Please provide a valid number of seconds!"); return; } // Separiamo il testo dai SteamID e gruppi List localSteamIds = new List(); List localGroups = new List(); string text = ""; bool foundTarget = false; for (int i = 1; i < arg.Args.Length; i++) { // Se l'argomento è un gruppo (inizia con @) if (arg.Args[i].StartsWith("@")) { foundTarget = true; string groupName = arg.Args[i].Substring(1); // Rimuove il @ localGroups.Add(groupName); } // Se l'argomento è un SteamID else if (arg.Args[i].StartsWith("7656")) { foundTarget = true; localSteamIds.Add(arg.Args[i]); } else if (!foundTarget) { // Se non abbiamo ancora trovato target, è parte del testo text += (text.Length > 0 ? " " : "") + arg.Args[i]; } } if (text.Length > 30) { arg.ReplyWith("Timer text cannot exceed 30 characters!"); return; } timerText = text; targetSteamIds.Clear(); targetGroups.Clear(); string targetMessage = ""; if (localSteamIds.Count > 0) { foreach (var id in localSteamIds) { targetSteamIds.Add(id); } targetMessage += $"{localSteamIds.Count} players"; } if (localGroups.Count > 0) { foreach (var group in localGroups) { targetGroups.Add(group); } targetMessage += (targetMessage.Length > 0 ? " and " : "") + $"{localGroups.Count} groups"; } if (targetMessage.Length > 0) { arg.ReplyWith($"Timer started for {targetMessage}!"); } else { arg.ReplyWith("Timer started for all players!"); } StartTimer(seconds, text); } void ConsoleStop(ConsoleSystem.Arg arg) { if (!endTime.HasValue) { arg.ReplyWith("No timer is currently running!"); return; } endTime = null; timerText = ""; targetSteamIds.Clear(); targetGroups.Clear(); foreach (BasePlayer p in BasePlayer.activePlayerList) { DestroyUI(p); } arg.ReplyWith("Timer stopped!"); } void RconTimer(ConsoleSystem.Arg arg) { if (!arg.HasArgs()) { arg.ReplyWith("Usage: timer start [@group1 @group2 steamid1 steamid2 ...] or timer stop"); return; } switch (arg.Args[0].ToLower()) { case "start": if (!arg.HasArgs(2)) { arg.ReplyWith("Usage: timer start [@group1 @group2 steamid1 steamid2 ...]"); return; } if (!int.TryParse(arg.Args[1], out int seconds)) { arg.ReplyWith("Please provide a valid number of seconds!"); return; } // Separiamo il testo dai SteamID e gruppi List localSteamIds = new List(); List localGroups = new List(); string text = ""; bool foundTarget = false; for (int i = 2; i < arg.Args.Length; i++) { // Se l'argomento è un gruppo (inizia con @) if (arg.Args[i].StartsWith("@")) { foundTarget = true; string groupName = arg.Args[i].Substring(1); // Rimuove il @ localGroups.Add(groupName); } // Se l'argomento è un SteamID else if (arg.Args[i].StartsWith("7656")) { foundTarget = true; localSteamIds.Add(arg.Args[i]); } else if (!foundTarget) { // Se non abbiamo ancora trovato target, è parte del testo text += (text.Length > 0 ? " " : "") + arg.Args[i]; } } if (text.Length > 30) { arg.ReplyWith("Timer text cannot exceed 30 characters!"); return; } timerText = text; targetSteamIds.Clear(); targetGroups.Clear(); string targetMessage = ""; if (localSteamIds.Count > 0) { foreach (var id in localSteamIds) { targetSteamIds.Add(id); } targetMessage += $"{localSteamIds.Count} players"; } if (localGroups.Count > 0) { foreach (var group in localGroups) { targetGroups.Add(group); } targetMessage += (targetMessage.Length > 0 ? " and " : "") + $"{localGroups.Count} groups"; } if (targetMessage.Length > 0) { arg.ReplyWith($"Timer started for {targetMessage}!"); } else { arg.ReplyWith("Timer started for all players!"); } StartTimer(seconds, text); break; case "stop": if (endTime.HasValue) { endTime = null; timerText = ""; targetSteamIds.Clear(); targetGroups.Clear(); foreach (BasePlayer p in BasePlayer.activePlayerList) { DestroyUI(p); } arg.ReplyWith("Timer stopped!"); } else { arg.ReplyWith("No timer is currently running!"); } break; default: arg.ReplyWith("Usage: timer start [@group1 @group2 steamid1 steamid2 ...] or timer stop"); break; } } void CmdTimer(BasePlayer player, string command, string[] args) { if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) { SendReply(player, "You don't have permission to use this command!"); return; } if (args.Length == 0) { SendReply(player, "Usage: /timer start [@group1 @group2 steamid1 steamid2 ...] or /timer stop"); return; } switch (args[0].ToLower()) { case "start": if (args.Length < 2) { SendReply(player, "Usage: /timer start [@group1 @group2 steamid1 steamid2 ...]"); return; } if (!int.TryParse(args[1], out int seconds)) { SendReply(player, "Please provide a valid number of seconds!"); return; } // Separiamo il testo dai SteamID e gruppi List localSteamIds = new List(); List localGroups = new List(); string text = ""; bool foundTarget = false; for (int i = 2; i < args.Length; i++) { // Se l'argomento è un gruppo (inizia con @) if (args[i].StartsWith("@")) { foundTarget = true; string groupName = args[i].Substring(1); // Rimuove il @ localGroups.Add(groupName); } // Se l'argomento è un SteamID else if (args[i].StartsWith("7656")) { foundTarget = true; localSteamIds.Add(args[i]); } else if (!foundTarget) { // Se non abbiamo ancora trovato target, è parte del testo text += (text.Length > 0 ? " " : "") + args[i]; } } if (text.Length > 30) { SendReply(player, "Timer text cannot exceed 30 characters!"); return; } timerText = text; this.targetSteamIds.Clear(); this.targetGroups.Clear(); string targetMessage = ""; if (localSteamIds.Count > 0) { foreach (var id in localSteamIds) { this.targetSteamIds.Add(id); } targetMessage += $"{localSteamIds.Count} players"; } if (localGroups.Count > 0) { foreach (var group in localGroups) { this.targetGroups.Add(group); } targetMessage += (targetMessage.Length > 0 ? " and " : "") + $"{localGroups.Count} groups"; } if (targetMessage.Length > 0) { SendReply(player, $"Timer started for {targetMessage}!"); } else { SendReply(player, "Timer started for all players!"); } StartTimer(seconds, text); break; case "stop": if (endTime.HasValue) { endTime = null; timerText = ""; targetSteamIds.Clear(); targetGroups.Clear(); foreach (BasePlayer p in BasePlayer.activePlayerList) { DestroyUI(p); } SendReply(player, "Timer stopped!"); } else { SendReply(player, "No timer is currently running!"); } break; default: SendReply(player, "Usage: /timer start [@group1 @group2 steamid1 steamid2 ...] or /timer stop"); break; } } #region Hooks for other plugins // Hook called when a timer starts private void OnTimerStarted(int seconds, string text) { // Rimuoviamo la chiamata ricorsiva // Interface.CallHook("OnTimerStarted", seconds, text); } // Hook called when a timer stops private void OnTimerStopped() { // Interface.CallHook("OnTimerStopped"); } // Hook called when a timer completes naturally private void OnTimerCompleted() { // Interface.CallHook("OnTimerCompleted"); } // Public methods for other plugins public bool StartTimerFromPlugin(int seconds, string text = "", List steamIds = null, List groups = null) { if (endTime.HasValue) return false; targetSteamIds.Clear(); targetGroups.Clear(); if (steamIds != null && steamIds.Count > 0) { foreach (var id in steamIds) { targetSteamIds.Add(id); } } if (groups != null && groups.Count > 0) { foreach (var group in groups) { targetGroups.Add(group); } } StartTimer(seconds, text); return true; } public bool StopTimerFromPlugin() { if (!endTime.HasValue) return false; endTime = null; timerText = ""; targetSteamIds.Clear(); targetGroups.Clear(); foreach (BasePlayer p in BasePlayer.activePlayerList) { DestroyUI(p); } OnTimerStopped(); return true; } public TimeSpan? GetRemainingTime() { if (!endTime.HasValue) return null; return endTime.Value - DateTime.Now; } public string GetCurrentTimerText() { return timerText; } #endregion private bool ShouldShowTimerToPlayer(BasePlayer player) { // Se non ci sono restrizioni, mostra a tutti if (targetSteamIds.Count == 0 && targetGroups.Count == 0) { return true; } // Controlla se il player è nella lista degli SteamID if (targetSteamIds.Contains(player.UserIDString)) { return true; } // Controlla se il player appartiene a uno dei gruppi target foreach (var group in targetGroups) { if (permission.UserHasGroup(player.UserIDString, group)) { return true; } } return false; } void StartTimer(int seconds, string text = "") { endTime = DateTime.Now.AddSeconds(seconds); timerText = text; // Rimuovi l'UI da tutti i giocatori prima foreach (BasePlayer player in BasePlayer.activePlayerList) { DestroyUI(player); playersWithUI.Remove(player); } // Mostra l'UI solo ai giocatori autorizzati foreach (BasePlayer player in BasePlayer.activePlayerList) { if (ShouldShowTimerToPlayer(player)) { CreateUI(player); playersWithUI.Add(player); } } // Chiamiamo l'hook qui invece che nel metodo OnTimerStarted Interface.CallHook("OnTimerStarted", seconds, text); timer.Every(1f, () => { if (!endTime.HasValue) return; TimeSpan remaining = endTime.Value - DateTime.Now; if (remaining.TotalSeconds <= 0) { endTime = null; timerText = ""; targetSteamIds.Clear(); targetGroups.Clear(); foreach (BasePlayer player in BasePlayer.activePlayerList) { DestroyUI(player); playersWithUI.Remove(player); } // Chiamiamo l'hook qui invece che nel metodo OnTimerCompleted Interface.CallHook("OnTimerCompleted"); return; } UpdateAllUI(); }); } void UpdateAllUI() { string displayText = GetTimeString(); foreach (BasePlayer player in playersWithUI) { if (player == null || !player.IsConnected) continue; if (!ShouldShowTimerToPlayer(player)) { DestroyUI(player); playersWithUI.Remove(player); continue; } DestroyUI(player); CreateUI(player); } } void OnPlayerConnected(BasePlayer player) { if (endTime.HasValue && ShouldShowTimerToPlayer(player)) { CreateUI(player); } } void OnPlayerDisconnected(BasePlayer player) { DestroyUI(player); playersWithUI.Remove(player); } void CreateUI(BasePlayer player) { DestroyUI(player); string displayText = GetTimeString(); float charWidth = 3f; float textWidth = displayText.Length * charWidth; float padding = 10f; float totalWidth = textWidth + (padding * 2); float minWidth = 40f; float width = Math.Max(totalWidth, minWidth) / Screen.width; string containerName = "Timer_Container"; var elements = new CuiElementContainer(); elements.Add(new CuiElement { Parent = "Hud", Components = { new CuiImageComponent { Color = config.TransparentBackground ? "0 0 0 0" : HexToRustColor(config.BackgroundColor) }, new CuiRectTransformComponent { AnchorMin = $"{1 - width} 0.95", AnchorMax = "1 0.98" } }, Name = containerName }); elements.Add(new CuiElement { Parent = containerName, Components = { new CuiTextComponent { Text = displayText, FontSize = config.FontSize, Align = TextAnchor.MiddleCenter, Color = HexToRustColor(config.TextColor) }, new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" } }, Name = "Timer_Text" }); CuiHelper.AddUi(player, elements); playersWithUI.Add(player); } void DestroyUI(BasePlayer player) { CuiHelper.DestroyUi(player, "Timer_Container"); } string GetTimeString() { if (!endTime.HasValue) return ""; TimeSpan remaining = endTime.Value - DateTime.Now; string timeStr; if (remaining.TotalMinutes >= 60) { timeStr = $"{(int)remaining.TotalHours}h {remaining.Minutes}m"; } else if (remaining.TotalSeconds >= 60) { timeStr = $"{remaining.Minutes}m {remaining.Seconds}s"; } else { timeStr = $"{remaining.Seconds}s"; } return string.IsNullOrEmpty(timerText) ? timeStr : $"{timerText}: {timeStr}"; } string HexToRustColor(string hex) { if (string.IsNullOrEmpty(hex)) return "1 1 1 1"; try { hex = hex.TrimStart('#'); if (hex.Length == 8) { byte a = Convert.ToByte(hex.Substring(0, 2), 16); byte r = Convert.ToByte(hex.Substring(2, 2), 16); byte g = Convert.ToByte(hex.Substring(4, 2), 16); byte b = Convert.ToByte(hex.Substring(6, 2), 16); return $"{r/255f} {g/255f} {b/255f} {a/255f}"; } else if (hex.Length == 6) { byte r = Convert.ToByte(hex.Substring(0, 2), 16); byte g = Convert.ToByte(hex.Substring(2, 2), 16); byte b = Convert.ToByte(hex.Substring(4, 2), 16); return $"{r/255f} {g/255f} {b/255f} 1"; } } catch { return "1 1 1 1"; } return "1 1 1 1"; } } }