C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
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.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;
namespace Oxide.Plugins {
[Info("AutoRestart", "crash", "2.5.1")]
[Description("All features are managed through an intuitive UI")]
public class AutoRestart : RustPlugin {
#region Fields & References
// Plugin References
[PluginReference]
private Plugin Economics;
[PluginReference]
private Plugin ServerRewards;
// Constants
private const string CurrentVersion = "2.5.1";
private const int DISCORD_SUCCESS_CODE = 204;
private const float DISCORD_ONLINE_DELAY = 300f;
private const float RESTART_DELAY = 65f;
private const float MINIMUM_CHECK_INTERVAL = 60f;
private const int MINIMUM_RESTART_INTERVAL = 60;
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";
// Configurations & Data Storage
private ConfigData config;
private List<int> notifiedWarningTimes = new List<int>();
private Dictionary<string, string> _inputFieldValues = new Dictionary<string, string>();
private Dictionary<string, string> _searchInputValues = new Dictionary<string, string>();
private HashSet<ulong> rewardedPlayers = new HashSet<ulong>();
private DateTime rewardStartTime = DateTime.MinValue;
// Timers & Time Tracking
private List<TimeSpan> restartTimes = new List<TimeSpan>();
private List<Timer> activeTimers = new List<Timer>();
private Timer nextRestartTimer;
private Timer rewardLimitTimer;
private DateTime lastRestartTime;
private DateTime lastRestartCheck = DateTime.MinValue;
// State Management
private static bool hasSentDiscordOnlineNotification = false;
private bool hasSentFinalRestartNotification = false;
private bool isCustomRestart = false;
private bool isCancelled = false;
private bool isCustomUICreated = false;
private bool isShuttingDown;
private int customRestartTime = 0;
// UI & Messages
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();
public bool DisableDiscordCountdownMessages {
get;
set;
}
= false;
public RewardsConfig Rewards {
get;
set;
}
= new RewardsConfig();
}
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;
public bool SkipWipeDays {
get;
set;
}
= false;
public List<string> WipeDays {
get;
set;
}
= new List<string>();
}
private class PlayerRestrictionConfig {
public int MaxPlayersBeforeCancel {
get;
set;
}
= 10;
public bool RestrictPlayerCount {
get;
set;
}
= false;
}
public class AlertConfig {
public List<int> InGameWarningTimes {
get;
set;
}
= new List<int>();
public List<int> DiscordWarningTimes {
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;
public bool UseFullMinuteCountdown {
get;
set;
}
= true;
}
private class RewardsConfig {
public bool EnableRewards {
get;
set;
}
= false;
public bool UseEconomics {
get;
set;
}
= false;
public bool UseServerRewards {
get;
set;
}
= false;
public Dictionary<string, int> ItemRewards {
get;
set;
}
= new Dictionary<string, int>();
public double EconomicsAmount {
get;
set;
}
= 0;
public int ServerRewardsPoints {
get;
set;
}
= 0;
public int RewardTimeLimit {
get;
set;
}
= 300;
}
private class RewardsData {
public List<ulong> RewardedPlayers {
get;
set;
}
= new List<ulong>();
public DateTime RewardStartTime {
get;
set;
}
}
#endregion
#region Oxide Hooks
private void Init() {
permission.RegisterPermission(PERMISSION_USE, this);
LoadConfigValues();
UpdateLanguage();
lastRestartTime = DateTime.UtcNow;
isShuttingDown = false;
DestroyAllUI();
ClearAllTimers();
LoadRewardsData();
lastChatMessage = string.Empty;
lastUIMessage = string.Empty;
isCustomUICreated = false;
notifiedWarningTimes.Clear();
hasSentFinalRestartNotification = false;
}
private void OnServerInitialized() {
try {
timer.Once(5f, () => {
LogInfo("Initializing restart schedule...");
if (restartTimes.Count > 0) {
LogInfo($"Configured restart times: {string.Join(", ", restartTimes.Select(t => t.ToString(@"hh\ : mm")))}");
if (nextRestartTimer == null && !isCustomRestart) {
ScheduleNextRestartQuietly();
}
}
else {
LogInfo("No restart times configured.");
}
if (config.Discord.DiscordNotifications && !hasSentDiscordOnlineNotification) {
NotifyDiscordOnline();
}
if (config.Rewards.EnableRewards) {
rewardedPlayers.Clear();
timer.Once(10f, () => StartRewardPeriod());
}
timer.Every(3600f, () => {
if (nextRestartTimer != null) {
var nextRestart = DateTime.UtcNow.AddSeconds(nextRestartTimer.Repetitions);
LogInfo($"Periodic check - Next restart at: {nextRestart} UTC");
}
else if (restartTimes.Count > 0 && !isCustomRestart && !isCancelled) {
LogWarning("Periodic check - No restart scheduled! Attempting to reschedule...");
ScheduleNextRestartQuietly();
}
}
);
}
);
}
catch (Exception ex) {
LogError($"Error in OnServerInitialized: {ex}", ex);
}
}
private void ScheduleNextRestartQuietly() {
if (isCustomRestart || isCancelled) {
LogInfo($"Skipping restart schedule: CustomRestart={isCustomRestart}, Cancelled={isCancelled}");
return;
}
if ((DateTime.UtcNow - lastRestartCheck).TotalSeconds < MINIMUM_CHECK_INTERVAL) {
LogInfo("Skipping restart check - too soon since last check");
return;
}
lastRestartCheck = DateTime.UtcNow;
ClearAllTimers();
if (restartTimes.Count == 0) {
LogWarning("No restart times configured");
return;
}
try {
DateTime now = DateTime.UtcNow;
DateTime nextRestart = FindNextRestartTime(now);
if ((nextRestart - lastRestartTime).TotalSeconds < MINIMUM_RESTART_INTERVAL) {
LogWarning($"Preventing duplicate restart: Last restart at {lastRestartTime}, next scheduled for {nextRestart}");
nextRestart = nextRestart.AddDays(1);
}
TimeSpan timeUntilRestart = nextRestart - now;
if (timeUntilRestart.TotalSeconds <= 0) {
LogWarning("Invalid restart time (in past), scheduling for next day");
nextRestart = nextRestart.AddDays(1);
timeUntilRestart = nextRestart - now;
}
int totalSeconds = (int)timeUntilRestart.TotalSeconds;
LogInfo($"Scheduling next restart:");
LogInfo($"Current time (UTC): {now}");
LogInfo($"Next restart (UTC): {nextRestart}");
LogInfo($"Time until restart: {timeUntilRestart.TotalHours:F2} hours");
if (timeUntilRestart.TotalSeconds > 0 && CanScheduleRestart(nextRestart)) {
nextRestartTimer = timer.Once((float)timeUntilRestart.TotalSeconds, CheckPlayerCountAndRestart);
activeTimers.Add(nextRestartTimer);
if (config.Alerts.InGameWarningTimes != null && config.Alerts.InGameWarningTimes.Count > 0) {
foreach (int warningMinutes in config.Alerts.InGameWarningTimes) {
int warningSeconds = warningMinutes * 60;
if (warningSeconds < totalSeconds) {
Timer warningTimer = timer.Once(totalSeconds - warningSeconds, () => {
if (!isCancelled) {
ShowMessage(warningSeconds.ToString(), false);
if (warningSeconds == 60) {
customRestartTime = 60;
CountdownFromMinute();
}
}
}
);
activeTimers.Add(warningTimer);
}
}
}
if (!config.Alerts.InGameWarningTimes.Contains(1)) {
Timer lastMinuteTimer = timer.Once(totalSeconds - 60, () => {
if (!isCancelled) {
customRestartTime = 60;
CountdownFromMinute();
}
}
);
activeTimers.Add(lastMinuteTimer);
}
}
else {
LogWarning("Invalid restart time calculated, skipping schedule");
timer.Once(300f, ScheduleNextRestartQuietly);
}
}
catch (Exception ex) {
LogError($"Error scheduling next restart: {ex}", ex);
timer.Once(300f, ScheduleNextRestartQuietly);
}
}
private void OnPlayerInit(BasePlayer player) {
if (!string.IsNullOrEmpty(lastUIMessage)) {
ShowMessage(lastUIMessage, false);
}
}
#endregion
#region Configuration Methods
protected override void LoadDefaultConfig() {
config = new ConfigData {
Alerts = new AlertConfig {
InGameWarningTimes = new List<int>(), DiscordWarningTimes = new List<int>()
}
}
;
Puts("Default config loaded without default WarningTimes.");
SaveConfig();
}
private void SaveConfig() {
Puts("Saving configuration to AutoRestart.json");
Config.WriteObject(config, true);
Puts("Configuration written to AutoRestart.json, please reload the plugin.");
}
private void LoadConfigValues() {
if (!Config.Exists()) {
Puts("Config does not exist, creating empty config.");
LoadDefaultConfig();
return;
}
config = Config.ReadObject<ConfigData>();
if (config.Alerts.InGameWarningTimes == null) {
config.Alerts.InGameWarningTimes = new List<int>();
}
if (config.Alerts.DiscordWarningTimes == null) {
config.Alerts.DiscordWarningTimes = new List<int>();
}
if (config.Restart.WipeDays == null) {
config.Restart.WipeDays = new List<string>();
}
else if (config.Restart.WipeDays.Count == 1 && config.Restart.WipeDays[0] == "Friday") {
config.Restart.WipeDays.Clear();
}
if (config.Version != CurrentVersion) {
Puts("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 (restartTime.TotalHours >= 0 && restartTime.TotalHours < 24) {
if (uniqueRestartTimes.Add(restartTime)) {
restartTimes.Add(restartTime);
LogInfo($"Valid restart time added: {timeString}");
}
else {
LogWarning($"Duplicate restart time ignored: {timeString}");
}
}
else {
LogError($"Invalid time range (must be 00:00-23:59): {timeString}");
}
}
else {
LogError($"Invalid time format (use HH:mm): {timeString}");
}
}
if (restartTimes.Count == 0) {
LogWarning("No valid restart times configured!");
}
else {
LogInfo($"Successfully loaded {restartTimes.Count} restart time(s)");
}
}
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);
var currentVersion = messages.ContainsKey("Version") ? messages["Version"] : "0.0.0";
if (currentVersion != CurrentVersion) {
PrintWarning($"Updating language file from version {currentVersion} to {CurrentVersion}");
messages["Version"] = CurrentVersion;
Config.WriteObject(messages, false, langPath);
PrintWarning("Language file version updated while preserving existing translations.");
}
}
else {
messages = GetDefaultMessages();
}
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 <time>[s|m|h] (e.g., 30s, 5m, 1h)", ["InvalidNumber"] = "Invalid time format.Use <number>[s|m|h] (e.g., 30s, 5m, 1h)", ["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", ["RewardEconomicsReceived"] = "You received ${0} from Economics reward!", ["RewardPointsReceived"] = "You received {0} RP from ServerRewards!", ["RewardItemReceived"] = "You received {0}x {1} from Item rewards!", ["RewardTimeLeft"] = "You received rewards for connecting during server restart.Rewards will be available for {0} more.", ["RewardPeriodEnded"] = "The reward period has ended."
}
;
lang.RegisterMessages(new Dictionary<string, string> {
["Version"]
= CurrentVersion, ["ChatRestartAlert"] = "Перезапуск сервера запланирован через {0}.", ["ChatRestartCanceled"] = "Перезапуск сервера был отменен.", ["ChatRestartNow"] = "ПЕРЕЗАПУСК...", ["ChatRestartCanceledTooManyPlayers"] = "Перезапуск отменен из-за большого количества игроков.", ["NoRestartScheduled"] = "В настоящее время перезапуск не запланирован.", ["RustUITitle"] = "ЗАПЛАНИРОВАННЫЙ ПЕРЕЗАПУСК СЕРВЕРА", ["RustUIMessage"] = "Сервер перезапустится через {0}.", ["CustomUITitle"] = "ЗАПЛАНИРОВАННЫЙ ПЕРЕЗАПУСК СЕРВЕРА", ["CustomUIMessage"] = "Сервер перезапустится через {0}.", ["NoPermission"] = "У вас нет прав для использования этой команды.", ["Usage"] = "Использование: /restart <время>[s|m|h] (пример: 30s, 5m, 1h)", ["InvalidNumber"] = "Неверный формат времени.Используйте <число>[s|m|h] (пример: 30s, 5m, 1h)", ["UpdateDetected"] = "Обнаружено обновление uMod.Сервер будет перезапущен для применения обновления.", ["TimeMinute"] = "минута", ["TimeMinutes"] = "минут", ["TimeHour"] = "час", ["TimeHours"] = "часов", ["TimeSecond"] = "секунда", ["TimeSeconds"] = "секунд", ["RewardEconomicsReceived"] = "Вы получили ${0} от Economics!", ["RewardPointsReceived"] = "Вы получили {0} RP от ServerRewards!", ["RewardItemReceived"] = "Вы получили {0}x {1} из награды предметами!", ["RewardTimeLeft"] = "Вы получили награды за подключение во время перезапуска сервера.Награды будут доступны еще {0}.", ["RewardPeriodEnded"] = "Период получения наград закончился."
}
, 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 <час>[s|m|h] (приклад: 30s, 5m, 1h)", ["InvalidNumber"] = "Невірний формат часу.Використовуйте <число>[s|m|h] (приклад: 30s, 5m, 1h)", ["UpdateDetected"] = "Виявлено оновлення uMod.Сервер буде перезапущено для застосування оновлення.", ["TimeMinute"] = "хвилина", ["TimeMinutes"] = "хвилин", ["TimeHour"] = "година", ["TimeHours"] = "годин", ["TimeSecond"] = "секунда", ["TimeSeconds"] = "секунд", ["RewardEconomicsReceived"] = "Ви отримали ${0} від Economics!", ["RewardPointsReceived"] = "Ви отримали {0} RP від ServerRewards!", ["RewardItemReceived"] = "Ви отримали {0}x {1} з нагороди предметами!", ["RewardTimeLeft"] = "Ви отримали нагороди за підключення під час перезапуску сервера.Нагороди будуть доступні ще {0}.", ["RewardPeriodEnded"] = "Період отримання нагород закінчився."
}
, this, "uk");
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 <tiempo>[s|m|h] (ejemplo: 30s, 5m, 1h)", ["InvalidNumber"] = "Formato de tiempo inválido.Usa <número>[s|m|h] (ejemplo: 30s, 5m, 1h)", ["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", ["RewardEconomicsReceived"] = "¡Has recibido ${0} de recompensa Economics!", ["RewardPointsReceived"] = "¡Has recibido {0} RP de ServerRewards!", ["RewardItemReceived"] = "¡Has recibido {0}x {1} de recompensa de objetos!", ["RewardTimeLeft"] = "Has recibido recompensas por conectarte durante el reinicio del servidor.Las recompensas estarán disponibles por {0} más.", ["RewardPeriodEnded"] = "El período de recompensas ha terminado."
}
, 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 <tempo>[s|m|h] (exemplo: 30s, 5m, 1h)", ["InvalidNumber"] = "Formato de tempo inválido.Use <número>[s|m|h] (exemplo: 30s, 5m, 1h)", ["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", ["RewardEconomicsReceived"] = "Você recebeu ${0} de recompensa Economics!", ["RewardPointsReceived"] = "Você recebeu {0} RP de ServerRewards!", ["RewardItemReceived"] = "Você recebeu {0}x {1} de recompensa de itens!", ["RewardTimeLeft"] = "Você recebeu recompensas por se conectar durante o reinício do servidor.As recompensas estarão disponíveis por mais {0}.", ["RewardPeriodEnded"] = "O período de recompensas terminou."
}
, 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 <temps>[s|m|h] (exemple: 30s, 5m, 1h)", ["InvalidNumber"] = "Format de temps invalide.Utilisez <nombre>[s|m|h] (exemple: 30s, 5m, 1h)", ["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", ["RewardEconomicsReceived"] = "Vous avez reçu ${0} de récompense Economics !", ["RewardPointsReceived"] = "Vous avez reçu {0} RP de ServerRewards !", ["RewardItemReceived"] = "Vous avez reçu {0}x {1} de récompense d'objets !", ["RewardTimeLeft"] = "Vous avez reçu des récompenses pour vous être connecté pendant le redémarrage du serveur.Les récompenses seront disponibles pendant encore {0}.", ["RewardPeriodEnded"] = "La période de récompenses est terminée."
}
, 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;
}
if (args == null || args.Length == 0) {
ShowCRestartUI(player);
return;
}
switch (args[0].ToLower()) {
case "rewards" : ShowRewardsUI(player);
break;
case "help" : ShowCommandHelp(player);
break;
default : ShowCRestartUI(player);
break;
}
}
private void ShowCommandHelp(BasePlayer player) {
var sb = new StringBuilder();
sb.AppendLine("Available CRestart Commands:");
sb.AppendLine("/crestart - Opens the main configuration UI");
sb.AppendLine("/crestart rewards - Opens the rewards configuration UI");
sb.AppendLine("/crestart help - Shows this help message");
player.ChatMessage(sb.ToString());
}
[ChatCommand("restart")]
private void CommandRestart(BasePlayer player, string command, string[] args) {
if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) {
player.ChatMessage(GetLang("NoPermission", player.UserIDString));
return;
}
if (args == null || args.Length < 1) {
player.ChatMessage(GetLang("Usage", player.UserIDString));
return;
}
int seconds = ParseTimeWithUnit(args[0]);
if (seconds <= 0) {
player.ChatMessage(GetLang("InvalidNumber", player.UserIDString));
return;
}
LogInfo($"Restart command received: Input='{args[0]}', Converted to {seconds} seconds");
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;
}
bool hasActiveRestart = nextRestartTimer != null || activeTimers.Count > 0 || isCustomRestart;
if (hasActiveRestart) {
CancelRestart();
}
else {
SendReply(player, GetLang("NoRestartScheduled", player.UserIDString));
}
}
[ConsoleCommand("restart")]
private void ConsoleRestart(ConsoleSystem.Arg arg) {
if (!arg.IsServerside && !arg.IsAdmin) {
Puts("You don't have permission to use this command.");
return;
}
if (arg.Args == null || arg.Args.Length < 1) {
Puts("Usage: restart <time>[s|m|h] (e.g., 30s, 5m, 1h)");
return;
}
int seconds = ParseTimeWithUnit(arg.Args[0]);
if (seconds <= 0) {
Puts("Invalid time format.Use <number>[s|m|h] (e.g., 30s, 5m, 1h)");
return;
}
StartCustomRestart(seconds);
Puts($"Initiated a custom restart in {seconds} seconds.");
}
[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):");
if (config.Restart.SkipWipeDays && config.Restart.WipeDays != null && config.Restart.WipeDays.Count > 0) {
sb.AppendLine("Skipped Wipe Days:");
foreach (var day in config.Restart.WipeDays) {
sb.AppendLine($"- {day}");
}
sb.AppendLine();
}
DateTime now = DateTime.UtcNow;
var upcomingRestarts = new List<DateTime>();
foreach (var time in restartTimes) {
var todayTime = now.Date.Add(time);
if (todayTime > now) {
if (!ShouldSkipRestart(todayTime)) {
upcomingRestarts.Add(todayTime);
}
}
}
foreach (var time in restartTimes) {
var tomorrowTime = now.Date.AddDays(1).Add(time);
if (!ShouldSkipRestart(tomorrowTime)) {
upcomingRestarts.Add(tomorrowTime);
}
}
if (upcomingRestarts.Count == 0) {
sb.AppendLine("No upcoming restarts found.");
}
else {
foreach (var restart in upcomingRestarts.OrderBy(x => x).Take(5)) {
var timeUntil = restart - now;
string timeUntilString = FormatTimeUntil(timeUntil);
sb.AppendLine($"- {restart:yyyy-MM-dd HH:mm:ss} (in {timeUntilString})");
}
}
Puts(sb.ToString());
}
private string FormatTimeUntil(TimeSpan timeSpan) {
if (timeSpan.TotalMinutes < 1) {
return "less than a minute";
}
else if (timeSpan.TotalHours < 1) {
return $"{(int)timeSpan.TotalMinutes} minute{(timeSpan.Minutes == 1 ? "" : "s")}";
}
else if (timeSpan.TotalHours < 24) {
int hours = (int)timeSpan.TotalHours;
int minutes = timeSpan.Minutes;
if (minutes == 0) return $"{hours} hour{(hours == 1 ? "" : "s")}";
else return $"{hours} hour{(hours == 1 ? "" : "s")} {minutes} minute{(minutes == 1 ? "" : "s")}";
}
else {
int days = (int)timeSpan.TotalDays;
int hours = timeSpan.Hours;
if (hours == 0) return $"{days} day{(days == 1 ? "" : "s")}";
else return $"{days} day{(days == 1 ? "" : "s")} {hours} hour{(hours == 1 ? "" : "s")}";
}
}
private bool ShouldSkipRestart(DateTime restartTime) {
if (!config.Restart.SkipWipeDays || config.Restart.WipeDays == null || config.Restart.WipeDays.Count == 0) return false;
string dayName = restartTime.DayOfWeek.ToString();
return config.Restart.WipeDays.Contains(dayName, StringComparer.OrdinalIgnoreCase);
}
[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($"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($"Received input for setting: {setting}, with value: {value}");
string key = $"{player.UserIDString}_{setting}";
_inputFieldValues[key] = value;
if (string.IsNullOrEmpty(value)) {
Puts($"Error: No value provided for setting {setting}");
return;
}
switch (setting) {
case "InGameWarningTimes" : try {
var newInGameWarningTimes = value.Split(',').Select(s => int.Parse(s.Trim())).Where(i => i > 0).Distinct().OrderByDescending(x => x).ToList();
config.Alerts.InGameWarningTimes = newInGameWarningTimes;
Puts($"Updated InGameWarningTimes: {string.Join(", ", config.Alerts.InGameWarningTimes)}");
}
catch (FormatException) {
PrintError($"Invalid format for InGameWarningTimes: {value}");
}
break;
case "DiscordWarningTimes" : try {
var newDiscordWarningTimes = value.Split(',').Select(s => int.Parse(s.Trim())).Where(i => i > 0).Distinct().OrderByDescending(x => x).ToList();
config.Alerts.DiscordWarningTimes = newDiscordWarningTimes;
Puts($"Updated DiscordWarningTimes: {string.Join(", ", config.Alerts.DiscordWarningTimes)}");
}
catch (FormatException) {
PrintError($"Invalid format for DiscordWarningTimes: {value}");
}
break;
case "RestartTimes" : try {
var newRestartTimes = value.Split(',').Select(s => s.Trim()).ToList();
config.Restart.RestartTimes = newRestartTimes;
LoadRestartTimes();
Puts($"Updated RestartTimes: {string.Join(", ", config.Restart.RestartTimes)}");
}
catch (Exception ex) {
PrintError($"Error updating RestartTimes: {ex.Message}");
}
break;
case "AlertSound" : config.Restart.AlertSound = value;
Puts($"Updated AlertSound: {value}");
break;
case "MaxPlayersBeforeCancel" : if (int.TryParse(value, out int maxPlayers)) {
config.PlayerRestrictions.MaxPlayersBeforeCancel = maxPlayers;
Puts($"Updated MaxPlayersBeforeCancel: {maxPlayers}");
}
else {
PrintError($"Invalid MaxPlayersBeforeCancel value: {value}");
}
break;
case "CheckInterval" : if (int.TryParse(value, out int interval)) {
config.UpdateCheck.CheckInterval = interval;
Puts($"Updated CheckInterval: {interval}");
}
else {
PrintError($"Invalid CheckInterval value: {value}");
}
break;
case "WebhookUrl" : config.Discord.WebhookUrl = value;
Puts($"Updated WebhookUrl: {value}");
break;
case "ServerName" : config.Discord.ServerName = value;
Puts($"Updated ServerName: {value}");
break;
case "WipeDays" : try {
var newWipeDays = value.Split(',').Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).Select(s => char.ToUpper(s[0]) + s.Substring(1).ToLower()).Where(day => Enum.TryParse<DayOfWeek>(day, out _)).ToList();
if (newWipeDays.Any()) {
config.Restart.WipeDays = newWipeDays;
Puts($"Updated WipeDays: {string.Join(", ", newWipeDays)}");
string inputKey = $"{player.UserIDString}_WipeDays";
_inputFieldValues[inputKey] = string.Join(", ", newWipeDays);
ShowCRestartUI(player);
}
else {
PrintError($"No valid days found in input: {value}");
}
}
catch (Exception ex) {
PrintError($"Error updating WipeDays: {ex.Message}");
}
break;
default : Puts($"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}");
CuiHelper.DestroyUi(player, "InputFieldsPanel");
CuiHelper.DestroyUi(player, "ScrollContainer");
CuiHelper.DestroyUi(player, "ScrollContainer.View");
CuiHelper.DestroyUi(player, "ScrollContainer.View.Content");
ShowCRestartUI(player);
}
private void UpdateConfigValue(string setting, string value) {
switch (setting) {
case "InGameWarningTimes" : try {
var newInGameWarningTimes = value.Split(',').Select(s => int.Parse(s.Trim())).Where(i => i > 0).Distinct().OrderByDescending(x => x).ToList();
config.Alerts.InGameWarningTimes = newInGameWarningTimes;
Puts($"Updated InGameWarningTimes: {string.Join(", ", config.Alerts.InGameWarningTimes)}");
}
catch (FormatException) {
PrintError($"Invalid format for InGameWarningTimes: {value}");
}
break;
case "DiscordWarningTimes" : try {
var newDiscordWarningTimes = value.Split(',').Select(s => int.Parse(s.Trim())).Where(i =>
// ... [Code preview truncated due to length. Full source in attached file] ...