C#:
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("AutoRestart", "crash", "2.1.1")]
[Description("All features are managed through an intuitive UI")]
public class AutoRestart : RustPlugin
{
#region Fields
private const string CurrentVersion = "2.1.1";
private const int DISCORD_SUCCESS_CODE = 204;
private const float DISCORD_ONLINE_DELAY = 300f;
private const float RESTART_DELAY = 65f;
private const string PERMISSION_USE = "autorestart.use";
private const string CHECKBOX_ON_SOUND = "assets/prefabs/locks/keypad/effects/lock.code.unlock.prefab";
private const string CHECKBOX_OFF_SOUND = "assets/prefabs/locks/keypad/effects/lock.code.denied.prefab";
private ConfigData config;
private List<int> notifiedWarningTimes = new List<int>();
private Dictionary<string, string> _inputFieldValues = new Dictionary<string, string>();
private static bool hasSentDiscordOnlineNotification = false;
private List<TimeSpan> restartTimes = new List<TimeSpan>();
private Timer nextRestartTimer;
private bool isCustomRestart = false;
private bool isCancelled = false;
private bool isCustomUICreated = false;
private int customRestartTime = 0;
private List<Timer> activeTimers = new List<Timer>();
private string lastChatMessage = string.Empty;
private string lastUIMessage = string.Empty;
#endregion
#region Configuration
private class ConfigData
{
public string Version { get; set; } = CurrentVersion;
public RestartConfig Restart { get; set; } = new RestartConfig();
public PlayerRestrictionConfig PlayerRestrictions { get; set; } = new PlayerRestrictionConfig();
public AlertConfig Alerts { get; set; } = new AlertConfig();
public UpdateCheckConfig UpdateCheck { get; set; } = new UpdateCheckConfig();
public UIConfig UI { get; set; } = new UIConfig();
public DiscordConfig Discord { get; set; } = new DiscordConfig();
}
private class RestartConfig
{
public List<string> RestartTimes { get; set; } = new List<string>();
public string AlertSound { get; set; } = "assets/prefabs/locks/keypad/effects/lock.code.lock.prefab";
public bool UseSound { get; set; } = true;
public bool UseChatAlerts { get; set; } = true;
}
private class PlayerRestrictionConfig
{
public int MaxPlayersBeforeCancel { get; set; } = 10;
public bool RestrictPlayerCount { get; set; } = false;
}
public class AlertConfig
{
public List<int> WarningTimes { get; set; } = new List<int>();
}
private class UpdateCheckConfig
{
public bool CheckForUpdates { get; set; } = false;
public int CheckInterval { get; set; } = 600;
}
private class UIConfig
{
public bool UseCustomUI { get; set; } = false;
public bool UseRustUI { get; set; } = true;
public CustomUISettings CustomUI { get; set; } = new CustomUISettings();
}
private class CustomUISettings
{
public string AnchorMin { get; set; } = "0 0.5";
public string AnchorMax { get; set; } = "0 0.5";
public string OffsetMin { get; set; } = "10 -45.9695";
public string OffsetMax { get; set; } = "177.325 46.7435";
public string CheckIcon { get; set; } = "✓";
public string CrossIcon { get; set; } = "✗";
public string CheckIconColor { get; set; } = "0.5568627 0.7764706 0.1843137 1";
public string CrossIconColor { get; set; } = "0.7764706 0.5137255 0.4196078 1";
[JsonIgnore]
public string BackgroundColor
{
get => _backgroundColor;
set
{
_backgroundColor = value;
BackgroundColorHex = value.ToHex();
}
}
private string _backgroundColor = "0.3137255 0.5843138 0.772549 0.85";
public string BackgroundColorHex { get; private set; } = "#5094C5";
public int TitleFontSize { get; set; } = 12;
public int MessageFontSize { get; set; } = 10;
}
private class DiscordConfig
{
public string WebhookUrl { get; set; } = "https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks";
public string ServerName { get; set; } = "Rust Server";
public bool DiscordNotifications { get; set; } = false;
}
#endregion
#region Oxide Hooks
private void Init()
{
permission.RegisterPermission(PERMISSION_USE, this);
LoadConfigValues();
UpdateLanguage();
ScheduleNextRestart();
if (config.UpdateCheck.CheckForUpdates)
{
CheckForUpdates();
}
}
private void OnServerInitialized()
{
try
{
NotifyDiscordOnline();
// Esperar un momento para asegurarnos que todo esté inicializado
timer.Once(5f, () =>
{
LogInfo("Initializing restart schedule...");
LogInfo($"Configured restart times: {string.Join(", ", restartTimes.Select(t => t.ToString(@"hh\:mm")))}");
// Limpiar cualquier estado anterior
ClearAllTimers();
isCustomRestart = false;
isCancelled = false;
customRestartTime = 0;
notifiedWarningTimes.Clear();
// Programar el primer reinicio
ScheduleNextRestart();
// Programar una verificación periódica del estado
timer.Every(3600f, () =>
{
if (nextRestartTimer != null)
{
var nextRestart = DateTime.UtcNow.AddSeconds(nextRestartTimer.Repetitions);
LogInfo($"Periodic check - Next restart at: {nextRestart} UTC");
}
else
{
LogWarning("Periodic check - No restart scheduled! Attempting to reschedule...");
ScheduleNextRestart();
}
});
});
}
catch (Exception ex)
{
LogError($"Error in OnServerInitialized: {ex}", ex);
}
}
private void OnPlayerInit(BasePlayer player)
{
if (!string.IsNullOrEmpty(lastUIMessage))
{
ShowMessage(lastUIMessage, false);
}
}
void Unload()
{
ClearAllTimers();
DestroyAllUI();
}
#endregion
#region Configuration Methods
protected override void LoadDefaultConfig()
{
config = new ConfigData
{
Alerts = new AlertConfig
{
WarningTimes = new List<int>()
}
};
Puts("[AutoRestart] Default config loaded without default WarningTimes.");
SaveConfig();
}
private void SaveConfig()
{
Puts("[AutoRestart] Saving configuration to AutoRestart.json");
Config.WriteObject(config, true);
Puts("[AutoRestart] Configuration written to AutoRestart.json, please reload the plugin.");
}
private void LoadConfigValues()
{
if (!Config.Exists())
{
Puts("[AutoRestart] Config does not exist, creating empty config.");
LoadDefaultConfig();
return;
}
config = Config.ReadObject<ConfigData>();
if (config.Alerts.WarningTimes == null)
{
config.Alerts.WarningTimes = new List<int>();
}
if (config.Version != CurrentVersion)
{
Puts("[AutoRestart] Config version mismatch, updating config.");
UpdateConfig();
}
config.UI.CustomUI.BackgroundColor = config.UI.CustomUI.BackgroundColor;
LoadRestartTimes();
}
private void UpdateConfig()
{
PrintWarning("Updating configuration to the latest version.");
config.Version = CurrentVersion;
SaveConfig();
PrintWarning("Configuration updated successfully.");
}
private void LoadRestartTimes()
{
try
{
LogInfo("Loading restart times...");
restartTimes.Clear();
HashSet<TimeSpan> uniqueRestartTimes = new HashSet<TimeSpan>();
foreach (var timeString in config.Restart.RestartTimes)
{
if (TimeSpan.TryParse(timeString, out TimeSpan restartTime))
{
if (uniqueRestartTimes.Add(restartTime))
{
restartTimes.Add(restartTime);
LogInfo($"Restart time added: {timeString}");
}
else
{
LogWarning($"Duplicate restart time ignored: {timeString}");
}
}
else
{
LogError($"Invalid time format: {timeString}");
}
}
LogInfo($"Total restart times loaded: {restartTimes.Count}");
}
catch (Exception ex)
{
LogError("Error loading restart times", ex);
}
}
#endregion
#region Language Methods
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(GetDefaultMessages(), this);
}
private void UpdateLanguage()
{
var langFile = $"{Name}.json";
var langPath = Path.Combine(Interface.Oxide.LangDirectory, "en", langFile);
Dictionary<string, string> messages;
if (File.Exists(langPath))
{
messages = Config.ReadObject<Dictionary<string, string>>(langPath);
}
else
{
messages = new Dictionary<string, string>();
}
var currentVersion = messages.ContainsKey("Version") ? messages["Version"] : "0.0.0";
if (currentVersion != CurrentVersion)
{
UpdateLanguageFile(messages, langPath);
}
lang.RegisterMessages(messages, this);
}
private void UpdateLanguageFile(Dictionary<string, string> messages, string langPath)
{
PrintWarning($"Updating language file from version {messages["Version"]} to {CurrentVersion}");
var defaultMessages = GetDefaultMessages();
bool changed = false;
foreach (var entry in defaultMessages)
{
if (!messages.ContainsKey(entry.Key) || messages[entry.Key] != entry.Value)
{
messages[entry.Key] = entry.Value;
changed = true;
}
}
if (changed)
{
messages["Version"] = CurrentVersion;
Config.WriteObject(messages, false, langPath);
PrintWarning($"Language file updated successfully.New version: {CurrentVersion}");
}
}
private Dictionary<string, string> GetDefaultMessages()
{
var messages = new Dictionary<string, string>
{
["Version"] = CurrentVersion,
["ChatRestartAlert"] = "Server restart scheduled in {0}.",
["ChatRestartCanceled"] = "The server restart has been canceled.",
["ChatRestartNow"] = "RESTARTING...",
["ChatRestartCanceledTooManyPlayers"] = "Restart canceled due to too many players.",
["NoRestartScheduled"] = "No restart is currently scheduled.",
["RustUITitle"] = "SCHEDULED SERVER RESTART",
["RustUIMessage"] = "The server will restart in {0}.",
["CustomUITitle"] = "SCHEDULED SERVER RESTART",
["CustomUIMessage"] = "The server will restart in {0}.",
["NoPermission"] = "You do not have permission to use this command.",
["Usage"] = "Usage: /restart <seconds>",
["InvalidNumber"] = "Invalid number of seconds provided.",
["UpdateDetected"] = "A uMod update has been detected.The server will restart to apply the update.",
["DiscordRestartAlert"] = "🔄 **SERVER RESTART**\nServer will restart in {0}.",
["DiscordRestarting"] = "⚠️ **RESTARTING**\nServer is restarting now.Please wait 5 minutes for the server to be back online.",
["DiscordOnline"] = "✅ **SERVER ONLINE**\nServer is now online and ready to play!",
["TimeMinute"] = "minute",
["TimeMinutes"] = "minutes",
["TimeHour"] = "hour",
["TimeHours"] = "hours",
["TimeSecond"] = "second",
["TimeSeconds"] = "seconds"
};
lang.RegisterMessages(new Dictionary<string, string>
{
["Version"] = CurrentVersion,
["ChatRestartAlert"] = "Перезапуск сервера запланирован через {0}.",
["ChatRestartCanceled"] = "Перезапуск сервера был отменен.",
["ChatRestartNow"] = "ПЕРЕЗАПУСК...",
["ChatRestartCanceledTooManyPlayers"] = "Перезапуск отменен из-за большого количества игроков.",
["NoRestartScheduled"] = "В настоящее время перезапуск не запланирован.",
["RustUITitle"] = "ЗАПЛАНИРОВАННЫЙ ПЕРЕЗАПУСК СЕРВЕРА",
["RustUIMessage"] = "Сервер перезапустится через {0}.",
["CustomUITitle"] = "ЗАПЛАНИРОВАННЫЙ ПЕРЕЗАПУСК СЕРВЕРА",
["CustomUIMessage"] = "Сервер перезапустится через {0}.",
["NoPermission"] = "У вас нет прав для использования этой команды.",
["Usage"] = "Использование: /restart <секунды>",
["InvalidNumber"] = "Указано неверное количество секунд.",
["UpdateDetected"] = "Обнаружено обновление uMod.Сервер будет перезапущен для применения обновления.",
["TimeMinute"] = "минута",
["TimeMinutes"] = "минут",
["TimeHour"] = "час",
["TimeHours"] = "часов",
["TimeSecond"] = "секунда",
["TimeSeconds"] = "секунд"
}, this, "ru");
lang.RegisterMessages(new Dictionary<string, string>
{
["Version"] = CurrentVersion,
["ChatRestartAlert"] = "Перезапуск сервера заплановано через {0}.",
["ChatRestartCanceled"] = "Перезапуск сервера було скасовано.",
["ChatRestartNow"] = "ПЕРЕЗАПУСК...",
["ChatRestartCanceledTooManyPlayers"] = "Перезапуск скасовано через велику кількість гравців.",
["NoRestartScheduled"] = "На даний момент перезапуск не заплановано.",
["RustUITitle"] = "ЗАПЛАНОВАНИЙ ПЕРЕЗАПУСК СЕРВЕРА",
["RustUIMessage"] = "Сервер перезапуститься через {0}.",
["CustomUITitle"] = "ЗАПЛАНОВАНИЙ ПЕРЕЗАПУСК СЕРВЕРА",
["CustomUIMessage"] = "Сервер перезапуститься через {0}.",
["NoPermission"] = "У вас немає прав для використання цієї команди.",
["Usage"] = "Використання: /restart <секунди>",
["InvalidNumber"] = "Вказано невірну кількість секунд.",
["UpdateDetected"] = "Виявлено оновлення uMod.Сервер буде перезапущено для застосування оновлення.",
["TimeMinute"] = "хвилина",
["TimeMinutes"] = "хвилин",
["TimeHour"] = "година",
["TimeHours"] = "годин",
["TimeSecond"] = "секунда",
["TimeSeconds"] = "секунд"
}, this, "uk");
lang.RegisterMessages(new Dictionary<string, string>
{
["Version"] = CurrentVersion,
["ChatRestartAlert"] = "Riavvio del server programmato tra {0}.",
["ChatRestartCanceled"] = "Il riavvio del server è stato annullato.",
["ChatRestartNow"] = "RIAVVIO IN CORSO...",
["ChatRestartCanceledTooManyPlayers"] = "Riavvio annullato a causa di troppi giocatori.",
["NoRestartScheduled"] = "Nessun riavvio programmato al momento.",
["RustUITitle"] = "RIAVVIO SERVER PROGRAMMATO",
["RustUIMessage"] = "Il server verrà riavviato tra {0}.",
["CustomUITitle"] = "RIAVVIO SERVER PROGRAMMATO",
["CustomUIMessage"] = "Il server verrà riavviato tra {0}.",
["NoPermission"] = "Non hai il permesso di utilizzare questo comando.",
["Usage"] = "Utilizzo: /restart <secondi>",
["InvalidNumber"] = "Numero di secondi non valido.",
["UpdateDetected"] = "È stato rilevato un aggiornamento di uMod.Il server verrà riavviato per applicare l'aggiornamento.",
["TimeMinute"] = "minuto",
["TimeMinutes"] = "minuti",
["TimeHour"] = "ora",
["TimeHours"] = "ore",
["TimeSecond"] = "secondo",
["TimeSeconds"] = "secondi"
}, this, "it");
lang.RegisterMessages(new Dictionary<string, string>
{
["Version"] = CurrentVersion,
["ChatRestartAlert"] = "Reinicio del servidor programado en {0}.",
["ChatRestartCanceled"] = "El reinicio del servidor ha sido cancelado.",
["ChatRestartNow"] = "REINICIANDO...",
["ChatRestartCanceledTooManyPlayers"] = "Reinicio cancelado debido a demasiados jugadores.",
["NoRestartScheduled"] = "No hay reinicio programado actualmente.",
["RustUITitle"] = "REINICIO DE SERVIDOR PROGRAMADO",
["RustUIMessage"] = "El servidor se reiniciará en {0}.",
["CustomUITitle"] = "REINICIO DE SERVIDOR PROGRAMADO",
["CustomUIMessage"] = "El servidor se reiniciará en {0}.",
["NoPermission"] = "No tienes permiso para usar este comando.",
["Usage"] = "Uso: /restart <segundos>",
["InvalidNumber"] = "Número de segundos proporcionado no válido.",
["UpdateDetected"] = "Se ha detectado una actualización de uMod.El servidor se reiniciará para aplicar la actualización.",
["TimeMinute"] = "minuto",
["TimeMinutes"] = "minutos",
["TimeHour"] = "hora",
["TimeHours"] = "horas",
["TimeSecond"] = "segundo",
["TimeSeconds"] = "segundos"
}, this, "es-ES");
lang.RegisterMessages(new Dictionary<string, string>
{
["Version"] = CurrentVersion,
["ChatRestartAlert"] = "Reinício do servidor programado em {0}.",
["ChatRestartCanceled"] = "O reinício do servidor foi cancelado.",
["ChatRestartNow"] = "REINICIANDO...",
["ChatRestartCanceledTooManyPlayers"] = "Reinício cancelado devido ao excesso de jogadores.",
["NoRestartScheduled"] = "Nenhum reinício está programado no momento.",
["RustUITitle"] = "REINÍCIO DO SERVIDOR PROGRAMADO",
["RustUIMessage"] = "O servidor será reiniciado em {0}.",
["CustomUITitle"] = "REINÍCIO DO SERVIDOR PROGRAMADO",
["CustomUIMessage"] = "O servidor será reiniciado em {0}.",
["NoPermission"] = "Você não tem permissão para usar este comando.",
["Usage"] = "Uso: /restart <segundos>",
["InvalidNumber"] = "Número de segundos fornecido inválido.",
["UpdateDetected"] = "Uma atualização do uMod foi detectada.O servidor será reiniciado para aplicar a atualização.",
["TimeMinute"] = "minuto",
["TimeMinutes"] = "minutos",
["TimeHour"] = "hora",
["TimeHours"] = "horas",
["TimeSecond"] = "segundo",
["TimeSeconds"] = "segundos"
}, this, "pt-BR");
lang.RegisterMessages(new Dictionary<string, string>
{
["Version"] = CurrentVersion,
["ChatRestartAlert"] = "Redémarrage du serveur prévu dans {0}.",
["ChatRestartCanceled"] = "Le redémarrage du serveur a été annulé.",
["ChatRestartNow"] = "REDÉMARRAGE...",
["ChatRestartCanceledTooManyPlayers"] = "Redémarrage annulé en raison de trop de joueurs.",
["NoRestartScheduled"] = "Aucun redémarrage n'est actuellement prévu.",
["RustUITitle"] = "REDÉMARRAGE DU SERVEUR PROGRAMMÉ",
["RustUIMessage"] = "Le serveur redémarrera dans {0}.",
["CustomUITitle"] = "REDÉMARRAGE DU SERVEUR PROGRAMMÉ",
["CustomUIMessage"] = "Le serveur redémarrera dans {0}.",
["NoPermission"] = "Vous n'avez pas la permission d'utiliser cette commande.",
["Usage"] = "Utilisation: /restart <secondes>",
["InvalidNumber"] = "Nombre de secondes fourni non valide.",
["UpdateDetected"] = "Une mise à jour uMod a été détectée.Le serveur va redémarrer pour appliquer la mise à jour.",
["TimeMinute"] = "minute",
["TimeMinutes"] = "minutes",
["TimeHour"] = "heure",
["TimeHours"] = "heures",
["TimeSecond"] = "seconde",
["TimeSeconds"] = "secondes"
}, this, "fr");
return messages;
}
#endregion
#region Command Handlers
[ChatCommand("crestart")]
private void CmdCRestart(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE))
{
player.ChatMessage(GetLang("NoPermission", player.UserIDString));
return;
}
ShowCRestartUI(player);
}
[ChatCommand("restart")]
private void RestartCommand(BasePlayer player, string command, string[] args)
{
if (!HasPermission(player))
{
player.ChatMessage(GetLang("NoPermission", player.UserIDString));
return;
}
if (args.Length < 1)
{
player.ChatMessage(GetLang("Usage", player.UserIDString));
return;
}
// Simpler number parsing
if (!int.TryParse(args[0], out int seconds) || seconds <= 0)
{
player.ChatMessage(GetLang("InvalidNumber", player.UserIDString));
return;
}
StartCustomRestart(seconds);
}
[ChatCommand("restartstop")]
private void ChatRestartStopCommand(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE))
{
SendReply(player, GetLang("NoPermission", player.UserIDString));
return;
}
if (isCustomRestart || nextRestartTimer != null)
{
CancelRestart();
}
else
{
SendReply(player, GetLang("NoRestartActive", player.UserIDString));
}
}
[ConsoleCommand("restart")]
private void ConsoleRestartCommand(ConsoleSystem.Arg arg)
{
if (arg.IsServerside || arg.IsAdmin)
{
if (arg.Args == null || arg.Args.Length < 1)
{
Puts("Usage: restart <seconds>");
return;
}
if (int.TryParse(arg.Args[0], out int seconds))
{
StartCustomRestart(seconds);
Puts($"Initiated a custom restart in {seconds} seconds.");
}
else
{
Puts("Invalid number of seconds provided.");
}
}
else
{
Puts("You don't have permission to use this command.");
}
}
[ConsoleCommand("restartstop")]
private void ConsoleRestartStopCommand(ConsoleSystem.Arg arg)
{
if (arg.IsServerside || arg.IsAdmin)
{
if (isCustomRestart || nextRestartTimer != null)
{
CancelRestart();
}
else
{
Puts("No restart is currently scheduled.");
}
}
else
{
Puts("You don't have permission to use this command.");
}
}
[ConsoleCommand("restart.list")]
private void CmdListNextRestarts(ConsoleSystem.Arg arg)
{
if (!arg.IsAdmin) return;
var sb = new StringBuilder();
sb.AppendLine("Upcoming Restarts (UTC):");
DateTime now = DateTime.UtcNow;
var upcomingRestarts = new List<DateTime>();
// Obtener reinicios para hoy
foreach (var time in restartTimes)
{
var todayTime = now.Date.Add(time);
if (todayTime > now)
{
upcomingRestarts.Add(todayTime);
}
}
// Obtener reinicios para mañana
foreach (var time in restartTimes)
{
upcomingRestarts.Add(now.Date.AddDays(1).Add(time));
}
// Mostrar los próximos 5 reinicios
foreach (var restart in upcomingRestarts.OrderBy(x => x).Take(5))
{
var timeUntil = restart - now;
sb.AppendLine($"- {restart:yyyy-MM-dd HH:mm:ss} (in {timeUntil.TotalHours:F1} hours)");
}
Puts(sb.ToString());
}
[ConsoleCommand("crestart.toggle")]
private void CmdToggleSetting(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null || !permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) return;
var setting = arg.GetString(0);
bool newValue = !GetConfigValue(setting);
SetConfigValue(setting, newValue);
string soundEffect = newValue ? CHECKBOX_ON_SOUND : CHECKBOX_OFF_SOUND;
Effect.server.Run(soundEffect, player.transform.position);
Puts($"[AutoRestart] Toggled setting: {setting} to {newValue}");
SaveConfig();
ShowCRestartUI(player);
}
[ConsoleCommand("crestart.input")]
private void CmdInputSetting(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null || !permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) return;
if (arg.Args == null || arg.Args.Length < 2)
{
PrintError("Insufficient arguments provided for crestart.input command.");
return;
}
string setting = arg.Args[0];
string value = string.Join(" ", arg.Args.Skip(1).ToArray());
Puts($"[AutoRestart] Received input for setting: {setting}, with value: {value}");
string key = $"{player.UserIDString}_{setting}";
_inputFieldValues[key] = value;
if (string.IsNullOrEmpty(value))
{
Puts($"[AutoRestart] Error: No value provided for setting {setting}");
return;
}
switch (setting)
{
case "WarningTimes":
try
{
var newWarningTimes = value.Split(',').Select(s => int.Parse(s.Trim())).Where(i => i > 0).Distinct().OrderByDescending(x => x).ToList();
config.Alerts.WarningTimes = newWarningTimes;
Puts($"[AutoRestart] Updated WarningTimes: {string.Join(", ", config.Alerts.WarningTimes)}");
}
catch (FormatException)
{
PrintError($"[AutoRestart] Invalid format for WarningTimes: {value}");
}
break;
case "RestartTimes":
try
{
var newRestartTimes = value.Split(',').Select(s => s.Trim()).ToList();
config.Restart.RestartTimes = newRestartTimes;
LoadRestartTimes();
Puts($"[AutoRestart] Updated RestartTimes: {string.Join(", ", config.Restart.RestartTimes)}");
}
catch (Exception ex)
{
PrintError($"[AutoRestart] Error updating RestartTimes: {ex.Message}");
}
break;
case "AlertSound":
config.Restart.AlertSound = value;
Puts($"[AutoRestart] Updated AlertSound: {value}");
break;
case "MaxPlayersBeforeCancel":
if (int.TryParse(value, out int maxPlayers))
{
config.PlayerRestrictions.MaxPlayersBeforeCancel = maxPlayers;
Puts($"[AutoRestart] Updated MaxPlayersBeforeCancel: {maxPlayers}");
}
else
{
PrintError($"[AutoRestart] Invalid MaxPlayersBeforeCancel value: {value}");
}
break;
case "CheckInterval":
if (int.TryParse(value, out int interval))
{
config.UpdateCheck.CheckInterval = interval;
Puts($"[AutoRestart] Updated CheckInterval: {interval}");
}
else
{
PrintError($"[AutoRestart] Invalid CheckInterval value: {value}");
}
break;
case "WebhookUrl":
config.Discord.WebhookUrl = value;
Puts($"[AutoRestart] Updated WebhookUrl: {value}");
break;
case "ServerName":
config.Discord.ServerName = value;
Puts($"[AutoRestart] Updated ServerName: {value}");
break;
default:
Puts($"[AutoRestart] Setting '{setting}' is not recognized.");
return;
}
SaveConfig();
LoadConfigValues();
UpdateCRestartUI(player);
}
[ConsoleCommand("crestart.submit")]
private void CmdSubmitSetting(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null || !permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) return;
var setting = arg.GetString(0);
var value = arg.GetString(1, "");
if (string.IsNullOrEmpty(value))
{
Puts($"Error: No value provided for setting {setting}");
return;
}
UpdateConfigValue(setting, value);
SaveConfig();
Puts($"Config updated: {setting} = {value}");
ShowCRestartUI(player);
}
private void UpdateConfigValue(string setting, string value)
{
switch (setting)
{
case "AlertSound":
config.Restart.AlertSound = value;
break;
case "MaxPlayersBeforeCancel":
if (int.TryParse(value, out int maxPlayers))
config.PlayerRestrictions.MaxPlayersBeforeCancel = maxPlayers;
break;
case "CheckInterval":
if (int.TryParse(value, out int interval))
config.UpdateCheck.CheckInterval = interval;
break;
case "WebhookUrl":
config.Discord.WebhookUrl = value;
break;
case "ServerName":
config.Discord.ServerName = value;
break;
case "WarningTimes":
try
{
config.Alerts.WarningTimes = value.Split(',').Select(s => int.Parse(s.Trim())).Where(i => i > 0).ToList();
}
catch (FormatException)
{
PrintError($"Invalid WarningTimes format: {value}");
}
break;
case "RestartTimes":
try
{
config.Restart.RestartTimes = value.Split(',').Select(s => s.Trim()).ToList();
LoadRestartTimes();
}
catch (Exception ex)
{
PrintError($"Invalid RestartTimes format: {value}.Error: {ex.Message}");
}
break;
}
}
#endregion
#region Restart Handling Methods
private void StartCustomRestart(int seconds)
{
if (isCustomRestart) return;
isCustomRestart = true;
customRestartTime = seconds;
isCancelled = false;
ClearAllTimers();
DestroyAllUI();
// Crear el timer principal para el reinicio
Timer countdownTimer = timer.Once(seconds, CheckPlayerCountAndRestart);
activeTimers.Add(countdownTimer);
// Mostrar mensaje inicial solo si es mayor a 60 segundos
if (seconds > 60)
{
ShowMessage(seconds.ToString(), false);
if (config.Restart.UseSound)
{
PlaySoundToAllPlayers(config.Restart.AlertSound);
}
timer.Once(8f, () =>
{
if (!isCancelled)
{
DestroyAllUI();
isCustomUICreated = false;
}
});
// Configurar los intervalos de aviso
List<int> warningIntervals = new List<int>();
if (config.Alerts.WarningTimes != null && config.Alerts.WarningTimes.Count > 0)
{
warningIntervals = config.Alerts.WarningTimes.Select(minutes => minutes * 60).Where(secs => secs < seconds).OrderByDescending(secs => secs).ToList();
}
else
{
int[] defaultIntervals = { 1800, 900, 600, 300 };
warningIntervals = defaultIntervals.Where(secs => secs < seconds).ToList();
}
foreach (int interval in warningIntervals)
{
Timer warningTimer = timer.Once(seconds - interval, () =>
{
if (!isCancelled)
{
ShowMessage(interval.ToString(), false);
if (config.Restart.UseSound)
{
PlaySoundToAllPlayers(config.Restart.AlertSound);
}
timer.Once(8f, () =>
{
if (!isCancelled)
{
DestroyAllUI();
isCustomUICreated = false;
}
});
}
});
activeTimers.Add(warningTimer);
}
// Programar countdown del último minuto
Timer countdownMinuteTimer = timer.Once(seconds - 60, () =>
{
if (!isCancelled)
{
CountdownFromMinute();
}
});
activeTimers.Add(countdownMinuteTimer);
}
else
{
// Para tiempos menores o iguales a 60 segundos, iniciar el countdown inmediatamente
timer.Once(0.1f, () =>
{
if (!isCancelled)
{
CountdownFromMinute();
}
});
}
if (config.Discord.DiscordNotifications)
{
NotifyDiscord(seconds);
}
LogInfo($"Custom restart scheduled for {seconds} seconds from now.Current time: {DateTime.UtcNow}");
}
private void CancelRestart()
{
if (isCancelled) return;
isCancelled = true;
ClearAllTimers();
notifiedWarningTimes.Clear();
foreach (var player in BasePlayer.activePlayerList)
{
string cancelMessage = GetLang("ChatRestartCanceled", player.UserIDString);
player.ChatMessage(cancelMessage);
if (config.UI.UseCustomUI)
{
ShowCustomUI(player, cancelMessage, true);
}
if (config.UI.UseRustUI)
{
string title = GetLang("RustUITitle", player.UserIDString);
player.SendConsoleCommand("gametip.showgametip",
$"<size=20>{title}</size>\n<size=14>{cancelMessage}</size>");
}
}
if (!isCustomRestart && nextRestartTimer != null)
{
ReScheduleNextRestartForNextDay();
}
else
{
ResetRestartState();
}
timer.Once(5f, () =>
{
DestroyAllUI();
isCustomUICreated = false;
});
}
private void ShowCancelMessageAndDestroyUI(string cancelMessage)
{
foreach (var player in BasePlayer.activePlayerList)
{
string localizedMessage = GetLang("ChatRestartCanceled", player.UserIDString);
if (config.UI.UseCustomUI)
{
ShowCustomUI(player, localizedMessage, true);
}
if (config.UI.UseRustUI)
{
player.SendConsoleCommand("gametip.hidegametip");
player.SendConsoleCommand("gametip.showgametip",
$"<size=20>{GetLang("RustUITitle", player.UserIDString)}</size>\n<size=14>{localizedMessage}</size>");
}
}
timer.Once(5f, () =>
{
DestroyAllUI();
isCustomUICreated = false;
});
}
private void BroadcastCancelMessage(string cancelMessage)
{
foreach (var player in BasePlayer.activePlayerList)
{
string localizedMessage = GetLang("ChatRestartCanceled", player.UserIDString);
player.ChatMessage(localizedMessage);
}
if (config.Discord.DiscordNotifications)
{
SendDiscordNotification(GetLang("ChatRestartCanceled", null));
}
}
private void ResetRestartState()
{
isCustomRestart = false;
customRestartTime = 0;
lastChatMessage = string.Empty;
lastUIMessage = string.Empty;
isCancelled = false;
}
private void ReScheduleNextRestartForNextDay()
{
if (restartTimes.Count == 0)
{
LogWarning("No restart times are scheduled.");
return;
}
try
{
DateTime now = DateTime.UtcNow;
DateTime nextRestart = FindNextRestartTime(now);
TimeSpan timeUntilRestart = nextRestart - now;
LogInfo($"Rescheduling restart:");
LogInfo($"Current time (UTC): {now}");
LogInfo($"Available restart times: {string.Join(", ", restartTimes.Select(t => t.ToString(@"hh\:mm")))}");
LogInfo($"Next restart time (UTC): {nextRestart}");
LogInfo($"Time until restart: {timeUntilRestart.TotalHours:F2} hours");
// Limpiar timers existentes
ClearAllTimers();
// Programar el próximo reinicio
nextRestartTimer = timer.Once((float)timeUntilRestart.TotalSeconds, CheckPlayerCountAndRestart);
activeTimers.Add(nextRestartTimer);
// Programar las alertas
if (config.Alerts.WarningTimes != null && config.Alerts.WarningTimes.Count > 0)
{
ScheduleAlerts(timeUntilRestart);
}
// Resetear estados
isCustomRestart = false;
isCancelled = false;
customRestartTime = 0;
notifiedWarningTimes.Clear();
LogInfo($"Next restart successfully scheduled for {nextRestart} UTC");
}
catch (Exception ex)
{
LogError($"Error scheduling next restart: {ex}", ex);
// Intentar recuperarse del error
timer.Once(300f, ScheduleNextRestart);
}
}
private void ScheduleNextRestart()
{
// No programar el siguiente reinicio si hay uno personalizado en curso
if (isCustomRestart)
{
LogInfo("Next scheduled restart skipped: custom restart is active");
return;
}
if (isCancelled)
{
LogInfo("Next scheduled restart skipped: restart is cancelled");
return;
}
ClearAllTimers();
if (restartTimes.Count == 0)
{
LogWarning("No restart times scheduled");
return;
}
try
{
DateTime now = DateTime.UtcNow;
DateTime nextRestart = FindNextRestartTime(now);
TimeSpan timeUntilRestart = nextRestart - now;
// Ensure we're not scheduling in the past
if (timeUntilRestart.TotalSeconds <= 0)
{
nextRestart = nextRestart.AddDays(1);
timeUntilRestart = nextRestart - now;
}
nextRestartTimer = timer.Once((float)timeUntilRestart.TotalSeconds, CheckPlayerCountAndRestart);
activeTimers.Add(nextRestartTimer);
// Usar los warning times configurados para el reinicio programado
if (config.Alerts.WarningTimes != null && config.Alerts.WarningTimes.Count > 0)
{
ScheduleAlerts(timeUntilRestart);
}
LogInfo($"Next scheduled restart set for: {nextRestart} UTC");
LogInfo($"Time until restart: {timeUntilRestart.TotalHours:F2} hours");
}
catch (Exception ex)
{
LogError($"Error scheduling next restart: {ex}", ex);
}
}
private void LogRestartInfo(DateTime now, DateTime nextRestart)
{
Puts($"Current time: {now} UTC");
Puts($"Available restart times: {string.Join(", ", restartTimes.Select(t => t.ToString(@"hh\:mm")))}");
Puts($"Next restart scheduled for {nextRestart} UTC");
}
private DateTime FindNextRestartTime(DateTime fromTime)
{
if (restartTimes.Count == 0)
{
throw new InvalidOperationException("No restart times are scheduled.");
}
// Obtener todos los tiempos posibles para hoy y mañana
var possibleTimes = new List<DateTime>();
DateTime today = fromTime.Date;
DateTime tomorrow = today.AddDays(1);
// Agregar todos los tiempos de reinicio para hoy
foreach (var time in restartTimes)
{
var todayTime = today.Add(time);
if (todayTime > fromTime)
{
possibleTimes.Add(todayTime);
}
}
// Agregar todos los tiempos de reinicio para mañana
foreach (var time in restartTimes)
{
possibleTimes.Add(tomorrow.Add(time));
}
// Ordenar los tiempos y tomar el más próximo
var nextRestart = possibleTimes.OrderBy(t => t).First();
L
// ... [Code preview truncated due to length. Full source in attached file] ...