C#:
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Game.Rust.Cui;
using Oxide.Core.Plugins;
using UnityEngine;
using System.Linq;
using System.Net;
using Rust;
using Network;
using Network.Visibility;
namespace Oxide.Plugins
{
[Info("ArgosEye", "sergioklv && magnus_dev", "2.3.0")]
[Description("Argos Eye AntiCheat")]
public class ArgosEye : RustPlugin
{
public float DoNotChangeMe = 300f; // DO NOT CHANGE THIS! OR ANTICHEAT WILL NOT WORK!
private const string CURRENT_VERSION = "2.3.0";
private string PrivateToken;
private Dictionary<ulong, Vector3> lastPosition = new Dictionary<ulong, Vector3>();
private Dictionary<ulong, float> lastMoveTime = new Dictionary<ulong, float>();
private HashSet<ulong> afkPlayers = new HashSet<ulong>();
public Configuration config;
private DateTime _serverStartTime;
public class Configuration
{
[JsonProperty("You private AntiCheat key")]
public string privateKey = "AntiCheat KEY HERE";
[JsonProperty("Message to kick players if they don't have AntiCheat")]
public string KickDontHaveAntiCheat = "You dont have AntiCheat - DownloadAntiCheat - URL";
[JsonProperty("Send logs to console?")]
public bool SeelogsInConsole = false;
[JsonProperty(PropertyName = "Config Version (DON'T MODIFY)")]
public string Version { get; set; } = CURRENT_VERSION;
}
private Configuration GetDefaultConfig()
{
return new Configuration
{
privateKey = "AntiCheat KEY HERE",
KickDontHaveAntiCheat = "You dont have AntiCheat - DownloadAntiCheat - URL",
SeelogsInConsole = false,
Version = CURRENT_VERSION
};
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<Configuration>();
if (config == null || config.Version != CURRENT_VERSION)
{
LoadDefaultConfig();
Puts("Configuration or the version is not matching! Loading default Config!!");
}
}
catch
{
Puts("Configuration file is corrupt! Loading new default config!");
LoadDefaultConfig();
return;
}
SaveConfig();
}
protected override void LoadDefaultConfig()
{
config = GetDefaultConfig();
}
protected override void SaveConfig()
{
Config.WriteObject(config);
}
private class PrivateTokenResponse
{
public string token { get; set; }
}
private class AntiCheatResponse
{
public string ip { get; set; }
public bool valid { get; set; }
public bool hban { get; set; }
public string hbanMessage { get; set; }
public ulong userId { get; set; }
}
public class BannedPlayer
{
public string userId;
public string reason;
public string expires;
}
private class ServerData
{
public string hostname { get; set; }
public string banner { get; set; }
public string version { get; set; }
public string description { get; set; }
public int max_players { get; set; }
public string url { get; set; }
public int port { get; set; }
public int query_port { get; set; }
public string server_level { get; set; }
public int server_seed { get; set; }
public int server_worldsize { get; set; }
public int server_fps { get; set; }
public long used_memory { get; set; }
public string uptime { get; set; }
public string network_in { get; set; }
public string network_out { get; set; }
}
private class PlayerData
{
public string playerIp { get; set; }
public string userId { get; set; }
public string playerName { get; set; }
public bool IsConnected { get; set; }
public bool IsAfk { get; set; }
}
private void GetPrivateToken(Action<bool> callback)
{
var body = new Dictionary<string, string>
{
{ "privateKey", config.privateKey }
};
string jsonBody = JsonConvert.SerializeObject(body);
var headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" },
};
webrequest.Enqueue("https://lincedns.ovh/internal_get_bearer_token", jsonBody, (code, response) =>
{
if (code == 200)
{
PrivateTokenResponse tokenResponse = JsonConvert.DeserializeObject<PrivateTokenResponse>(response);
PrivateToken = tokenResponse.token;
callback(true);
}
else
{
callback(false);
}
}, this, Oxide.Core.Libraries.RequestMethod.POST, headers);
}
object OnUserApprove(Network.Connection connection)
{
string userId = connection.userid.ToString();
string ip = connection.ipaddress.Split(':')[0];
string name = connection.username;
var playerData = new
{
userId = userId,
playerName = name,
playerIp = ip,
IsConnected = true,
IsAfk = false
};
string jsonData = JsonConvert.SerializeObject(new List<object> { playerData });
var headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" },
{ "Authorization", PrivateToken }
};
webrequest.Enqueue("https://lincedns.ovh/internal_player_state", jsonData, (code, response) =>
{
if (code == 200)
{
AntiCheatResponse res = JsonConvert.DeserializeObject<AntiCheatResponse>(response);
if (!res.valid)
{
ConnectionAuth.Reject(connection, config.KickDontHaveAntiCheat);
}
else if (res.hban)
{
ConnectionAuth.Reject(connection, res.hbanMessage);
ServerUsers.Set(connection.userid, ServerUsers.UserGroup.Banned, name, res.hbanMessage);
ServerUsers.Save();
}
}
else
{
if (config.SeelogsInConsole)
{
Puts($"[OnUserApprove] Error from API: Code {code}");
}
}
}, this, RequestMethod.POST, headers);
return null;
}
private void SendServerInfo()
{
var serverData = new ServerData
{
hostname = ConVar.Server.hostname,
banner = ConVar.Server.headerimage,
version = Rust.Protocol.printable,
description = ConVar.Server.description,
max_players = ConVar.Server.maxplayers,
url = ConVar.Server.url,
port = ConVar.Server.port,
query_port = ConVar.Server.queryport,
server_level = ConVar.Server.level,
server_seed = ConVar.Server.seed,
server_worldsize = ConVar.Server.worldsize,
server_fps = Performance.current.frameRate,
used_memory = System.GC.GetTotalMemory(false) / (1024 * 1024),
uptime = FormatUptime(DateTime.UtcNow - _serverStartTime),
network_in = GetNetworkStat(true),
network_out = GetNetworkStat(false)
};
string jsonData = JsonConvert.SerializeObject(serverData);
var headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" },
{ "Authorization", PrivateToken }
};
webrequest.Enqueue("https://lincedns.ovh/internal_server_info", jsonData, (code, response) =>
{
if (code != 200)
{
if (config.SeelogsInConsole)
{
Puts($"Error when sending info Code: {code}");
}
}
}, this, Oxide.Core.Libraries.RequestMethod.POST, headers);
}
private void SendBannedPlayersList()
{
var bannedPlayers = new List<BannedPlayer>();
foreach (var user in ServerUsers.GetAll(ServerUsers.UserGroup.Banned))
{
bannedPlayers.Add(new BannedPlayer
{
userId = user.steamid.ToString(),
reason = user?.notes ?? "No reason",
expires = user.expiry > 0 ?
DateTimeOffset.FromUnixTimeSeconds((long)user.expiry).ToString("yyyy-MM-dd HH:mm:ss") :
"none"
});
}
if (bannedPlayers.Count == 0)
{
PrintWarning("There is no banned players in the server cant send request, please ban some fake steamid!.");
return;
}
string jsonData = JsonConvert.SerializeObject(bannedPlayers);
var headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" },
{ "Authorization", PrivateToken }
};
webrequest.Enqueue("https://lincedns.ovh/internal_banned_players", jsonData, (code, response) =>
{
if (code != 200)
{
if (config.SeelogsInConsole)
{
Puts($"Error sending players data to API. Error code: {code}");
}
}
}, this, Oxide.Core.Libraries.RequestMethod.POST, headers);
}
private void VerifyAntiCheatCheck()
{
var connectedPlayers = new List<PlayerData>();
foreach (var player in BasePlayer.activePlayerList)
{
var playerData = new PlayerData
{
userId = player.UserIDString,
playerName = player.displayName,
playerIp = player.net.connection.ipaddress.Split(':')[0],
IsConnected = true,
IsAfk = afkPlayers.Contains(player.userID)
};
connectedPlayers.Add(playerData);
}
if (connectedPlayers.Count == 0)
{
return;
}
string jsonData = JsonConvert.SerializeObject(connectedPlayers);
var headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" },
{ "Authorization", PrivateToken }
};
webrequest.Enqueue("https://lincedns.ovh/internal_players_check", jsonData, (code, response) =>
{
if (code == 200)
{
List<AntiCheatResponse> result = JsonConvert.DeserializeObject<List<AntiCheatResponse>>(response);
if (result == null)
{
PrintError("Failed to deserialize internal_players_check response.");
return;
}
foreach (var data in result)
{
if (!data.valid)
{
BasePlayer player = BasePlayer.activePlayerList.FirstOrDefault(p => p.userID == data.userId);
if (player == null)
{
PrintWarning($"Player: {data.userId} not found");
continue;
}
if (config.SeelogsInConsole)
{
Puts($"Player {player.displayName}-{player.userID} do not have AntiCheat kicking...");
}
if (player.IsConnected)
{
player.Kick($"{config.KickDontHaveAntiCheat}");
}
}
else
{
BasePlayer player = BasePlayer.activePlayerList.FirstOrDefault(p => p.userID == data.userId);
if (player == null)
{
Puts($"Player: {data.userId} not found");
continue;
}
if (data.valid && data.hban)
{
player.Kick($"{data.hbanMessage}");
ServerUsers.Set(player.userID, ServerUsers.UserGroup.Banned, player.displayName, data.hbanMessage);
ServerUsers.Save();
}
if (config.SeelogsInConsole)
{
Puts($"Player {player.displayName}-{player.userID} have launched AntiCheat");
}
}
}
}
else
{
if (config.SeelogsInConsole)
{
Puts($"Error checking player list. Code: {code}");
}
}
}, this, Oxide.Core.Libraries.RequestMethod.POST, headers);
}
private void SendPlayerStatus(BasePlayer player, bool IsConnected)
{
if (player == null) return;
var connectedPlayers = new List<PlayerData>();
var playerData = new PlayerData
{
userId = player.UserIDString,
playerName = player.displayName,
playerIp = player.net.connection.ipaddress.Split(':')[0],
IsConnected = IsConnected,
IsAfk = afkPlayers.Contains(player.userID)
};
connectedPlayers.Add(playerData);
string jsonData = JsonConvert.SerializeObject(connectedPlayers);
var headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" },
{ "Authorization", PrivateToken }
};
webrequest.Enqueue("https://lincedns.ovh/internal_player_state", jsonData, (code, response) =>
{
if (code == 200)
{
AntiCheatResponse playerRes = JsonConvert.DeserializeObject<AntiCheatResponse>(response);
if (IsConnected && !playerRes.valid)
{
player.Kick($"{config.KickDontHaveAntiCheat}");
}
if (config.SeelogsInConsole)
{
Puts("List of players sent to API.");
}
}
else
{
if (config.SeelogsInConsole)
{
Puts($"Error sending player list to API. Code: {code}");
}
}
}, this, Oxide.Core.Libraries.RequestMethod.POST, headers);
}
private void CheckAfkPlayers()
{
foreach (var player in BasePlayer.activePlayerList)
{
if (!player.IsConnected || player.IsSleeping()) continue;
var currentPosition = player.transform.position;
var userId = player.userID;
if (!lastPosition.ContainsKey(userId))
{
lastPosition[userId] = currentPosition;
lastMoveTime[userId] = UnityEngine.Time.realtimeSinceStartup;
continue;
}
var lastPos = lastPosition[userId];
var moved = Vector3.Distance(currentPosition, lastPos) > 0.1f;
if (moved)
{
lastPosition[userId] = currentPosition;
lastMoveTime[userId] = UnityEngine.Time.realtimeSinceStartup;
if (afkPlayers.Contains(userId))
{
afkPlayers.Remove(userId);
}
}
else
{
var inactiveTime = UnityEngine.Time.realtimeSinceStartup - lastMoveTime[userId];
if (inactiveTime >= DoNotChangeMe && !afkPlayers.Contains(userId))
{
afkPlayers.Add(userId);
}
}
}
}
private void SendDataToAPI(string url, object data)
{
if (string.IsNullOrEmpty(PrivateToken))
{
PrintError("You key is not correct or is in inactive status.");
return;
}
string jsonData = JsonConvert.SerializeObject(data);
var headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" },
{ "Authorization", PrivateToken }
};
webrequest.Enqueue(url, jsonData, (code, response) =>
{
if (code == 200)
{
if (config.SeelogsInConsole)
{
Puts($"Statistics sent correctly to API!");
}
}
else
{
if (config.SeelogsInConsole)
{
Puts($"Error sending statistics to API Code: {code}");
}
}
}, this, RequestMethod.POST, headers);
}
private string GetNetworkStat(bool incoming)
{
try
{
if (Network.Net.sv == null) return "N/A";
ulong bytes = incoming
? Network.Net.sv.GetStat(null, BaseNetwork.StatTypeLong.BytesReceived_LastSecond)
: Network.Net.sv.GetStat(null, BaseNetwork.StatTypeLong.BytesSent_LastSecond);
return FormatBytes(bytes);
}
catch
{
return "N/A";
}
}
private string FormatUptime(TimeSpan uptime)
{
if (uptime.TotalDays >= 1)
{
return $"{(int)uptime.TotalDays}d {uptime.Hours}h";
}
if (uptime.TotalHours >= 1)
{
return $"{(int)uptime.TotalHours}h {uptime.Minutes}m";
}
return $"{uptime.Minutes}m {uptime.Seconds}s";
}
private string FormatBytes(ulong bytes)
{
if (bytes >= 1024 * 1024)
{
return $"{bytes / (1024.0 * 1024.0):F1} MB/s";
}
if (bytes >= 1024)
{
return $"{bytes / 1024.0:F1} KB/s";
}
return $"{bytes} B/s";
}
private void OnPlayerDeath(BasePlayer victim, HitInfo info)
{
if (info == null || victim == null) return;
var npcAttacker = info.Initiator as BaseNpc;
if (npcAttacker != null && !(victim is BasePlayer))
{
string prefab = npcAttacker.ShortPrefabName;
if (prefab.Contains("bear") || prefab.Contains("wolf") || prefab.Contains("boar") || prefab.Contains("chicken") || prefab.Contains("stag"))
return;
}
var attackerPlayer = info.Initiator as BasePlayer;
var attackerNpc = info.Initiator as BaseNpc;
var attackerEntity = info.Initiator as BaseCombatEntity;
string attackerId = attackerPlayer?.UserIDString ?? "N/A";
string attackerName = attackerPlayer?.displayName
?? attackerEntity?.ShortPrefabName
?? "Unknown";
string attackerHealth = attackerEntity != null
? attackerEntity.Health().ToString("0")
: "Unknown";
string victimHealth =
(victim.Health() + info.damageTypes.Total()).ToString("0");
bool attackerIsNpc = (attackerPlayer != null && attackerPlayer.IsNpc) || attackerNpc != null;
bool victimIsNpc = (victim != null && victim.IsNpc) || attackerNpc != null;
float? distance = null;
if (attackerEntity != null && victim != null)
{
distance = Vector3.Distance(attackerEntity.transform.position, victim.transform.position);
}
var hitArea = info?.boneArea ?? (HitArea)(-1);
var data = new
{
timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
victimId = victim.UserIDString,
attackerId = attackerId,
attackerHealth = attackerHealth,
victimHealth = victimHealth,
weapon = info.WeaponPrefab != null ? info.WeaponPrefab.ShortPrefabName : "Unknown",
hitBone = (int)hitArea == -1 ? "Body" : hitArea.ToString(),
distance = distance
};
if (!victimIsNpc && !attackerIsNpc && victim.UserIDString != attackerId)
{
SendDataToAPI("https://lincedns.ovh/internal_server_death", data);
}
}
private void OnPlayerConnected(BasePlayer player)
{
if (player == null) return;
lastPosition[player.userID] = player.transform.position;
lastMoveTime[player.userID] = UnityEngine.Time.realtimeSinceStartup;
SendPlayerStatus(player, true);
}
private void OnPlayerDisconnected(BasePlayer player)
{
if (player == null) return;
if (!player.IsValid()) return;
lastPosition.Remove(player.userID);
lastMoveTime.Remove(player.userID);
afkPlayers.Remove(player.userID);
SendPlayerStatus(player, false);
}
void OnServerShutdown()
{
foreach (var player in BasePlayer.activePlayerList)
{
if (player == null) return;
lastPosition.Remove(player.userID);
lastMoveTime.Remove(player.userID);
afkPlayers.Remove(player.userID);
SendPlayerStatus(player, false);
}
}
private void OnServerInitialized()
{
_serverStartTime = DateTime.UtcNow;
LoadConfig();
GetPrivateToken((result) =>
{
if (result)
{
SendServerInfo();
SendBannedPlayersList();
VerifyAntiCheatCheck();
timer.Every(60f, () =>
{
SendServerInfo();
VerifyAntiCheatCheck();
SendBannedPlayersList();
});
timer.Every(10f, CheckAfkPlayers);
Puts("You AntiCheat Key is in active status! AntiCheat loaded correctly!");
LoadConfig();
}
else PrintError("Error! you AntiCheat key is not correct or is not in active status!");
});
}
}
}