C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
namespace Oxide.Plugins {
[Info("AnitTeam", "Yoshi", "1.0.8")]
class AntiTeam : CovalencePlugin {
[PluginReference]
private Plugin Clans, AutoDemoRecord;
private bool _clansEnabled;
private bool _demoRecord = false;
private string permissionName = "AntiTeam.admin";
private string _banListID;
private string _serverId;
private string _orgId;
private int _groupLimit;
private string _reportWebhook;
private string _notesWebhook;
private string _alertWebhook;
private ulong _reportColor;
private bool _ignoreAdmins;
private string _roleID;
private string _BanRoleID;
private string _serverName;
private AutoBanRules _autoBanConfig;
public ProximityConfig _proximityConfig;
private FriendsConfig _friendsConfig;
private BanConfig _banConfig;
private ProximityKillConfig _killConfig;
private AutoDemoRecorder _demoConfig;
private GroupTempBanConfig _tempBans;
public static AntiTeam _instance;
private ProximityTracking _proximityInstance;
private GameObject _proximityObject;
private WebClient _client = new WebClient();
private Dictionary<string, Parameter> alertConfigs = new Dictionary<string, Parameter>();
public Dictionary<ulong, Dictionary<ulong, ProximitySubInfo>> ProximityData = new Dictionary<ulong, Dictionary<ulong, ProximitySubInfo>>();
public Dictionary<ulong, Dictionary<ulong, List<string>>> AlertTracking = new Dictionary<ulong, Dictionary<ulong, List<string>>>();
public Dictionary<ulong, List<ulong>> MarkedAsTeaming = new Dictionary<ulong, List<ulong>>();
#region Startup
void OnServerInitialized(bool initial) {
_instance = this;
if(Interface.Oxide.DataFileSystem.ExistsDatafile("Alerts_Data")) AlertTracking = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, Dictionary<ulong, List<string>>>>("Alerts_Data");
_demoConfig = config.AutoDemoRecorderConfig;
_groupLimit = config.groupLimit;
_reportWebhook = config.ReportWebhook;
_alertWebhook = config.AlertsWebhooks;
_reportColor = config.ReportColor;
_tempBans = config.tempBans;
_autoBanConfig = config.autoBanRules;
_notesWebhook = config.noteConfig.NotesWebhook;
_proximityConfig = config.proximityConfig;
_friendsConfig = config.steamFriendsConfig;
_banConfig = config.banConfig;
_killConfig = config.proximityKillConfig;
_ignoreAdmins = config.AdminBypass;
_serverName = config.ServerName;
_roleID = config.RoleID;
_BanRoleID = config.BanRoleID;
_banListID = _banConfig.BanlistID;
if (_proximityConfig.Enabled) {
_proximityObject = new GameObject();
_proximityInstance = _proximityObject.AddComponent<ProximityTracking>();
_proximityInstance.InitTracking();
if(Interface.Oxide.DataFileSystem.ExistsDatafile("Proximity_Data")) ProximityData = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, Dictionary<ulong, ProximitySubInfo>>>("Proximity_Data");
}
_client.SetFriendsCheck(config.steamFriendsConfig.Enabled);
#region gross
if (!string.IsNullOrEmpty(_banConfig.Token)) _client.SetToken(_banConfig.Token);
else {
if(_autoBanConfig.BattlemetricsBan) {
PrintError("Battlemetrics ban is enabled but no token was given, disabling battlemetrics banning");
_autoBanConfig.BattlemetricsBan = false;
}
}
if(String.IsNullOrEmpty(_friendsConfig.SteamAPIKey) && _friendsConfig.Enabled) {
PrintError("Check friends is enabled but no api key was given! disabling friends check");
_friendsConfig.Enabled = false;
}
if (!Clans) {
PrintWarning("Clans plugin not found, disabling clans support");
_clansEnabled = false;
}
else {
PrintWarning("Clans plugin found, enabling clans support");
_clansEnabled = true;
}
if(!AutoDemoRecord) {
PrintWarning("Auto Demo Recorder plugin not found, disabling demo support");
}
else {
if (_demoConfig.Enabled) {
PrintWarning("Auto Demo Recorder plugin found, enabling demo support");
_demoRecord = true;
}
}
#endregion
if ((RelationshipManager.maxTeamSize <= _groupLimit) || !_tempBans.Enabled) Unsubscribe(nameof(OnPlayerConnected));
alertConfigs = _autoBanConfig.parameters;
alertConfigs.Add("SteamFriends", new Parameter(config.steamFriendsConfig.weight, true, 0, false, false, false, false));
AddCovalenceCommand("prox", nameof(ProximityCommand));
AddCovalenceCommand("alerts", nameof(AlertsCommand));
AddCovalenceCommand("friends", nameof(FriendsCommand));
AddCovalenceCommand("voicelogs", nameof(VoiceLogs));
permission.RegisterPermission(permissionName, this);
InitBattlemetrics();
}
void Unload() {
_instance = null;
_proximityInstance.Destroy();
MonoBehaviour.Destroy(_proximityObject);
foreach (var obj in activeWatchers.Values) {
if (obj != null) obj.Destroy();
}
}
void OnNewSave(string filename) {
if(Interface.Oxide.DataFileSystem.ExistsDatafile("Proximity_Data")) Interface.Oxide.DataFileSystem.WriteObject("Proximity_Data", new Dictionary<ulong, Dictionary<ulong, ProximitySubInfo>>());
if (Interface.Oxide.DataFileSystem.ExistsDatafile("Alerts_Data")) Interface.Oxide.DataFileSystem.WriteObject("Alerts_Data", new Dictionary<ulong, Dictionary<ulong, List<string>>>());
}
#endregion
#region Config
public Configuration config;
public class Configuration {
[JsonProperty(PropertyName = "ignore admins (true/false)")]
public bool AdminBypass = true;
[JsonProperty(PropertyName = "group limit")]
public int groupLimit = 3;
[JsonProperty(PropertyName = "The webhook where the teaming alerts will be sent to (leave blank to disable)")]
public string AlertsWebhooks = "";
[JsonProperty(PropertyName = @"Server name sent with the alert embed (put ""default"" to use the servers host name, or blank not include any name)")]
public string ServerName = "default";
[JsonProperty(PropertyName = "Role ID, the id of the role you want pinged with an alert (leave blank to disable)")]
public string RoleID = "";
[JsonProperty(PropertyName = "Role ID, the id of the role you want pinged for the automatic ban alerts")]
public string BanRoleID = "";
[JsonProperty(PropertyName = "The webhook where the F7 teaming reports will be sent to (leave blank to disable)")]
public string ReportWebhook = "";
[JsonProperty(PropertyName = "Report Color, the color of the teaming report embed")]
public ulong ReportColor = 0xFF0000;
[JsonProperty(PropertyName = "When a player gives a note to another player, this feature will log the note contents and player info to discord")]
public NoteLoggingConfig noteConfig = new NoteLoggingConfig();
[JsonProperty(PropertyName = "Team kill config options")]
public ProximityKillConfig proximityKillConfig = new ProximityKillConfig();
[JsonProperty(PropertyName = "Proximity config options")]
public ProximityConfig proximityConfig = new ProximityConfig();
[JsonProperty(PropertyName = "Friends check config options")]
public FriendsConfig steamFriendsConfig = new FriendsConfig();
[JsonProperty(PropertyName = "Temp bans for players that are in a group exceeding the limit (requires the team UI to not be limited)")]
public GroupTempBanConfig tempBans = new GroupTempBanConfig();
[JsonProperty(PropertyName = "Auto demo recoreder config options")]
public AutoDemoRecorder AutoDemoRecorderConfig = new AutoDemoRecorder();
[JsonProperty(PropertyName = "Battlemetric ban options")]
public BanConfig banConfig = new BanConfig();
[JsonProperty(PropertyName = "Parameters for auto bans")]
public AutoBanRules autoBanRules = new AutoBanRules();
public static Configuration DefaultConfig() {
return new Configuration {
steamFriendsConfig = new FriendsConfig() {
Enabled = true, weight = 0.5f, SteamAPIKey = "", MinimumFriendTime = 0
}
, noteConfig = new NoteLoggingConfig() {
Enabled = true, NotesWebhook = ""
}
, AutoDemoRecorderConfig = new AutoDemoRecorder() {
demoLength = 2, Enabled = false, DemoWebhook = ""
}
, tempBans = new GroupTempBanConfig() {
Enabled = false, BanLength = 10, MinutesInGroup = 3, BanMessage = "Temp banned for violating the group limit", TempBanWebhook = "", TempBanColor = 0x14F8FF
}
, proximityKillConfig = new ProximityKillConfig() {
MaximumDistance = 5, MaximumHeight = 3, MinimumDistanceToTarget = 20, MaximumAngle = 140
}
, proximityConfig = new ProximityConfig() {
Enabled = true, SaveToFile = true, WipeFileOnWipe = true, TrackKills = true, HasToBeVisible = false, ProximityDistance = 20, ProximityKillTrigger = 3, ProximityKillWeight = 0.3f, ProximityTimeTrigger = 900, ProximityTimeWeight = 0.3f
}
, banConfig = new BanConfig() {
Token = "", BanlistID = "", BanReason = "Banned for teaming, banned for {{timeLeft}}", Note = "Automated teaming ban, proof =>"
}
, autoBanRules = new AutoBanRules() {
AutoBanWebhook = "", AutoBanColor = 0x000000, AutoBanWeight = 1, BattlemetricsBan = true, NativeBan = false, EnableAutoBan = false, banLength = 7, teamBan = true, parameters = new Dictionary<string, Parameter> {
{
"Team Raid", new Parameter(0.5f, true, 0x000000, true, true, false, true)
}
, {
"Team Kill", new Parameter(0.5f, true, 0xFF0000, true, true, false, true)
}
, {
"Code Lock", new Parameter(0.5f, true, 0x30FF46, false, false, false, true)
}
, {
"Cupboard", new Parameter(0.1f, true, 0x8A6000, false, false, false, true)
}
, {
"Shared Vehicle", new Parameter(0.3f, true, 0x4F008A, false, true, false, true)
}
, {
"Turret", new Parameter(0.3f, true, 0xFF9414, false, false, true, true)
}
, {
"Revive", new Parameter(0.1f, true, 0x14F8FF, true, true, true, true)
}
, {
"Looting", new Parameter(0.2f, true, 0x2914FF, true, true, false, true)
}
, {
"Team Accept", new Parameter(0.4f, true, 0x14FF74, false, true, false, true)
}
, {
"Relationship Alert", new Parameter(0.2f, true, 0xB026FF, false, true, true, true)
}
, {
"Voice Chat", new Parameter(0.1f, true, 0xCDE500, false, true, false, true)
}
, {
"Bag", new Parameter(0.2f, true, 0xFF5317, false, true, false, true)
}
}
}
}
;
}
}
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) throw new Exception();
foreach(var alert in Configuration.DefaultConfig().autoBanRules.parameters) {
if (!config.autoBanRules.parameters.ContainsKey(alert.Key)) config.autoBanRules.parameters.Add(alert.Key, alert.Value);
}
SaveConfig();
}
catch(Exception e) {
PrintError("Your configuration file contains an error.Using default configuration values.");
LoadDefaultConfig();
}
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultConfig() => config = Configuration.DefaultConfig();
#endregion
#region Proximity Kills
void OnPlayerDeath(BasePlayer player, HitInfo info) {
if (!_proximityConfig.TrackKills) return;
if (info == null || player == null || !player.userID.IsSteamId()) return;
if (info.InitiatorPlayer == null || info.InitiatorPlayer == player) return;
if (!info.InitiatorPlayer.userID.IsSteamId()) return;
var Initiator = info.Initiator as BasePlayer;
if (!PlayerCheck(player, Initiator)) return;
if (IsPlayerFriendly(player.userID, Initiator.userID)) return;
_proximityInstance.getProximityDataFiltered(player.userID, Initiator.userID).selfKills++;
List<BasePlayer> list = Facepunch.Pool.GetList<BasePlayer>();
BaseNetworkable.GetCloseConnections(player.transform.position, _proximityConfig.ProximityDistance, list);
foreach (BasePlayer otherPlayer in list) {
if ((otherPlayer == player) || (otherPlayer == Initiator)) continue;
if (IsPlayerFriendly(otherPlayer.userID, Initiator.userID)) return;
if (_proximityConfig.HasToBeVisible && !AntiTeam.IsPlayerVisibleToUs(Initiator, otherPlayer)) return;
_proximityInstance.getProximityDataFiltered(Initiator.userID, otherPlayer.userID).proxKills++;
}
}
#endregion
#region Alert Methods
#region Team Kill
List<ulong> IsOnCoolDown = new List<ulong>();
void OnEntityTakeDamage(BasePlayer entity, HitInfo info) {
Parameter parameter;
if (alertConfigs.TryGetValue("Team Kill", out parameter)) {
if (!parameter.Enabled) return;
}
else return;
if (info.InitiatorPlayer == null) return;
if (!entity.userID.IsSteamId()) return;
if (!info.InitiatorPlayer.userID.IsSteamId()) return;
if (info.ProjectileID == 0) return;
var player = info.InitiatorPlayer;
var target = entity;
if (target == player) return;
List<BasePlayer> list = Facepunch.Pool.GetList<BasePlayer>();
BaseNetworkable.GetCloseConnections(player.transform.position, RelationshipManager.seendistance, list);
foreach (var subPlayer in list) {
if (_ignoreAdmins && subPlayer.IsAdmin) continue;
if (IsPlayerFriendly(player.userID, subPlayer.userID)) continue;
if (target == subPlayer) continue;
if (subPlayer == player) continue;
if (!subPlayer.userID.IsSteamId()) continue;
if (subPlayer.IsSleeping()) continue;
if (!subPlayer.IsConnected) continue;
var playerPos = player.transform.position;
var subPlayerPos = subPlayer.transform.position;
var targetPlayerPos = target.transform.position;
Vector3 direction = subPlayer.eyes.HeadForward();
direction.y = target.eyes.HeadForward().y;
subPlayerPos.y = targetPlayerPos.y;
float difference = Vector3.Angle(direction, targetPlayerPos - subPlayerPos);
float aimDifference = Vector3.Angle(direction, playerPos - subPlayerPos);
if (aimDifference > _killConfig.MaximumAngle) continue;
if (difference <= 2) {
subPlayerPos = subPlayer.transform.position;
if (subPlayer.GetActiveItem() == null) continue;
if (subPlayer.GetActiveItem().GetHeldEntity() is BaseProjectile) {
if (IsOnCoolDown.Contains(subPlayer.userID)) continue;
if (!IsPlayerVisibleToUs(player, subPlayer)) continue;
var distance = Vector3.Distance(playerPos, subPlayerPos);
var angle = Vector3.Angle(playerPos, subPlayerPos);
var playerGun = player.GetActiveItem().info.name;
var subPlayerGun = subPlayer.GetActiveItem().info.name;
var distanceToTarget = Math.Max(Vector3.Distance(targetPlayerPos, playerPos), Vector3.Distance(targetPlayerPos, subPlayerPos));
var YAxis = Math.Sin(angle) * distance;
var XAxis = Math.Cos(angle) * distance;
if (Math.Abs(YAxis) >= _killConfig.MaximumHeight) continue;
if (Math.Abs(XAxis) >= _killConfig.MaximumDistance) continue;
if (Math.Abs(distanceToTarget) <= _killConfig.MinimumDistanceToTarget) continue;
ProximityPacket packet = new ProximityPacket() {
Angle = angle, Distance = distance, playerGun = playerGun, subPlayerGun = subPlayerGun, targetPlayer = target.userID, targetPlayerDistance = distanceToTarget
}
;
string alertType = "Team Kill";
var players = new List<ulong> {
player.userID, subPlayer.userID
}
;
string Optinfo = $"{player.displayName} and {subPlayer.displayName} were detected teaming up in PVP, targeted player => {target.displayName} - {target.userID}";
AlertPacket alert = new AlertPacket(players, alertType, packet, playerPos.ToString(), Optinfo);
var alerts = _instance.getAlertData(players[0], players[1]);
_instance.SendToDiscord(alert);
if (_instance.alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
if (_demoRecord && alertConfigs[alertType].AutoDemoRecord) {
StartDemoRecording(player, $"Team Kill - {player.userID} {subPlayer.userID}");
}
var id = subPlayer.userID;
IsOnCoolDown.Add(subPlayer.userID);
timer.Once(60f, () => {
IsOnCoolDown.Remove(id);
}
);
}
}
}
}
#endregion
#region Raid Detection
private List<ulong> justReported = new List<ulong>();
void OnExplosiveThrown(BasePlayer player, BaseEntity entity, ThrownWeapon item) {
Parameter parameter;
if (alertConfigs.TryGetValue("Team Raid", out parameter)) {
if (!parameter.Enabled) return;
}
else return;
if (_ignoreAdmins && player.IsAdmin) return;
if (justReported.Contains(player.userID)) return;
RaycastHit hit;
if (Physics.Raycast(player.eyes.HeadRay(), out hit, 30f, LayerMask.GetMask("Default", "Construction"))) {
var hitEntity = hit.GetEntity();
if (hitEntity == null) return;
var combatEntity = hitEntity.GetComponent<BaseCombatEntity>();
if (combatEntity != null) {
if (IsPlayerFriendly(player.userID, combatEntity.OwnerID)) return;
if (combatEntity.OwnerID == player.userID || !combatEntity.OwnerID.IsSteamId()) return;
if (activeWatchers.ContainsKey(player.userID)) {
activeWatchers[player.userID].Reset();
return;
}
activeWatchers.Add(player.userID, new RaidWatch(player, combatEntity.OwnerID));
}
}
}
void CheckRaiding(int ammoType, BasePlayer player) {
Parameter parameter;
if (alertConfigs.TryGetValue("Team Raid", out parameter)) {
if (!parameter.Enabled) return;
}
else return;
if (_ignoreAdmins && player.IsAdmin) return;
if (justReported.Contains(player.userID)) return;
if (ammoType == -1321651331) {
RaycastHit hit;
if (Physics.Raycast(player.eyes.HeadRay(), out hit, 30f, LayerMask.GetMask("Default", "Construction"))) {
var hitEntity = hit.GetEntity();
if (hitEntity == null) return;
var combatEntity = hitEntity.GetComponent<BaseCombatEntity>();
if (combatEntity != null) {
if (IsPlayerFriendly(player.userID, combatEntity.OwnerID)) return;
if (combatEntity.OwnerID == player.userID || !combatEntity.OwnerID.IsSteamId()) return;
if (activeWatchers.ContainsKey(player.userID)) {
activeWatchers[player.userID].Reset();
return;
}
activeWatchers.Add(player.userID, new RaidWatch(player, combatEntity.OwnerID));
}
}
}
}
void OnRocketLaunched(BasePlayer player, BaseEntity entity) {
Parameter parameter;
if (alertConfigs.TryGetValue("Team Raid", out parameter)) {
if (!parameter.Enabled) return;
}
else return;
if (_ignoreAdmins && player.IsAdmin) return;
if (justReported.Contains(player.userID)) return;
RaycastHit hit;
if (Physics.Raycast(player.eyes.HeadRay(), out hit, 30f, LayerMask.GetMask("Default", "Construction"))) {
var hitEntity = hit.GetEntity();
if (hitEntity == null) return;
var combatEntity = hitEntity.GetComponent<BaseCombatEntity>();
if (combatEntity != null) {
if (IsPlayerFriendly(player.userID, combatEntity.OwnerID)) return;
if (combatEntity.OwnerID == player.userID || !combatEntity.OwnerID.IsSteamId()) return;
if (activeWatchers.ContainsKey(player.userID)) {
activeWatchers[player.userID].Reset();
return;
}
activeWatchers.Add(player.userID, new RaidWatch(player, combatEntity.OwnerID));
}
}
}
private Dictionary<ulong, RaidWatch> activeWatchers = new Dictionary<ulong, RaidWatch>();
public class RaidWatch {
private BasePlayer player;
private ulong raider;
private DateTime startTime;
private Dictionary<ulong, RaidInfo> raidData = new Dictionary<ulong, RaidInfo>();
public RaidWatch(BasePlayer playerIn, ulong raiderIn) {
this.startTime = DateTime.Now;
this.player = playerIn;
this.raider = raiderIn;
playerIn.InvokeRepeating(CheckSurroundings, 0, 1);
}
private void CheckSurroundings() {
if (player == null || player.IsDead()) {
this.Destroy();
return;
}
if ((DateTime.Now - startTime).TotalMinutes >= 3) {
this.Destroy();
return;
}
List<BasePlayer> list = Facepunch.Pool.GetList<BasePlayer>();
BaseNetworkable.GetCloseConnections(player.transform.position, RelationshipManager.seendistance, list);
foreach (BasePlayer otherPlayer in list) {
if (_instance._ignoreAdmins && otherPlayer.IsAdmin) continue;
if (_instance.IsPlayerFriendly(player.userID, otherPlayer.userID)) continue;
if (_instance.IsPlayerFriendly(raider, otherPlayer.userID)) continue;
if (otherPlayer == player) continue;
if (otherPlayer == null) continue;
if (otherPlayer.IsSleeping()) continue;
if (otherPlayer.IsDead()) continue;
if (!otherPlayer.IsConnected) continue;
if (otherPlayer.userID == raider) continue;
var playerPos = player.transform.position;
var otherPlayerPos = otherPlayer.transform.position;
if (Math.Abs(Vector3.Distance(playerPos, otherPlayerPos)) <= 10) {
if (IsPlayerVisibleToUs(player, otherPlayer)) {
if (!raidData.ContainsKey(otherPlayer.userID)) raidData.Add(otherPlayer.userID, new RaidInfo {
inProximity = 0, lookingAt = 0
}
);
float angel = Vector3.Angle(player.eyes.HeadRay().direction, otherPlayerPos - playerPos);
if (Mathf.Abs(angel) <= 60) raidData[otherPlayer.userID].lookingAt++;
else {
angel = Vector3.Angle(otherPlayer.eyes.HeadRay().direction, playerPos - otherPlayerPos);
if (Mathf.Abs(angel) <= 60) raidData[otherPlayer.userID].lookingAt++;
}
raidData[otherPlayer.userID].inProximity++;
if (raidData[otherPlayer.userID].inProximity > 30 && raidData[otherPlayer.userID].lookingAt > 10) {
var distance = Vector3.Distance(player.transform.position, otherPlayer.transform.position);
var angle = Vector3.Angle(playerPos, otherPlayerPos);
Item weapon = null;
foreach (var item in otherPlayer.inventory.containerBelt.itemList) {
var wcheck = item.GetHeldEntity();
if (wcheck == null) continue;
var wfcheck = wcheck.GetComponent<BaseProjectile>();
if (wfcheck != null) {
weapon = item;
break;
}
}
if (weapon == null) continue;
var subPlayerGun = weapon.info.name;
weapon = null;
foreach (var item in player.inventory.containerBelt.itemList) {
var wcheck = item.GetHeldEntity();
if (wcheck == null) continue;
var wfcheck = wcheck.GetComponent<BaseProjectile>();
if (wfcheck != null) {
weapon = item;
break;
}
}
if (weapon == null) continue;
var playerGun = weapon.info.name;
ProximityPacket killConfig = new ProximityPacket {
Distance = distance, Angle = angle, playerGun = playerGun, subPlayerGun = subPlayerGun, targetPlayerDistance = 0, targetPlayer = raider
}
;
string alertType = "Team Raid";
var players = new List<ulong> {
player.userID, otherPlayer.userID
}
;
string Optinfo = $"{player.displayName} and {otherPlayer.displayName} were detected raiding together, owner of base being raided => {raider}";
AlertPacket alert = new AlertPacket(players, alertType, killConfig, playerPos.ToString(), Optinfo);
if (_instance._demoRecord && _instance.alertConfigs[alertType].AutoDemoRecord) {
_instance.StartDemoRecording(player, $"Team Raid - {player.userID} {otherPlayer.userID}");
}
var alerts = _instance.getAlertData(players[0], players[1]);
_instance.SendToDiscord(alert);
if (_instance.alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
_instance.justReported.Add(player.userID);
_instance.justReported.Add(otherPlayer.userID);
this.Destroy();
}
}
}
}
}
public void Destroy() {
if (player == null) return;
_instance.activeWatchers.Remove(player.userID);
player.CancelInvoke(CheckSurroundings);
}
public void Reset() {
startTime = DateTime.Now;
}
}
public class RaidInfo {
public int lookingAt;
public int inProximity;
}
#endregion
#region Voice Chat
private Dictionary<ulong, Dictionary<ulong, int>> voiceChatData = new Dictionary<ulong, Dictionary<ulong, int>>();
void OnPlayerVoice(BasePlayer player, Byte[] data) {
if (_ignoreAdmins && player.IsAdmin) return;
Parameter parameter;
if (alertConfigs.TryGetValue("Voice Chat", out parameter)) {
if (!parameter.Enabled) return;
}
else return;
if (player.InSafeZone() || player.IsBuildingAuthed()) return;
List<BasePlayer> list = Facepunch.Pool.GetList<BasePlayer>();
BaseNetworkable.GetCloseConnections(player.transform.position, 5, list);
foreach (BasePlayer otherPlayer in list) {
if (otherPlayer != player && !otherPlayer.isClient && otherPlayer.IsAlive() && !otherPlayer.IsSleeping() && !otherPlayer.limitNetworking) {
if (IsPlayerFriendly(otherPlayer.userID, player.userID)) return;
if (otherPlayer.InSafeZone() || otherPlayer.IsBuildingAuthed()) return;
if (_ignoreAdmins && otherPlayer.IsAdmin) return;
if (!IsPlayerVisibleToUs(player, otherPlayer)) return;
IncreaseVoiceChatDataFiltered(player, otherPlayer);
}
}
}
public void IncreaseVoiceChatDataFiltered(BasePlayer player, BasePlayer otherPlayer) {
var ent1 = player.userID;
var ent2 = otherPlayer.userID;
if (ent2 > ent1) {
var tmp = ent2;
ent2 = ent1;
ent1 = tmp;
}
Dictionary<ulong, int> proximityData;
if (voiceChatData.TryGetValue(ent1, out proximityData)) {
int time;
if (!proximityData.TryGetValue(ent2, out time)) proximityData.Add(ent2, 1);
else {
proximityData[ent2]++;
if ((time++ / 10) == 60) {
SendVoiceChatAlert(player, otherPlayer);
proximityData[ent2] += 10;
}
}
}
else {
voiceChatData.Add(ent1, new Dictionary<ulong, int> {
{
ent2, 1
}
}
);
}
}
public int GetVoiceChatDataFiltered(ulong ent1, ulong ent2) {
if (ent2 > ent1) {
var tmp = ent2;
ent2 = ent1;
ent1 = tmp;
}
int time = 0;
Dictionary<ulong, int> proximityData;
if (voiceChatData.TryGetValue(ent1, out proximityData)) proximityData.TryGetValue(ent2, out time);
return time;
}
public void SendVoiceChatAlert(BasePlayer player, BasePlayer otherPlayer) {
if (_ignoreAdmins && (player.IsAdmin || otherPlayer.IsAdmin)) return;
if (IsPlayerFriendly(player.userID, otherPlayer.userID)) return;
string alertType = "Voice Chat";
List<ulong> players = new List<ulong> {
player.userID, otherPlayer.userID
}
;
string OptInfo = $"{player.displayName} and {otherPlayer.displayName} have talked through voice chat for over 60 seconds";
AlertPacket alert = new AlertPacket(players, alertType, player.transform.position.ToString(), OptInfo);
var alerts = getAlertData(players[0], players[1]);
if (_demoRecord && alertConfigs[alertType].AutoDemoRecord) {
StartDemoRecording(player, $"Voice Chat Alert - {player.userID} {otherPlayer.userID}");
}
if (alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
SendToDiscord(alert);
}
#endregion
#region Bag
void OnEntityBuilt(Planner plan, GameObject go) {
if (plan == null || go == null) return;
var player = plan.GetOwnerPlayer();
var Entity = go.ToBaseEntity();
if (Entity == null || player == null) return;
if(Entity is SleepingBag) HandleNewBag(player, Entity as SleepingBag, player.userID);
}
void CanAssignBed(BasePlayer player, SleepingBag bag, ulong targetPlayerId) => HandleNewBag(player, bag, targetPlayerId);
void HandleNewBag(BasePlayer player, SleepingBag bag, ulong targetPlayerId) {
var bags = SleepingBag.sleepingBags.Where(x => x.buildingID == bag.buildingID && x != bag).Select(x => x.deployerUserID).Distinct().ToList();
if (!bags.Contains(targetPlayerId)) bags.Add(targetPlayerId);
if (bags.Count() > _groupLimit) {
if (_ignoreAdmins && player.IsAdmin) return;
if (bags.Count > _groupLimit) {
var otherPlayer = BasePlayer.FindByID(targetPlayerId);
string otherPlayerName = otherPlayer != null ? otherPlayer.displayName : targetPlayerId.ToString();
string alertType = "Bag";
if (bags.Count - _groupLimit > 1) {
var existingCombinations = from item1 in bags select new PlayerPair(item1, player.userID);
foreach (var pair in existingCombinations) {
if (IsPlayerFriendly(pair.player1, pair.player2)) continue;
var alerts = getAlertData(pair.player1, pair.player2);
if (alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
}
if (_demoRecord && alertConfigs[alertType].AutoDemoRecord) {
string players = "";
foreach (var user in bags) players += $"{user}, ";
players = players.Remove(players.Length - 2, 2);
StartDemoRecording(player, $"Bag - {players}");
}
SendToDiscord(new AlertPacket(bags, alertType, bag.transform.position.ToString(), $"{player.displayName} has bagged {otherPlayerName} in a base that already has {_groupLimit} players bagged"), false);
return;
}
var combinations = from item1 in bags from item2 in bags where item1 < item2 select new PlayerPair(item1, item2);
foreach (var pair in combinations) {
if (IsPlayerFriendly(pair.player1, pair.player2)) continue;
var alerts = getAlertData(pair.player1, pair.player2);
if (alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
}
if (_demoRecord && alertConfigs[alertType].AutoDemoRecord) {
string players = "";
foreach (var user in bags) players += $"{user}, ";
players = players.Remove(players.Length - 2, 2);
StartDemoRecording(player, $"Bag - {players}");
}
SendToDiscord(new AlertPacket(bags, alertType, bag.transform.position.ToString(), $"{player.displayName} has bagged {otherPlayerName} in a base that already has {_groupLimit} players bagged"));
}
}
}
#endregion
#region Code Lock
void OnCodeEntered(CodeLock codeLock, BasePlayer player, string code) {
Parameter parameter;
if (alertConfigs.TryGetValue("Code Lock", out parameter)) {
if (!parameter.Enabled) return;
}
else return;
if (_ignoreAdmins && player.IsAdmin) return;
if (codeLock.code != code) return;
var authlist = codeLock.whitelistPlayers;
if (!authlist.Contains(player.userID)) authlist.Add(player.userID);
if (authlist.Count > _groupLimit) {
string alertType = "Code Lock";
if (authlist.Count - _groupLimit > 1) {
var existingCombinations = from item1 in authlist select new PlayerPair(item1, player.userID);
foreach (var pair in existingCombinations) {
if (IsPlayerFriendly(pair.player1, pair.player2)) continue;
var alerts = getAlertData(pair.player1, pair.player2);
if (alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
}
if (_demoRecord && alertConfigs[alertType].AutoDemoRecord) {
string players = "";
foreach (var user in authlist) players += $"{user}, ";
players = players.Remove(players.Length - 2, 2);
StartDemoRecording(player, $"Code Lock - {players}");
}
SendToDiscord(new AlertPacket(authlist, alertType, codeLock.transform.position.ToString(), "codelock code - " + code), false);
return;
}
var combinations = from item1 in authlist from item2 in authlist where item1 < item2 select new PlayerPair(item1, item2);
foreach (var pair in combinations) {
if (IsPlayerFriendly(pair.player1, pair.player2)) continue;
var alerts = getAlertData(pair.player1, pair.player2);
if (alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
}
if (_demoRecord && alertConfigs[alertType].AutoDemoRecord) {
string players = "";
foreach (var user in authlist) players += $"{user}, ";
players = players.Remove(players.Length - 2, 2);
StartDemoRecording(player, $"Code Lock - {players}");
}
string OptInfo = $"{player.displayName} has authed on a codelock that already has {_groupLimit} players authed, codelock is owned by this account => {codeLock.OwnerID}";
SendToDiscord(new AlertPacket(authlist, alertType, codeLock.transform.position.ToString(), OptInfo));
}
//scan
}
#endregion
#region Cupboard
void OnCupboardAuthorize(BuildingPrivlidge privilege, BasePlayer player) {
Parameter parameter;
if (alertConfigs.TryGetValue("Cupboard", out parameter)) {
if (!parameter.Enabled) return;
}
else return;
if (_ignoreAdmins && player.IsAdmin) return;
var authlist = privilege.authorizedPlayers.Select(x => x.userid).ToList();
if (!authlist.Contains(player.userID)) authlist.Add(player.userID);
if (privilege.GetProtectedMinutes() > 0 && authlist.Count() > _groupLimit) {
string alertType = "Cupboard";
if (authlist.Count - _groupLimit > 1) {
var existingCombinations = from item1 in authlist select new PlayerPair(item1, player.userID);
foreach (var pair in existingCombinations) {
if (IsPlayerFriendly(pair.player1, pair.player2)) continue;
var alerts = getAlertData(pair.player1, pair.player2);
if (alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
}
if (_demoRecord && alertConfigs[alertType].AutoDemoRecord) {
string players = "";
foreach (var user in authlist) players += $"{user}, ";
players = players.Remove(players.Length - 2, 2);
StartDemoRecording(player, $"Cupboard - {players}");
}
SendToDiscord(new AlertPacket(authlist, alertType, player.transform.position.ToString(), ""), false);
return;
}
var combinations = from item1 in authlist from item2 in authlist where item1 < item2 select new PlayerPair(item1, item2);
foreach (var pair in combinations) {
if (IsPlayerFriendly(pair.player1, pair.player2)) continue;
var alerts = getAlertData(pair.player1, pair.player2);
if (alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
}
if (_demoRecord && alertConfigs[alertType].AutoDemoRecord) {
string players = "";
foreach (var user in authlist) players += $"{user}, ";
players = players.Remove(players.Length - 2, 2);
StartDemoRecording(player, $"Cupboard - {players}");
}
string OptInfo = $"{player.displayName} has authed on a cupboard that already has {_groupLimit} players authed, cupboard is owned by this account => {privilege.OwnerID}";
SendToDiscord(new AlertPacket(authlist, alertType, player.transform.position.ToString(), OptInfo));
}
}
#endregion
#region Shared Vehicle
void OnEntityMounted(BaseMountable entity, BasePlayer player) {
Parameter parameter;
if (alertConfigs.TryGetValue("Shared Vehicle", out parameter)) {
if (!parameter.Enabled) return;
}
else return;
if (_ignoreAdmins && player.IsAdmin) return;
if (entity.GetParentEntity() == null) return;
if (player.InSafeZone()) return;
if (!player.userID.IsSteamId()) return;
if (entity.GetParentEntity() is BaseVehicle) {
var vehicle = entity.GetParentEntity().GetComponent<BaseVehicle>();
List<ulong> mounted = new List<ulong>();
foreach(var mount in vehicle.mountPoints) {
if(mount != null) {
var mountedPlayer = mount.mountable.GetMounted();
if (mountedPlayer != null && mountedPlayer.userID.IsSteamId() && (!mountedPlayer.IsAdmin || !_ignoreAdmins)) mounted.Add(mountedPlayer.userID);
}
}
if (!mounted.Contains(player.userID)) mounted.Add(player.userID);
if (mounted.Count <= _groupLimit) return;
string alertType = "Shared Vehicle";
AlertPacket alert = new AlertPacket(mounted, alertType, entity.transform.position.ToString(), "");
var combinations = from item1 in mounted from item2 in mounted where item1 < item2 select new PlayerPair(item1, item2);
foreach (var pair in combinations) {
if (IsPlayerFriendly(pair.player1, pair.player2)) continue;
var alerts = getAlertData(pair.player1, pair.player2);
if (_instance.alertConfigs[alertType].Stackable || !alerts.Contains(alertType)) alerts.Add(alertType);
}
if (_demoRecord && alertConfigs[alertType].AutoDemoRecord) {
string players = "";
foreach (var user in mounted) players += $"{user}, ";
players = players.Remove(players.Length - 2, 2);
StartDemoRecording(player, $"Shared Vehicle - {players}");
}
string OptInfo = $"{player.displayName} has mounted in a vehicle that already has {_groupLimit} players mounted";
SendToDiscord(new AlertPacket(mounted,
// ... [Code preview truncated due to length. Full source in attached file] ...