C#:
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
namespace Oxide.Plugins
{
[Info("PlayersProtected", "OxideBro / RustPlugin.ru", "1.2.11")]
class PlayersProtected : RustPlugin
{
static PlayersProtected ins;
[PluginReference] Plugin ImageLibrary;
public bool AddImage(string url, string shortname, ulong playerid = 2827264) => (bool)ImageLibrary?.Call("AddImage", url, shortname, playerid);
public string GetImages(string shortname, ulong skin = 0) => (string)ImageLibrary?.Call("GetImage", shortname, skin);
public bool IsReady() => (bool)ImageLibrary?.Call("IsReady");
private PluginConfig config;
protected override void LoadDefaultConfig()
{
PrintWarning("Благодарим за приобретение плагина на сайте RustPlugin.ru. Если вы передадите этот плагин сторонним лицам знайте - это лишает вас гарантированных обновлений!");
config = PluginConfig.DefaultConfig();
}
protected override void LoadConfig()
{
base.LoadConfig();
config = Config.ReadObject<PluginConfig>();
if (config.PluginVersion < Version) UpdateConfigValues();
Config.WriteObject(config, true);
}
protected override void SaveConfig()
{
Config.WriteObject(config);
}
private class PluginConfig
{
[JsonProperty("Включить отображение UI защиты у игрока | Enabled UI")] public bool UIEnabled = true;
[JsonProperty("Привилегия на использование защиты | Protected permission ")] public string Permission = "PlayersProtected.use";
[JsonProperty("Отключить защиту на постройки игрока (Если он сам атакующий) | Disable player building protection (If he is the initiator)")] public bool OwnerEnabled = true;
[JsonProperty("Задержка отправки сообщений об защите в чат (в секундах) | Delay in sending chat protection messages in seconds")] public int CooldownTimer = 15;
[JsonProperty("Время защиты новых игроков или же после вайпа (в секундах) | Time to protect new players or after wipe (in seconds)")] public double TimeToProtection = 7200.0;
[JsonProperty("Включить защиту построек у игрока | Enable player building protection")] public bool EnabledProtectionBuild = true;
[JsonProperty("Включить защиту игрока | Enable player protection")] public bool EnabledProtectionPlayer = true;
[JsonProperty("Включить автоматический вайп данных при вайпе карты | Enable automatic data wipe with wipe map")] public bool AutoWipe = true;
[JsonProperty("Ссылка на изображение UI | Image URL")] public string ImageURL = "https://i.imgur.com/b7PMaQ5.png";
[JsonProperty("Версия конфигурации | Configuration version")] public VersionNumber PluginVersion = new VersionNumber();
public static PluginConfig DefaultConfig()
{
return new PluginConfig()
{
CooldownTimer = 15,
OwnerEnabled = true,
TimeToProtection = 7200,
UIEnabled = true,
AutoWipe = true,
EnabledProtectionBuild = true,
EnabledProtectionPlayer = true,
ImageURL = "https://i.imgur.com/b7PMaQ5.png",
PluginVersion = new VersionNumber(),
}
;
}
}
private void UpdateConfigValues()
{
PrintWarning("Config update detected! Updating config values...");
PluginConfig baseConfig = PluginConfig.DefaultConfig();
if (config.PluginVersion < new VersionNumber(1, 1, 0)) config.Permission = "PlayersProtected.use";
config.PluginVersion = Version;
PrintWarning("Config update completed!");
}
class DataStorage
{
public Dictionary<ulong, PlayerProtected> PlayersDatabase = new Dictionary<ulong, PlayerProtected>();
public DataStorage() { }
}
private List<BasePlayer> Players = new List<BasePlayer>() { }
;
DataStorage data;
public class PlayerProtected
{
[JsonProperty("Время защиты")] public double EndProtected;
}
void OnServerInitialized()
{
ins = this;
if (!plugins.Exists("ImageLibrary"))
{
PrintError("ImageLibrary can not be found! Can not continue");
Interface.Oxide.UnloadPlugin(Title);
return;
}
LoadData();
lang.RegisterMessages(Messages, this, "en");
Messages = lang.GetMessages("en", this);
permission.RegisterPermission(config.Permission, this);
AddImage(config.ImageURL, config.ImageURL, 0);
BasePlayer.activePlayerList.ToList().ForEach(OnPlayerInit);
timer.Repeat(600, 0, () => SaveData());
}
void OnUserPermissionGranted(string id, string permName)
{
if (permName == config.Permission.ToLower())
{
var player = BasePlayer.FindByID(ulong.Parse(id));
if (player != null) OnPlayerInit(player);
}
}
private bool GetConfig<T>(string MainMenu, string Key, ref T var)
{
if (Config[MainMenu, Key] != null)
{
var = (T)Convert.ChangeType(Config[MainMenu, Key], typeof(T));
return false;
}
Config[MainMenu, Key] = var;
return true;
}
void OnPlayerInit(BasePlayer player)
{
if (player.HasPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot))
{
timer.Once(2, () => OnPlayerInit(player));
return;
}
if (permission.UserHasPermission(player.UserIDString, config.Permission))
{
if (!data.PlayersDatabase.ContainsKey(player.userID)) data.PlayersDatabase.Add(player.userID, new PlayerProtected()
{
EndProtected = GrabCurrentTime() + config.TimeToProtection
}
);
if (config.UIEnabled)
{
var endtimer = data.PlayersDatabase[player.userID].EndProtected;
double time = GrabCurrentTime();
if (endtimer > time)
{
var Component = player.GetComponent<PlayerMono>();
if (Component == null) player.gameObject.AddComponent<PlayerMono>();
}
}
}
}
void OnNewSave()
{
PrintWarning("Обнаружен вайп, очищаем данныe об игроках");
LoadData();
data = new DataStorage();
SaveData();
Interface.Oxide.ReloadPlugin(Title);
}
void LoadData()
{
try
{
data = Interface.GetMod().DataFileSystem.ReadObject<DataStorage>("PlayersProtected");
}
catch
{
data = new DataStorage();
}
}
void Unload()
{
SaveData();
foreach (var player in BasePlayer.activePlayerList)
{
PlayerMono component = player.GetComponent<PlayerMono>();
if (component != null) component.Disconnect();
}
BasePlayer.activePlayerList.ToList().ForEach(player => CuiHelper.DestroyUi(player, "UIPlayer"));
}
void SaveData() => Interface.Oxide.DataFileSystem.WriteObject("PlayersProtected", data);
private double GrabCurrentTime() => DateTime.UtcNow.ToLocalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
private Dictionary<BasePlayer, DateTime> Cooldown = new Dictionary<BasePlayer, DateTime>();
[PluginReference] Plugin Duel;
bool InDuel(BasePlayer player) => Duel?.Call<bool>("IsPlayerOnActiveDuel", player) ?? false;
void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
{
if (entity == null || info == null || entity?.net?.ID == null || info.Initiator == null || entity as BaseCorpse) return;
BasePlayer attacker = info?.Initiator as BasePlayer;
if (attacker == null || IsNPC(attacker) || InDuel(attacker)) return;
if (attacker.GetComponent<BaseAnimalNPC>() != null || entity.GetComponent<BaseAnimalNPC>() != null) return;
if (entity.GetComponent<LootContainer>() != null) return;
double time = GrabCurrentTime();
if (permission.UserHasPermission(attacker.UserIDString, config.Permission) && data.PlayersDatabase.ContainsKey(attacker.userID))
{
if (IsNPC(entity as BasePlayer)) return;
double AttacketTime = data.PlayersDatabase[attacker.userID].EndProtected;
if (AttacketTime > time)
{
info.damageTypes.ScaleAll(0.0f);
if (!Cooldown.ContainsKey(attacker) || Cooldown[attacker].Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds < time)
{
SendReply(attacker, Messages["ProtectedOn"], FormatTime(AttacketTime - time));
Cooldown[attacker] = Cooldown[attacker] = DateTime.UtcNow.ToLocalTime().AddSeconds(config.CooldownTimer);
return;
}
}
}
if (config.EnabledProtectionBuild && entity is BuildingBlock || entity is SimpleBuildingBlock || entity is Door || entity is DecayEntity)
{
if (config.OwnerEnabled) if (attacker.userID == entity.OwnerID) return;
if (entity.OwnerID == 0) return;
var block = entity as BuildingBlock;
if (block != null) if (block.grade <= 0) return;
if (permission.UserHasPermission(entity.OwnerID.ToString(), config.Permission) && data.PlayersDatabase.ContainsKey(entity.OwnerID))
{
double OwnerTime = data.PlayersDatabase.ContainsKey(entity.OwnerID) ? data.PlayersDatabase[entity.OwnerID].EndProtected : 0;
if (OwnerTime > time)
{
var reply = 2761;
if (reply == 0) { }
info.damageTypes.ScaleAll(0.0f);
if (!Cooldown.ContainsKey(attacker) || Cooldown[attacker].Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds < time)
{
SendReply(attacker, Messages["OwnerProtected"]);
Cooldown[attacker] = Cooldown[attacker] = DateTime.UtcNow.ToLocalTime().AddSeconds(config.CooldownTimer);
return;
}
}
}
}
BasePlayer victim = entity as BasePlayer;
if (config.EnabledProtectionPlayer && victim != null && !IsNPC(victim))
{
if (InDuel(victim)) return;
if (attacker == victim) return;
if (permission.UserHasPermission(victim.UserIDString, config.Permission) && data.PlayersDatabase.ContainsKey(victim.userID))
{
double VictimTime = data.PlayersDatabase[victim.userID].EndProtected;
if (VictimTime > time)
{
info.damageTypes.ScaleAll(0.0f);
if (!Cooldown.ContainsKey(attacker) || Cooldown[attacker].Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds < time)
{
SendReply(attacker, Messages["ProtectedOnPlayer"]);
Cooldown[attacker] = Cooldown[attacker] = DateTime.UtcNow.ToLocalTime().AddSeconds(config.CooldownTimer);
return;
}
}
}
}
}
private string FormatTime(double time)
{
TimeSpan dateDifference = TimeSpan.FromSeconds(time);
var days = dateDifference.Days;
var hours = dateDifference.Hours;
var mins = dateDifference.Minutes;
var secs = dateDifference.Seconds;
if (days > 0) return string.Format("{0:00}:{1:00}:{2:00}:{3:00}", days, hours, mins, secs);
if (hours > 0) return string.Format("{0:00}:{1:00}:{2:00}", hours, mins, secs);
else return string.Format("{0:00}:{1:00}", mins, secs);
}
private bool IsNPC(BasePlayer player)
{
if (player == null) return false;
if (player is NPCPlayer || player.GetComponent<NPCPlayer>() != null) return true;
if (!(player.userID >= 76560000000000000L || player.userID <= 0L) || player.userID.ToString().Length < 17) return true;
return false;
}
[ChatCommand("protected")]
void cmdProtected(BasePlayer player, string cmd, string[] args)
{
double time = GrabCurrentTime();
if (args == null || args.Length <= 0)
{
var messages = data.PlayersDatabase[player.userID].EndProtected > time ? Messages["ProtectedAlready"].Replace("{0}", FormatTime(data.PlayersDatabase[player.userID].EndProtected - time)) : Messages["ProtectedAlreadyOff"];
if (player.IsAdmin) messages = $"{messages}\n\n{Messages["CmdHelpAdmin"]}";
SendReply(player, Messages["CmdHelp"], messages);
return;
}
switch (args[0])
{
case "off":
data.PlayersDatabase[player.userID].EndProtected = time;
SendReply(player, Messages["CmdProtectedOff"]);
break;
case "on":
if (!player.IsAdmin) return;
data.PlayersDatabase[player.userID].EndProtected = time + Convert.ToDouble(args[1]);
OnPlayerInit(player);
break;
case "wipe":
if (!player.IsAdmin) return;
OnNewSave();
SendReply(player, "PlayersProtected Wiped!");
Interface.Oxide.ReloadPlugin("PlayersProtected");
break;
}
}
private class PlayerMono : FacepunchBehaviour
{
public BasePlayer player;
List<BasePlayer> PlayersListed = new List<BasePlayer>();
void Awake()
{
player = GetComponent<BasePlayer>();
InvokeRepeating(Repeater, 1f, 1f);
CreateUI(player);
}
void Repeater()
{
if (player == null || !player.IsConnected)
{
Destroy(this);
return;
}
if (ins.data.PlayersDatabase.ContainsKey(player.userID))
{
var endtimer = ins.data.PlayersDatabase[player.userID].EndProtected;
double time = ins.GrabCurrentTime();
if (endtimer > time)
{
if (!PlayersListed.Contains(player)) PlayersListed.Add(player);
UpdateTimer(player);
}
else
{
if (PlayersListed.Contains(player)) CuiHelper.DestroyUi(player, "UIPlayer");
Disconnect();
}
}
else
{
Disconnect();
}
}
public void CreateUI(BasePlayer player)
{
CuiHelper.DestroyUi(player, "UIPlayer");
if (!ins.IsReady())
{
ins.timer.In(1f, () => CreateUI(player));
return;
}
CuiElementContainer container = new CuiElementContainer();
container.Add(new CuiPanel
{
Image = {
Color="1 1 1 0", Material="assets/content/ui/uibackgroundblur-ingamemenu.mat"
}
,
RectTransform = {
AnchorMin="0.001 0", AnchorMax="0.001 0", OffsetMin=$"0.1 610", OffsetMax=$"60 670"
}
,
}
, "Hud", "UIPlayer");
container.Add(new CuiElement
{
Parent = "UIPlayer",
Components = {
new CuiRawImageComponent {
Png=ins.GetImages(ins.config.ImageURL), Color="1 1 1 0.5"
}
, new CuiRectTransformComponent {
AnchorMin="0.1 0.15", AnchorMax="0.9 0.95"
}
,
}
,
}
);
CuiHelper.AddUi(player, container);
UpdateTimer(player);
}
public void UpdateTimer(BasePlayer player)
{
CuiHelper.DestroyUi(player, "UIPlayer_timer");
CuiElementContainer Container = new CuiElementContainer();
double time = ins.GrabCurrentTime();
double currentime = ins.data.PlayersDatabase[player.userID].EndProtected;
Container.Add(new CuiElement
{
Name = "UIPlayer_timer",
Parent = "UIPlayer",
Components = {
new CuiTextComponent {
Text=ins.FormatTime(currentime - time).ToString(), Align=TextAnchor.MiddleCenter, FontSize=11, Color="1 1 1 0.5",
}
, new CuiRectTransformComponent {
AnchorMin="0 -0.1", AnchorMax="1 0.2"
}
, new CuiOutlineComponent {
Color="0 0 0 0.3", Distance="1 -1"
}
}
,
}
);
CuiHelper.AddUi(player, Container);
}
void OnDestroy() => Disconnect();
public void Disconnect()
{
if (PlayersListed.Contains(player)) PlayersListed.Remove(player);
Destroy(this);
}
}
Dictionary<string, string> Messages = new Dictionary<string, string>() {
{
"ProtectedOn", "У Вас включена защита после вайпа! Ваши постройки защищены, Вы так же не можете наносить урон по игрокам и по постройкам. Время до окончания <color=#F0AE79>{0}</color>.\nЧто бы отключить защиту введите /protected off"
}
, {
"ProtectedOnPlayer", "У игрока включена защита после вайпа! Вы не можете наносить урон по игроку"
}
, {
"OwnerProtected", "Даже не старайтесь! Игрок и его постройки защищены.\nПриходите немного позже"
}
, {
"ProtectedAlreadyOff", "\tВы больше не состоите под защитой после вайпа."
}
, {
"CmdHelp", "<size=16>Players Protected by RustPlugin.ru</size>\n{0}"
}
, {
"CmdHelpAdmin", "<size=16>Команды администратора:</size>\n\tВы можете как администратор сделать \n\tручной вайп данных, команда: /protected wipe\n\tТак же вы можете включить себе защиту: /protected on \n\ttime (в секундах)"
}
, {
"ProtectedAlready", "\tВы состоите под защитой, до окончения: {0}\n\tЧто бы отключить защиту введите /protected off"
}
, {
"CmdProtectedOff", "Вы успешно отключили защиту от рейда. Теперь вы можете наносить урон по постройкам и по игрокам, вы и Ваши постройки больше не защищены"
}
,
}
;
}
}