using System.Collections.Generic; using System.Linq; using Newtonsoft.Json.Linq; using Newtonsoft.Json; using Oxide.Core; namespace Oxide.Plugins { [Info("Team Fix", "Khan", "1.0.2")] [Description("Fixes FacePunche Team Logic when incorrect shutdown/crashes occurs")] public class TeamFix : RustPlugin { #region Fields private readonly Dictionary _teams = new Dictionary(); private Configuration _config; private StoredData _storedData; #endregion #region Config protected override void LoadDefaultConfig() => _config = new Configuration(); protected override void LoadConfig() { base.LoadConfig(); try { _config = Config.ReadObject(); if (_config == null) { throw new JsonException(); } if (MaybeUpdateConfig(_config)) { PrintWarning("Configuration appears to be outdated; updating and saving"); SaveConfig(); } } catch { PrintWarning($"Configuration file {Name}.json is invalid; using defaults"); LoadDefaultConfig(); } } protected override void SaveConfig() { PrintWarning($"Configuration changes saved to {Name}.json"); Config.WriteObject(_config, true); } private class Configuration : SerializableConfiguration { public Options Option = new Options(); } public class Options { public bool TeamLimit = false; public int MemberLimit = RelationshipManager.maxTeamSize; } #endregion #region Oxide private void Init() { _storedData = StoredData.LoadData(); } private void OnServerInitialized() { if (_config.Option.TeamLimit) RelationshipManager.maxTeamSize = _config.Option.MemberLimit; if (_storedData.Teams.IsEmpty()) { foreach (var playerTeam in RelationshipManager.ServerInstance.teams.Values) { if (playerTeam.teamID == 0UL || !playerTeam.teamLeader.IsSteamId()) continue; TeamData teamData = new TeamData(); teamData.SetLeader(playerTeam.teamLeader); teamData.Members = playerTeam.members; _teams[playerTeam.teamID] = teamData; } _storedData.SaveData(); } else { RelationshipManager.ServerInstance.playerToTeam.Clear(); foreach (RelationshipManager.PlayerTeam playerTeam in RelationshipManager.ServerInstance.teams.Values) { RelationshipManager.PlayerTeam team = playerTeam; ClearTeam(ref team); } RelationshipManager.ServerInstance.teams.Clear(); RelationshipManager.ServerInstance.lastTeamIndex = 1; if (_storedData.Teams.Count <= 0) return; Unsubscribe("OnTeamCreated"); foreach (TeamData teamData in _storedData.Teams.Values.ToArray()) { RelationshipManager.PlayerTeam playerTeam = teamData.Restore(); _teams[playerTeam.teamID] = teamData; } _storedData.Teams = _teams; _storedData.SaveData(); Subscribe("OnTeamCreated"); } } private void Unload() { _storedData.Teams = _teams; _storedData.SaveData(); foreach (var player in BasePlayer.allPlayerList) { RelationshipManager.ServerInstance.FindTeam(player.currentTeam) ?.RemovePlayer(player.userID); } } private void OnServerSave() { _storedData.Teams = _teams; _storedData.SaveData(); } private void OnNewSave(string filename) { _storedData.Teams.Clear(); _storedData.SaveData(); } private void OnTeamCreated(BasePlayer player, RelationshipManager.PlayerTeam playerTeam) { TeamData teamData = FindTeamByID(playerTeam.teamID); teamData.SetLeader(player.userID); teamData.AddMember(player.userID); } private void OnTeamAcceptInvite(RelationshipManager.PlayerTeam team, BasePlayer player) { FindTeamByID(team.teamID) ?.AddMember(player.userID); } private void OnTeamDisbanded(RelationshipManager.PlayerTeam playerTeam) { _teams.Remove(playerTeam.teamID); } private void OnTeamPromote(RelationshipManager.PlayerTeam playerTeam, BasePlayer player) { FindTeamByID(playerTeam.teamID) ?.SetLeader(player.userID); } private void OnTeamKick(RelationshipManager.PlayerTeam playerTeam, BasePlayer player, ulong userID) { FindTeamByID(playerTeam.teamID) ?.DelMember(userID); } private void OnTeamLeave(RelationshipManager.PlayerTeam playerTeam, BasePlayer player) { FindTeamByID(playerTeam.teamID) ?.DelMember(player.userID); } private void OnPlayerConnected(BasePlayer player) { if (player == null) return; var teamData = _teams.Values.FirstOrDefault(x => x != null && (x.Members.Contains(player.userID) || x.LeaderID == player.userID)); teamData?.Team.AddPlayer(player); } #endregion #region Storage private class StoredData { public Dictionary Teams = new Dictionary(); public static StoredData LoadData() => Interface.Oxide.DataFileSystem.ReadObject("TeamFix"); public void SaveData() => Interface.Oxide.DataFileSystem.WriteObject("TeamFix", this); } private class TeamData { public ulong LeaderID; public List Members = new List(); [JsonIgnore] public RelationshipManager.PlayerTeam Team; public void SetLeader(ulong userID) => LeaderID = userID; public void AddMember(ulong userID) { if (Members.Contains(userID)) return; Members.Add(userID); } public void DelMember(ulong userID) => Members.Remove(userID); public RelationshipManager.PlayerTeam Restore() { RelationshipManager.PlayerTeam playerTeam = Team ?? RelationshipManager.ServerInstance.CreateTeam(); foreach (ulong member in Members) { BasePlayer player = BasePlayer.FindAwakeOrSleeping(member.ToString()); if (player == null) continue; playerTeam.AddPlayer(player); } playerTeam.SetTeamLeader(LeaderID); playerTeam.MarkDirty(); Team = playerTeam; return playerTeam; } } #endregion #region UpdateChecker internal class SerializableConfiguration { public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary ToDictionary() => JsonHelper.Deserialize(ToJson()) as Dictionary; } private static class JsonHelper { public static object Deserialize(string json) => ToObject(JToken.Parse(json)); private static object ToObject(JToken token) { switch (token.Type) { case JTokenType.Object: return token.Children().ToDictionary(prop => prop.Name, prop => ToObject(prop.Value)); case JTokenType.Array: return token.Select(ToObject).ToList(); default: return ((JValue)token).Value; } } } private bool MaybeUpdateConfig(SerializableConfiguration config) { var currentWithDefaults = config.ToDictionary(); var currentRaw = Config.ToDictionary(x => x.Key, x => x.Value); return MaybeUpdateConfigDict(currentWithDefaults, currentRaw); } private bool MaybeUpdateConfigDict(Dictionary currentWithDefaults, Dictionary currentRaw) { bool changed = false; foreach (var key in currentWithDefaults.Keys) { object currentRawValue; if (currentRaw.TryGetValue(key, out currentRawValue)) { var defaultDictValue = currentWithDefaults[key] as Dictionary; var currentDictValue = currentRawValue as Dictionary; if (defaultDictValue != null) { if (currentDictValue == null) { currentRaw[key] = currentWithDefaults[key]; changed = true; } else if (MaybeUpdateConfigDict(defaultDictValue, currentDictValue)) changed = true; } } else { currentRaw[key] = currentWithDefaults[key]; changed = true; } } return changed; } #endregion #region Helpers TeamData FindTeamByID(ulong teamID) { TeamData teamData; if (!_teams.TryGetValue(teamID, out teamData)) _teams[teamID] = teamData = new TeamData(); return teamData; } private static void ClearTeam(ref RelationshipManager.PlayerTeam playerTeam) { playerTeam.invites.Clear(); playerTeam.members.Clear(); playerTeam.onlineMemberConnections.Clear(); playerTeam.teamID = 0UL; playerTeam.teamLeader = 0UL; playerTeam.teamName = string.Empty; Facepunch.Pool.Free(ref playerTeam); } #endregion } }