C#:
using ProtoBuf;
using Newtonsoft.Json.Linq;
using UnityEngine;
using Oxide.Core;
using Facepunch;
using System;
using System.Text;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using Object = System.Object;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Oxide.Plugins {
[Info("IQTeamLimitter", "Mercury", "1.4.5")]
[Description("Monitoring and logging the actions of players with teams")]
class IQTeamLimitter : RustPlugin {
private void OnServerInitialized() {
RelationshipManager manager = RelationshipManager.ServerInstance;
Int32 countTeams = manager ? manager.teams.Count : 0;
Puts(LanguageEn ? $"There are {countTeams} teams of players on the server" : $"На сервере существует {countTeams} команд из игроков");
foreach (BasePlayer player in BasePlayer.activePlayerList) OnPlayerConnected(player);
}
private void OnServerShutdown() => Unload();
private void OnTeamLeave(RelationshipManager.PlayerTeam team, BasePlayer player) {
if (team.members.Count == 1) {
String logMessageLeaveCountNull = LanguageEn ? $"{player.displayName}({player.UserIDString}) has disbanded their team and left it." : $"{player.displayName}({player.UserIDString}) удалил свою команду и покинул ее";
LoggedProcess(ref logMessageLeaveCountNull);
return;
}
PlayerInfo pData = PlayerInfo.Get(player.UserIDString);
if (pData == null) return;
if (pData.IsLimit()) return;
pData.cooldownAddedTeams = config.GetCooldown();
SendChat(GetLang("ALERT_LEAVE_TEAM", player.UserIDString, FormatTime(pData.GetCooldown()), pData.GetLimit()), player);
String logMessageLeave = LanguageEn ? $"{player.displayName}({player.UserIDString}) has left the team." : $"{player.displayName}({player.UserIDString}) покинул команду";
LoggedProcess(ref logMessageLeave);
}
private void Init() {
ReadData();
LoadDataFiles();
}
private Object OnTeamCreate(BasePlayer player) {
PlayerInfo pData = PlayerInfo.Get(player.UserIDString);
if (pData == null) return null;
if (pData.IsLimit()) {
SendChat(GetLang("INFO_ALERT_CREATE_TEAM", player.UserIDString), player);
return false;
}
String messageKey = pData.IsCooldown() ? "INFO_ALERT_PLUS_COOLDOWN" : "INFO_ALERT";
SendChat(GetLang(messageKey, player.UserIDString, pData.GetLimit(), FormatTime(pData.GetCooldown())), player);
String logMessageCreate = LanguageEn ? $"{player.displayName}({player.UserIDString}) has created a team." : $"{player.displayName}({player.UserIDString}) создал команду";
LoggedProcess(ref logMessageCreate);
return null;
}
private readonly Dictionary<BasePlayer, Int32> inviteCooldownLog = new Dictionary<BasePlayer, Int32>();
private class PlayerInfo : SplitDatafile<PlayerInfo> {
private const String BaseFolder = "IQSystem" + "/" + "IQTeamLimitter" + "/" + "Players" + "/";
public static PlayerInfo Save(String id) => Save(BaseFolder, id);
public static void Import(String id, PlayerInfo data) => Import(BaseFolder, id, data);
public static void Remove(String id) => Remove(BaseFolder, id);
public static PlayerInfo Get(String id) => Get(BaseFolder, id);
public static PlayerInfo Load(String id) => Load(BaseFolder, id);
public static PlayerInfo Clear(String id) => ClearAndSave(BaseFolder, id);
public static PlayerInfo GetOrLoad(String id) => GetOrLoad(BaseFolder, id);
public static PlayerInfo GetOrCreate(String id) => GetOrCreate(BaseFolder, id);
public static String[] GetFiles() => GetFiles(BaseFolder);
public Int32 cooldownAddedTeams;
public Int32 addTeamCount;
public Boolean IsLimit() => addTeamCount >= config.limitTeam;
public Boolean IsCooldown() => GetCooldown() > 0;
public Int32 GetCooldown() => cooldownAddedTeams - CurrentTime;
public Double GetLimit() => (config.limitTeam - addTeamCount) <= 0 ? 0 : config.limitTeam - addTeamCount;
}
public Dictionary<UInt64, String> kickAlert = new Dictionary<UInt64, String>();
private static Configuration config = new Configuration();
private void OnNewSave(String filename) => DetectedWipe();
protected override void SaveConfig() => Config.WriteObject(config);
public class Configuration {
[JsonProperty(LanguageEn ? "Disband all teams after the wipe (true - yes/false - no)" : "Расформировать все команды после вайпа (true - да/false - нет)")]
public Boolean clearAllTeamAfterWipe;
[JsonProperty(LanguageEn ? "Configuration of supported plugins" : "Настройка поддерживаемых плагинов")]
public ReferencePlugins referencePlugins = new ReferencePlugins();
internal class LogController {
internal class LogToDiscord {
[JsonProperty(LanguageEn ? "Webhooks: Log all actions with teams to the Discord channel" : "Webhooks: Логирование всех действий с командами в канал Discord")]
public String webhooksDiscord;
[JsonProperty(LanguageEn ? "Webhooks: Avatar for the webhook (direct link)" : "Webhooks: Аватар для вебхука (прямая ссылка)")]
public String avatartDiscord;
[JsonProperty(LanguageEn ? "Webhooks: Name for the webhook" : "Webhooks: Название для вебхука")]
public String nameDiscord;
}
[JsonProperty(LanguageEn ? "Log data to file" : "Логирование данных в файл")]
public Boolean logToFile;
[JsonProperty(LanguageEn ? "Log data to Discord" : "Логирование данных в Discord")]
public LogToDiscord discordLogSetting = new LogToDiscord();
}
public class ReferencePlugins {
[JsonProperty(LanguageEn ? "IQChat: Chat notification settings" : "IQChat: Настройка уведомлений в чате")]
public IQChatPreset iqchatPreset = new IQChatPreset();
public class IQChatPreset {
[JsonProperty(LanguageEn ? "IQChat: Custom prefix in chat" : "IQChat: Кастомный префикс в чате")]
public String customPrefix;
[JsonProperty(LanguageEn ? "IQChat: Custom avatar in chat (if required)" : "IQChat: Кастомный аватар в чате (если требуется)")]
public String customAvatar;
}
}
[JsonProperty(LanguageEn ? "Limit on adding to the team during the wipe (if the limit is exhausted, the player will not be able to join teams)" : "Лимит на добавление в команду в течение вайпа (если лимит исчерпан, игрок не сможет вступать в команды)")]
public Int32 limitTeam;
public Int32 GetCooldown() => CurrentTime + (cooldownMinutes * 60);
[JsonProperty(LanguageEn ? "Cooldown time for joining a new team or inviting players after being removed from a team or leaving it (in minutes)" : "Время перезарядки для вступления в новую команду или приглашения игроков после удаления из команды или выхода из неё (в минутах)")]
public Int32 cooldownMinutes;
public static Configuration GetNewConfiguration() {
return new Configuration {
limitTeam = 3, cooldownMinutes = 60, clearAllTeamAfterWipe = true, logController = new LogController {
logToFile = false, discordLogSetting = new LogController.LogToDiscord {
webhooksDiscord = String.Empty, nameDiscord = "[IQTeamLimitter]", avatartDiscord = ""
}
}
, referencePlugins = new ReferencePlugins() {
iqchatPreset = new ReferencePlugins.IQChatPreset() {
customAvatar = "0", customPrefix = "<color=#1F6BA0>[IQTeamLimitter]\n</color>",
}
}
}
;
}
[JsonProperty(LanguageEn ? "Logging settings for team actions" : "Настройки логирования действий с командами")]
public LogController logController = new LogController();
}
private abstract class SplitDatafile<T> where T : SplitDatafile<T>, new() {
public static Dictionary<String, T> _players = new();
protected static void Import(String baseFolder, String userId, T data) {
_players[userId] = data;
Save(baseFolder, userId);
}
protected static String[] GetFiles(String baseFolder) {
try {
Int32 json = ".json".Length;
String[] paths = Interface.Oxide.DataFileSystem.GetFiles(baseFolder, "*.json");
for (Int32 i = 0; i < paths.Length; i++) {
String path = paths[i];
Int32 separatorIndex = path.LastIndexOf("/", StringComparison.Ordinal);
paths[i] = path.Substring(separatorIndex + 1, path.Length - separatorIndex - 1 - json);
}
return paths;
}
catch {
return Array.Empty<string>();
}
}
protected static T Save(string baseFolder, String userId) {
T data;
if (!_players.TryGetValue(userId, out data)) return null;
Interface.Oxide.DataFileSystem.WriteObject(baseFolder + userId, data);
return data;
}
protected static void Remove(string baseFolder, String userId) {
if (!_players.ContainsKey(userId)) return;
_players.Remove(userId);
Interface.Oxide.DataFileSystem.DeleteDataFile(baseFolder + userId);
}
protected static T Get(string baseFolder, String userId) {
T data;
if (_players.TryGetValue(userId, out data)) return data;
return null;
}
protected static T Load(String baseFolder, String userId) {
T data = null;
try {
data = Interface.Oxide.DataFileSystem.ReadObject<T>(baseFolder + userId);
}
catch (Exception e) {
Interface.Oxide.LogError(e.ToString());
}
return _players[userId] = data;
}
protected static T GetOrLoad(String baseFolder, String userId) {
T data;
if (_players.TryGetValue(userId, out data)) return data;
return Load(baseFolder, userId);
}
protected static T GetOrCreate(String baseFolder, String userId) {
return GetOrLoad(baseFolder, userId) ?? (_players[userId] = new T());
}
protected static T ClearAndSave(String baseFolder, String userId) {
T data;
if (_players.TryGetValue(userId, out data)) {
data = new T();
Interface.Oxide.DataFileSystem.WriteObject(baseFolder + userId, data);
return data;
}
return null;
}
}
private static Int32 CurrentTime => Facepunch.Math.Epoch.Current;
protected override void LoadDefaultConfig() => config = Configuration.GetNewConfiguration();
[ConsoleCommand("tc")]
private void TCConsoleCMD(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Player();
if(player != null) if (!player.IsAdmin) return;
if (!arg.HasArgs(1)) {
Puts(LanguageEn ? "Use the correct syntax : tc Steam64ID" : "Используйте корректный синтаксис : tc Steam64ID");
return;
}
String userID = arg.Args[0];
if (!userID.IsSteamId()) {
Puts(LanguageEn ? "The Steam64ID is specified incorrectly" : "Неверно указан Steam64ID");
return;
}
PlayerInfo pData = PlayerInfo.Get(userID);
if (pData == null) {
Puts(LanguageEn ? "Such a player does not exist in the database" : "Такой игрок не существует в базе данных");
return;
}
pData.cooldownAddedTeams = 0;
Puts(LanguageEn ? $"You have successfully reset the friend request cooldown for player {userID}" : $"Вы успешно сбросили перезарядку на добавления в друзья для игрока {userID}");
}
private void WriteData() => Oxide.Core.Interface.Oxide.DataFileSystem.WriteObject("IQSystem/IQTeamLimitter/AlertPlayersAfterKick", kickAlert);
private Boolean IsCooldownLog(BasePlayer target) {
if (!target) return false;
if (inviteCooldownLog.TryGetValue(target, out Int32 cooldownLog)) {
if (cooldownLog > CurrentTime) return true;
}
inviteCooldownLog[target] = CurrentTime + 60;
return false;
}
[PluginReference]
Plugin IQChat;
private readonly Dictionary<BasePlayer, BasePlayer> activeInvite = new Dictionary<BasePlayer, BasePlayer>();
[ConsoleCommand("tl")]
private void TLConsoleCMD(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Player();
if(player != null) if (!player.IsAdmin) return;
if (!arg.HasArgs(2)) {
Puts(LanguageEn ? "Use the correct syntax : tl Steam64ID Number" : "Используйте корректный синтаксис : tl Steam64ID Количество");
return;
}
String userID = arg.Args[0];
if (!userID.IsSteamId()) {
Puts(LanguageEn ? "The Steam64ID is specified incorrectly" : "Неверно указан Steam64ID");
return;
}
PlayerInfo pData = PlayerInfo.Get(userID);
if (pData == null) {
Puts(LanguageEn ? "Such a player does not exist in the database" : "Такой игрок не существует в базе данных");
return;
}
if (!Int32.TryParse(arg.Args[1], out Int32 count)) {
Puts(LanguageEn ? "You must specify the value in numbers" : "Вы должны указать значение в цифрах");
return;
}
pData.addTeamCount -= count;
if (pData.addTeamCount < 0) pData.addTeamCount = 0;
Puts(LanguageEn ? $"You have successfully reset the player's limit on {count} values. The current player limit is: {pData.addTeamCount}/{config.limitTeam}" : $"Вы успешно сбросили лимит игрока на {count} значения. Текущий лимит игрока : {pData.addTeamCount}/{config.limitTeam}");
}
private void LoadDataFiles() {
String[] players = PlayerInfo.GetFiles();
foreach (String player in players) PlayerInfo.Load(player);
Puts(LanguageEn ? $"The {players.Length} database of players has been initialized" : $"Инициализирована база данных {players.Length} игроков");
}
private void OnTeamRejectInvite(BasePlayer rejector, RelationshipManager.PlayerTeam team) {
if (!activeInvite.TryGetValue(rejector, out BasePlayer inviter)) return;
String logMessageAccepted = inviter == null ? (LanguageEn ? $"{rejector.displayName}({rejector.UserIDString}) decided not to accept the team invitation." : $"{rejector.displayName}({rejector.UserIDString}) отклонил приглашение в команду") : (LanguageEn ? $"{rejector.displayName}({rejector.UserIDString}) has declined the team invitation from {inviter.displayName}({inviter.UserIDString})." : $"{rejector.displayName}({rejector.UserIDString}) отклонил приглашение в команду от {inviter.displayName}({inviter.UserIDString})");
LoggedProcess(ref logMessageAccepted);
activeInvite.Remove(rejector);
}
private void OnPlayerConnected(BasePlayer player) {
PlayerInfo playerInfo = PlayerInfo.Get(player.UserIDString);
if (playerInfo == null) {
playerInfo = new PlayerInfo {
cooldownAddedTeams = 0, addTeamCount = 0,
}
;
PlayerInfo.Import(player.UserIDString, playerInfo);
}
if (kickAlert == null || !kickAlert.ContainsKey(player.userID)) return;
String messageKey = playerInfo.IsCooldown() ? "ALERT_ACCEPT_KICK_PLAYER_COOLDOWN" : "ALERT_ACCEPT_KICK_PLAYER";
SendChat(GetLang(messageKey, player.UserIDString, kickAlert[player.userID], playerInfo.GetLimit(), FormatTime(playerInfo.GetCooldown())), player);
kickAlert.Remove(player.userID);
}
private void SendChat(String Message, BasePlayer player, ConVar.Chat.ChatChannel channel = ConVar.Chat.ChatChannel.Global) {
if (IQChat) IQChat?.Call("API_ALERT_PLAYER", player, Message, config.referencePlugins.iqchatPreset.customPrefix, config.referencePlugins.iqchatPreset.customAvatar);
else player.SendConsoleCommand("chat.add", channel, 0, Message);
}
private void OnTeamAcceptInvite(RelationshipManager.PlayerTeam team, BasePlayer target) {
if (!activeInvite.TryGetValue(target, out BasePlayer inviter)) return;
PlayerInfo iData = PlayerInfo.Get(inviter.UserIDString);
if (iData == null) return;
PlayerInfo tData = PlayerInfo.Get(target.UserIDString);
if (tData == null) return;
Int32 cooldown = config.GetCooldown();
iData.cooldownAddedTeams = cooldown;
tData.cooldownAddedTeams = 0;
iData.addTeamCount++;
tData.addTeamCount++;
activeInvite.Remove(target);
SendChat(GetLang("ALERT_ACCEPT_TEAM", target.UserIDString, tData.GetLimit()), target);
SendChat(GetLang("ALERT_ACCEPT_TEAM_INVITER", inviter.UserIDString, iData.GetLimit()), inviter);
String logMessageAccepted = LanguageEn ? $"{target.displayName}({target.UserIDString}) has accepted the team invitation from {inviter.displayName}({inviter.UserIDString})." : $"{target.displayName}({target.UserIDString}) принял приглашение в команду от {inviter.displayName}({inviter.UserIDString})";
LoggedProcess(ref logMessageAccepted);
}
public static String FormatTime(in Single source) {
StringBuilder stringBuilder = Pool.Get<StringBuilder>();
TimeSpan ts = TimeSpan.FromSeconds(source);
try {
String str;
if (ts.TotalHours >= 24) {
str = stringBuilder.AppendFormat("{0:D2}:{1:D2}:{2:D2}", (Int32)ts.TotalHours, ts.Minutes, ts.Seconds).ToString();
}
else if (ts.Hours > 0) {
str = stringBuilder.AppendFormat("{0:D2}:{1:D2}:{2:D2}", ts.Hours, ts.Minutes, ts.Seconds).ToString();
}
else {
str = stringBuilder.AppendFormat("{0:D2}:{1:D2}", ts.Minutes, ts.Seconds).ToString();
}
return str;
}
finally {
stringBuilder.Clear();
Pool.FreeUnmanaged(ref stringBuilder);
}
}
private void ReadData() => kickAlert = Oxide.Core.Interface.Oxide.DataFileSystem.ReadObject<Dictionary<UInt64, String>>("IQSystem/IQTeamLimitter/AlertPlayersAfterKick");
private void Request(String url, String payload, Action<Int32> callback = null) {
Dictionary<String, String> header = new Dictionary<String, String>();
header.Add("Content-Type", "application/json");
webrequest.Enqueue(url, payload, (code, response) => {
if (code != 200 && code != 204) {
if (response != null) {
try {
JObject json = JObject.Parse(response);
if (code != 429 || response.Contains("key:73888965")) PrintWarning($"Discord rejected that payload! Responded with \"{json["message"].ToString()}\" Code: {code}");
}
catch {
PrintWarning($"Failed to get a valid response from discord! Error: \"{response}\" Code: {code}");
}
}
else {
PrintWarning($"Discord didn't respond (down?) Code: {code}");
}
}
try {
callback?.Invoke(code);
}
catch (Exception ex) {
}
}
, this, RequestMethod.POST, header);
}
private new void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<String, String> {
["INFO_ALERT"]
= "Limit on joining and inviting to the team: <color=#738D45>{0}</color>", ["INFO_ALERT_PLUS_COOLDOWN"] = "Limit on joining and inviting to the team: <color=#738D45>{0}</color>\nCooldown before the next joining: <color=#CD412B>{1}</color>", ["INFO_ALERT_CREATE_TEAM"] = "Вы исчерпали командный лимит, вам запрещено создавать команды!", ["ALERT_ACCEPT_KICK_PLAYER_COOLDOWN"] = "<color=#1F6BA0>{0}</color> kicked you from the team.\n\nRemaining available joining limits: <color=#738D45>{1}</color>\nCooldown before the next joining: <color=#CD412B>{2}</color>", ["ALERT_ACCEPT_KICK_PLAYER"] = "<color=#1F6BA0>{0}</color> kicked you from the team.\n\nRemaining available joining limits: <color=#738D45>{1}</color>", ["ALERT_ACCEPT_KICK_LEADER"] = "You have successfully removed a player from the team.\n\nRemaining available invitation limits: <color=#738D45>{0}</color>\nCooldown before the next invitation: <color=#CD412B>{1}</color>", ["ALERT_ACCEPT_TEAM_INVITER"] = "The player has successfully joined your team.\n\nRemaining available invitation limits: <color=#738D45>{0}</color>\n\nNote: if you remove the player or leave the team, you will receive a temporary invitation ban and lose one limit point.", ["ALERT_ACCEPT_TEAM"] = "You have successfully joined the team.\n\nRemaining available joining limits for another team: <color=#738D45>{0}</color>\n\nNote: if you leave the team or are removed, you will receive a temporary joining ban and lose one limit point.", ["ALERT_LEAVE_TEAM"] = "You have left the team. You will be able to join a new one in <color=#CD412B>{0}</color>.\n\nRemaining available joining limits for another team: <color=#738D45>{1}</color>", ["ALERT_COOLDOWN_TARGET_TEAM_INVITE"] = "Cannot invite player to the team: they cannot accept invitations for another <color=#CD412B>{0}</color>.", ["ALERT_COOLDOWN_TEAM_INVITE"] = "You cannot invite players to the team.\n\nPlease wait another <color=#CD412B>{0}</color>.", ["ALERT_LIMITED_TEAM"] = "Your limit for joining teams is exhausted.\n\n<color=#CD412B>You are prohibited from joining and inviting players until the next wipe.</color>", ["ALERT_LIMITED_TEAM_PLAYER"] = "<color=#1F6BA0>{0}</color> has exhausted their limit for joining teams.\n\n<color=#CD412B>They are prohibited from joining and inviting players until the next wipe.</color>",
}
, this);
lang.RegisterMessages(new Dictionary<String, String> {
["INFO_ALERT"]
= "Лимит на вступления и приглашения в команду: <color=#738D45>{0}</color>", ["INFO_ALERT_CREATE_TEAM"] = "Вы исчерпали командный лимит, вам запрещено создавать команды!", ["INFO_ALERT_PLUS_COOLDOWN"] = "Лимит на вступления и приглашения в команду: <color=#738D45>{0}</color>\nВремя ожидания до следующего вступления: <color=#CD412B>{1}</color>", ["ALERT_ACCEPT_KICK_PLAYER_COOLDOWN"] = "<color=#1F6BA0>{0}</color> исключил вас из команды.\n\nОсталось доступных лимитов для вступления: <color=#738D45>{1}</color>\nВремя ожидания до следующего вступления: <color=#CD412B>{2}</color>", ["ALERT_ACCEPT_KICK_PLAYER"] = "<color=#1F6BA0>{0}</color> исключил вас из команды.\n\nОсталось доступных лимитов для вступления: <color=#738D45>{1}</color>", ["ALERT_ACCEPT_KICK_LEADER"] = "Вы успешно исключили игрока из команды.\n\nОсталось доступных лимитов для приглашения: <color=#738D45>{0}</color>\nВремя ожидания до следующего приглашения: <color=#CD412B>{1}</color>", ["ALERT_ACCEPT_TEAM_INVITER"] = "Игрок успешно присоединился к вашей команде.\n\nОсталось доступных лимитов для приглашения: <color=#738D45>{0}</color>\n\nВажно: если вы удалите игрока или покинете команду, вы получите временную блокировку на приглашения и потеряете единицу лимита.", ["ALERT_ACCEPT_TEAM"] = "Вы успешно вступили в команду.\n\nОсталось доступных лимитов для вступления в другую команду: <color=#738D45>{0}</color>\n\nВажно: если вы покинете команду или вас исключат, вы получите временную блокировку на вступление и потеряете единицу лимита.", ["ALERT_LEAVE_TEAM"] = "Вы покинули команду. Вы сможете вступить в новую через <color=#CD412B>{0}</color>.\n\nОсталось доступных лимитов для вступления в другую команду: <color=#738D45>{1}</color>", ["ALERT_COOLDOWN_TARGET_TEAM_INVITE"] = "Невозможно пригласить игрока в команду: он не может принимать заявки ещё <color=#CD412B>{0}</color>.", ["ALERT_COOLDOWN_TEAM_INVITE"] = "Вы не можете приглашать игроков в команду.\n\nПожалуйста, подождите ещё <color=#CD412B>{0}</color>.", ["ALERT_LIMITED_TEAM"] = "Ваш лимит на объединение в команды исчерпан.\n\n<color=#CD412B>Вам запрещено присоединяться и приглашать игроков до следующего вайпа.</color>", ["ALERT_LIMITED_TEAM_PLAYER"] = "<color=#1F6BA0>{0}</color> исчерпал свой лимит на объединение в команды.\n\n<color=#CD412B>Ему запрещено присоединяться и приглашать игроков до следующего вайпа.</color>",
}
, this, "ru");
}
private class BotMessage {
[JsonProperty("content")]
public String Content {
get;
set;
}
[JsonProperty("username")]
public String Username {
get;
set;
}
[JsonProperty("avatar_url")]
public String AvatarUrl {
get;
set;
}
}
private void LoggedProcess(ref String message) {
message = $"{DateTime.UtcNow:[dd.MM.yyyy-HH:mm]} {message}";
LogToFile(message);
SendDiscord(message);
}
private Object OnTeamMemberInvite(PlayerTeam playerTeam, BasePlayer inviter, UInt64 targetId) {
PlayerInfo iData = PlayerInfo.Get(inviter.UserIDString);
if (iData == null) return null;
BasePlayer target = BasePlayer.FindByID(targetId);
if(target == null) return null;
PlayerInfo tData = PlayerInfo.Get(target.UserIDString);
if (tData == null) return null;
Boolean isCooldownLog = IsCooldownLog(target);
if (!isCooldownLog) {
String logMessageTryInvite = LanguageEn ? $"{inviter.displayName}({inviter.UserIDString}) is trying to invite {target.displayName}({target.UserIDString}) to join their team." : $"{inviter.displayName}({inviter.UserIDString}) пытается пригласить в команду {target.displayName}({target.UserIDString})";
LoggedProcess(ref logMessageTryInvite);
}
if (iData.IsLimit()) {
SendChat(GetLang("ALERT_LIMITED_TEAM", inviter.UserIDString), inviter);
return false;
}
if (tData.IsLimit()) {
SendChat(GetLang("ALERT_LIMITED_TEAM_PLAYER", inviter.UserIDString, target.displayName), inviter);
return false;
}
if (iData.IsCooldown()) {
SendChat(GetLang("ALERT_COOLDOWN_TEAM_INVITE", inviter.UserIDString, FormatTime(iData.GetCooldown())), inviter);
return false;
}
if (tData.IsCooldown()) {
SendChat(GetLang("ALERT_COOLDOWN_TARGET_TEAM_INVITE", inviter.UserIDString, FormatTime(tData.GetCooldown())), inviter);
return false;
}
activeInvite.TryAdd(target, inviter);
if (!isCooldownLog) {
String logMessageInvited = LanguageEn ? $"{inviter.displayName}({inviter.UserIDString}) has invited {target.displayName}({target.UserIDString}) to the team." : $"{inviter.displayName}({inviter.UserIDString}) пригласил в команду {target.displayName}({target.UserIDString})";
LoggedProcess(ref logMessageInvited);
}
return null;
}
[ChatCommand("info.team")]
private void InfoTeamChatCommand(BasePlayer player) {
if (player == null) return;
PlayerInfo pData = PlayerInfo.Get(player.UserIDString);
if (pData == null) return;
String messageKey = pData.IsCooldown() ? "INFO_ALERT_PLUS_COOLDOWN" : "INFO_ALERT";
String message = GetLang(messageKey, player.UserIDString, pData.GetLimit(), FormatTime(pData.GetCooldown()));
SendChat(message, player);
player.ConsoleMessage(message);
}
private void LogToFile(in String message) {
if (!config.logController.logToFile) return;
LogToFile("TeamLogs", message, this, true, true);
}
private void SaveDataFiles() {
foreach (KeyValuePair<String, PlayerInfo> player in PlayerInfo._players) PlayerInfo.Save(player.Key);
PlayerInfo._players.Clear();
}
private void OnTeamKick(RelationshipManager.PlayerTeam team, BasePlayer player, UInt64 targetID) {
PlayerInfo pData = PlayerInfo.Get(player.UserIDString);
if (pData == null) return;
String targetIDString = targetID.ToString();
PlayerInfo tData = PlayerInfo.Get(targetIDString);
if (tData == null) return;
pData.addTeamCount++;
pData.cooldownAddedTeams = config.GetCooldown();
tData.addTeamCount++;
tData.cooldownAddedTeams = config.GetCooldown();
BasePlayer target = BasePlayer.FindByID(targetID);
if (target == null) kickAlert.TryAdd(targetID, player.displayName);
else SendChat(GetLang("ALERT_ACCEPT_KICK_PLAYER_COOLDOWN", targetIDString, player.displayName, tData.GetLimit(), FormatTime(tData.GetCooldown())), target);
SendChat(GetLang("ALERT_ACCEPT_KICK_LEADER", player.UserIDString, pData.GetLimit(), FormatTime(pData.GetCooldown())), player);
String logMessageKick = LanguageEn ? $"{player.displayName}({player.UserIDString}) has kicked {(target != null ? target.displayName : "Unknown")}({targetID}) from the team." : $"{player.displayName}({player.UserIDString}) кикнул из команды {(target != null ? target.displayName : "Unknown")}({targetID})";
LoggedProcess(ref logMessageKick);
}
private String GetLang(in String LangKey, in String userID = null, params Object[] args) {
if (args == null) return lang.GetMessage(LangKey, this, userID);
StringBuilder sb = Pool.Get<StringBuilder>();
try {
sb.AppendFormat(lang.GetMessage(LangKey, this, userID), args);
return sb.ToString();
}
finally {
sb.Clear();
Pool.FreeUnmanaged(ref sb);
}
}
/// <summary>
/// - Исправлена ошибка в OnPlayerConnected, когда игрок впервые зашел на сервер и его данные некорректно обработались
/// - Обновлен хук, который устареет после 31 декабря
/// </summary>
private const Boolean LanguageEn = false;
private void DetectedWipe() {
List<String> playersID = Pool.GetList<String>();
playersID.AddRange(PlayerInfo._players.Keys);
foreach (String playerKeys in playersID) PlayerInfo.Remove(playerKeys);
Pool.FreeList(ref playersID);
if (!config.clearAllTeamAfterWipe) return;
RelationshipManager manager = RelationshipManager.ServerInstance;
Int32 countTeams = manager ? manager.teams.Count : 0;
Puts(LanguageEn ? $"{countTeams} player teams have been removed." : $"Удалено {countTeams} команд игроков");
if (manager == null) return;
manager.cachedPlayers.Clear();
manager.playerToTeam.Clear();
manager.relationships.Clear();
manager.teams.Clear();
manager.SendNetworkUpdate();
}
private void Unload() {
WriteData();
SaveDataFiles();
if (inviteCooldownLog != null && inviteCooldownLog.Count != 0) inviteCooldownLog.Clear();
if (activeInvite != null && activeInvite.Count != 0) activeInvite.Clear();
}
private void SendDiscord(in String message) {
Configuration.LogController.LogToDiscord discordController = config.logController.discordLogSetting;
if (discordController.webhooksDiscord == null || String.IsNullOrWhiteSpace(discordController.webhooksDiscord)) return;
BotMessage newMessage = new BotMessage {
Content = message, Username = discordController.nameDiscord, AvatarUrl = discordController.avatartDiscord,
}
;
String jsonPayload = JsonConvert.SerializeObject(newMessage, Formatting.Indented);
Request(discordController.webhooksDiscord, jsonPayload);
}
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) LoadDefaultConfig();
}
catch {
PrintWarning(LanguageEn ? $"Error reading #321562 configuration 'oxide/config/{Name}', creating a new configuration!!" : $"Ошибка чтения #3212 конфигурации 'oxide/config/{Name}', создаём новую конфигурацию!!");
LoadDefaultConfig();
}
NextTick(SaveConfig);
}
[ConsoleCommand("tinfo")]
private void TIFConsoleCMD(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Player();
if(player != null) if (!player.IsAdmin) return;
if (!arg.HasArgs(1)) {
Puts(LanguageEn ? "Use the correct syntax : tinfo Steam64ID" : "Используйте корректный синтаксис : tinfo Steam64ID");
return;
}
String userID = arg.Args[0];
if (!userID.IsSteamId()) {
Puts(LanguageEn ? "The Steam64ID is specified incorrectly" : "Неверно указан Steam64ID");
return;
}
PlayerInfo pData = PlayerInfo.Get(userID);
if (pData == null) {
Puts(LanguageEn ? "Such a player does not exist in the database" : "Такой игрок не существует в базе данных");
return;
}
String cooldownInfo = pData.GetCooldown() <= 0 ? (LanguageEn ? "missing" : "отсутствует") : FormatTime(pData.GetCooldown());
Puts(LanguageEn ? $"Information about player {userID}:\nLimits: {pData.addTeamCount}/{config.limitTeam}\nCooldown: {cooldownInfo}" : $"Информация о игроке {userID}:\nЛимиты : {pData.addTeamCount}/{config.limitTeam}\nПерезарядка : {cooldownInfo}");
}
}
}