C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
#define DEBUG
/*
TODO:
- Add automatic admin exclusion
- Add optional protection while AFK
- Add chat command to mark yourself as AFK
- Cancel "AFK" status when input detected
- Players can get around kick by sleeping?
*/
using System;
using System.Collections.Generic;
using Oxide.Core.Libraries.Covalence;
namespace Oxide.Plugins
{
[Info("AFK", "Wulf/lukespragg", "1.1.4", ResourceId = 1922)]
[Description("Kicks players that are AFK (away from keyboard) for too long")]
class AFK : CovalencePlugin
{
// Do NOT edit this file, instead edit AFK.json in oxide/config and AFK.en.json in the oxide/lang directory,
// or create a new language file for another language using the 'en' file as a default
#region Initialization
const string permExcluded = "afk.excluded";
void Init()
{
LoadDefaultConfig();
LoadDefaultMessages();
permission.RegisterPermission(permExcluded, this);
}
void OnServerInitialized()
{
foreach (var player in players.Connected) AfkCheck(player);
}
#endregion
#region Configuration
int afkLimitMinutes;
bool kickAfkPlayers;
//bool warnBeforeKick;
protected override void LoadDefaultConfig()
{
Config["AfkLimitMinutes"] = afkLimitMinutes = GetConfig("AfkLimitMinutes", 10);
Config["KickAfkPlayers"] = kickAfkPlayers = GetConfig("KickAfkPlayers", true);
//Config["WarnBeforeKick"] = warnBeforeKick = GetConfig("WarnBeforeKick", true);
SaveConfig();
}
#endregion
#region Localization
void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["KickedForAfk"] = "You were kicked for being AFK for {0} minutes",
//["NoLongerAfk"] = "You are no longer AFK",
//["YouWentAfk"] = "You went AFK"
}, this);
}
#endregion
#region AFK Checking
void OnUserInit(IPlayer player) => AfkCheck(player);
void OnUserSpawned(IPlayer player) => AfkCheck(player);
readonly Hash<string, GenericPosition> lastPosition = new Hash<string, GenericPosition>();
readonly Dictionary<string, Timer> afkTimer = new Dictionary<string, Timer>();
void AfkCheck(IPlayer player)
{
if (HasPermission(player.Id, permExcluded)) return;
ResetPlayer(player.Id);
lastPosition[player.Id] = player.Character.Position();
afkTimer.Add(player.Id, timer.Every(afkLimitMinutes * 60, () =>
{
if (!IsPlayerAfk(player)) return;
//player.Message(Lang("YouWentAfk", player.Id));
if (kickAfkPlayers)
{
// TODO: Send timed message/warning to player before kick
player.Kick(Lang("KickedForAfk", player.Id, afkLimitMinutes));
}
}));
}
bool IsPlayerAfk(IPlayer player)
{
var last = lastPosition[player.Id];
var current = player.Character.Position();
#if DEBUG
PrintWarning($"Last position: {last}");
PrintWarning($"Current position: {current}");
PrintWarning($"Positions equal: {last.X.Equals(current.X)}");
#endif
if (last.X.Equals(current.X) || last.X.Equals(current.Y) || last.X.Equals(current.Z)) return true;
lastPosition[player.Id] = current;
return false;
}
void OnUserDisconnected(IPlayer player) => ResetPlayer(player.Id);
void ResetPlayer(string id)
{
if (afkTimer.ContainsKey(id))
{
afkTimer[id].Destroy();
afkTimer.Remove(id);
}
if (lastPosition.ContainsKey(id)) lastPosition.Remove(id);
}
void Unload()
{
foreach (var player in players.Connected) ResetPlayer(player.Id);
}
#endregion
#region Helpers
T GetConfig<T>(string name, T value) => Config[name] == null ? value : (T)Convert.ChangeType(Config[name], typeof(T));
string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
bool HasPermission(string id, string perm) => permission.UserHasPermission(id, perm);
#endregion
}
}