using Newtonsoft.Json; using System.Collections.Generic; using UnityEngine; using System; namespace Oxide.Plugins { [Info("Auto Radio Manager", "RustySats", "1.0.1")] [Description("Automatically manages boomboxes in your Rust server with various configuration options")] public class AutoRadioManager : RustPlugin { #region Configuration private Configuration config; public class Configuration { [JsonProperty("Radio Stream URL")] public string RadioStreamUrl = "https://radio.goodmorningbitcoin.com/listen/goodmorningbitcoin/radio.mp3"; [JsonProperty("Additional Radio Stations (name,url format)", ObjectCreationHandling = ObjectCreationHandling.Replace)] public Dictionary AdditionalRadioStations = new Dictionary { { "Good Morning Bitcoin", "https://radio.goodmorningbitcoin.com/listen/goodmorningbitcoin/radio.mp3" } }; [JsonProperty("Enable for deployed boomboxes")] public bool EnableForDeployed = false; [JsonProperty("Enable for handheld boomboxes")] public bool EnableForHandheld = true; [JsonProperty("Enable for static/monument boomboxes")] public bool EnableForStatic = true; [JsonProperty("Auto-restart interval (minutes)")] public int AutoRestartInterval = 60; [JsonProperty("Enable auto-restart")] public bool EnableAutoRestart = true; [JsonProperty("Turn on radios after server restart")] public bool TurnOnAfterServerRestart = true; [JsonProperty("Debug mode")] public bool DebugMode = false; } protected override void LoadDefaultConfig() => config = new Configuration(); protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) { throw new JsonException(); } SaveConfig(); } catch { PrintWarning($"Configuration file {Name}.json is invalid; using defaults"); LoadDefaultConfig(); } } protected override void SaveConfig() { Config.WriteObject(config, true); Puts($"Configuration saved to {Name}.json"); } #endregion #region Fields private Timer autoRestartTimer; private string originalServerUrlList; #endregion #region Oxide Hooks private void Init() { // Initialize any necessary resources Puts("Auto Radio Manager initialized"); // Store the original server URL list for restoration on unload originalServerUrlList = BoomBox.ServerUrlList; } private void OnServerInitialized() { // Update the server URL list with additional radio stations UpdateServerUrlList(); // Start the auto-restart timer if enabled if (config.EnableAutoRestart) { StartAutoRestartTimer(); } // Turn on all radios after server restart if enabled if (config.TurnOnAfterServerRestart) { // Use a slight delay to ensure all entities are fully loaded timer.Once(5f, () => TurnOnAllRadios()); } } private void OnEntitySpawned(DeployableBoomBox boombox) { if (!config.EnableForDeployed || boombox == null) return; if (config.DebugMode) Puts($"DEBUG: Deployable boombox spawned: {boombox}"); // Use a slight delay to ensure the entity is fully initialized timer.Once(0.5f, () => { if (boombox != null && boombox.BoxController != null) SetBoomboxRadioIP(boombox.BoxController); }); } private void OnEntitySpawned(HeldBoomBox boombox) { if (!config.EnableForHandheld || boombox == null) return; if (config.DebugMode) Puts($"DEBUG: Handheld boombox spawned: {boombox}"); // Use a slight delay to ensure the entity is fully initialized timer.Once(0.5f, () => { if (boombox != null && boombox.BoxController != null) SetBoomboxRadioIP(boombox.BoxController); }); } private void OnEntitySpawned(BoomBox boombox) { if (config.DebugMode) Puts($"DEBUG: BoomBox entity spawned: {boombox}, Type: {boombox?.GetType().Name}"); if (!config.EnableForStatic || boombox == null) return; // Use a slight delay to ensure the entity is fully initialized timer.Once(0.5f, () => { if (boombox != null) { bool isStatic = IsStaticBoombox(boombox); if (config.DebugMode) Puts($"DEBUG: IsStaticBoombox result: {isStatic} for {boombox}"); if (isStatic) SetBoomboxRadioIP(boombox); } }); } void Unload() { // Clean up the timer when the plugin is unloaded if (autoRestartTimer != null) { autoRestartTimer.Destroy(); autoRestartTimer = null; } // Restore the original server URL list BoomBox.ServerUrlList = originalServerUrlList; if (BoomBox.ServerValidStations != null && !string.IsNullOrEmpty(originalServerUrlList)) { BoomBox.ServerValidStations.Clear(); Server.Command($"BoomBox.ServerUrlList \"{originalServerUrlList}\""); } Puts("Auto Radio Manager unloaded"); } #endregion #region Methods private void UpdateServerUrlList() { if (config.AdditionalRadioStations == null || config.AdditionalRadioStations.Count == 0) { Puts("No additional radio stations configured"); return; } List stationEntries = new List(); // Process additional radio stations from config foreach (var station in config.AdditionalRadioStations) { string entry = $"{station.Key},{station.Value}"; stationEntries.Add(entry); } if (stationEntries.Count > 0) { // Clear existing stations if needed if (BoomBox.ServerValidStations != null) { BoomBox.ServerValidStations.Clear(); } // Build the comma-separated list for the server string urlList = string.Join(",", stationEntries); // Apply the list through server command Server.Command($"BoomBox.ServerUrlList \"{urlList}\""); Puts($"Added {stationEntries.Count} additional radio stations to server list"); } } // Fixed method to identify static boomboxes based on name private bool IsStaticBoombox(BoomBox boombox) { if (boombox == null) return false; if (config.DebugMode) { Puts($"DEBUG: Checking if {boombox} is static. Type: {boombox.GetType().Name}"); // Debug the hierarchy if (boombox.GetComponentInParent() != null) Puts($"DEBUG: Has DeployableBoomBox parent"); if (boombox.GetComponentInParent() != null) Puts($"DEBUG: Has HeldBoomBox parent"); // Log entity name if available if (boombox.baseEntity != null) { Puts($"DEBUG: Entity name: {boombox.baseEntity.name}"); Puts($"DEBUG: Owner ID: {boombox.baseEntity.OwnerID}"); } } // Check if the name contains "static" - this is the most reliable way to identify them if (boombox.baseEntity != null && boombox.baseEntity.name != null) { string entityName = boombox.baseEntity.name.ToLower(); if (entityName.Contains("static")) { if (config.DebugMode) Puts($"DEBUG: Identified as static by name: {entityName}"); return true; } } // The original method wasn't reliable, so we'll skip parent component checks // As a fallback, use the owner ID check if (boombox.baseEntity != null && boombox.baseEntity.OwnerID == 0) { // For additional safety, make sure it's not already counted as handheld var handhelds = UnityEngine.Object.FindObjectsOfType(); foreach (var handheld in handhelds) { if (handheld != null && handheld.BoxController == boombox) return false; // It's already identified as handheld } if (config.DebugMode) Puts($"DEBUG: Identified as static by having no owner"); return true; } if (config.DebugMode) Puts($"DEBUG: Not identified as static"); return false; } private void SetBoomboxRadioIP(BoomBox box) { if (box == null) return; try { if (config.DebugMode) Puts($"DEBUG: Setting radio IP for {box}"); // Set the radio IP to the configured stream URL box.CurrentRadioIp = config.RadioStreamUrl; // Notify clients of the change box.baseEntity.ClientRPC(null, "OnRadioIPChanged", box.CurrentRadioIp); // Turn on the radio box.SetFlag(BaseEntity.Flags.On, true); box.baseEntity.SendNetworkUpdate(); if (config.DebugMode) Puts($"DEBUG: Radio configured successfully for {box}"); } catch (Exception ex) { PrintError($"Error setting boombox radio IP: {ex.Message}"); } } private void StartAutoRestartTimer() { // Destroy any existing timer if (autoRestartTimer != null) { autoRestartTimer.Destroy(); autoRestartTimer = null; } // Validate interval (minimum 1 minute) int interval = Math.Max(1, config.AutoRestartInterval); // Create a new timer with the configured interval autoRestartTimer = timer.Every(interval * 60f, RestartAllRadios); Puts($"Auto-restart timer started with interval of {interval} minutes"); } private void TurnOnAllRadios() { Puts("Turning on all radios..."); int deployedCount = 0; int handheldCount = 0; int staticCount = 0; try { // Find all deployed boomboxes if (config.EnableForDeployed) { var deployables = UnityEngine.Object.FindObjectsOfType(); if (config.DebugMode) Puts($"DEBUG: Found {deployables.Length} deployable boomboxes"); foreach (var deployable in deployables) { if (deployable != null && deployable.BoxController != null) { // Skip if this is a static boombox incorrectly identified as deployed if (deployable.BoxController.baseEntity != null && deployable.BoxController.baseEntity.name != null && deployable.BoxController.baseEntity.name.ToLower().Contains("static")) { if (config.DebugMode) Puts($"DEBUG: Skipping static boombox incorrectly identified as deployed: {deployable.BoxController.baseEntity.name}"); continue; } SetBoomboxRadioIP(deployable.BoxController); deployedCount++; } } } // Find all handheld boomboxes if (config.EnableForHandheld) { var handhelds = UnityEngine.Object.FindObjectsOfType(); if (config.DebugMode) Puts($"DEBUG: Found {handhelds.Length} handheld boomboxes"); foreach (var handheld in handhelds) { if (handheld != null && handheld.BoxController != null) { SetBoomboxRadioIP(handheld.BoxController); handheldCount++; } } } // Find all static boomboxes if (config.EnableForStatic) { var allBoomboxes = UnityEngine.Object.FindObjectsOfType(); if (config.DebugMode) Puts($"DEBUG: Found {allBoomboxes.Length} total boomboxes of all types"); // First, find all boomboxes with "static" in their name foreach (var boombox in allBoomboxes) { if (boombox != null && boombox.baseEntity != null && boombox.baseEntity.name != null && boombox.baseEntity.name.ToLower().Contains("static")) { if (config.DebugMode) Puts($"DEBUG: Found static boombox by name: {boombox.baseEntity.name}"); SetBoomboxRadioIP(boombox); staticCount++; } } // If no static boomboxes were found by name, try other methods if (staticCount == 0) { foreach (var boombox in allBoomboxes) { if (boombox != null && boombox.baseEntity != null && boombox.baseEntity.OwnerID == 0) { // Make sure it's not already handled as handheld or deployed bool isHandheld = false; bool isDeployed = false; var handhelds = UnityEngine.Object.FindObjectsOfType(); foreach (var handheld in handhelds) { if (handheld != null && handheld.BoxController == boombox) { isHandheld = true; break; } } if (!isHandheld) { var deployables = UnityEngine.Object.FindObjectsOfType(); foreach (var deployable in deployables) { if (deployable != null && deployable.BoxController == boombox) { isDeployed = true; break; } } } if (!isHandheld && !isDeployed) { if (config.DebugMode) Puts($"DEBUG: Found static boombox by elimination: {boombox}"); SetBoomboxRadioIP(boombox); staticCount++; } } } } } Puts($"Turned on {deployedCount} deployed, {handheldCount} handheld, and {staticCount} static boomboxes"); } catch (Exception ex) { PrintError($"Error in TurnOnAllRadios: {ex.Message}"); } } private void RestartAllRadios() { Puts("Auto-restarting all radios..."); int deployedCount = 0; int handheldCount = 0; int staticCount = 0; try { // First turn off all radios // Deployed boomboxes if (config.EnableForDeployed) { var deployables = UnityEngine.Object.FindObjectsOfType(); foreach (var deployable in deployables) { // Skip if this is a static boombox incorrectly identified as deployed if (deployable != null && deployable.BoxController != null && deployable.BoxController.baseEntity != null && deployable.BoxController.baseEntity.name != null && deployable.BoxController.baseEntity.name.ToLower().Contains("static")) { continue; } if (deployable != null && deployable.BoxController != null) { deployable.BoxController.SetFlag(BaseEntity.Flags.On, false); deployable.BoxController.baseEntity.SendNetworkUpdate(); deployedCount++; } } } // Handheld boomboxes if (config.EnableForHandheld) { var handhelds = UnityEngine.Object.FindObjectsOfType(); foreach (var handheld in handhelds) { if (handheld != null && handheld.BoxController != null) { handheld.BoxController.SetFlag(BaseEntity.Flags.On, false); handheld.BoxController.baseEntity.SendNetworkUpdate(); handheldCount++; } } } // Static boomboxes if (config.EnableForStatic) { var allBoomboxes = UnityEngine.Object.FindObjectsOfType(); foreach (var boombox in allBoomboxes) { if (boombox != null && boombox.baseEntity != null && boombox.baseEntity.name != null && boombox.baseEntity.name.ToLower().Contains("static")) { boombox.SetFlag(BaseEntity.Flags.On, false); boombox.baseEntity.SendNetworkUpdate(); staticCount++; } } } Puts($"Turned off {deployedCount} deployed, {handheldCount} handheld, and {staticCount} static boomboxes"); // Wait a short delay to ensure they're all off timer.Once(2f, () => { Puts("Turning radios back on..."); TurnOnAllRadios(); }); } catch (Exception ex) { PrintError($"Error in RestartAllRadios: {ex.Message}"); } } #endregion } }