using System.Collections.Generic; using System; using UnityEngine; using Newtonsoft.Json; using Oxide.Core; using Oxide.Core.Libraries.Covalence; using System.Text; namespace Oxide.Plugins { [Info("Noob Protection", "Marat", "1.0.1")] [Description("Temporarily protecting the player from players and NPCs")] public class NoobProtection : RustPlugin { #region Field private const string permissionUse = "noobprotection.use"; private readonly int layerMask = LayerMask.GetMask("Terrain", "World", "Default", "Construction", "Deployed"); #endregion #region Oxide Hooks private void OnServerInitialized() { LoadData(); AddCovalenceCommand(config.Commands, nameof(CmdProtectOff)); permission.RegisterPermission(permissionUse, this); for (int i = 0; i < BasePlayer.activePlayerList.Count; i++) { OnPlayerConnected(BasePlayer.activePlayerList[i]); } } private void Unload() { SaveData(); config = null; } private void OnServerSave() => SaveData(); void OnNewSave() { storedData = new StoredData(); SaveData(); PrintWarning("New save file detected. All player data has been deleted!"); } private void OnPlayerConnected(BasePlayer player) { if (player == null || !player.IsConnected) return; if (!permission.UserHasPermission(player.UserIDString, permissionUse)) return; if (!storedData.playerData.ContainsKey(player.userID)) { storedData.playerData.Add(player.userID, new Data { time = CurrentTime() + config.protectionTime, pos = player.transform.position }); } } private object OnPlayerDeath(BasePlayer player, HitInfo hitInfo) { if (player == null || hitInfo == null || !config.respawnLastDeath) return null; if (!permission.UserHasPermission(player.UserIDString, permissionUse)) return null; var pos = player.transform.position; if (!player.IsOnGround() || player.HasParent()) pos.y = GetGroundPosition(pos); if (player.TimeAlive() < config.minLifetime) pos = Vector3.zero; if (!storedData.playerData.TryGetValue(player.userID, out var data)) storedData.playerData.Add(player.userID, new Data { pos = pos }); else data.pos = pos; return null; } private object OnPlayerRespawn(BasePlayer player, BasePlayer.SpawnPoint spawnPoint) { if (player == null || !player.userID.IsSteamId()) return null; if (!permission.UserHasPermission(player.UserIDString, permissionUse)) return null; if (!storedData.playerData.TryGetValue(player.userID, out var data)) return null; if (config.updateTime) data.time = CurrentTime() + config.protectionTime; if (!config.allowRespawn && CurrentTime() >= data.time) return null; if (config.respawnLastDeath && data.pos != Vector3.zero) { spawnPoint.pos = data.pos; return spawnPoint; } return null; } private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo) { if (entity == null|| hitInfo == null || hitInfo.Initiator == null) return null; var attacker = hitInfo.Initiator as BasePlayer; if (attacker == null || attacker.IsNpc) return null; if (entity is BasePlayer) { var player = entity as BasePlayer; if (player == null || player == attacker || player.IsNpc) return null; BasePlayer target; if (storedData.playerData.TryGetValue(player.userID, out var playerData) && CurrentTime() < playerData.time) target = player; else if (storedData.playerData.TryGetValue(attacker.userID, out var attackerData) && CurrentTime() < attackerData.time) target = attacker; else return null; if (!permission.UserHasPermission(target.UserIDString, permissionUse)) return null; hitInfo.damageTypes.ScaleAll(0); var time = TimeSpan.FromSeconds(storedData.playerData[target.userID].time - CurrentTime()); attacker.SendConsoleCommand("gametip.showtoast", 1, $"Protection is enabled. You cannot damage the target for another {FormatTime(time)}"); } else if (config.protectBuilding) { var owner = entity.OwnerID; if (owner == 0 || owner == attacker.userID) return null; ulong target; if (storedData.playerData.TryGetValue(owner, out var playerData) && CurrentTime() < playerData.time) target = owner; else if (storedData.playerData.TryGetValue(attacker.userID, out var attackerData) && CurrentTime() < attackerData.time) target = attacker.userID; else return null; if (!permission.UserHasPermission(target.ToString(), permissionUse)) return null; hitInfo.damageTypes.ScaleAll(0); var time = TimeSpan.FromSeconds(storedData.playerData[target].time - CurrentTime()); attacker.SendConsoleCommand("gametip.showtoast", 1, $"Protection is enabled. You cannot damage the target for another {FormatTime(time)}"); } return null; } private object OnNpcTarget(BaseEntity entity, BasePlayer player) { if (player == null || entity == null || player?.net?.connection == null || player.IsNpc) return null; if (!storedData.playerData.TryGetValue(player.userID, out var data)) return null; if (config.npcIgnoreTarget && CurrentTime() < data.time) return true; return null; } #endregion #region Commands private void CmdProtectOff(IPlayer ipPlayer) { var player = ipPlayer?.Object as BasePlayer; if (player == null) return; if (!permission.UserHasPermission(player.UserIDString, permissionUse)) { PrintToChat(player, "You don't have permission to use this command."); return; } if (!storedData.playerData.TryGetValue(player.userID, out var data)) return; data.time = CurrentTime(); PrintToChat(player, "Your protection has been disabled."); } [ConsoleCommand("resetProtectData")] private void CmdResetData(ConsoleSystem.Arg arg) { if (!arg.IsAdmin) return; for (int i = 0; i < BasePlayer.activePlayerList.Count; i++) { var player = BasePlayer.activePlayerList[i]; if (storedData.playerData.TryGetValue(player.userID, out Data info)) info.time = CurrentTime() + config.protectionTime; } SaveData(); SendReply(arg, "The protection time for all players has been reset."); } #endregion #region Configuration private static PluginConfig config; private class PluginConfig { [JsonProperty("Command to force protection disabling")] public string[] Commands; [JsonProperty("Player protection time (in seconds)")] public float protectionTime; [JsonProperty("Enable protection for buildings owned by the player")] public bool protectBuilding; [JsonProperty("Update protection time after death")] public bool updateTime; [JsonProperty("NPCs ignore player with protection")] public bool npcIgnoreTarget; [JsonProperty("Respawn player at the location of the last death")] public bool respawnLastDeath; [JsonProperty("Allow respawn at last death location after protection ends")] public bool allowRespawn; [JsonProperty("Minimum lifetime for respawning at the location of the last death")] public int minLifetime; } protected override void LoadDefaultConfig() { config = new PluginConfig { Commands = new string[] {"np", "protectoff"}, protectionTime = 3600f, protectBuilding = true, updateTime = false, npcIgnoreTarget = true, respawnLastDeath = true, allowRespawn = false, minLifetime = 60 }; } protected override void SaveConfig() => Config.WriteObject(config); protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) LoadDefaultConfig(); } catch { PrintWarning("The config file contains an error and has been replaced with the default config."); LoadDefaultConfig(); } SaveConfig(); } #endregion #region Methods private static double CurrentTime() => DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds; private static string FormatTime(TimeSpan time) { StringBuilder result = new StringBuilder(); if (time.Days > 0) result.Append($"{time.Days} d"); if (time.Hours > 0) result.AppendFormat("{0}{1} h", result.Length == 0 ? "" : " ", time.Hours); if (time.Minutes > 0) result.AppendFormat("{0}{1} min", result.Length == 0 ? "" : " ", time.Minutes); if (time.Seconds > 0) result.AppendFormat("{0}{1} sec", result.Length == 0 ? "" : " ", time.Seconds); return result.ToString(); } private float GetGroundPosition(Vector3 pos) { float y = TerrainMeta.HeightMap.GetHeight(pos); if (Physics.Raycast(pos, Vector3.down, out RaycastHit hit, 10000, layerMask)) y = Mathf.Max(hit.point.y, y); return y; } #endregion #region Data private StoredData storedData; private class StoredData { public Dictionary playerData = new Dictionary(); } private class Data { public double time; public Vector3 pos; } private void SaveData() { if (storedData != null) { Interface.Oxide.DataFileSystem.WriteObject($"{Name}_Data", storedData, true); } } private void LoadData() { storedData = Interface.Oxide.DataFileSystem.ReadObject($"{Name}_Data"); if (storedData == null) { storedData = new StoredData(); SaveData(); } } #endregion } }