C#:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Oxide.Plugins {
using ClansEx;
[Info("Clans", "k1lly0u", "0.2.10")]
public class Clans : CovalencePlugin {
#region Fields
private bool isInitialized = false;
private Regex hexFilter = new Regex("^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
public static Clans Instance {
get;
private set;
}
private static readonly DateTime Epoch = new DateTime(1970, 1, 1);
private static readonly double MaxUnixSeconds = (DateTime.MaxValue - Epoch).TotalSeconds;
private const string COLORED_LABEL = "[{0}]{1}[/#]";
#endregion
#region Oxide Hooks
private void Loaded() {
Instance = this;
LoadData();
}
protected override void LoadDefaultMessages() => lang.RegisterMessages(Messages, this);
private void OnServerInitialized() {
if (!configData.Tags.Enabled) Unsubscribe(nameof(OnPluginLoaded));
InitializeClans();
}
private void OnPluginLoaded(Plugin plugin) {
if (configData.Tags.Enabled && plugin?.Title == "Better Chat") Interface.CallHook("API_RegisterThirdPartyTitle", this, new Func<IPlayer, string>(BetterChat_FormattedClanTag));
}
private void OnUserConnected(IPlayer player) {
Clan clan = storedData?.FindClanByID(player.Id);
if (clan != null) {
clan.OnPlayerConnected(player);
}
else {
List<string> invites;
if (storedData.playerInvites.TryGetValue(player.Id, out invites)) {
player.Reply(string.Format(Message("Notification.PendingInvites", player.Id), invites.ToSentence(), "clan"));
}
}
}
private void OnUserDisconnected(IPlayer player) => storedData?.FindClanByID(player.Id)?.OnPlayerDisconnected(player);
private void Unload() {
SaveData();
Instance = null;
}
#endregion
#region Functions
private void InitializeClans() {
Puts("Initializing Clans...");
List<string> purgedClans = ListPool.Get<string>();
foreach (KeyValuePair<string, Clan> kvp in storedData.clans) {
Clan clan = kvp.Value;
if (clan.ClanMembers.Count == 0 || (configData.Purge.Enabled && UnixTimeStampUTC() - clan.LastOnlineTime > (configData.Purge.OlderThanDays * 86400))) {
purgedClans.Add(kvp.Key);
continue;
}
if (configData.Clans.Alliance.Enabled) {
for (int i = clan.AllianceInvites.Count - 1; i >= 0; i--) {
KeyValuePair<string, double> allianceInvite = clan.AllianceInvites.ElementAt(i);
if (!storedData.clans.ContainsKey(allianceInvite.Key) || (UnixTimeStampUTC() - allianceInvite.Value > configData.Clans.Invites.AllianceInviteExpireTime)) clan.AllianceInvites.Remove(allianceInvite.Key);
}
for (int i = clan.Alliances.Count - 1; i >= 0; i--) {
string allyTag = clan.Alliances.ElementAt(i);
if (!storedData.clans.ContainsKey(allyTag)) clan.Alliances.Remove(allyTag);
}
}
for (int i = clan.MemberInvites.Count - 1; i >= 0; i--) {
KeyValuePair<string, Clan.MemberInvite> memberInvite = clan.MemberInvites.ElementAt(i);
if (UnixTimeStampUTC() - memberInvite.Value.ExpiryTime > configData.Clans.Invites.MemberInviteExpireTime) clan.MemberInvites.Remove(memberInvite.Key);
}
foreach (KeyValuePair<string, Clan.Member> member in clan.ClanMembers) storedData.RegisterPlayer(member.Key, clan.Tag);
}
if (purgedClans.Count > 0) {
Puts($"Purging {purgedClans.Count} expired or invalid clans");
StringBuilder str = new StringBuilder();
for (int i = 0; i < purgedClans.Count; i++) {
string tag = purgedClans[i];
Clan clan = storedData.clans[tag];
if (clan == null) continue;
str.Append($"{(i > 0 ? "\n" : "")}Purged - [{tag}] | {clan.Description} | Owner: {clan.OwnerID} | Last Online: {UnixTimeStampToDateTime(clan.LastOnlineTime)}");
storedData.clans.Remove(tag);
}
if (configData.Purge.ListPurgedClans) {
Puts(str.ToString());
if (configData.Options.LogChanges) LogToFile(Title, str.ToString(), this);
}
}
Puts($"Loaded {storedData.clans.Count} clans!");
ListPool.Free(ref purgedClans);
if (configData.Tags.Enabled) Interface.CallHook("API_RegisterThirdPartyTitle", this, new Func<Oxide.Core.Libraries.Covalence.IPlayer, string>(BetterChat_FormattedClanTag));
isInitialized = true;
foreach (IPlayer player in players.Connected) OnUserConnected(player);
TimedSaveData();
}
private bool ClanTagExists(string tag) {
ICollection<string> collection = storedData.clans.Keys;
for (int i = 0; i < collection.Count; i++) {
if (collection.ElementAt(i).Equals(tag, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
private string FormatTime(double time) {
TimeSpan dateDifference = TimeSpan.FromSeconds((float)time);
int days = dateDifference.Days;
int hours = dateDifference.Hours + (days * 24);
int mins = dateDifference.Minutes;
int secs = dateDifference.Seconds;
if (hours > 0) return string.Format("{0:00}h:{1:00}m:{2:00}s", hours, mins, secs);
else if (mins > 0) return string.Format("{0:00}m:{1:00}s", mins, secs);
else return string.Format("{0:00}s", secs);
}
private string BetterChat_FormattedClanTag(IPlayer player) {
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) return string.Empty;
return $"[#{(string.IsNullOrEmpty(clan.TagColor) || !configData.Tags.CustomColors ? configData.Tags.TagColor.Replace("#", "") : clan.TagColor.Replace("#", ""))}][+{configData.Tags.TagSize}]{configData.Tags.TagOpen}{clan.Tag}{configData.Tags.TagClose}[/+][/#]";
}
private static int UnixTimeStampUTC() => (int)DateTime.UtcNow.Subtract(Epoch).TotalSeconds;
private static DateTime UnixTimeStampToDateTime(double unixTimeStamp) {
return unixTimeStamp > MaxUnixSeconds ? Epoch.AddMilliseconds(unixTimeStamp) : Epoch.AddSeconds(unixTimeStamp);
}
#endregion
#region Clan Management
[HookMethod("CreateClan")]
public void CreateClan(IPlayer player, string tag, string description) {
if (player == null) return;
if (storedData.FindClanByID(player.Id) != null) {
player.Reply(Message("Notification.Create.InExistingClan", player.Id));
return;
}
if (tag.Length < configData.Tags.TagLength.Minimum || tag.Length > configData.Tags.TagLength.Maximum) {
player.Reply(string.Format(Message("Notification.Create.InvalidTagLength", player.Id), configData.Tags.TagLength.Minimum, configData.Tags.TagLength.Maximum));
return;
}
if (ClanTagExists(tag)) {
player.Reply(Message("Notification.Create.ClanExists", player.Id));
return;
}
storedData.clans[tag] = new Clan(player, tag, description);
storedData.RegisterPlayer(player.Id, tag);
player.Reply(string.Format(Message("Notification.Create.Success", player.Id), tag));
Interface.CallHook("OnClanCreate", tag);
if (configData.Options.LogChanges) LogToFile(Title, $"{player.Name} created the clan [{tag}]", this);
}
[HookMethod("InvitePlayer")]
public bool InvitePlayer(IPlayer inviter, string targetId) {
IPlayer invitee = covalence.Players.FindPlayerById(targetId) ?? null;
if (invitee == null) {
inviter.Reply(string.Format(Message("Notification.Generic.UnableToFindPlayer", inviter.Id), targetId));
return false;
}
return InvitePlayer(inviter, invitee);
}
[HookMethod("InvitePlayer")]
public bool InvitePlayer(IPlayer inviter, IPlayer invitee) {
if (inviter == null || invitee == null) return false;
Clan clan = storedData.FindClanByID(inviter.Id);
if (clan == null) {
inviter.Reply(Message("Notification.Generic.NoClan", inviter.Id));
return false;
}
Clan other = storedData.FindClanByID(invitee.Id);
if (other != null) {
inviter.Reply(string.Format(Message("Notification.Invite.InClan", inviter.Id), invitee.Name));
return false;
}
return clan.InvitePlayer(inviter, invitee);
}
[HookMethod("WithdrawInvite")]
public bool WithdrawInvite(IPlayer player, string partialNameOrID) {
if (player == null) return false;
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
player.Reply(Message("Notification.Generic.NoClan", player.Id));
return false;
}
if (!clan.IsOwner(player.Id) && !clan.IsModerator(player.Id)) {
player.Reply(Message("Notification.WithdrawInvite.NoPermissions", player.Id));
return false;
}
foreach (KeyValuePair<string, Clan.MemberInvite> invite in clan.MemberInvites) {
if (partialNameOrID.Equals(invite.Key) || invite.Value.DisplayName.Contains(partialNameOrID)) {
storedData.RevokePlayerInvite(partialNameOrID, clan.Tag);
clan.MemberInvites.Remove(invite.Key);
clan.Broadcast("Notification.WithdrawInvite.Success", player.Name, invite.Value.DisplayName);
return true;
}
}
player.Reply(string.Format(Message("Notification.WithdrawInvite.UnableToFind", player.Id), partialNameOrID));
return false;
}
[HookMethod("RejectInvite")]
public bool RejectInvite(IPlayer player, string tag) {
if (player == null) return false;
Clan clan = storedData.FindClan(tag);
if (clan == null) {
player.Reply(string.Format(Message("Notification.Generic.InvalidClan", player.Id), tag));
return false;
}
if (!clan.MemberInvites.ContainsKey(player.Id)) {
player.Reply(string.Format(Message("Notification.RejectInvite.InvalidInvite", player.Id), tag));
return false;
}
clan.MemberInvites.Remove(player.Id);
storedData.OnInviteRejected(player.Id, clan.Tag);
clan.Broadcast("Notification.RejectInvite.Reply", player.Name);
player.Reply(string.Format(Message("Notification.RejectInvite.PlayerMessage", player.Id), tag));
if (configData.Options.LogChanges) Instance.LogToFile(Instance.Title, $"{player.Name} rejected their invite to [{tag}]", Instance);
return true;
}
[HookMethod("JoinClan")]
public bool JoinClan(IPlayer player, string tag) {
if (player == null || string.IsNullOrEmpty(tag)) return false;
Clan clan = storedData.FindClanByID(player.Id);
if (clan != null) {
player.Reply(Message("Notification.Join.InExistingClan", player.Id));
return false;
}
clan = storedData.FindClan(tag);
if (clan == null) return false;
return clan.JoinClan(player);
}
[HookMethod("LeaveClan")]
public bool LeaveClan(IPlayer player) {
if (player == null) return false;
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
player.Reply(Message("Notification.Generic.NoClan", player.Id));
return false;
}
return clan.LeaveClan(player);
}
[HookMethod("KickPlayer")]
public bool KickPlayer(IPlayer player, string playerId) {
if (player == null) return false;
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
player.Reply(Message("Notification.Generic.NoClan", player.Id));
return false;
}
return clan.KickMember(player, playerId);
}
[HookMethod("PromotePlayer")]
public bool PromotePlayer(IPlayer promoter, string targetId) {
if (promoter == null) return false;
Clan clan = storedData.FindClanByID(promoter.Id);
if (clan == null) {
promoter.Reply(Message("Notification.Generic.NoClan", promoter.Id));
return false;
}
Clan other = storedData.FindClanByID(targetId);
if (other == null || !clan.Tag.Equals(other.Tag)) {
string Name = covalence.Players.FindPlayer(targetId)?.Name ?? targetId;
promoter.Reply(string.Format(Message("Notification.Promotion.TargetNoClan", promoter.Id), Name));
return false;
}
return clan.PromotePlayer(promoter, targetId);
}
[HookMethod("DemotePlayer")]
public bool DemotePlayer(IPlayer demoter, string targetId) {
if (demoter == null) return false;
Clan clan = storedData.FindClanByID(demoter.Id);
if (clan == null) {
demoter.Reply(Message("Notification.Generic.NoClan", demoter.Id));
return false;
}
Clan other = storedData.FindClanByID(targetId);
if (other == null || !clan.Tag.Equals(other.Tag)) {
string Name = covalence.Players.FindPlayer(targetId)?.Name ?? targetId;
demoter.Reply(string.Format(Message("Notification.Promotion.TargetNoClan", demoter.Id), Name));
return false;
}
return clan.DemotePlayer(demoter, targetId);
}
[HookMethod("DisbandClan")]
public bool DisbandClan(IPlayer player) {
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
player.Reply(Message("Notification.Generic.NoClan", player.Id));
return false;
}
if (!clan.IsOwner(player.Id)) {
player.Reply(Message("Notification.Disband.NotOwner", player.Id));
return false;
}
string tag = clan.Tag;
clan.Broadcast("Notification.Disband.Reply", Array.Empty<object>());
clan.DisbandClan();
player.Reply(string.Format(Message("Notification.Disband.Success", player.Id), tag));
return true;
}
#endregion
#region Alliance Management
[HookMethod("OfferAlliance")]
public bool OfferAlliance(IPlayer player, string tag) {
if (!configData.Clans.Alliance.Enabled) return false;
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
player.Reply(Message("Notification.Generic.NoClan", player.Id));
return false;
}
Clan alliedClan = storedData.FindClan(tag);
if (alliedClan == null) {
player.Reply(string.Format(Message("Notification.Generic.InvalidClan", player.Id), tag));
return false;
}
if (!clan.IsOwner(player.Id)) {
player.Reply(Message("Notification.Alliance.NoPermissions", player.Id));
return false;
}
if (clan.AllianceInvites.ContainsKey(tag) && (UnixTimeStampUTC() - clan.AllianceInvites[tag] < configData.Clans.Invites.AllianceInviteExpireTime)) {
player.Reply(string.Format(Message("Notification.Alliance.PendingInvite", player.Id), tag));
return false;
}
if (clan.AllianceInviteCount >= configData.Clans.Invites.AllianceInviteLimit) {
player.Reply(Message("Notification.Alliance.MaximumInvites", player.Id));
return false;
}
if (clan.AllianceCount >= configData.Clans.Alliance.AllianceLimit) {
player.Reply(Message("Notification.Alliance.MaximumAlliances", player.Id));
return false;
}
clan.AllianceInvites[tag] = UnixTimeStampUTC();
alliedClan.IncomingAlliances.Add(clan.Tag);
player.Reply(string.Format(Message("Notification.Alliance.InviteSent", player.Id), tag, FormatTime(configData.Clans.Invites.AllianceInviteExpireTime)));
alliedClan.Broadcast("Notification.Alliance.InviteReceived", clan.Tag, FormatTime(configData.Clans.Invites.AllianceInviteExpireTime), "ally");
return true;
}
[HookMethod("WithdrawAlliance")]
public bool WithdrawAlliance(IPlayer player, string tag) {
if (!configData.Clans.Alliance.Enabled) return false;
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
player.Reply(Message("Notification.Generic.NoClan", player.Id));
return false;
}
Clan alliedClan = storedData.FindClan(tag);
if (alliedClan == null) {
player.Reply(string.Format(Message("Notification.Generic.InvalidClan", player.Id), tag));
return false;
}
if (!clan.IsOwner(player.Id)) {
player.Reply(Message("Notification.Alliance.NoPermissions", player.Id));
return false;
}
if (!clan.AllianceInvites.ContainsKey(tag)) {
player.Reply(string.Format(Message("Notification.Alliance.NoActiveInvite", player.Id), tag));
return false;
}
clan.AllianceInvites.Remove(tag);
alliedClan.IncomingAlliances.Remove(clan.Tag);
clan.Broadcast("Notification.Alliance.WithdrawnClan", player.Name, tag);
alliedClan.Broadcast("Notification.Alliance.WithdrawnTarget", clan.Tag);
clan.MarkDirty();
return true;
}
[HookMethod("AcceptAlliance")]
public bool AcceptAlliance(IPlayer player, string tag) {
if (!configData.Clans.Alliance.Enabled) return false;
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
player.Reply(Message("Notification.Generic.NoClan", player.Id));
return false;
}
Clan alliedClan = storedData.FindClan(tag);
if (alliedClan == null) {
player.Reply(string.Format(Message("Notification.Generic.InvalidClan", player.Id), tag));
return false;
}
if (!clan.IsOwner(player.Id)) {
player.Reply(Message("Notification.Alliance.NoPermissions", player.Id));
return false;
}
bool noActiveInvite = !alliedClan.AllianceInvites.ContainsKey(clan.Tag);
if ((UnixTimeStampUTC() - alliedClan.AllianceInvites[clan.Tag] > configData.Clans.Invites.AllianceInviteExpireTime)) {
alliedClan.AllianceInvites.Remove(clan.Tag);
noActiveInvite = true;
}
if (noActiveInvite) {
player.Reply(string.Format(Message("Notification.Alliance.NoActiveInviteFrom", player.Id), tag));
return false;
}
if (alliedClan.AllianceCount >= configData.Clans.Alliance.AllianceLimit) {
player.Reply(string.Format(Message("Notification.Alliance.AtLimitTarget", player.Id), tag));
return false;
}
if (clan.AllianceCount >= configData.Clans.Alliance.AllianceLimit) {
player.Reply(string.Format(Message("Notification.Alliance.AtLimitSelf", player.Id), tag));
return false;
}
clan.Alliances.Add(tag);
clan.IncomingAlliances.Remove(tag);
alliedClan.Alliances.Add(clan.Tag);
alliedClan.AllianceInvites.Remove(clan.Tag);
clan.MarkDirty();
alliedClan.MarkDirty();
clan.Broadcast("Notification.Alliance.Formed", clan.Tag, alliedClan.Tag);
alliedClan.Broadcast("Notification.Alliance.Formed", clan.Tag, alliedClan.Tag);
Interface.Oxide.CallHook("OnClanUpdate", clan.Tag);
Interface.Oxide.CallHook("OnClanUpdate", alliedClan.Tag);
Interface.Oxide.CallHook("OnClanAllianceCreated", clan.Tag, alliedClan.Tag);
return true;
}
[HookMethod("RejectAlliance")]
public bool RejectAlliance(IPlayer player, string tag) {
if (!configData.Clans.Alliance.Enabled) return false;
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
player.Reply(string.Format(Message("Notification.Generic.InvalidClan", player.Id), tag));
return false;
}
Clan alliedClan = storedData.FindClan(tag);
if (alliedClan == null) {
player.Reply(Message("Notification.Generic.InvalidClan", player.Id));
return false;
}
if (!clan.IsOwner(player.Id)) {
player.Reply(Message("Notification.Alliance.NoPermissions", player.Id));
return false;
}
if (!alliedClan.AllianceInvites.ContainsKey(clan.Tag) || (UnixTimeStampUTC() - alliedClan.AllianceInvites[clan.Tag] > configData.Clans.Invites.AllianceInviteExpireTime)) {
player.Reply(string.Format(Message("Notification.Alliance.NoActiveInvite", player.Id), tag));
return false;
}
clan.IncomingAlliances.Remove(tag);
alliedClan.AllianceInvites.Remove(clan.Tag);
alliedClan.MarkDirty();
clan.Broadcast("Notification.Alliance.Rejected", clan.Tag, alliedClan.Tag);
alliedClan.Broadcast("Notification.Alliance.Rejected", clan.Tag, alliedClan.Tag);
return true;
}
[HookMethod("RevokeAlliance")]
public bool RevokeAlliance(IPlayer player, string tag) {
if (!configData.Clans.Alliance.Enabled) return false;
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
player.Reply(string.Format(Message("Notification.Generic.InvalidClan", player.Id), tag));
return false;
}
Clan alliedClan = storedData.FindClan(tag);
if (alliedClan == null) {
player.Reply(Message("Notification.Generic.InvalidClan", player.Id));
return false;
}
if (!clan.IsOwner(player.Id)) {
player.Reply(Message("Notification.Alliance.NoPermissions", player.Id));
return false;
}
if (!clan.Alliances.Contains(alliedClan.Tag)) {
player.Reply(string.Format(Message("Notification.Alliance.NoActiveAlliance", player.Id), alliedClan.Tag));
return false;
}
alliedClan.Alliances.Remove(clan.Tag);
clan.Alliances.Remove(alliedClan.Tag);
alliedClan.MarkDirty();
clan.MarkDirty();
clan.Broadcast("Notification.Alliance.Revoked", clan.Tag, alliedClan.Tag);
alliedClan.Broadcast("Notification.Alliance.Revoked", clan.Tag, alliedClan.Tag);
Interface.Oxide.CallHook("OnClanUpdate", clan.Tag);
Interface.Oxide.CallHook("OnClanUpdate", alliedClan.Tag);
Interface.Oxide.CallHook("OnClanAllianceDissolved", clan.Tag, alliedClan.Tag);
return true;
}
#endregion
#region Chat
private void ClanChat(IPlayer player, string message) {
if (player == null) return;
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) return;
string str = string.Format(Message("Chat.Alliance.Format"), clan.Tag, clan.GetRoleColor(player.Id), player.Name, message);
clan.Broadcast(string.Format(Message("Chat.Clan.Prefix"), str));
Interface.CallHook("OnClanChat", player, message, clan.Tag);
}
private void AllianceChat(IPlayer player, string message) {
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) return;
string str = string.Format(Message("Chat.Alliance.Format"), clan.Tag, clan.GetRoleColor(player.Id), player.Name, message);
clan.Broadcast(string.Format(Message("Chat.Alliance.Prefix"), str));
for (int i = 0; i < clan.AllianceCount; i++) {
Clan alliedClan = storedData.FindClan(clan.Alliances.ElementAt(i));
if (alliedClan != null) {
alliedClan.Broadcast(string.Format(Message("Chat.Alliance.Prefix"), str));
}
}
Interface.CallHook("OnAllianceChat", player, message, clan.Tag);
}
#endregion
#region Chat Commands
[Command("a")]
private void cmdAllianceChat(IPlayer player, string command, string[] args) {
if (!configData.Clans.Alliance.Enabled || args?.Length == 0) return;
AllianceChat(player, string.Join(" ", args));
}
[Command("c")]
private void cmdClanChat(IPlayer player, string command, string[] args) {
if (args?.Length == 0) return;
ClanChat(player, string.Join(" ", args));
}
[Command("cinfo")]
private void cmdChatClanInfo(IPlayer player, string command, string[] args) {
if (args?.Length == 0) {
player.Reply(Message("Notification.Generic.SpecifyClanTag", player.Id));
return;
}
Clan clan = storedData.FindClan(args[0]);
if (clan == null) {
player.Reply(string.Format(Message("Notification.Generic.InvalidClan", player.Id), args[0]));
return;
}
clan.PrintClanInfo(player);
}
[Command("clanhelp")]
private void cmdChatClanHelp(IPlayer player, string command, string[] args) {
StringBuilder sb = new StringBuilder();
Clan clan = storedData.FindClanByID(player.Id);
if (clan == null) {
sb.Append(Message("Notification.ClanInfo.Title", player.Id));
sb.Append(string.Format(Message("Notification.ClanHelp.NoClan", player.Id), "clan"));
player.Reply(sb.ToString());
return;
}
sb.Append(Message("Notification.ClanInfo.Title", player.Id));
sb.Append(string.Format(Message("Notification.ClanHelp.Basic", player.Id), "clan", "c"));
if (clan.IsModerator(player.Id) || clan.OwnerID.Equals(player.Id)) {
if (configData.Clans.Alliance.Enabled && clan.OwnerID.Equals(player.Id)) sb.Append(string.Format(Message("Notification.ClanHelp.Alliance", player.Id), "ally"));
sb.Append(string.Format(Message("Notification.ClanHelp.Moderator", player.Id), "clan"));
}
if (clan.OwnerID.Equals(player.Id)) {
sb.Append(string.Format(Message("Notification.ClanHelp.Owner", player.Id), "clan"));
if (configData.Tags.CustomColors) sb.Append(string.Format(Message("Notification.ClanHelp.TagColor", player.Id), "clan"));
}
player.Reply(sb.ToString());
}
[Command("ally")]
private void cmdChatClanAlly(IPlayer player, string command, string[] args) {
if (!configData.Clans.Alliance.Enabled) return;
if (args?.Length < 2) {
player.Reply(string.Format(Message("Notification.ClanHelp.Alliance", player.Id), "ally"));
return;
}
string tag = args[1];
switch (args[0].ToLower()) {
case "invite" : OfferAlliance(player, tag);
return;
case "withdraw" : WithdrawAlliance(player, tag);
return;
case "accept" : AcceptAlliance(player, tag);
return;
case "reject" : RejectAlliance(player, tag);
return;
case "revoke" : RevokeAlliance(player, tag);
return;
default : player.Reply(string.Format(Message("Notification.ClanHelp.Alliance", player.Id), "ally"));
return;
}
}
[Command("clan")]
private void cmdChatClan(IPlayer player, string command, string[] args) {
Clan clan = storedData.FindClanByID(player.Id);
if (args?.Length == 0) {
StringBuilder sb = new StringBuilder();
if (clan == null) {
sb.Append(Message("Notification.ClanInfo.Title", player.Id));
sb.Append(Message("Notification.Clan.NotInAClan", player.Id));
sb.Append(string.Format(Message("Notification.Clan.Help", player.Id), "clanhelp"));
player.Reply(sb.ToString());
sb.Clear();
}
else {
sb.Append(Message("Notification.ClanInfo.Title", player.Id));
sb.Append(string.Format(Message((clan.IsOwner(player.Id) ? "Notification.Clan.OwnerOf" : clan.IsModerator(player.Id) ? "Notification.Clan.ModeratorOf" : "Notification.Clan.MemberOf"), player.Id), clan.Tag, clan.OnlineCount, clan.MemberCount));
sb.Append(string.Format(Message("Notification.Clan.MembersOnline", player.Id), clan.GetMembersOnline()));
sb.Append(string.Format(Message("Notification.Clan.Help", player.Id), "clanhelp"));
player.Reply(sb.ToString());
sb.Clear();
}
return;
}
string tag = clan?.Tag ?? string.Empty;
switch (args[0].ToLower()) {
case "create" : if (args.Length < 2) {
player.Reply(string.Format(Message("Notification.Clan.CreateSyntax", player.Id), "clan"));
return;
}
CreateClan(player, args[1], args.Length > 2 ? string.Join(" ", args.Skip(2)) : string.Empty);
return;
case "leave" : LeaveClan(player);
return;
case "invite" : if (args.Length < 2) {
player.Reply(string.Format(Message("Notification.Clan.InviteSyntax", player.Id), "clan"));
return;
}
IPlayer invitee = players.FindPlayer(args[1]);
if (invitee == null) {
player.Reply(string.Format(Message("Notification.Generic.UnableToFindPlayer", player.Id), args[1]));
return;
}
if (invitee == player) {
player.Reply(Message("Notification.Generic.CommandSelf", player.Id));
return;
}
InvitePlayer(player, invitee);
return;
case "withdraw" : if (args.Length < 2) {
player.Reply(string.Format(Message("Notification.Clan.WithdrawSyntax", player.Id), "clan"));
return;
}
WithdrawInvite(player, args[1]);
return;
case "accept" : if (args.Length < 2) {
player.Reply(string.Format(Message("Notification.Clan.AcceptSyntax", player.Id), "clan"));
return;
}
JoinClan(player, args[1]);
return;
case "reject" : if (args.Length < 2) {
player.Reply(string.Format(Message("Notification.Clan.RejectSyntax", player.Id), "clan"));
return;
}
RejectInvite(player, args[1]);
return;
case "kick" : if (args.Length < 2) {
player.Reply(string.Format(Message("Notification.Clan.KickSyntax", player.Id), "clan"));
return;
}
string target = clan.FindPlayer(args[1]);
if (string.IsNullOrEmpty(target)) {
player.Reply(Message("Notification.Kick.NoPlayerFound", player.Id));
return;
}
if (target == player.Id) {
player.Reply(Message("Notification.Generic.CommandSelf", player.Id));
return;
}
KickPlayer(player, target);
return;
case "promote" : if (args.Length < 2) {
player.Reply(string.Format(Message("Notification.Clan.PromoteSyntax", player.Id), "clan"));
return;
}
string promotee = clan.FindPlayer(args[1]);
if (string.IsNullOrEmpty(promotee)) {
player.Reply(string.Format(Message("Notification.Generic.UnableToFindPlayer", player.Id), args[1]));
return;
}
if (promotee == player.Id) {
player.Reply(Message("Notification.Generic.CommandSelf", player.Id));
return;
}
PromotePlayer(player, promotee);
return;
case "demote" : if (args.Length < 2) {
player.Reply(string.Format(Message("Notification.Clan.DemoteSyntax", player.Id), "clan"));
return;
}
string demotee = clan.FindPlayer(args[1]);
if (string.IsNullOrEmpty(demotee)) {
player.Reply(string.Format(Message("Notification.Generic.UnableToFindPlayer", player.Id), args[1]));
return;
}
if (demotee == player.Id) {
player.Reply(Message("Notification.Generic.CommandSelf", player.Id));
return;
}
DemotePlayer(player, demotee);
return;
case "disband" : if (args.Length < 2 || !args[1].Equals("forever", StringComparison.OrdinalIgnoreCase)) {
player.Reply(string.Format(Message("Notification.Clan.DisbandSyntax", player.Id), "clan"));
return;
}
if (clan == null) {
player.Reply(Message("Notification.Generic.NoClan", player.Id));
return;
}
if (!clan.IsOwner(player.Id)) {
player.Reply(Message("Notification.Disband.NotOwner", player.Id));
return;
}
clan.Broadcast("Notification.Disband.Reply", Array.Empty<object>());
clan.DisbandClan();
player.Reply(string.Format(Message("Notification.Disband.Success", player.Id), tag));
return;
case "tagcolor" : if (!configData.Tags.CustomColors) {
player.Reply(Message("Notification.Clan.TagColorDisabled", player.Id));
return;
}
if (args.Length < 2) {
player.Reply(string.Format(Message("Notification.Clan.TagColorSyntax", player.Id), "clan"));
return;
}
if (!clan.IsOwner(player.Id)) {
player.Reply(Message("Notification.Disband.NotOwner", player.Id));
return;
}
string hexColor = args[1].ToUpper();
if (hexColor.Equals("RESET")) {
clan.TagColor = string.Empty;
player.Reply(Message("Notification.Clan.TagColorReset", player.Id));
return;
}
if (hexColor.Length < 6 || hexColor.Length > 6 || !hexFilter.IsMatch(hexColor)) {
player.Reply(Message("Notification.Clan.TagColorFormat", player.Id));
return;
}
clan.TagColor = hexColor;
player.Reply(string.Format(Message("Notification.Clan.TagColorSet", player.Id), clan.TagColor));
return;
default : player.Reply(string.Format(Message("Notification.Clan.Help", player.Id), "clanhelp"));
return;
}
}
#endregion
#region Hook Rewrite
private static readonly DateTime OnClanHookDeprecation = new DateTime(2027, 6, 1);
#if RUST
private static readonly List<ulong> RustMemberList = new List<ulong>();
#endif
private static readonly List<string> UniversalMemberList = new List<string>();
private static void OnClanMemberJoined(string tag, string joining, ICollection<string> members) {
const string HOOK_NAME = "OnClanMemberJoined";
const string NEW_HOOK = "OnClanMemberJoined(string tag, string joining, List<string> members)";
#if RUST
RustMemberList.Clear();
#endif
UniversalMemberList.Clear();
foreach (string member in members) {
#if RUST
RustMemberList.Add(ulong.Parse(member));
#endif
UniversalMemberList.Add(member);
}
#if RUST
Interface.CallHook(HOOK_NAME, tag, ulong.Parse(joining), RustMemberList);
#endif
Interface.CallHook(HOOK_NAME, tag, joining, UniversalMemberList);
// Deprecated
Interface.Oxide.CallDeprecatedHook(HOOK_NAME, NEW_HOOK, OnClanHookDeprecation, joining, tag);
Interface.Oxide.CallDeprecatedHook(HOOK_NAME, NEW_HOOK, OnClanHookDeprecation, joining, UniversalMemberList);
#if RUST
RustMemberList.Clear();
#endif
UniversalMemberList.Clear();
}
private static void OnClanMemberGone(string tag, string leaving, ICollection<string> members) {
const string HOOK_NAME = "OnClanMemberGone";
const string NEW_HOOK = "OnClanMemberGone(string tag, string leaving, List<string> members)";
#if RUST
RustMemberList.Clear();
#endif
UniversalMemberList.Clear();
foreach (string member in members) {
#if RUST
RustMemberList.Add(ulong.Parse(member));
#endif
UniversalMemberList.Add(member);
}
#if RUST
Interface.CallHook(HOOK_NAME, tag, ulong.Parse(leaving), RustMemberList);
#endif
Interface.CallHook(HOOK_NAME, tag, leaving, UniversalMemberList);
// Deprecated
Interface.Oxide.CallDeprecatedHook(HOOK_NAME, NEW_HOOK, OnClanHookDeprecation, leaving, tag);
Interface.Oxide.CallDeprecatedHook(HOOK_NAME, NEW_HOOK, OnClanHookDeprecation, leaving, UniversalMemberList);
#if RUST
RustMemberList.Clear();
#endif
UniversalMemberList.Clear();
}
private static void OnClanDisbanded(string tag, ICollection<string> members) {
const string HOOK_NAME = "OnClanDisbanded";
const string NEW_HOOK = "OnClanDisbanded(string tag, List<string> members)";
#if RUST
RustMemberList.Clear();
#endif
UniversalMemberList.Clear();
foreach (string member in members) {
#if RUST
RustMemberList.Add(ulong.Parse(member));
#endif
UniversalMemberList.Add(member);
}
#if RUST
Interface.CallHook(HOOK_NAME, tag, RustMemberList);
#endif
Interface.CallHook(HOOK_NAME, tag, UniversalMemberList);
// Deprecated
Interface.CallDeprecatedHook(HOOK_NAME, NEW_HOOK, OnClanHookDeprecation, UniversalMemberList);
Interface.CallDeprecatedHook(HOOK_NAME, NEW_HOOK, OnClanHookDeprecation, tag);
#if RUST
RustMemberList.Clear();
#endif
UniversalMemberList.Clear();
}
#endregion
#region API
private JObject GetClan(string tag) {
if (!string.IsNullOrEmpty(tag)) return storedData.FindClan(tag)?.ToJObject();
return null;
}
private JArray GetAllClans() => new JArray(storedData.clans.Keys);
private string GetClanOf(string playerId) => storedData.FindClanByID(playerId)?.Tag ?? null;
private string GetClanOf(ulong playerId) => GetClanOf(playerId.ToString());
private string GetClanOf(IPlayer player) => GetClanOf(player?.Id ?? string.Empty);
#if RUST
private string GetClanOf(BasePlayer player) => GetClanOf(player?.UserIDString ?? string.Empty);
#endif
#if HURTWORLD
private string GetClanOf(PlayerSession session) => GetClanOf(session?.SteamId ?? string.Empty);
#endif
private List<string> GetClanMembers(ulong playerId) => GetClanMembers(playerId.ToString());
private List<string> GetClanMembers(string playerId) => storedData.FindClanByID(playerId)?.ClanMembers.Keys.ToList() ?? new List<string>();
private object HasFriend(ulong ownerId, ulong playerId) => HasFriend(ownerId.ToString(), playerId.ToString());
private object HasFriend(string ownerId, string playerId) {
Clan clanOwner = storedData.FindClanByID(ownerId);
if (clanOwner == null) return null;
Clan clanFriend = storedData.FindClanByID(playerId);
if (clanFriend == null) return null;
return clanOwner.Tag.Equals(clanFriend.Tag);
}
private bool IsClanMember(ulong playerId, ulong otherId) => IsClanMember(playerId.ToString(), otherId.ToString());
private bool IsClanMember(string playerId, string otherId) {
Clan clanPlayer = storedData.FindClanByID(playerId);
if (clanPlayer == null) return false;
Clan clanOther = storedData.FindClanByID(otherId);
if (clanOther == null) return false;
return clanPlayer.Tag.Equals(clanOther.Tag);
}
private bool IsMemberOrAlly(ulong playerId, ulong otherId) => IsMemberOrAlly(playerId.ToString(), otherId.ToString());
private bool IsMemberOrAlly(string playerId, string otherId) {
Clan playerClan = storedData.FindClanByID(playerId);
if (playerClan == null) return false;
Clan otherClan = storedData.FindClanByID(otherId);
if (otherClan == null) return false;
if ((playerClan.Tag.Equals(otherClan.Tag)) || playerClan.Alliances.Contains(otherClan.Tag)) return true;
return false;
}
private bool IsAllyPlayer(ulong playerId, ulong otherId) => IsAllyPlayer(playerId.ToString(), otherId.ToString());
private bool IsAllyPlayer(string playerId, string otherId) {
Clan playerClan = storedData.FindClanByID(playerId);
if (playerClan == null) return false;
Clan otherClan = storedData.FindClanByID(otherId);
if (otherClan == null) return false;
if (playerClan.Alliances.Contains(otherClan.Tag)) return true;
return false;
}
private List<string> GetClanAlliances(ulong playerId) => GetClanAlliances(playerId.ToString());
private List<string> GetClanAlliances(string playerId) {
Clan clan = storedData.FindClanByID(playerId);
if (clan == null) return new List<string>();
return new List<string>(clan.Alliances);
}
#endregion
#region Clan
[Serializable]
public class Clan {
public string Tag {
get;
set;
}
public string Description {
get;
set;
}
public string OwnerID {
get;
set;
}
public double CreationTime {
get;
set;
}
public double LastOnlineTime {
get;
set;
}
public Hash<string, Member> ClanMembers {
get;
set;
}
= new Hash<string, Member>();
public Hash<string, MemberInvite> MemberInvites {
get;
set;
}
= new Hash<string, MemberInvite>();
public HashSet<string> Alliances {
get;
set;
}
= new HashSet<string>();
public Hash<string, double> AllianceInvites {
get;
set;
}
= new Hash<string, double>();
public HashSet<string> IncomingAlliances {
get;
set;
}
= new HashSet<string>();
public string TagColor {
get;
set;
}
= string.Empty;
[JsonIgnore]
public int OnlineCount {
get;
private set;
}
[JsonIgnore]
public int ModeratorCount => ClanMembers.Count(x => x.Value.Role == Member.MemberRole.Moderator);
[JsonIgnore]
public int MemberCount => ClanMembers.Count;
[JsonIgnore]
public int MemberInviteCount => MemberInvites.Count;
[JsonIgnore]
public int AllianceCount => Alliances.Count;
[JsonIgnore]
public int AllianceInviteCount => AllianceInvites.Count;
public Clan() {
}
public Clan(IPlayer player, string tag, string description) {
this.Tag = tag;
this.Description = description;
CreationTime = LastOnlineTime = UnixTimeStampUTC();
OwnerID = player.Id;
ClanMembers.Add(player.Id, new Member(Member.MemberRole.Owner, player.Name));
OnPlayerConnected(player);
}
#region Connection
internal void OnPlayerConnected(IPlayer player) {
i
// ... [Code preview truncated due to length. Full source in attached file] ...