runs on both windows and linux..
edit UpdateNotice.json in your config folder after compiling
windows "SteamCMD Path": "c:\\stemcmd\\steamcmd.exe"
linux "SteamCMD Path": "/home/steam/steamcmd/steamcmd.sh"
Make sure steamcmd.sh has execute permissions
Reload the plugin: oxide.reload UpdateNotice
type in the server console updatenotice steamtest to check that its working
UpdateNotice.cs
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using UnityEngine;
using static ConsoleSystem;
namespace Oxide.Plugins
{
[Info("Update Notice", "FiREST0N3D", "4.1.17")]
[Description("Rust update notifier with SteamCMD + Local Manifest check. Windows + Linux support.")]
internal sealed class UpdateNotice : RustPlugin
{
private Configuration _configuration;
private UpdateInfo _updateInfo;
private const string AdminPermission = "updatenotice.admin";
private bool _initLoad = true;
private int _checkingInterval = 900; // 15 minutes
private Timer _restartTimer;
private sealed class UpdateInfo
{
public string rustServer { get; set; }
}
private sealed class DiscordMessageEmbeds
{
public string content { get; set; }
}
private sealed class Configuration
{
[JsonProperty("Only Notify Admin")]
public bool OnlyNotifyAdmins { get; set; } = false;
[JsonProperty("Enable Discord Notifications")]
public bool EnableDiscordNotify { get; set; } = false;
[JsonProperty("Discord role id to mention (0 = no mention)")]
public ulong DiscordRoleId { get; set; } = 0;
[JsonProperty("Discord Webhook URL")]
public string DiscordWebhookURL { get; set; } = "";
[JsonProperty("Enable GUI Notifications (Needs GUIAnnouncements)")]
public bool EnableGuiNotifications { get; set; } = true;
[JsonProperty("Enable Chat Notifications")]
public bool EnableChatNotifications { get; set; } = true;
[JsonProperty("GUI Notifications Tint Color")]
public string GUINotificationsTintColor { get; set; } = "Purple";
[JsonProperty("GUI Notifications Text Color")]
public string GUINotificationsTextColor { get; set; } = "Yellow";
[JsonProperty("Enable Auto Restart on Update")]
public bool EnableAutoRestart { get; set; } = true;
[JsonProperty("Restart Countdown Minutes")]
public int RestartCountdownMinutes { get; set; } = 5;
[JsonProperty("SteamCMD Path")]
public string SteamCmdPath { get; set; } = ""; // Leave empty - auto detected
}
protected override void SaveConfig() => Config.WriteObject(_configuration);
private void LoadNewConfig() => _configuration = Config.ReadObject<Configuration>();
protected override void LoadDefaultConfig() => _configuration = new Configuration();
protected override void LoadConfig()
{
base.LoadConfig();
_configuration = Config.ReadObject<Configuration>();
}
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["ServerUpdated"] = "Server Update Detected! Restarting in {0} minutes.",
["RestartWarning"] = "Server will restart in {0} minute(s) for update.",
["RestartingNow"] = "Restarting server now...",
["TestUpdateTriggered"] = "Test: Simulating server update...",
["Chat.Prefix"] = "<size=20><color=#ff0000>Update Notice</color></size>",
}, this);
}
private void Init() => permission.RegisterPermission(AdminPermission, this);
private void Loaded()
{
if (GUIAnnouncements == null)
{
PrintWarning("GUIAnnouncements not found.");
_configuration.EnableGuiNotifications = false;
}
// Auto-detect SteamCMD path if not set
if (string.IsNullOrEmpty(_configuration.SteamCmdPath))
{
_configuration.SteamCmdPath = IsLinux() ? "/home/steam/steamcmd/steamcmd.sh" : @"C:\rustserver\steamcmd.exe";
}
timer.Every(_checkingInterval, CompareVersions);
Puts($"Update Notice 4.1.17 loaded - Checking every 15 minutes (SteamCMD: {_configuration.SteamCmdPath})");
}
private bool IsLinux() => Environment.OSVersion.Platform == PlatformID.Unix;
private string GetLocalBuildId()
{
try
{
string serverDir = Directory.GetCurrentDirectory();
string manifestPath = Path.Combine(serverDir, "steamapps", "appmanifest_258550.acf");
if (!File.Exists(manifestPath))
{
Puts("[Local] Manifest not found");
return null;
}
string content = File.ReadAllText(manifestPath);
var match = Regex.Match(content, @"""buildid""\s+""(\d+)""", RegexOptions.IgnoreCase);
if (match.Success)
{
string id = match.Groups[1].Value;
Puts($"[Local] Installed Build ID: {id}");
return id;
}
}
catch (Exception ex)
{
PrintWarning($"[Local] Error: {ex.Message}");
}
return null;
}
private string GetLatestBuildId()
{
Puts("[SteamCMD] Checking latest version...");
if (string.IsNullOrEmpty(_configuration.SteamCmdPath) || !File.Exists(_configuration.SteamCmdPath))
{
PrintError($"SteamCMD not found at: {_configuration.SteamCmdPath}");
return null;
}
string args = IsLinux()
? "+login anonymous +app_info_update 1 +app_info_print 258550 +quit"
: "+login anonymous +app_info_update 1 +app_info_print 258550 +quit";
try
{
var psi = new ProcessStartInfo
{
FileName = _configuration.SteamCmdPath,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var p = Process.Start(psi))
{
string output = p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd();
p.WaitForExit(30000);
var match = Regex.Match(output, @"(?s)""public""\s*\{.*?""buildid""\s*""?(\d+)""?", RegexOptions.IgnoreCase);
if (!match.Success)
match = Regex.Match(output, @"(?s)""gid""\s*""?(\d+)""?", RegexOptions.IgnoreCase);
if (match.Success)
{
string id = match.Groups[1].Value;
Puts($"[SteamCMD] SUCCESS -> Build ID: {id}");
return id;
}
}
}
catch (Exception ex)
{
PrintError($"SteamCMD Error: {ex.Message}");
}
return null;
}
private void CompareVersions()
{
if (_initLoad)
{
_updateInfo = new UpdateInfo();
_initLoad = false;
}
string local = GetLocalBuildId();
string remote = GetLatestBuildId();
Puts($"[Debug] Local: {local ?? "Unknown"} | Remote: {remote ?? "Failed"}");
if (!string.IsNullOrEmpty(remote) && remote != local && remote != _updateInfo.rustServer)
{
_updateInfo.rustServer = remote;
Puts("=== SERVER UPDATE DETECTED ===");
HandleServerUpdate();
}
}
private void HandleServerUpdate()
{
Interface.CallHook("OnServerUpdate", _updateInfo.rustServer);
StartRestartCountdown();
}
private void StartRestartCountdown()
{
if (_restartTimer != null) _restartTimer.Destroy();
int minutes = _configuration.RestartCountdownMinutes;
BroadcastNotification(string.Format(Lang("ServerUpdated"), minutes));
_restartTimer = timer.Every(60f, () =>
{
minutes--;
if (minutes > 0)
BroadcastNotification(string.Format(Lang("RestartWarning"), minutes));
else
{
BroadcastNotification(Lang("RestartingNow"));
RestartServer();
_restartTimer.Destroy();
}
});
}
private void RestartServer() => ConsoleSystem.Run(ConsoleSystem.Option.Server, "quit");
private void BroadcastNotification(string message)
{
Puts(message);
if (_configuration.EnableChatNotifications)
rust.BroadcastChat(null, $"{Lang("Chat.Prefix")}\n{message}");
if (_configuration.EnableGuiNotifications && GUIAnnouncements != null)
SendtoGui(message);
if (_configuration.EnableDiscordNotify)
SendToDiscord(message);
}
private string Lang(string key) => lang.GetMessage(key, this);
private void SendtoGui(string message)
{
foreach (var p in BasePlayer.activePlayerList)
{
if (_configuration.OnlyNotifyAdmins && !permission.UserHasPermission(p.userID.ToString(), AdminPermission)) continue;
GUIAnnouncements.Call("CreateAnnouncement", message, _configuration.GUINotificationsTintColor, _configuration.GUINotificationsTextColor, p);
}
}
private void SendToDiscord(string message)
{
if (string.IsNullOrEmpty(_configuration.DiscordWebhookURL)) return;
var payload = new DiscordMessageEmbeds { content = message };
webrequest.Enqueue(_configuration.DiscordWebhookURL, JsonConvert.SerializeObject(payload), null, this, RequestMethod.POST);
}
private void TestUpdate()
{
Puts(Lang("TestUpdateTriggered"));
if (_updateInfo == null) _updateInfo = new UpdateInfo();
_updateInfo.rustServer = "TEST_" + DateTime.Now.Ticks;
HandleServerUpdate();
}
[ConsoleCommand("updatenotice")]
private void AdvertCommands(Arg arg)
{
if (arg.IsClientside && !permission.UserHasPermission(arg.Player()?.userID.ToString() ?? "", AdminPermission))
{
SendReply(arg, "No permission");
return;
}
if (arg.Args == null || arg.Args.Length == 0)
{
SendReply(arg, "Commands: forcecheck | steamtest | test | current | loadconfig");
return;
}
string cmd = arg.Args[0].ToString().ToLowerInvariant();
switch (cmd)
{
case "forcecheck":
case "steamtest":
CompareVersions();
break;
case "test":
TestUpdate();
break;
case "current":
SendReply(arg, $"Current stored build: {_updateInfo?.rustServer ?? "None"}");
break;
case "loadconfig":
LoadNewConfig();
Puts("Config reloaded");
break;
}
}
[PluginReference] Plugin GUIAnnouncements;
}
}should work basically the same :)