using System; using System.Collections.Generic; using Newtonsoft.Json; using Oxide.Core; using Rust; using UnityEngine; namespace Oxide.Plugins; [Info("Raid Alert", "Michael Keulemans", "1.0.0")] [Description("Tells the people on a cupboard when explosives hit their base: in chat now, or on their next login.")] public class RaidAlert : RustPlugin { private ConfigData _config; private Dictionary> _notes; private readonly Dictionary _cool = new Dictionary(); private class Note { public string Grid; public DateTime When; } private class ConfigData { [JsonProperty("Seconds between alerts for the same base")] public float Seconds = 60f; [JsonProperty("Online message")] public string Online = "{attacker} is raiding your base at {grid}!"; [JsonProperty("Offline message")] public string Offline = "While you were away: your base at {grid} was raided {ago}."; } protected override void LoadDefaultConfig() => _config = new ConfigData(); protected override void LoadConfig() { base.LoadConfig(); try { _config = Config.ReadObject() ?? new ConfigData(); } catch (Exception ex) { Debug.LogException(ex); _config = new ConfigData(); } } protected override void SaveConfig() => Config.WriteObject(_config, true); private void Init() { try { _notes = Interface.Oxide.DataFileSystem.ReadObject>>(Name) ?? new Dictionary>(); } catch (Exception ex) { Debug.LogException(ex); _notes = new Dictionary>(); Store(); } } private void OnEntityTakeDamage(DecayEntity entity, HitInfo info) { if (!info.damageTypes.Has(DamageType.Explosion)) return; var attacker = info.InitiatorPlayer; if (attacker == null || attacker.IsNpc) return; var priv = entity.GetBuildingPrivilege(); if (priv == null || priv.IsAuthed(attacker) || Cooling(priv)) return; Alert(priv, attacker); } private void OnEntityKill(BuildingPrivlidge priv) => _cool.Remove(priv.net.ID.Value); private void OnPlayerConnected(BasePlayer player) { var id = (ulong)player.userID; Dictionary bases; if (!_notes.TryGetValue(id, out bases)) return; foreach (var note in bases.Values) player.ChatMessage(_config.Offline.Replace("{grid}", note.Grid).Replace("{ago}", Ago(note.When))); _notes.Remove(id); Store(); } private bool Cooling(BuildingPrivlidge priv) { var id = priv.net.ID.Value; float last; if (_cool.TryGetValue(id, out last) && Time.realtimeSinceStartup - last < _config.Seconds) return true; _cool[id] = Time.realtimeSinceStartup; return false; } private void Alert(BuildingPrivlidge priv, BasePlayer attacker) { var grid = MapHelper.PositionToString(priv.transform.position); var line = _config.Online.Replace("{attacker}", attacker.displayName).Replace("{grid}", grid); var pid = priv.net.ID.Value; var write = false; foreach (var id in priv.authorizedPlayers) { var member = BasePlayer.FindByID(id); if (member != null) member.ChatMessage(line); else { Keep(id, pid, grid); write = true; } } if (write) Store(); } private void Keep(ulong player, ulong priv, string grid) { Dictionary bases; if (!_notes.TryGetValue(player, out bases)) _notes[player] = bases = new Dictionary(); Note note; if (!bases.TryGetValue(priv, out note)) bases[priv] = note = new Note(); note.Grid = grid; note.When = DateTime.UtcNow; } private static string Ago(DateTime when) { var secs = (int)(DateTime.UtcNow - when).TotalSeconds; if (secs < 3600) { var m = secs / 60; if (m < 1) m = 1; return m == 1 ? "1 minute ago" : m + " minutes ago"; } var h = secs / 3600; if (h < 48) return h == 1 ? "1 hour ago" : h + " hours ago"; var d = h / 24; return d == 1 ? "1 day ago" : d + " days ago"; } private void Store() => Interface.Oxide.DataFileSystem.WriteObject(Name, _notes); }