Suggestion can you add a feature for player IDs not to be tracked. I often log in using my Admin. I don't want to be seen to be online for 2 hours ect.
Whitelist players ? Not to TrackSuggestion
Bump, since I agree
i need this for the toplist.
i don´t want to be listed on my servers playtime rankings.
please add this <3
DUMP Anyone implement this ? I dont want my admin account showing up in " /playtime top " early on in wipe...
Whitelist players not to be tracked.
Whitelist players not to be tracked.
Not saying I've waited a long time. But my kids have grown up and left home...
done.
unload the plugin first, change the code and reload it.
permission: playtime.ignore
set to the group or player as you like after that you need to rejoin server
the player time will never be registered.
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
namespace Oxide.Plugins
{
[Info("Playtime Tracker", "k1lly0u", "0.2.2")]
[Description("Track player time spent on the server edit by FrZ added ignore permission playtime.ignore")]
class PlaytimeTracker : CovalencePlugin
{
#region Fields
private StoredData storedData;
private DynamicConfigFile data;
private static Action<string, double> TimeReward;
private static Action<string, string> ReferralReward;
private static PluginTimers Timer;
private static Plugin RewardPlugin;
#endregion
#region Oxide Hooks
protected override void LoadDefaultMessages() => lang.RegisterMessages(Messages, this);
void Init()
{
LoadData();
permission.RegisterPermission("playtime.ignore", this); // Registering the playtime.ignore permission
}
private void OnServerInitialized()
{
TimeReward = IssueReward;
ReferralReward = IssueReward;
Configuration.Reward.RegisterPermissions(this, permission);
Timer = timer;
ValididateRewardSystem();
foreach (IPlayer user in players.Connected)
OnUserConnected(user);
TimedSaveData();
}
private void OnUserConnected(IPlayer user)
{
if (permission.UserHasPermission(user.Id, "playtime.ignore"))
{
Puts($"{user.Name} has the 'playtime.ignore' permission, skipping tracking.");
return; // Skip tracking for this player
}
storedData.OnUserConnected(user);
}
private void OnUserDisconnected(IPlayer user) => storedData.OnUserDisconnected(user);
private void OnPluginLoaded(Plugin plugin)
{
if (plugin != null && plugin.Name.Equals(Configuration.Reward.Plugin, StringComparison.OrdinalIgnoreCase))
RewardPlugin = plugin;
}
private void Unload()
{
SaveData();
TimeReward = null;
ReferralReward = null;
Timer = null;
RewardPlugin = null;
Configuration = null;
}
#endregion
#region Functions
private void ValididateRewardSystem()
{
RewardPlugin = plugins.Find(Configuration.Reward.Plugin);
if (RewardPlugin == null)
PrintError("The selected reward system is not loaded. Unable to issue rewards");
}
private static double CurrentTime => DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
private static string FormatTime(double time)
{
TimeSpan dateDifference = TimeSpan.FromSeconds((float)time);
int days = dateDifference.Days;
int hours = dateDifference.Hours;
hours += (days * 24);
return string.Format("{0:00}h:{1:00}m:{2:00}s", hours, dateDifference.Minutes, dateDifference.Seconds);
}
#endregion
#region Rewards
internal void IssueReward(string id, double amount)
{
float multiplier = 1f;
foreach (KeyValuePair<string, float> kvp in Configuration.Reward.CustomMultipliers)
{
if (permission.UserHasPermission(id, kvp.Key))
{
if (kvp.Value > multiplier)
multiplier = kvp.Value;
}
}
amount *= multiplier;
switch (Configuration.Reward.Plugin)
{
case "ServerRewards":
RewardPlugin?.Call("AddPoints", ulong.Parse(id), (int)amount);
break;
case "Economics":
RewardPlugin?.Call("Deposit", id, amount);
break;
default:
break;
}
IPlayer user = players.FindPlayerById(id);
if (user != null && user.IsConnected)
Message(user, $"Reward.Given.{Configuration.Reward.Plugin}", (int)amount);
}
internal void IssueReward(string referrer, string referee)
{
IssueReward(referrer, Configuration.Reward.Referral.InviteReward);
IssueReward(referee, Configuration.Reward.Referral.JoinReward);
}
#endregion
#region Commands
private double _nextTopUpdate;
private List<StoredData.UserData> topList = new List<StoredData.UserData>();
[Command("playtime")]
private void cmdPlaytime(IPlayer user, string command, string[] args)
{
if (args.Length == 0)
{
double time = storedData.GetPlayTimeForPlayer(user.Id);
double afkTime = storedData.GetAFKTimeForPlayer(user.Id);
if (time == 0 && afkTime == 0)
Message(user, "Error.NoPlaytimeStored");
else
{
if (Configuration.General.TrackAFK)
Message(user, "Playtime.Both", FormatTime(time), FormatTime(afkTime));
else Message(user, "Playtime.Single", FormatTime(time));
}
Message(user, "Playtime.Help");
return;
}
switch (args[0].ToLower())
{
case "top":
string str = lang.GetMessage("Top.Title", this, user.Id);
if (CurrentTime > _nextTopUpdate)
{
storedData.GetTopPlayTime(topList);
_nextTopUpdate = CurrentTime + 60f;
}
for (int i = 0; i < Math.Min(Configuration.General.TopCount, topList.Count); i++)
str += string.Format(lang.GetMessage("Top.Format", this), topList[i].displayName, FormatTime(topList[i].playtime));
user.Reply(str);
return;
default:
if (user.IsAdmin)
{
IPlayer target = players.FindPlayer(args[0]);
if (target == null)
{
Message(user, "Error.NoPlayerFound", args[0]);
return;
}
double time = storedData.GetPlayTimeForPlayer(target.Id);
if (time == 0)
{
Message(user, "Error.NoTimeStored");
return;
}
user.Reply($"{target.Name} - {FormatTime(time)}");
}
else Message(user, "Error.InvalidSyntax");
break;
}
}
[Command("refer")]
private void cmdRefer(IPlayer user, string command, string[] args)
{
if (!Configuration.Reward.Referral.Enabled)
{
Message(user, "Referral.Disabled");
return;
}
if (args.Length == 0)
{
Message(user, "Referral.Help");
return;
}
if (storedData.HasBeenReferred(user.Id))
{
Message(user, "Referral.Submitted");
return;
}
IPlayer referrer = players.FindPlayer(args[0]);
if (referrer == null)
{
Message(user, "Error.NoPlayerFound", args[0]);
return;
}
if (referrer.Id.Equals(user.Id))
{
Message(user, "Referral.Self");
return;
}
storedData.ReferPlayer(referrer.Id, user.Id);
Message(user, "Referral.Accepted");
if (referrer.IsConnected)
Message(referrer, "Referral.Acknowledged", user.Name);
}
#endregion
#region API
private object GetPlayTime(string id)
{
double time = storedData.GetPlayTimeForPlayer(id);
return time == 0 ? null : (object)time;
}
private object GetAFKTime(string id)
{
double time = storedData.GetAFKTimeForPlayer(id);
return time == 0 ? null : (object)time;
}
private object GetReferrals(string id)
{
int amount = storedData.GetReferralsForPlayer(id);
return amount == 0 ? null : (object)amount;
}
#endregion
#region Config
private static ConfigData Configuration;
private class ConfigData
{
[JsonProperty(PropertyName = "General Options")]
public GeneralOptions General { get; set; }
[JsonProperty(PropertyName = "Reward Options")]
public RewardOptions Reward { get; set; }
public class GeneralOptions
{
[JsonProperty(PropertyName = "Data save interval (seconds)")]
public int SaveInterval { get; set; }
[JsonProperty(PropertyName = "Track player AFK time")]
public bool TrackAFK { get; set; }
[JsonProperty(PropertyName = "Number of entries to display in the top playtime list")]
public int TopCount { get; set; }
}
public class RewardOptions
{
[JsonProperty(PropertyName = "Reward plugin (ServerRewards, Economics)")]
public string Plugin { get; set; }
[JsonProperty(PropertyName = "Playtime rewards")]
public PlaytimeRewards Playtime { get; set; }
[JsonProperty(PropertyName = "Referral rewards")]
public ReferralRewards Referral { get; set; }
[JsonProperty(PropertyName = "Custom reward multipliers (permission / multiplier)")]
public Hash<string, float> CustomMultipliers { get; set; }
public class PlaytimeRewards
{
[JsonProperty(PropertyName = "Issue rewards for playtime")]
public bool Enabled { get; set; }
[JsonProperty(PropertyName = "Reward interval (seconds)")]
public int Interval { get; set; }
[JsonProperty(PropertyName = "Reward amount")]
public int Reward { get; set; }
}
public class ReferralRewards
{
[JsonProperty(PropertyName = "Issue rewards for player referrals")]
public bool Enabled { get; set; }
[JsonProperty(PropertyName = "Referrer reward amount")]
public int InviteReward { get; set; }
[JsonProperty(PropertyName = "Referee reward amount")]
public int JoinReward { get; set; }
}
[JsonIgnore]
private Permission permission;
internal void RegisterPermissions(CovalencePlugin plugin, Permission permission)
{
this.permission = permission;
foreach (string str in CustomMultipliers.Keys)
permission.RegisterPermission(str, plugin);
}
}
public VersionNumber Version { get; set; }
}
protected override void LoadConfig()
{
base.LoadConfig();
Configuration = Config.ReadObject<ConfigData>();
if (Configuration.Version < Version)
UpdateConfigValues();
Config.WriteObject(Configuration, true);
}
protected override void LoadDefaultConfig() => Configuration = GetBaseConfig();
private ConfigData GetBaseConfig()
{
return new ConfigData
{
General = new ConfigData.GeneralOptions
{
SaveInterval = 900,
TrackAFK = true,
TopCount = 10
},
Reward = new ConfigData.RewardOptions
{
Plugin = "Economics",
Playtime = new ConfigData.RewardOptions.PlaytimeRewards
{
Enabled = true,
Interval = 3600,
Reward = 5
},
Referral = new ConfigData.RewardOptions.ReferralRewards
{
Enabled = true,
InviteReward = 5,
JoinReward = 3
},
CustomMultipliers = new Hash<string, float>
{
["playtimetracker.examplevip1"] = 1.5f,
["playtimetracker.examplevip2"] = 2.0f,
}
},
Version = Version
};
}
protected override void SaveConfig() => Config.WriteObject(Configuration, true);
private void UpdateConfigValues()
{
PrintWarning("Config update detected! Updating config values...");
if (Configuration.Version < new VersionNumber(0, 2, 0))
Configuration = GetBaseConfig();
Configuration.Version = Version;
PrintWarning("Config update completed!");
}
#endregion
#region Data Management
private void TimedSaveData()
{
timer.In(Configuration.General.SaveInterval, () =>
{
SaveData();
TimedSaveData();
});
}
private void SaveData() => data.WriteObject(storedData);
private void LoadData()
{
if (!Interface.Oxide.DataFileSystem.ExistsDatafile("PlaytimeTracker/user_data") && Interface.Oxide.DataFileSystem.ExistsDatafile("PTTracker/playtime_data"))
RestoreOldData();
else
{
data = Interface.Oxide.DataFileSystem.GetFile("PlaytimeTracker/user_data");
storedData = data.ReadObject<StoredData>();
if (storedData == null)
storedData = new StoredData();
}
}
[Command("ptt.restorenames")]
private void FindMissingNames(IPlayer player, string message, string[] args)
{
if (!player.IsAdmin)
return;
int missing = 0;
int restored = 0;
foreach (KeyValuePair<string, StoredData.UserData> kvp in storedData._userData)
{
if (string.IsNullOrEmpty(kvp.Value.displayName))
{
missing++;
IPlayer user = players.FindPlayerById(kvp.Key);
if (user != null)
{
if (!string.IsNullOrEmpty(user.Name))
{
restored++;
kvp.Value.displayName = user.Name;
}
else kvp.Value.displayName = "Unnamed";
}
}
}
player.Reply($"Restored {restored}/{missing} names");
}
private class StoredData
{
[JsonProperty]
internal Hash<string, UserData> _userData = new Hash<string, UserData>();
[JsonProperty]
internal HashSet<string> _referredUsers = new HashSet<string>();
public bool HasBeenReferred(string id) => _referredUsers.Contains(id);
public void ReferPlayer(string referrer, string referree)
{
_referredUsers.Add(referree);
UserData userData;
if (!_userData.TryGetValue(referrer, out userData))
userData = _userData[referrer] = new UserData();
userData.referrals += 1;
ReferralReward(referrer, referree);
}
public void OnUserConnected(IPlayer user)
{
UserData userData;
if (!_userData.TryGetValue(user.Id, out userData))
userData = _userData[user.Id] = new UserData();
userData.OnUserConnected(user);
}
public void OnUserDisconnected(IPlayer user)
{
UserData userData;
if (_userData.TryGetValue(user.Id, out userData))
userData.OnUserDisconnected();
}
public double GetPlayTimeForPlayer(string id)
{
UserData userData;
if (!_userData.TryGetValue(id, out userData))
return 0;
return userData.PlayTime;
}
public double GetAFKTimeForPlayer(string id)
{
UserData userData;
if (!_userData.TryGetValue(id, out userData))
return 0;
return userData.AFKTime;
}
public int GetReferralsForPlayer(string id)
{
UserData userData;
if (!_userData.TryGetValue(id, out userData))
return 0;
return userData.referrals;
}
public void GetTopPlayTime(List<UserData> list)
{
list.Clear();
list.AddRange(_userData.Values);
list.Sort((UserData a, UserData b) =>
{
return a.playtime.CompareTo(b.playtime) * -1;
});
}
internal void InsertData(string id, string displayName, double playTime, double afkTime, double lastReward, int referrals)
{
UserData userData;
if (!_userData.TryGetValue(id, out userData))
userData = _userData[id] = new UserData();
userData.displayName = displayName;
userData.playtime = playTime;
userData.afkTime = afkTime;
userData.lastRewardTime = playTime;
userData.referrals = referrals;
}
internal void InsertReferral(string id)
{
_referredUsers.Add(id);
}
public class UserData
{
public double playtime;
public double afkTime;
public double lastRewardTime;
public int referrals;
public string displayName;
[JsonIgnore]
private IPlayer _user;
[JsonIgnore]
private Timer _timer;
[JsonIgnore]
private double _timeStarted;
[JsonIgnore]
private GenericPosition _lastPosition = new GenericPosition();
private const float TIMER_INTERVAL = 30f;
public double PlayTime
{
get
{
if (_user == null || !_user.IsConnected)
return playtime;
return playtime + (_user.Position() != _lastPosition ? (CurrentTime - _timeStarted) : 0);
}
}
public double AFKTime
{
get
{
if (_user == null || !_user.IsConnected)
return afkTime;
return afkTime + (_user.Position() == _lastPosition ? (CurrentTime - _timeStarted) : 0);
}
}
public void OnUserConnected(IPlayer user)
{
displayName = user.Name;
_user = user;
_lastPosition = user.Position();
if (_timer != null)
OnUserDisconnected();
StartTimer();
}
private void StartTimer()
{
_timeStarted = CurrentTime;
_timer = Timer.In(TIMER_INTERVAL, OnTimerTick);
}
public void OnUserDisconnected()
{
_timer.Destroy();
_timer = null;
}
private void OnTimerTick()
{
if (_user != null && _user.IsConnected)
{
if (Configuration.General.TrackAFK && _user != null)
{
if (EqualPosition(_user.Position(), _lastPosition))
afkTime += TIMER_INTERVAL;
else playtime += TIMER_INTERVAL;
_lastPosition = _user.Position();
}
else playtime += TIMER_INTERVAL;
if (RewardPlugin != null && Configuration.Reward.Playtime.Enabled)
{
double rewardMultiplier = (playtime - lastRewardTime) / Configuration.Reward.Playtime.Interval;
if (rewardMultiplier >= 1f)
{
TimeReward(_user.Id, (double)Configuration.Reward.Playtime.Reward * rewardMultiplier);
lastRewardTime = playtime;
}
}
}
StartTimer();
}
private bool EqualPosition(GenericPosition a, GenericPosition b)
{
if (a == null || b == null)
return false;
return Math.Abs(a.X - b.X) <= (Math.Abs(a.X) + Math.Abs(b.X) + 1) * float.Epsilon &&
Math.Abs(a.Y - b.Y) <= (Math.Abs(a.Y) + Math.Abs(b.Y) + 1) * float.Epsilon &&
Math.Abs(a.Z - b.Z) <= (Math.Abs(a.Z) + Math.Abs(b.Z) + 1) * float.Epsilon;
}
}
}
#region Data Converter
private void RestoreOldData()
{
data = Interface.Oxide.DataFileSystem.GetFile("PlaytimeTracker/user_data");
storedData = new StoredData();
LoadOldData();
foreach (KeyValuePair<string, PlayData.TimeInfo> kvp in playData.timeData)
{
IPlayer player = players.FindPlayerById(kvp.Key);
storedData.InsertData(kvp.Key, player?.Name ?? "Unnamed", kvp.Value.playTime, kvp.Value.afkTime, kvp.Value.lastReward, kvp.Value.referrals);
}
foreach (string str in referData.referrals)
storedData.InsertReferral(str);
if (permData.permissions.Count > 0)
{
foreach (KeyValuePair<string, float> kvp in permData.permissions)
Configuration.Reward.CustomMultipliers[kvp.Key] = kvp.Value;
Configuration.Reward.RegisterPermissions(this, permission);
SaveConfig();
}
playData = null;
referData = null;
permData = null;
SaveData();
}
private void LoadOldData()
{
TimeData = Interface.Oxide.DataFileSystem.GetFile("PTTracker/playtime_data");
PermissionData = Interface.Oxide.DataFileSystem.GetFile("PTTracker/permission_data");
ReferralData = Interface.Oxide.DataFileSystem.GetFile("PTTracker/referral_data");
playData = TimeData.ReadObject<PlayData>();
if (playData == null)
playData = new PlayData();
referData = ReferralData.ReadObject<RefData>();
if (referData == null)
referData = new RefData();
permData = PermissionData.ReadObject<PermData>();
if (permData == null)
permData = new PermData();
}
private PlayData playData;
private PermData permData;
private RefData referData;
private DynamicConfigFile TimeData;
private DynamicConfigFile PermissionData;
private DynamicConfigFile ReferralData;
private class PlayData
{
public Dictionary<string, TimeInfo> timeData = new Dictionary<string, TimeInfo>();
public class TimeInfo
{
public double playTime;
public double afkTime;
public double lastReward;
public int referrals;
}
}
private class PermData
{
public Dictionary<string, float> permissions = new Dictionary<string, float>();
}
private class RefData
{
public List<string> referrals = new List<string>();
}
#endregion
#endregion
#region Localization
private void Message(IPlayer user, string key, params object[] args)
{
if (args == null || args.Length == 0)
user.Reply(lang.GetMessage(key, this, user.Id));
else user.Reply(string.Format(lang.GetMessage(key, this, user.Id), args));
}
private readonly Dictionary<string, string> Messages = new Dictionary<string, string>
{
["Playtime.Both"] = "[#45b6fe]Playtime[/#] : [#ffd479]{0}[/#]\n[#45b6fe]AFK Time[/#] : [#ffd479]{1}[/#]",
["Playtime.Single"] = "[#45b6fe]Playtime[/#] : [#ffd479]{0}[/#]",
["Playtime.Help"] = "You can see the top scoring playtimes by typing [#a1ff46]/playtime top[/#]",
["Top.Title"] = "[#45b6fe]Top Playtimes:[/#]",
["Top.Format"] = "\n[#a1ff46]{0}[/#] - [#ffd479]{1}[/#]",
["Referral.Disabled"] = "The referral system is disabled",
["Referral.Help"] = "[#ffd479]/refer <name or ID>[/#] - Add a referral for the specified player",
["Referral.Submitted"] = "You have already submitted your referral",
["Referral.Self"] = "You can not refer yourself",
["Referral.Accepted"] = "Your referral has been accepted",
["Referral.Acknowledged"] = "[#a1ff46]{0}[/#] has acknowledged a referral from you",
["Reward.Given.ServerRewards"] = "You have received [#a1ff46]{0} RP[/#] for playing on our server!",
["Reward.Given.Economics"] = "You have received [#a1ff46]{0}[/#] coins for playing on our server!",
["Error.NoPlaytimeStored"] = "No playtime has been stored for you yet",
["Error.NoPlayerFound"] = "No player found with the name [#a1ff46]{0}[/#]",
["Error.NoTimeStored"] = "No time stored for the specified player",
["Error.InvalidSyntax"] = "Invalid syntax",
};
#endregion
}
}