From 5fe3398d750f42ca019f65b4d89ac0322bc2a2c0 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:16:01 -0300 Subject: [PATCH 001/146] Ploogin update --- Cerberus.props | 47 ++++ UltimateAFK/AFKComponent.cs | 248 ------------------ UltimateAFK/API/Base/AFK.cs | 12 + UltimateAFK/API/Base/Handler.cs | 29 ++ UltimateAFK/Config.cs | 64 +++-- UltimateAFK/GlobalSuppressions.cs | 10 + .../Handlers/Components/AFKComponent.cs | 244 +++++++++++++++++ UltimateAFK/Handlers/MainHandler.cs | 186 +++++++++++++ UltimateAFK/MainClass.cs | 101 ------- UltimateAFK/PlayerEvents.cs | 172 ------------ UltimateAFK/Resources/AFKData.cs | 31 +++ UltimateAFK/UltimateAFK.cs | 45 ++++ UltimateAFK/UltimateAFK.csproj | 145 +++------- 13 files changed, 678 insertions(+), 656 deletions(-) create mode 100644 Cerberus.props delete mode 100644 UltimateAFK/AFKComponent.cs create mode 100644 UltimateAFK/API/Base/AFK.cs create mode 100644 UltimateAFK/API/Base/Handler.cs create mode 100644 UltimateAFK/GlobalSuppressions.cs create mode 100644 UltimateAFK/Handlers/Components/AFKComponent.cs create mode 100644 UltimateAFK/Handlers/MainHandler.cs delete mode 100644 UltimateAFK/MainClass.cs delete mode 100644 UltimateAFK/PlayerEvents.cs create mode 100644 UltimateAFK/Resources/AFKData.cs create mode 100644 UltimateAFK/UltimateAFK.cs diff --git a/Cerberus.props b/Cerberus.props new file mode 100644 index 0000000..f4daa2f --- /dev/null +++ b/Cerberus.props @@ -0,0 +1,47 @@ + + + + + Cerberus Team + + + + net472 + 7.3 + x64 + false + $(MSBuildThisFileDirectory)\bin\$(Configuration)\ + + + + + 3.2.1 + + false + + 2.0.4 + 9.1.4 + 1.1.118 + 1.3.0 + + Copyright © $(Authors) 2020 - $([System.DateTime]::Now.ToString("yyyy")) + Git + https://github.com/cerberusServers + https://github.com/cerberusServers + + + $(DefineConstants);PUBLIC_BETA + + + + True + True + Portable + + + + + $(NoWarn);SA0001 + + + diff --git a/UltimateAFK/AFKComponent.cs b/UltimateAFK/AFKComponent.cs deleted file mode 100644 index ced8af9..0000000 --- a/UltimateAFK/AFKComponent.cs +++ /dev/null @@ -1,248 +0,0 @@ -using System; -using System.Linq; -using UnityEngine; -using MEC; -using Exiled.API.Features; -using Exiled.Loader; -using PlayableScps; -using scp035.API; -using System.Reflection; -using Exiled.API.Enums; - -namespace UltimateAFK -{ - public class AFKComponent : MonoBehaviour - { - public MainClass plugin; - - public bool disabled = false; - - Player ply; - - public Vector3 AFKLastPosition; - public Vector3 AFKLastAngle; - - public int AFKTime = 0; - public int AFKCount = 0; - private float timer = 0.0f; - - // Do not change this delay. It will screw up the detection - public float delay = 1.0f; - - private Player TryGet035() => Scp035Data.GetScp035(); - private void TrySpawn035(Player player) => Scp035Data.Spawn035(player); - - // Expose replacing player for plugin support - public Player PlayerToReplace; - - - void Awake() - { - ply = Player.Get(gameObject); - } - - void Update() - { - timer += Time.deltaTime; - if (timer > delay) - { - timer = 0f; - if (!this.disabled) - { - try - { - AFKChecker(); - } - catch (Exception e) - { - Log.Error(e); - } - } - } - } - - // Called every 1 second according to the player's Update function. This is way more efficient than the old way of doing a forloop for every player. - // Also, since the gameObject for the player is deleted when they disconnect, we don't need to worry about cleaning any variables :) - private void AFKChecker() - { - //Log.Info($"AFK Time: {this.AFKTime} AFK Count: {this.AFKCount}"); - if (this.ply.Team == Team.RIP || Player.List.Count() <= plugin.Config.MinPlayers || (plugin.Config.IgnoreTut && this.ply.Team == Team.TUT)) return; - - bool isScp079 = (this.ply.Role == RoleType.Scp079); - bool scp096TryNotToCry = false; - - // When SCP096 is in the state "TryNotToCry" he cannot move or it will cancel, - // therefore, we don't want to AFK check 096 while he's in this state. - if (this.ply.Role == RoleType.Scp096) - { - PlayableScps.Scp096 scp096 = this.ply.ReferenceHub.scpsController.CurrentScp as PlayableScps.Scp096; - scp096TryNotToCry = (scp096.PlayerState == Scp096PlayerState.TryNotToCry); - } - - Vector3 CurrentPos = this.ply.Position; - Vector3 CurrentAngle = (isScp079) ? this.ply.Camera.targetPosition.position : this.ply.Rotation; - - if (CurrentPos != this.AFKLastPosition || CurrentAngle != this.AFKLastAngle || scp096TryNotToCry) - { - this.AFKLastPosition = CurrentPos; - this.AFKLastAngle = CurrentAngle; - this.AFKTime = 0; - PlayerToReplace = null; - return; - } - - // The player hasn't moved past this point. - this.AFKTime++; - - // If the player hasn't reached the time yet don't continue. - if (this.AFKTime < plugin.Config.AfkTime) return; - - // Check if we're still in the "grace" period - int secondsuntilspec = (plugin.Config.AfkTime + plugin.Config.GraceTime) - this.AFKTime; - if (secondsuntilspec > 0) - { - string warning = plugin.Config.MsgGrace; - warning = warning.Replace("%timeleft%", secondsuntilspec.ToString()); - - this.ply.ClearBroadcasts(); - this.ply.Broadcast(1, $"{plugin.Config.MsgPrefix} {warning}"); - return; - } - - // The player is AFK and action will be taken. - Log.Info($"{this.ply.Nickname} ({this.ply.UserId}) was detected as AFK!"); - this.AFKTime = 0; - - // Let's make sure they are still alive before doing any replacement. - if (this.ply.Team == Team.RIP) return; - - if (plugin.Config.TryReplace && !IsPastReplaceTime()) - { - Assembly easyEvents = Loader.Plugins.FirstOrDefault(pl => pl.Name == "EasyEvents")?.Assembly; - - var roleEasyEvents = easyEvents?.GetType("EasyEvents.Util")?.GetMethod("GetRole")?.Invoke(null, new object[] { this.ply }); - - // SCP035 Support (Credit DCReplace) - bool is035 = false; - try - { - is035 = this.ply.Id == TryGet035()?.Id; - } - catch (Exception e) - { - Log.Debug($"SCP-035 is not installed, skipping method call: {e}"); - } - - // Credit: DCReplace :) - // I mean at this point 90% of this has been rewritten lol... - var inventory = this.ply.Inventory.items.Select(x => x.id).ToList(); - - RoleType role = this.ply.Role; - Vector3 pos = this.ply.Position; - float health = this.ply.Health; - - // New strange ammo system because the old one was fucked. - uint ammo1 = this.ply.Ammo[(int)AmmoType.Nato556]; - uint ammo2 = this.ply.Ammo[(int)AmmoType.Nato762]; - uint ammo3 = this.ply.Ammo[(int)AmmoType.Nato9]; - - // Stuff for 079 - byte Level079 = 0; - float Exp079 = 0f, AP079 = 0f; - if (isScp079) - { - Level079 = this.ply.Level; - Exp079 = this.ply.Experience; - AP079 = this.ply.Energy; - } - - PlayerToReplace = Player.List.FirstOrDefault(x => x.Role == RoleType.Spectator && x.UserId != string.Empty && !x.IsOverwatchEnabled && x != this.ply); - if (PlayerToReplace != null) - { - // Make the player a spectator first so other plugins can do things on player changing role with uAFK. - this.ply.Inventory.Clear(); // Clear their items to prevent dupes. - this.ply.SetRole(RoleType.Spectator); - this.ply.Broadcast(30, $"{plugin.Config.MsgPrefix} {plugin.Config.MsgFspec}"); - - PlayerToReplace.SetRole(role); - - Timing.CallDelayed(0.3f, () => - { - if (is035) - { - try - { - TrySpawn035(PlayerToReplace); - } - catch (Exception e) - { - Log.Debug($"SCP-035 is not installed, skipping method call: {e}"); - } - } - PlayerToReplace.Position = pos; - - PlayerToReplace.ClearInventory(); - PlayerToReplace.ResetInventory(inventory); - - PlayerToReplace.Health = health; - - PlayerToReplace.Ammo[(int)AmmoType.Nato556] = ammo1; - PlayerToReplace.Ammo[(int)AmmoType.Nato762] = ammo2; - PlayerToReplace.Ammo[(int)AmmoType.Nato9] = ammo3; - - if (isScp079) - { - PlayerToReplace.Level = Level079; - PlayerToReplace.Experience = Exp079; - PlayerToReplace.Energy = AP079; - } - - PlayerToReplace.Broadcast(10, $"{plugin.Config.MsgPrefix} {plugin.Config.MsgReplace}"); - if (roleEasyEvents != null) easyEvents?.GetType("EasyEvents.CustomRoles")?.GetMethod("ChangeRole")?.Invoke(null, new object[] { PlayerToReplace, roleEasyEvents }); - PlayerToReplace = null; - }); - } - else - { - // Couldn't find a valid player to spawn, just ForceToSpec anyways. - ForceToSpec(this.ply); - } - } - else - { - // Replacing is disabled, just ForceToSpec - ForceToSpec(this.ply); - } - // If it's -1 we won't be kicking at all. - if (plugin.Config.NumBeforeKick != -1) - { - // Increment AFK Count - this.AFKCount++; - if (this.AFKCount >= plugin.Config.NumBeforeKick) - { - // Since this.AFKCount is greater than the config we're going to kick that player for being AFK too many times in one match. - ServerConsole.Disconnect(this.gameObject, plugin.Config.MsgKick); - } - } - } - - private void ForceToSpec(Player hub) - { - hub.SetRole(RoleType.Spectator); - hub.Broadcast(30, $"{plugin.Config.MsgPrefix} {plugin.Config.MsgFspec}"); - } - - private bool IsPastReplaceTime() - { - if (plugin.Config.MaxReplaceTime != -1) - { - if (Round.ElapsedTime.TotalSeconds > plugin.Config.MaxReplaceTime) - { - Log.Info("Since we are past the allowed replace time, we will not look for replacement player."); - return true; - } - } - return false; - } - } -} diff --git a/UltimateAFK/API/Base/AFK.cs b/UltimateAFK/API/Base/AFK.cs new file mode 100644 index 0000000..9f75f22 --- /dev/null +++ b/UltimateAFK/API/Base/AFK.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UltimateAFK.API.Base +{ + internal class AFK + { + } +} diff --git a/UltimateAFK/API/Base/Handler.cs b/UltimateAFK/API/Base/Handler.cs new file mode 100644 index 0000000..00f50e3 --- /dev/null +++ b/UltimateAFK/API/Base/Handler.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UltimateAFK.API.Base +{ + /// + /// Base API for allow create handlers in a "easily" way. + /// + public abstract class Handler + { + /// + /// Plugin Instance + /// + protected UltimateAFK Plugin => UltimateAFK.Instance; + + /// + /// Triggered when plugin is loaded + /// + public abstract void Start(); + + /// + /// Triggered when plugin is stopped or server restart. + /// + public abstract void Stop(); + } +} diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 868f589..34d34dc 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -1,32 +1,42 @@ -namespace UltimateAFK -{ - using System.ComponentModel; - using Exiled.API.Interfaces; +using Exiled.API.Interfaces; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; - public sealed class Config : IConfig +namespace UltimateAFK +{ + public class Config : IConfig { - [Description("Is the plugin enabled?")] public bool IsEnabled { get; set; } = true; - [Description("Minimum required players for uAFK to be active.")] - public int MinPlayers { get; set; } = 2; - [Description("Do AFK players get replaced by spectators?")] - public bool TryReplace { get; private set; } = true; - [Description("Should Tutorials be ignored?")] - public bool IgnoreTut { get; private set; } = false; - [Description("How long can player not move?")] - public int AfkTime { get; private set; } = 30; - [Description("How long to wait before player gets kicked after getting a warning for not moving?")] - public int GraceTime { get; private set; } = 15; - [Description("After how many changes to spectator for AFK should player get kicked?")] - public int NumBeforeKick { get; private set; } = 2; - [Description("Maximum replace time, if the round time is past this value it will not replace the player (Set to -1 to disable)")] - public int MaxReplaceTime { get; private set; } = -1; - [Description("Don't touch this if you do not understand the repercussions! - Ignore Perm and IP Checks.")] - public bool IgnorePermissionsAndIP { get; private set; } = false; - public string MsgPrefix { get; private set; } = "[uAFK]"; - public string MsgGrace { get; private set; } = "You will be moved to spectator in %timeleft% seconds if you do not move!"; - public string MsgFspec { get; private set; } = "You were detected as AFK and automatically moved to spectator!"; - public string MsgKick { get; private set; } = "[Kicked by uAFK] You were AFK for too long!"; - public string MsgReplace { get; private set; } = "You have replaced a player that was AFK."; + + [Description("If the number of players is less than this the plugin will not work.")] + public int MinPlayers { get; set; } = 8; + + [Description("Tutorials should be ignored ?")] + public bool IgnoreTut { get; set; } = true; + + [Description("The time it takes for a player to stand still before he is detected as AFK")] + public int AfkTime { get; set; } = 80; + + [Description("After being detected as AFK a message will appear on his face and he will be given this time to move or he will be Kicked/Moved to bystander.")] + public int GraceTime { get; set; } = 30; + + [Description("The number of times a player must be moved to spectator for a player to be kicked from the server. Use -1 to disable it")] + public int AfkCount { get; set; } = -1; + + [Description("When the player is detected as AFK and is in grace period this message will appear on his face. {0} represents the seconds the player has to move or be moved to spectator.")] + public string MsgGrace { get; set; } = "[Cerberus.AFK] You will be moved to spectator if you do not move in less than {0} seconds."; + + [Description("This message will be sent to the player who has been moved to spectator when he is detected as AFK, it is also sent to the player's console.")] + public string MsgFspec { get; set; } = "You were detected as AFK and were moved to spectator. WAKE UP WAKE UP WAKE UP WAKE UP WAKE UP WAKE UP"; + + [Description("This will be the reason for the Kick, due to the VSR it is obligatory to clarify that it is a plugin with flags like [UltimateAFK] or something similar.")] + public string MsgKick { get; set; } = "[Ultimate-AFK] You were removed from the server for being AFK for too long.!"; + + [Description("When a player replaces another player, this message will appear on the player's face and on the player console.")] + public string MsgReplace { get; set; } = " You replaced an AFK player."; } } diff --git a/UltimateAFK/GlobalSuppressions.cs b/UltimateAFK/GlobalSuppressions.cs new file mode 100644 index 0000000..92e5b5e --- /dev/null +++ b/UltimateAFK/GlobalSuppressions.cs @@ -0,0 +1,10 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; +//Shit zzzzzzz +[assembly: SuppressMessage("CodeQuality", "IDE0051:Quitar miembros privados no utilizados", Justification = "", Scope = "member", Target = "~M:UltimateAFK.Handlers.Components.AFKComponent.OnDestroy")] +[assembly: SuppressMessage("CodeQuality", "IDE0052:Quitar miembros privados no leídos", Justification = "", Scope = "member", Target = "~F:UltimateAFK.Handlers.Components.AFKComponent.CountHandler")] +[assembly: SuppressMessage("CodeQuality", "IDE0051:Quitar miembros privados no utilizados", Justification = "", Scope = "member", Target = "~M:UltimateAFK.Handlers.Components.AFKComponent.Awake")] diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs new file mode 100644 index 0000000..fb7dc50 --- /dev/null +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -0,0 +1,244 @@ +using UltimateAFK.Handlers; +using Exiled.API.Features; +using Exiled.API.Features.Roles; +using Exiled.Events.EventArgs; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace UltimateAFK.Handlers.Components +{ + /// + /// Cringe componente for Players. Warning doing an AFKChecker is not a pretty thing to look at but I will try to document so you understand what I am trying to do :) + /// + public class AFKComponent : MonoBehaviour + { + #region Variables and Stuff + public Player MyPlayer { get; set; } + + /// + /// If True, the component will ignore this player, but will not be destroyed. + /// + public bool IsDisable { get; set; } + + public Player ReplacementPlayer { get; internal set; } + + // Using a MEC Coroutine is more optimized than using Unity methods. + private CoroutineHandle CountHandler; + + public Vector3 AFKLastPosition; + + public Vector3 AFKLastAngle; + + public int AFKTime; + + public int AFKCount; + #endregion + + private void Awake() + { + if (!(Player.Get(gameObject) is Player ply)) + { + Log.Error($"{this} Error Getting Player"); + Destroy(); + return; + } + + MyPlayer = ply; + + Exiled.Events.Handlers.Player.Destroying += OnDestroying; + Exiled.Events.Handlers.Player.Jumping += OnJumping; + + // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. + CountHandler = Timing.RunCoroutine(CheckAfkPerSecond().CancelWith(this).CancelWith(gameObject)); + } + + private void OnDestroy() + { + Destroy(true); + } + + // With this you can destroy the component in a more controlled way. + public void Destroy(bool value = false) + { + try + { + if (value) + { + // This is to avoid a loop where the component constantly calls OnDestroy because Destroy calls OnDestroy and OnDestroy calls Destroy. + Exiled.Events.Handlers.Player.Destroying -= OnDestroying; + Exiled.Events.Handlers.Player.Jumping -= OnJumping; + } + else + { + Exiled.Events.Handlers.Player.Destroying -= OnDestroying; + Exiled.Events.Handlers.Player.Jumping -= OnJumping; + Destroy(this); + } + } + catch (Exception e) + { + Log.Error($"Exception: {e}\n Couldn't destroy: {this}\nIs ReferenceHub null? {MyPlayer is null}"); + } + } + + + private IEnumerator CheckAfkPerSecond() + { + while (true) + { + if (!IsDisable) + { + AFKChecker(); + } + + yield return Timing.WaitForSeconds(1.2f); + } + } + + #region AFKChecker | CRINGE AAAAAAAAAAAAAAA + private void AFKChecker() + { + bool cantContinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || !Round.IsStarted; + + if (!cantContinue) + { + #region Check if player is 079 or 096 + Scp079Role scp079Role = MyPlayer.Role as Scp079Role; + Scp096Role scp096Role = MyPlayer.Role as Scp096Role; + + bool isNotCrying = false; + bool is079 = scp079Role != null; + bool is096 = scp096Role != null; + + if (is096 || is079) + { + if (is096) + { + scp096Role = MyPlayer.Role as Scp096Role; + isNotCrying = scp096Role.State == PlayableScps.Scp096PlayerState.TryNotToCry; + } + else if (is079) + { + // Unnecessary, but whatever + scp079Role = MyPlayer.Role as Scp079Role; + } + } + #endregion + + Vector3 position = MyPlayer.Position; + Vector3 vector = is079 ? scp079Role.Camera.HeadPosition : (Vector3)MyPlayer.Rotation; + + bool isMoving = position != AFKLastPosition || vector != AFKLastAngle || isNotCrying; + + if (isMoving) + { + this.AFKLastPosition = position; + this.AFKLastAngle = vector; + this.AFKTime = 0; + this.ReplacementPlayer = null; + } + else + { + AFKTime++; + + bool isNotAFK = AFKTime < UltimateAFK.Instance.Config.AfkTime; + + if (!isNotAFK) + { + int num = UltimateAFK.Instance.Config.AfkTime + UltimateAFK.Instance.Config.GraceTime - this.AFKTime; + + bool isInGrace = num > 0; + + if (isInGrace) + { + var message = string.Format(UltimateAFK.Instance.Config.MsgGrace, num); + + MyPlayer.Broadcast(2, message, Broadcast.BroadcastFlags.Normal, true); + } + else + { + Log.Info($"{MyPlayer.Nickname} ({MyPlayer.UserId}) Detected as AFK"); + + if (MyPlayer.IsAlive) + { + var items = MyPlayer.Items; + var role = MyPlayer.Role; + var plyposition = MyPlayer.Position; + var health = MyPlayer.Health; + var ammo = MyPlayer.Ammo; + ReplacementPlayer = Player.List.FirstOrDefault(p => p.Role.Type == RoleType.Spectator && !p.IsHost && !p.IsOverwatchEnabled && p != MyPlayer); + + if (ReplacementPlayer != null) + { + MyPlayer.ClearInventory(); + MyPlayer.SetRole(RoleType.Spectator, Exiled.API.Enums.SpawnReason.ForceClass, false); + MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, false); + MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); + + MainHandler.ReplacingPlayers.Add(this.ReplacementPlayer, new Resources.AFKData + { + AfkComp = this, + SpawnLocation = plyposition, + Ammo = ammo, + Items = (List)items, + Is079 = is079, + Level = scp079Role != null ? scp079Role.Level : (byte)0, + Xp = scp079Role != null ? scp079Role.Experience : 0f, + Energy = scp079Role != null ? scp079Role.Energy : 0f, + Health = health, + }); + + if (UltimateAFK.Instance.Config.AfkTime != -1) + { + AFKCount++; + + if (AFKCount > UltimateAFK.Instance.Config.AfkTime) + { + MyPlayer.Kick(UltimateAFK.Instance.Config.MsgKick, "[UltimateAFK]"); + } + } + + + this.ReplacementPlayer.SetRole(role); + + } + else + { + //Do nothing. + } + } + } + } + } + } + + } + #endregion + + #region Events + // Technically this is not necessary since the component is destroyed when the player is destroyed but for fear of leaving a ghost component I better do this. + public void OnDestroying(DestroyingEventArgs ev) + { + if (ev.Player == MyPlayer) + { + Destroy(); + } + } + + // It is better to do it here than in MainHandler. For those who do not understand when jumping the player restarts his AFK time, since if he jumps technically he is not afk. + public void OnJumping(JumpingEventArgs ev) + { + if (ev.Player != null && ev.Player == MyPlayer && !IsDisable) + { + AFKTime = 0; + } + } + + #endregion + } +} diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs new file mode 100644 index 0000000..2afd80e --- /dev/null +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -0,0 +1,186 @@ +using Exiled.API.Features; +using Exiled.API.Features.Roles; +using Exiled.Events.EventArgs; +using Exiled.Permissions.Extensions; +using MEC; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UltimateAFK.Handlers.Components; +using UltimateAFK.Resources; + +namespace UltimateAFK.Handlers +{ + /// + /// Class where the will be given to the and the afk counter will be reset by the player's actions. + /// + public class MainHandler : API.Base.Handler + { + public static Dictionary ReplacingPlayers = new Dictionary(); + + public override void Start() + { + Exiled.Events.Handlers.Player.Verified += OnVerify; + Exiled.Events.Handlers.Player.ChangingRole += OnChangingRole; + + Exiled.Events.Handlers.Player.Shooting += OnShooting; + Exiled.Events.Handlers.Player.InteractingDoor += OnInteractDoor; + Exiled.Events.Handlers.Player.InteractingElevator += OnInteractElevator; + Exiled.Events.Handlers.Player.InteractingLocker += OnInteractLocker; + Exiled.Events.Handlers.Player.InteractingScp330 += OnInteract330; + + Exiled.Events.Handlers.Scp914.Activating += OnInteract914; + Exiled.Events.Handlers.Scp914.ChangingKnobSetting += OnChaningKnob914; + + Exiled.Events.Handlers.Player.DroppingItem += OnDroppingItems; + Exiled.Events.Handlers.Player.DroppingAmmo += OnDroppingAmmo; + + Exiled.Events.Handlers.Scp079.GainingExperience += OnGaingExp; + + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + } + + public override void Stop() + { + Exiled.Events.Handlers.Player.Verified -= OnVerify; + Exiled.Events.Handlers.Player.ChangingRole -= OnChangingRole; + + Exiled.Events.Handlers.Player.Shooting -= OnShooting; + Exiled.Events.Handlers.Player.InteractingDoor -= OnInteractDoor; + Exiled.Events.Handlers.Player.InteractingElevator -= OnInteractElevator; + Exiled.Events.Handlers.Player.InteractingLocker -= OnInteractLocker; + Exiled.Events.Handlers.Player.InteractingScp330 -= OnInteract330; + + Exiled.Events.Handlers.Scp914.Activating -= OnInteract914; + Exiled.Events.Handlers.Scp914.ChangingKnobSetting -= OnChaningKnob914; + + Exiled.Events.Handlers.Player.DroppingItem -= OnDroppingItems; + Exiled.Events.Handlers.Player.DroppingAmmo -= OnDroppingAmmo; + + Exiled.Events.Handlers.Scp079.GainingExperience -= OnGaingExp; + + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + } + + + public void OnVerify(VerifiedEventArgs ev) + { + if (!ev.Player.GameObject.TryGetComponent(out var _)) + { + ev.Player.GameObject.AddComponent(); + } + } + + public void OnChangingRole(ChangingRoleEventArgs ev) + { + if (ev.Player != null && ev.Player.GameObject.TryGetComponent(out var component)) + { + if (ev.Player.CheckPermission("uafk.ignore") || ev.Player.SessionVariables.ContainsKey("IsNPC")) + { + component.IsDisable = true; + } + + if (ReplacingPlayers.TryGetValue(ev.Player, out var data)) + { + ev.Items.Clear(); + ev.Items.AddRange(data.Items); + + Timing.CallDelayed(0.8f, () => + { + data.AfkComp.ReplacementPlayer = null; + + ev.Player.Position = data.SpawnLocation; + ev.Player.Broadcast(16, UltimateAFK.Instance.Config.MsgReplace, Broadcast.BroadcastFlags.Normal, true); + ev.Player.SendConsoleMessage(UltimateAFK.Instance.Config.MsgReplace, "white"); + ev.Player.Health = data.Health; + ev.Player.Inventory.UserInventory.ReserveAmmo = data.Ammo; + ev.Player.Inventory.SendAmmoNextFrame = true; + + if (ev.NewRole == RoleType.Scp079 && data.Is079) + { + var scprole = ev.Player.Role as Scp079Role; + + scprole.Level = data.Level; + + scprole.Experience = data.Xp; + + scprole.Energy = data.Energy; + } + + ReplacingPlayers.Remove(ev.Player); + + }); + } + } + } + + public void OnRoundStarted() + { + ReplacingPlayers.Clear(); + } + + private void ResetAFKTime(Player ply) + { + if (ply != null && ply.GameObject.TryGetComponent(out var comp)) + { + comp.AFKTime = 0; + } + } + + #region Reset AFK Timers + + public void OnInteractDoor(InteractingDoorEventArgs ev) + { + ResetAFKTime(ev.Player); + } + + public void OnInteractElevator(InteractingElevatorEventArgs ev) + { + ResetAFKTime(ev.Player); + } + + public void OnInteractLocker(InteractingLockerEventArgs ev) + { + ResetAFKTime(ev.Player); + } + + public void OnInteract330(InteractingScp330EventArgs ev) + { + ResetAFKTime(ev.Player); + } + + public void OnInteract914(ActivatingEventArgs ev) + { + ResetAFKTime(ev.Player); + } + + public void OnChaningKnob914(ChangingKnobSettingEventArgs ev) + { + ResetAFKTime(ev.Player); + } + + public void OnDroppingItems(DroppingItemEventArgs ev) + { + ResetAFKTime(ev.Player); + } + + public void OnDroppingAmmo(DroppingAmmoEventArgs ev) + { + ResetAFKTime(ev.Player); + } + + public void OnGaingExp(GainingExperienceEventArgs ev) + { + ResetAFKTime(ev.Player); + + } + + public void OnShooting(ShootingEventArgs ev) + { + ResetAFKTime(ev.Shooter); + } + #endregion + } +} diff --git a/UltimateAFK/MainClass.cs b/UltimateAFK/MainClass.cs deleted file mode 100644 index 84cb339..0000000 --- a/UltimateAFK/MainClass.cs +++ /dev/null @@ -1,101 +0,0 @@ -/* -* +==================================================================================+ -* | _ ___ ____ _ _ | -* | | |/ (_) _ __ __ _ ___ | _ \| | __ _ _ _ __ _ _ __ ___ _ _ _ __ __| | | -* | | ' /| | '_ \ / _` / __| | |_) | |/ _` | | | |/ _` | '__/ _ \| | | | '_ \ / _` | | -* | | . \| | | | | (_| \__ \ | __/| | (_| | |_| | (_| | | | (_) | |_| | | | | (_| | | -* | |_|\_\_|_| |_|\__, |___/ |_| |_|\__,_|\__, |\__, |_| \___/ \__,_|_| |_|\__,_| | -* | |___/ |___/ |___/ | -* | | -* +==================================================================================+ -* | SCP:SL Ultimate AFK Checker | -* | by Thomasjosif | -* | | -* | Special thanks to iopietro for his awesome suggestions :) | -* | https://kingsplayground.fun | -* +==================================================================================+ -* | MIT License | -* | | -* | Copyright (C) 2020 Thomas Dick | -* | Copyright (C) 2020 King's Playground | -* | | -* | Permission is hereby granted, free of charge, to any person obtaining a copy | -* | of this software and associated documentation files (the "Software"), to deal | -* | in the Software without restriction, including without limitation the rights | -* | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | -* | copies of the Software, and to permit persons to whom the Software is | -* | furnished to do so, subject to the following conditions: | -* | | -* | The above copyright notice and this permission notice shall be included in all | -* | copies or substantial portions of the Software. | -* | | -* | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | -* | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | -* | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | -* | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | -* | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | -* | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | -* | SOFTWARE. | -* +==================================================================================+ -*/ - -using System; -using Handlers = Exiled.Events.Handlers; -using Exiled.API.Enums; -using Exiled.API.Features; - -namespace UltimateAFK -{ - public class MainClass : Plugin - { - public override string Author { get; } = "Thomasjosif"; - public override string Name { get; } = "Ultimate AFK"; - public override string Prefix { get; } = "uAFK"; - public override Version Version { get; } = new Version(3, 1, 6); - public override Version RequiredExiledVersion { get; } = new Version(2, 2, 4); - public PlayerEvents PlayerEvents; - - public override PluginPriority Priority { get; } = PluginPriority.Medium; - - public override void OnEnabled() - { - base.OnEnabled(); - try - { - PlayerEvents = new PlayerEvents(this); - - Handlers.Player.Verified += PlayerEvents.OnPlayerVerified; - Handlers.Player.ChangingRole += PlayerEvents.OnSetClass; - Handlers.Player.Shooting += PlayerEvents.OnPlayerShoot; - Handlers.Player.InteractingDoor += PlayerEvents.OnDoorInteract; - Handlers.Scp914.Activating += PlayerEvents.On914Activate; - Handlers.Scp914.ChangingKnobSetting += PlayerEvents.On914Change; - Handlers.Player.InteractingLocker += PlayerEvents.OnLockerInteract; - Handlers.Player.ItemDropped += PlayerEvents.OnDropItem; - Handlers.Scp079.GainingExperience += PlayerEvents.OnSCP079Exp; - - Log.Info($"UltimateAFK plugin loaded.\n Written by Thomasjosif for King's Playground"); - } - catch (Exception e) - { - Log.Error($"There was an error loading the plugin: {e}"); - } - - } - public override void OnDisabled() - { - base.OnDisabled(); - Handlers.Player.Verified -= PlayerEvents.OnPlayerVerified; - Handlers.Player.ChangingRole -= PlayerEvents.OnSetClass; - Handlers.Player.Shooting -= PlayerEvents.OnPlayerShoot; - Handlers.Player.InteractingDoor -= PlayerEvents.OnDoorInteract; - Handlers.Scp914.Activating -= PlayerEvents.On914Activate; - Handlers.Scp914.ChangingKnobSetting -= PlayerEvents.On914Change; - Handlers.Player.InteractingLocker -= PlayerEvents.OnLockerInteract; - Handlers.Player.ItemDropped -= PlayerEvents.OnDropItem; - Handlers.Scp079.GainingExperience -= PlayerEvents.OnSCP079Exp; - - PlayerEvents = null; - } - } -} \ No newline at end of file diff --git a/UltimateAFK/PlayerEvents.cs b/UltimateAFK/PlayerEvents.cs deleted file mode 100644 index 1f55f64..0000000 --- a/UltimateAFK/PlayerEvents.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System; -using System.Reflection; -using Exiled.API.Features; -using Exiled.Events.EventArgs; -using Exiled.Permissions.Extensions; -using Exiled.Loader; -using System.Linq; - -namespace UltimateAFK -{ - public class PlayerEvents - { - public MainClass plugin; - - public PlayerEvents(MainClass plugin) - { - this.plugin = plugin; - } - - public void OnPlayerVerified(VerifiedEventArgs ev) - { - // Add a component to the player to check AFK status. - AFKComponent afkComponent = ev.Player.GameObject.gameObject.AddComponent(); - afkComponent.plugin = plugin; - } - - // This check was moved here, because player's rank's are set AFTER OnPlayerJoin() - public void OnSetClass(ChangingRoleEventArgs ev) - { - try - { - if (ev.Player == null) return; - AFKComponent afkComponent = ev.Player.GameObject.gameObject.GetComponent(); - - if (afkComponent != null) - { - if (!plugin.Config.IgnorePermissionsAndIP) - if (ev.Player.CheckPermission("uafk.ignore") || ev.Player.IPAddress == "127.0.0.1") //127.0.0.1 is sometimes used for "Pets" which causes issues - afkComponent.disabled = true; - if (IsGhost(ev.Player)) - afkComponent.disabled = true; - } - - - } - catch (Exception e) - { - Log.Error($"ERROR In OnSetClass(): {e}"); - } - } - - /* - * The following events are only here as additional AFK checks for some very basic player interactions - * I can add more interactions, but this seems good for now. - */ - public void OnDoorInteract(InteractingDoorEventArgs ev) - { - try - { - ResetAFKTime(ev.Player); - } - catch (Exception e) - { - Log.Error($"ERROR In OnDoorInteract(): {e}"); - } - } - - public void OnPlayerShoot(ShootingEventArgs ev) - { - try - { - ResetAFKTime(ev.Shooter); - } - catch (Exception e) - { - Log.Error($"ERROR In ResetAFKTime(): {e}"); - } - } - public void On914Activate(ActivatingEventArgs ev) - { - try - { - ResetAFKTime(ev.Player); - } - catch (Exception e) - { - Log.Error($"ERROR In On914Activate(): {e}"); - } - } - public void On914Change(ChangingKnobSettingEventArgs ev) - { - try - { - ResetAFKTime(ev.Player); - } - catch (Exception e) - { - Log.Error($"ERROR In OnLockerInteract(): {e}"); - } - } - - public void OnLockerInteract(InteractingLockerEventArgs ev) - { - try - { - ResetAFKTime(ev.Player); - } - catch (Exception e) - { - Log.Error($"ERROR In OnLockerInteract(): {e}"); - } - } - public void OnDropItem(ItemDroppedEventArgs ev) - { - try - { - ResetAFKTime(ev.Player); - } - catch (Exception e) - { - Log.Error($"ERROR In OnDropItem(): {e}"); - } - } - - public void OnSCP079Exp(GainingExperienceEventArgs ev) - { - try - { - ResetAFKTime(ev.Player); - } - catch (Exception e) - { - Log.Error($"ERROR In OnSCP079Exp(): {e}"); - } - } - - /// - /// Reset the AFK time of a player. - /// Thanks iopietro! - /// - /// - public void ResetAFKTime(Player player) - { - try - { - if (player == null) return; - - AFKComponent afkComponent = player.GameObject.gameObject.GetComponent(); - - if (afkComponent != null) - afkComponent.AFKTime = 0; - - } - catch (Exception e) - { - Log.Error($"ERROR In ResetAFKTime(): {e}"); - } - } - - /// - /// Checks if a player is a "ghost" using GhostSpectator's API. - /// - /// - /// - public static bool IsGhost(Player player) - { - Assembly assembly = Loader.Plugins.FirstOrDefault(pl => pl.Name == "GhostSpectator")?.Assembly; - if (assembly == null) return false; - return ((bool)assembly.GetType("GhostSpectator.API")?.GetMethod("IsGhost")?.Invoke(null, new object[] { player })) == true; - } - } -} diff --git a/UltimateAFK/Resources/AFKData.cs b/UltimateAFK/Resources/AFKData.cs new file mode 100644 index 0000000..de40761 --- /dev/null +++ b/UltimateAFK/Resources/AFKData.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UltimateAFK.Handlers.Components; +using UnityEngine; + +namespace UltimateAFK.Resources +{ + public struct AFKData + { + public AFKComponent AfkComp { get; set; } + + public Vector3 SpawnLocation { get; set; } + + public Dictionary Ammo { get; set; } + + public List Items { get; set; } + + public float Health { get; set; } + + public bool Is079 { get; set; } + + public byte Level { get; set; } + + public float Xp { get; set; } + + public float Energy { get; set; } + } +} diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs new file mode 100644 index 0000000..5948c1b --- /dev/null +++ b/UltimateAFK/UltimateAFK.cs @@ -0,0 +1,45 @@ +using Exiled.API.Features; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UltimateAFK +{ + /// + /// Main class where all the handlers are loaded. + /// + public class UltimateAFK : Plugin + { + // This is my programming style and if you don't like it, good for you. + + #region Variables + public static UltimateAFK Instance; + + public List Handlers; + #endregion + + public override string Author => "SrLicht"; + + public override string Name => "Ultimate-AFK"; + + public override string Prefix => "Ultimate_Afk"; + + public override Version RequiredExiledVersion => new Version(5,0,0); + + public override Version Version => new Version(1,0,0); + + public override void OnEnabled() + { + base.OnEnabled(); + } + + public override void OnDisabled() + { + base.OnDisabled(); + } + + + } +} diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index d615735..8efd758 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -1,116 +1,45 @@  - - - - Debug - AnyCPU - {9280B8BA-6290-4FC8-A58F-BC24E43AA486} + + + Library - Properties - UltimateAFK UltimateAFK - v4.7.2 - 512 + true + 3.0.0 - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - x64 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - False - ..\..\..\EXILED_REFERENCES\0Harmony.dll - - - False - ..\..\..\EXILED_REFERENCES\Assembly-CSharp-firstpass.dll - - - ..\..\..\EXILED_REFERENCES\Assembly-CSharp-Publicized.dll - - - False - ..\..\..\EXILED_REFERENCES\CommandSystem.Core.dll - - - ..\packages\EXILED.2.2.4\lib\net472\Exiled.API.dll - - - ..\packages\EXILED.2.2.4\lib\net472\Exiled.Bootstrap.dll - - - ..\packages\EXILED.2.2.4\lib\net472\Exiled.Events.dll - - - ..\packages\EXILED.2.2.4\lib\net472\Exiled.Loader.dll - - - ..\packages\EXILED.2.2.4\lib\net472\Exiled.Permissions.dll - - - ..\packages\EXILED.2.2.4\lib\net472\Exiled.Updater.dll - - - False - ..\..\..\EXILED_REFERENCES\Mirror.dll - - - False - ..\..\..\EXILED_REFERENCES\scp035.dll - - - - False - ..\..\..\EXILED_REFERENCES\UnityEngine.dll - - - False - ..\..\..\EXILED_REFERENCES\UnityEngine.CoreModule.dll - - - False - ..\..\..\EXILED\bin\Debug\YamlDotNet.dll - - - False - ..\..\..\EXILED_REFERENCES\zxing.unity.dll - + + + + + + + + + + - - \ No newline at end of file From 02bd1fc57696e59d15bf352da21a99698dd892dc Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Mon, 2 May 2022 05:19:12 -0300 Subject: [PATCH 002/146] Update README.md --- README.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/README.md b/README.md index 0413626..fdc90c4 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,12 @@ # Ultimate-AFK -Handles AFK Checking in SCP:SL for King's Playground Servers. +This is an updated version of the original Ultimate AFK plugin from https://github.com/kingsplayground/Ultimate-AFK. # Features - Detects AFK Players via in-game movement, camera movement, and in-game interactions - Moves players to spectator after a determined AFK Time and grace period -- (Optional) Try to replace the player with a random spectator using code from [DCReplace](https://github.com/Cyanox62/DCReplace) - (Optional) Kick players from the server after repeated AFK detections! - Custom broadcasts to AFK Players to indicate to them if they are AFK. - Works with SCP-079 by checking camera angle, and experience interactions - -# Default config: -```yaml -uafk_enable: true -# The time is the time in seconds of non-movement before the player is detected as AFK. -uafk_time: 30 -# The grace period is the time in seconds that the player has after the AFK Time where a message is displayed via broadcast. -uafk_grace_period: 15 -uafk_prefix: [uAFK] -uafk_grace_period_message: You will be moved to spec in %timeleft% seconds if you do not move! -uafk_fspec_message: You were detected as AFK and automatically moved to spectator! -uafk_try_replace: true -uafk_replace_message: You have replaced a player who was AFK -# Fspec count before player is kicked. (Set to -1 to disable kicking) -uafk_kick_count: 2 -# Maximum replace time, if the round time is past this value it will not replace the player (Set to -1 to disable) -uafk_max_replace_time: -1 -``` # Installation **[EXILED](https://github.com/galaxy119/EXILED) must be installed for this to work.** From 8f88872559fb8adfc8a487ae580c892e5aa2b906 Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Mon, 2 May 2022 05:25:19 -0300 Subject: [PATCH 003/146] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index fdc90c4..c9c1cd8 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,10 @@ This is an updated version of the original Ultimate AFK plugin from https://gith - (Optional) Kick players from the server after repeated AFK detections! - Custom broadcasts to AFK Players to indicate to them if they are AFK. - Works with SCP-079 by checking camera angle, and experience interactions + +# Permission +If you give a role the `uafk.ignore` permission it will be ignored by the plugin and will never be set to afk, useful for administrators. + # Installation **[EXILED](https://github.com/galaxy119/EXILED) must be installed for this to work.** From cc0e25ceb5c4b42a4f52ce67f864c2d18e55bed8 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:25:44 -0300 Subject: [PATCH 004/146] Now works --- UltimateAFK/Config.cs | 2 +- UltimateAFK/Handlers/Components/AFKComponent.cs | 6 ++++-- UltimateAFK/UltimateAFK.cs | 10 ++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 34d34dc..4834ecd 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -28,7 +28,7 @@ public class Config : IConfig public int AfkCount { get; set; } = -1; [Description("When the player is detected as AFK and is in grace period this message will appear on his face. {0} represents the seconds the player has to move or be moved to spectator.")] - public string MsgGrace { get; set; } = "[Cerberus.AFK] You will be moved to spectator if you do not move in less than {0} seconds."; + public string MsgGrace { get; set; } = "[Ultimate-AFK] You will be moved to spectator if you do not move in less than {0} seconds."; [Description("This message will be sent to the player who has been moved to spectator when he is detected as AFK, it is also sent to the player's console.")] public string MsgFspec { get; set; } = "You were detected as AFK and were moved to spectator. WAKE UP WAKE UP WAKE UP WAKE UP WAKE UP WAKE UP"; diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index fb7dc50..7e9cee5 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -193,12 +193,14 @@ private void AFKChecker() Health = health, }); - if (UltimateAFK.Instance.Config.AfkTime != -1) + if (UltimateAFK.Instance.Config.AfkCount != -1) { AFKCount++; - if (AFKCount > UltimateAFK.Instance.Config.AfkTime) + if (AFKCount > UltimateAFK.Instance.Config.AfkCount) { + MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgKick, "white"); + MyPlayer.Kick(UltimateAFK.Instance.Config.MsgKick, "[UltimateAFK]"); } } diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 5948c1b..0fe4563 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -32,11 +32,21 @@ public class UltimateAFK : Plugin public override void OnEnabled() { + Instance = this; + + Handlers = new List() + { + new Handlers.MainHandler() + }; + + Handlers.ForEach(h => h.Start()); base.OnEnabled(); } public override void OnDisabled() { + Handlers.ForEach((h) => h.Stop()); + Instance = null; base.OnDisabled(); } From bbb0616e3017625badcc9b6c61f13e7a5519c8e7 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:29:11 -0300 Subject: [PATCH 005/146] Eliminating VERY old things --- .github/FUNDING.yml | 12 - packages/EXILED.2.2.4/.signature.p7s | Bin 9465 -> 0 bytes packages/EXILED.2.2.4/EXILED.2.2.4.nupkg | Bin 252538 -> 0 bytes packages/EXILED.2.2.4/images/Exiled_Icon.jpg | Bin 27350 -> 0 bytes .../EXILED.2.2.4/lib/net472/Exiled.API.xml | 3669 ------------ .../lib/net472/Exiled.Bootstrap.xml | 23 - .../EXILED.2.2.4/lib/net472/Exiled.Events.xml | 5267 ----------------- .../EXILED.2.2.4/lib/net472/Exiled.Loader.xml | 318 - .../lib/net472/Exiled.Permissions.xml | 283 - .../lib/net472/Exiled.Updater.xml | 30 - 10 files changed, 9602 deletions(-) delete mode 100644 .github/FUNDING.yml delete mode 100644 packages/EXILED.2.2.4/.signature.p7s delete mode 100644 packages/EXILED.2.2.4/EXILED.2.2.4.nupkg delete mode 100644 packages/EXILED.2.2.4/images/Exiled_Icon.jpg delete mode 100644 packages/EXILED.2.2.4/lib/net472/Exiled.API.xml delete mode 100644 packages/EXILED.2.2.4/lib/net472/Exiled.Bootstrap.xml delete mode 100644 packages/EXILED.2.2.4/lib/net472/Exiled.Events.xml delete mode 100644 packages/EXILED.2.2.4/lib/net472/Exiled.Loader.xml delete mode 100644 packages/EXILED.2.2.4/lib/net472/Exiled.Permissions.xml delete mode 100644 packages/EXILED.2.2.4/lib/net472/Exiled.Updater.xml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index e1409be..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: kingsscpplayground # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: ['https://paypal.me/Thomasjosif']# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/packages/EXILED.2.2.4/.signature.p7s b/packages/EXILED.2.2.4/.signature.p7s deleted file mode 100644 index 1426aa3a843196940d24807b3b92b716ad021130..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9465 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 Gk^3JJayHoj diff --git a/packages/EXILED.2.2.4/EXILED.2.2.4.nupkg b/packages/EXILED.2.2.4/EXILED.2.2.4.nupkg deleted file mode 100644 index 40d33c42b9d1ccd357e02afef180436d78cb8f1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252538 zcmV)-K!?9jO9KQH000080O)#BQXd!~jTHd^0Q3O>01E&B0AF%tY;!Lza%F6Dm66M8 z!!Qs<_l5q0&_0$QsvQ?Qp|xWObQQAbR+h$&X!Jl*Li_g>G=+pxXqUsxIo!)M>vwfT zXK-E`-Qp}sF#^qvka}+MDNKtcUa#)&njIWjh&TQqtw#~kzQu?0wbA3!mm7z;XX zCgT(fadb1WjK8rNNS>x8an}wUtq9$C(f-52537>g8E>YLaHB55m@X=_l%y3=Ov?#; zz-Z6h457tj^SXW6^oc%s3ml`ZXz^|{lzq`XP)dumES@Ti$X^rus~1ram|%esZX7JE zi?j<8{8heh+Jiew4!<*Gq;-`8tHw+)n&Z=1QY-O00;o+dQwv72I@-A0RRAR0{{RG0000*SV>GpL@sW1 zb8uy2jZ@o-+b|G)FZ3UT-&f<9rI$tVQc`ybgk>Qq^r27`d17nVwp3}{O+KmL(1(To zP=BGyT22h5v`^NVGjpUlGye7CNnG#ShJ8clwdv-`jHe01PMK17)ja8Gckw2<&R@O| zR;mYCAq(l9pC>huy#ny6##VaXYUPYKJL1Z;FysaW$kJH`=_}}ZZ&C5RFOwj~m}rq? zDM>z~fOVM{pO&lQy?}6x!T78^;~BpcFv7@^rUp+{1B#UFsW#5%n;KcM*A13zgR&Jc z5;4y_c0n`0k&WCRX0x{fVkGhozxLYUN-OLW+!Xacs4*+Q_B(7|fCC~nsUZ%Y9anb9s4e9&zei2pn^_+=SF{N7f< zKy^0BI&+VT{__O_CizjJM6aB-baei%>BCsr&dC-ZjeB4_f}t#Oi! zJ8%Kh$|$CcJvd#}lrP2;`}uu3BbKFE$}WzdnSgWtX(HBmeY}9t!Qwo*(Uh>>HN#5& zqrZf~xh;!b8CG56IPMUwoarqi=_;fTW0&)U-xiw_yyUNWHc^5$`1R6>{7n%S7073x(82|tPY-wUIZe?^dH!?3pcxh~9WG+EaNiJk; zZ0vn^d{jmE_sre9cT4If*_MQCNaJSH3#2Rr0tt{%6qPPj5Rh_V0|IY+%9iC>D6nIWt=T_2=*NzJI=aJ~?;3-*aZ>-03s-?q$%Z zYnX{Kro!Ke6O28<#J^tbfBx42fAPMj;@KA4&dyKpfjc{&Gj&=^=Io#`B{*Sb=6Mrl z%`)a@PMn+>oHr|T+N{imGtSPOX-t}292aNLk!hUS$k?Q13Y$=G{wkF2cPuM2Mk!^i zI~R_H{$+}6c_^$z)LPgnPCWiIvGJFN?<9OfiP33AGbjHW&)HnlU*G$Kqc}U~uhswa zKghp-6fB-QdBI$`E)HSN6AhifKYhbekzxGJVLq!qP|SjLh|O)S7DABSlkIG<+M3QP1X zG@=wVl14U&Lbsknd?xWIja*E|<$pV~p?B(snfXJ9-Z3gFvXKX3grp;JjV?(0lS?gZ zBHb@@qy)i(*`6y?z?EE=h;2_|xpOSw(n=IXYFHggr!1ptijL z@@Xvu;X2O!d5THZi}0?60-#Y$6^`{?!muwgN6UwtX?9I%JBKq>Q`?5(#oRUwE+*}` z_7P}Xz}Buq*SF_I;;I<;KQiuc5qE@$J5t8|_>{Qaq#H&VdT~_{RI~MRYzh@9Dp9F- z1?lz0=#^M18%%{+e{GiPvkSAoNy_7k6E;`;wT(Mfm|ROH3H-PV8ZW12V{{8!sVFsn zH-!`?y$WxlxV;*Syhem;F&q$mDh))pE53nQj<*j006_SEEd*aOdx^6Iuu!f zaVAD`V5DX3acD6_SYTLJ(Bdq#NER0HXmPf%z_4yYi*wK-NmwMH#ZX~^VO>RwVQ7(p z7OCwnycD+&$1)j#l{!m@I@8?64!U}ZZFEhAuIBdlCcD;=*xG-?c9L9brgxS&a)I(0VSWHwH5wi-|(J(89Ie zpcvy&GafY)K(tIm$DQCYG~LX4w_86C>#ib=IM>xs(p;YuN=)P&O3bYplku9MiGX{g zaXvLMxCa~cb^NE0dnUzL3K!KJug04dV<8S<#te{#hQ&7({po|Z&oC}@9%jM&?}7KP zs9NhZyr~Ol=Ahto>`KlSNWo4?#%uAGcq=wgjpikdZG<$e*oS6PPn<=)rqzIpbYDsF zQ~EK`)|@c*@g!5cwa98|oekOoCEnULo7!Z&rELxc)Fkz|6KaG*n+;-BfGs*xz}2>C zWTCaTz_o3z1oIHgCtzy35U#dZ%_`kRAJA3sRb}8JgMQ>tPVrA*Rav|M?|C5>(jpKE zCcPDJO3;f#wBCjS%B=wU3fy`@uF1FvsN8t7(Lq<$Sb{~UUyQ9%^VjO_9IHk(E{Tji zSZ{VL`zcsEOR?Ar;xyw@F#>mj2%3?KcYk;&fz1;`*`hUl_nff;)q~qt!o_7=Dcrh> z$@PfBV3GtcAfKx-2Uko%vS!&3Yi+#-Eh`kO-{Kw-W6`dfR{nVkOo6*`$kF6l2G#>C z7UQl$<0NKW3)j4mmlmycHRhAzPlG<%14frPoXhsmT-Lq@>*6{TYv~5pVeoov0WiL) zr>Otb+E|adwQm6FF>U~vcI9$f&;+MJ?}r78X;ZEzxRAnLtre5ql$#oCLqfliVr|40 zfEA~;js|sZktq-y@0@X*;z+V;6Jrr{eY-<7Zi9`=nhOsz>4m2!9 zj4jM5h86~cZ^v`NTe0XL0YVIHVo)Jn&n$IVe9tT_pO!7*A%+DXhZtgnVHBH28Lfk6 zp!a;K`lIND8)vDHNuJ55mMXTVixm=i&v}|JG=s>N|-8O znuKWrrc0PEU?&MX378=vU8%pVmUD!D4$cSUj}FtJro(iq1%C_qsEu|(=4!g!vgx4T` z9)f!?O#AiPLy5LFnEY3VT&Yt_{H5{3;Cei_4aM0h)fTSWL349};q0mDFnzCdnn zvO_zSZ0So*h{%ydZivX0L|%x<6NElbrqv~+=prrk5J7>yicW*pt5^xIVL>!UF(%`6 zr22}|ppngZLoj-HtZUsF9Ss>%8&Qy1ekim2=*%eAn_~K+J^?EWT+licoZ~ebd<&IS zzkt48##=OTIxE7Q<{n|}!jaq(+=)$kHx9G;mf(kBkYNda83xIg;2&XNEwH*^5$f@K z{i*1qKjfo7+Q+x?Uf+qnv$v^0-i^L}GWz#v%zLLtdtVfYei>E^WTsz^fs3v-4q=coGJRpvOroIMNGf!X-=o(9~A zarab)!HbeD&J}^w;&c)46eovNb`kK>CN4T} z+Im6ZnP|nSpJ1X~(OBQfMUyQFHPw=+rf2+$p8tli(_sGsOLCS#<3LzxGJa=reJ2;@ z0kJTT(K{gJ+#*w6L4Hd37%Zxy|6p|DWx+L!wn;zEa9JxBWY0z~AM#||%rc;et z)zggH)Mz6!07FCO+9<|kSWqfD^bu`(H>+ZrrtgSuR20n`owkT(J2mZA`)O&n*+tr~ zN78=d)U;b;PD{HjCOYj{bRaI;D3%~YZJpxl}B1UmU>yvoYz=16j+eeDO zH5vJ}QM?!t3ms9EsV(_0VyFD0*eQP=TX+7G*oNyb@^=5DyvgdG)2xa#7-ye9;g23B zJZSpSNv5>*=KhoX@p4>EMWUrf*Hs!;XgV?*PR~V#N^OIwE$g3Woqc*){aJQ7 z(S=NdOD+$cLBoaupYu4{^RV!{kR!Mhf*rfgvDj1mnt$x6T9c74`vNmLYmM8R1_r&bExxxm9_rE6IeeI%;BZ&kxly(7cE-sp11HBBjnl#iCBvcYXxARL^jwJ=5(V0-RYtP`-DNN z5*!)^aY}G{2ow%D<&NVDIwhItj+0e~YG8L#XbVwjcBu9O8z|{+KiUKHAH&o8$PN2^ z$!7S|6r&O2TTV^7`e|%BGIguNYFrHZnme&?Sn>Q&tJx3*9v1r?P*36_Se|G z1<47)C6K|cn3Erz{0lfog5J#Kx+^7iFS9~JsUIlz>v&cII~Ml5*dK~98~(n*Gc)*m zhDZ3&zdc<1!4?VU8=hejr=1g{M%vsWCC_hi=>zy_%T|WxY99FzSs#d_=^$E%48|H* za|(F|NVOFP4SO4kQ&pz6A%DWEG`zy1yX~yM)V38wW*!{YPPKBm)W{%R;Ez(P#@Q!F zRZp@NmXQgQiVrZ=6mK<#hIFVlAOF5$c&H*)v}Z&1!c5EE;Mua9P$m3?j9U zqr+KuUi(-G9M=xva5o$jxgDs{p=lHp<|z6NlnqLKBxqqSRr+*|RZKMI=o_$(E{Gb$ zO=9#`&BiG7Y?%q4IBJC+h>$}mWs%Io$jO|}rEF|uc>a$pBu#jFY!~?pzV%!zG&-ao zLk43>jw3l9=OAMO4zALA7*)fKIMK3Mn)nrb(rw3*IRB)b z#si`h&&N^>ib9pg;3iXnS)7hfLFHr7Yj`tMsg~D16~gf`~W4K)!} z#{mh#E(}v`#-ba86*>tv1s{OXz$Lg*`b8Y8fxJ-7F5?2}ybGL9K#N*e%jdZNQ`(?F zRsU;Zk8I8@O3XXuxpnYqNY_NqXH_@dhcO-R<1BhuBi50s$!yG^l}r?A%#_OmY9v`# zz5l`6E|<%g#l^Dk)TVQK`w*tGEx_`Ed8jRT@d1=!U@tB!Ga0j~K_o!!&%tR{vEYsY zmUVg%8^af(G3epO5Sd3^EWKQ`h3$%|ef-JY>oe?LE!4eGqsQU_V;l;%JoTUr(|jJa zd%6(8U|GF*f(e7k0v@%RBhFaJ#hTPi&z-<2hx^J2wEZ@3Ez`KzMCK|SZUKC!qVtc zYZF6o0>(M(B+hJHF3U}2zSGi4jaD%W_$?h(F(1aaVKyEwZUwl6ysm$;awo8!X>`@6nswz z)6x&xKrle*r7Fg~M5F~9&7lXY_(BxS2qN#`bhBKio=mSknP%d-KD1$$5xy7WK2A@^ zt6^AX^ng70G(#BmXi>wcT@Fqb=o zM=+t+PD-fIyypzrp$Zp z#h98k$znW-M4VNB3eC|~Sj3wn4OM>{vx;wj1{1It&tio%g2DEa4U+Y(aA|!m%E|{; zubgZZ+x~pSAmo(s0$i*>z6jU7&SYC~ClsB-7JX)Fqr=tO!^dGuV7C=&HZi(}-aY8K z929<5Un%+}K2!S|{$PcSds14f$3V`+JNgX z$$|f6)NDub3W(T(MM;Xj18WL&uVUq#QetsbVm9M7EQ&!Pqf<&PDN<_EGCO2>O3B4g z$q6IK`E_hai^K*=Iqs?s&o|)l8>s6obTAinjhc(-+_`{V@u--K6c)=~-Yvp-b_K%; zzO&&_jGdg`_%s=BVt2|1)8j6b2JF}Dll{fwg^OL`zO69OVtj|tz^Ef%%6J=#>>ZJu z{w`dnkJSH-@#YKtdl2>%$47v81W?}oJ}Qh4&{P1o09)HX#Kb3u)+Cc?P4X?SNj}1U z(198~1(zdEoK$+dul%A8drZx-)zu^b^c{6cvMF?86DL z&;s+@eld*dpUNvfx4=aeJ=_@2#26b0ohAk3Be?D%f&ydd7)Uv;jX(Jq2n>6K3@I>6 z(P4S7=?AE<3Sb=Mv`zx!x{xsinuT8MclASDtmUGp&xFf{hZZ^5MlP^0TysD_g9f1` z!hHDw9ad#JKC9~JJspS6%wKD*RlOf%7@T_Xx0zkf(K6!{PeRu`Epx@$@0tRuRaE`Z zQ5qM>HenF8f!)AX$5{~nQhRNBpr;vwO+OsX2;8lN(h7`*2-Tl!h(G$z>qVI6mdn1$ z4jiV0vOUE`fr+M&HZU3@RzFGBk9muW19M0M<1S#l6!4POe^=Ho4m?F^Vkrl9Vo4pK z>^@kmY>`DB@AKY-u3uhy!8*)l`ct#k2JWE*lCS!7ZQwr22DO35Fo8{!fb;(H>jQ^K zHEG2~Hw3;_LumzG#KK!lR>Pb7-GyoPxFGG$Ks}{-+%@)J0jnvb4K#p%)q}}8@v0T~ z1h$dYBcHf82U2**)xbzhQzxtPy+htWe;*k5&~un})66fo1bUF^w#IHx2KrGDwSkRT zEZ>u72QSWeA&^PI`IU8V1in>5X$1zM)f=SR_xQp0G0i`cZ+$B;g%aBOW5TO6IqwsT=q-gS4!y38~=4Y5K9qW?DNO*KoVLlz;$6TpvhMRYWHDA=Mgz1OzuQ- zFp-nPx*C3fdkbE*s&O-7Kk#B`b>K40*+#e?U zBl$FuKZnU~NZv_gf+a-eAi0XjE@3hW$;Cugg-LwTy?P3f!^7k^SW0IRIXz5%jARcY zTf^jQRR2U?9VVYZGMUJm!{nVv9#@du7ADsq`6ZG636mEi`7V(^g~{1SK1Zb48Y0Ic zc@L4!FgXy(>xj$`leI{85ZNb8;=9q+(}_GgOuCR9M&#r$sUlfVVZ2-937g59TwVbTkH3@ zLaa!ojX^=Ora3g@2i)0MG4vy@npOQLB-X>SSUv9Q5ndKs-6LYH7T82ggEg}jIRndQ z)7M1S#^Lu36`h`vor?>lGjQD~2;t3WWQRmpeGC%k2qG2NjKjq{EBep4IvQ&-KEu2WN4T{xN%*nYYLw7q_+2z@3Zz4FH82K zr7N_m2l^1@OKGuSzP*IH9xvTOWWD%}h!s<|DYRw4uwvK|2t{j5l%g#rN)a2ah>cRj zMTO&|!U-7eO<_F|^K>YuKL2m;4K5WX(Zz^*fRWP<%YyKm76e*gzp8bhVS$!HSBmAo zq8$=bWj{?IQv>$%1szC}4on(OGf77iq)F$0s=bj+lCfsecHJD>R|YSE?(Ea`6f8e9 zAv-8pr$S5l4!pLzFh#elA&)|>%_}acifD9ng@)i34MCp8_W9qmyHFd8)#S^2X^^JN z?U5;BdVT+w>50M3<=3(*Djum*jC5>o(nq&Ezx;oZX2?r7)J5OZ>Y@|Y1=6I0J#W8= ziCxb?Eiy!dwtuwNhgxKa_803b(V+}#s}xoYAGJySN>pSD#g(M!EEFpMv}Q%ZMs`#< zM?_db5ptsxc~rmpdO1>Uz^hEFOH`aT0@&u~huSEDJexx`0$4W91k6Tv z0Al_sf5AVjr3dz9G;FEEP;>aqTKJuh`45QpyJy&Mw80+*`Ei{vuN6iKyM{kW>xC8x z&@WG1)X!to<4AyoPGgTVz6urjj)Jgv%Um5uc|Ezl;Vf zml{znjj~+opj`S=iICUHs!hL(N;o#pCT3kcP0;V*R7{wRLxrO07b%*p(w9mtbp9aE z9Zp=jWq$8U+Od6g+s#9IUrn3gd*c^pb*+O9>*)EOu4!6k!tb`C<0gS!hxwtk^tc0k*QZ$)OCYRn{(<6{W3E$(X9USnh zAx{F=;3lA%tV)ibH>PWomU*Z&<19>Y@ro@&0w#)uRNyt-Rr$#D*DvlH8rVvCZM|^H z=s<5ZltADxu9uGy{<7k`>6nlwZ^xLx`AUci^u;{iBa{EMUf&YeLw&O@u@cR>YVC*{%ytq2Bi-o8F{(!f-JDFUuqxeob3D7dXZTWUf;5;SdNnkIo zzg=YIx@Y3ESY|&xJm(2a@Zx#jJ{pK8Q=|gR(NrA!-0{}VCjuiVxcrN&o({aFgc1mN z(CL>+HOIB{6HI8B_1_-^)+r$>a0hy}iA=UE{Q7`A3FyAJ|Br#rRAy7pfAm*6>X5tP zJHmSiC9)fSD@=YD*$p2JlY1k(VP{N;d?T_Ot_qV+MRvnygvq-iyW#0!a&2Tcyd+Fs z64?#k946;PcEc}($#Ie0@H=60P-HiJI7|j2yI~$1BJ(4=VOyAt+6`xi$*A3ML70r% z4cCXssNHZ=nA{oJ4UY?xPepdaQ^Mq3k=^jTFu69e8(tnJFNy4i*M-SBk=^j-FgZ4| z8{QTs2Sj$m`@>{SWH>c)nRgNWH)?Am|Pm#4J*N?LuApsf`v-(P#8`txBxe?NwgDA zmiy$4&|cU_``k>JY)-;rN3ue8Fm*-2;D*GVvzb z>+ts;{NWSk>d!&!IuKf~`*$6T1P?`mpGAV7M}l8Of?r00Uqyn4Bf%q);MbAhH<94C zk>Gcc;L%9%`$+JINbtu<@TW-dzmee2;b1T>KJ;+~TCdosB=MhEhT}UVib<6}_7*Qk z`8O!^ii{#&kx>P2I2f;)-(=UrMo0@p+0<6`#AZ|v*1{TV z>mbyNh*&8bS_h-idte^7t0?rMgiSvK*gFs>t9#9Pdzs7czy&L(j}8rF-7aP(e0EFk zs&IN=)6w#S^mFQIeoj5v&zkS?m?`AykALQB&0E+f|BG(f)RjdvQeyu<6Yc(uZ%$0Z zpLf&kv`-H`7QBES3;v1;4#D+$2Ws%6G6lBd+RsRzjq!7UXrFO%q~B0`4n;x@euAbz z&%+ZdHL4|oyFa1fP~1We!!@s2+?_*h8%xj3on@XVdxu$TMYS=Umet{UEynU7zpPiT{6)Qvpn9c<<4&#D%v0($^B>o13Hqr>KZB?F8T`9?HT{{ZqF&94sMpLt zsn-^&SG=9}nI~7#?~!_)8C9>Ea#Fp{IyurGk$Ro=xAm$jr`Bsw)a$svsMp}1)N3i0 zmqnJ>+|$Zy?qAlc^{?u6B-JZL9Di!P>ZjDJ{*UXm4E?l8Kj)q1=e)nGSNmVrYpkqS z{ZHz3KGiGUPP=h(6&;V%s}WVNaVOX7?2{v%h}7%szpdAp)9Q7esMqm-QLpo&>NT`K zFUNiG!07$?sFU{R)4--8WMhm*rvqZIe6HLpH^E+c%t?FY*0E?-PeAIk35{TZ+>}W`jW0)wahac2hZ;6)G}W`y3A5MT;SrF)o;+w z&DK1)rFe)i4%~tZnk~dX{NPo$VkF-KSKU&4hQP&>tJjgT37^|w8X^K-*{a8VNv0)XTs+);tT`deW}E@9ED8Ucq zK+8zLmN5a#y9>0N*7FqlQPbi{aBan}RnQMh{PzfEF@B>$pDa(DqPW)cF@;|ly`pZ) z#1G&ZcydecTBNArC#a$K6Y;F6D?RU-qTtD;iLT@IXDQC&@V9{QR`5$be%(;S>nZr#Xz}yh zr<6wgE&{%Og-qOSM~mKBeGqJA{baBV$4^sJ>ZNt#5P$I7cJuZp~ebfLfaC|k-V zUS#2dT6IIcra_;wvJXMijmoMYJprLhjOHF;agS)34Yw1k6)6_KrAX2IuDF6YtIcK0 zb&rVCVOwmsX=UHKlGTDls|$LN3VgCk3WsVbJM2z27qqyOwSx0Z4$VE%2!cX!m}RKg zIz(N%b#?kj(#*P(ow8;yC?JX{;-mqeOd*Kv4*)?CnXp(1~a(O2wj97_Ab z(9pPmhQ=gp*9&N9M2+spxC^oUG*UrgfoZ`H@!04CNj5cg$~6Pwp}W1$HI0KN+D=TV6!vyOYxSMi9sNkkZ@Az^oH2-lN1y?H2z}U z)nYz{U1&7L&oHGZ7HUS?gv`~P@lkeq7gA(iet-_{@$F#$M5PtKD5$g!C5hh?6dxlD z8O#@718f~m`Vl1Y%Yot>g(0h;De?uP*2}OV)8~z7>XtA$fp+pt^Sd*~= zhvC6gHPgaXQ`~Y4{kY&{Kh&0$6xn24sr)12G>_)|ox8zP+ztK@?zWuPlEO*N{kweT zos!SI{~@3Gr{xng(O2b^R-CcLoQ$7;3YDD$#<-3J(Pg(mb(L97`c*hXTNAi`HRcv{ zC*b!g3DBG3&`^7y-Q1hYFUI05G{3UAw78~zzsc{6>bx!OWFN~HZ|bM65wiZC%g9yV`6BogjP3v+@=^C4obiB z3Vb+&xkGEDXR$CwGkhU!0T;6=_{ypvjPI*)(Vys9%?t47lr}?{(BS}?BCps7=|e|s zV7|CJjScR+FrcwMStuVQxh8XKZ8CcZSEtP0`O_SY{YWM|StXm%Kme{7V-XdryDD39+it<^|Jj z)_AtC@0_}L_P80>65|?CUWA{gW%EoQc1dMgUn4!9O@`_60rqJr{u z20N6Fmg@@V)M>1TivG{EqGkO6l&u3M_laja2Vu&W6`*DRin{_?>|PU=K!b`UR!w)5 zWkH*jD%yOWkKser7>*$iQz}r#SJW2fv1f}fu7Q#-!Mv_6v-FDLBgr>2l-n8n2Q$hy z&3M{5q3^{t@$5khY75KJbDs%gZS2n2baoR=BL)|x@nR>mf7WzyO)LKbpYpMfpf=-K zZWa0;+t8=?bSgKEO{KcCd$9!Gtoa0LKi-OUTG?-9G33=LCKqbI0CU;VH6}NMomY!@ zQP~Z1aY5-l%^&n*Iv=N@&3-7WS9n%2Y8$MY8Dg3CR>rd*T^N3*3N3q=V#*a=(I#C- z*^i{P2HSuG?kk3$PbJU}rSuzru!OM_e6kbc{;Z;(FZDnVKPpA3Q!f}z{J+clG;8e6 z0t{b4HF8@nhF>m6->#<;8(ZA!cd?y0%QY7p>RqluJ5NJ@c6c$zVAq7Ac=nkYd*67| zT4)0omtYE$p?osf+fXksq()+dg?^O~TF!==+QNAD4Ykz=dr%9pG4_^HQj6i{uIS12gDqiCM_Jl!eT7RBMk?A6X7Q+u2(b>rL--8A&WPZ)vBzFgY$Tn3ckAPi5V5^hb`%z5lk8kYIp#Jmso z2l&tUlOi@e54}z7gtgGa(x-Pk`z;RTbvY=rVo+W~%u$jz5kHs0hjLN&r0`uNH^rlD zrda*mC~Jt>XhnG$*|eD{O(DG~*FjigeW9H5*vyi1pawh2vCJ>dUt5>NzVC@O-iun@ zaxZ?@jIjjztUS$eu$bx*XKas!fZSl42;=bB z9z(z`lPv+=d=H`+Xy=F$^8_j-w4zL)Zh#p3qX!~4JDXD3UxmoW&SOge%`hPKK*%r)G?UO86;VB#Lr5cCZ`ML+NtHltgl@4T z>dRIT>fST2=MdJ!ZXvXg&;WJ^p*e+!2D1AIUEmjJGoeeR?gc_qDBd9UGNIMQ0=-IT zt5=}c3Eh?}&|4gCkp0-{$GT3+Aoc;F{e*@{bO)i~5*gHhlc;zwq7xDolFrTrdY4j(m*{{CQ7?)5l4W0s#*pP; zK-lm+8n(J~0GXg94l;p0B-Do^8qWVi=s`lG_#Q%bvOJf6A}k4w;rqF%b0WLSifAl9 zK)SJJL=*VuQMyU|Ytp?*@uu*jgo-GKsr&~*w=0OI@&87}yMX^fx)EdvOK1hrEJ8u9 z5PF~DwQw_`rKFq7Erd1@n#XN~Y$inWc`Ts?_ms{s4KNt|U9yc-@j*#u9tPf_$US1+t`v z4`i<%`N^90rM5#E)-lXnkvy#9D-%kaRuSfpC^}1 zl^V)8Gs=yW!mLJ=3rOxRLwSJY4w4^{{B$tNBP5@sSoe}ls75)TQnS^f{6R-KfO71T zkFp1Oa)i=}BPN4l9Vg3M{U}TFP>!Hjn<%xp6m~XK3QkI4Aj+*IZ|O#sy-@y_ykcjd z^icW_5MM;ORMGv8Cs{)>(}VJwDwHO=>*YR_w^Q7h7?gLXQ`thPhRVOFrV*I;OelZ% zqnuxfvThK{&dn&BohS`6%F+Uqdk3PtpX6L0+1H}Xp;#`mSwZ$1$qEB!8<$ z`75RHCdok*tA?}_NzSFzMv{lu5Hq|H<;5hgCvV%yrk5M#wN{i3WIv7go#c5IG0Vx% z@99p{>QK%i{`qv24|hYkMr{n2Ob<&l`Wc7vx*U{QF(|Jg<|xUVh@VU0L%Aq>Qur>C zo8nP6Q>^}Olr_X`w4%I>Y}(9}rjTBg>mZCibmG`M&Y&_mgUSTuZ61_&n^BG?W+}*! z&w9UefcYsGQVM`qwLfNR2N8BcL5s!%#fyW9C@QmCw+ zE1QWURrXA(O_aO3pj@6lGpaU&z-+5Qc}FA47f2rOk8%x#Ujdtt{ch*0kV_92%9wsA zf9;F%te97mH1>geE9@%f!0e{6{ew^n=11pRMPuBBval~Q+xdmb8v8nb3CL~U%J^=~ ztSw7+u~BRV$a~n@WX^nfS_{0?GW$$m=ipNV^qW!5>hO)v|eDbesQ zD>TkV2;|ACbpV=8XgkZPJCST+k4yB0tAVTRh(u+%o5TNC*7mqhW9y>Ocd?nK2^76=qL+$N0XefmzW7p-Stp4;PR|9DC(&)qbu5JyOLSjzrYVJ0 z5n5}#D`!)Tll2vd?Q*}$oa{`AUUe6yc-VM}rnoCo(%6j>z32|4_}Kdr?Jnw*k_{_j zfj%v5O37umY=Khp2B+k)Qi;APX-d)AIT9_dX-dgw7fMv>h!v zmiSFECG0VYob^*uO4v&hy;wIL(C;|{J>P2{pp0CBF0AbU)Jvj&_g(>LqC`u2tOc}G zqOIL-1++q99) z`XZ$#+Y(0H*`F2Z0_~}FI|s88iSBCZ>^zh8lIYIm_=ie7(GP*~&T*_=qJ2FUsN>nC5*_I=-Z`FKDbeJd1?mL0 zMxxm{NdU(0D>itkbNM z0qv5yGaGs;Q`iSmH?CoZa|+upbtPrDIj6EOrLMZ{Zb08l-IM_jI;XK;rS8H3j{!23 z2;Ig(&pIz)F(pEG@1SjfQlxILrBRvA(j}T!fG9^=wiUeQoX(1*uB>9Wa|Y`wbqy8o z1FDm{^KA`$COcE2nJ|*iWamkA#I~8uV)G>Wnb2htS?!5{Hb~^PZ)OJDMCcN07tk5( zUa7MbZf3LD^AhdpwAVSC)s+e~Hl_}AeM?2WnK7BB+3XCdTa&h#&0!NHx+^Ua&|HZw z16`0Uljx|{$sA1H;Uy(!VM(nLV}CAtN4 z^VpXXJqo&c?0bo(bU)yn&wiDttGABLXG)nsjowVtd=?|o7?@WsWSt~xiAPi*(ULw# zoENeRi4HgX1SlZU^m?=$B+;^1MB^md2=n`eEGW?`sJ{hlsYEwH{VibYB>Jp)fU=O? zEzu9fxu%8eMTxfcJnme?K9p#8Pt~=E9hK;}q5(=PGnLDlhIp;aCDHx)F|NfdTcY*e zk>7g+$vdrGT!N=zUj}>mqiCL}$T0 zT*Nj@Gz0G8BKDX>cXG)=Q)()Uk`%y%JR> zWSTB!TP0$kyM*nMCMo?jq}gEW^iYgd8V^KLO;%2Fj-o_9N-o)V>X zY2eG)5Q*};nAtKmNuo6n?=m)DqPrp9W$Xrtw&peP%h_g$-pezy%h~f1-BEVGc{zJq zqAg`x03DL3!T+@R3iiE3=lFZLu3+YF0>uny;44^?L^<6XTq~GgqVnzo0G%mORo&UH zm29d+z3WB+x=Nzb%FVE9zf+>_m5G4fkm%iB6J1xagAyI=H5HJ(N}vg@Tg+Fpt`g05 z-41AmM9VCO>l)TB(S!OI=4;qp5*o9*Ml5 zyO!;i=s2{A)$E`|m&00rHH)heXl-gDpj?Ue`RBRTuyTn!<%<9{NR(CH0ce~=OJPNQ z9h)Q3wXmYTjxCjF5ah6yt(9mL)BL^o)0we^=!ICy8~vnp3Ri#i`qK2fz2kg+PbLMD%S?K zfY4g@dBaVB?h2!OT{o}~!srRtjqFeuJ@48mUM(QB!*vtW!)TZ5X7)@Nz2~}xW!6#d zuv+`rbt}^)`qq+~vWeA)QK{=TcBVu(bUEOiQRp?wSnyI!I)KXc#5evv2%bemaheF$-OKP!=FzS{|?HjFIp2iZ9it!_wU zkFYrsZD>kxZ(%owk=OkMdqJYn`PuHL*}o;ax4`dyh8>Y;Mi(c0j{PiAK~9wn5_NNR`*-iyWs;@UBmWbln-Zy^ z!<1XSGcE-0)-miqMox}sOu!*)MC_yIY)6(IOrd7=RUP!8WyG7nW4-tbqGp?r>Elnp zEv`nVknt&M4%ai~(}-=fco5W8tKWoq*sR^9fNh3&rx8;}oW6ooHrTnz8*IelhhXYl z8okwWN1UTid9<(jplX2c#81mkL`Bd?mN0P-xVkt`ih*xtRik*@)%M6O#enaZpa$iv zf+zP6WKR~hvEqdd z$Zfr*X=s7Xhzo;8ZZYD8Mv(sL`bfV^w<-T8<^k0>QZ(92yhL>aEwhkYHKj*n<~JoIyJ{>ErUQ-8xe{EXqlB5=JaFCx>w8jvEkq&lHy zXd{;XI)J%!i?@ytcW4)c854rtGex)sTw1vGiW31YEgsW_ZADef@30rj=7ddXIqk`19T`lN|xEDug;A{3${O} zIJ1j7AILYsRow0JFlPi9W~+TM!PZD-nKt?O{p;K*RJ`?|Eqlc=pSpAR&ut>~E=NE< zXlM6lG!>Zbf`!%f4eqmlNuxfHC9{w^`BQ0?FMMn@@f<>QN)@bUxdj|jM&y>Q!Je2! z*t_(CpGZdNmBTNfopVL_l<$*FJ|}=xR~qKCUM5=_&4&>V&r@`eny4b|ucB_o-=O3N zCM4kQcj*)`7KbyYod7}w2&aTnPbh`qm@*4Z(gwi5F$EGw?R5o3haD2 zhti+DBDPNNQ1baGtb;X5Z^I~fxjspRr=<_Tb254vXB!{Fx6&+qT5ky1gotN1?2|xz zsb&xJ=bx`o8A6vT9Oo@pMf01j`ORI5QgqwA|GRp`il+%*YWN+WNPTF(u%Qyri8YB8 z==z2~$}H@PxQG|zeF%9~@!#kHzVpW}>u+Qtauv9|FS>;OES@uZm$QWm{kAW<7``Uh z^|;x>_7f*nj9JGUpC5s_PZ+s~+3zN|o*KIzzPm9I&s-;&g!qK|qfXC4ww%>xLM7oO zb&xCl2Oq9G!l&O9AXl7X0Nu+I{pu;;(Nzu`3`iCp)DIvTupDXPt#j+Ih{^+J3bra#HYe+r&$SC3?xFXHaxjMF+Nac;n|TPG>g}WIon<{@ z+16W72QC8v{rssEMbFBY1)M)BYnO=EDQx1WgaLCGQ;Qh|F7s?#w{FLe+6i+P{rZjK zU7804bPMWH41P5e&vJDwh?0~W_`qEi-xBTlM3jk57~+iO*#1=^m>E~Bv)4!Sh~n;t;|hc7K0z} zbcihWpEFUcYrqe(WJk@<{G&YauSii89mL0t%Wckuu$6w<|4no3+TI9f+JDAzmbiqp zl{w=U6eUH@OX1Cpi*3m6n@;M0PxdTFdk&=D6yG9og%{yUFr-or@$@+Jd~cy1mww+; zLOR=9koM6{wn%!>RZdNMd7Hb{IlNsM^T`u`D*vZadd)=c1?!^vqWoWA0rQh9$P?kp zp}T|Nh3LrG5M)Xmq|YUHt>1Z|-=dJQxJE6Ph0f{6BbNd2Wll~B6X8-os{d`-+@wfN_;9VS(=YM>P1oq?myBNoAU>H#%2;()W zDK=y?gD}h*Ag-Harlp42icVno7G_An<(;InZkdFCrygIYjh9rA9<(EZHl{HPv^wNYOWO)wjxiSykB? zM01qgU%i*pir_i0g!YcqqA0FTS7M##^K%q5+``YyA1eKR;=7elVIGhJmNauunm^YI5gsuUfk4~}$$J!& zp+#6IXa3%&?ZqA`!vnr3mB$hZYr|2d3WW<_lT7BrTjPmJ{~IAYD@{&`gKgc&B@70p zsuyRPpUBFTwWTlFIh%)=o$|MaxU-jyA2ohr_XV{h1&GZGXu=Xj z(GL#6XIAChSq7Efn8P15Djt-4=t$LcpFDS(JN5}w2Zh{SoRDUx=Sq9g|B3@C3lIXi)| zFkv@yMU|D|StqVhCpWt8{kkk{dtgPi`AdmNT`CjX;vAkt(kqt;_e}T%xmKmSMx(W4 z1HRX~VAxhmZ^|6K>E`PIZQ|jA{unKdp7gHhV~7n|u=19S#kBmHrf*BY28Rb7UO6GX zC#Y#(cTNnuz{Zn0%$rZ=3dE=xa#qQs3^&NcWdYGjrR_|g330ESz!YYM2UO%~S(o%j z&ANGuc+7az?)c)9Orl6nUT03Viht$JwfdQ$JC$I(?@})yZ_qvl5A1fXr|=m+H<2wO zZ@k0f2!OsuB*Xh$O`Aeq>sGTf|NInGb4b(x-^~@6Ey_c9;>@=~HL?MJTbLR4Oir5z z_$z0uWz0t1Z1ujU!1sb6pcn@sVZ&V+f;o+1szw;vya zT-}lgFT7`6;U3^|Nr4;h7<{L#S?%p-%v*AywfY#z z^;zLwZF1*J$|f|(K7$~{uJYz*rQH7Vivl&-3)N;)4%#c_RdvV%jmw=e!?q*EP>oYuyG8@uu|+RNKF>_2*nF`y-M*Ah zzu4`Go~>Gwv0UGRcHh|!Pu|c;!@u-YaEa|rI%!f*^{!yd$glPQExwQrDYNTnaI=@) z?oZpx(ch+OTX@~1kH+$e_n%Kfa?>Dr1;*W_#wrdNNLoV}ONui}-9=Czi2da_EK|aLGzGDMm`3 zv;&pIN)LxSQ`e$Ib#DOgUmyD|{P;9voqPpmMQOt-mrE#|tD@@E3=j>Nr0$)yxX*HqHq+Nl}6uo0|?>)p92dIr2d*?hM=2 z3z+s6ohj?a%7QCY^Dfasi-mu?fIhX=fLc}|3pTN;+!mQTuSW3KAul$%azNem(z;LO z>TD~~7~7=5A?7j23EvZsv;5m)!E3_KIxy1lsIcUa$Mw9-ik?0s_NHE+D&vZCUzfr? zi|O$yptHxO&d6`nM{K+OCOwm{s_q>1);TYTI5CoG7U2bQMV)CLD8*y{k;Nb+s|V$+ChLjIfzCvOOvJ% z)ga(^u0P-C{v@JL_gv+h!kw3y(k37Ajc>>UTu*78!At#eSl>NUFXTbSVQb;iV1HOd zOZU;{(Ud+iNF(JJd_30?5vVqXGx~uC{93D)J=b=1lkU<0Hb;8;Zr{0v;ON#=YL;Kc z`{4VZc1#n7Tt21Cu=5Zlgc{I&J+ufJCO%-7$HqW=cKv0=-tyv?9aiN zU8@+aqh(#?SH24+zhE~%rlv@AFDE zsKcb!+8IA8BIDES3hB)&{DP9-O&bn`XWlY4D#wS1wDM<%>RGF4=FZbt0qw-KfPL;d zYP$L8gx2VhoeYx7~r6`jzGAbaWkT4k}b+0Jx{OYA<>hCWAh4KUa|S?tx`;*_6f z81yiJQxZFr0LbPl*^JNlGbIa(b-cQ3EX8`Yc4_%oOM|99Xqgsi$XGs5pfg9)8~jxQo&= z)NwIN$;HJ~D_{cvkA7t3&6f*Mb?MwY^hfSi$oh23@$zKKG1ex30hhWNFgKloC`tct zbd`x(jRZNu9_A!<98I@TtJ{7n>sSAP9Yd}+UQyk=mRy_prWt+y@V|tVqS)KGoF)S@ik-RV3W&l zsx9q7y)InAk4I}dXQ5W(ZXXi?DHAI=$(K%SE1uZMD4x&H3$ake7OaYAQ!kbg!%hZ@ z3zopg_Yd7}x712P+iT6=Q9Q#s6F05SQ9L!m-y@LvNvWHj`PI30xZwz&nf5Fr z+p`Qu&siO?=Xat9EVWG9wP#SD6nrvH?r>EQ+=4v*DTo)27#sol4V>er0?Ior7pas z>|$H$Z?V=zFItm@Y-O8#DJZ|sIvQlMd?Q|BfF5ru)|GVS>P*EQZEy!jCf3&z?0D+UF;9dQT7vC|*h#M*@#X^f47{8_zV2jGmBgGbq2&5ws` zf7rM7w*lE zC~^4M+|&8yIWgF+M2tc(&{5QuG=3Q>D`^=OGet7w5A+l?Fj1?o*lBiXVPc?btYY1N z2u|}njgSXj3SAxFapqk4PC5I1c}?AAJ6}#DHcM98Bbjk+f^ReRURA0~iU|pP;q?Vo zBdhCUo~%DxE;?HmHQRd;X6sjfX*_(xIGp~i!@T4yot6)YJohE05I@`fB$FkB@tqdsQLJS1IzKh@e zsa(3K>0!OD#Y0_Wx=Z(u(e)<3er=BfC9?LA)595^{Pb|uQPX=46V80-UT#}LhB{`? zt;7SBxj3EicYBwA(At5=+JxxpIBhwz5PNF%!STS*s4o@E(-OY@y}a;Np%~iY=10WJ zzZt=5smSPqV)U(-2!MyzgS;xyc(ppiR1z=8S9>?U=x{3~7{jcnmWSl)I;GloHTCl2 z+Tvmw{8*6>{(Xx_OI0*4gYh8=8_Puz1#S4u-d~d&zy{=D|0nX<-{8E6wANUKVg$>M?5GI;m`ppUc zKxL`eluhd|4xcbdZ>2P;2I>mce-f^l1R^;{w|xACLA(#MeEcJ&zn_x;HEW1_rupt` zr3J`Pjafo?0+l%;d~&-kxZF8V3KnpsY$2+a3xs*@rQCSWhkGLh4iKL{yUw%DF1Q&E z&}ZugG1ekpNfz?gb3C<@%Me+|lGeDclIBz8+Ee$u!*0VrFD9b|)#t-k-E}9|0e75T zxW0Td?Kg{-SZQ@pvU4L8r~8Rjvj)PouBYs}oo)P@CsT46KfHB1JDfbvSuYk-Y`!sW zdBVs%e;$5v{*8@9Y|N|h;gy@aEo!sWSDb)JfZ(I?>+sdI{aNR_#el&UsC(?o?Z){G zcmFkhpK{Z$g-T-x|L*)&YMj#_8j}b5A@g=@oU)q|;~vOnGp}jPfT76lPnNoL%1{KB z7OXLrW=u6GY4ZuvhJ#R`oi8Y67ETq*K3*mbJ|^IG3c+? zUi!Gotetr;o0rhRYPxbz3>rqhi?rme+CaA(v5>Xs|i;4s=EmS^ZU=I(XYZ>5XRXs!1x2g@8(?Sn)xSit}{*0^#_(6 z%n!6a#t+p#!Vk({d46=)Dj#=!boWf4Pbco+Nw3{~Mj!wBnm*-!DfmNZ&GdqI$s9i} zm&eu2e$#w;oc+u<&3)4>k>u$50})7X*%yk3xW8K>W9-?&xI3A<{>I?FrpsC9+dY#K z)5(Gj^67$2+=-AxmBYV+F~xh;9sbh1{uRGm9;TC8 zg~*7cSH_~Rf=}J&p1<#^dQoZZKYK+tS#C4FaL;0Irtkl4fH^q9zyElMiD-QQExJQ$ zO@#3wz-JK7{0x<*-Mvy4sgl^k#y7Qd2&0}p3Zp*{2_txrGsC_Y;vZcK z3tZCyXm&Qn+lBh91kv;B?_rj2)~m4A0|#QlK#!}+w(WY^FQwQVdBF@LQ`$5qOOj>WKG5EBy40X=ky-p$?zOGL);=y zN1S<0h;nx~wr;9t`pIwpD9w3 z@@prNV!bHYp}D`E6v54Cg|KqjB=ku+GKn|;n?O7n(iy~#)q_gT^V=|Rkx6=;iZ*IOcs{BoRsx%4$LggPiAOka%n&55g*22&>5PQ}kM~4=boLVZVIGYiGP) z<1CGMta3S!yxagealwmC%IQ2#BvI!ILfRUVuB;>h&U(xeviYFsyQRTcNJKoxv)FcC zaVL4>Y`98^Akef%k1Mb8Jt0*0F(~E5R?#!%Knc`jCev zKim*Go3K~5l$pu}?hazUG~<(Ep0v-36$*m19aJGOPUNr1cG^@a{ zM>>yWx**U4%Q^O2^iA>v*Qh6py{WCy#-Js0ENd(ap%FjOBl`wopJsSIx*$;1JeqCF z%+`n??*$Q>z?6Xla6cynAI=eDen!Ws z!UAu%cW^}*@5fwELp1kkLbDC-Jm7C1!L%f0pZGhojnTF+$K)HBeaB%N%nt(}*Qpx` z{50k4amVk~e5o9}_K!{XaA5JkZZ2KkFm+S?&LSAJk_Kvi)CUtBef4WqD&u{^#%i^fB&S z|IZoU>i=_x%pPyzgt5Tx&tZY+!ss7AP~Xm+_84#2w3Hm}GCyfP7(b3*JNgg-WB?)v zFbtwvk4|GFOW!hv1)Md)4w?l#K|%w})7X`Sqy%J5Bu#~yhD!Si>na14?uyr|N-pu% zpF-%HSlygn?60Qx$g_Mlm$jZ%N0J<0I(J!_KrLN*f=;?enX2-ZW;Zqs4ta=ctiOgK zWg^mN;#zVTDSN&JaH+=4TI2FdXVN}3CTcKxC=GUeC}Y!(7x+*Y8v@V9-(+G&gqai$KL zjiR2wL27q@WC9>FeR{-(!0I|GBd}ASl`4wxe+=1L@QQ+#+}HHWNR!~-s&UlPH%_Zs6?e1<(T?uR!Cjt@;LVk`+3^%nVfk|{GKlA6ZUx}I!`G^;|qP| zuUXqC{s0!XKi{#$^|nx+cWH=|f9!mA!})e!Ui-ObA61qg`1ezXn=9!diLomb4AZ+H z_dIw9BXKa9agP}A1v%l!J1t+DX#2~{m0{OqnD>ZYn;17(t1AHbH3uE>`2ro`SHuJJ zI_F#&(5xHL=(I^blf@|q{>V`3^l80sI}`GxTm?OF45G-$kJZ)?#chNAfD1glyZSz+ z$y*nHFCGtQy%~LHz9XAo4!DC#^LHoP?KT=XURLR;)fc`W;cVgPdeTehhCeuucit_H zI?0uqM8*Bix{6txZbJP4F?j>Z;%2i8jgiSb?iV6zvd`e-fcW|CGdGLL5^TJ`w+3AtXH$_u)q$aTM<`+{`&7)S$j!y7_=R>gb+ zY54E36`N4Dni7G|;QI`V23A62MxXX<56e+mh7R$lF_(erJ7~jsl%&ErJ%D7tzqqXO zBc-92+#APQlewSY|6*AHXNlMG>k)k#nR__CLi+TMJ=zo9L`Lwl zJ+V4ob}c+z5kry|z!u`dP?!YQiC;eB<26@1uCPABfZ*&9ic0Zz!nVYdc|sziU?4lY z7HwLP0vV#bX+Xy)DWbfVu>L_jRJY8pfT_Wj*WnD^iZL}Ff<&)iB_g@JyX)(AAGdnj`=zrrq{eG%to{x%B} z{9BR)p?FJJ%m?B^n#w%#SaT_gDeci{MUs%(>kqz+zR%{Y{MywnkLRgvN>1n z(?!u3k$JY-vAbR#I^HRaSO;{pfeU6n^DBjMJo3yKLd;#B66Aj&l|?c2OQM;!pYBon zxZIdsvsYCZg86cJ@MlHyx8}X_W`fJ1V8&N1oPhU zOnHsBXS4&pr`rcT#Ihq4E(zF1-v$*V?}7Kx$_Du?^a!-i1a-X$e2*FC#np0{g&Ll6s z-Y6ju!ygXwlf3z{=wHYungHH8U#xo@VS>|7oM$`{L)EAS4*f1hFx-m}&44?;{Md!T z{B4LcsczKgyaL>nqD7GDWov+J6YsA^PMkKiEI7Rqd2q&MbIT?^L9{lNEJ(fDNl^Ql zBQU0gBTz2uI{?EP*ssbKm^O7jg{l))~Ggj6MT9 z3~paxDO5YeYg9W1?#_S?=B9@}&wQchB%n?9FCJ0s1T zeah8D+$A{St}ta$gF0XyG3H>ki=lmA5Gc-ivfp2Zk6;c9tbM(U^v`~{;XF0^u&?IC zM{VZdLX{YRR?D~kUW{*U42V#vFM9SmAbo#rF<%JzFaEM1l8-tbcuxtzQUyy3mRRN& zc^$&@GytRT7_2ft2(b#Z2SqCiBVYYIAo`uU=&vS=*s_&wQ`2MFW6`7YMb)FEU2hM~ zZ>ycNK5)P`1Z_}M;4-8TK^McbX&r47scEpvpV=9qU_Xr4=^2S#wP(rQyb#3@XtMHA zyM#9~2MO`RXkbXnm)6TB5Q~a6ChRz}^o>&#odw*shkRq5B%!Qkb{j@C{4`uqNP zHZyZ7Q+)DICL0H-23{xcVXfEZdw-11*D12DUMoV?;0=u{Cj zt=MU}Px~9|szwei|5B6UoY-b}-leruE%#EB=A2lL@6P(EmP4t@P)@9YkI>SFwrJ{z zPQ?w|7?%N=@e|K|C&By+&0nt%jL+eF%!hNOY5RvRErBc8F4WgV4~4hHX9~gILCN`E ziqsP!%;mGI$jrXDp@Y5<9nEX=i@1L12b$eRFYa5SS7=fWz?Hc5O$K1PqVEXUB1|M0 z0fcWR()6Xf-mhqUy{hOzgVFrrt`qOo_Evav-N`&7iW1tnLlSf@vRye%j={?v=`*OS)tyb^p{_dak#IZ6%A>((Q7X=x1DedgWkGX+27R->SVT zx+POO`5p=yCd6Xgh~{`oOWBIOYFfa}E#-)A<3r8__rq7iVQIyt1*c`qQE!@KJbc4w z&HN8r=kx?-)jLdBJRzEjzCmo~M)fjWse!;X+a0H7WX9hUY4kd>BHk*AvjwNRW#gR2R@A<7cln>)K&)=tzu)3 z!{jPf#0zol!h$xlv~1SNP5KtBc`FlQyGtv~96Jq4ivt2p$(D2$0!_s&xf0Z$fD?vL zf#?NqdOAc+O`zN8RIZC7bqZwjAbaBG>b(@6e33Yb*^kD3U;%&;H(@p1(~uJTz+m8S zsoKps!5AfageOlr?pfafKFN0lczu_~SRBhF!4i6_sYRNP!sYT8dj1D)u@GT#!&DwbY-%`RiKtGbm#M+6|o)p`t!(yQ2rkC|pR)bs+> zGfr-^g_HS?^`?usXL@WxmgI6yE*WnV>_7M>ARw9(fSI07_MDAQ*a?MZ#1i|_QZk6h zP3TxS3!HS?!TbU5%RL&^k#CY{@oeo(!zR)pl| zI1^WVTQ(^9CQl#=Lq?U=egyC#!%c^>esUBTE{9^#slDQG_*6h%v(1eea>jt2kzBxc zk>f_ocTjg8L2nS{iBeZf>VAOQ{U$v@*Q!Y|)HkE9RuB%F>#Bb{tI-6nQBtflS_%~j z*|TbGoU1KnDFS*lh5Jsw$UI+17x zuyA?eitVnO-4D&!o3@@Ssv>a*UAuR1S@9#|2i(XxK`Q?9c|1e=Ypmuvi6~VC7 zKpp&gwXH0#9mU^Y3ME?^$sg%zF^FK1$q~>`JsI+e=*a}|>JbDj+q-ne(g8R8g-Xn* z-Wd}`x1My{-9rB;iOs5kA-uXQ=QjwK!X~}b>&^*FJG_ww9vtd2k!QE(tXueI)j7@^W-JuqRdNwDQH6P4RUg}!!NJM)!MyO{<`aq+S7CcsWkh$fkmeajl1X$W zon|5^p>jvlcDW8!v1d;c2|cE^qoK&!8c=!&=OQq5Mfog(>7*4S zGOL(Bs)zUmRNTUHn4R*y!u}voN=PnEvF^$`)xt_ADZ0f&De3e%3hK2rB{YhNY0IM9 z73iFAT#o|KQ!U(|n|#akX_1;Y$(CE6eSLj-gFkjSIGEBdSNEP8$8Do$_lbvfnjHFEIx zrz_&&1{yL9p$X_k@XwAX@f^Mbofal?}H>*Mc+z*;qg_ zH$sn;N(v|FqAbsfZ0DX3mJB8u&y>G$zN|~)m*&)}T6I!+_<`lD{r;-F5;~v{4p`bu zJvrGCo>@UZSz;vos3TCSgp$91mN46V52u;IQ0xEa2l9-mJ-NbWx;>6`J^BrV6wuDl~GNmqFoyr5H+O?{9Z3$b> zMRB$#u{Acqi2$bW<1CBj!mF8>`H`s+c7)V}7X&SjX;v82$K~K?FS@^~%LNiy_eCC+ zaEPYYnAiq4^t)J#rL;Y@D$sn2GKwMM8k+I3UYapg^EtI&ARut*(sJsEc^jJ$$KJTv zQ3O&{+TG3X_PGh5;XKKde`gmJ!jBqy*#@eVMSmLTuqN5&*XrzZ!)e&_;rfKB2iv4s z9aPaLlk0F)cb7E%Y{^I$(&LqAUrPC{DPMzhFz#1iM-*ca@IdkJ=o}>cLZV1N`5p3x#YN+fi~gUM780_RZy9KWr>C8x)9jvn z;A{BNVbLkm$YLrqQH`6U@nkea#dwYUiSa#%Z_`E>!eEe%6cOs%Mm%mLUUt3XO(SeT z`>V)kFU((kB~0~mkW?b=xy`uJhZ;Hwd5Fc3b6RuNFX#fl&@PDC!d$Mvd8}^BA``uG zu_Y1Ft!4LQWGZ3X9;Fc%|#K6H}cts4$Ou?_1tN@0R>Z z2#S@hXle#d+SaSO)aixmsQ}pGE ztVlxMi)=QR#}->o{|bha^W~o}iiD9asA-R-Br)N0R)ZTdoRVsTH`(}&OHo^HcL4xt zng?@IR{DbUqJ)k(UidTdeo7PiRe@1Kr)lEO<(M}b4rNL!ZImR>E$7F?@s87q4{EnE^`*mAGY`SW8m8h6ptUjAIYbhy zI%|n{6pr5|b4*jiKjOoT!%B!+-R#sXCqNK6v0**^`o~EyDaWV5{DkAlrLB9aI7MxW zKDMfLxhbmb35bBN*wl|f6qtiGB#hiU68+q$0f_2GIVtaS>1Nkclc+6QI2##<0pOUp zrIRD69JPMVXt1QpoLreikky$DO~-1+y;NSXX{#YGa}XsNnrB!(Dr>)Y*;Ea!^TBSo zDnj|q>SoYBcO(fiS-R=$w07ptpQ+##h-frnD@NKlOuCk*mh`+i1%8yAtH^TLNd2XF z4XI9Td`lk~7-klf65_FjrN+*s|2+f%s*y&`&4E)YghWNJ7vq$2XG%yY^ zUBwG;zQWhi<|nr|69t}-woQP`Xd=x&mi0-%9*S+BMwO->^O$)xPU5;V&m=j|eoJdc ztG$@BfA1tYri}%17}*t7h)NMGK22CCXX4NP-(kG2+DIDq1_Z+W(azoY;8Ac=))~iS zrf^rK`NNrN<&-ZTEVA8DbWa z`2`W6*knsi{?6HnvQg{$7f@YT9J7V;e|rp$QzGF!pO7t2di78>eyw7S^%`qI;Xt)r zh0>jmwSg(&RVGh;&WL@aNG0*987XJqZM0BWr{ny_cLL`sfYD1h6A>+HB`M4)fjXJa z+n!tzwJ2MY(!x}dAGuhDr8hp2DkIn7UpQ_yy}_olTdtWKjiOAkqg-P4zSehscB! zeIx+nfK(|`pvemJSK<-iQe-NG%dIXNftD}Cy(Pr??Wb)J3>YSqgf6A@x>AXL9}4GN zG|8VD0u?4y56W~pBn<{N8#Qd9VuaRMm* z;7aAT30p`gl#D@$^Fhf25$C=)y(=DfqufNBUN2G28-A3Wgu)S@|JWxoblHKaZovbrqbM8l{JB~LOa$!UhgRI zmRs(X)K=Ryilh478GS)q)^*^;ga~%hcyIZ%t8goi`bmQ+&uIilEFt}-2YgVRzNvAd zxZjzrOfK7?20rdleb6Ztyo@<#96RohtSXneiKqA|Hnv7FVk4I8GUF{w>H=cBIMZed zX&LQITk2_lFjYH<6hysP!-+W-1dTsFpmd6)W<@EIP=Jv;wY_5L*LU7kCA$z6rp?VE zFr9wq(jrm^yH+?~Q~HQ%rHvuMHTdy(BqlApl1850+&N{cmZDoL(y^(aj|2S0`qxwZ zS!qci3GaoS4=4>HPYeaJE48~d<1yQX{&N=_4|TcrP|7+bWaG%_z+&PQB3?!37)+}^ zk-C1T?F=gN_*K4dP7|wEr1qYv6cgEDDh9W7d#eKkuJ?9$0rY`bY=LiyEv)p|1v`&7 zIdq{Gsuhkf?yD!=a-u>d9lI`qbcHCNLbw|rCXrRXXQa^ zq@|nQFxs(lN%!K>IB-3&Nps4S6Dlt0$#SOjtH8xK4lcj;Fcu7D$XBFR6)_M_=V-fK zU&7=zd5FCH+&aHciP?lDVsIF1)`5gEchywjDGI%O@j$xOZcyRbP~P=u7}Q|Aq(Al< zbvGK>|Bn2&)}4%*OG5}(hW&68i4VE;4f~88dSJ*jUeW@z7vuPXm%lA)OT9)k#5;E-DHvE-h$;*}HaE%s{po}baYK*5 zFxl0Pt7)K@`a^rzROyRviB>EpxIc!;Piga=Vyl@J3(T%*Z|xbTTV>}w+=93Z@n$tI zS_v)Q)oAb{Mfj-&BqW?ZoJ_o#;9LWHvTJ-!f!g}fZKt&FbYFZ4*2?TD!Q2sL&QKZroGhpP2Q4>X zb;Z9Ejb=w2a>=^kv^J8e{0<&`8Tw=Hy0#}wXPzj|O~+u6_&K;idn$5ao$`SRIb)n3-Gf75Xnc*{R{D;Ukg^vwGY6>FW+s`OYKP@n zPBWJk@F(R{Jv)L&scPQ{`Ll>W0hA1Nb_@${X0>ZQ=v!(| z+8_J@`p3cltke4V6P!> z*f0s0a?W=9V^ehxnrBic9@)jqiC2=@ju*g_*G*iIMpgP&B-3>2catV3v#ux`Bv($K zH9X!W{oY7{UkwXHZpC0wf2(prY)jT-Pd1TdF;pr#x3BjkcUXm`OOp{UEZUq8qK)mE zC^mrEjmS<^NQbMLXB*c`-5`GnKb**FO40Q85wEW&kv`_v?bvv^^uk%~Hn5!@N|Z~4 zjWcZw4mSwHe!A5j)1Uo;vHcM6BHFCnsySMPl5;x~H_YDoatWd;P^H3Bo=jA7=m?s$ z9|L86yEWuN*`AT_Y&|QIQ}w#W5?LQgA-4_{lwv;ZX;z+;Jk>?+DL*lz9QGB$vZem0 z7YlV{Vy49UG(_A?x0n3{>$(75K|_*26dOTon62X0FxOjt1$WtI&k+{mOH&yRr=vaY zTrYao7-}cdITmS)Q^zv8Ham>c86PSD>vT2oq91(L5?E-~REi*n(J4&d?ki&aU6Ct~ z+_QAHP9V&gAffhk+{REP%&l$9m02in(X>OAjLB=&=j@+Op$b;Z2cO901%0Wd5A--; zFT(|Xasszh!WSi3e;8g@rwO0MLJO}K>CgZvRqaT`MuVBG+ng_vu72DNOHuT_y%z1w zKa(cY>Sz%zx)yXHIWU~U+WfEZKXOb?geI@7CbH3ZskjA zshr?k#W5t@x2Xw5Sn);kd|vgw7EG3J`~i=LcJ=myx1XN_zkzP@-YOF_22Mn4sSYJ; zMRBIEmc+da zS~{_;=DVq+i?9y|jLn80a!g-pB3G=}>xwj`@Yh5mtTIwNepGFJBsB?zvMt-WR{x!* zzTnP_n^W+?uc(?X{|l-=XBWs%TY~u95d7rL_eLu(h}dFEG{U$#T0fo*Hq)$mC+x1k zTQp{rMx9ZRSIx`e_Dh9$)~GUFp@Oa$n42j0fzz^hc=8JkK$TgFx|X|o+QhV-X6`jW zVZ%f4{#218+tLO0%#Jb?FG;Uq5W!J$P>Zu9q-JPc+J&}3Ti$3WY7$`!T(7)FKjdq1hLFF2a$& z(-R8YM0F57wuk)8ZPnd9Bb}~avrS|9t_EpR$)fIhu>H8YZ_LnxH^`Jrfemcgm+FlE zSg9*v{|8P$vA^n?gK|)9d@&R$yH#$FxOWHS)`;iKL;m`l6nMyU_L^x`sD#ug-EGGm zu$}1Mq)?mAo;#P|Ns%AN&&%bUUOW)J#-NGPPiSfN3WcCo3v51OQ-@kYo>`PTLiH)w zP5dP!0^^AuYU{J#lFlui?OonxKDRsKA?ZJj{4gPErWEg9!{gzDFmv6Vlxr zD=QgWV7W+Yv-;`*;d}>yXoXtPZYc*$Q`j_41}@DvEz(T#$W}`Roj#jca;tZix1nH z020-4@Dh5+Ek0*!zt56;KJra(zCZ443(_Rx>uJfPnu}_B`WW&M&u5QKu-YbAZ2r59 zP`jbpSUIN0E%T)6C7sEx4wU4HnR*w(bIW<;xl51Ntv=5DnSZTskidXhFo(nio7BPQI_7&h&C zXa|#)w~m9}ODT?MmOBiv-rbCf)IAw~81k`$*8YOJ?yr_azAs1HYvWmLiFp^JeRrW* z_9)4^3G+xvDKY4y$4M%6B$Y3yHHgW)5?}0~%^SYpCHH!q6+;ZT6u(Q6EIsg6GgShnIy@mR4$&_s%<2l%I1m%y3#=px+qF#gMQUjSu+P@ zcQPR_OmjH8hM>Z@TQkflNN?!p#(pUJ02Et>Q%@QI(zG3ReAKH%vVyMUYgAk+o;pdHjwX?*X=22ly^EJt1f|lOjkoIzuGs^&HvM-ph0H zwv*4@XSL+>cuFxvmrV9c*P7<#j!W*%mAX&}hcWI_fNH)(I+de)izDjIc+bsxhB@LI zKSc8>4yOK)46aDID{#Q);kj5}R4T8$?43?SLb$9J+qpH#Z$6hwSTf7lYllI$#fY=K z^C?h)VrNI3%^JD}s%~G%trsIdQt|>Gx#ixl=a%6i12SG{$0fr$E;+A@8FujU=DxG2 zcDY*e87uGfkaEZ`FJ+-U0xZHFKjl)+%lb*Wkpg#ioSTXTp6hmAbL`0EWR}kH zFSjudJl44+rM``*;~-s00|7UEt>5C0ijRu8u<==s&qu`)akXd!m2x@F;KcE{T8PP) zs}Q?gA5pnOSoq5bVfTnMe}8UDWH|*&6(U}YFlUHe7SIq0{F(5l5Kere2f!VKD~~6I z9>#NoV@OVdp9X(dZ&Ea&{9D+)523tt&KEtP5@Ij>3St+hmqvIIxLkl`%|T4m5HY2lU3GgyHT0~moo{T7`;_is2A)!3TtrN{`ZUj z|EQQXG#U)Vp!lM89YR>#NO_Da5!fI^-8%G?$CaGR#fL%d7Uzl!i8J+b^lJyYr$w}( z>_qKGYHgf0ji1HfXj6OP+k+P968&&fCm0B|jZ0*xGKrcF{?6$xPDRxEN2@Kw3~J0A z4}m0Og6nQ`Zq%WJTyMB%$vLo?%kJ=-P}2-j)D}CwI}kg=ePE*WNp80+a<@2iK^(wG z+v;zn1!_Lf958vLXSpx8pqvBT##A4tUk6k_7Lh`lzURBz=Deu?L~WAiUxHf}b)TGN zih6lJ&lB)!bKFvt57k2~=;g`K%lV`^bt*-kV_x7&2T`+8_;RY-D8}VAb)zVg8^Vpt zF8~QNU#8jQ%9-&wE~Om9o4I77dY9-%`*czN1?CB{qR$<*DISM%B#&@EIueLleIM5- zC1u6}nWrb)WG?hvrk=|w*NQmeiD{)JY;wv}Nj*UGLClx)z>#0F6ef?n0dB`6dPgiM z7fQC=Uk^uZMzh4=vB}};Iz=1Hy9|#Cv~Z?H^fwwZUV~pNDT0OzlGcAHsZVL5vLNH-d5IaN9UH>Ju@;;q`Lw8?O8iwT{mcwCz%# zdkx&neKFg(Mm7sQDw~&kpU+R!dEpJ7LtZ#J&$28y#WXLrrCOns-vVx4iS)g`ay5%p z7$ahimyaWd+EF_i-}`A~`&wZb@pGcpm&P)rv9wH>7KGX2w#O>8ftcsGJKT^amng^O zrrL;UP8etXTgK8_%-G`A(d3@ZJL|NY%NCvy{#=(+u9LS+EZK}L@6{7->}JGYi9T@L z+c5%YdG*qhC7~AcO5MvNZXcsvCht}s&#R%A>vFLIZRL;Wj=RYns_kgxQp`c*D|wyV zb|$ZC+$MFBzgg)PRO1dcZrV_;eta@4L(*TXGwX=CP_VN0h zGQG7T~9 zJ#?w5tZXls#WQLdtJHN z&Z(iSbK5YcJJ1SS(HGt5tyLV;A)d8PTMYaqU0T1 z-yPs|tNguI@NvUR^3Lc*tQ}$#>scK-o+Lr2p8ebdn=u0Vd^wh+S?7c#O>z^5b8;!EeVxN~>Lw~T!>3uSF4Nv_ z7~dM!0{;$Q>LsOE~lF`nFsYPKPzbbi#44kno^wg&_P~t z3QD6XBK0`tDaoXL$^ap|iq-+CgD+OAh*s^BrPV$u6YfCz;8n`!QpIUioL1tr^zj-t zq>MAEd&F4+6j^4QC3;?lr9+1EAUg!w(JZnfmY_6iz+Eg3BB#aNs=LK`FrgO0&XAnl z^ouv)xHt#>BEnZ7o%2wiewfDl7yoPfw=VtXt#`li?O^lQ7lRuYS}?9@kqSWFR3c*) zab1hWn9;(xvubL+p-ruyq}4~+Qzv$P29fp9)cVn1Ytdj_Os2#dLKJMWpa8RCk*W0& zeX^LW1w!x*P1dTyn!^fDtv@suAuBN$Bo&V96JpSJG3TRf2f(cPl6kb=|Lgt zO(?_lht?x%EY{Ev7N}U==#7+O2SKMEs+bwBK>cn;ykqkJ)Vk5{C|6ketVXdSsOX_> z#E*oSnoO!R`%k9dM4AO3Y7CiD720NM{n#w{Ns@zw1o_2KT)lquVf;tD`b^!Som@WI zA2L}g!ekL~isGbuYiN=tX3Z8ARpfBEcd+vg$9X3^?{u6;*?APz9X)~8s2lwrk;iJn z0R*@QmBU{n28AB0SfYv9B8cFt5oBx|g}Z|qaP%1ASPe&iwn7kAqUG_x|5{NIsXMfL zR^8~mQ4q4pQ(=`l>V%3=84hL8J9VQE219kDr}a>@G9J{TvHHOLnD;tv(*>|Tq69B2xRp5ZhwJX)|4m}j+TD(P3QaW1Wt%ytTT$wakqkgz` z8nw|=Do;G#*6SPbpJ&JQ1U#y5M(nW>t?tmRXm`Y}=49)}?x{a?XO%~yx|3-J3oSM) z7N+SAe#A+2#w1PW>dqGK13t5bPGh4nC@@wivnyp|+=G;;h|kc>j7Fnq#8};-`vXG6 zDuhnMny^7uut829o5ro(P8ZNDMs_2>@adlm`VOpYPF#9-GU(KQ@=5rHK7B!80@pFeaip zjW8ZiWAhbp=vmM_8%|QBw<-MdoK`cDCwaQ8C$BXzo@dl)_#12C$#F6i*BcsUHBhU) z1lba$Q4^(@XEWfbWwQM-{&1*KC#{fXQofw0Dv z{B>A1eM~*bpPG+i88iv;&~-f;)=~Ybbz`PhHzll|JUD;I_>*9mDiIE1_|=VFDZ7LI z!jx>JzOjls#_c5vL)lb$p=kxt=7uNLH%3Ymi9})ykw^q>6qYlfF+}||DH0I@l)CQF z+hGwTDc(SHDXc>bp>EE+88addLG^m8l7}o>PoIp@tdASV7#r}y_=!qsQH@qWU!pX+ z&fWU_5LQ!`|k!=lShGhUw{FefS3B{~-Y6|-Olbot*r8SpP}JF zewhe84>#9ms^YRs5HsQ|QE| zlyCP!aMfT9VVsY@WmKw5d?MC(XspGbOxcbSie8Y9pQ{$Qba4%j350aRxu7=$&K}lEcr8&)w*7kk7s6qq*9DM3*YCFU=5AQt2y73yiSh2g#S?5fRLrM zL>_8kr6kOOPBWfiJWI7YJce>aqcpx(D+#}v`}G>0C=n3#w6Ktp2aP7ejMWi`!9lNc zf?mf`AI6N;Cn+`dt;udOw?g~_WJJkfsT8dx<>wR@+WN6~p};@H&Hk=4 zDBhKW;$7k%(jWma`eT#ASX^J0QR+v36^YQ&AFG49Q4v5WHe2X9vy8|@kl7+KTdS#} zAWp~_XXm)|s;c+!iBwm4G=?2n4u?~*(dqg?7Yol^fX65#;v zxB-q(fH9#0j0GbRZs?nwhQ6uJZOl8VG4G7ToTj{!`yiqkZS;&j1qDYipoP1T<1*cvLT*6vjZ95}}{EgTaJy(J8vT~2t_8H9R-uKcOGbmyKmGa7!4N!y|3$&x)MVI?Yopp&a3Qf45zwzI>Qy4@ zKZvdJ&h%n(Bg^Iua+7{aapT6(ziZM)ulWlv12p}G?}}@TJHOb|7*}6^v3_~e68dY5 zw-wX$ddm8&SuEs@bYr}yIFL>ywwfdSMIh@3R;@BtBv!0!UbAeuxn%X4sal^tiQYVy zzbqiXGeo1wJr+^x>uP7A9lh11?r^C)UFv8=2^SgR9yh{0Zm@gYsQ2*RLx{EoszR>) z7E$`TA|kxFjnyd=r6DY{iqI2FUxe&eN0Fl+y-n;)1tnJ zsiM)!kSiwRn%=;cMl7O}wHFH`;V~#(VX_v(tfh^~{f{x;Vv9y$XyKvj@)?anO7ni2 zra{i@S_9?+q<-D#^>w2+l8poMMhLnH@AL8asB);$TRrj)kG#_(kMe%fgN)y!J-bHQ z6}Y?BaJYiK3!CT$;R3Zs6E^Pprt$LL8K1&t`1e2%`Wp2sTa8DXSmr;gzr zEabt~PNUqnIjPGX?CA(YiZUrGh!NvmM;bekO4_dC(Iu$Oa zR69GlWOaLVC+Tu^2P%Mw&IG3q6hb5~VUmKB+lv!tYM;BCvr2*Lp$@QATR?6pV=IShNWxT9ms9a|knwjNVTGf98N4}h$U$!XPz zG0huOo#e)7#iIg4Q@Y;82y<7bSllJ)Qyur#F%CU?hV<@01bWwGL7)W|BHM+c-UJbw zv~c_`$3og(Apau#Blv`wH5P~nDCAg;BJOGw@m8aVw;Dyf)hObvMiE|(BD@+!uo@w$ zSarlF&?BxukGKLo;tKSIEYKJ7p~*rL>x>nQm5eovbtrk=RaD}!xsipA{9fqD?}d*1 zUdTr-6l^}@BIcZAdNE@&r?-M}4dZ&w{|*lM02(IB+zt-i8CuA)Tt0TP&t;6e5we2X zb2U4S%7w1Pj`$=tH4S7_gk%%v@Cf65l?z=Fv(ObWoYKP_s0w6*HAi@G5!U7@>ECQ<{i08Q->$ro+wS|u zF8+lGTZ=e9XTD7n4NcoS`rI#-ZglkSeao9xH7!LVu_>-+yZ5^RQz%y4^@(%ndQH@* z7m+*m@q2}0k|wGpE6(5WD59DO=gqW9Ur;Ud#7#2 zw;8#n&HmqljHj|Nc8nxUL#%1%m3g7QNxr0W-h|)U71K0PM~Tua8g4qsY@H@n5nsJ+ zuyDY#VDR{VD70zZm)Bf%lA2#WgaM|B( zNclE-e841Xe>QIA<5{b~m1&@+9ru-({zgWTrgH16pX2rW&Ck3Dk)8OQ)$+c{Nm1+4x|Q##mfUDH|<51HDV>$ZrSp=in4yz4y`N{7R=2 z#u*>t@-~HdkZr@`K_;s z%0wwW%(=y*Q0$C5I9`bRrcm+sNT^nm9xJwW?Qm+&1b z=QdR*-9ZW4C7fR^!h5$Ng@h$AN zFO31}ix$3FN^fS+-BG%~|K4$ApZx#!@Bac&O9KQH000080NZ*}Qt&1E80kj<0LY92 z02u%P0BmVuFK%UYG&eFYMR;j!Wn?ZvP)RO$ZEWp*{c{_)lHl)Gb^pVZx2`N-$+8{i z#pi6E_asWTc#&+KD95|m+Df1qkesLCfNwA(n%Ud`{m>0yzDZ(+14zls+uB&72%x{v zXf*ochkyCHh`f86@Q}rSee&w)<&%GT_uv294*~PVKc4vC4}^2NxQ*88cl<0S3;OQt zYZ%es==f^zTT!wUnr^{@aH=R}OXC%=iro{N0{oFFP^|+s$(CT7ah4c@ zRt&IDv5ELo43>o0fF)1Z*C9{5Z@*BXY~`3)Y-`y1Dsu>5eroUzvJmjCPBdPA~+vyd&U;V1T?h+~Q1CL>@}AMr3Mw zrq^(~82v2pn%Xkks$dSFno6f1fKG5mC%|7tM8MJy_$7=*F$X8H0Ng&rUq(aG`O8S$ zj`5syLg%-yz8%WA5L3AVc}PpbCFsW&;cUL|5u0zvmCOr#P6oy2 z7mCIzCbvyWOsQWjBc1_1Id%egygwP zPD*#jgCOaSlMjU?>`7ePzRo;%r85V`XS2?n!!l09@a3DgR|y^BOjd}AKRs7Q5PURE zgxrDYFBbac{rGxvP~jEoDk<5PEj7zz52M=*SH z8Ttu(4v08oz!#DTvvYqYl+IuHltjxp@m6y3hc(~}B(K??YM^*5Y2xUbW0O^w`tyNa zN~)-EoZ2Na!#cjJ*tl|m3vAxLHjPi+?V5dEx5k|b*Z|BZBui{@=f=ycm*2e)6W-Iy ziSM8gK9(th{W$ z{O*^l>+$8W;+-?Ru96U@?S~j3b@uTFJbw+o)iEk7jrvw|%*DO&J=uV-WbmWH_?oJv zjl;ioX_+ZqWQlj1P;%$!iVutvC?q`}JZsL};l7K2&#@X`3=&Dr(HSv*aF z#Q=M)r1>5UjX^Gqr5B)3&Fcb(416`kBE^$I1RFr{}Z2>Ev42i{wdo1?nMD4b& z*r7e9-T}`Na6>x4_-;`uO?BvtK5kuiqb!&K&90iIh^H*-%(01b+E&d?T^&4%9A({qoE{O2`x6 zI4N?gR4K?;B)W6s6s1wPBt0*r=Vuq=t7Cl=b#N4LpDgLm=4n|qc+V!I$#uL{&jrQa)Hzd6!6YH@k2;+;i*Tziu2CLd|@>u533T@9(Rw`J-Kc{pNN58-f_@&BSOoaE@3UQ@?lA7~WUPfr=V>wBPgxR^LNw z2<>x_xJj(60lb$IaguJB1N`Co)&J@TD2#MP)85%iVxSWkN4aDL1gA!(%U_ zLwYlu+8q+=J?#z!w`0=-Ln-+_C(7jP`lqw&iPKZ1|JwriOdTnZJzjATN;leK&sZuq zp;HC4@OoX!7whY_TDQD|J5+P$Vsez(l360IKPbCKnXi)5H%ZN zuJ!||*^k*B-A9V?x!9o?1H9_Jcn^Rbyl>|a3rn4|-fu^19owOw@O&CYZeG3iewJ@M z!aML&ynfU3?Mn=F0^@O*M9jbI83U(BySd)8zG@8xR*iOez1%ATz#W+5xOc>~x&eP&Gj6mWex~sV&o{1g5omedP`B*)OD2KQnh_o}PicuK{s)Le4p*UQMb z01+=|psiAK!e7&c&=cHy_40pc8$K5uNnCF!0O05ncYs95;UF=k^CV&`urgP16>`7{ z?x^+wK8txw5_5o4w&V{6>(@ESxcPZ_PNI8Shn9Q|7ulk6F6y5XIum;&k-c3KdvOdU zl-`T`=G+(|`E9q`@?31NB$*TAw1YM)MD4`YT&&~qWfI12)c82Q4`(dtU_)KgAoJ@& z^ZwIMhu$m%VG84E7>6mX2$1{}=Ii0wpoubJP$Vm{vQR!ObD?{#NF17a^a|6U)mjwF zQjA-+bc3=1Hv`v=`PtO86sfQj*WO>0mgk3nM*-xzM0xJRrU}y;Na0F{LfHfXaUB;23}_ zcP6tAAh7^ov|M$p3}nX!AL`phmLJaH0L(5S1v9KcIC%K$mcLh^uQ>PX4);{O?wpJ4Dv)27OooZ&SYFj7=CW-a-E! z3;PI7Q(6qS+QPC1rnuqX(}*oQbYX!bBPd3dAwR@?HT6eo?VYZU!wuyfyb}O_Dn~S7 z*MCUo;=iuozeF?+SzTwhQR#Z4>4zgx3RJdM8;J1%pt( zWutybXiNgSpOG|^lUO2e3B;rK37M(iO+6A~#0CI&^bM9{V7$F%$Q9hccXCUNw{HIZ zUTDE!H+W&vr0H!LpEW2srm!qY{K{Vai;jEttco~<4RL?!uV zP|7bq44~)YVaj4=PIs(>`7&IUigeU9TP3|(|#vuVPTs|sRd;r%hqACr& zdP#io&Rf9r5auQ>U-uiwVW)cdI#?X0b33EjM9joGe|0PdHwbZWaBm!g+aagA2`mhD1*kBh;&cGI%6Xhg0~3GT z2W&as6}T!rv;SEp$GZa8JhQJ|k3oW;t9^#H`UFhK{@V@a@J>CP#Tnr9yQOgePZQ*F z?2aRdKLD9d*w@}b;{=|V!6l826C^|HD-n?}MDsZ=KP#i&kp_ z`w&{K<7J}>wA0Im?EbcwG(awfBT)B>7RNwwM}Xc?9+}oUXi>K?+7+m(v}1n}kmFq{ z1XELTt3q&NN}hMT%x^h+T@kp0hp!2(Qx6{;WybaVHymYe-6*fK-+nTWtIrL^Ye7gD z=Lp1(bq8}W^*Bdh?vsP%2EtReie>ZDE8leI=a62Fz1@Ch(k6p+b%w?aZWJ;lrtEshGI5G4b;17>ag<{v3OOdc^Z-0)}xpO6Dey zl7Xn0TN*KYiI4y}BujN5C5LD(!mDvTn9d;#dwVfT2vdO1@sZ;eFsU94ARAF^UikmED;T87Y8xcDH-rmf-W0AFU>me^7O_bOa~;0#OEZC#<&Ag>X2`ljGbFZxV+~noN9VUcnhhcsRt4 zwu6c(9h9B6JY*LznjBvY_;bvc)Q8}A)3AW!qC@&`0|8jw*ji6x9hZhE z;}!lOU+-Cqp}tKoif^+iT$*u$8n5s*1VHsM1gsZybQVI9N%@CyJY}Ea3#g@Z!3f;# z0DGVKb6UrLC(&z2?n5uZ{0Lz3pz{6@^4c31Q_CaGl4dT@J|uvtqzpiflQ2GnRs?Y- zgf^Udv9Q0Uh6PUW^Cd~hLeWCb`sCeBoxkNhf?@zK+DAXUz`^wy6N&P>G!~k+IavdNE5h@i(NKt zKP@_J7a}(H10E={ss$zs>V(0~Tnyn^JO+1a25+uahfw~_Bguww(0agsj>WCzES`li z6ii*vPq%-gKDZ>212#=CXf4#=0L!f}4t;N+d^!kq1vNB6h@V3F5H1@^GQ>`<{W%RI z`O{A%38k0U<`G`SSE}@`AP%hgb$6$pdOyIPV4x$gzt?a|r3}}gdZWgJlZ28x_{&Q z?kN(3LvKnVP8E{yc^2#bRkt+%k{VORUX@a?%K0%z&HzZ(ct4HY8GeCED!L3Y%NCx=P1F*-&Z>#9o!V>?8OR&ImH~$V{W6I7a@VEeF+PE%>)M+U$^O zaG_Lr#;(ECLIr6xZ2_sj4aECYgMyyjAE zmztxqETYLlRp_?-_H4`fUj~)S7hVhs!{?rYgc9>Pxu@R2OLgIZg8qlzEBFn*h&9aA zKypvQ2yOMS%m{BmVltzNtoqEEVAC@bz-~#BK!i0wCV$o(Rr6|?JU`>9IDO=o8o;Ts zIdom&KvVU}J1mvzku)X#e1Rp5qwr3wk(azZgmRb^%_k5`&sUXOcWB?T$4^PvhMhdD zK~~mix*71%WF7nP2k?V+&}dy%Rs3jll@zajy6GQCVLT3DrRf*k`A`_SCbSpgHjxYN z26^svxT?Aq!PJ;6G z@JHk5hb>!FQ?bKMtIyW!$D@rz7WdvRN>PoC=rl#ZEvUgUm&8?;wkbI=dmCJG4Lar~ zd!e+zf)d|D42C*c(Z#=6TLT@S3jf>w2V-Y3B#!q(! zC~4}$fi%B^nRB4>8Vp~(4E;o$WfgaH&J+CO`S?^Wz~?NB0$3y^;E4}o@Ob!W0~f3r zmhpPCS>*g=boKi6ODB5`x*CeLTrqo(?FOm;c4_X4;C~W(;xGRswku-a%oDbfFNfAi zF3?qrNe9?tCw>iHn{0p<5k84rFkvB=`kk<#46RKDz*A+Iu3a!7lS}r!Ck_0Xoa3_~~^t9K2+9SKT&tm3McSlF3w@K-XFK0_u-R6(OgRz2Y5`{C}l%3n3 zK8r+CTmHwM3_RQnY6rj7AB?vxTfOvLh^X4|Po3a%kmXIN-kkm*oa(j%?<((+oYW+bN)omjdU2tvf!=4OmZo?eZPP_0A#oO(h z5K-;32RFrGU4&CNae~bNwTRwBQHsrxg8*Ce9jAM0XamPQ?6eF6@1Gu8N5xrIXA?7w zZ-kEr&h)6E*zYxa+7`K}xIAF?M;+EdbzTo)>c9xIIOe*%ZHr~r> z&wNd00gIx}*f~fh%mPBxiS(zfr{5{F|0!{X2h23tAs6Sl7^HYR7hWCgz;OK zm=VqLWj?LEs-kzAWr|(R}Q%zpsXq7gjW&6gMY{y2k_~Xb19R#rSFfNOZ&J8$&ntC; zG~a5El@1jjR_bHlwSk*k5J10S?E%FRC}-=6A?;PbZ8D{>y0LipZmyWYYmXC9Ab1Zh z!0;#Upa`^m4y06{J53O(Tfztg3(PqhkUM}@uYJ-WfF_XI+OzPUZZ2lucWcP4crDe2 zIZ+jC!YF_V_rYj0HNLFlb zd>c}M`I(v(H$^rnexl=j1XLt33tH#YXK_%TP=3;F#V2rj00+I8s^$QsVG>*zeTc%u%w!rX8ntA2?y4m(!no~Jtgisfp$gay7HTVZ+5!Iw z9OF0HGD0=(%Qq_&0Rad7>f<^z*g_U79KB(anB373;Tv{r*OU?#-wUgL6BR1N<*@QE zzE~RQ0nf5M(`JRptIbv2RS1gVM#XHAYr2exPbW~`ibSJ1fkv9mWD_p`4(VnH~}mTO7w9e*N{8<~Wq)m$z1Mc*Qgc{i8A13p9c7=l}fm*O&jb zw>-qH0g@<~nCwMdTb&yC z^DgpFvfC;GX?f`IOqeXAFonveQS)#%uWljc;8?s?oL0rbf~W6N997Rbsyx^xE0zTO zNSJ5edlR;x`O4n&cld#apLp=m9!wh5LD@-mKmp*hcqB^oF#&?`{S***XOXZS?LyyLGOVMeI2PxkFsj4f@T>rPxLnZAD6mA*Q=Hvmqh$T6+NI*grwcrZ$vpVo zC_5c}(WQa-L5Il7VnxFjv5Pg@>H4SH676tP`yHTk6ytb%-_1aPiJJh8)<-lD9^l*k``$_Xz z94@cEs~s+f#nAkx_Ys+Km#0o2O5x|#10+td71j@7EOtwn2FzE>ZrGza<|8%q#XH0D zl``<9`$DIBtkFu~vgotQ1jZSyAdk-OE2w9q;f}%vD>`UQp#piX+sm2;CbFpFn_S%+ zXf0bVpzeBfwnJ?B7_G{54`_ECr#T`UJcZT{){#(iVdo&5l);r)6^ztZn9zb|^Koq~y;0)peJFBWYg2M_4+3mLy^)M{AG7G0S2fZqDAR zj%E#GKgt5?1r*#P4-1%n+id%B`ILgEE$F~ycRIi$(-bON zdp=Fzd0f>51;WU%iC&iVU~&-v>B>p!lh9?4(OS8Z`#H(e=4IwO#A92 z`x>ett4nbQVZlF`tp1@@<%mRn2L1T-4QZGN89gh{?@O{dlSQNGP;Br}kSSkK*Ms*p zFlHs;$?9VU)O+&o6jCPy{RI|Ke9MdRbHRuBX<7-s0bzqT$Y~}M7hZ%Kn5Kk~iBIe)8qc=^~FXV^9=2eSeksKAAOP6>M^$+84%3R=3 zO|jMalZSFi7T`N+R{?-6T|(ty_CR9_l(*^xQUsx8$zsV8<8aN=lfIs5)1&^L%(^D4 z_Je32_CUEad#&HnQND&dp1pj81Tf7Il$9@{#1Ck$PKrc&7 zEAHeG6Y6dPnqE)l&GOmof)9mZIspt3gvRA&bhllrC_rP`{ZQ!BxZ4S85RICU{g9J$ zynOT48PrfBYd@HEB`zo6-cTNq4{#&c3+8+pcY;r+B#Tm276ps@bX7fSiq<+S`Z9f3 z9g7BUSV>c8w;ljQY+tD6%M)*Fv6-^e3RDQ;N9SSMv5{iySKD6(D$$=h4){flG__K0 z9Z)+g(`{G`o^?nlOsM$uj@LfMTZ=qd)iqZZ0hKoq*%wXbZ7PXS$UKEh$+X$hs=3NO zCIe_(aWw)N=p1jT-mkItOVK)4&1l)wQLBvO&%6`dyC3PxNK{|xgq=f>E=ts8%eHNs zw`|+CZQHhS%eHUX`pUL#+tu$Mbw@`>^eE>!$sH%xS=)iy7vH@Q_lOCqu*;mezJUz? zx3I2N(m*c^mS1INg%tR^5hArg$(?; zN)r-?5?!6Q>KE@*lBwtRYR`&WE^h^BVRhL6s_1!`bHdE*m|xMwvgF%*}4*y3Pev;jxe{qn+>CaPJ(55UW8v#9E5ChMjWX|vnF zmx?r=@7oGh{_dU-%I%B(~RmsI+D^Rp@w>5J9f)P?(W z+%EjzW^2^0<^Z1?<7fAFBAMIt{y0HDB3tPa3+*b{#{7L8hQ8OII5W#uh$$Z$GpUAJ zu^j#G&mEZ*S;_x4UY$P^K))uAwP_}bo$k=^?>+XLbR8Yq8e{So>rESDEx%0+Y(qCJ z81+3#F%;8QIzdV~!Wz)UrY|x^>bMJDr89g2zG;L1LjMpzLlXjE9g!s->SwD+f15vO zzi#u$+EaJ1j9cZBz1V`c0vAQ<=?ZV5Q+fSBd|>@U7k@=s{hcE#p`^DE^!*+`HqQ%R zztY}j+v@wg*!^Cv@~aEJG;&MmML+gOAc9*n#5UB};e+J+Ue11S-#i^O80zDb6g$lnEYL zeRhY-^l+`zSfuG^^?jdkEZ{9NNDZX*6l>S3Il4jAR)7?^(Z5Q@$b+OLov}dSNv_bL z&6z@3<=jvlcm5Mvv5YE?9oge!m`+psye3!?0X5%wyZ6Il2hv7#_9Sc@S=(PcTvLQ8 zl$Rdv$%I97v}plrysIwTK$I`2VhP2z=idsq&nW^4*T(cU_EZQJU$3;KH;6q4w(q@9 zuPR<)K?y-i5qaw3$Bc0yyT{l!du+Lrx!WLqlRvZHP!~I6PIfiK1gi4+B2T10htHj-0=HA0DC}Nj+hpVPM}c(zSW;>tDsj{`rw}u z=U@Lv@U2D2!$O7-wH)(^kEUE7sr=;(2U4R+i#L>tM0KNBDjqarPdCBhxqh2;KDj#- zymMzc;TKNCTPrtVY=Ive5ePJIesQ5kmD*qdBjd_ zO}FZU{@Bl)j#viO{62#1jc3pQKLakjQ)4UTi1vepJ9#}DHRQCiM) z^%xA(q(Ne&bOO#-%biv=R>ES_xrlKI+eTk27Y%|2$*Uvm%KoZ|(qq$ZEab*ChY6c3 zj5OLD|2$V^#tmV{fOf=*=|0_>s(d(2dIEAYP5fySu;NzGxfT*K-;*$WH4vD@w3{OQ zCYM5KgvO}yW+en4^N$6{xW*kD5~-0i^%x!W{CxgW_yfX-z)Y3Sh$1V(xO`xQfidw$ zP#L0JX5jNlg`u?)43p4zI{{A|y{mpF9*SeViM|>LQz&Bey zq4*4>faYSMb=jN=!CTnB@f^(qsc6zoayRwOh|H6H&#KtGH!d;&Fm=Hqau4d_>i|Kjfdb&_GQJS^$C3P}2l}cXw@1|Qic$X@GCbg{ zCZ1za+s|5)%ocQ><805p-^~KfbZqa6>X4P!fGFtD^gHQ^{ve-~6bW@%2rvs2sechw zseYi$cdX)8YFE#jUerZhX$S!?Kky6bXE*RG+!xM^pG{8+#|x>IA_x8&u$#(yae~qm zliz>!aMF_zW%jYm3CP1*U$OQ0oap2d~{yA>gb$cmmJ}#y?j43RX%Rw}al-3fbz(RDqd|K?hg;wWZ$bsPQ=c zK6Cpuk0Y6RA@3fmB7#fHwR>Sd;o_~|RHR-wG;RD4`Q9PSqOELp;m`fJLjjYwfTFq%{sW(T$h*Q8~=4Q<6}rMHqCo$NGeP`RpFlMQPl>ibFn-i=sGq& z$5}xw+-5YqaAq>mvNUlw7G#gB8TIci`4f&xQi=>-)K@D)Ub0>LRGjSzU#(J>U7a}p zvHR7S`n}}%CtoUFN)6+!I`%>ygDI;D}?X% z?W2KS|CQI8c5=IcXQT34?fIy7>bR6C%LZ&C+lYOmNG(?7>|tiJ%!@f^Vov}Xh)CJ> zZ9{It15~VS2?uoPHkdcm5?snua~f*LRtpbWNVtVw5od|V46HK^gM+#l! zYN7HtsX$;yA!aBKYjNbdP2w0$_Hx`B3l5xv#&UT@UjbHCO)#-uGWXqeQPnjPA=nY` znF*|ew64HxzMV8D)S_VsNR+i77gQYKRM8wZ7X##|ky4|%+YuNr7FBohJc#Wknu;$X zx|kC*8o0FD;DKaSFs?naJ?|ad%n_@O9^~A;hral7dBTn9E`rOPJ2gNZnh*{y)Q;nm zfle;->duhq`Iyev@?Z_--MI#K-HT3ZRe8!gTVy*A&`vDgj4ksf?j&l33 z|Gw^#=x#)=geX=u5SH>`OuF^j!E^2<9lg0D=iysYs!5!#sk(NCU1~2ZY1ZH=53H2H z#dNw84AXk}Y;sG$Yx^$DkTc_cFmc%tuN9j{z}pp)&g+j+{v3l5L1XKKr%e^zfEhu@ zD<`0+aDpmDeN9Jx2#g#D3$FWkBsA0^Q1;06J=FE2R>LBRH;CC?2`I<0U{<&hVHvw(sc#YUQo=_E_c6+0 zV}AjHnzFe1kFvIsMAqCXV&nzhiiBwo=sE)ypj>MLrNR{gv(F+v`20hEPR$93F6`yR z?jI$_FfZ(XmGWL6(38dt8JRse@-{Rga^|}PP$553UG2Mc9mK-|@h~>1|lS*P8bg zZ)mCRwqGnhg%47Iw>0p~o%-KWo|7H@$3ZS_F(tPTjPv51HzwS1$Mntd31vTf?HCP- zDG^_qMDkdNAN0fPn7;Z!dC-#7N??>zeFgF3!MvZ83n>JRna5Z)B#y-*Fh4 z!f5*wKqH$Yu;zmHDBnX7z>0zWq(mwc>H_PPc;^NZD~R zGU@{H$ytzB$TDj$OmAbD)}RtcfmEdHd(y3*n5+&lx|knXs36%lgo-z#ud>&Ol3u2n z#*Sk#v@(#}7QPmx8pDPG6vAO4=5rLS>Ny?@(Fp62KxC(&DE+^{I6pf}93x(Q3@pck&6=*5 zrcbL<4wZ`dlhQn2p$?d`ceI2MmyEvKvC;`56tnoG!=Z6+=*q@xRDRrek_TNMsb-$3 znDJqzC1MXUW8hhGrEgUeCVvqU&?3hF@l5nkUWg1f9RrA+ug#fKtOhoOYF2GVxg~I{ zb=Zxnf(+IRkHR;G6gPxcTzpr#JRA6G(DkDl&}vZ&Ad}2P*Q1#JXFM4tFUfTIRlqt_ zxI-UXD{e|~!A7JP#bw^;r-~Hy$OP4Soc=1SnP?dk#{)pmeQU4p*Opfp0*~_o6r|t$ z*u4YSoq>BxL0bBU(<&hrF9s&S@GX-gaGyDK!~@I}Gu?DH40`>)A6Q*8&)t$p!oGrl zNls8S`%Q%h%6p*o5BR6Z1{NaJ5KOKH=e4+@aAz&9jgR@ zPH75zbN@QI8Wz|-#yL5$&~`LE5V!V6`aEME(ClEosle~Bn<0(687nt|6iR7hAZ}DO zcAI&pYX7I|I<;D8;XUUkvr|{O!5h=8e|d}4YBV@M&XuL+2)LO|_O?5If%-T7OijrA zJ?Uq`t>V`abaC<6t)e)exkMGwA@s{cwTMq~UhwS(g^zR(Otsm(oJ?`OG4!DUOa=!F zlf8Bg|BwX6Eujjgp{iv^ivAU) zUuHntDMkOM-J6_IAD;BY1YBPEbq?XU*?vrWepdcql|fnrnen#rMS@sYMxaxAmvh+{ zhI=0R8fIR=?i=t*!T3gL1MgYbLiQ2HlhS!mb0vbKm4E=1(#7-m6Di(9Yq5|I&no%) z+dBV=jOa^{=@1cbBQq1JLT!GVAtuQ~1}?&{zkdYtz=W#hwoyAb@Nf3sEc{7|+Sh+x>c zfDc@tewePW5XKn(4nf3+$*U`nQwnMadOO%Ep8g(8k*laaM0SjdF%Zg?PZ?k!azq!8 zCnI$TRe74>%56GKYhZVOGOH&nr?V=#kpSqY2eEiLHF>r4{T?nM4q;X~Lz^!M4iRg| z(HhC?u?_Khp>J|hFhEYj1lHetFnJeML*Y@l<=a8x$j4cD9+XW=r-RNyUlNGe+xeOsB(K{(@5>Cd)&5mRp$ahclV-KE+jFFD2Y-JOfy^4FBv#zk~!Whrh&Ra?(q~Jz@r2DZUA8tHiy}3BVcHRh35SnN25eO2y2JB7|(h><`Us4B_BNwJI4>=b2DMJW{y9d`4^f7J~a<26$Od#U#J1g2mrVuvHj-r=O z*CEVf-BB{Bkl_GvM#_JKNVBYp5S1Zh@;d5rEBWvc`hmo`zE6RFTi+QSxNZLxdi^w% zR;D+6DjH_IUIVqf7oHB;4+qcQYx_eWT9Bb&s5OKGSnSpJTZQ&A>wr-8d0)rZ?+2ME zUt45d;LQdWBFu%Wmwv{R2NDwM;-WU?6Yz#b?u!Y{ngY4doROo|dLKdZRumAvVJy8- zs!sVM_mXwv2`TkavxRkTnu>MZX3+A}@k8@@-oYL%LrCEBQ{$(u$)TSY6n2Q2_k6E! zy~*EAHwiLqU_Ihg5;bWIRD+m>9q7eRh8R}d;?uWcw7z$kUknj5lgKOfz3OG!&4TE( z3tUFqLiyR8@PAJDvYOFdlXmx_gjVbBe^Op+(FahabmcXIOCJ@}U$6 zbp~xG_~^(Daa7+oQ#_hfdnN8QDiNy(((z-SL$v81kILvRoiSCNDUEw92a0?r|Q0dTUC6F(}1vwANzFi6N~CB$5U<6?u8t74yEqUC0W2?sE`VwE`=m zR7k595dxu+&FvArIEt;HQ7aJV1c?jNF1r3Z$7H+XA@nwIl0Pixl?T^1hdp4@xlcpM zdg*TC$6YU;8;srRGwYn#{`oA73dwdbevd}_EY*WyaFc2m+JqO<==RW~)mV{2j#U^~ z3dcs@N4Yd4+T4&!@Y*weP%rLPtd+^JRU7$2M6k%m5RfrxTx1qaG*+8aox8Pjb{prJ zx&^YAJ!E%;Iv+@RgPxt}2aUx=k)!F3R3OL@XVQueivRsdW_=hKM^8e3Q2pD;CdQEz24~el*U}y7m01^Y zk6x>yeHlh&V7f%I;fXV$_Jq|V+?0ZrgUo(_N7)Sbk)LD5CAc)LJlmzC7ca?Rhde5Zf z@#MZUvBZ_&5M72od3k&jfKeCt`tL(52A5S$&fZfg!ID?z^j}uD3ieaTi3pa$5T|VA zM^&k8I|DfIWToT#;iF;cZ|h$t*@>5QZjxM@Alr$e;4{fX()X*&WT&&9&OvS=EG_8w z+y`JAA&^7P#c*w0=7H}lh68Yc%eihLJ^;#galnJhp>dB1aY`X4LBP-_3q(XJ+}3{u zInw5sZind6fSecyiRrtfJdhR>E^Mbu(@I*Q^M>{Ckr`uv7PkX3x=v;oozU~8X-B^0 zf_B#GjP>f%tyboop#S*wM%9X;%I&7MJWaC^#%AFp|HxmO>rHJ1zSIt#yKHe;JO&2L zoUp;zP*6|H`Sp&MQ%D15N)&0gf-V2|pR8vjXu+_G6hQ z7J4dwxH@&-O6-_9RbWttB2AxFnIqk~Z8l_e>U|^A#^TK2uQSr`>>sQcS}ym`n>_@n z`OKeeZx5zUbxKKj1o~`>J5XDt%)YEds-TAWW4ZQ%{6n_bPUaf&f_N1p6bN0cEEPi) zdGHaw|HcE~Q^rzGam$$sXju`pmI@_;>cPqO)V|28X<|u!7!2Apz_FBaXt~2CMDTT zZ&zRtk{%XgMRA{_Ka}2aq$jbw4txmYacJAIG(vzoIUfz#c;(N^d%5BYU!dfaL9~(` zsZ(OF(gYRVS1Ztua}*E0S`Aa*Vrk<0Xm3^ZhH)ddv{mAiHV$Qe+CBr{Owe} zDYw8wU>J4TEY$k2j!^0|z)iMag*=%C+CXXePVte3IxL}JZS=_|GZHN9p>;sT`UYp` zX|*}-B3Qayr0Q2^D@0+_jswDdm{yM2@=1Stg6$|5e^^l(c67#GgzGYYoO~-rVVA-h zN{rzhFBqNrl2vxndl;r_1G6+JsW%wx1{C?~`5^G&H}n*Z@BmNkT;%LJp|8DH1f9Xy z8(G13-R(xkjg5e}HP;VgMa9XnMVCf&9NG>%NTVCJhL_ac*2bNFAb?FHHzx zrB!>K&8m@Ghclq6(d@Qj8RK0gMh>)`e7G_04_X|@!ykEbY*$JA*wyY;E_TTsud$SJ zbHo!S$~XL1ZaxN%W_plcwz%)(X^l&}e&g!rh#_gud72kcM;e@a)RXDnoZw?`s8YjMroeen9SSuXDUa|fO6@>8RQ*p!{6c4 zRmlAmEVK$hWFFhFKabapnAN@iYOh@A?7@kO`|7I97USI2#PlS2>l}B?Sn?te!As|c zP^OK@@mo=5cY$}ZMv|bbZ81|2`ez|gkeH~hFz*S>Ns#=mu=8-x zYKZ(gdMt_V0I_gen~ja87*iK|%^!TVz-4x^MkdB{@ZFQ48z|5>YlItdJGC?0)X1I` zm`Pj`2FE+*>?8b%v}MPX9a2KldN&k<`iwa0{c{LmhpT6lG6l8pBFG`)AK=l|bV8AV zmh$eGQjvb*75La5^bq6ZHII_BA9(K$Zp;flGSkwKm^Z69`+pE8qxQOCT}KkRAo)uf z(eTQB)OLsVkK(RUH^aKRSfOZ5hMYQzM!=rfc;aLZII%tUL}BpEBdXIFxZ4G;&lpoU zGjsNld&2PR^gRoX8%JJr+$MJ5PJH|!WvS+^|3HKQL9CayQ|Wj zb(@<4Q)`BVA)qYl& z%WdIkLmoB{W;Nnh>l4FpGBc6`t;|Z2z-d!G@T%qYRBm~(^hSv)7LO?oxKpLI)DEXE zJUz2Xol)~!UIdJjvG+*J;JS{bw0P_mGIP`@GkGjg=h?uhq zD+K$xO7+U05XW_Hn54kJU?KSk^}_Ihq+CeIhGFkSH40lM8d)h%?O5~%BG_Io9ZW_F zWBRJIogMEiAFDmS&=@*4e09)`WbuKx-QA6rIkwWc*6(x^2%inJ&4kL=9B0Eel*lo3 z`#oPH=^`z19tLY(o&hqq<(Yt+XZ2)-ba}XsrXC10cdNSeYTf0&9i?7{Ns?4FA=Sda z44Mj3cuEm!C*DwtOf-x@hE0CL$UTNe9ERQt?txN(X}iZpw--p2y3OcNAy&X*32FrI z-E|LDw|qmlQ)arnyM*yzjwy!yUW4)h=zsE?T(e5`8mQ-tI9J%Yug&vUsP(S-_0!p7 zJ_ze2d!GAT#e8TR$E6GZaF&ZfCq3q~+Dp&tvP2KtkGdGR(spOOHIU65Y3f3nmQ*v= z#UUw!31A)$*FoM!%W5~ryUs6kke=}0pDv>;hhL@|#9b9j7^Bk|V)yvsm!Ao7P{sy# zEtMA58n0{C5qy>0Osu+A4PwSK+$SM#@UE#np!C-@T5qIE+N81IS>@b6a6+M6IW5OI z9FljJQcT?MBOTD1`(B`NCzEfg9o`xCDhLx-&42i1J|#!cV{EF*UCWJZ85UMn#joUR zpjrEL*SkJuA|QC(XtB3wYwOjQ>1)WC_LT)0b{W`gnE#NFNj!d$gBMdQb<7@oJp;8) z87*(uGJu@$Knc|I(~m756ZFpWs00@LDudW)2!bu;@`j!w@nIN8Rmsmz6wjuLcK2v^ zXR8Ya0Nl=H-su}Fm;M|%e;KXICMPIM^fwHCddi07TrBj$nHOR{5=dk>y zZm4D{Jz|#1>5g0Ps-~_nYGJGN)jquxJv_&k7#>JyZpSdL>aNe?-VckyYt4)J`!ny3P<| zHX{Ecl-}OfakPgd=35PHoKvcHCxsB$MVShouYr_X*&xue{nPha5OewQ<#0tS>v_VR zStvcqhz>j^_0vQcVQwoJzV-M!t0i9cW~HV(9YB1IE;3dYTNVQKPo+| zD%o0KtOn_y4$EL-K4~S@#8#u3yORjge4(yt+3Bruv^y!?ww!@V&!_#N4^};gcUJ>$sL^eHY1ML|Ox{31$ zxP;0Q`(wjH;#JcdxZ zz?%lObl3gi(VwE~C z8PP>TtFT<3kA=YtcY=6JpSOm~P0wkL55enJ$LZ8f&oA~uR0YCpsf=j7@|^qp@+My1 zom_p``CxKem%bUQg9o`e{p=Vt2kBHz5(_@yIgX4&bcdM^fVGav(PW=3kwI^xZIH^$ zE}iNxO#0bV2}|CQKQ}$9OfSBq&Q`M&D@acp_Ya0k^?(a!&usDh?9fbM0^y(PWYy@D z!(M<;U-xa_8L6~xp6@fnw@+1>Qhxr!eBYdMi`+-uG94EVdY+`x{GYXoKLtIbPkO7? zqXea8c|R*+Pk+rsuLSN3t7^T_XfOZNE}p0=*qF#YA*noQlL8gynFlenQGh`H{gX77 zO36_h)l!674}==DDJMsKj~Y9|gW^PrBtv?gyl*nKDxPk#wy3V{TG>R^1fu64Le+%w zO;0>O9$hj<6Bxu{XcmH%lkin0Opq0E#iiHd{twKiq-wFh!{+!*jO(J`Ql#F0@}%)4 zO<@S5)o7%ue6_MyTh8nI9iFzDUG46aGfEQ>rK`@@>6r>>7L|k z;x$E#1*nSq?vz)nsQk}(>q7Ki&NklMQhVLnkRW*iy{EMA0PTANh9+TA#ySI^XgI}w z`BL?+poCl0ts$v)Bu>jMkbj8OTT<{QdXDUf!Ys%y zx72&RrNuuA8mb-}v>*C@k);~86Cv2ejA!@Q-6gW;qMgwDTXtx_y$>LnlJ`yLhEAd73o7)+D#M5i{?1Lr6H_qm1dVX4<)!}P+)ioL!QO6 zVg@cB<7IJ8{Hv|)4U~-v87Y)DoQ5cSaN9z97z8TLioNz=1J%2N;Id$hJ?FPRB?FlR zYOYhXuRl)o!UAMEm&F|Cv@LoAJVEc({GtNol+Vl`^nMEYV7?j&i=WnC{2d=c!?vjS zz`QbmGs4utb)CVcBPH6d0s&bC>URh};q;Z#J@Gj@dTALe%9sPy1mHSygf%&YJ#%k- zN7^2EPs(ou$IOfV0LFhHqm`g{iNVwttM1fUagkFhOJTXmpM5b9nRw@(Z2g^FULP%0 zqECf?1n8%gR!KMxEJh;L4Gz>WpY?pUU3X4ARJ!-xd2#V~B~t3{EEGEJy$7Y0S7bT{ zM-2zr7gP6tlAII}yt2D5#`Q|)w*d)w*eoi5`}BCNE%`}`n!jaEr=dPMiMoXwhPYfM{;P+Ilc=g8_@mHsV z&AR33oHuwbVk=KVY}Yt-oC?Qb$Czd9W7<0hC=FbI8d-fRY}jq}F)RDO-_ z>#2R2Te?D;S?gj_>pACWG@<9+hdNqycfDyXwrjeDc}&;5jJY`^HZ5ng2MPmbav|qW zp?{MG@LjIiFjdL~WQxlhe9q4jFF*1ar*zruHpP^Vx3OiVb|+xonEyg%&Z|)8!CFd( zH7RHkAPvkXbt#ah1NG6W(pjUW=icDQ&t{WI+~X-|mEo^RjudlUZPXQ1ps6&O470Gt zU%snP=$!eSOHM4>m9}f}+BYE8W0x&%$s%sNC=LL~nEXcDY!$2@8=G^oCSH$ zgHC zvWwV68T|Qgxbwm6ck%cFbsDk(1zLN2PZUfboTzIcCsMIoTv+Rg#Er>>980~I^TyQp z=4!8x} zZ2WnUwua}{g3B+ifLO?{9xkser+}gqaKj{MsZ&CoFJVQ{+8$M&3#})89x-#JR@;V!jnSSMQnd}pDgD{Ef6sjQzSyAVL z|IbPf8-Z?2v{Vo=Wapz)+~{~I%LmM_sG!Y>hk#7A&jd5+2zuV%qU|=^!sNNpOJ-!j z=c%bv*i2BgAs-wA_F$j;@BdmSJEL|#@ zpd`!8a|{XU&o}D0^YR#c>*`6M(Y0+Ndz{jm)|r8cQo)&%JC4sYbzOPgW{17j83(W@ z)SN-lmicN=+pM1mO`Rx|g|)g<4T+FN$lQ_CCM_0MqpD2-cNErfB2)&HT&A)r8mqTc z@~voAHN3D~M9RlgOt^{s*KX#!dlmU2BH7*}i`UqlV~0V(Y?o0T%QB+4MIW0E5j834 z$QqMC6pkon@1liMh*AE0Y-`{BoUAlr{RH?h5wE{$1)KaR{#Lre;_ty)u7;j>7N=*A zO0qX;vCTf!olTB17GK+L?#+L^IlG+H{ITW7mg`vWjg2}eZZMk^+&v4rk5by|aZ9Uz z*-`lYaWnQQKg#0`1O#dq*DAY9%lpC}fD?1WZ=BQMC|w5-FWx7;^RN6zbVOWpH@d~Z4w7kyz~#yso~#qp)g z5_Z0aSp0nHi@4x__kAbesN5T%fBoY?WHcer0b~68g?Qf2@TB&eaXs%eW+l+u9uE}1 zz$She>VP)|00uCnUjy6-e`(*OlPO#4wgL*NBp?C?b`6_0`lG+w6Ty$bGM+dY%(E~| zb?Vj0m&_P!ka2rzC{2x_HV`j*X@64#@sEpO;Tnh555SZjY%=Bw*G#w32y&x+w24WzY^zghX2!lNwWjw|DQPj^HSC-Z$=L? zA^Bl`MHCXwomGp5g^7WM`Q1T|n*Rr1h$VZT@7|WwQlX2bUyEdO9c@V-Js891P>Eh| zcW0)j7JocIze)vo~#ux5YQkf5Z?bY zqKmnk*}J;@Pd;zyfpkR$ya@Deq^XZI-Aju*1IsoyH=2U}0sgzABPk3;EC@rNm^es6 zt(PQ>V-@-&?k#!S^O~_E^5HXZ&d2!OlabM#xl7NwP-mOidm8Y=58z?-IB~hn&Ej#h zkm#c0)|p2Wa~k?xsf~Up7z{v7JqWPb?<@M*hdI{&tp+7f;DT02i6o;M?@U2xN^RevPk|%RS!Tn=3k(}bfHEve1Ff%Fd^y61UO=-0`^O=Ll zX13E>9P=CWh23g6X+#^(4Y#F)Xj6lGqIV6UwU8`Jo)MDo`l*>ZdnhkPnX2yh6v15) zEuqygRr=*0TGjqDi@o3HZ;exMQx;T}PK=18Dy0Z6lmaG)w3WPv0hU1K2_}>Qriav> zyoU~!Pxb~jzzBT=ygewQX4W*P5|`|~S8-)|)xLh;P~04XxBT-79OzkjLL>&wHm&^W zu-*CGE(@LnL0UUfjvunR)l$Hkc?uAL}Bc3w55+;NQKHY$h={ zLZ^iS?7VbfX2`$vnS?8k2RV5nRBC)=OvQH%;UsLqy+$?G`mZa(+qd*f-}C<+j7#5r zVVwdx6+XDuS{zHoSD+t4df3(|c{Zx-dJy3FdPsAuXY;Fl5c4OFlDQkuPXGFa*dgao zv(xiJFLX@e`zw;}&fqf0F+oRUkeczOq*G*EF<8y1j4Eg~wiVl)iT@Fg>?xlGh6ZV> zNis_W)yKk&+r&Wxkj{+;ZhmQ5h)JGDPp+gIseb^1`<~=%6Z9OT7VYA^=&!H2LFbR_i5+IM)iQ}6v6u@s^qffoeO>eu6s$OHF@-uWG8r{bYWW8pMG4OP(Dm{AnZ8#_ z(*bOV)Q4iW*RCosnv}@$EmPlHGEWP|X2K*?V!Zf2UIl*-*8|$oJTm+tGUctgSNNVh zgZ{1Qs<6&S#Idu(`Yq`v?1|S|!%q*49?cVR{$mYI7@r#L7NOk%*Rvhzf*Pz#7R*y$ z-h$tH#y~>_TYxH#4zv#e{iizN(oR)rCzoaygQ_}2%CCxExXrxkf}E0I=PD}94?0g_ zj-A9?=yxz@SYc?{qlBHCbrj;02w~Y*E>eFw&;oz%HD)7}#SubKpS^>cgW*8dek>?i zARd{Mn}AVwv7*0cj*(~$t8^;u&QYVhD2b1vzOK@9Cd-7W0YOT7+z6aARsUHlR`I)| zNjv8bx@t@l;40LS?^JtRb?yg#=+_3oB6YJ7@C@I+E*ESSv3Fr^f=HPkB1i}DVp%c- zMeH}G7X@JRa@&VLM2GfVTd43EN)P#9un1s?Zwh(`cm_JnWU_oAN!WUWJ8MQI`-L<5 z0jW;}L1~70E1Zc}*+k*;!*-TqNeW<9V&(ILq2fnhw{6`u0bTLB4dLn`y^uivBk02$ z&3$2Pj4@fRmIz2he8T^ZhJD;~2)< zaC0bL6*Rk!P!T7HCfJoy(Shas2DKiAdZ{ogRl`v(OY&*;nIW(o{g zWUAujpQ55P-C~;v1G=O-QiU%pW-iJA#r#XT%n6YqJWJRdKVoV^Vg&l8#=QZEE zThoRy#8gmqtqafe)v$|HCg3l-x>B3uSn*xn%@^LzFw{>Lvd^S;eul|}X;f8ei-psZ z1-@I+l_i~~Ka%E8o?K;{wzGM*U86SAE$)E&ua+fYNVhz0XGQx-6^G!ihzeC&D?%A+ zUB(D8DeFYb+yM{7atCO;eMc99DYmlSvXUu5Ld{2q(h&GL0}sfXWO1H3!q{UwJPavV z7|Mu-1`Pgcdq=(|<<5y^RgU9>+jE%rgj(~2+ohr&XJdBmnm)jV%#G&7sluv;xz< zFKT&`>{%_GbR$v}(-uJ@Hu9*|<8t$go#a_9=4Bf%9%p{6!8(egNS@bKq};>|@8fi( zmWiROizI5R9S_=Fd}w(*#VC}y>d{1pZLRXH05zX8r~H>`Q|XUzLMztCZgeo0$KcR! zzrp}B(x)FDhS7_)CC$G%AX%;%Ak0yA8@~2zH;QMLs((Sk7Dl=)=jL+coAatS%QK^r z?7KsyGfJYbQ-?8ifpyUtM`Ny*f%cEO#815IooxdrL}?#>m%gfi>TJvcl9RBv90z^T zp1}=?vTUYnxElD=lgVt5W>fI8<8U1_5%C;1=!vY}l64zrkE0EMEzX!W-cFbRZm<}OicvlqstAJ%A zR&Uyn9*Zww1RqT(r5PcSiz@ElDad&ko9S62DY5gs16TB_{+_{os_@33ZovKz4SQlx z@)vcAiLAYPrBUUIr`)`U15611(3L#&-q)}lf2i0pp)uhouZLjXv;R@wtJ4kRVYSw!zwlqAgce9Hea&6$6{@BGIpsdK za!t8nNkYiRG#=d7Xval$)6_!IBpYh$5rp!#j=4??_r z5PWqJtXyUc_)NHX!70E{B)no>lw>6$G8M%pYL!h?tYjTJ!3L)) zm(qvS84P{ki(e>nM5(M)hNND~`R5INxiQ$Rr z6w>VpoN*x5FKSQmeLT=na|I2u6x_dJ;iQL-Pqp$pV4Olld|-81&z52*7GB8fvo0r; z%Jp-L27%2~#(tH1RdI5Q4*p?fO<~ubj{_5oS#rrn z1c+~0yfZ*kkc&519sF!z%7ytuh2tOlI8sLcK%LT%I$6N~%<~`Xf}hV0fp%&gd;I&v zGtU|QeGZ=eIwB|deAx9x^{G@!T0-2~ydvYN{B`ss3HMN2>@&pvjy}F%HgHi|1$qRp z2N|sZbIM}@e8ojS8Qj5@{L0z=5eN$uhis0{quhJF~lx%zDY`Wc*H>r(9D8pkBYbX!rU zOF8yVt85QEHsrf@-Y)_tu!1MJPs}|SWg~6j8mAA86Ky7lo!?LLmC{%VLdJbSincvd83I0LdXq{!;gEo%Ae`4aWlKBtOCCoEnKni}V?Wjx($imf$30wxav z0wxP7#uX}t9WH@p!WG>tdL#Rwu=NlA=Eeob#W5HK_MHfHNmT<-mFWgi`L^B+N)ev^ zaC4|%V(jxKj9oG2P1eik0f>vzB62FBn^A(sb&(ygS+G_Jc0IW<{^%>QgTd2Fhy;@| z!RGquZX~M7WCAN|rpI^|wuoV&CZc?eKnau~%=VmEwDSqrp+tbA<>`2vk=i9b4QaE? z(OXaEsKXh-^Ab=?(?2Ye!5mU^;ZqcoKi1xfu#i_u_SyAY(0|HwOo$ToE{<4{-r*?t zu}soQ37K9rv#NAVw%hf#_6G55q4yxk$s6ZKaDCkr0Zh0f|MHTyfnmo&0m5`lW(Qu; znf-HQq2d3jIIvT40z0rmyPd9 zGl3ik6LXryi)f@QbjG*RDm)c)I}X8DDFobcY-@yvdGt3gGrVx+VRN@3UTjTXcvRD7 zSP`hU4AfEtLq{fAIcioySOgd9qX+UBFO4lZVADE^rxgr;SZS7?kZ2|fCDlm{&4+Z2 zy&X6xqRlCZ>6xMOV+Wu{#3UT6Lh2q9ZW$@NUjiM@YH14Sx#uCsm`aJ-) zHs$QN4z|9E6-~NRbtMovZEw6kw?Scw^#4@~+UdtD3V8lqtJ{_adxdS>RY(Agn#u`w zGorXVyvIa)88g5&6N<4?3ULyNw{gzz4!CkN3O8~zQWW10xq8F2>uHxvqPxO<^)7Ly zRXwp)El?vxDuf&V1vvG#^Y-mr3 zfLg42INqROLSer-sUo2e(P-H!`U0fG;cXxMpXK}a19Tsw!2hqOB+WQ8flXDupYPUL~PR zYa@~ul9?raf$toJ?*$%lCt_1xS)+aItk?AYpBE#1#-RYtcJQ1LtN8u;)PjY*^}iB8 zSs4YG$aLo$Ip~Rmd}X!{kEAH4V0|29gi&Pm)(8iY*h?s+m6wxN7NO$AMUe*ijoldG z-=>xf&R6ZUGa;U&duImjWLyT0bwwNw*SiPdyPY1PFS4RTi*kXfC)WL@bp-UX^Zy#W z_+1}-dRKwlQryN2PoY)Vr$Jw)75#6cjS3I+~; zpbY(7^TxG%MC%Plt=L!l+v8Dk?Lr)F2)Ck|du?E5 z5v;EV-7b5LpO94Vm2iiB_Zp^%xb%0=^)t3$NdfxbuZFcXm5>1)H+GUOX;d4gJ^hC% zx*81hv$nU9YS^7lX4vNS(Kci%=j!Mw5~j@+Oz=q7Sky5zJ91Ue()wBa#u_=Pb9hWF9MPz%YlH8$4ds5v-xNnk?EilXC4~}AaEvKrAsIwSM zhwWyWe>BMPGyiEeag~YrqLEAY+%<@Y{$9?X?~+8DE0uHk}g+ z%&`|Y3SA-iurO6SMwDWa=f89-_!6FbcRURRhlp81Q+<60nGsqVd?dkrhBBS_eoYUu z(+K?W5rHwvo1OMzU_l6`+)+|pvXj`&&p8`>g$wnX4&T>;?Bhb=+9lGG-BxGx1!@cA zy=(Dgzy6Aa?_K-QaQN1{!A(=4@3YpqIp_>m?|Hwsp>16JwlsywV>|7d;mgJ;ooSve z&C~L-dqi8iwFqp1Qo&)WJ%=) zh}RLUelAxlzxoO9hhF1x34`gI5!6I5^g@q0V(J(b*aRC zhc5C^$L=3~F*9iDB|&>uQOz73^sJiwjT7}|rSE?7yJy(j?G1|#TsYfM6>J0TXjjty}5Gc-Qt8fiM<#$vDzkdXEtPuH$Iktsz!Rn?iv1m|ZNmG8C!D85P`* zEb()u2T(Izc;8p^shR4K+6H!6*Gl>nbskgjCxl>#ffTVFSqQCN#dCvfCzyzU*=L;m%>T$QwayU8%`&U(0x5-us%ZZ zG$&mUTXkU>k>%tl2AdTaLZP})?w&c2zMen&&-vP{-ZJSQ;>~kLM%OU80ZB^Kw=SB9 z){42?PL?98a^DseUJ!${`S0^7);*BQ*PPk4;;W>I053c29#el@as(S#s}~S_*rBX!I-yG~!oJ>{R_0 zR!lC0JHP)_L0)RK;Z$ug<*9Aw&~ysduM9r!FaJRfSRid>xpn;ZYXuQJ>}bi8=yZO3 zc>3-L-+XhG=AkU9N17j@0aA2|FQ6u;H7sy_JZbltMKS+mk}oEfGAIR^`6IMsyFOt`#`%WD2?)IH|gUOIeiR^R*Z_N$yJF}Lkqw_;<@uCm1Dc+PgjtgNr0 z^AIRIy~CivJKS}f`6GC|VNjmr^K_NS+1XigW8C9~x!noraS;lpmx&Qa5j4-#r#&^& zJMT>}@!O5_8cs7fVA2e!-2~e$jo0OZp|ffIk5c#3O$xs0diVAph&O%kF6~V7u@^2? zO-3Wq1+V6(#&sbt$TWdY9q)=e78-Qe)^<-qNIa4Ab?j66?`~RdKa?gOYMJn3gZbuq zw+9&CU}0wA`Jq*hNd&jRaGn_cspIM!STS5|FouwfQ7@tx!?bN=gn4Dw26opgE@f8L z3_T?)YbxK$gWQ$NrU+B+z09TQ|9JlU%+oOZtSxhAYUD#d{9LhUXt>3buT6s*KyYlf z`%!I2A)n~7qalP}jF=OIR^P_sCiVMy6C)*yhx6t3)|x?|-J_|6$miY#XUB1+P<|)o zowE*QeLw*?(ZoM-QtnWyUh(VJh?D2pfyy-vr$O!4+DgN5Do3sh5qQPw<3Pz2k3VP9 zqCkQ!;$Y@lw3d7)#ME3lSKn!*PDgY)T+kR3HEo6`8fL%J(Z07X{K-O6#c9w%k1>c% zk0+k69o#kkY)OFK!YYF|`D~ni`$~NvE7u8rWYzH1k=~`}w$W(AqGud$8vRGH_~%LB zz2yhwE0BzA+=dLm8*eOe;VZ}3Z6LSTGK&ur#!Ho-KPJ`B6a#-X&og!Ec*1|)`$P?G z^x?O|RwBOjsGW7AaPYv(^_NW2gz&qBCb*5=nP}WH*Cb(b#9;n~2(Y53mCrmV*mj;~CnH zjY5)-U)=qIEdzX0uL-{ef8Vh3c5l|1y+!tG^TzH$h4jyb#@2vR=8ni ztI(cAW~Ff^K9sXIzNg@FS0*-_e5jkM+M4WUxzpA18eS{x=q~K>Rfecg@|nE-0C{t8 z7ji2GYCAN+P0<83jmBv|+{uPye%$Po*JM$>D1E1VfpB)AXY8km(jh$8yHIWFHwz>eB^0h(n>U3!)XI+sBCl1eI zIhyQmQt6akh48O72Y(5;JJJw#aAhOtZ>H;f##o`8x)=!3QX2#_rJ<(=)A2^i+^D9u z_QNvQW>~3(dA_z*Vn0)Fw{^g0fj&JR@ujrK7etO3l+^QUjq`l1TUGSw%DqRpzNpxF zc^tIsBY1CsYFt`&Sam}@C(+T4Zu*6I-aPdELC91j*K&k1Z~spl%`mM2nU#kcPh~`f zI6Wbsu=8`Y2^t}~3%9@ybdT{wl6Py#0cY_~3#aOhZy&YW8LCI?>cH*OpWV7TS5)1X zx@h~jzUxhBf6uwjzHEiB607w41@w+{V97a zt0$R!8Gkc2qrUhq{YpHQ`+KMSRIeL@KQm@Mxv1Q`@zCz!J{&+|7-i$e?f(PO-8H0&k zV&4G+*4;z4DsK`)bYeb}thy>)oiX8(!B?j3{&0S<%uuzne|3ZP@!cvUpvY)bRA|Py z`gLNIWoIP5UFu+iAmpP&@I73ccZD|pqlhFWZx++>ySg8+YlN-5z#vW&>=K~tVQ$ir z)jsMNTl1Hyxe;rwt^>fh&&N z|87h0V2|nW;hJ!`!G<&Ytg;$)u&Uo~;WZ&@vtNO6Z(!A?HE1_pOQoUbG%~e=8I#vJ zTB-M^xZ3zOSI2Z_<6k}geRfvdA#2> zA7No+$hEDMsl02Vs8_0$v(2GGCz&aAo$fL0TrcA6pwiCq#cFlVqP4cf7U6|`PqpH7PyyAs@Nk46+sx{zv$#71r`yK!8`8)uzRf!_-f|Kj^BOto5=5z1#vrqsi!PYzNIeAnv{ve;~C zUoNdVSd*W5#dn$B+WvT5DxrZh?hXN)qq++A{t^7)3g$N2J1%LWPttZQ zFJB^G4yK%Ixk(?TMm$eKud!FQ?v6xJYVvJffp=#?^LBgvcLqUN=XubscoTkef*T*Z zTlpDR2MsAN$eBF}#c)lEc?}caK8Et%Zy4g3MI|LzCmrsZ{*DK~qG{o>Y4xniTXU@B z6yT4XqJNNohG;9Hc--JE?^DoOmZjtnCYfi{tpCtxz<4_0V+h86TLl`RI%8lpAHrvy zAFt;wnlwR4%w7G5^skzj~Odw6^xJ%PGXvnmyr`l;rXq7Ulsjf zxpKl%>h`5@*D~j*)>KA{_VsVqAR%Gdzbh0e1_wyg3{jMeMbcEnZceHjk+90v>LVdr z=pjj~CxTjg*%+BHrv~qh3N2PCM1Gk|fX3y_#NMwAgsG`VjNwW(4Cp9TYl?eSZ8MAo zWj}j9|9`GSLQ9yn13fl!O4aG@We{OPrOEElbL$)xy045IwI*7ApzpmQ7vaYDZepUJ zL9#TC@ee~1e9W|(_GJF71l*?`O9`ax!?c%sU;7f~`psIpFHKbkexGt2-#dEy;T=3d z2G4m5<7*Z4QmA+TuS}8Z1TSBweKsp|<1X2SV>fo&0EC!n^k)lViugOLr7v9K2Cml@ z1o`OG1C7!x*CN9ezLp?RCq0VvPf6*6(iL2@deI42vuR`#GrywCZlwl;v7*{#uL;XJ zo`-MFj0qcSW4r?g9B{UYT7#O$}7}C+7X3&;n0OHt^lXS zMMnK5KsEy=L9ElsquJ03Vk6~-aSBhKlxAEyLRCI1V2#7u4XKj(_7F5{6eIPqKGAS9 zH32vDiJYcF{;3R98rhoKC3iC>k@F#v8Rwp2N=&p~?vxCKtcv@29YG>SShEXhfrbam~Yp@WmFkOCXYG<})}k=IBlcsr=k*?=d$ zkOnF1kdpm}eeEkx0HP&PM~LUR+EVH%2+rCs&wqL>Zd9#mq}YaRE)+59?XxEZ^_L-$ zE(lXHfjSCGW!NlH_SrYK>^?{s5b}eFCF+-`aj>Ml>cfDtfL3cOyJt$HnD_apFE)-?(poyJM^0KesS+i?BWWmD^ z+hs|`%(mb9@L%uKz!eTbj)ezz{@7Vg^}P)$)>uZ6B~hq!57 zQsmhD7#uBQSD8x-MRTqWY*#Vu*v&Y?((RaBEB9V6kxzP2!Re%2MZQDYo*tmM9@Do~ zU^BgTTi$tmb&VDrvm2sDTUl`%w5_hD|6n+c)XhO0+pFasC$6Z|O^y4F>~Woz3tO4} zLiTW2S~YXzLw^x?MS*Sb9%5qY)5E7*jA&RlXTF7y10-H;T|&_6%8KO*eE3_JWhhU% z4u`*`BqvW8aYn%4tQx6OPz4|5j~?t)e#iE^fH{b1bXcVU?&#mIecvdig?+9Jch&L_E~Uu&~9Uk55jydl-C805pqbvoKRsA zd0|0d;!4d5gN94fJN*#S#b^^D#agrS$H+wNUN}R!v2m=be5+79@%b;~cg4Rg;Ee5M zjD0X}V*=_lz~2@if98u{RtD@3^|2hz$ILI9P74aB;T-npFY@V}={tm0gHH8Q!REll zy}N5$OL0FH!g17oCIJ3vWQmEz!d8BthA#T|uA1RWnolEP0(J#6h-Ab@`bheUI9|X@ zfTj!QVaMkxZ!7Kv{v%ZRkp-zqCp7TJ5(%uDfMxzB+ z4@q^DDqqgFT~(yV<2@7~0p@n0y*HqU4i0YKU)fAAaK1ZJS$~bAEw9B2E{f|2e>%v; z&I8fDi)#2KXf)hN0kyU{eXriK*F z`jGh8X8XVote4_@&N?-fe}=(mJ)(Vqm=(r)DLd$FwGw^oQkzapfgaf^nU{|AB`pX9 zzfENJJ>Nb8P7FbuN)*>NrA+H-{~XdoJ^k*rDNXcgk~tr=@lqz|WTdZ#!{Mt7_N<_4 zh@rQC&btk1!TlW@9dEQ=^bZQhx6tWLZQ5%Gz2NtcyJ^UjJjrG|!!fx`g66Wvcjz(t zr-z)D!BOXG+Tc2!UO`wlo(darIaBNPP;QtIk^QqJ{eL5@keG`#itC;bOflxsmvBFz z*zq{}y!o}b&tsHEU>m@kIUk~>_Rf0s*@cqTjx4X&+ zwIcPD7mb0=8=JQM_tv6~`=n(CNPHHJ_+Wa)6IQ9@ zl$lQ%m^Rz%yA(+Xf&xa@<#u)9R=qeR2E|f;e)25D(EK(He!+ed3&46K;m7dr-1^<( zkgdF^OT45)8@!;dv4khSA|OF%7wgl)9s0t7h&JuesXXO)H(7#nZhcFig=psiKYWw% zEW>cb{dAK#v3^=8g_8+1zSmrYz&dU_>Qyhl4>1{q7+y>7XAt4aDvl^CjMfy@MexS!DJgZ(%9UtxG#1 zJD`Fgzm+cb;6|;G#8k|k0QSFMR|3bh?Tx97#R|*S8X9ndUKB9SJ>=@*z((FOn`TV; z^stDv&{iS$llq_|jtrDWXGCU?bg>CEWo+-|^-sCqvTu=V+qQ|qA zv2lxLI6Fb2C(PO&Zoh$g3;O%H^)0=S|0=}nGqx*ULQH!3skisac;sF}9PYNU?K|Sz zVo*?-E_?JM=3T;B>3)gI>2v9fq^Gp;3X74-IsxR*c0XOGZQxVnVzV0cl68>g1yXvSmb|aLVkzd(iQcpE0f!>l z(sDbW2RT|t_hpl!{%RVn!H4V-{R5hSnuX%$*4c}S78Ry|=E||A4mn=ii}LI5Ou23` z4Fu(@Wlw9Z#8LCNFMso0y6|1d&vJZ(&fp6ii^d*@OK16rZKH-#vC~vy5~&!!*3(>5bdu5!^5#I{&MbMj>jmy*%!x zVX)#LE-mO)wrt%xev^4;>q9+nLKESO^U6Mguv*LBaw#umG}b_KK{zh$!FS~aG854h z_615BLb9%ZabdVep(?z&ifWE$M|}YS4+6LbJ!Yl(AvPGd;}mY(_aa@|FY_APAvEHl z=-03!HrEG6u_v##^!0tH^86z9EfXOO9g4>zKq$scpGWM&oyYvpQXOKuQq?1d#E=DZ zRn%OQdGGvdV!4j)0exYEiV(GXbe1O^f~b3f7N@<@t0)%2yR3a&bj8%COV6acBn$|( zHUWG@Ydv-l0!F!QIQOo>-k|OwhT2QOhLKw=r_pd+m4lZEPd2g2>;`GNUFI9*BM(n6 zfk@jEnAQ4b%VHy#!6UAIC35*Men@3UY&>dCZ`G<4hW^ZV_XsvmrZR!YM4}GC~zU z%ruH=PBDs69SZbhW+$F8`@E-BAtFgby))@K?2Dj0}PhHi3nC%(5s z%x-JGrjN85fN8J7r8{#pYiGjrju6NpY%nO;@Y^1EcdrqYYTif8x*8!wrFd{cF4a9S zAhclp2`XNm#q|VLOeAb7TTE)R3qtAVwmvWtM@&QoS+G&Z^_t`hwlo!vGs2zvyQ z@~$_U?VYyGmHax8=sQVQVB4CEg@G4Iuo?|J9dr2E(HD_GcGUHuWUN-# zz5<8AkH%HRu=&ti1Fjg&cws0JJviI<;U9{eR3x$`Uj2*d=Ay+|%m8 zBNxAuHDdjLxLWFt`u$6g`l-juVyD;!`GdT>k#dmLHyPEmZ@nd9J?I||ncjdSj#pB5 z%+iA43CCzmq{Xr2>;CVwnK4~|C_67L#=TJBU`^J@_LX+HmvGVz8IS4MANl6g6Hcca zT^<+O=R#n>9TgZIB4|OM&9odJwO-*nuQ{wCp?-q>Yk?vHQsr8sEtKT~g3?~#A4TPp z%H!VWilt{yR@y?&8~`lWZNG_y?7S2wIX8^KekhJH3KSJV>}BOjD}Po>EFh3elb`RwMy)N=vKo_u&fp#wc! z&zn}Yv1ziFHq#7j(B=|TU5>26P}NGA{uhk;&R%KB%`G@fbu>%lk5G51yP3C{3c)`K z3~{mZ6-QRl-^-C>x3cdp(95mY4oiE4v|42^gOAH@97@9lZpvodF?y4#v|}Vho?BqL zD|*^0XLk=2lq(S*S6_%)7?qFEyaazHv{y6+`Bo1kHCNX$h`Lvi;z2`P3sMDKh?M6Y zCxmU_H$V1wf*k?Cvr zBtgDHPeqPqa7UFq&REEV?`DTK{H<^0$yCt;=chFGzMq?sZs)F2Yl8RHr{7pwI!w}WZvI(F~fG?U$m*)U+nL1Vv-$@ewhlztEx=-RvDM$r&5p+Cx!dr zRA{HNzuqzaL_7{Th3FF=I+<>x*-Ro^`(qSA%3*e28#(7SDn*Jqc@S4lzzbX1TT?X- zIa$P@kNA@2v!Bn1T36LI|0meH_aWsgiIQdE>yNvjJKHddn|>7BPzV~u2suaMG~DNc zk17*!A6JjLV6VIDjKJ~-MyR`c{x#UjaS;j$Ly*)soTcq>rtx;Bd95!fSA2vXQx)jD z<_?aftg`co@ZHYSF_7hXf9+{T0%(BDU9dX_w~2%eHiqUldAgiX@%~l)y-C3;O zU~5~t+;F1(IsW&|CS>lG_4-!BYmYrzN}#8;t-%nu=X zdBnxjlf5P%d}-HzyIHms{dU`W28dU;$#Xjl0#Np~c%~$r0-+8t5xlX0IQ&+P@VA6rrhmhTJ9j038wb>sIE`DP-u^+g8Lzlx*m&C! zXI=V_mlXT3wY$1DStEtT>(jN^v9v#rG-~>Fls|iVg#$GDnhaeVM~C!x`_5wJfvm{s zlP8fx+#3T5>V39wtj1tL&L3?gi+kOEsDx^4zid7VVbdbK%+E%BF-JFu=v3@lc>RiA zm(5c=pV}$Lo-(uKy)|0SeDCGXeeY>`ci&Fu-TgdVdOyEI9_kl&RU%%Ntxq57yMadS zx%eKd7v{C+zJgxz(w~1`zMan4u21RPcIS4M9K0+`-&lOs122-B$v&xU?XlZ?S;oBk zCFrI48j8~N`(ukiQ}JyC6ZY-zhSc#(sr6H(BdCu4cFlfYVjGUE{8W#?mho!M>iS2s zof(ZA>uhUgdgvNF%>chbhsfRb?^dC?EQl!y^MiWDnwwVVISyXJWeUOP@KrL59c10; z2SeuV_Q(VGSLR1Hb5HXagoO!-2%a2&Gf@2cB72u~k>$B+JqPeO?e>CxDCCJXCRYT$ z#H$$(ubuixLx(I_x=B}KQ-#)}w40>zO&`gCjz(dEEN_@*r)^U%w46 z(1R!D_xCfHD{CBQ_E+KRQA7*v=qL4@4s)jKA*%oE z!tKmFDz-$zmpJvHRCz?+BEykpj6W+a;*9OiiA&&VyNJU+fLjl{MtMHgkZS!@TWxT7 z{NDK4mO-p#+v7xl#W6%h++v-8{_us4&#$2A&cbHLAf%6u)YmSUi*Gn_ZlHIUKC?WS z9|v9bf%i_x&1syiogLyEQ3Y#sm{{zcpp_J=k7`TMuV(>soJ)E761;I{>Pn35i8)Y) zPxw;nYPWn;id+EmZ7D9*LPCF`m*J=-Npzs>z|Fc=j$UCozlZS)U*@@fQ^c7_R%{S2*AC$UpxzgACa_>1Kf@Mz|v_KFb^CK@lQ@IP25ZHVJ5oOqs;cV|t z{Nc@Zt^F`)Tl3jYiLDVzvEdL&$t3l#EP!WdAj%kJwYMQr;1+C)8G24R4+5;IBUaSc zCyVDX+<b0w6{WxC%cyN#3E9GZ=!WkCaXhO!o))DsY4J-I|Qo75rBqBD~yf zl$unI-$eUUXR#p#aY?s|LOjzO_N+Qu5)}k1HZxe3(_|OJ=oHCJo>9YdPu0wqY+3H zLx6N~=7=9m?IaZE$*AOVS;#+M~e!vM?*+`LF;@Fp1O|KX&Q7}qStOJXv? zu6O%Vfie*7Ji45~w1ZWz_$Bx;5bZW((vLQO^{|uxR02oo=rVmaZ2NZwdrbT8Qq&9~(Dq%9y`f39t zJpDMCHJM2TyT0Q;75hR1a?YdJ5&+BSK6*-@brsh)&!;-Y-i0c!A$t_ z7_~Wy(Y^8b)>mJLiOd32CV%$P8IB`=2`mTbevPJ!079a<|9lauIY8$$nl1wnkLIrU z0v-hD%yue4HnShX)B6u-lK8lQ0{1LTJ)%kMCr}%xS(%rziuC=n6?|$3I;ae)_${Gm zP%VT3Y6m>14C=a6FnTnu957KT2T-WcADdzb@P$3ncoxMyBX|~tJv<2Tr9ISmOA(-@ zPKMwR3OhYguvwD;jjhSDYm0$ov0HJ+wez$#ZACxqQWj{sT=7}&p|I8oe< z#bpMw_?U|^4qx*JSLOdal2#PpEz8La#Y}Jr`2W7|R{2Vd+w7MhEU=JkLy<7<6@%9W zApVNM+U?bT^5w?3L6~K;oLni!QHFpTUn7OYIF)B_oMQaP5YXUDfMT4=y>}`CFpc)e z`_E>f`_*IFPVVb%y36OfZmlW`%;O{B%Nx|@8hxiz0I09=w?J$bqB3b0tknw$`x?^< z!s9ZfS=_-|BY?245(?tuAPU^=sQcfB_)=~zj1NdkpX z&?Ae-1psAKiDTT$if}@3Jm$#V#p`p@KdR9I`EOkUGe&S3U5;W3W7(honkw~3^()lo zAV%t}`;7&F0avE#WczvVY}s2S*pA+Sc{KMVDAkb{n_XM(J1waW`u)zi9Sj`QNBPF&q@4;PkxuLrBrRt7QOR2jaFza%wOo7*A|J*4#wj{%AeM4Bf8z;4Y ze8oan+c?>_k$dV#zqqrwcGC6jKi|x49P2i!B@EcXzLEbQKYK9z%hxcg&3mYxfA(xm zYJ|ok6u4=nn^CcEY?bAVc>(G)2J^46-{0I}y{GQH0;_Gaq>w)b!ln0TEH;NXNx#RH zz!kpELN#58TpGW$*b!82L3mrU0<84JibhE;-Q`^Ho)@h8hP!*eS;OqfOm}M+HR*|R zQs0F`b(3$c9h13~9jyF+_@`yh+S%$i~f5 zeMRPYuvz1}v8}2_34MnxvPA}kHX^t2w>5;h%O?K5s>*vCU@2A4_84Hm4Doby#e}WF zmyf+=xbSdn2>YJE%W)wKN|{`E!puz&URH_^V(KB?dT>@*`N;SZ@@rmk(gy9iZ88nf zXxM96|w&v<8@~Od0Q)Zt~0ck$p_O84hASmQjCDM|{)&b?Nrj~akaX-u%PzjDWqOIvi>-C#ECeFhQ< zI?Rba7Ye6TWJ&YJC!fj+j2(WB6RJinQwjE}5X(Il%N;tL#eNeE5RlJPz&yuBkKw3k= z89iui%nafaQaPi-9%l7tG5;b;G9Q}Wxv^fN)~J&crn3f_AtX~sFwTSaxItl_jcSMs zqjVK-BxQ*h)`P9&ugPeE6s zWaoCc=ODpd66rCe*W6TWS?-x~H&+4GTpWYCQA({_M$h-GnTV{Jkpi}oNcasR7Vko_ z4Jv3(=|1^DhkCMja~w36nxfS#aW6R8tU(lWIO%3+fo2(jX4pR4M2F1!sn%@THw8Od zXYkBbh+~9^Hc_PWUj-M&94}2D2R?Ae0>mg-9r3iN{F&^ z{ehR^^_3pUA=TTU?pa>noEs0V5Ricc=$pf2&BNeZj#~o6q&Y~zl%F}Py-)fH`x~g= z(P-bPR_B!3S9(Z+!J5u&*~H zPjD9q;uq@C;BGLY#G3YN4-bCxM-#n~N=Gs%tW>lNLjQrt6BTBL4a(x6@KVQyFZ zd?cMbB$3urg57RR!v)!_k}e5oJt1g4Q{o+B;xBUyZ@Yfpb`g0K!4N|CZIEwW+~c)O zoWvKJt@pgzqrBQ9zkU!ae<0ZiBnKlx7)6oUAO|Z*AO1>xl+;xvbXH!Y2KOdYB7}QD zjd&lhq{($+2GSblM(*a0?B;Tx>^UpEBQ-BGie>tUdIJK=^85kG_LPWxSa{mBFk%W!aZ{$(Me~73Nlbi-w^$gipvp8uT+QP(ID}$OLAm@%T4m@YU40`Bn9S@hIkhj~v-%{C#Xr@lp+m{y4={U&lFCVn|6v-|coiCq1#7O= zMYWg6q&g6nghSgAZ-s{FkOSBIQjQ9K(0FhhB-ffDvy<_uj3u(T?0T~J{GFe`Nhr7K zt(ItMJk3BxHlRH830~EZIXj{I<4f0=bCRDc^nhCCxpkjralQNU1fshM24UkbC7?t> zcqk_j9`YSy1QN!ruHFe3ytTL7l5{N6d8WBes$M$W4lNJA=nx;gH~G``?nk&^5l?KT zuPNX(XC~lGq4I*S;_JcI-r1qQRbf1^Hiqr#4@Wd$4J^59S0jeoBHoakHZcwjlQ%@+ z%KP%Ba)wb?x3pJKa2w;8IlOW*&$7%z0PlH~V4!mKpr+sO9A-Y>^slCLEq0_2Ny?29>n9^E-zXYeTep z)wM)m7vHOTIg`%~@2ti^JY#|E0u6htb|rfS7T;mYHqtv+=@NzbiTQFm@%(Bu<0lfq zzQn0!tr$fbhRvdeTgak*=o)3*s(|URQAZlgGRKvtE7WY zj;L*2n=JbQi(Ss&o*Ga=5h)z%r)acbS7{5(UdESXrN@n z%Ukf~OyC&Sh-oxYbV~5=Kqdv0A(%dQX{-x0z5obDlinA&UfPgNu#iodz%j8A(*h#7 z#9-+_CM^^SxIQ##z&4&{iIeH%Zh&b9mS#y*fN3a}<^nWC39Q|0Ft1(i1WsMbK+=487?ew8r zUN{G+r5$!#u72WSiw{0A#M+h9yzX~`7+;BUX~;{=f_T54Y7oDG5;v;HsOm)_uiJqa z&#L~sy?epcVyb?cq(u{1Zv>al#9#FB$!)N!wd#aR<#H#pD3FS>J~qkf2Fob3$1$k_ zzcD)dJv7p6yErqgoiS@Bvc+vDksDcbTH2xAG_+*fVf*Y7>oSRmpn!8m>S#9ifw$3h zVce6w9&RA3Ys*ICrq%bG2s>;6*|aDZ#6XW9TpsQqZ}^j{H`PBC&De75n=#F&_;eZ) zogsGkA^M;C)?{1Yiu@r0>y%J1c<$CRad!rRSe&p;UT|{)D2;JMblW1+EKy}N45&vB zDMx=r?-#z^H^IDoP+IUq7Df|%22Z(^S0mazKF$s`>RT-;t=U=>h3%qTh5%Y$@qb5v z;L#6zqUegiNqXZO?q3PEx9ONjaWNH(SrDh!e22tQVfT+V+sn`zVz1Z5T7L(|063MuAMMlcdQR{L1BwGr zShg52&)@s>h>NR{f>RRf5@Ghw{=$R_j9h<959F&A2IHPv7gGP71NHfV{XI`>V?d24r$874fN7IF}Js{dem&Y!!vB({H;A!~Bhp z9PJOdXN&`CqO1r~Vxka%cP)3jp#gD{F-%zgtn5joE_mnnx9JiW{2En+0}^f#w2_1Y zDSb4xhd-(p<{7U^Ko#7gd=_+({=~Ne0liT_`Kwq{o}QGv4pgHJ@j0bi1EeKXWE) zIE{__CE&7skUBa3nf{sWpqPM@O#)pi(^nqokU^G30bMHFcNFLlN|r?oeL`;3Nt8tm zV-DK~DZNAlO_|RtUx>T!=rGSh83CyO`Qy5y$PBS3lc9F;@&IGWFNlIc@%$f&UJZaY zksB8d(JbB6Zt`wOs5*szaLYZ+2@1Mv*H zrBBtWO9hQ7-6vk9%2q$KPOCQ#WJE4!#HfL{Pqw(moAoKZ{Bb@&8_bgi3p7QM%u&Zv zzM{u0Ww{zP*Ll*NxW6;zi2GWv`(Pr;CqZihi{i>$Q6$l(t1NCWis7=tV zK@OHrtQ*m?Tyz?GZZA(kKs&8LFFPSS<`Hhu|Bv|H^`4crzbok??G&7xwJn*M8c~LR zL7T3m?fVFUzDc{jNm8G8r9e99W~n~X;nyqtVEPQGmPHe?VOXXts-A01kwh z=col6^T`Z=H3KN;{{v${oWCh|av%)V=8f3z_&v!95ZFD^6jHJ`;aGk%4u`+cub1M} zf>}$T(Y%%BSlQp?rQE;LrQA>8v7_5Gyp$Vb%pVjbX8s6|AcK=%0B;LsDFLr_2Qt4C zAILFTGf~9&Y{an>9m#zQ^Kq<4q`cjLTzLKFPaq{MbTr&}l2F{z?H8e#C%Ig*OI8Ji zuMuMJL3a0I?n5v^+#L!$d8AC)C@E)33IOgyfFpL3E%ttleZU_3h%NTPZn3}GVjt?p z**_^C3lp-y`Z}Db5Cea70hdcl&|Wt{F86c9_|LkwZuN*roZReAbt(vtfH&IorJ5WB zGBRi#JZQ8?%}Y(*BK2%~SWVu6DW9a2KSRA_ zo+A81NPU{9kD;yN4?xn!_mjdjpAzoV+(D=2Kmk}!V4_A(@~uf^mbKZxiB!)B_gUdS z=c2iuo9>Oj9_%_AFc$*{%^h-C%fZvEMgnec6n}>@V6K!g2NlK~BV)3KF^9^SL_v%m zhjue@GAJ1HdXiV7M{jygO_oi89HX9UR2FP_3Kr-qHteA_ZoL{uPL@hd-N149XfhwzUNYOPckqSNT|g@;K{t;(Bhb>sKtw<#Yq_b zl3k1aQH$qUi0BW_R-LsWvU%ol06naHbwG-zPQF(|{I5JzfSM9&WMe zv1JO~+pjr^oNMB9!&i$ zq4wLt#WYQys^$V|>MGf+@E)4?uFQLnu{VqGOwlaf6&&9a?)$?10Nv}|$#-Yk3P_p7 z=JGcznGdL*?wKV^=4bF_-gD&5zcc3Teac%e@Mk`-m&|ckG9(X0NXJXWc_NZxE`Wet5`z)&QoMxjupQEhgv@o)LL^5B%@#que!gtXIl`>6HZooZ( zG^W7Ds7FUXswzsPN$t(3?2A}(=}*Crzlj&fe2LUFKC@d~Vhlsoy7Ud8W$I-b!!X8r zg+{vQ0#0qc3c=RV$(^AP-WVo-ABKMe;|8F4`S&AoKZGF!6ZAFQyle8-|Fxh@O@EG* z*ERf+hO|wYPNyYtuH@wB&Ex425K$o zmx6^O?&D|Q2KSBOxu3at3UYvp$$&^uoqZPtR;-7mzXa7}-vigbesrEsZT5ZSqvd`G z<}Kv&L7q=t_V1VgM(R3F@I^s_KAb?!arm_$LEr2@Z~|bL+K;8an$x~vwjCpj$Dc1A ze1VdM#ciNUu&56(kLDbYiNGUzrb);*hDh=?{Ifn1RmR#TbJ>|WJNMH+GEFeMr9fe; z6HOtK`3hM23nkw7!xB0ewoNUYA+S>9zb8Y!&Di3^Szmd)7 zYid3l^UViitWVG`u=V{2p?s}dpK{wl7GA20^k-O%_zP{=J`3p97o@ z;&-;xeGA1OT%cb+*+JMc1#RqmXB+!xVTS=Ns7Q|i?dN@Gk8pT_==$>QXN8-at!8@c zFC%z3mYXA+(J#5?_#HRLZ)J1*HWx3O<1aZ1qn*w1XGe4V&ew$^mTPU?RpNGQtw)N1D-;%?Yd8gtJDq<}DJ|O-?ZzQngFWk@T&C(;8 z-_cRi|Hcxj!!4TsBBA~-if_M(-a{h^e}W7f9Hwth`?&vjU-lpIRX(cU_)>Ncht#fe ze|)Z=zQIUtLJN5aUk6NpdLx-^MEL>kF^3Ule&Z@eEuw+W*;1oet?J{rY8f|F#tB97 zdzw+xw{G3aWu;b8mLhbP>{={5oqr3~E%r7eF8=q5@M>8cVY*y5KtXf3}vt%ZVQ zE6|hR`2Yc;7XjCV~G{~Cs(kDYnE|B7=19cj&lmNi!`p?+eGbh-m!k)ew*{YT4sni zV4V^tS*L$G>-0}Yo!*`+FRs)7IqLL}T%F!3>a+{%^eb?V)Iof1Bc&w@HtRjN~DBkPn{Vx9g!-u?qD zs^fe7$7kl=r7y7bjwmV$D$;vbK~%sB_JUEP1{J%4pxCh+qp^2QOzep!8e0;(V(cyU z7JG>;mfy777D)2R`}_a?aZAy=gi!>v%56SMM|q9rEy`>m~LZZYATQEcYiyk zN)2R8P1R#cyLGSZVLG|#WUXbzU zmO=8$#=L2{rw4QS*@oxwZfdmpu_)gOcvVGoqO75OTft4W&vSTHrEUpcLcLU@b>S;* z`B>xfHkv;0wq?y!3S^N2uZ`DWie8xcz2VC)Tf~=LOv?zeWtSy0Z=#aUvTo9JdmS?9 z@UqKRj$x&>%PtxtFS{(Y25QNEJ8qV2I)?6VYd`?Octr;}`-F0=Ng}1c2q_IM^D!BX~%)$8i0D%`W7n$N zH_I_)TPS{Sxi-2=+Q@Rc%G&6_w9#JDMteD3(neP~MkPVkMi-4XI%u`g0m;wgL?1WH z`Bkv>-R9PXj$qnBpBXoaVSEwnuziawf1 z^M!{>AH8LLtVa8QgoW;sKDtqTJfr9%Nuk?VE;*Jgv!szeNO_*FMkfRdNfrN6>5KD! z@!c*OWp!oBdg9+GtD8ny-HnxXP&sAgTUjS6RaVdPp4-cK%A0KL?#=1Lu6eobB-T@0 z&6NSDP|c0g)$=mnv2ZHzl`Mf}_LVKclUag?WC*|UITwe+gekxk%0oYk(w$SsV&<`UD`V&EUY2f zNOfu>*AyG6A^U7o$6%`g3yCt!r#)xtLUykLBulYkRaL&f@o#rF^=02n`sya-k&9xR zQ-e#{aRpq<1W9R^Ir+U8$S(}D1}Zhu?X|oQBu!T~P__%_W~v6snR$ckO_V6bg6vII zv60PqY>hG}IkFLsbSYmcz5}_@c^?Huj9FQl+(UV|tap_>y#BWsFuxgJ`PiHDKK6E! zypehbnRz2~+0CeT&s;TWZiP@(I})<>ZZMafhT1=QQvYNSW$&O_w3!zoT#-Si*>hvt zBnA^@DM)tvjWStjnt)e-f~aZ(UjETK%#_7m1$EdKinx$VX^`?pz}sL{(5|9__AHaB z#17>)$g6uh7V4A+O8e#YB#*>wr=IMQ{Fq0oDS4!ta=N_*GFV`bR8Nj!rL`WZuErzz zX+4r3((IRiFWWiuNwqXSsiy3cu5+K{PyA4A=9B7>>j23o-DN)MKJiI`YM<0x$TPur zA7CNSFLrUFPN<=f*BeN`QEZBn$w?3HmV&u2at|~EHB9LC8*XDT^+gTL0Q(>#ITh9& z|Lw+W$p`qaGk&^ltOHmU@+36q2v9sq?#^8EM~HG$Ito zjW8#mXFomO1_v%dJJnUVXsbXY!-$)6Y=wtg`MGj?lZYGS)0QUm_sZ=}4W|5O$MISZ z7)@CdX zD|7h@hk;h=GRPH;2%}DfDn5yF&R>pzNkKT78Jy~S(0i-nPa7HFi$;)mr#$k}bAKmB zEQjFY51nZ|i}gDW$z>?5oJ8cU!D#_{4quT9pc89^B0Lf=`hjzin@E&cdPTps89lLM zC-_aJpA~Z%aE->e+94j}HsBSFakqm!#$$j_28r>s6aA*s7_R}XXpFbHosLD(PjA^< z8Z7#9xP-ALy~kSxyGzu8+MgB8usqP)|C4xqx$ zSjvDYSgN;0o+qii3ZCu&`S6cLL8RR44&-~*EdqvV>{F$2IW2jgt)iXFhKm#@pRX~o z*BkVHWvafWeMxl3pwC;3GvNOg;s59;(NQe)Vu?%1CknhpdZ$t3PV1=e)Wi5rqv)O5 zTO)%t&CX)HgIHUfbJ_;&X2SETh?Vr7=fCZ}pr8YV{mD0Jd10@X=R-!Pw%W}i2dbay z{)-BF`|+cb{5kiH;N@y)O+@1vFGMu~1~DwP!?Qe|to|gERkV7tBIgIQak=f-3k!Kw zMe+flylVJAOM$(N*_k1|j0rQD+RHgO&~$1qm*Co_PG9Ev;%sF)>7A#a9*=*3!PJ>0 zm^gh&V>Ao!SY-ho&*o+Aes(NU?Pt63{p`1u?`NBWw!043HN2g)xXfwGegqVDgeYwbUTSSVU6D>0k?aQQty-PRR2UHBEBkve-K} za!t4I+Zc24zFP~N`?d8WI z%>t03yf^cMtSfB&N7PVR|Iu3M`tKEA|0Uv>0-`2#&G$k3ekNH_=LV>v_cO_;g7-6m zu2OwA0e<*Cf0WCr-p}BLD>%d6&zLYBrqZ1XQkg0@lvO#6t1?NC*hF*yjYyMpxu7xa zI8vxiJ{m!YDR(VVdV+4xqlsD%B$LtrL#FzLY^tJ8%0pnhvBst_l}~0W&{}E`VwJOG z7b!VhPIll8Op_HQj=r2ForV+BWJQ5GswiqbA@8%jjr1g0u8rtPnp#g%w0e@F(UTqW z7%)9)pwW|LSx3x&1u!&^p>D1CKb@KiMNs;!HNmgkGk6c&$wuh#8n@dm7>X$Xn z|MarvKc0b6KTx}|4Zq?4X2Z{2^R(^3gy+i&+auF>!;c@58D8mzpI5mJKfZTIdxh`a zDIH(Q7Ih26AA?y=g6uK48MBzC(xRcMoGxw5wUA>}66D8V8Y4djH`8i&Go+kTA^N>j z)+eUl85;d=D(klu*Y7N%-LU*-|Ba3MZdE(`rQm0@K3Zotrh+LDDA(s zR_S++tlu4ICy=nPm89P-seU^t`rS&U-|g^qS*d0#%ZgQ?def=rG39mNo=cNobyD#5 zkSu||uypI-OZjo#YQanLn&T{9z}xKMWG`^2oE%=N#lcK%Co`>QzyOO%T@W#9Wc5|CizZ! z&bAjaddqCZ}3SzWz5SvnQ}Y)(y?G}L*25<`3?)_if2^rJ1p6edF^KUr;-J+!TeOVpzh3qawQAO zmD8ouuAj;=thCmGdTA`EyViobYu3nnWW8hd^NGfOa%KCe!R@Cfv6&u{%>*!;sYk4! zcSTlE(YcGhiWTH()(~EJxv-bEr>|lM$0a-HtFnWBvKrwhQFL)VwUl7 z45JLw%IUAC##FqRsm%O0>vL=!8e_50q9vV>&C5?rR>ns4O?jizvph2^H!5qjQIXYv zdWi69fXs>&2sap4eB5|epQuj z1DZB3_nDJde}CuLK~z$?jdA7We2Z=Q#XesPk3z~ufsulkm)j^Pk{kldStL7zVay>6 zl^nuQIbB*L7s)Xy39>^NrEv(uv<_hy(!4MKMYbj84~A>}!BE*B#BhHwlK6lT%pVkz z>jKFiBr$)`fcS&aYJc#E`-2OzKd8z5L1W?%ln;IMRy4xT+W6H%iQ*B)sn*AXL;!xJ z_fVqvgBl|D2PGQ&do9S|`3G#hsD6-JwIG2ZeqPjZ~Yxq|g~iSl^a+}MK{pG7O@AO67yxzodl+Q(WC z)hWLUKfCG4ypGbYSw^saPp zOC;}FBB$G%A%hwAuBCDeE3NgelQrITtk%0ylzi)c*)K8QI!@zTOJv{Li2K$F#Gj65 zzI770o+$a&rp&iy65l#S?OUbukq-r<^O1wdP&<4Oun(o*Arpt&bAY@9HeQ?6=K0Cm z47*Qvld>xE>DGtF3p6rsn9=X{Bmc_z^+>f}SIqk(8Cltsl4Mh=#M94EJpC-q+OLWZ zfP=J8XDGh@gyidIsC@lw+1Jmdqd2F$hs8dfq!mt;W`HSl2FOxofT_}o(28X>)11Ml zG0xdu6(4>6|IOM?dmNv*8q;z%>RuQqmU}H?Ckt|Ip*nXdN0W1xb`?wYEUYhTkLGr0` zV2Y=ud2)Arav zGRrMQ73?S|pD?eko@vI)-tEU0{0&9JU^_E2F!|Gt6sAIKlsp$_CuASqI#O3hI#H^ zY;IThLR~SbJLOEuli9(!zj>h^X1ou^Zk{_ir6VhOm%dW67M63RY%NQewJesbWwD$t z?SikAV^k7kYgwVOmL*zip{RP7eni#}W-m)M_Oe*Emu}o%mJ=IUCfP_Ywsz}7w0ae) zU>`hOEWZ!FM$y)i@&a8erqDxAlpuPjmwD6OTD0ZDVh zw?xas5$ks};#*nE*D@_%BWd{>Io%482bFJSW@VXy)#~_KBON~`>lD-R zuQWQoM%MAqxQ?$QI{vk!;{$b?b93tz?cInfIybi-sm{&0@^f>;{_fn|dHLKNDQjs; zKQ=&8Ey!2jh$wnilw^#$K+f8YAgOs#N|QObQnA6N21w5zacv5I~~+#{m6Hal^LD&Qz|59 z{b-J`%+nXfaE@1w6xqiw*79^ znndaBO4cNMq%{f4xkvWaKQIg2E_v(ia=P>$Z;u?Kk|3{1c5D3g4_b}>0cqdkmB~89 zboxh)PH&fWdNkMRokXXJO5y8W36>9# zeb!C=*@86OmJ`aplDw7)2xTjDOH;ZN6;Q);CHWuU=Fjx@e<8h;tvvagpl zNlSi3|Nf0)b6F#IwKU$aBA@^5QQ3Y~3-wdP?{Y2saV_!#>>SM=39Rq|R{PGPBAMqZ zmU&Nki>#LE4=Q_TJ_i0q=D^DCv$I;J(p%55O!-s9C2E-gpu+pqe2Q4x0LdA~$%|Pa z?>qjc=!eoKY2UH2yzjWYyy2FAj6140*5i_6C4Tjh1pp_hO&nGH>gN{RuhLTaQ?Ozc zoTBPSLT+|tvY%y|`5O+))=Gz(q`g8K`<*7%ipPGhiPf`M`JImfIyIQEHc)GDa*^CE z^^US^J7?}4oy&Vi=Zd%gs6%tKH+8Z%Qwb>CFHIZ2(rIIcGHvXarVWE>Hu(s(Qs#iR z%uJ=s0lAD>w!MX_jKSPsVKC3Zb{#L0&!SbBM2^=QES-$LrqN*Os%)>?TS;ipQEy#9 zVqNS_owc`hP}{^=N)t&6+xs%UtwY)}vz0Q32 zBWHcQt+d;1|8UsO^}p;KxX~79zZQ~z2du1x1I$~gwv6jk><-I6)+qN&XE~P~du!u3 zdmClnQF}CkKAC08PiBTz|Ci^>O1IA|?rk^C3g!QNj=F;5QL-cZI|D9%Jc@m_i0lMI zTa|ks#y1`-aFoi%W4WUV_O_n?q;5t7-maWvmr721xU%=dIz^oGlzd2JIXUDfh~ynl zqR2X#sI+kf@;h4-^*dX>)cX!p_GPOc6(1e5H+b8s5380x-sP<6a;`Rk|8zs%)=ceX-{ex>60x&T9uUY+XQ;lZjNXvi+1cB9smHNWt^C-;N|lvqwwqS7-3yaNwOp;|s44f_)^bMWS(qX}T}hR@Ybv+5S5F(( z23v!{?->C820Qkjp8aRZ{6KzYp2-Hl_~bHG{qMDqAkNNKbTCF^3q(Is!0i#9Cbl*iRV{)hw=?3Nd^ZtDLYo0 zY&0>@j?(ZWQo~)c?VZ%U>tt}0w((Ar2g5j|k#<^sIy}an4v$JthezdfdlzJI z!B2;$nk8hMU1y&?T>2zuE_xlC0q z)96<=oFQ+6&eM({q2F;pz7T+1=HdG^iTs&GU)D^nEkvf(%*4F_%IhzqjJyz_!A^Q>}-XIB$TJLoqHi}g#V z-}XStI6LPx5#=^uFJls5#Z&T{%DM8f0-5-g0lEQ3BIp2~RSjSeL+RuEtH_h^MO49_ z6LC<+drp^;{AhFqslT&u38Eh@#^ZBY8J{fa%q%U&_3J8 z%eL}Iy!ey|TbFCHL3LEUg42xhJBnL~?|L!CxF!3w-;36&dir*FO0|LUmDL7$#~ePKaq{+9y6Z1lJVS?( zneF_cv7NiJ?QG_@^MKgPeP%n4$n`_XcDAd}e1xG9Pw6Z-TA*h)ej*n2Ty0T7g7Uj< z*kF=cG)}^k8{Mb0!heH9>IT*g4h*gzNn&&WKmUSt<`7gX1i-}tf=g2XsMccOfL@>V z$R{~od;vj1YaG|A1qgWrfh~@!)~Hn)`HlJ%%vLPk;dGQAmIcWUdcQ=$OLSUB;n48zEe!Vf`S)m7T}9$Yh3@b zh^SAj!HdX0k}W_w`nHD&c!pl2;Tq5*OB3*Em`#=)_!VDUfR0o44s-%dCTHRJ+!im= zx`Fg=0Q9Dz378U2O8q{7gl~#S7@S1HDv>1I-u3KY6EL6F8C8$OYzuvn<_4VGViyQf z60m6UFHHIm@dBmklY-qqowg+A zleR=UqWxxz>vsafvq%fObn92-1Rmy->tK3Emy-Jx;r7%cElH1@(cKCB6`W=11`K0K zJuL!B*xG`GfmS3O7(znd;xF^vz_s?|I<5l=_lzVGb%?aTaT<2Vp&Mx4 zl;n(VdpH)izaa?+1a`-Ldqp6n`V1qfJtvbeu@L|naNKxO&mLNKWY@vzZlE^}!*XbQ za>#W~U`&`3XzEAq=z4a)A~!H6I7e3l{FF_4qk0?KT6!<{x?CIP1{%}rd{YwE4kcm3 za1y>~@@2jS$Y}g+4;+IZxh;TIA{qB_A*5~HP5TeQ^)$r#4W>*YHQz`hr50q6@YQ7U zHH3FiZr8&w8ISvZIJXsve@Oc$2HW3RsHd`$x{xrZ2MI^hw*5u>b0fWLyMX|_g7a}N zt%mx>By3JoA7zrT>o^iF=?OqOd`#=#XiC1_}oPO*aj*02@W( zG1_&{?OGL#pF+Zj&beJzgT(>lPFHj$?Wx)HMVc4r*_im5MM;x_Yk-N}$@K#4b4X)__lbT0#JB&T;PhDQ51%Y^UA>R)8)Vtrt4tSyaZQQ`PL8P@;a9??WfzhOeMbu+7 z?FGO^p|l4H7g2qV9!_*-WDhd>_d6VpeJu13CGF`E=ZC%Vd+H6Q_yb@KIw#KPZUQ1( zk{I`#m@wRu#-u&{sFdhnBIR1Z8muR`iv~`y0R3Bk+hZF13ERaqSU_#OYc0|$%WP7^ z&S-Mo6%h@O9!%`T8HeA)x2;J0fqEoQ*A!C9oVNTgswYip&#$Nbyt*r~pf4s9JGDq9 zrEUvRSh^>+hP)QkG$qHrgi#Mi@?)eyuFjvEvN;u>DauMK{~_GuSQ>~(NA;Q6#01L`G3mLRC)DG;KlM^qz!bDedMIM=8A#qj*X! zBN!!6>fe%4BBi%&7$s49W52+fP%@=y@r+U^ec72&14FlvP)ByrG}Y|dQ#eN z$EX*jD=~~drPRI+quxvhM=l{kSFpAMIR*M6p;gqU&Wi*1)FS(3H zQYzun3z%kc35Ap{wq`Vn(p=tyMU?agMx&Vya_M6zEl*=qLTPOrqp@sk3mA>3w4(*1 z2~0vyMiZG%gfp5%>5mddlPT?uWmHPZqamXyOn15Tsg&F-8BL?)>d$C8rLE?SW-v?b z#%Ly`-}v~=qSTAliRVbxYFkEgDJ6|$^d-CZW-VJ2nop@oEk+AiFY&a6l+Z**i?{ zNLxLG&>BkpTNCmC-%{$+g^(ZkjZ$}0LLESVNa!m{oxl)EUziZ;31(8NNvRK5L}{Z) zC=ajx3FYVr^#fl~TG5$MKG;lYyEUOAZ~;;e?a(1P4QtYEN>6Jtx zTmb?o?e9fsHHf2hWB|$eHE2l5ryoiC8Z@GGn3nq%G^G^(8KI4!IVE3uw_89PN{?yI z??DGj>nUvqT_}~(``ZCNVeQ0eSJ57j$L^ip!yddUB{ZDY`7;9TFVhIj*=Pe!J}XTrQS4W8JI#Tk>)%JW>Jd9^46iV z;7dwhQt4;Gd`d59Ef>LZN@etpu7GuvKBawj9c-l3l#bs`u$5A4+RnRR2c-a-b|37e zltXKI1P)L#p=mF`2}%cO%U*#ql;UX4x8NeBO*HKTxK3#_Z5e|1DE&xt3h+K9dz#Y( zKB3f}-jOAIM`;l)XAPl1XeYe~8z@k!LC4Pynp2uU(_El|(l=Clyr3JU>r|c(^rqC8 z)>#c!qtujAby$OvAEg@5kJ39@t`@9EsSvm92&xOCDcz=hRu{%ldPb!O!DLF0@%SA; zp)i$FKiZZsm`=%@))@_3QOc$Fo&Y;h8cyqM0DDogqCJrghf#{4Ez5#qC_Sg+&>W7X z)Cu>*3e*ZtqO_d0r4^h^$&Tt$TR4l-3)+^Ba3Q5lw9YPYIi(YndcqBq_R_ZG!5=A= z(zfKoy_9OxI)}p}lu~HU5%4&rRy1uCJVWUXy}uH8jnXNcGewvVUsJk9(`LZ8l(J~6 zXTy(_meF3C3r$2qJ*kBG(2~+NETNCE5ZX|Rpyd`q1EnkU{+2)|N*`#s70{DXKCN># ztVU@z&G`+iN9hc$WdjVQG@NSIW*9|jF}Og)7kU;|1{w9j_H#v)nkR~h6o zF%9g5O(@ly=rYk$*bST0oM8<-ZPLI#m`y3L7oq*I6RTwsp~LVKN>v6CIu83%3bFCD z@f1$Nfs_WPle9B%xJWiu&*0~dG(0;Li->&41Cd?Wi=3=GFd8cgKG;$%NzRLKqKYoT zQc=L|On`*0!0D>AyKt^5?GHF#MbF`K6}^S)R0PphRV`-dCsmpS+M`M{po6M3dvrvV z=7vtG(mc>vRa$j)MU_?)U00HB@Q6QC(G9UsPX}HW)>!(uSfKN@;X= zy%?paa*ie>=PW^uRB2OChAM3aYAM%}hG&lUDw>Zvs>&@yomJ%)qi(9SRj9X4>9aNH zGgZ#@Xs{~h_h^(VZ95vRO52Mjsigmmrl`_>MblK}j-oFpb)s|zEmq~ch*qj{UPfzF zX}8e^RoY#&nbI68?Oj!16S*$*5$o0m&#GG0^#L=hJJf~1r@9Ni zZq;!ww&b{ZeP+@L;1wIBesvhSV1K z4Gy2je2+uDVGje%dtPuveNp z4(Hlc$Kej!dN_PHDiViR5|VLPC7~$}ze{L?!{N4FaOfD=8;7p`gK)SabrcS}+fT&d z^0ZkbHD)2Ht>)^0OVK@#8!V_`0COI94 zui{(bFekPX4r|2r!eQ>fd>p#R7m%C@<8hcVl#FA(BMHB)IRnS|+0Mt|oth-ZyVFX1 z{iNY~9ImXf9f$8~?8D){_@g*<>P7OjoJ2wwGZJRRoyGCf;;-TGjXjA;8hO9I54cg~ z&-xH_z?#pleSlZhw?;8vd%_?o=7d5-(o2Kj&_cSoFORtBk` zwQ6crHym>{tr||<;!!O~-DU`~oo8A?u+4XfP8)CT?PrveT~|}ZklK8}QCjDWgfA`1 z_tHPru*>bGi4VBzQwP^@f_j+io=ShFl&j%@=K#5#Wxip!)O%NL%YDEij{|z?n)MzV zh3Kl5K~g=7RQ;gN)5g>CANmJ^g}ys#Ls0AyjdLcsC?j~pDIw_ZdjR*plkr_B{h*D> z@KW*|qGdbyGzem&=>uFmNc>ILeOB7~=TylI(&~n_vvC^+xVOT2=2kQAx27IM>w0)7 zy65L}!`25(^ETGxX@f~GMTV@g#$}CCrP@eYZ4A3FJD;{e+W2IjPWXpAPRmP&z|oEL=Nfl%z06tCOi2fl+)3(wZ)Gg%s8VNm-Vc!S zu++!Sq&>C0h;?uCA^p&)I*BRqEeMh%CPa{U*0u{(N6Ya;Jc;Ld>rPUcZm>L{y3+EI zG{)SOgc-g>uDX{Z8f`K40qv6|)6q7lFQt=Q&4vZ#DLlKHvehlr-w!{;HzX zc`|_H>E}Hjk8HNLwiE<|sw!>k#z%qZyxM}4cpp&ePRhRXsOR7VHc-#N{6lYFQjb~( zvfYWrv3hDalRW+K=xBR>h-}>uG_WT3#e7BiF&bK(T(dffpTRzymMU>3cWS2c*yp|e z&Ngp*kl$zY@HDo~o9;9IMz>ihbxdcMIXEWNQ)?p-Sa=OEs(H0{M<=3V^Kt&Ww3qzc zml(&%KKypR(ToU*HBk7UWMXrrw z>VreBYl13{|E7}oO_judUqO7uId@c$GZ-E+*%K574x0W-!^1&NzzdY&umG0f9*d43 z7EwCp0>1vmJ(4h0uk$PV7dj4At|5R#?K*laW zeD{y_xA+e^N&8KL|6TjFcWwEfO41wQqG7=!j(er@n3ZKle-y zt)V0Bncl%9X6S!vNx}czlF|QD4JP1jEq7Z|&-nkjC8fb?FX0603je)TXWRT)pR{c> zPA#4=gZA*8|J1|taj7l;r&9lvv%dhb&%YuPZJeiDLff|Xf9}0+xviN1>miSDo_=Fl z{N@UJ)d}nqNjOkP!j&ffH@U~)Hf)c(1ofQ`p_;H0$A2g6#o;~imVrp$|3BZ?k^fOY z3%U?Z=x2F?_VXEB+f{HeIJF8nk8#`ll_3K_kjY7BSB&T097^NNMvEB$fi9dDip6>a zKIN2ak^uxTfK#zav0eZpI8DZNieL<oWY_Z-H?BaBCU>r(XgN*|Da|oER_l*o3(%a?!A~Az%HedQ;5DYslzuhKnsU|8 z0`&A@5@II95DUWQKwt@ct1=3(BXoe$IFO!5D4`mo%bySezKpVH zS_q|LVCYGxh|^C)X)T=gQaZ)y6s4<_X1Ki1^%X6FZFTA1Z38Sp-Rf-ITaIZDtUy?G z*6Pkw0AqHR)z$j>fdx$*<;q<7pZ-^rp z%Bf-J$tEsfR)BK1u3%k&Bq1=w75vW0r$cmz2l!J?%L?%VA602pfKwetcP9FZRY8k7 zQqHy^RYC7MiuP0k`Epv9kZNGMOnpMCgKhPsI){Y#fdeY41|)D(5TugEtxi zflAB#fvt+_fGR4g2f|fUA2j1MBzsawFvt#+>YNo43UAZUOh{ueGD^vr4oX$j6f9R!2G|iL zmAf301#~eI-3e&{YN@CtXe!gAkXE2nrq>~D!0uRyU}#&Aogk5QXa}%HCg;#jpkFGx z_l}@yXjf2@Dy7xNw63W{p`qQu-DVQSgnj}}Wk@~WFtjHy%aqbGLq7!`EhK6eng@o; z)IGF6aA_$~-_U&E*Gfqn1h&XDD0B$u(^io;5?Hm9Xl!U97%J2B&|=`xUg^QnAf&yN z_C;t3nA<_3MWJKCyUt3^3BaL?l(sf>5(w-j(dN)nuq9WbJ)zS;lTRc%7CIB;%5*w( z7AWf>(bdp7psc4ve}v8jDZM3n9y%Ym^pWVV(1jqTFYEbvV}!89V0vGvKc|N-1umb- zBNny{l*r^9wgM#NN#qr_5^R&Hde|!PGpFRNI$^6ppF$}uBy0_sJxcD+u&==EB8gJM z)`8Swr6;}t0iz{K4_go3l}gklY%5qXU7}CJwt*!xB$^zy7nIJFXlB?x@LHyYVZVZb zvn2XD>=2kfTN#JLV99LO6T}*if_|Sf%A&OAb1CPLkfY!PBWjJuz?07v>BoTC7fR0K zz=hMiF`L55z`HLLc_)DVT&djlu#><~roCaO!4{bggq;Ca^W>2ZI}ZY7Y9w9+DKh;Y zb`b>3S0r2lDf6YYTVcP0Eiyd_y8>Jm$XXC~19&Wy#$6wN7x*t!YWV{ssptV{Picxc9vA)`q%tylmDC{o z1sKW6EV)7WpI`&0etucuFTqVt#h6|Jm&J^_HtP`n8Z_Zlgy{_^;b)Rri26dfYWhI5LQ{r=F8f0jJMWZ6X}uT2A{LIYl_Z z?>GfG_=?W(=jFV2<2)mr;cHG)F}c8i6^wq4sTJW0J98?-~3~8yED0$s|G~1M2L3AJXI(6 z3_>YhL7&h+(c;q!E@Hn3E884XYn#wTQz84ZC6PZjvkEfEin^B#4R{m)ETI-e`5@WGW+ z-yaQ%(nXE0=SouIp3$g>ir@d)a^R;4Ni!41iYakS~Cx1yI;Ce#k*uA#njYiBT(>m<3J$KzQ$ih>aPaS zm$&mz9F%S>Fs|J30W2nUc~J>SY`e|4lG86D^T(Jz?SC1L>|flX6oaIp1*W)MZ^F!z z+A)eD9smnd`R{ZKyi{3Qk`?y`RyD$l7&w_~6z_%&rxJh47K2&Wb zRNAs_4ytEjOJwYrK4NZXPOwq?J%*(q`L{Ui2Mhlj{kBc0o~bRN|9Ta-uZcPJMLJ4V zafgMzaIS{P#YOv?S+_YBJBwo#IGXAElD z+MHE5C1L2u+T6-BD8X#&vWNS*ou$SDb#2Oawz|QR9{b;{u*a#+OZS2P?p{<=w8>8I z_A&2|Uc9w8c`ttMYfenrWs;`M85W(mBSldwqRRYwY6YJiTDDeBYB`O1~k5k2(+-LPye1JQ*t>l6~`a(g% z;$82b8!c<7q>>9fnf60k(-pMw2BYTUXmdmUHUPHD3T^(Sqt+ghiPJW?9Le*>WZlCGgXMllW#NAY5LUZIXFW;~p2CAjBST(6 zp;&~hKhcbfUNY5lalEtT(Lo7e41sVeYNV=gquw!6LqXB>L`*mn1|Me3oKO%k>&|5 z17QE|AWD{fka8yDA#y_$kCn(T~j2ZvP)pwTK{ByX^}QJi{4dL`dxn zA2~&A2>gBo^R1`On`j?`%G55r#FFpy+f-hbZj>!K0=*lsRLw-uXr@)VsGG+_>xocr zc8q>qV!~iy#PIe~EwqN*C}#eD{;TH3jyw=Xu|I6g$8skup6Muxw6a{GzuV9jnnMN% zfzQBZ?-RoTt&8Y5U7rVt)cvoEL^52wzQis7-{C*ZmekPxO%BDGB@>yib`KU(=@$XO zM(T_1>nHn1&<9)rrtTkNG9$aprt$d!ZPQMCl5M2Gp;Y%aF!b!#`{*e`dPfW)icV*Z zRi-tyHhPLc-?`zN4L$TvnCC$D5)rV&?B;&9Bb7ucXrSz$2SuJu>e*jAAZo_XsLIsr|p{VFrq=f7c>E7!1s$a^U*9W-gN*&AJ42tsLDQ0nZ<(AC4K5+Xpmd`LCzg z&K_4ycX!`IDW(3|ti6Dj*Wxb73BI`ykZ^G4cvSNP9w*yxo7`2GX>vMAH00jI5JwEZ z1m794PK0{K%_^F192D$ zex;f5M})C1pezUPuQBdkz>qrs=Q}iAnC_WB!Gz=1w@n_;FjPnSvDw1M8EoXb34~Z( zII~=r_UazdnkxZHwk{e)EI=Icz%>cQr}!K|^Y!G{%DPUF8emfd98Lc33$XS5EICIm zA#})OBqsy<4nz|z@R1wum2Y@2nZVMCSyW}YCGL5_OL5(r09Jn}WXko=5NK>xAw?HT zIk)Y_JNwka84@EGBQLC(?aDh#1zsTw?tLu2cmmsObJW*Z<7FJ)`C50c`s>4VqEqr+ z6;>dY7XNg;s&sutNA!IniX$VX{LGpYrZ3PGn62lO^mFmOT-G9SU%+ zK>rB>9YB+vfIKc)%H1%JD3_1k!hn+_a>HR&;2HZloo!rDv&bDwi*xUX3q#5m_5&$ZNp5h_Y9TX~~Grrgi>iku=aUKl`5YI~ib zrtr+R6wds(dL_}}#jgnIIbrgtjSrk#f7zzjleviqR})PVtY3vbHQyxUS{dfd5*%m$ zlJaBSg}r*@yHr&yD*4y5*dQ(E=>&~p=)a;DSU0n-q2ZO zjI}Xkhx8QvY~lJM%>xkVCmPAmA%=>WZ-1bACtlP8inIf=A5k%;B*ZrvI&Bu-QzqEp z#l>M0SaU_p3mdZ7c5cce%!ErGz^+PwCYYcXPqSGpbCAM-(3t?v_fpKw0IK}>c^akI zeB(&a_ZC3Wh7@(x>92R`;D12iga`>HSpDmbuc9n~_L*P!W|aY&Ly_21Z!Yq+A>K)V zx@!oL>xWmcU(kk&=)x;DX^7Qm3aZiM>@aiqH2}t1@FwkgxiQD$opjf^RHJ7-D$2CP zI}ZzWFCY;a+We{~@8jSat?sd{g^}2J2@~5sxsccZz!3ShuD&Ff?O#423W0Rc8f;&YXqq$6y>q zCfxs5umnpwr2Zz8t<~l5zjPnHhI5m{OY6Tj`FNo}!B7#s$SC>%cb6>-zE`M^K-sQ^ z5khHJ;Y`|WYXF&{N-uRtDPooI^SOl=|4L}y`Mbdwo)7j`WSi23?646*#BziR72ld%$f_fBJ)iPa__IEiOde-M%bnW*gG952$M@b5?>{YbG(zU`elk7;&*ARoYH&f#G2v{W5Tq1UwUq# z&i))K2Y|I3t)HnbYFtG49IDaqWPw5gn1yb)D@cV0xLvt(0{Ztq%}mg+;~gmLf%`#a z|HQIt+&>KlxmmNpet*+%aI@xub$pZRce73~*z2!z_y-7x_}K2nX64!b(%i$Sx1PMd*TxB7UsYI{k-t}v3>kKD1TG|j z(FMks^ew(OJz!Iua^~8^pBa9g6hvFN*fg4~vrUWZBQGbN-G3ILldfjM)}h*a4y<~d z6H_8ftS6Ol3-4hP&Hl4ob?86_?O)fh1ldaURQn>PozKLYq-QxP6+y&@l8l}ob4?Df zRHF#`#A#&Q03>Cel)+AKj1GCj(NayzB%?oG9EQuPtUr>*s3!V;jPoONi(6Y`q4~Vx zIX!!bCH4EP4k)3Lq(o6b*9ADg2#39N(VKC6Rv-YCntA5<6uh`BP0>uDnj{Y1G8O55NwpvYh)#LEF~F^6hT#HIsk8U3!es zV_p)R9tgWAp4pHEc392HNFuhH0;3CY0QS3yFwIIMHQeT99RQiueu^ISISP^XLba#` z+2oW&5&G9x3i6r6vE9r?L)G}i!z%KUfhX2avT_K;&=WfC{8drvy{wYVVxJoUaY;k` zuHR6T8_x_%BT=iI5T7c2n%(ON?;sKQ=wGp#i_gfa*S zaDBX_4WwgeoumqaLEllwR}r^z_=2Bvmc7N%pzQ5#)eE1}JK7q@ODVkfS;%}xqzQo@1R_teDKi9C zgnU-$Z*^wm=L^kJ6yvb^^HO#<%#&nj*XlU%gk%TMmis|*4=NJ<$8=_=@zftY?XmtW>IRC6ma#`xN>USCdgM7Dj1`# zF0Y_Df(@!>-}Gx|(S=NO=(Rf~o&0Z!6ZYx2CG1{1xhB`WcG^3o zxDAr8hXkMm@(sk>uF^HQCHYjIXeJSmngV?73}Y#o-p9Ixus+1+(gXwspE*ybc6>ES zW#>|SyDqiEpqSdTsTg5nn#vo9s(xAXTJb_{Y{=0h4W7BwK2Z@g((z;o3Lny0VrR{6 zKM2w>*>f{u^_z}*k?aWh$0o)A>}kpQ5{4`f}bGGJ2L=8u87ZAF?1gToLirOTWIgGyn@;yJ>hzSDRl$ zqqxKJ>#)lG;#DCm)=l3=L74$vGm@P3NgD`yH%z*`!FtXQ5K@-r7ll-m;two ztM6_kfZhf1LLWR8ic)vYpS07aKW<_bR~n$Ob)MGd9e0j>K4cR!hv;DV)S&pDPDRAGe9+0g z14n;^yx!aEKGY4RDSfMya97c1xmf!!#}L#aDXq>MgQb&l6TJ~NTnN$6dCwGiG>?+; z={0b$^nXreCfH)K{cG*hvwTyP5|W4g!N}X#>5)e;u&Gpbw-&yn;~QmrL{KxdCt-$t zi5Es~7U+ryPJrIfARZus6`V7DSY13R(?CtIO%omSNi7vs%* zrS?Bp@;qSDr+W$|++U_S?N)8p0qO}g_%i_UAv*F}rOCX8P;mRlF>$*WKt~Aqhmj$d zcYlv31($e$9-)@sb^{K+4M*g3Vs-<@9^_j{J|QAbOvb+Ul!(<66y20%2!lnnW9k*G zuRl~ogBuJ#xd1yj_DH9l-xxWi{8M5!sr{#q1FuT*Z%&&SM=55LR!8|egrSc9*)01l zvA}Y>GUCq&h=mD@-3ZV=ZDBjlb}8na-iUs_dNlW0hN9cz+k=2CRHjoW=0O1JGcKG{ zJkirJFDhLgTGB1Y3hPR)&DiHG=-DJ!9qV(I;mVayLcqCC4cpNK&GhV8XM-O9!jpa{ z6DFAJv3|q5oFMgRuUv~XUg>1V_nScn50t4vWAgqoll?`b?BX|(jylXs#g1xs2z!wh zGlVY$h`Zc?9>gXDGCY7%+^nvG-;QTfNQi03>;!y0`tqurAYhlV$RZ`4`>C{8)DFWO z35(+!{xwuOC69|d5Tkgy_Wn2s-G1F2|0putn~f7Km33wO6nCd?A``rWdrx(f5`@{n zyLba_+%66@HA`4+4T8m=oai^S1_?p+@>%t6n18Z{2YCc^zL0JgZMz9$YGc1_etsbp zccA)Xk&JpT@mlqZ%Gi`;z6%S@jCqAda=S9nnYpJaOW@3w<*V8UUmS)c76Kw zMbw?*pzA4i>^FsQ)Th~Vsd^8B!{71&!JcZ&ZJ(N$CP<-nZ0#_4F2P&?%u}C$ntJ%p z=2Zdh3$T`pp?*gARMiq?ovh}&V)-hR`{CTvMfbt<7jPIjiLURWUT7c&puvr4noPPr;s6y#i`LI;GxcHC{@28Cn z*_ytFrHZ`znNHcph5(kgq~q=xs&mg4ISQQapN5~dn7Y&t5g(3qifLy0Ek6gYPW39& zoGRL+WeXdPy_0lmFi_ZE)msK;jmvAt<07^MztH6wBagv7PYC0~GS{W%KaG*|xV&-5 z*$S`KJlYD2(ZHe1+{=yhzBJY)f9zu}BkNHY!9bXkHJ7P2w(ZfbML}c>2R+^`wZAGBSL^jx}~1PAh`4!!!aHleI{36r86X8J`Rr6V!7?L<-Sn(5C3ZRFNp*<6nf{~(f^Jc7c#GAT+64ZE$mR8J3?v*VrHQiqRpy$ zbBa5MGB$4feEU%&Tb9eQLh^`=FolJ;T>YfVEvkCh+j0;3f2x|^w+H7_R?5ADJf**34f63ThqeF&n=&jd{Vc4@;vqXhbPif55IGWkdy_`icScUP`ejTNZ=?X z&7HgN;V&!pW21T7YyS3F0)$;~)0Ws4TBt#cDK8BZ7w z0k_sI#!%s^73h$5P-flFdB~+#aEr06J_NT(jcwK@3-ZYkt|1_KEeHmmt#)JMP5(<&4F%?Xo`!`{9$dGiM58ccH@*y|EXQ|@5U+Oe3ukeK8F+9gbM3sQ`D>_UYOCG zgmE$7t>gc=DhIOk8{4n~Ste+rvP;sGv?LFB6lAOfb+I(Qow!xOzZYQ3gd|Z+IDbYl z3l<}fw5v$((;hx#LBMezb11KIF`*4m_e#$jX9fI86HjK6mCOf2zQ)(S~G`}eXZ|gT_T(?mQ|M9EmgF&CM-ZP zr*O|$Iwnj_VA^ExnAEN4Z#r5-V8LY21)jcEVJWD|<*3V_0Z(oyPE*p<#TBv5nn|B}Ry})H1nz!U#NG+VqAW)o^Rw=h{8C`KrJ#agis_&oZV6klX`K{d;&saJ-AO03c3tJ7i z0J49yhGn>Z_V+U5j0O8>2TtB9U>O(VQ9plQzV4j%ROpV7207Q0q-E$$N>Nk6_=d~q zrBcZ-8VjBzekmYV=J^ScDdcT32IzNcpWh`^B*jG@S7JTQ}lHv!Q)Neu8 z$2X2g3;!eGCeGFno}=>3f{6S_zB^*98U!$6V@!W>cHhA7vk|c|iNDhd#~QWIwsq2B zPx*_zhP0Vqjf+tf+Wbcle@Pmbijbuqzy44A{wm9w58|;lgLdCV;_moP|4yf>y3+-0 z@030p9CO&MZ3c@CG8abDb?X}>X6^{M<8%fXr;8`vv2$IApF_JBfIFv}$21_zHFgg4 zsOo%^e|;Yk;Ff50Zv3kWR-&B~l38OEOqFQ{Rt-*1HvlW;7858=LLMdN5)euTz( zbuG@OvvC+KRudndPuI+lo6-NcL29*wQj-4$&3j|Km+9@j~+j*)Lc zvxc?j1{?^q-~jOwUF{+_9LvJ&i%*h|x;lJ4e}BHO^rHDaP6L zbf7GRhLy4_cUp*_=5?dRjd6T1L&e4%=FvAo7T3DSo<; z966n9dkU5WMZm`n{l;Hva%g@9t5#EK{70j&gT;DLJijfqO5~sB@_SSABeV#f7N?b{ zQWF%XjTItr_E;gsV;z2kY8f%ESl&D>u8NGW$e(oGs6JY2ob5(u9i^SeO=BHZt5rgO zM?57YUVMSdx1<5>13IbRYCA3&o`82FYh{cPG{NH=AkDx}pw?ndrKcs>3N4G5 zdi#@lWBLEm(INs(I6?&AcdWMiAy9d0d-@z?xjh!bFpdj73I2?0o_6-La?2VcU-6Fg zDTtftU7>50nr z^-LjzxN@kKV>Gk11p@Td|1IZM_ZkByWi%+l++w!euWBD-U)=}sfK_Xa zzV7C8ZFQOcw?d)sZ4=Q@J2|bOuTl3O1D(c=rgN#HD!XQK^@S9I&HfHl>-Wv@n+ zLHc9)D;&r|tkq)QG@ zPlIhYr9dZO+qM6Ql>&iki)vZ*^c$6E)2#>XGBA)_flBb!q!h?jr&d$k)P@F&*WtNM=GTn%bL#u*>-~#p?&EznF;|p#%^hLgOJdpn^KFV zGA`nXXzZznIrVaA)~#E2A~ek>J+I_s+o9j6PH{pL>?;2H<3!oS24=*lwM^-7&M}h{ zAvP9BmWvqL{g`Q7Mp@O_WEvri;lLJpL_%C=1JD7xkG4EL&K?Aan4UBX`_5Un z$<&F-?a>5^HcQ170R7{14mQ&W!W+dx0?gqGN+{{vXxhV zU~X&r#0xsgb%w+=lmuUbgyq4Dn1X zXzE{YAMUs->{%UqTmc|mv_&JJq|ynHVeHQdy%_Aub3D8!OJv~8PaQuh{4g^~dD~rT%)&m}KjZdzzZ06OUQK3Smeksdq>YaD>DnmV zA69l7^~yJvM4o`HPicG$s47$>m#+C1T$a)nQv z)f?&czZz=#A~~2P(YNW|s8c)YuQ^TDEwfE>+^~tBmjsoeSuTIk`ogt+nISu&1MKWCELdc-7gjWV(~uq0rF(qo9BV|yCg#$LMa#(g59MFo z{=}4>DY~-Q1`24o&QW(~`JQZySg9dzjJDGH@lRase0JzYo1cDi zsVutu1>;c5iC?x+!!GIEEqU`v+&`i70T;mZU)qGB9R|qFkSi2RxzVS5NR7C$9U?xN z08>2^_RiR_O6XhO@b*}L>&puDW4a!bxLpwit-+Fs(yz6>mHpKOrPUS(RC5(-fs6Yu zxJSJA@7)Mm`ZV3_Ty`f5PWWS@(NWcwlyk^TnM~ghn6Y$TE9&0_QnHmQ+x{fM+m&qY zZxObr8-pB*TR>j!c**F`!6bxk=e(RS!*p1t8I$YQh2+5(CDkp|bj_{TSJfk4LOXpf zXG z%cR%x@AUS{Tst4wb?lKfj%H+Q!k*OZEc?4SJx1CUrSvUuMeSQnMQFEHOKV|em5ni0 zr$5yYevFuzn@=25?=K{GQ4qfiWEa-Vx53{|U2=UO4cAq=oNQ!WnmMT6)}7x#v@AaV zq6{%Z1!res&i9uMozz-lvY+^E9UkBFljuNNy^-@_iC$#ACnWY5Ga_TzkGb|@>%dZHObLvY4QE`y)@vQ>oe_chkv*AJ7kKe*qY zQtt8lboQbmI~E(XV`VPMc=c)u+2bd}@(jzj`NZ$UUurTeo2y;Q@(q7nT92Z+;QwEU z%>QrUHvhlHsb@L1lOf{s%g}+S|64fmbxA9j%;kFGW0@HhWRfE}U+KQ9&@eCRQH#re zqad9e!bzx0Rq@{3riY4}@UZfQq`s6or$hLfQh5_wML|zG11O~URfQ$ah#xYOkGdBKDp=h39h`DJr`m_NbW?$WEN z4w4mXhvTFy%*?-&+UirG%l!vC9`CdyKOHqmU z^jT=SwqW6hl3LK)qcz*7$B=S%dq&9HBYUlk9SrVLn$+f+3!}RcXm|p4y@JH?{UJoY z`uZac>hIvmikZ+CN*td)rCHC&W0fda&q(0U4pu0nvuG)5&E}Gp0%5_9$4b~{hhP~~ zm}Si;v#Dvz8gcjA+-Lepm&1L)FY|7j*DtiSnY4+vzzE*G$XzP}(-^{!67@Q0$o6>U zZSxuacGpy2WXyER^x0G;4gbFpap%&KNl&xSfGzGJ%N-Lw0}*a(gn*u_4p_ACF_E5w z=vRNSI{ah#+cq+-pC}Khfae!@ro?*lRn%0n)j0uN*1+pj_m=;p zHJU@vvP@`)oOj&F(qP1|j+e$MLTy(&t>zIqeBj@0gFu(lX$Yrx?%!Q{Zcyow?=rNvhFzrn#eJFUdgo$ny0JH9&970v2irdn=MJ3rn!V1H zx)T0s|GB4b{Abn`Y0;&Sw7mm}wgi<{P*6)oPu;5=59UPxQo+$H+uVT~Y1j{lncWbv zvK7NK;W&{UBX8MmXj8gG@wzd6_os$ZFq!bFRXtbkJeo*rVNu zc5mFbCB_5ZQ|{?o!1%cZ3YlCNu(w$lUlK_7nC)@NK`_d80k>D`lC0!|ea|p&WS^a9 zER_E~){#x4p`_XWcMM(T{bl9Gub;MZ(Ud{sVxIBwTzHR9a{DsKETOJY`GnK+9vBEQ zauG8hlDnYTOVbSroJnPYkp{F+b zr~&sse4d9MLxDv8p$S7jXDhm19n;EQhcS**IiO*kqf{W#hEC-jhPN=hxn-s9`DK$mxea`fBzA7^OZHb!0BtnlV;-2+w_n+D42S%-I2ziGvHvX2DjiJ;BT?b+3ot{yh+kXpX zEhGaWf?IC@-dIsSh@=mVI!}mat5gN0Tr*8JxbB1;xd%_d^kuJq*%bIf8@DoD?R4tc z5#`h7E(P)4%zOWy$(+aaS`9VtCWWCmb?;>JHK2`~K+>UU zTW7$b2<8OFqv~+)STWths_nW%JD9V6G=Cmy_e?B*pY4*p;r5)J|E%utn{!u7-+679 zBloy=yk$g2_n8)kv$R$+T-?}&Ftp z@TR-+(+nR|zZn$sKQKIdf7~)Oofv*ycO#!IicOO6XgY&=6aL_Eu_PmmKBi8nkooMZ zsmxzlAVE4aywfam{=Cu8*+6#6eel^vS&s)Ms$tQ>32$dQM?Tv^e1J{3A;j6i>Whns zra>^uK<7mY!@eHG3pz0cInWxg`sXPG*%mM|W-dK)uUHa6p+#0{VK?ITiOb?C`!+e3 zjw!OsPX`g~=iN#w80c2niPtJxf`^}&sK+BYElMWS@-2&gb!qHN&?E*B?7g$`eh_ESXB1a zWd~MjW^vhyW;!MKAh)ABGfTDI4bsg<8=5ZYjwF0F zdeEj<-LRtVm5eD!&!EC7-HN86pFr(%@OW`OZ%fy#zA)8Gk#0snnXCNuz2mr9AGAsq z3Hf?#u*KoWP;gtZXKPN`{7hyUO!l1{q)!&JUgJ-!{K^h z0;Qtmo;P_#RW|=?jVt{YL9MA_prM02z}{fr@E5xFo#t1^J!)5(N?Po9+~x(>b*_!j6Pdt{{WX_`dll1sjL zwk?Oir~hu~_k7SkboqE=O_vj_1KJqB^6r}2JH3iF&x~uOx zeCQYy>c`dSW~#f8=rvJ9oh0lay)vtNQgc>8U>y{k&1BH&tJZm)Zd}G%8&-NdUnz`1K4ldltQ+YxRJS{xgPWwx8Oj4Mw6j6sB=w_)j5FB_<6w1=wKYF6YW zMZxcmSR1HE)1&SSI}b2pt7D>2`k5kvC2ayU9&Biu_p((pB>t0jqOIV-z@gzG*h+sS z`8Gu0Er0moL$?PD-S1XHkA?~DvDHHk9yWpAekIMjTDZy0Zq1e6Q}d5QGslikuc=QP z%RZJk@kJMk*Blypm>DY_OHMBSIBv9WyP!ONK$A~icho%{BYJLhp&m$iESq7pz0~%% z9FN6{dXLft{g7fu954*-Xi6KE4lyg z{c&(PVh&lfftpsxb6@O5=j3wC+VFv19A(PxIPf$NWfq(yF(}PN@He;Dd8JES*?QIr zj2r)$u6;Ja=jPU=WL|ja>D;e!d@-9;Ei+Mnsrx{C~>^`dP9mBnAF4d|0(khr`)$McXG z2pemy<2T=K;R_=l5+r!>f`3)6iQtuU;jN8#NU?`;^!94RZ?k~}3PCwV_53;yZ_TOP zmat&kf2;SxjMkn$qmZ_}E_4c$-fB3K3&XSPXP>ECGd$ow~IA zvt-TsK_7$1l+Y5@y9XdJw{h%6=1BIFaECtHkGPp^vjj6HT^wwEp}FS0t=I zIK$cOE*wpRo&0yy!@PI6uIsko*UW=tZE*~(C}*--TH8<)T79xu=x8;!@%)O9mS z`S*&(6JLFT`(k!8G)RsekvDQKqZ@L830q)HrFk=WBfTp1sX+6f<7&m6;aNDPe>Pv? zxm$C8X-b21a6`Qsk1vKWuUWd_ot-VI^UJFJ*MOv?vFW zWI-bCpb4)O(~=beydr?Dk-frX^)W}eiv|O>Jn8JJ=5h$Iwe_d(*DfDA9h4Bjht{;~ z(2FssVBTb{wgNvI$DkLGo2eUKhFmCihP_1pO`N4a;BgkJPycgGUluPg^| zr6c#x^a(SUuFZ3v_N#PEw0#arK$lIBO*)6uK+vyudGA_Y3~f}`-M15pj%Kd~3n|Wu zn_b|Sk9&HpdHO&gblEihcBs&7DPlPyev7g`P|S7+h&!up#6U<>Sl!tG9o?J05OY*vh&%SvESLVT2xPM6p_dWin|Thv*C?GLw41l(nfDxC+^BDm^^Y6pAm+K z{yK4NO2!6&WAcOPo2IZ0NrFcXJ3YViPx@S4<1WMEI?-iQAnQ zA1(t>JNad2{0fA2-n7|>eOhl(37eX|V#$50uDLj8mCPSkZF_D2jy*X!^%cmY6?Bgi zm`OFaRAdhAH$|nJ1?ehh< zD#|7DXSQAFtwcL5z{nZ}V=lDigPoq$wnJ!;K+1Hg>@D<_W*LpyNiifkHNqQ|ry-8a zfKE$4NKkiyW(`5IE>n+L>QOeI`^PO;M&C_*Rj=)>5K1nG_}t^n#La|IA5g9uMiLif~ltmng`&*=B)9`?l4N zy?~vQ9aUHA-ZLlSh*qVAN|E$6F!78?y4isS6v>~TSD&{VeG$F>)8H8xbzUq6ncU&k ziy3LY<{DTEJYG=X3a`&Oh85unC+H{5XA2$p@bK@s;H?mg#=nTpPkvSNP^c8K%OMDV zHZxs1X!kKx)dD2keWO0n5%LPY9n`q<8I1Ab+-O)3D$kh^oD((h1UsY%RgzCj*l7ot zQ$*93m2hOqHPvA3cE{m-Y@J9=r+D z;(pc9N$4`V)YeaJ@8QN%_zC+wp$(h-mmECaW~QtCzSvD%r8=xPpC$Bc#KM)-kCRzzWLV&iD`~ zvVmdC$%mjc@wN^3|Nbq#ejIo}BT<%p15HAe<~M}57QL<9Ubs$t6?0UIg_u4^0rZ7pgwUSQ==^?@Ec!8P_mThVv=-_lj&len#|3>_P0n zV$g9uz|4=aR^r)hyMLEDwsE>5@GrY=C%ak6zNxJaKB%+5vynShI@rc>NQ+0q(4Ze4 za*PM@AiB8ob0vp)iqT}~8BtplJkRI1eoJLu(}QkX8M`Z!%1RegG!swI{9^ph`>V(y zg^Iwlphs;x_=4CXz@VS%_}&hiAT|=d9a<3>&8%>4g{<1t9jw}1h_k2Y{@rakU329| z%D-dOXn)Bvy|JJ&Skx{_WO1ici* zCQ2+6WN)Q74{6qD3e35{h0!=s!@x>RRppzMqzze@V7m4Xx-4nps{^{Ik62ZNaRo7@ zP)+&|3=l@wPnWj~_<+7*Rr)+{S`D0EA9!N!v2q6_v?naklQ(mA6mPy4Fw5@3!S~pId-U*<}q7`cpHMBnnmE%I;=!tzk{%g zqYCN@=xVlzxgSU+qm}S$bVXBFN@k!ne+1ZQ=^5HdQmhMu?K7_Ek5t0P(4D`OC<3QQ zlVilF<^T)zxN(8ac|mXUJ`I|&5jlUhv1HEiGyU9VdO2B}f(^!lVJ{J{=(Fxu<%XrY zrz#n{6~?TF3wrR~da0RL*^BI^nl>%`l==-drGptqztBa37DFz9JULi$Ggs={h6z=F zNlL4HMMLn92g_5P3pIe(Sv->SmSr?KNcF4bQ|V}G&mbD!rm51|H)&~e?dB-+J+w-n3JO~a63`3jTkE!rV;su+2o}9rN2RkdFUJECl!P05I*~i z%*(TRML06EA%US1|AdS}YpL+iDsgN8F%K0<5Am~VxZ_fJf0iiyVmYzjer|(Lv605= z$fYO4VG=Nz@meaNP6#&vy#Lw|0xb|&)N>UT|6rUKS~NJATt&Y7F?3kBDrE21`N+iA z1|2p(n%Iw(9i*v@<6-NF=`fwqU8xFT{ER18f85`wR|VAP((Z63w;P#0;CwE+`20|q zhK;EGaG6%;lhuzhJji5g%FEo_ID}6zw(41Ljpn`ak9vWtSWSlUYS)xWX1d0A9- zzlvKMeohe^@2~%Nbc$e9+}dQQ%684+bp@-SgxRxNdAoF2&&q&S@gMo440AnHiH^(= z;)+jWceszrxj$yU%N>~Hd!?!yk0Hd2S=|EzG3Mkfo zLod;se9C>LTJ8a%-NyFJ4E>nge_4Vz#t$%`kPTQ`d>d_OdK~H6J!d-f^BGIpmmU7* zVgeOo5frlF;pr8BV6k>CcdBMTgkmeha;$tlreS#J38c|qMLgm27h6jL^jy1B%hKwP zA;aa>9aknS68SZLbF|q@m*Qv3s@mN?;BgcFTnj(O?ShB{2`x7PgV)iu9!q1afgo!n zQ_=6}UcX`iPy2_xsqGM!(AZhYi62WFe2r3=rf1`6g$OimOC7UsB+}4Uh5;g8A78KA zI~kh9_R3dXTYr4gdlg)*=$@l=*e)`DRX(b3go_=v#oA9u(e1oTx=V3)K77~S@NC~7 zWu>^(P5H@P@}zF>CD&uTv|_0vWVqgVql!00Xu0dzq6o`X+>SBLN9Xg!yJoEN@R3uE57D?=xjE#(zlEXC zpgHI%0{SP!?RV5XW-X&WXW)b0b}KeMh;+DbEP##_oF$7GeV%ZwydDC8R=4au^^l_eC9cIYBwGW(iy zfxA6M`t#oi3L6_@=08(PW@I}xx45OIH0FfFd2@eHcExi&gLeH+Svty6YR;s(1gTJh zKPS;XC;iGPV{Mr_KMh%yA$iONBKsH4ualfvX3D6ys-(BGbs@!Uo(g`hVe>Prl?CY9 zN|Ka!O0$+Qx5_NHDk8{JWXRLn1jVkL;>W+Yq@K>mCbnq*XE7M^4BC4P!pk}Y&w2ku z`3?_3z0m)?zRaR0+me4gm*l8O_^1S&ppaZ!zPBOf%s#=!rEF$h7u!fxN%C`y8(nabXZe5RNLdI8 zeU#*w2>pMV=KTLQjsHMGAzhKf;sYoyrd*K!)SRM$F4Q1!enf`+{zi}&S~|2zR4NN{ ze!LJAAv6g1h9DS=xT^p9{%jdEKP&O2VblBHX?#rFk)!i~_w5h_HB%l45(Z099`rar z9Up>Jg0>~>v`lQSchJ&6xoq%zD$Plr0)d{y`O*3}Gbk-CBGFTxo?#KmnW+Dr_MURl zK+uf$tix578b0qF#}8DhZ%!VYBf%hy_8P@tuuo@KF5 z|4^9plsw}YMEAdZy%BA))U7SfZjA43EKPy2WU4H zS#^^57Z5*~_YD18DLi-Y`Tk#+emL>=zcBsXfY74I{}kpwaX(pz2rWVWH-@t5$kVdW zCkww2i&Ux7!*P$9E{~^K>#zvnv9eGYi?9$&s^wyq6=hQ7Db{G^EhG7(JYj;|Jd15p z@m@}GNs%gfp*d+t&QFwIuxhAp->0Erzb=J%mc~Yak6DJnz|gkvTNW9Jiy`m+Sq4RH*~kiU zib`-2>3Nc;%zz?z8Ic0n2eEY;9cxdgG`hYNYiJ3MgMdo~h(vj7T+(fDsbAn)?>1OECE7E-1;2EzPr$m0DNe=+~- zf1WYjfAPgmI!Obm{Rg)QQyV3a+n|v3eq{2!+x?7g_%C4y@+gLq|H4T@Nsxd^1w zm>3lC?KB+raVyaCot%qZMgNdyZ_E-Tno~-@Ct-VJ($)T7fI-ft$-j@aLEg(LoFTGx=K5PbXbKwxMgQ`^=0Np54!4@WapY~=kCpw!qCmbHR0{8C z8q++NfKvWp!Ab9^0G+a(%5}kO#?`8 zSA^$K>!*49TDj&(0i4<|$EH)TLhAeQlb!;&qi&!rqvdgMd}l_W$N<>E_!@F!KC{w@_siHdQb(t4b$bwJ zoc-Bcak7o#dS|R9IZIoA&dGW-RQuahonwW6rCHODx?dcX9hSdpT+indb*e3WypHX4 zn#Tw-UA5-VzK7Nc4_hUz(XAv3u|FFu$?}alRgJfjo4Zfw@uBVpIcJY~FqgHJMauV!I;P<vN7bvN+Nv{sS087#o7C+v{j^`>CnT*L%;IZH6x=!+O$8kCf1 zt)IG@S8f7RvP95G@Hp{e6j}#9+WRnz`ZV0b;pOm3`T7of>}-CP#oQ_wPBgN+qONh> za(VbPVzcP7smojVM_g0A1LhjryY>&y#7?5xj-y1$W(7yhnq)dtSOIjP~ zT7!NX6^pt3Wst7oo&AOpF@K;3Q8Jc;l5vw&spGldvlV&s%*&$Y}%DIb9U~@u)#(dH5 zy|wN7SP{s^r7gO;WZV%HweW)vN%@*KIGBAnY$`U`iG9C%Sen9zjepW&3-ob1B2UC+ z@iJ8Pr#WQN;*zV7Td`f;;yL0<{Q#$YAr{&QBe54(`Z?W8a^q0{kkJCoWY?dtQnTwu zVhM-sx3;)U257d$s*~pZ zI-p?eK#L8po_-RzY~f|jMiU9A31-j6s2!Ni_jeTK+D18m ze;#o;;ANbD*>ORLu3|NJ>`O;$h9BBTv?IZ`@$iX}y#p;Wnx$WLhoN@tK^rQH67+EH z%NP8O$X^3&wFA7&^8_?~o;x&-t(WyJJ!k_-A(pzPi&2#!y^CfoQN&W2j?r7(Ra^;C zaq%{~_JssIc$NdvQYI$N({n9L7G|pvJLDhA+V~~KJ&e9<;f##(IioB0+DyNrd)+J8|$!OEcPOPI~4nJpUl+U+q-TAUr)-secPJZ zNT@jFZ*@s)R9Ax(IvavvaLLL&rg^%3v;Dm=>RMO1V|Ca$bgi?#WxOU~6x#S>{E)#g z(XL_=kB5JRCy}?pLuU(8ugc##)a#q|ti$E)MS)mzk1vkZ0re`huV(eZEUSL=3Eq^i z$j#APY?u3h(6<3vmLJ*vpoeF?lpB0^MH~qhubSmanZkCQT6-R^->qM@P<{B@Agj~< zfJa>r^GIcKH;0?w8OFS9H}|PsGZDqP?~kEkuixqruT4%1wR&%1mo&ECM%_~^e|QaG zcsqS-ZGDmktaRlVrLTBaS2tCF`*!@A<{$mGub$aKs;DGPXw=x+Taw6?(gKs|IcV+q zyG_Tg7rfKpI7C*#S3Y0o%Mq_LitWz^(F`s%^Rs4wr{jWt;Vyo;5~X3O(cpe_l50Jh z%vKFA*Lmy4QTx!l0+^$x*jH&U0ip0^d#5-2NBPg56yd0V&5I&P*h5O+Lws9qMLhq) zXGvUSUf1XW6eMXGKG zE8ljAUOYVaN!_Pi2LZ<|(In)kF4OWl(am?eQQ`A5N+FXZ4&@3imf8U%K9g`+Z2}7C zq58+JGgY2%_+ExGd)pfcjlE00?rU#E1Z&9_qm|d)1TRgkUuBkoGhzv~iy4QCbFB|)x%2z@(LTFm-b1tlDerXoCGpz@G3&F~1 zRoB(g`_nRH)q1ty{xOHRB*(hsfX8*(S`E2_;8udq6ZK;?RZjJp&wvV>$e*l@yT^XE z>EjnY^ESk^@cF~(<6+W`GWg@phsx$dEt5JQpUmv=lfu(%!}g5cftO!eg5DR3H(G+b zWA!!)&3fiS%k@&#ye4b2D_AY}WFD4_X$KR9=;r!m^H>{~EBN=KDPAhL7eYVEX9LZ@ z3;d|8QhTn?LF{jmhRx(Rt4jOR=U2ZbkgsudIHok|W%}Fac6<)Ec77zjVlurg=laO6 z{=oiKmgF(Y? z7elyLo-bK|5%nQ^|EEUe)&NC8-<9|caB6*lxRpH2$sNUAue`ovyx05BAnAa4*Igof zp>{}bD*WJE90eW_=~7V#ULAq-1)wiKtwaw_OPm1}|iXq9>uF<1;DK z$vkv9C9j}>7LLqM_DEUVrqf7|49eS>pPhHLrs(vse-9Z1s(y_`+T{aDgXRp)07$uM zsjn1|B^Kek3bg$^QVdk~c{I%cLOI92p`ITb;w1oBq1OJN$`R*LL{#T_s64{c4PsaA z@sU&%S3z$&ksdPNSeK30Os43B(N5z?15}B4mMI_6a!ytssOG?jJELM`gmJRsydjEs zja)$Al@}{{__9kWf>iXdbQrM9F^xTZpd4WkWhc{twifRVPtDvYR8s|>waqY%`M7{4 zoqSRh%XEmfu~}cJL*%bY>p|i~=n`QsNxCad9z(jMK`Js8u4F;7yCFd&GE)&tdWa4=^Hpq2c=D3xFsB{2gg z7f(KTd_9?(JBdImiRi(WKxIOa@Re4wI5UW<|AJgLShXSHTht-}|7Ey4`;`>hU?roN zQw$rlECG;0I+?=KMmL}k9++R58BtiqhCJM=9N|`n)ZQ4XI6amc)5ZtP51i{HJ%-DA zU=;d%*jdQ`**C{8Gq{bzB?4!_W;aba!rO!Hig}7on;?bU9=hm|{($TpCM&;@4c7w) znEx{~v<=oJ!p5Xy%D2E(_$u4U|Lteb6x}i&V*G(Po#&|2?tvJM@B)kYI*#15hB}$0 zIiu7JiQHDa{LMyCh@$tEK|~uRFhA$}M}4rY{KQ%$4|7^c-&UeWutTgzDKHOQ+WtrO zu)>O32w5Il$voZgt`h5jkz><3`{-2U$doXy9D(6MtgYy;=$M2q-T^JALT?>! z!v%T|G8fRed^PxMh9hLcsd9wn)%nx8tVBu6d<*)ct8&Ees}EGjB=}E)f|~I`QWrs$=oZzn=>ne|Ct7)9sH4#I0JbhVOFI_6xS+ z9#5n^eFOY-OzAKYr^6Zf8NX_~8n{bBE7{tH%Y-Xd(z4Kk`skw^f#4x>)jvgd#&ffF z4o=&z+KM_XyhAGU23}MOM9tr0Kg2>MKqdEulS2ohQW}72&1eUGH&&Fce78V`gsVrU zM>#^R4n;QW5-(ve7weakNW$yDjtoht>f8`boS>0l_^vE%zxE?mLP=CfydZLsCJm}# z-|Z9~3f?4{FN~aHt2fSY{eZJfcA%;aaI#bYg5*nYR_5dn!j% z?27p;aOlsw3(@xDJVtaJ<5wU3Bmo*%cq1R{BNr`x*br?emUE2u#ykVsQ?}yBIc9m| zy28n+FlZQk^=a64=G_XqW(hxW7o_Mni7i>*b%03M4}Vgixy6g!7kcv4^e=2dT%a!s6>WV&??Ghlu^vb z(;(HBv1!(MBE+3BV?6tqQ271+8h<``rZwdVs#(;;ag0>sMKxu7u-VoV66;9YD0a+#_-1?wILZ;pX3#dK%>G(t)MIQZz1(JK`{@kR z?5Nul)>YzbDBJ5+>IG+Tjv`#v%I+a8q3OrU?*1+I>4eJefh`*8gest#&os9S@EIGg zscwaCg_Yals+L+q-HtJ{3xyo(?DpSR+zli@|HL3XLu{_Efj5k|NdGjjX0h6 ze*2XDdhA#BKB1n~P)~$DpKfooTWU0SsB5FSS+c6f{Q`#&Em-sP=Pkx-c-|Fn1br(K z(b3UdtjKZmEsSfAmN{>94{D%sng`mg6Rl*IcmI@c;0^X1!`Omh(j$znZ3leCc=Hy) zwO&J+zv_3MjZnfdPe7{&oE@h~&kO@u6=0vHv^uC|BrS9c=h~S{Cl5;_!ytbhrfAXb zTV>|~k=!NEHC)@gAVM`qr!+}GOM2R%vH_|i7e{PwX9mq?^u8=0@ey$~Iuf?Z3{8V$ z_TxL%)n`MVxWlZ|e~bFS0*$NjA5lK(1ZDiAM|I5NZ8J-i)QT8L*WvFu&G&h$v9DiAedtkhcoEp z4M!i2mHcnua+e89BqW>yJt^Z8Lk9jW9cg;XoiL!mpLuUIeYfK${!dJ_{VQfHbLXPE zdt=wiov=lKjgv@zh@ywCn;r=>@RunfzZXi{{=t2+xg1eCVJ<`!(vX(MTQm(pHJB_bm?)L{xSt^Wk5uuh_sL}aR9k)t zrWWFKeN{vG`~^C>SX_5+4g(NV9%O07cQt^vz>ocL4NGqNQ)STjFM6uqpboX@AJBb` ziod$lt%z+Zxw{w}(dFyOtGui*kGRnmgc|W(N%~xsK*vd`;-^PRMlrvdT^KDtImQYe!V)RF2k}YqxQrf8S%;qAxKy71LBuaBY$B!WO^dRXuxh# z-7?;k9po9b!_ia(USQlJWR~j(&dw3md}@a`FKgbixn*wq(vHgV`PRXo9}2fT*0A*n zidj8$3*(kgx4a`L>Bzc8i`ZXhP6bf;L=rN`21w*bxi7`MP;^zzIX}YnVP@zBZi@vY z1_5-JHWNp{4Zg5le`KPSV1hy8MsW~$YAo*lUMz45)|MU89wN3u77^(ari`40IQ*DS z&a1!y2_1LpU38`M-LiG{>xS29`z|l}Yp#p8r2X-W_4zi`b%FHzf@CP#9NY!Sz3Xi-WnmUOsF8zy1<}X0 zL+FtMNfG4^G8D)?#O+Ga#{gagAWIl}uD4he#2%tC5v>7QGuYmJ|BCyV=p*H&?0XAK`Ax=~fYz7l4fDN)Lu-KU3j?eUqw%}KzB~|pjCf#I z?x1*s0QD@jS2g6+lj8-Rl@^`eg^mptS4{`bv(SA*LEqK}xD6hUtvUxeN-kcHQn zm-O!~%HR%|-!C6ukY+p-3bm_3?#q+)E+!gI{N3e8xi0Lim!dj*XX{YciJ6}JC;R$E zA318lX?;I1LyzQy+vUEbwGe@%DkzY|15xDQ*0ee4bZXa4y10f2>}hR+_I0@9&&8U! zWqL6A3hP(b&4&O)o`6&4<}DCi+KhrU4Rl5X3C-Z9f!A)iHZE79ONXBzOba-WfzcBJ zes$AyV5SA1Ax5N1Ab^$!`njJ3?!ir$AGtscXVEk_96$C`#`g~7TpP!ANFdXMJ)~*m zwJ=Ef21~1xi3$dieu^$_d!x~=Wcoq^Z6|2_>c$_NMVIyk5kOKW>h3y#;PGqQ?4~cm-Yk^fqD}gU?7Pp_r$3K5+u3% z*Nr}sinh)_3M*H+>O+k*PK>@wu(`U&Z3sZ?EZ_GoYthyTORIK*^|X1t3p>j-$n&Oo z>kFPxZJyqr1+JuTC@RO_YS)+GfyY^EqOI#oh@ghGzf-$2v2vAr73|3E1bA-SVkzTuq;&hthw>6KHYyAWFED;CctxUJK`fJGp z*@NSW4PM`awv(4ROSwu}J>W9%DQPZy&VNqy$l_>zZjDX^yKp^CTTUqTDQoV$NI2P( z@is^GkH*nxS#{AH{q4qQmSRI!3k%zpTqBcSLc%J$ma4pxtx9AQWOr@=a#08-vNJL& z=Zjl!r`>~0FDZ|A`IZtvwgsSkixAm&<-)sHi9ofYse1V*z2TCP`w=Ym^GR>o>(I2>w26JfbXaXWOAqPM^TxKht5_x-ZF`&N z#wOpDyRs&w?{A5}P#gbfJsULrX};^U`}2I)&DF|vu6w=C^(^4YZ+G1C)1-K3f&c9E z#bF^)PiOvZV7aqxGwb;6-j&qgzSULfyTD6Gwf(orWZ&k!GNu%}wKms-`HiKgwaPLB zbo&vT=UZD;o1ZU)$lHVy<$Nz|JenCnPxtL!!F!n78ciEZ5q7|l@l64_`zu2syMsoe zg@N-&1(o}Ao#we6l4Y&fQ6;KAh1?ZSTc0%2cH|9|B&pYx4Q7W~7r-U7|k5a~W zHk}{x#thcAa?WwB9^PUs(EbqnMq_86hjV_9wY+rC$5-z(dx~y4kK|(Q5$KCFX8O)) z-M);p-SuakmVE+c`h*A!#a*<4-yZz!7N>R@AM12NrFNi| zxMl7%f7V0bBDWdRz4f#1<|=gC4)Y}p4Fj{wRUxtT+C%?DLrdSKQS!5K!8OW^x50iU z+S&w%F{WUOz((=1x#6#pWJi*V=~vHQ|NZK4-(L?A53)Pj2m8gD^=z;D2Qy=xZf#?f zn{QpMkUhSQd)5>i>~Ws=*)A1Q?Fq5d2!8=#}QuKqa6i(x6g zbAKCDbLS+rZZkYx=)=a{&8)=rX5;ehCgs*k_mGV0*ur*PAac>lAS_FPL}yif~*G$+WD z832kmJ zu20WO2=#*wNH;x@v*(IS2-zpi`YC(CF?#(=$OsAbN(^g|7}~717_!jP~yrhpzd1nt%?S2b*VeSfHRr3;2-(e@{V>Hxyp|7f!{g`R}>o-lhmUnRT_CyQ;!IH%KSioKciBHMZ)qSlLqIR^7%=>ub;0OyH0-?Yfmm zMni9fjBtFqpm*fUc~~Um9_^LcZHwmU&?1ciipprL;k714K&&Ot6Wl&D_HJr=BoPhW*U-}u6mDTT6IO2 z?;EZ&zO&%2)}z?|$`IwaVCfFEZ`rt*Wc!3e-le z4_iU8sShv{T$h89y7gLySm4Eae3q0!nC*g~xU`|8=_bEl*XsN96HWIvV$tfkZVA(0 zN314woa9D+YN!5ZA9*#uX8G9?(|}V_me>8cI&ss_j$Sv!&Js9Iy!}w=Q0b?3`Y)&h zQ(Rhodp^+JQ|VXb(rrgBT6uEo;oYj5$X0VCdELhGMU$wgPyTlv92hMrdS{z!b%E}q zDC^_IZ8|qe@*bqKZivl2NJJ&st4wx|h+Ot<>g1E->x_@mx*}lqol3!ufm#=a0;k-k zP;5e(ra$}R{X6^<)vB=|=rrh7naoPr+)h_0VHCv?_uQ2SLjPxforM|0Lbcekd4LQ)hZMe)4pKR-@u+(Ax(Nw_`fp3G)F-$jz!4=|AVwV7;a z6~c0cwqeA=-Q0Y7fZl9`6DJIZlpcB1?*5663p$;S_r?%?pSw;R_kg{IYJnw)2Z1Li z^tMIW?OOj^gEW+Wt>DfQo+eDRd2Y4ucUbcuv^jolu+Pv{X;5I3MU9xEyGdQtp61|p z@|wF&el_1ePY#i}-Wo1CUzo*bg{YBbGHdjbBYr?Pf&;k1!Jpz06~js1?{1;9DT^m_ zap%u>vt{QngR_e11%sz}Y^EL}e^?VgM2Bj__9vgry8J>!VcOihYZ{w`mRc@H=9faZ ztl5@ovaP()a!99u-PT>E%6DN`l1;+o{CPvsl&3>fLTkvoF3#N%JfC()U(=yXes>Xe zppSQn#qq|0E~a~^%g9g{Y%-?5suwloNLkI(#3-hPIweA}%Yxdy0qIkzZbtftk5xEv zh*{!iSht6@XsQ9lj8Ax>+R~nY{T+vt>r7EbyNw1N2umZm{I5nPi(9Hl;LrpDTfGf& z-)NP5RzyT1V3>Kdm;?|-WKL9iS=a?FH6i-va^#O+z2Q_rcO zi4-dVM<2<3A^LL5-LVFk{X*ArHJQgLG?AS5a>2l=p?Ok9Fex(83PPP5 zP|>B$DA)7YZyere=XMO0nZ?m41_QNH&FG(u(3sFC6nhc-w7{H3YtW1dz9}j&QF>{K z>9az5r`$no8{!Xm{Hib{43k~yWpac{M_fY4qrauagF2jw!s?B)>#n?M1%W*~?NnCc zXOUjK9Y(qjz}rg`*)=@?7cB_&!jmR!7<_O6t49k!S=Age^NHhd99EVogbf#lDudQm z`G6<3YpbNp3}D9zn<8si|NJ@LMg$v0TLC*x>;xOFh<*7c=*&e!%Q`{bd{@^}o8Y^YQE)q(-oh^b0G>bw zD`K$*FrPhRbZ(k06n(<7LdYVpfj!=EVIaD`a$G`FFE2 zA7^4a5Qo#1wfxmHB23!AekPX!Rc#2%0uZi>2jv-E|uN=e|lc`|rFmN-WZm25< zW#++g0{tmuhDSHSFWVwB9;zPN&GHt=YA@v}q)Fo-kxvK~jIDMku_qX2s*mNtdf^X` zWoD#=izt?is%m$}TUaLEV=BRm;T1V{9Bsf!}J|#?O*{4f+p}eKlLE6zLyX`k+h&;0d82QuZKgSuQA$ z-dcDvoFP+{)xI?L$-{c{mKg@c0K-|oEo#t)TFrQOwC#(h9GLXy~{}zstyxQHd;atFpgx zmP{YVp{z%3=FYJ-X0GMa$sJHMAh4*GAL27Eo}*F`hA16p#=T)1mNJxp5w8!_!QcxK z3qkJ`JzP@ZD@haEpkDy)T7WN$lcESqL1cse6G22kZ!OZozP=|k zoa3WQw!ETjQFuD|z51ZQARNh7!!(Q!AChavsN*ZK`n-ktk~6=<>oL+8sB)%jg>yBV z%VBiaiK(W4BKqFvV`v6U9F)VBmJ>HC+7n0V#zENa2?G7tq1%*@4F+0U%-@}Z>R;H*&?^e{RDyJ+iEM9}axK)i2g zZsNQ`78gJh0)%XzFQf4L2GwiTfW~J%#;MT5c#tiAypmT;4#K)`fJwpEY z`=0b1Wd&haIRb%D<=FZY@t;UMWz?ay3FcZIoi?nNUdg6g-(lt9L{u5RyvlVeAsm;f z>j7bt6EQJ9v>MP-ZG~Qf<&v!FRP^9LNNc7pV$GVO6=Fi{CS;)Hhp4D78sg1k?0kZ) znfn06YOQn>AQluotAfJ{VcTEYLxU17yjWgeNHc^ID zpVL>-O`^e1IfF~DP;;MlAb#C;PeT{#SD9UxfZLzfVj z3@bUz8Qw4)g^}$U1I?EGt$d9#)reB?*EaqBX+~-z;I;pRQ*@|jmYC~$TAxYoRw9I* zztrU8ub{a2uFy&=aT%pI;>MbhS7zfK(d>0P1H8-WaLP6bmx1z!;>stcJ%~^Af+FxG zMm|ZNm=^GXBiP_f&F)80xKJLN-BUH8$?U=waje9ZwXV?RFk*1(;(+ z;zWRRm9D{QWq&wCS^_wRB%9j8M5#s4cSeifR$kUNB^4V9suD$OFVGgNQ?w&?mkP0P z4AE#yk2vwhi^cM8licsp<@KQ*09m!DWi=JCe>2oU4)^lMU0aCA7fG=IgV5`S&{rMk zwv+BC%{0D8Nijx|)(M=#5P{DZVZt5keOrQ)F=;f!7@;Hsr^h2T&J7E)f{l$>?1j}- z4l4LH44&GZ8bojHwCO;H&*I(}C4aljZTk2N3vo)fHq0$lE)zqQfpawsDi92I0}yCH8< zUXkBD)m8b*>{3PTY2`s0O4Oqj_W*q$5;vCaHN_GXv>^v@*i?19%* zb5CR#TkKaC#w6q-Fdio(n3mWBX!VK8y~!WKOKtuX zLRZ)p67%F+Y}X&deF%F1WD&8%%Gb*aWXTzEr$+_T$~8;bTR_ZchpP~L)2ipxzo)MKDBs6i)x z_fO=zBSjPvqJqxJ-`DgltW9)zi_(SNhDw#{b~bA8>th_}65lZ@s{5E*N34#tl~R7# zl>oi1f}g9e`9&VX4H+``NA%t*L`kr^9`xO|tQ2YXSDALh^OM6fo!28yLM}%6dKyIu z4xEGZ6E^vA+MlBX59m3js7x6e6FO{R13RW6OWaCXqK@oM($PSCk(+4|1aa~)8jRz` zP5dWX+66;DzzmD5G9NV?tK3v*G7UyzaD>`0j6&4oZVj66)ZkSx*}7Qu$cYLmer+4Y z$FZ{rhk>JrwiyKJUe%rQPkeC9vbYLXUpZur6qXr@CB16OrU}Rj5!5|m#ghj08tQp9 zh4Cpfh`Wpd0=c$=+_q7{>Z881)-dY{3#9l|8Ma{ChNH!@trO7lcf zdB~t{gYq;oX~&LMfpRN#ThpaABuQCFSZsCP$we=Tsd{-w7;-t039;YB;Kcip5(&QUGI!tF3f=0{XMS|fN{Xhhp+QSKp zrbLeR16-5#`<`%x0sOF#+@{!!85->#ZouQm@Gm*+wicXv59LyqA#z`1z4U{jPK_47 zPp+|;^1rw1Zt$`RP?we+BD_q-ILBA`tSvBe+DTRiQ z`w#+sM&Gwdb|b!L!j{zq56nsnf+olUV>zX7+8ZTQt|r*6Z_T9(6-16VtDV85^<$w> z&NCc8l>97nfJa-stcsYj2N(%hETp7%s_S5HCr1&EI zgMN_ThYAGaJOaYoH_+fd%8{9rRMXF)xw*I~< zz&p;Wq+d!9O=wzIzqU%(kU6X&tP2gt&(7m9Ktx}{v|Id~-XA5Knri_NGM2Kv4@%}V z(jFu8<@cx%kZArYHc>rS4mI5<1~ud|ldW5L)0HU1=3h-ominn&VW~x+$W|QuW(j|{ zDzRJ*!q$#arTJ%n0950Y0?EFI4Ib(&2@ftf9ZQ%9oms%Q;S8HOhvBD%K)a zRMZw)Zz!MJeN&WsWVAyGV=V z2R|beXCko;o?mp!cw~5Ej$xNaWsxQ__1p<5+ zG){nxq#B&?F)4%P;NYC6h34P6(b+$GZgw4)`L71SMB4q5s}VMA`4Ve_sZsY~-*kxz zO}e1f8cRY>pPQv|TPt-dVUL`F`FI)2N;yD9lO@DZ@bKm^bHxsKFVsyXHyM@Ul;%~H zLfJ*4#deFsDUv^gn7JRVa1S_mpbpypb?bckFs#sx_lVWC8PbQ6f1^6`tk_%Q*m!^H zHiX3&3XB#ak#F#wKU>gR%BVL3>+++Qwm^dUspkbCLrF3<6{Qfb%yzLjmBQ1+X4!3N zm3E`v)#IKqnVFZsLk4?3^?@Iv!geZ3#-)K;+8h-kU7=8-*N;i3Q-N>8*A(;ybF|#t z?*4`#THiB0Z(wZnZ9s+70j|3u`v3yA?S?fYFHzfc4Bt+g@YP?KHs3mI!VVl;eWGP5KFL8cp~-l z`XKe|18@zyi+@Wkg?t@B)!WZAWg~H^?NBqxzYi_6z&<0BQcWm240H?#%&M%u@eVgO zD*uRXb7ZrNif*B17x6O3*kDmPsqirU4rwz>3o6XH;=Ii4Jr>Ox zL4=fkY9JqcbFB-S9VLJ&^Lr11OMc;XxUrNFDbIJ6uXLbCv50%RZN5Fy>@%vU=jh`7 z5mSJgT^x;$*)Z{x4*PQZjC958p%bndBMfQ*`KR4a(`B?RjC!rymhHskv6qg8Bg&QI zZZYVYS{((!QT^eYW2rNahx%U_6`^1wTTYWqX*3Ob>#D@^EO72u^mY6iFWI!_QP_=b z1<8LrX2;19e{1;?nok|pOfg9Bwo8kFlNUz%jI$36DH^Wt(28V*<3wPT#dXb8AH}4W5Ddoe}|sIxcjG9_rpVUw8QK8Kby=06MLlvmX|N(xO8r zxV+|@Y_%Z6bZHh5vjoF*cY1?rO?$6yG2SS2#;dt;J}E?s)NPU{Df4zdM1j9b9?+XUv9;yHT5Cw*-Z~)w4t?PTxW#KC_ljkC zDTw9J-%w41KfYk#zE#F~E^Q!0g}v48J_)%OL8TxLWO+Hkjf=hv4Y?>YKi;#eYTI1! z99VplSGF8Z;xR5Ee>~Q-%`b10LmV6|whBoES_`KKM z+4_+MZMaTKIjJ<|@w-lLWywGt*Ow06sM&sd(r_~HL4l}4YATORTnY6ZxF~Iv?1H@` zJZE!V$ii&#Qfs)Hc;alTQEE)?FWKcZeM$@jjF+g+Pdy>v+&$iDh@nDSJ5nN(rc+0R zdN7a@dg686;{6PGUy1`~GAJ?WeuLM_i7&(HJ_YiN@L8#Dl83~GAuz^=-A|*HMetd} zx&29HPTw2u6{`{mUD@;eU$V?H{VFQrevTRf1pxzRwPd;AYzKKWY{rV?P$)gWJF@@m|Mi;Cwv@J}wr7 zqKUhaX%^j?ZbD=OKd*4u*M#~{Av~{k72NGF zWS`ZRc2toL74%oG7&_t(w4sy#1I0i(zluTVa0&5Yuk`R2BqO_Hx-l5v9y8MHkJ-;^ zlODDuqfHUx@hE{Ixt(KAXnRTun9_s+YH*>xFyC@)nFIl_u|Y-Gz)5H;?;hIp;diXi zi0mGN5XVV+++P;Omng*5X~U@pY{)K@d&EP3l_maaFJ0}FW0t_2!9?$OD!X#!rFBAi z_>fomj5(zaC%!IMX@#sa(TRmi9iQ?>KX|9G@_LAh!m2c#1C5Jl0=h^^sxcfp3D~1v zRaYC+ufYCTW3pi#s#wmGqC!m{_Jp>lqyREX)E7bcmXz_bNt=bhg#k@YEL72eth1yK zTC*f2VOztba53$YLL1FeG%{;+>mo)~<)_;yt21LF`DRInWB=?0i>xdhm-`ZM7%~eGG9WE7Ou{pE` zP~aiY+OvlWTmKzuVZJ!aXP|U5nwBYW?6VEr1_xzAYjZFQ#~+f-^2Xc38`Bbli|uo* z5V~zOLXwt+wbs+nLWfbWoUd$v7{;P|K|Q{*41S=7Z#3_NtrH4|QUicYwS;e<-lT;_ z`A(26Xq90W?Qw0E(x1TZSlWI%DHP-7W0eZ_pBGM#5=RA(R{`c~`||j)BIza3%f5*& zCL%ukl%W)`<+!hdDl44286VlsU`rZMea#Wgv7w==C|%gzG4`@thBxNrQ7M5wJ1c*1 zn2x(B0YB`Q-+Y(m?`n@LeK?zFunY$hgCAUV3k4|56gGbpJ6KA_57TIpIK|w0XRU(B zJN}|7-4!dqMY8PP1*t(i>B04S9K}oA&rMpyen>Kd#1t%8-!?%uAaT8}yorM=J*gqA zqy|IjK|2Zy&rn4b1e#TAG;ksEC7Z3mZULr%`;M{_PzvQh?q(3u%n&^}dLQvik{Gvp ztfWFMa^(~J5fg0d4D%;ReSGX~F zo~Rd-Wad~7r0E#!NrF5wMZj3U4zR*!ZFgArcMh0O^X`Z#Uv32Y>dFaP3Dptv?#x1Y z1{@sHUyhI$L6lGOW3(hRNIF$2NwYMAR_99fA;YBvBz>K);+2xpsKXDJ>QJ=BDU`#c z_;vWY&IF)mTpfJ8&~BAaa0Ts3Q?C zIOyg$h@25L=H}mPCsDyhH0iuh*M)XZ&(a zvq*!$qo}dIkx=wZgo5{ihid?4bGcB+i%Dwe_51Er2^K~~$?J9Urbj*tAbm;?0o({_ z%V_n5TA1~Cw>)HJDel$*##SPvTWT$Fw0ABO4b~aMMd(u$8U=MG!NXDp1 zY|LO$nQ0@l_Ay-DHxAXEl2BfY4W*^D&o^2_X6(@~rHE?GZ;LoPh>obquZu`heb z!?rkye^P<8Zy3DhCLFMxtL{`6h>rB9kMlbxXfC7&_9XoM`Bm( zE`yP~;sGdl#c|Ub!g|o>Z$z(Ol2@|9)92xp`vNYS(nx^ulk~QK^FV&;B>uw(3T=M< zKw;P~w;zO2S0Zp@F3lA~b6qLk82UFxgg(rNfxKtWP7^S0fzi+Vn}GO-`%OsnJNX80 zeZ<}%&F2~o4!_21eCo@}2E(65w$d=4@ij>PXs_|}S9z`SoA2NnWc}A(>nm*I5z@LeWb?Ccv`Nx9i51 z_pvs#jOU|$8bK&CcdJO+iapw^5rneou}UEi`0ZM-p2KSJxIwoy7^BA8R(I$lK`Uix zS{-Y#2qtPGZFPk)gSOhW7DFVMDO+WojTK!P9M=skvLdxojaYOh)zLPitQ&J=rf9G+ z1H$^osKZB%`|O!*Tgb&Cuz^{9(?qehk(bppmKm4bVD&a!CNyTuRX?7ViM3ov#Folx zy%kJd>PPkR1j!~SN$e!1;z=Tj8YLtS5OXeWH)!I)x!u8$V?(Pm%{f|q57YTWefvw* zWBqum3rJEaCN6EP1GLK<)xji2!d<8}0P40xCM>?$v8o!|#w2E>HW>9SWJD&r>@hLM zDr03Tpl+b6Hi^l(xoP_5U40^N3ML{tuB zT)m*mcy&I{zQ`7Xw_|0}kA*vskcm$45`pR{?a9&^YUBrscA#xGR+dd25sp|}huZ3x zmi(|*q^q-~Jg|sBnFWXVEgTJ#q9QVcwUQ!=9WKQX^d&L|i|$DVJpO$zHiJV!8&zW* zw;z1})RAFOUDMt20sDTNslX>}BOUuqhVPIX+wxwFi38ktK4K)>;R6aWwwTaeP z>m(r|RP6ywfRxfLgjO^~kP#h~6++BzrBbNPxiWu>gi$3ezp`|+aX0gCQrM`N=_8lI z_E#);+P94h!vyQXFu}YqjM*1jn}ePE_77L2MP3-Oh70j9xq`}MZMhOADDZHs8S5)G zk@3f0EoAJ6I9XHW+n5uyW~!;6j3h$&&6kmKNX7$YrNBUlzQ~0D8W%+r8FXgK#s#4| zNe}Tmf+b!9LwOJuA4=*bk-9H^*ejs*4XNSPkpyJt)2~P5=@M0Fslh{ePCp}ec{X;u z7L*JIC;6=|-Ecfu4ir|-^ZJ0{Qa!FBU8RqsQp&4Tc}vk2qICypC8z~C7W-rN8`~%G z5UfI!Xy}HD2Bm#M&5*jX6TuNv;IGayJCLx{H)EbdA#Bzyo4ajGdkuy6fOL%;K5|Zt` zf|p-LEWI1UeP{zlKa;1h*{TFkTR8(O9DlS2rCYsGP~JnZH?AsAHmu;6NML%?z%GY4 zi)xTosTxKMXe=owZboAV8Dkn7OYN^d42)%zT7QFcH58dakxxQNl$Ic|AY}EJA1jn) zF4Cln5h1;mHa5)R0g@j^8kFXIs6Wb4i&ol5X7M^(A*RX}GIBGgPR^^lr&CX1-P)-S zv{Sbv#CWoHkV((RhU|DJD+6u-OktLr0#+3%5_>&W6!5TX00K6UbF1>>OZb@wRM`ea z*lfzdYw7r-K2-!gT#94t$!4TvDLyk6kAxWV z*rBr?V8uKS)q$mjL4qmcMUF4Qa)rP7p}g$y>IT)v6-_`3<#^$OfIKYU7zEr9%ApoN zkVDbvGD$Nozj<9ZVC(U0I?EuLhg(|#Z53|ghD#AInU+ZyQkPKGB)H5jLffY-6Fgj6 zE`(2zl6M~F7-^Ys9B0b}QGOC$;d~Gx>>%a1^>seWucQ`6(P-5lMjJtsIHI(!Ws5R9 z^g#gFF6qwzARLWPsIVS|KWQk5)wj#(yhCI0iVTXxDPRm$ z9YJ-r*N5@O*R+A}qYXR${IH(`7{of*a1{J%y@CCXfg+%@MbuR5FDl3-xMC2I4=xDx zi<5HJTLFFo$~|Fxunbg1Z=os%=^8tLRv0g!6c? zG$RqCz``P?1kx5!36&a+Ohjprd#vBbjI2eLpUQ!ClxF;(%rF;I-%nGr z1K-w*TwMd9gpp@)Tn?l7fZ9X&ID~K9A?<9>Hug$~?JFc@Pos0pF;-s7Hu!v$`E`=B z%Wacdce3`_DAGmRMHY+!}biJI!5ivM2P|A;>$)hxL)81T}&RDBb5@RH@Sb01$ z68JJ>vN)2E{FOU2Pi0FaoWqAl_*Vj0s$wuSpyVF)zdgX>bYcjZTPFMvf%-gseSpi3 z`Q&xEsy?lvIUm6|n}$ms!T8|?JeCWJr@+$EJSEPWdU<6oJOzI)3V-H#FXJmoyUuR zOvk5J{Wu<$`#6gCL3L(EY_D0C<&{4Ag6Rhu39`O0CH+&WWgyWqlWUl{S_ zG&C5(mRtQEq`ZQRKZ_x6AQOncK0p9;WUf{~t5i}y$sbb35Z<-PnMfG7LJp~I6fhVP zNIwUF(jv|~{=R&*w+1_M{45P^J&~vBZ;&C(Up|HHEcx&{4gF4Ze@}i!crzf&&fvm5 zL(Ax>dqQb_AfXe?G|TL=!dHEVgRCSZip~>IHj{q3dKgg?>l=qr>~JZDIKj>`KmClC zd}L$b&xxpyYa-~g!$uY=j3Jc18HJzG!JUA0cicTmS2zbtdGDk$qjn4IJ*0kc13h?U zL_&Dw6Gkcfe4>!QKq65(v5~-=^iSe<)T`*?)t=t+N#L{WN6*r#Hcw;@GQMA_)h7G1b<{4w?p88)p8QQ%vYFIca-ekd%<5YjriS& zZ`7ra!lN)NF9FPool;ET*&*T3g5?>oz%L)-B*8($Kl)mOVHC{rCzF+jw`as&&`Va3 zWGT_d=0~&C!P=vHT4ShI3Txj>vwxY!S}US>Uqi5Ovu?(Ymk$F^K|EjF!)utJRD!9vOpQ&1)m;hZ%ivM^mqD|t>9oDRY@|H68<2B^z$b& zBEi1$42F2gbT}9&R>;RL{=32xzzak{cp@)$z#G`DC<#~|!1qqEQ`|4a{s9ZyEh@qV zC?}>VPB-9c;LCw8g#VM^E5QF*u}O%nBbwHS_)|bV2^f{%CefnuSa%G<<0*@+QLIWM zT1xycay^u5G^`uqxa98^6YxfSHR&EER z>n0qm{!rEl$f-)LvSJ$z)VI+}DJ|1}*=X5(s%e$V#=a6j0pVt$CPUxw#;@X1&`2dJkCgug*t0odsO8^r-g#~1fP{2RnB z@DEty8%ES+2gKEy0;#XIQoK=!^_&xq{axT*PbJ=sd9NM;3J>bzLyF>>+aX4cMve>h z*bHUwrtk`-guYp#2p~5P(w~IdJF902#@tQ_vH2jisVm2=sxOYcaqzn^BD*1F=d#Vz zzM&kud0C2*oU_iF5lF$MQHrfEHgZWq>faNR4#yLwAR1t{yoE{d z5bAhhG*Y7@)aov!7RY(N(madTOO2>#IYZq_-1{)@PYgu(<3NP-j9Bw0tP35ZG-v3& zLqyBZb4c~F8R0E5XR&FNTDU}I<=*7>tID{-5v?a^HX>&bYp0;yuuZm$Nc9ApnUDtbOL__xWtOH|z)4Xpuj8>Be|Zoe$a3{9glr4mqMJ8OC< z^-xWMxxZB$)FaNWvWix-zJa=Rl~2gA5UqM8q4ECND*cvG2!ES62JbN#O?@|#t&l?{cf5n z_Qb;cZlxtgO^k!oq$9CuZNviX*m9%>IK7;8<5YqYO;Volur!s&pe!RWJa℞#z3( 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%0Dp)TMT&G3Z)$z}U8zjOVEiP(&5Bh5 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 D7oGKN diff --git a/packages/EXILED.2.2.4/images/Exiled_Icon.jpg b/packages/EXILED.2.2.4/images/Exiled_Icon.jpg deleted file mode 100644 index fd459756664a0c6c7b9bf9069db239290d5fd70a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27350 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 diff --git a/packages/EXILED.2.2.4/lib/net472/Exiled.API.xml b/packages/EXILED.2.2.4/lib/net472/Exiled.API.xml deleted file mode 100644 index 19d259f..0000000 --- a/packages/EXILED.2.2.4/lib/net472/Exiled.API.xml +++ /dev/null @@ -1,3669 +0,0 @@ - - - - Exiled.API - - - - - Ammo types present in the game. - - - - - 5.56mm Ammunition. - Used by . - - - - - 7.62mm Ammunition. - Used by and . - - - - - 9mm Ammunition. - Used by , and . - - - - - Players authentication types. - - - - - Indicates that the player has been authenticated through Steam. - - - - - Indicates that the player has been authenticated through Discord. - - - - - Indicates that the player has been authenticated as a Northwood staffer. - - - - - Indicates that the player has been authenticated as a Patreon. - - - - - Indicates that the player has been authenticated through an unknown provider. - - - - - Possible barrel weapon modifications. - - - - - No barrel - - - - - Suppressor. - - - - - Silencer. - - - - - Muzzle Brake. - - - - - Heavy Barrel. - - - - - Muzzle Booster. - - - - - Unique identifier for the different types of SCP-079 cameras. - - - - - Represents an unknown camera. - - - - - Represents the camera inside the Class-D spawns. - - - - - Represents the camera inside SCP-372's containment room. - - - - - Represents the camera outside of SCP-173's containment chamber. - - - - - Represents the camera outside the 173_ARMORY door. - - - - - Represents the camera inside of SCP-173's containment chamber. - - - - - Represents the camera above the 173_BOTTOM door. - - - - - Represents the camera outside of SCP-012's room - - - - - Represents the camera at the bottom of SCP-012's containment chamber. - - - - - Represents the LCZ cafe. - - - - - Represents the camera inside the LCZ armory. - - - - - Represents the plant room in LCZ. - - - - - Represents the camera inside the WC hallway. - - - - - Represents the camera inside SCP-914. - - - - - Represents the camera outside SCP-914. - - - - - Represents the camera in the LCZ A in LCZ. - - - - - Represents the camera in the LCZ B in LCZ. - - - - - Represents the camera facing the LCZ/HCZ A checkpoint, on the Light Containment side. - - - - - Represents the camera facing the LCZ/HCZ B checkpoint, on the Light Containment side. - - - - - Represents the camera facing the LCZ/HCZ A checkpoint, on the Heavy Containment side. - - - - - Represents the camera facing the LCZ/HCZ B checkpoint, on the Heavy Containment side. - - - - - Represents the camera in the LCZ A in HCZ. - - - - - Represents the camera in the LCZ B in heavy. - - - - - Represents the camera in the pre-hallway in front of 079_FIRST door. - - - - - Represents the camera in the hallway between the 079_FIRST and 079_SECOND doors. - - - - - Represents the camera inside SCP-079's containment chamber. - - - - - Represents the camera outside of SCP-096's containment chamber. - - - - - Represents the camera in the SCP-049 hallway that is in front of the elevator. - - - - - Represents the camera in the SCP-049 hallway that is not in front of the elevator. - - - - - Represents the camera that faces toward the 049_ARMORY door. - - - - - Represents the hallway camera in the server room. - - - - - Represents the camera on the bottom level of the server room. - - - - - Represents the camera on the top level of the server room. - - - - - Represents the camera in the HID hallway. - - - - - Represents the camera inside the Micro-HID room. - - - - - Represents the camera outside of the alpha warhead in HCZ. - - - - - Represents the camera inside of the alpha warhead room. - - - - - Represents the camera above the alpha warhead switch. - - - - - Represents the camera inside the alpha warhead armory. - - - - - Represents the camera inside SCP-939's containment room. - - - - - Represents the camera above the HCZ_ARMORY door. - - - - - Represents the SCP-106 MAIN CAM camera (above the door leading to SCP-106's room). - - - - - Represents the SCP-106 SECOND camera (outside the 106_PRIMARY door). - - - - - Represents the 106 ENT A camera (above the 106_PRIMARY door inside the containment room). - - - - - Represents the 106 ENT B camera (above the 106_SECONDARY door inside the containment room). - - - - - Represents the camera above the femur breaker. - - - - - Represents the camera facing toward the stairs in SCP-106's containment chamber. - - - - - Represents the camera facing toward the entrance zone checkpoint (in HCZ). - - - - - Represents the camera facing toward the heavy containment zone checkpoint (in EZ). - - - - - Represents the camera outside the INTERCOM door. - - - - - Represents the camera inside the INTERCOM door. - - - - - Represents the camera facing the intercom. - - - - - Represents the camera inside of Gate A (entrance zone). - - - - - Represents the camera inside of Gate B (entrance zone). - - - - - Represents the camera outside of the Gate A elevator (surface). - - - - - Represents the camera above the Gate A balcony. - - - - - Represents the camera on the tower at Gate A. - - - - - Represents the camera facing the NUKE_SURFACE door. - - - - - Represents the camera facing the SURFACE_GATE door (Gate B side) - - - - - Represents the camera on the Gate B walkway. - - - - - Represents the HELIPAD camera. - - - - - Represents the ESCAPE ZONE camera (facing toward the ESCAPE door). - - - - - Represents the EXIT camera (above the Class-D and Scientist extraction point). - - - - - Unique identifier for the different types of doors. - - - - - Represents an unknown door. - - - - - Represents the 012 door. - - - - - Represents the 012_BOTTOM door. - - - - - Represents the 012_LOCKER door. - - - - - Represents the 049_ARMORY door. - - - - - Represents the 079_FIRST door. - - - - - Represents the 079_SECOND door. - - - - - Represents the 096 door. - - - - - Represents the 106_BOTTOM door. - - - - - Represents the 106_PRIMARY door. - - - - - Represents the 106_SECONDARY door. - - - - - Represents the 173 gate. - - - - - Represents the door between the 173 gate and the 173 armory. - - - - - Represents the 173_ARMORY door. - - - - - Represents the 173_BOTTOM door. - - - - - Represents the GR18 door. - - - - - Represents the 914 door. - - - - - Represents the CHECKPOINT_ENT door. - - - - - Represents the CHECKPOINT_LCZ_A door. - - - - - Represents the CHECKPOINT_LCZ_B door. - - - - - RRepresents any entrance zone styled door. - - - - - Represents the ESCAPE_PRIMARY door. - - - - - Represents the ESCAPE_SECONDARY door. - - - - - Represents the SERVERS_BOTTOM door. - - - - - Represents the GATE_A door. - - - - - Represents the GATE_B door. - - - - - Represents the HCZ_ARMORY door. - - - - - Represents any heavy containment styled door. - - - - - Represents the HID door. - - - - - Represents the HID_LEFT door. - - - - - Represents the HID_RIGHT door. - - - - - Represents the INTERCOM door. - - - - - Represents the LCZ_ARMORY door. - - - - - Represents the LCZ_CAFE door. - - - - - Represents the LCZ_WC door. - - - - - Represents any light containment styled door. - - - - - Represents the NUKE_ARMORY door. - - - - - Represents the NUKE_SURFACE door. - - - - - Represents any of the Class-D cell doors. - - - - - Represents the SURFACE_GATE door. - - - - - Represents the 372 door. - - - - - Represents the Airlocks door. - - - - - Represents the ContDoor door. - - - - - Represents the ESCAPE door. - - - - - Represents the ESCAPE_INNER door. - - - - - Represents the 173 door. - - - - - Status effects as enum. - - - - - The player isn't able to open their inventory or reload a weapon. - - - - - Drains the player's stamina and then health. - - - - - Damages the player over time. - - - - - Blurs the player's screen. - - - - - Increases damage the player gets. - - - - - Blurs the player's screen when rotating. - - - - - Teleports the player to the pocket dimension and drains health. - - - - - Deafens the player. - - - - - Removes 10% of the player's health per second. - - - - - Slows down the player's movement. - - - - - Stops the player's movement. - - - - - Halves the player's maximum stamina and stamina regeneration rate. - - - - - Flashes the player. - - - - - Drains the player's health while sprinting. - - - - - Reduces the player's FOV, gives infinite stamina and gives the effect of underwater sound. - - - - - Increases the player's stamina consumption. - - - - - Damages the player every 5 seconds, starting low and ramping hight. - - - - - Makes the player faster but also drains health. - - - - - Makes the player invisibility. - - - - - Slows down the player's movement with SCP-106 effect. - - - - - Gives the player the sound vision of SCP-939. - - - - - The unique type of elevator. - - - - - Unknown elevator Type. - - - - - Entrance Gate A elevator. - - - - - Entrance Gate B elevator. - - - - - Heavy Containment Zone Nuke elevator. - - - - - Heavy Containment Zone SCP-049 elevator. - - - - - Light Containment Zone checkpoint A elevator. - - - - - Light Containment Zone checkpoint B elevator. - - - - - A set of environment types. - - - - - The development environment, for developers. - - - - - The testing environment, for testing things. - - - - - The production environment, for the public. - - - - - The ptb environment, for Public Test Builds. - - - - - The unique type of grenade. - - - - - Frag grenade. - Used by . - - - - - Flashbang. - Used by . - - - - - Scp018 ball. - Used by . - - - - - The team that is currently leading the round. - - - - - Represents Scientists, Guards, and NTF. - - - - - Represents Class-D and Chaos Insurgency. - - - - - Represents SCPs. - - - - - Represents a draw. - - - - - Possible other weapon modifications. - - - - - No other - - - - - Flashlight. - - - - - Gyroscopic Stabilizer. - - - - - Ammo Counter. - - - - - Laser. - - - - - Provides simple and readable plugin priority values. - - - - - - - - Execute the plugin last, after other ones. - - - - - - - - Default plugin priority. - - - - - Low plugin priority. - - - - - Medium plugin priority. - - - - - Higher plugin priority. - - - - - Higher plugin priority. - - - - - - - - Execute the plugin first, before other ones. - - - - - Layers game respawn effects. - - - - - Plays the music to alive and . - - - - - Summons the van. - - - - - Summons the NTF chopper. - - - - - Unique identifier for the different types of rooms. - - - - - Unknown Room Type. - - - - - Lower Containment Armory. - - - - - Lower Containment L-Shaped Room. - - - - - Lower Containment |-Shaped Room. - - - - - Lower Containment SCP 012 Room. - - - - - Lower Containment SCP 914 Room. - - - - - Lower Containment X-Shaped Room. - - - - - Lower Containment T-Shaped Room. - - - - - Lower Containment Cafe Room. - - - - - Lower Containment T-Shaped Plants Room. - - - - - Lower Containment Toilets Room. - - - - - Lower Containment Airlock Room. - - - - - Lower Containment SCP 173 Room. - - - - - Lower Containment Class D Spawn Room. - - - - - Lower Containment Checkpoint B Room. - - - - - Lower Containment Glass Box Room. - - - - - Lower Containment Checkpoint A Room. - - - - - Heavy Containment SCP 079 Room. - - - - - Heavy Containment Entrance Checkpoint Room. - - - - - Heavy Containment T-Shaped Armory Room. - - - - - Heavy Containment SCP 939 Room. - - - - - Heavy Containment HID-Spawn Room. - - - - - Heavy Containment SCP 049 Room. - - - - - Heavy Containment Checkpoint A Room. - - - - - Heavy Containment X-Shaped Room. - - - - - Heavy Containment SCP 106 Room. - - - - - Heavy Containment Nuke Room. - - - - - Heavy Containment Tesla Room. - - - - - Heavy Containment Servers Room. - - - - - Heavy Containment Checkpoint B Room. - - - - - Heavy Containment T-Shaped Room. - - - - - Heavy Containment L-Shaped Room. - - - - - Heavy Containment SCP 096 Room. - - - - - Entrance Red Vent Room. - - - - - Entrance Intercom Room. - - - - - Entrance Gate A Room. - - - - - Entrance PC Room With Downstairs. - - - - - Entrance L-Shaped Room. - - - - - Entrance PC Room. - - - - - Entrance X-Shaped Room. - - - - - Entrance Red Collapsed Tunnel Room. - - - - - Entrance |-Shaped Dr.L Room. - - - - - Entrance |-Shaped Room - - - - - Entrance Cafeteria Room. - - - - - Entrance PC Room With Upstairs. - - - - - Entrance Gate B Room. - - - - - Entrance Shelter Room. - - - - - Pocket Dimension. - - - - - The Surface. - - - - - In which side a certain belongs. - - - - - The same as . - - - - - Mobile Task Forces team. - Contains , , , , - and . - - - - - Chaos Insurgency team. - Contains and . - - - - - . - - - - - . - - - - - Possible sight weapon modifications. - - - - - No sight - - - - - Collimator - - - - - Holo Sight - - - - - Blue Dot Sight - - - - - Red Dot - - - - - Night Vision Sight - - - - - Sniper Scope - - - - - Facility zone types. - - - - - The Surface Zone. - - - - - The Entrance Zone. - - - - - The Heavy Containment Zone. - - - - - The Light Containment Zone. - - - - - An unknown zone. - - - - - Contains an extension method to get from , as well as additional methods to get the and of a camera. - Internal class to cache the and on level load. - - - - - Returns the the camera is in, or null if not found. - - The to check. - A , or null if not found. - - - - Gets the . - - The to check. - The . - - - - Returns the the camera is in. - - The to check. - The of the zone the camera is in. - - - - Gets all the and values for for the instances using and name. - - - - - Contains extensions related to . - - - - - Gets the . - - The Door to check. - The . - - - - Breaks the specified door, if it is not already broken. - - The to break. - True if the door was broken, false if it was unable to be broken, or was already broken before. - - - - Indicates when the door can be broken. - - The . - true if the door can be broken; otherwise, false. - - - - Gets a nametag of a door. - - The . - A nametag of the door or null. - - - - Gets all the values for the instances using and name. - - - - - Contains an extension method to get from . - - - - - Gets an instance of points to an effect. - - The enum. - The . - - - - A set of extensions for . - - - - - Spawns a in a desired position. - - The type of the item to be spawned. - The durability (or ammo, depends on the weapon) of the item. - Where the item will be spawned. - The rotation. We recommend you to use . - The sight the weapon will have (0 is nothing, 1 is the first sight available in the weapon manager, and so on). - The barrel of the weapon (0 is no custom barrel, 1 is the first barrel available, and so on). - Other attachments like flashlight, laser or ammo counter. - Returns the spawned . - - - - Spawns an in a desired position. - - The item to be spawned. - Where the item will be spawned. - The rotation. We recommend you to use . - Returns the spawned . - - - - Set the ammo of an item. - - The list of items. - The weapon to be changed. - The ammo amount. - - - - Set the ammo value of an . - - The player instance. - The weapon to be changed. - The ammo amount. - - - - Get the ammo of an . - - The weapon to be get. - Returns the weapon left ammo. - - - - Check if an item is an ammo. - - The item to be checked. - Returns whether the is an ammo or not. - - - - Check if an item is a weapon. - - The item to be checked. - Indicates whether the MicroHID item should be taken into account or not. - Returns whether the is a weapon or not. - - - - Check if an item is an SCP-330. - - The item to be checked. - Returns whether the is an SCP or not. - - - - Check if an item is an SCP. - - The item to be checked. - Returns whether the is an SCP or not. - - - - Check if an item is an SCP. - - The item to be checked. - Returns whether the is an SCP or not. - - - - Check if an item is a throwable item. - - The item to be checked. - Returns whether the is a throwable item or not. - - - - Check if an item is a medical item. - - The item to be checked. - Returns whether the is a medical item or not. - - - - Check if an item is a utility item. - - The item to be checked. - Returns whether the is an utilty item or not. - - - - Check if an item is a keycard. - - The item to be checked. - Returns whether the is a keycard or not. - - - - Gets sight modification of the weapon. - - The player instance. - The weapon with attachment. - Returns . - - - - Sets sight modification of the weapon. - - The player instance. - The weapon with attachment. - Type of the sight. - - - - Gets barrel modification of the weapon. - - The player instance. - The weapon with attachment. - Returns . - - - - Sets barrel modification of the weapon. - - The player instance. - The weapon with attachment. - Type of the barrel. - - - - Gets other modification of the weapon. - - The player instance. - The weapon with attachment. - Returns . - - - - Sets other modification of the weapon. - - The player instance. - The weapon with attachment. - Type of the other. - - - - Contains an extension method to get from . - Internal class to cache the on level load. - - - - - Gets the . - - The to check. - The . - - - - Gets all the values for the instances using and name. - - - - - A set of extensions for . - - - - - Finds and returns the the ragdoll is located in. - - The to check the room of. - The the ragdoll is located in. - - - - Returns the of the ragdoll. - - The to check the role of. - The of the ragdoll. - - - - Returns the owner , or null if the ragdoll does not have an owner. - - The to get the owner of. - The owner of the ragdoll, or null if the ragdoll does not have an owner. - - - - Returns the killing , or null if the killer is not a player. - - The to get the killer of. - The killing , or null if the killer is not a player. - - - - A set of extensions for . - - - - - Invoke a static method. - - The method type. - The method name. - The method parameters. - - - - Copy all properties from the source class to the target one. - - The target object. - The source object to copy properties from. - - - - A set of extensions for . - - - - - Get a role's . - - The to get the color of. - The of the role. - - - - Get a role's . - - The to check the side of. - . - - - - Get a team's . - - The to get the of. - .. - - - - Get the of the given . - - Role. - . - - - - A set of extensions for . - - - - - Compute the distance between two . - - The first string to be compared. - The second string to be compared. - Returns the distance between the two strings. - - - - Extract command name and arguments from a . - - The to extract from. - Returns a containing the exctracted command name and arguments. - - - - Converts a to snake_case convention. - - The string to be converted. - Indicates whether special chars has to be replaced or not. - Returns the new snake_case string. - - - - Converts an into a string. - - The type of the IEnumerable. - The instance. - Indicates whether the enumerator index should be shown or not. - Returns the converted . - - - - Removes the prefab-generated brackets (#) on names. - - Name of the . - Name without brackets. - - - - Retrieves a string before a symbol from an input. - - The input. - The symbol. - Substring before the symbol. - - - - Splits camel case string to space-separated words. Ex: SomeCamelCase -> Some Camel Case. - - Camel case string. - Splitted string. - - - - Removes all space symbols from string. - - Input string. - String without spaces. - - - - Gets the player's user id without the authentication. - - The user id. - Returns the raw user id. - - - - Represents the in-game badge. - - - - - Initializes a new instance of the struct. - - The badge text. - The badge color. - The badge type. - Indicates whether the badge is global or not. - - - - Gets the badge text. - - - - - Gets the badge color. - - - - - Gets the badge type. - - - - - Gets a value indicating whether the badge is global or not. - - - - - Useful class to save broadcast configs in a cleaner way. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets or sets the broadcast content. - - - - - Gets or sets the broadcast duration. - - - - - Gets or sets the broadcast type. - - - - - Gets or sets a value indicating whether the broadcast should be shown or not. - - - - - A set of tools to use in-game C.A.S.S.I.E more easily. - - - - - Gets a value indicating whether or not C.A.S.S.I.E is currently announcing. Does not include decontamination messages. - - - - - Reproduce a non-glitched C.A.S.S.I.E message. - - The message to be reproduced. - Indicates whether C.A.S.S.I.E has to hold the message. - Indicates whether C.A.S.S.I.E has to make noises or not during the message. - - - - Reproduce a glitchy C.A.S.S.I.E announcement. - - The message to be reproduced. - The chance of placing a glitch between each word. - The chance of jamming each word. - - - - Reproduce a non-glitched C.A.S.S.I.E message after a certain amount of seconds. - - The message to be reproduced. - The seconds that have to pass before reproducing the message. - Indicates whether C.A.S.S.I.E has to hold the message. - Indicates whether C.A.S.S.I.E has to make noises or not during the message. - - - - Reproduce a glitchy C.A.S.S.I.E announcement after a certain period of seconds. - - The message to be reproduced. - The seconds that have to pass before reproducing the message. - The chance of placing a glitch between each word. - The chance of jamming each word. - - - - Calculates duration of a C.A.S.S.I.E message. - - The message, which duration will be calculated. - Determines if a number won't be converted to its full pronunciation. - Duration (in seconds) of specified message. - - - - A set of tools to print messages on the server console. - - - - - Sends a level messages to the game console. - - The message to be sent. - - - - Sends a level messages to the game console. - Server must have exiled_debug config enabled. - - The message to be sent. - Indicates whether the log can be sent or not. - - - - Sends a level messages to the game console. - - The message to be sent. - - - - Sends a level messages to the game console. - This should be used to send errors only. - It's recommended to send any messages in the catch block of a try/catch as errors with the exception string. - - The message to be sent. - - - - Sends a log message to the game console. - - The message to be sent. - The message level of importance. - The message color. - - - - Sends a raw log message to the game console. - - The message to be sent. - The of the message. - - - - A set of tools to easily handle the in-game map. - - - - - Gets a value indicating whether decontamination has begun in the light containment zone. - - - - - Gets the number of activated generators. - - - - - Gets all objects. - - - - - Gets all objects. - - - - - Gets all objects. - - - - - Gets all objects. - - - - - Gets all objects. - - - - - Gets the Default , - used in - and . - - - This value can be modified to change the default Ragdoll's info. - - - - - Gets the current state of the intercom. - - - - - Gets a value indicating whether or not the intercom is currently being used. - - - - - Gets the that is using the intercom. Will be null if is false. - - - - - Tries to find the room that a is inside, first using the 's parents, then using a Raycast if no room was found. - - The inside the room. - The that the is located inside. - - - - Spawns a ragdoll for a player on a certain position. - - Victim, represented as a player. - The message to be displayed as his death. - Where the ragdoll will be spawned. - The rotation for the ragdoll. - The initial velocity the ragdoll will have, as if it was exploded. - Sets this ragdoll as respawnable by SCP-049. - The Ragdoll component (requires Assembly-CSharp to be referenced). - - - - Spawns a ragdoll on the map based on the different arguments. - - - Tip: You can do ': true, : MyPlayer.Id' to skip parameters. - - - - // Code to spawn a fake ragdoll - if (ev.Player == MyPlugin.TheInmortalPlayer) - { - var fakeRagdoll = Map.SpawnRagdoll(RoleType.ClassD, DamageTypes.Fall, "The Falling Guy", new Vector3(1234f, -1f, 4321f)); - } - - - The to use as ragdoll. - The death cause, expressed as a . - The name from the victim, who the corpse belongs to. - Where the ragdoll will be spawned. - The rotation for the ragdoll. - The initial velocity the ragdoll will have, as if it was exploded. - Sets this ragdoll as respawnable by SCP-049. Must have a valid . - Used for recall. The to be recalled. - Can be ignored. The 's PlayerId field. - The Ragdoll component (requires Assembly-CSharp to be referenced). - - - - Spawns a ragdoll on the map based on the different arguments. - - - Tip: You can do, for example, ': "Vector3.up * 3"' to skip parameters. - - - - // Code to spawn a fake ragdoll - if (ev.Player == MyPlugin.TheInmortalPlayer) - { - var fakeRagdoll = Map.SpawnRagdoll(ev.Player.Role, ev.Player.Position, victimNick: ev.Player.DisplayNickname, playerId: ev.Player.Id); - } - - - The to use as ragdoll. - The name from the victim, who the corpse belongs to. - The that displays who killed this ragdoll, and using which tool. - Where the ragdoll will be spawned. - The rotation for the ragdoll. - The initial velocity the ragdoll will have, as if it was exploded. - Sets this ragdoll as respawnable by SCP-049. - Used for recall. The to be recalled. - Can be ignored. The 's PlayerId field, likely used in the client. - The Ragdoll component (requires Assembly-CSharp to be referenced). - - - - Optimized method to Spawn a ragdoll on the map. - Will only allocate the newly created GameObject, requires extra work and pre-loaded base game roles. - - - - - - EXILED already has an internal, default Ragdoll.Info: the use of this - method to try to optimize a plugin is absolutely optional. - - We recommend using: Map.SpawnRagdoll(RoleType roleType, string victimNick, Vector3 position) - - - This method should only ever be used if you're dealing with massive - server-sided lag. - - - Ragdoll.Info's "ownerID" isn't the SteamID, but the - 's PlayerId field. - - - - Main game's thad defines the role to spawn a ragdoll. - object containing the ragdoll's info. - Where the ragdoll will be spawned. - The rotation for the ragdoll. - The initial velocity the ragdoll will have, as if it was exploded. - Sets this ragdoll as respawnable by SCP-049. - The component created. - - - - Spawns hands at the specified position with specified rotation. - - Hands position. - Hands rotation. - - - - Broadcasts a message to all players. - - The duration in seconds. - The message that will be broadcast (supports Unity Rich Text formatting). - The broadcast type. - - - - Shows a hint to all players. - - The message that will be broadcasted (supports Unity Rich Text formatting). - The duration in seconds. - - - - Clears all players' broadcasts. - - - - - Gets a random spawn point of a . - - The to get the spawn point from. - Returns the spawn point . - - - - Starts the light containment zone decontamination process. - - - - - Turns off all lights of the facility. - - The duration of the blackout. - Indicates whether or not only lights in the heavy containment zone will be turned off. - - - - Gets the camera with the given ID. - - The camera id to be searched for. - The with the given ID. - - - - Gets the camera with the given camera type. - - The to search for. - The with the given camera type. - - - - Gets the door with the given door name. - - The door name. - The or null if a door with this name doesn't exist. - - - - Changes the color of a MTF unit. - - The index of the unit color you want to change. - The new color of the Unit. - - - - Clears the lazy loading game object cache. - - - - - A set of useful paths. - - - - - Gets AppData path. - - - - - Gets managed assemblies directory path. - - - - - Gets or sets exiled directory path. - - - - - Gets or sets plugins path. - - - - - Gets or sets Dependencies directory path. - - - - - Gets or sets configs path. - - - - - Gets or sets configs path. - - - - - Gets or sets logs path. - - - - - Reloads all paths. - - The new root directory name. - - - - Represents the in-game player, by encapsulating a . - - - - - Initializes a new instance of the class. - - The of the player to be encapsulated. - - - - Initializes a new instance of the class. - - The of the player. - - - - Finalizes an instance of the class. - - - - - Gets a containing all on the server. - - - - - Gets a list of all 's on the server. - - - - - Gets a containing cached and their user ids. - - - - - Gets a containing cached and their ids. - - - - - Gets the encapsulated . - - - - - Gets the encapsulated . - - - - - Gets the player's ammo. - - - - - Gets the HintDisplay of the player. - - - - - Gets the player's inventory. - - - - - Gets the encapsulated 's PlayerCamera. - - - - - Gets the encapsulated 's PlayerCamera. - - - - - Gets the player's grenade manager. - - - - - Gets or sets the player's id. - - - - - Gets the player's user id. - - - - - Gets or sets the player's custom user id. - - - - - Gets the player's user id without the authentication. - - - - - Gets the player's authentication token. - - - - - - - - Gets a value indicating whether or not the player is verified. - - - This is always false if online_mode is set to false. - - - - - Gets or sets the player's display nickname. - May be null. - - - - - Gets the player's nickname. - - - - - Gets or sets the player's player info area bitmask. - You can hide player info elements with this. - - - - - Gets or sets the player's player info area bitmask. - You can hide player info elements with this. - - - - - Gets or sets the player's custom player info string. - - - - - Gets or sets the player's custom player info string. - - - - - Gets the dictionary of player's session variables. It is not being saved on player disconnect. - - - - - Gets or sets a value indicating whether or not the player is invisible. - - - - - Gets a value indicating whether or not the player can be tracked. - - - - - Gets a value indicating whether or not the player is connected to the server. - - - - - Gets a list of player ids who can't see the player. - - - - - Gets a list of player ids who can't see the player. - - - - - Gets a value indicating whether or not the player has Remote Admin access. - - - - - Gets or sets a value indicating whether or not the player's overwatch is enabled. - - - - - Gets or sets a value indicating the cuffer id. - - - - - Gets or sets the player's position. - - - - - Gets or sets the player's rotations. - - Returns a representing the rotation of the player. - - - - Gets or sets the player's rotation. - - Returns the direction he's looking at, useful for Raycasts. - - - - Gets the player's . - - - - - Gets or sets the player's . - - - - - Gets the of the player's role. - - - - - Gets a value indicating whether or not the palyer is cuffed. - - - - - Gets a value indicating whether or not the player is reloading a weapon. - - - - - Gets a value indicating whether or not the player is zooming with a weapon. - - - - - Gets the player's current . - - - - - Gets a value indicating whether or not the player is jumping. - - - - - Gets the player's IP address. - - - - - Gets or sets a value indicating whether or not the has No-clip enabled. - - indicating status. - - - - Gets the player's command sender instance. - - - - - Gets the player's command sender instance. - - - - - Gets player's . - - - - - Gets a value indicating whether or not the player is the host. - - - - - Gets a value indicating whether or not the player is alive. - - - - - Gets a value indicating whether or not the player is dead. - - - - - Gets a value indicating whether or not the player's is any NTF rank. - Equivalent to checking the player's . - - - - - Gets a value indicating whether or not the player's is any SCP rank. - - - - - Gets a value indicating whether or not the player's is any human rank (except the tutorial role). - - - - - Gets or sets the camera SCP-079 is currently controlling. - Only applies if the player is SCP-079. - - - - - Gets the player's they're currently in. - - - - - Gets or sets a value indicating whether the player friendly fire is enabled or not. - This only isAllowed to deal friendly fire damage, not take friendly fire damage. - - - - - Gets or sets the player's scale. - - - - - Gets or sets a value indicating whether the player's bypass mode is enabled or not. - - - - - Gets or sets a value indicating whether or not the player is muted. - - - - - Gets or sets a value indicating whether or not the player is intercom muted. - - - - - Gets or sets a value indicating whether or not the player has godmode enabled. - - - - - Gets or sets the player's health. - If the health is greater than the , the MaxHealth will also be changed to match the health. - - - - - Gets or sets the player's maximum health. - - - - - Gets or sets the player's adrenaline health. - If the health is greater than the , the MaxAdrenalineHealth will also be changed to match the adrenaline health. - - - - - Gets or sets the player's maximum adrenaline health. - - - - - Gets or sets the player's current SCP. - - - - - Gets or sets the item in the player's hand, returns the default value if empty. - - - - - Gets the index of the current item in hand. - - - - - Gets or sets the abilities of SCP-079. Can be null. - Only applies if the player is SCP-079. - - - - - Gets or sets the levels of SCP-079. Can be null. - Only applies if the player is SCP-079. - - - - - Gets or sets the speaker this player is currently using. Can be null. - Only applies if the player is SCP-079. - - - - - Gets or sets the doors this player has locked. Can be null. - Only applies if the player is SCP-079. - - - - - Gets or sets the amount of experience this player has. - Only applies if the player is SCP-079. - - - - - Gets the class. - - - - - Gets or sets this player's level. - Only applies if the player is SCP-079. - - - - - Gets or sets this player's max energy. - Only applies if the player is SCP-079. - - - - - Gets or sets this player's energy. - Only applies if the player is SCP-079. - - - - - Gets a value indicating whether the staff bypass is enabled or not. - - - - - Gets or sets the player's group name. - - - - - Gets the current room the player is in. - - - - - Gets or sets the player's group. - - - - - Gets or sets the player's rank color. - - - - - Gets or sets the player's rank name. - - - - - Gets the global badge of the player, can be null if none. - - - - - Gets or sets a value indicating whether or not the player's badge is hidden. - - - - - Gets a value indicating whether or not the player is in the pocket dimension. - - - - - Gets or sets a value indicating whether player should use stamina system. - - - - - Gets or sets a player's SCP-330 usages counter. - - - - - Gets a value indicating whether player has hands. - - - - - Gets a dictionary for storing player objects of connected but not yet verified players. - - - - - Gets a filtered by team. - - The players' team. - Returns the filtered . - - - - Gets a filtered by role. - - The players' role. - Returns the filtered . - - - - Gets the belonging to the CommandSender, if any. - - The command sender. - Returns a player or null if not found. - - - - Gets the Player belonging to the ReferenceHub, if any. - - The player's . - Returns a player or null if not found. - - - - Gets the Player belonging to the GameObject, if any. - - The player's . - Returns a player or null if not found. - - - - Gets the player belonging to the player with the specified id. - - The player id. - Returns the player found or null if not found. - - - - Gets the player by his identifier. - - The player's nickname, steamID64 or Discord ID. - Returns the player found or null if not found. - - - - - - - Sets the camera the player is currently located at. - Only applies if the player is SCP-079. - - Camera ID. - - - - Sets the camera the player is currently located at. - Only applies if the player is SCP-079. - - The object to switch to. - - - - Sets the player's rank. - - The rank name to be set. - The group to be set. - - - - Handcuff the player. - - The cuffer player. - - - - Sets the player's . - - The new to be set. - Indicates whether it should preserve the position and inventory after changing the role or not. - Indicates whether the player is escaped or not. - - - - Broadcasts the given to the player. - - The to be broadcasted. - - - - Drops an item from the player's inventory. - - The item to be dropped. - - - - Indicates whether or not the player has an item. - - The item to search for. - true, if the player has it; otherwise, false. - - - - Removes an item from the player's inventory. - - The item to be removed. - - - - Removes the held item from the player's inventory. - - - - - Sends a console message to the player's console. - - The message to be sent. - The message color. - - - - Sends a console message to a . - - The message target. - The message to be sent. - The message color. - - - - Disconnects a player. - - The disconnection reason. - - - - Hurts the player. - - The damage to be inflicted. - The damage type. - The attacker name. - The attacker player id. - - - - Hurts the player. - - The damage to be inflicted. - The attacker. - The damage type. - - - - Kills the player. - - The that will kill the player. - - - - Bans the player. - - The ban duration. - The ban reason. - The ban issuer nickname. - - - - Kicks the player. - - The kick reason. - The kick issuer nickname. - - - - Blink the player's tag. - - Used to wait. - - - - Sends a message to the player's Remote Admin console. - - The message to be sent. - Indicates whether the message should be highlighted as success or not. - The plugin name. - - - - A simple broadcast to a . Doesn't get logged to the console and can be monospaced. - - The broadcast duration. - The message to be broadcasted. - The broadcast type. - - - - Clears the player's brodcast. Doesn't get logged to the console. - - - - - Add an item of the specified type with default durability(ammo/charge) and no mods to the player's inventory. - - The item to be added. - - - - Add an item to the player's inventory. - - The item to be added. - - - - Resets the player's inventory to the provided list of items, clearing any items it already possess. - - The new items that have to be added to the inventory. - - - - Resets the player's inventory to the provided list of items, clearing any items it already possess. - - The new items that have to be added to the inventory. - - - - Clears the player's inventory, including all ammo and items. - - - - - Drops all items in the player's inventory, including all ammo and items. - - - - - Sets the amount of a specified ammo type. - - The to be set. - The amount of ammo to be set. - - - - Gets the amount of a specified . - - The to get the amount from. - Returns the amount of the chosen . - - - - Simple way to show a hint to the player. - - The message to be shown. - The duration the text will be on screen. - - - - Gets a describing whether or not the given status effect is currently enabled. - - The to check. - A determining whether or not the player effect is active. - - - - Disables all currently active status effects. - - - - - Disables a specific status effect on the player. - - The to disable. - - - - Disables a specific status effect on the player. - - The to disable. - - - - Enables a status effect on the player. - - The to enable. - The amount of time the effect will be active for. - If the effect is already active, setting to true will add this duration onto the effect. - - - - Enables a status effect on the player. - - The name of the to enable. - The amount of time the effect will be active for. - If the effect is already active, setting to true will add this duration onto the effect. - A bool indicating whether or not the effect was valid and successfully enabled. - - - - Enables a status effect on the player. - - The to enable. - The amount of time the effect will be active for. - If the effect is already active, setting to true will add this duration onto the effect. - - - - Gets an instance of by . - - The . - The . - - - - Tries to get an instance of by . - - The . - The . - A bool indicating whether or not the was successfully gotten. - - - - Gets a indicating the intensity of the given status effect. - - The to check. - Thrown if the given type is not a valid . - The intensity of the effect. - - - - Changes the intensity of a status effect. - - The to change the intensity of. - The intensity of the effect. - - - - Changes the intensity of a status effect. - - The name of the to enable. - The intensity of the effect. - The new length of the effect. Defaults to infinite length. - - - - Removes the player's hands. - - - - - - - - Expose how a plugin has to be made. - - The config type. - - - - Initializes a new instance of the class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A set of tools to handle team respawns more easily. - - - - - Gets the next known that will spawn. - - - - - Gets the amount of seconds before the next respawn will occur. - - - - - Gets a value indicating whether or not a team is currently being spawned or the animations are playing for a team. - - - - - Gets the amount of spawn tickets belonging to the NTF. - - - - - Gets the amount of spawn tickets belonging to the Chaos Insurgency. - - - - - Gets the actual . - - - - - Play an effect when a certain class spawns. - - The effect to be played. - - - - Play an effect when a certain class spawns. - - The effect to be played. - - - - Play effects when a certain class spawns. - - The effects to be played. - - - - Play effects when a certain class spawns. - - The effects to be played. - - - - Summons the NTF chopper. - - - - - Summons the van. - - Whether or not to play the Chaos Insurgency spawn music. - - - - Grants tickets to a . - - The to grant tickets to. - The amount of tickets to grant. - Whether or not to override ticket locks. - Whether or not tickets were granted successfully. - - - - Forces a spawn of the given . - - The to spawn. - Whether or not effects will be played with the spawn. - - - - The in-game room. - - - - - Gets the name. - - - - - Gets the . - - - - - Gets the position. - - - - - Gets the in which the room is located. - - - - - Gets the . - - - - - Gets a of in the . - - - - - Gets a of in the . - - - - - Gets a value indicating whether or not the lights in this room are currently flickered off. - - - - - Flickers the room's lights off for a duration. - - Duration in seconds. - - - - Sets the intensity of the lights in the room. - - The light intensity multiplier. Cannot be brighter than 2 or darker than 0. - - - - Factory method to create and add a component to a Transform. - We can add parameters to be set privately here. - - The Game Object to attach the Room component to. - The Room component that was instantiated onto the Game Object. - - - - A set of tools to handle the round more easily. - - - - - Gets the time elapsed from the start of the round. - - - - - Gets the start time of the round. - - - - - Gets a value indicating whether the round is started or not. - - - - - Gets or sets a value indicating whether the round is locked or not. - - - - - Gets or sets a value indicating whether the lobby is locked or not. - - - - - Restarts the round. - - - - - Restarts the round with custom settings. - - - Indicates whether or not it'll be a fast restart. - If it's a fast restart, then players won't be reconnected from - the server; otherwise, they will. - - - Overrides a value of . - Makes sense if someone used a command to set another action. - - - The . - - - does nothing, just restarts the round silently. - - restarts the server, reconnects all players. - - shutdowns the server, also disconnects all players. - - - - - - Restarts the round silently. - - - - - Forces the round to end, regardless of which factions are alive. - - A describing whether or not the round was successfully ended. - - - - Start the round. - - - - - A set of tools to modify SCP-096's behaviour. - - - - - Gets or Sets a value indicating the max shield amount SCP-096 can have during his docile state. - - - - - Gets a list of player ids who will be turned away from SCP-096. - - - - - A set of tools to modify SCP-173's behaviour. - - - - - Gets a list of player ids who will be turned away from SCP-173. - - - - - A set of tools to modify SCP-914's behaviour. - - - - - Gets or sets SCP-914 . - - - - - Gets or sets SCP-914 recipes. - - - - - Gets or sets SCP-914 config mode. - - - - - Gets a value indicating whether the SCP-914 was activated and is currently processing items. - - - - - Gets the intake booth . - - - - - Gets the output booth . - - - - - Starts the SCP-914. - - - - - A set of tools to easily work with the server. - - - - - Gets the player's host of the server. - Might be null when called when the server isn't loaded. - - - - - Gets the cached component. - - - - - Gets the cached component. - - - - - Gets the cached . - - - - - Gets or sets the name of the server. - - - - - Gets the Ip address of the server. - - - - - Gets the port of the server. - - - - - Gets or sets a value indicating whether friendly fire is enabled or not. - - - - - Restarts the server, reconnects all players. - - - - - Shutdowns the server, disconnects all players. - - - - - Redirects players to a server on another port, restarts the current server. - - The port to redirect players to. - true, if redirection was successful; otherwise, false. - If the returned value is false, the server won't restart. - - - - Redirects players to a server on another port, shutdowns the current server. - - The port to redirect players to. - true, if redirection was successful; otherwise, false. - If the returned value is false, the server won't shutdown. - - - - A set of tools to easily work with the alpha warhead. - - - - - Gets the cached component. - - - - - Gets the cached component. - - - - - Gets the cached component. - - - - - Gets or sets a value indicating whether the warhead lever is enabled or not. - - - - - Gets or sets a value indicating whether the warhead has already been activated or not. - - - - - Gets a value indicating whether the warhead has already been detonated or not. - - - - - Gets a value indicating whether the warhead detonation is in progress or not. - - - - - Gets or sets the warhead detonation timer. - - - - - Gets the warhead real detonation timer. - - - - - Gets or sets a value indicating whether the warhead can be disabled or not. - - - - - Gets or sets a value indicating whether the warhead can be disabled or not. - - - - - Gets a value indicating whether the warhead can be started or not. - - - - - Starts the warhead countdown. - - - - - Stops the warhead. - - - - - Detonates the warhead. - - - - - Shake all players, like if the warhead has been detonated. - - - - - Defines the contract for basic config features. - - - - - Gets or sets a value indicating whether the plugin is enabled or not. - - - - - Defines the contract for basic plugin features. - - The config type. - - - - Gets the plugin assembly. - - - - - Gets the plugin name. - - - - - Gets the plugin prefix. - - - - - Gets the plugin author. - - - - - Gets the plugin commands. - - - - - Gets the plugin priority. - Higher values mean higher priority and vice versa. - - - - - Gets the plugin version. - - - - - Gets the required version of Exiled to run the plugin without bugs or incompatibilities. - - - - - Gets the plugin config. - - - - - Fired after enabling the plugin. - - - - - Fired after disabling the plugin. - - - - - Fired after reloading the plugin. - - - - - Fired before registering commands. - - - - - Fired before unregistering configs. - - - - diff --git a/packages/EXILED.2.2.4/lib/net472/Exiled.Bootstrap.xml b/packages/EXILED.2.2.4/lib/net472/Exiled.Bootstrap.xml deleted file mode 100644 index 5df0458..0000000 --- a/packages/EXILED.2.2.4/lib/net472/Exiled.Bootstrap.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - Exiled.Bootstrap - - - - - The assembly loader class for Exiled. - - - - - Gets a value indicating whether exiled has already been loaded or not. - - - - - Internally called loading method. - - - - diff --git a/packages/EXILED.2.2.4/lib/net472/Exiled.Events.xml b/packages/EXILED.2.2.4/lib/net472/Exiled.Events.xml deleted file mode 100644 index a43d449..0000000 --- a/packages/EXILED.2.2.4/lib/net472/Exiled.Events.xml +++ /dev/null @@ -1,5267 +0,0 @@ - - - - Exiled.Events - - - - - The reload configs command. - - - - - Gets static instance of the command. - - - - - - - - - - - - - - - - - The reload configs command. - - - - - Gets static instance of the command. - - - - - - - - - - - - - - - - - The reload gameplay command. - - - - - Gets static instance of the command. - - - - - - - - - - - - - - - - - The reload plugins command. - - - - - Gets static instance of the command. - - - - - - - - - - - - - - - - - The reload command. - - - - - Initializes a new instance of the class. - - - - - - - - - - - - - - - - - - - - The reload remoteadmin command. - - - - - Gets static instance of the command. - - - - - - - - - - - - - - - - - The command to show all plugins. - - - - - - - - - - - - - - - - - The command to show all plugins. - - - - - Initializes a new instance of the class. - - - - - - - - - - - - - - - - - - - - - - - - - - Gets or sets a value indicating whether SCP-173 can be blocked or not by the tutorial. - - - - - Gets or sets a value indicating whether SCP-096 can be triggered or not by the tutorial. - - - - - Gets or sets a value indicating whether the name tracking is enabled or not. - - - - - Gets or sets a value indicating whether the inventory should be dropped before being set as spectator, through commands or plugins. - - - - - Gets or sets a value indicating whether the blood can be spawned or not. - - - - - Gets or sets a value indicating whether configs has to be reloaded every time a round restarts. - - - - - Gets a value indicating whether bans should be logged or not. - - - - - Gets or sets a value indicating the max shield amount for Scp096. - - - - - Contains all informations before a player activates SCP-914. - - - - - Initializes a new instance of the class. - - - - - - - Gets the player who's activating SCP-914. - - - - - Gets or sets a value indicating whether or not SCP-914 can be activated. - - - - - Contains all informations before a player activates the warhead panel. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's trying to activate the warhead panel. - - - - - Gets a list of permissions, required to activate the warhead panel. - - - - - Gets or sets a value indicating whether or not the warhead can be activated. - - - - - Contains all informations before a player activates a workstation. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's trying to activate the workstation. - - - - - Gets a workstation. - - - - - Gets or sets a value indicating whether or not the workstation can be activated. - - - - - Contains all informations before adding a target to SCP-096. - - - - - Initializes a new instance of the class. - - who is SCP-096. - who is the target to be added. - amount of temporary health to add to . - amount of time to add to 's enrage timer. Note: This does not affect anything if he doesn't already have any targets before this event is called. - - - - Gets the that is controlling SCP-096. - - - - - Gets the being added as a target. - - - - - Gets or sets a value indicating whether or not the target is allowed to be added. - - - - - Gets or sets the amount of AHP to add to SCP-096 if is true. - - - - - Gets or sets how much time is added to SCP-096's enrage timer if is true. - - - - - Contains all informations before C.A.S.S.I.E announces light containment zone decontamination. - - - - - Initializes a new instance of the class. - - - - - - - Gets or sets the announcement id, from 0 to 6. - - - - - Gets or sets a value indicating whether the announcement is going to be global or not. - - - - - Gets or sets a value indicating whether the event can be executed or not. - - - - - Contains all informations before C.A.S.S.I.E announces the NTF entrance. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the number of SCPs left. - - - - - Gets or sets the NTF unit name. - - - - - Gets or sets the NTF unit number. - - - - - Gets or sets a value indicating whether or not the NTF spawn will be announced by C.A.S.S.I.E. - - - - - Contains all informations before C.A.S.S.I.E announces an SCP termination. - - - - - Initializes a new instance of the class. - - - - - - - - - - Gets the player who killed the SCP. - - - - - Gets the killed . - - - - - Gets or sets the hit info. - - - - - Gets or sets the termination cause. - - - - - Gets or sets a value indicating whether or not the SCP termination will be announced by C.A.S.S.I.E. - - - - - Contains all informations after banning a player from the server. - - - - - Initializes a new instance of the class. - - The banned player. - The issuer player. - The ban details. - - - - - Gets the banned player. - - - - - Gets the banned player. - - - - - Gets the issuer player. - - - - - Gets the ban details. - - - - - Gets the ban type. - - - - - Contains all informations before banning a player from the server. - - - - - Initializes a new instance of the class. - - The ban target. - The ban issuer. - The ban minutes duration. - The ban reason. - The ban full message. - Indicates whether the event can be executed or not. - - - - Gets or sets the ban duration. - - - - - Contains all informations before a players blink near SCP-173. - - - - - Initializes a new instance of the class. - - - - - - - Gets the player who controlling SCP-173. - - - - - Gets a list of players who have triggered SCP-173. - - - - - Contains all informations before SCP-096 calms down. - - - - - Initializes a new instance of the class. - - The instance. - The player who's controlling SCP-096. - Indicates whether or not SCP-096 can calm down. - - - - Contains all informations before changing item attachments. - - - - - Initializes a new instance of the class. - - - - - - - - - - Contains all informations before changing item attributes. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the old item. - - - - - Gets or sets the item. - - - - - Gets or sets the item type. - - - - - Gets or sets the new item durability. - - - - - Gets or sets the new item unique id. - - - - - Gets or sets the new item sight attachment. - - - - - Gets or sets the new item barrel attachment. - - - - - Gets or sets the new item other attachment. - - - - - Gets or sets a value indicating whether the event can be executed or not. - - - - - Contains all informations before a SCP-079 changes the current camera. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player who is SCP-079. - - - - - Gets or sets the camera SCP-079 will be moved to. - - - - - Gets or sets the amount of AP that will be required to switch cameras. - - - - - Gets or sets a value indicating whether or not SCP-079 can switch cameras. - Defaults to a value describing whether or not SCP-079 has enough AP to switch. - Can be set to true to allow a switch regardless of SCP-079's AP amount. - - - - - Contains all informations before changing item durability. - - - - - Initializes a new instance of the class. - - - - - - - - Contains all informations before a player changes his group. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's changing his group. - - - - - Gets or sets the player's new group. - - - - - Gets or sets a value indicating whether or not the player can change groups. - - - - - Contains all informations before a player's intercom mute status is changed. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's being intercom muted/unmuted. - - - - - Gets or sets a value indicating whether or not the player can be intercom muted/unmuted. - - - - - Gets a value indicating whether the player is being intercom muted or unmuted. - - - - - Contains all informations before a player's held item changes. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's changing the item. - - - - - Gets or sets the old item. - - - - - Gets the new item. - - - - - Contains all informations before a player changes the SCP-914 knob setting. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's changing the SCP-914 knob setting. - - - - - Gets or sets the SCP-914 knob setting. - - - - - Gets or sets a value indicating whether or not SCP-914's knob setting can be changed. - - - - - Contains all informations before a player changes the warhead lever status. - - - - - Initializes a new instance of the class. - - - - - - - Gets the player who's changing the warhead status. - - - - - Gets or sets a value indicating whether or not the lever status will change. - - - - - Contains all informations before a player's mute status is changed. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's being muted/unmuted. - - - - - Gets or sets a value indicating whether or not the player can be muted/unmuted. - - - - - Gets a value indicating whether the player is being muted or unmuted. - - - - - Contains all informations before a player's changes. - - - - - Initializes a new instance of the class. - - - - - - - - - - Gets the player whose is changing. - - - - - Gets or sets the new player's role. - - - - - Gets base items that the player will receive. - - - - - Gets or sets a value indicating whether the player escaped or not. - - - - - Gets or sets a value indicating whether the position has to be preserved after changing the role. - - - - - Contains all informations before a player closes a generator. - - - - - Initializes a new instance of the class. - - The player who's closing the generator. - The instance. - Indicates whether or not the generator can be closed. - - - - Contains all informations before containing SCP-106. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's controlling SCP-106. - - - - - Gets the player who pressed the button. - - - - - Gets or sets a value indicating whether or not SCP-106 can be recontained. - - - - - Contains all informations before SCP-106 creates a portal. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's controlling SCP-106. - - - - - Gets or sets the portal position. - - - - - Gets or sets a value indicating whether or not SCP-106 can create a portal. - - - - - Contains all informations before damage is dealt to a . - - - - - Initializes a new instance of the class. - - - - - - - Gets the object that is damaged. - - - - - Gets or sets the damage the window will receive. - - - - - Contains all informations before a player deactivates a workstation. - - - - - Initializes a new instance of the class. - - - - Gets or sets a value indicating whether or not the workstation can be deactivated. - - - - Contains all informations before decontaminating the light containment zone. - - - - - Initializes a new instance of the class. - - - - - - Gets or sets a value indicating whether or not light containment zone decontamination can begin. - - - - - Contains all informations after a player dequipes a medical item. - - - - - Initializes a new instance of the class. - - - - - - - Gets the player who used the medical item. - - - - - Gets the medical item that the player consumed. - - - - - Contains all informations before a player's object is destroyed. - - - - - Initializes a new instance of the class. - - - - - - Gets the destoying player. - - - - - Contains all informations after a player dies. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the killer player. - - - - - Gets the killed player. - - - - - Gets or sets the hit informations. - - - - - Contains all informations before a player drops an item. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's dropping the item. - - - - - Gets or sets the item to be dropped. - - - - - Gets or sets a value indicating whether or not the item can be dropped. - - - - - Contains all information before a player dies. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the killing player. - - - - - Gets the dying player. - - - - - Gets or sets the hit information. - - - - - Gets or sets a value indicating whether or not the player can be killed. - - - - - Contains all informations before a player ejects a tablet from a generator. - - - - - Initializes a new instance of the class. - - The player who's ejecting the tablet. - The instance. - Indicates whether or not the tablet can be ejected. - - - - Contains all informations before SCP-079 changes rooms via elevator. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player who is controlling SCP-079. - - - - - Gets or sets the that SCP-079 will be moved to. - - - - - Gets or sets the amount of AP will be consumed during the level change. - - - - - Gets or sets a value indicating whether or not SCP-079 can teleport. - Defaults to a describing whether or not SCP-079 has enough AP to teleport. - - - - - Contains all informations before ending a round. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets or sets the round summary class list. - - - - - Gets or sets the leading team. - - - - - Gets or sets a value indicating whether the round is going to finish or not. - - - - - Gets or sets a value indicating whether the event can be executed or not. - - - - - Contains all informations before SCP-096 gets enraged. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the SCP-096 instance. - - - - - Gets the player who's controlling SCP-096. - - - - - Gets or sets a value indicating whether or not SCP-096 can be enraged. - - - - - Contains all informations before a player enters the femur breaker. - - - - - Initializes a new instance of the class. - - - - - - - Gets the player who's entering the femur breaker. - - - - - Gets or sets a value indicating whether or not the player can activate the femur breaker. - - - - - Contains all informations before a player enters the pocket dimension. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player who's entering the pocket dimension. - - - - - Gets the SCP-106 who sent the player to the pocket dimension. - - - - - Gets or sets the pocket dimension position. - - - - - Gets or sets a value indicating whether or not the player can enter the pocket dimension. - - - - - Contains all informations before a player escapes. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's escaping. - - - - - Gets or sets the role that will be assigned when the player escapes. - - - - - Gets or sets a value indicating whether or not the player can escape. - - - - - Contains all informations before a player escapes the pocket dimension. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's escaping the pocket dimension. - - - - - Gets or sets the position in which the player is going to be teleported to. - - - - - Gets or sets a value indicating whether or not the player can successfully escape the pocket dimension. - - - - - Contains all informations before a grenade explodes. - - - - - Initializes a new instance of the class. - - - - - - - - - - Finalizes an instance of the class. - - - - - Gets the player who thrown the grenade. - - - - - Gets the players who could be affected by the grenade, if any, and the damage that would hurt them. - - - - - Gets the players who could be affected by the grenade, if any. - - - - - Gets a value indicating whether the grenade is a frag or flash grenade. - - - - - Gets the grenade that is exploding. - - - - - Gets or sets a value indicating whether or not the grenade can be thrown. - - - - - Contains all informations before a player dies from walking through an incorrect exit in the pocket dimension. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's escaping the pocket dimension. - - - - - Gets the PocketDimensionTeleport the player walked into. - - - - - Gets or sets a value indicating whether or not the player dies by failing the pocket dimension escape. - - - - - Contains all informations before SCP-049 finishes recalling a player. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's getting recalled. - - - - - Gets the player who is controlling SCP-049. - - - - - Gets or sets a value indicating whether or not the player can be recalled. - - - - - Contains all informations before SCP-079 gains experience. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player who's controlling SCP-079. - - - - - Gets the experience gain type. - - - - - Gets or sets the amount of experience to be gained. - - - - - Gets or sets a value indicating whether or not the experience is successfully granted. - - - - - Contains all informations before SCP-079 gains a level. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player who's controlling SCP-079. - - - - - Gets the old level of SCP-079. - - - - - Gets or sets the new level of SCP-079. - - - - - Gets or sets a value indicating whether or not the level is successfully granted. - - - - - Contains all informations after activating a generator. - - - - - Initializes a new instance of the class. - - - - - - Gets the activated generator. - - - - - Contains all informations before handcuffing a player. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the cuffer player. - - - - - Gets the target player to be cuffed. - - - - - Gets or sets a value indicating whether or not the player can be handcuffed. - - - - - Contains all informations before a player gets damaged. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the attacker player. - - - - - Gets the target player, who is going to be hurt. - - - - - Gets the hit informations. - - - - - Gets the time at which the player was hurt. - - - - - Gets the damage type. - - - - - Gets the tool that damaged the player. - - - - - Gets or sets the amount of inflicted damage. - - - - - Gets or sets a value indicating whether or not the player will be dealt damage. - - - - - Contains all informations before a player inserts a tablet into a generator. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's inserting a tablet into the generator. - - - - - Gets the instance. - - - - - Gets or sets a value indicating whether or not the tablet can be inserted. - - - - - Contains all informations after a player has interacted with an interactable. - - - - - Initializes a new instance of the class. - - - - - - Gets the player who interacted. - - - - - Contains all informations before a player interacts with a door. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's interacting with the door. - - - - - Gets or sets the instance. - - - - - Gets or sets a value indicating whether or not the player can interact with the door. - - - - - Contains all information before a player interacts with an elevator. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player who's interacting with the elevator. - - - - - Gets the instance. - - - - - Gets the instance. - - - - - Gets the current . - - - - - Gets the . - - - - - Gets or sets a value indicating whether or not the player can interact with the elevator. - - - - - Contains all informations before a player interacts with a locker. - - - - - Initializes a new instance of the class. - - - - - - - - - - - Gets the player who's interacting with the locker. - - - - - Gets the instance. - - - - - Gets the interacting chamber. - - - - - - - - Gets the locker id. - - - - - Gets the chamber id. - - - - - Gets or sets a value indicating whether or not the player can interact with the locker. - - - - - Contains all informations before SCP-079 triggers a tesla gate. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's controlling SCP-079. - - - - - Gets the that SCP-079 is triggering. - - - - - Gets or sets a value indicating whether or not SCP-079 can interact with the tesla gate. - - - - - Contains all informations before a player speaks to the intercom. - - - - - Initializes a new instance of the class. - - - - - - - Gets the player who's going to speak to the intercom. - - - - - Gets or sets a value indicating whether or not the player can speak to the intercom. - - - - - Contains all informations after a player drops an item. - - - - - Initializes a new instance of the class. - - - - - - - Gets the player who dropped the item. - - - - - Gets the dropped pickup. - - - - - Contains all informations after a player joins the server. - - - - - Initializes a new instance of the class. - - - - - - Gets the joined player. - - - - - Contains all informations after kicking a player from the server. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the kicked player. - - - - - Gets the kicked player. - - - - - Gets or sets the kick reason. - - - - - Gets or sets a value indicating whether the event can be executed or not. - - - - - Contains all informations before kicking a player from the server. - - - - - Initializes a new instance of the class. - - - - - - - - - - Gets or sets the ban target. - - - - - Gets or sets the ban issuer. - - - - - Gets or sets the kick reason. - - - - - Gets or sets the full kick message. - - - - - Gets or sets a value indicating whether or not action is taken against the target. - - - - - Logs the kick, anti-backdoor protection from malicious plugins. - - The message to be logged. - - - - Contains all player's information, after he leaves the server. - - - - - Initializes a new instance of the class. - - The player who left the server. - - - - Contains informations before a report is sent to local administrators. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the report issuer. - - - - - Gets the report target. - - - - - Gets or sets the report reason. - - - - - Gets or sets a value indicating whether the report can be processed or not. - - - - - Contains all informations before a player opens a generator. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's opening the generator. - - - - - Gets the generator that is opening. - - - - - Gets or sets a value indicating whether or not the generator can be opened. - - - - - Contains all informations before a player picks up an item. - - - - - Initializes a new instance of the class. - - The player who's picking up the item. - The pickup to be picked up. - - - - - Gets or sets a value indicating whether or not the item can be picked up. - - - - - Contains all informations before a player interacts with SCP-330. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player who's interacting with SCP-330. - - - - - Gets or sets a value indicating whether or not the player can interact with SCP-330. - - - - - Gets or sets player's pickup counter. - - - - - Gets or sets a value indicating whether or not the interaction should be severe. - - - - - Gets or sets a value indicating what item will be picked up. - - - - - Contains all informations before a player places blood. - - - - - Initializes a new instance of the class. - - - - - - - - - - Gets the player who's placing the blood. - - - - - Gets or sets the blood placing position. - - - - - Gets or sets the blood type. - - - - - Gets or sets the blood multiplier. - - - - - Gets or sets a value indicating whether or not the blood can be placed. - - - - - Contains all informations before placing a decal. - - - - - Initializes a new instance of the class. - - - - - - - - - - Gets the decal owner. - - - - - Gets or sets the decal position. - - - - - Gets or sets the decal rotation. - - - - - Gets or sets the decal type. - - - - - Gets or sets a value indicating whether or not the decal can be placed. - - - - - Contains all informations before pre-autenticating a player. - - - - - Initializes a new instance of the class. - - - - - - - - - - - Gets the player's user id. - - - - - Gets the reader starting position for reading the preauth. - - - - - Gets the flags. - - - - - Gets the player's country. - - - - - Gets the connection request. - - - - - Gets a value indicating whether the player can be authenticated or not. - - - - - Delays the connection. - - The delay in seconds. - Indicates whether the player has to be rejected forcefully or not. - - - - Rejects the player and redirects them to another server port. - - The new server port. - Indicates whether the player has to be rejected forcefully or not. - - - - Rejects a player who's trying to authenticate. - - The ban reason. - The ban expiration time. - Indicates whether the player has to be rejected forcefully or not. - - - - Rejects a player who's trying to authenticate. - - The ban reason. - The ban expiration time in .NET Ticks. - Indicates whether the player has to be rejected forcefully or not. - - - - Rejects a player who's trying to authenticate. - - The instance. - Indicates whether the player has to be rejected forcefully or not. - - - - Rejects a player who's trying to authenticate. - - The custom rejection reason. - Indicates whether the player has to be rejected forcefully or not. - - - - Rejects a player who's trying to authenticate. - - The rejection reason. - Indicates whether the player has to be rejected forcefully or not. - The custom rejection reason (Banned and Custom reasons only). - The ban expiration ticks (Banned reason only). - The delay in seconds (Delay reason only). - The redirection port (Redirect reason only). - - - - Disallows the connection without sending any reason. Should only be used when the connection has already been terminated by the plugin itself. - - - - - Contains all information before a player receives a . - - - - - Initializes a new instance of the class. - - The receiving the effect. - The being added to the player. - The state the effect is being changed to. - The current state of the effect being changed. - - - - Gets the receiving the effect. - - - - - Gets the being received. - - - - - Gets or sets a value indicating whether or not the effect will be applied. - - - - - Gets or sets a value indicating how long the effect will last. - - - - - Gets or sets the value of the new state of the effect. Setting this to 0 is the same as setting IsAllowed to false. - - - - - Gets the value of the current state of this effect on the player. - - - - - Contains informations after SCP-079 recontainming. - - - - - Initializes a new instance of the class. - - - - - - Gets the player that previously controlled SCP-079. - - - - - Contains all informations before a player's weapon is reloaded. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's reloading the weapon. - - - - - Gets a value indicating whether only the reload animation is being reproduced or not. - - - - - Gets or sets a value indicating whether or not the weapon can be reloaded. - - - - - Contains all informations before freeing a handcuffed player. - - - - - Initializes a new instance of the class. - - The cuffer player. - The target player to be uncuffed. - Indicates whether the event can be executed or not. - - - - Contains all informations before reporting a cheater. - - - - - Initializes a new instance of the class. - - - - - - - - - - Gets the reporter player. - - - - - Gets the reported player. - - - - - Gets the server id. - - - - - Gets or sets the report reason. - - - - - Gets or sets a value indicating whether or not the report will be sent. - - - - - Contains all informations before spawning a wave of or . - - - - - Initializes a new instance of the class. - - - - - - - - Gets the list of players that are going to be respawned. - - - - - Gets or sets the maximum amount of respawnable players. - - - - - Gets or sets a value indicating what the next respawnable team is. - - - - - Gets or sets a value indicating whether or not the spawn can occur. - - - - - Gets the current spawnable team. - - - - - Contains all information before ending a round. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the leading team. - - - - - Gets or sets the round summary class list. - - - - - Gets or sets the time to restart the next round. - - - - - Contains all informations before sending a console message. - - - - - Initializes a new instance of the class. - - - - - - - - - - - - Gets the player who's sending the command. - - - - - Gets the command name. - - - - - Gets the command arguments. - - - - - Gets a value indicating whether the command is encrypted or not. - - - - - Gets or sets the return message, that will be shown to the user in the console. - - - - - Gets or sets the color of the return message. - - - - - Gets or sets a value indicating whether the event can be executed or not. - - - - - Gets or sets a value indicating whether or not the console command can be sent. - - - - - Contains all informations before sending a remote admin message. - - - - - Initializes a new instance of the class. - - - - - - - - - - Gets the sending the command. - - - - - Gets the player who's sending the command. - - - - - Gets the command name. - - - - - Gets the command arguments. - - - - - Gets or sets the message that will be returned back to the . - - - - - Gets or sets a value indicating whether whether or not the command was a success. - - - - - Gets or sets a value indicating whether or not the RemoteAdmin command can be sent. - - - - - Contains all informations before a player fires a weapon. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player who's shooting. - - - - - Gets the target the player's shooting at. - - - - - Gets or sets the position of the shot. - - - - - Gets or sets a value indicating whether or not the shot can be fired. - - - - - Contains all informations after a player has fired a weapon. - - - - - Initializes a new instance of the class. - - - - - - - - - - - Gets the player who shot. - - - - - Gets the target of the shot. - - - - - Gets the hitbox type of the shot. - - - - - Gets the hitbox type of the shot. - - - - - Gets the shot distance. - - - - - Gets or sets the inflicted damage. - - - - - Gets or sets a value indicating whether or not the shot can hurt the target. - - - - - Contains all informations after the server spawns an item. - - - - - Initializes a new instance of the class. - - - - - - Gets or sets the item pickup. - - - - - Contains all informations before spawning a player. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the spawning player. - - - - - Gets the player role type. - - - - - Gets or sets the player's spawning position. - - - - - Gets or sets the rotation y axis of the player. - - - - - Contains all informations before the server spawns an item. - - - - - Initializes a new instance of the class. - - - - - - - - - - Gets or sets the item to be dropped. - - - - - Gets or sets the position to spawn the item. - - - - - Gets or sets the rotation to spawn the item. - - - - - Gets or sets a value indicating whether or not the pickup will be locked. - - - - - Gets or sets a value indicating whether or not the item can be spawned. - - - - - Contains all informations before spawning a player ragdoll. - - - - - Initializes a new instance of the class. - - - - - - - - - - - - - - - - - Gets the player who killed the owner of the ragdoll. - - - - - Gets the owner of the ragdoll (typically the player who died). - - - - - Gets or sets the spawning position of the ragdoll. - - - - - Gets or sets the ragdoll rotation. - - - - - Gets or sets the adapted ragdoll velocity. - - - - - Gets or sets the RoleType of the ragdoll owner. - - - - - Gets or sets the hit informations on the ragdoll. - - - - - Gets or sets a value indicating whether or not the player can be revived by SCP-049. - - - - - Gets or sets the ragdoll dissonance id. - - - - - Gets or sets the ragdoll player nickname. - - - - - Gets or sets the ragdoll player id. - - - - - Gets or sets a value indicating whether or not the ragdoll will be spawned. - - - - - Contains all informations before starting the warhead. - - - - - Initializes a new instance of the class. - - The player who's going to start the warhead. - Indicating whether the event can be executed or not. - - - - Gets a value indicating whether or not the nuke was set off automatically. - - - - - Contains all informations before SCP-049 begins recalling a player. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's getting recalled. - - - - - Gets the player who is controlling SCP-049. - - - - - Gets or sets a value indicating whether the recall can begin. - - - - - Contains all informations before SCP-079 uses a speaker. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player who's controlling SCP-079. - - - - - Gets the room that the speaker is located in. - - - - - Gets or sets the amount of AP that will be removed for the first time when using speakers through SCP-079. - - - - - Gets or sets a value indicating whether or not SCP-079 can use the speaker. - - - - - Contains all information before SCP-096 begins prying a gate open. - - - - - Initializes a new instance of the class. - - The Scp096 who is triggering the event. - The gate to be pried open. - - - - Gets the player that is controlling SCP-096. - - - - - Gets the to be pried open. - - - - - Gets or Sets a value indicating whether or not the gate can be pried open by SCP-096. - - - - - Contains all informations before stopping the warhead. - - - - - Initializes a new instance of the class. - - - - - - - Gets the player who's going to stop the warhead. - - - - - Gets or sets a value indicating whether or not the warhead can be stopped. - - - - - Contains all informations before a player cancels usage of a medical item. - - - - - Initializes a new instance of the class. - - The player who's stopping the use of the medical item. - The medical item that won't be consumed. - The cooldown left for completing the use of the medical item. - Whether or not the player can cancel usage of the medical item. - - - - Gets the medical item cooldown. - - - - - Contains all informations before SCP-079 finishes using a speaker. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's controlling SCP-079. - - - - - Gets the room that the speaker is located in. - - - - - Gets or sets a value indicating whether or not SCP-079 can stop using the speaker. - - - - - Contains all informations before syncing player's data with the server. - - - - - Initializes a new instance of the class. - - - - - - - - - Gets the player of the syncing data. - - - - - Gets the player's speed. - - - - - Gets or sets the current player's animation. - - - - - Gets or sets a value indicating whether or not the player's data can be synced with the server. - - - - - Contains all informations before SCP-106 teleports using a portal. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's controlling SCP-106. - - - - - Gets or sets the portal position. - - - - - Gets or sets a value indicating whether or not SCP-106 can teleport using a portal. - - - - - Contains all informations before a player throws a greande. - - - - - Initializes a new instance of the class. - - - - - - - Indicates whether the event can be executed or not. - - - - Gets the player who's throwing the greande. - - - - - Gets the instance. - - - - - Gets the grenade id. - - - - - Gets or sets the grenade type. - - - - - Gets or sets a value indicating whether the throw is slow or not. - - - - - Gets or sets the fuse time. - - - - - Gets or sets a value indicating whether or not the grenade can be thrown. - - - - - Contains all informations before triggering a tesla. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who triggered the tesla. - - - - - Gets or sets a value indicating whether or not the player is in hurting range. - - - - - Gets or sets a value indicating whether or not the tesla is going to be activated. - - - - - Contains all informations before a generator is unlocked. - - - - - Initializes a new instance of the class. - - - - - - - - Gets the player who's unlocking the generator. - - - - - Gets the generator that is going to be unlocked. - - - - - Gets or sets a value indicating whether or not the generator can be unlocked. - - - - - Contains all informations before SCP-914 upgrades players and items. - - - - - Initializes a new instance of the class. - - - - - - - - - - Gets the instance. - - - - - Gets a list of players inside SCP-914. - - - - - Gets a list of items to be upgraded inside SCP-914. - - - - - Gets or sets SCP-914 working knob setting. - - - - - Gets or sets a value indicating whether or not the upgrade is successful. - - - - - Contains all informations after a player uses a medical item. - - - - - Initializes a new instance of the class. - - - - - - - Gets the player who used the medical item. - - - - - Gets the medical item that the player consumed. - - - - - Contains all informations before a player uses a medical item. - - - - - Initializes a new instance of the class. - - The player who's going to use the medical item. - The medical item to be used. - - - - - - Gets or sets the medical item cooldown. - - - - - Gets or sets a value indicating whether or not the player can use the medical item. - - - - - Contains all informations after the server verifies a player. - - - - - Initializes a new instance of the class. - - - - - - Gets the verified player. - - - - - Patch and unpatch events into the game. - - - - - The below variable is used to increment the name of the harmony instance, otherwise harmony will not work upon a plugin reload. - - - - - The custom delegate. - - The type. - The instance. - - - - The custom delegate, with empty parameters. - - - - - Gets the plugin instance. - - - - - Gets a list of types and methods for which EXILED patches should not be run. - - - - - Gets a set of types and methods for which EXILED patches should not be run. - - - - - - - - Gets the instance. - - - - - - - - - - - Patches all events. - - - - - Checks the list and un-patches any methods that have been defined there. Once un-patching has been done, they can be patched by plugins, but will not be re-patchable by Exiled until a server reboot. - - - - - Unpatches all events. - - - - - A set of tools to execute events safely and without breaking other plugins. - - - - - Executes all listeners safely. - - Event arg type. - Source event. - Event arg. - Event or its arg is null. - - - - Executes all listeners safely. - - Source event. - Event is null. - - - - Handles event. - - - - - Called once the map is generated. - - - This fixes an issue where - all those extensions that - require calling the central - property of the Map class in - the API were corrupted due to - a missed call, such as before - getting the elevator type. - - - - - Handles some round clean-up events and some others related to players. - - - - - - - - - - - - - - - - - Handles scene unload event. - - - - - Called once when the server changes the scene. - - - This fixes the main issue with ghost mode, - when it spams with a NRE error. - Before that, we were clearing the cache - on WaitForPlayers event, but - sometimes (ordinally on silent rount restart) - the server accepts players' tokens before - WaitForPlayers event is called. - - - - - Item related events. - - - - - Invoked before the durability of an item is changed. - - - - - Invoked before item attachments are changed. - - - - - Called before the durability of an item is changed. - - The instance. - - - - Called before item attachments are changed. - - The instance. - - - - Map related events. - - - - - Invoked before placing decals. - - - - - Invoked before placing bloods. - - - - - Invoked before announcing the light containment zone decontamination. - - - - - Invoked before announcing an SCP termination. - - - - - Invoked before announcing the NTF entrance. - - - - - Invoked after a has been activated. - - - - - Invoked before decontaminating the light containment zone. - - - - - Invoked before damaging a window. - - - - - Invoked before a grenade explodes. - - - - - Invoked before an item is spawned. - - - - - Invoked after an item is spawned. - - - - - Invoked after the map is generated. - - - - - Called before placing a decal. - - The instance. - - - - Called before placing bloods. - - The instance. - - - - Called before announcing the light containment zone decontamination. - - The instance. - - - - Called before announcing an SCP termination. - - The instance. - - - - Called before announcing the NTF entrance. - - The instance. - - - - Called after a has been activated. - - The instance. - - - - Called before decontaminating the light containment zone. - - The instance. - - - - Called before damaging a window. - - The instance. - - - - Called before a grenade explodes. - - The instance. - - - - Called before an item is spawned. - - The instance. - - - - Called after an item is spawned. - - The instance. - - - - Called after the map is generated. - - - - - Player related events. - - - - - Invoked before authenticating a player. - - - - - Invoked before kicking a player from the server. - - - - - Invoked after a player has been kicked from the server. - - - - - Invoked before banning a player from the server. - - - - - Invoked after a player has been banned from the server. - - - - - Invoked after a player uses a medical item. - - - Invoked after , if a player's class has - changed during their health increase, won't fire. - - - - - Invoked after a player dequips a medical item. - - - Invoked before , if a player cancels the - use of a medical item, won't fire. - - - - - Invoked after a player has stopped the use of a medical item. - - - - - Invoked after a player interacted with something. - - - - - Invoked before spawning a player's ragdoll. - - - - - Invoked before activating the warhead panel. - - - - - Invoked before using a medical item. - - - - - Invoked after a player has joined the server. - - - - - Ivoked after a player has been verified. - - - - - Invoked after a player has left the server. - - - - - Invoked before destroying a player. - - - - - Invoked before hurting a player. - - - - - Invoked before a player dies. - - - - - Invoked after a player died. - - - - - Invoked before changing a player's role. - - - - - Invoked before throwing a grenade. - - - - - Invoked before dropping an item. - - - - - Invoked after an item has been dropped. - - - - - Invoked before picking up an item. - - - - - Invoked before handcuffing a player. - - - - - Invoked before freeing a handcuffed player. - - - - - Invoked before a player escapes. - - - - - Invoked before a player begins speaking to the intercom. - - - - - Invoked after a player shoots a weapon. - - - - - Invoked before a player shoots a weapon. - - - - - Invoked before a player enters the pocket dimension. - - - - - Invoked before a player escapes the pocket dimension. - - - - - Invoked before a player fails to escape the pocket dimension. - - - - - Invoked before a player reloads a weapon. - - - - - Invoked before spawning a player. - - - - - Invoked before a player enters the femur breaker. - - - - - Invoked before syncing player's data. - - - - - Invoked before a player's held item changes. - - - - - Invoked before changing a player's group. - - - - - Invoked before a player interacts with a door. - - - - - Invoked before a player interacts with an elevator. - - - - - Invoked before a player interacts with a locker. - - - - - Invoked before a player triggers a tesla gate. - - - - - Invoked before a player unlocks a generator. - - - - - Invoked before a player opens a generator. - - - - - Invoked before a player closes a generator. - - - - - Invoked before a player inserts a workstation tablet into a generator. - - - - - Invoked before a player ejects the workstation tablet out of a generator. - - - - - Invoked before a player receives a status effect. - - - - - Invoked before a workstation is activated. - - - - - Invoked before a workstation is deactivated. - - - - - Invoked before an user's mute status is changed. - - - - - Invoked before an user's intercom mute status is changed. - - - - - Called before pre-authenticating a player. - - The instance. - - - - Called before kicking a player from the server. - - The instance. - - - - Called after a player has been kicked from the server. - - The instance. - - - - Called before banning a player from the server. - - The instance. - - - - Called after a player has been banned from the server. - - The instance. - - - - Called after a player used a medical item. - - The instance. - - - - Called after a player dequipped a medical item. - - The instance. - - - - Called after a player has stopped the use of a medical item. - - The instance. - - - - Called after a player interacted with something. - - The instance. - - - - Called before spawning a player's ragdoll. - - The instance. - - - - Called before activating the warhead panel. - - The instance. - - - - Called before using a medical item. - - The instance. - - - - Called after a player has joined the server. - - The instance. - - - - Called after a player has been verified. - - The instance. - - - - Called after a player has left the server. - - The instance. - - - - Called before destroying a player. - - The instance. - - - - Called before hurting a player. - - The instance. - - - - Called before a player dies. - - instance. - - - - Called after a player died. - - The instance. - - - - Called before changing a player's role. - - The instance. - - - - Called before throwing a grenade. - - The instance. - - - - Called before dropping an item. - - The instance. - - - - Called after a player drops an item. - - The instance. - - - - Called before a user picks up an item. - - The instance. - - - - Called before handcuffing a player. - - The instance. - - - - Called before freeing a handcuffed player. - - The instance. - - - - Called before a player escapes. - - The instance. - - - - Called before a player begins speaking to the intercom. - - The instance. - - - - Called after a player shoots a weapon. - - The instance. - - - - Called before a player shoots a weapon. - - The instance. - - - - Called before a player enters the pocket dimension. - - The instance. - - - - Called before a player escapes the pocket dimension. - - The instance. - - - - Called before a player fails to escape the pocket dimension. - - The instance. - - - - Called before a player reloads a weapon. - - The instance. - - - - Called before spawning a player. - - The instance. - - - - Called before a player enters the femur breaker. - - The instance. - - - - Called before syncing player's data. - - The instance. - - - - Called before a player's held item changes. - - The instance. - - - - Called before changing a player's group. - - The instance. - - - - Called before a player interacts with a door. - - The instance. - - - - Called before a player interacts with an elevator. - - The instance. - - - - Called before a player interacts with a locker. - - The instance. - - - - Called before a player triggers a tesla. - - The instance. - - - - Called before a player unlocks a generator. - - The instance. - - - - Called before a player opens a generator. - - The instance. - - - - Called before a player closes a generator. - - The instance. - - - - Called before a player inserts a workstation tablet into a generator. - - The instance. - - - - Called before a player ejects the workstation tablet out of a generator. - - The instance. - - - - Called before a player receives a status effect. - - The instance. - - - - Called before a workstation is activated. - - The instance. - - - - Called before a workstation is deactivated. - - The instance. - - - - Called before an user's mute status is changed. - - The instance. - - - - Called before an user's intercom mute status is changed. - - The instance. - - - - SCP-049 related events. - - - - - Invoked before SCP-049 finishes recalling a player. - - - - - Invoked before SCP-049 begins recalling a player. - - - - - Called before SCP-049 finishes recalling a player. - - The instance. - - - - Called before Scp049 starts to recall a player. - - The instance. - - - - SCP-079 related events. - - - - - Invoked before SCP-079 switches cameras. - - - - - Invoked before gaining experience with SCP-079 - - - - - Invoked before gaining levels with SCP-079 - - - - - Invoked before triggering a tesla with SCP-079. - - - - - Invoked before triggering a door with SCP-079. - - - - - Invoked before SCP-079 teleports using an elevator. - - - - - Invoked before SCP-079 uses a speaker. - - - - - Invoked before SCP-079 finishes using a speaker. - - - - - Invoked after Scp079 recontainment. - - - - - Called before SCP-079 switches cameras. - - The instance. - - - - Called before gaining experience with SCP-079. - - The instance. - - - - Called before gaining levels with SCP-079. - - The instance. - - - - Called before triggering a tesla with SCP-079. - - The instance. - - - - Called before interacting with a door with SCP-079. - - The instance. - - - - Called before SCP-079 teleports using an elevator. - - The instance. - - - - Called before interacting with a speaker with SCP-079. - - The instance. - - - - Called before SCP-079 finishes using a speaker. - - The instance. - - - - Called after SCP-079 is recontained. - - The instance. - - - - SCP-096 related events. - - - - - Invoked before SCP-096 is enraged. - - - - - Invoked before SCP-096 calms down. - - - - - Invoked before adding a target to SCP-096. - - - - - Invoked before SCP-096 begins prying open a gate. - - - - - Called before SCP-096 is enraged. - - The instance. - - - - Called before SCP-096 calms down. - - The instance. - - - - Called before adding a target to SCP-096. - - The instance. - - - - Called before SCP-096 begins prying open a gate. - - The instance. - - - - SCP-106 related events. - - - - - Invoked before SCP-106 creates a portal. - - - - - Invoked before SCP-106 teleports using a portal. - - - - - Invoked before containing SCP-106. - - - - - Called before SCP-106 creates a portal. - - The instance. - - - - Called before SCP-106 teleports using a portal. - - The instance. - - - - Called before containing SCP-106. - - The instance. - - - - SCP-173 related events. - - - - - Invoked before players near SCP-173 blink. - - - - - Called before players near SCP-173 blink. - - The instance. - - - - Handles SCP-914 related events. - - - - - Invoked before SCP-914 upgrades players and items. - - - - - Invoked before activating the SCP-914 machine. - - - - - Invoked before changing the SCP-914 machine knob setting. - - - - - Called before SCP-914 upgrades players and items. - - The instance. - - - - Called before activating the SCP-914 machine. - - The instance. - - - - Called before changing the SCP-914 machine knob setting. - - The instance. - - - - Server related events. - - - - - Invoked before waiting for players. - - - - - Invoked after the start of a new round. - - - - - Invoked before ending a round. - - - - - Invoked after the end of a round. - - - - - Invoked before the restart of a round. - - - - - Invoked when a player reports a cheater. - - - - - Invoked before respawning a wave of Chaos Insurgency or NTF. - - - - - Invoked when sending a command through the in-game console. - - - - - Invoked when sending a command through the Remote Admin console. - - - - - Invoked when sending a complaint about a player to the local server administrators. - - - - - Invoked after the "reload configs" command is ran. - - - - - Invoked after the "reload gameplay" command is ran. - - - - - Invoked after the "reload remoteadminconfigs" command is ran. - - - - - Called before waiting for players. - - - - - Called after the start of a new round. - - - - - Called before ending a round. - - The instance. - - - - Called after the end of a round. - - The instance. - - - - Called before restarting a round. - - - - - Called when a player reports a cheater. - - The instance. - - - - Called before respawning a wave of Chaso Insurgency or NTF. - - The instance. - - - - Called when sending a command through in-game console. - - The instance. - - - - Called when sending a command through the Remote Admin console. - - The instance. - - - - Called when sending a complaint about a player to the local server administrators. - - The instance. - - - - Called after the "reload configs" command is ran. - - - - - Called after the "reload gameplay" command is ran. - - - - - Called after the "reload remoteadminconfigs" command is ran. - - - - - Handles warhead related events. - - - - - Invoked before stopping the warhead. - - - - - Invoked before starting the warhead. - - - - - Invoked after the warhead has been detonated. - - - - - Invoked before changing the warhead lever status. - - - - - Called before stopping the warhead. - - The instance. - - - - Called before starting the warhead. - - The instance. - - - - Called after the warhead has been detonated. - - - - - Called before changing the warhead lever status. - - The instance. - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patch the . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the and events. - - - - - Patch the . - Adds the event. - - - - - Patch the . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the and events. - - - - - Patches . - Adds the event. - - - - - Patch the . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patch the . - Adds the event. - - - - - Patches . - Adds the and events. - - - - - Patch the . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the and event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the and events. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Handle the player connection. - - The instance. - - - - Patches the method. - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the and events. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches a method, the class in which it's defined, is compiler-generated, . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the and event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the , , and event for SCP-079. - - - - - Prefix of . - - The instance. - The command to be executed. - The target game object. - Returns a value indicating whether the original method has to be executed or not. - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches the method. - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - - Patches . - Adds the event. - - - - - Patch the . - Adds the event. - - - - - Patches . - Adds the RestartingRound event. - - - - - Patches . - Adds the and event. - - - - - Patches . - Adds the RoundStarted event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches a method, the class in which it's defined, is compiler-generated, . - Adds the WaitingForPlayers event. - - - - - Patches . - Adds the WarheadDetonated event. - - - - - Patch the . - Adds the event. - - - - - Patch the . - Adds the event. - - - - - Patches . - Adds the event. - - - - - Patches . - Fixes triggering due to the R.2 code by using the Y coordinate from the player's scale to multiply the offset. - - - - - Patches . - Fixes triggering due to the R.3 code by using the Y coordinate from the player's scale to multiply the offset. - - - - - Fixes property. - - - - - Fixes property. - - - - - Fixes property. - - - - - Fixes method. - - - - - Patches . - - - - - Patches . - - - - - Patches the property. - - - - - Patches . - - - - - Patch the . - - - - - Patches . - - - - - An implementation of that - allows to get two elements in one move. - Very useful in patches. - - - - - - Initializes a new instance of the class. - - The innner . - - - - - - - Gets the next element in the collection. - Might be null if there's no element after . - - - - - - - - - - - - - - - - diff --git a/packages/EXILED.2.2.4/lib/net472/Exiled.Loader.xml b/packages/EXILED.2.2.4/lib/net472/Exiled.Loader.xml deleted file mode 100644 index 3395ded..0000000 --- a/packages/EXILED.2.2.4/lib/net472/Exiled.Loader.xml +++ /dev/null @@ -1,318 +0,0 @@ - - - - Exiled.Loader - - - - - The configs of the loader. - - - - - - - - Gets or sets a value indicating whether outdated plugins should be loaded or not. - - - - - Gets or sets the environment type. - - - - - Used to handle plugin configs. - - - - - Gets the config serializer. - - - - - Gets the config serializer. - - - - - Loads all plugin configs. - - The raw configs to be loaded. - Returns a dictionary of loaded configs. - - - - Reads, Loads and Saves plugin configs. - - Returns a value indicating if the reloading process has been completed successfully or not. - - - - Saves plugin configs. - - The configs to be saved, already serialized in yaml format. - Returns a value indicating whether the configs have been saved successfully or not. - - - - Saves plugin configs. - - The configs to be saved. - Returns a value indicating whether the configs have been saved successfully or not. - - - - Read all plugin configs. - - Returns the read configs. - - - - Clears the configs. - - Returns a value indicating whether configs have been cleared successfully or not. - - - - Reloads RemoteAdmin configs. - - - - - Spurce: https://dotnetfiddle.net/8M6iIE. - - - - - Initializes a new instance of the class. - - The inner type description instance. - - - - - - - Source: https://dotnetfiddle.net/8M6iIE. - - - - - Initializes a new instance of the class. - - The inner descriptor instance. - The comment to be written. - - - - Gets the comment to be written. - - - - - - - - - - - - - - - - - Source: https://dotnetfiddle.net/8M6iIE. - - - - - Initializes a new instance of the class. - - The next visitor instance. - - - - - - - Source: https://dotnetfiddle.net/8M6iIE. - - - - - Initializes a new instance of the class. - - The base descriptor instance. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Basic configs validation. - - - - - Initializes a new instance of the class. - - The node deserializer instance. - - - - - - - This class implements all possible MultiAdmin features. - - - - - Comparator implementation according to plugin priorities. - - - - - Public instance. - - - - - - - - Used to handle plugins. - - - - - Gets the plugins list. - - - - - Gets a dictionary containing the file paths of assemblies. - - - - - Gets the initialized global random class. - - - - - Gets the version of the assembly. - - - - - Gets the configs of the plugin manager. - - - - - Gets a value indicating whether the debug should be shown or not. - - - - - Gets plugin dependencies. - - - - - Runs the plugin manager, by loading all dependencies, plugins, configs and then enables all plugins. - - The dependencies that could have been loaded by Exiled.Bootstrap. - - - - Loads all plugins. - - - - - Loads an assembly. - - The path to load the assembly from. - Returns the loaded assembly or null. - - - - Create a plugin instance. - - The plugin assembly. - Returns the created plugin instance or null. - - - - Enables all plugins. - - - - - Reloads all plugins. - - - - - Disables all plugins. - - - - - Loads all dependencies. - - - - - Contains the extensions to get a path. - - - - - Gets a path of an assembly. - - The . - The provided assembly is null. - The path of the assembly or null. - - - - Gets a path of a plugin. - - The . - The provided plugin is null. - The path of the plugin or null. - - - diff --git a/packages/EXILED.2.2.4/lib/net472/Exiled.Permissions.xml b/packages/EXILED.2.2.4/lib/net472/Exiled.Permissions.xml deleted file mode 100644 index b9bde10..0000000 --- a/packages/EXILED.2.2.4/lib/net472/Exiled.Permissions.xml +++ /dev/null @@ -1,283 +0,0 @@ - - - - Exiled.Permissions - - - - - Adds a permission to a group. - - - - - - - - - - - - - - - - - Adds a group to permissions. - - - - - - - - - - - - - - - - - Handles commands about permissions groups. - - - - - Initializes a new instance of the class. - - - - - - - - - - - - - - - - - - - - Removes a group to permissions. - - - - - - - - - - - - - - - - - Handles commands about permissions. - - - - - Initializes a new instance of the class. - - - - - - - - - - - - - - - - - - - - Reloads all permissions. - - - - - - - - - - - - - - - - - Removes a permission from a group. - - - - - - - - - - - - - - - - - - - - Initializes a new instance of the class. - - - - - Gets a value indicating whether the debug should be shown or not. - - - - - Gets the permissions folder path. - - - - - Gets the permissions full path. - - - - - - - - - - - Gets groups list. - - - - - Gets the default group. - - - - - Create permissions. - - - - - Reloads permissions. - - - - - Save permissions. - - - - - Checks a sender's permission. - - The sender to be checked. - The permission to be checked. - Returns a value indicating whether the user has the permission or not. - - - - Checks a sender's permission. - - The sender to be checked. - The permission to be checked. - Returns a value indicating whether the user has the permission or not. - - - - Checks a player's permission. - - The player to be checked. - The permission to be checked. - true if the player's current or native group has permissions; otherwise, false. - - - - Represents a player's group. - - - - - Gets or sets a value indicating whether group is the default one or not. - - - - - Gets or sets the group inheritance. - - - - - Gets or sets the group permissions. - - - - - Gets the combined permissions of the group plus all inherited groups. - - - - - Handles all plugin-related permissions, for executing commands, doing actions and so on. - - - - - Gets the permissions instance. - - - - - - - - - - - Classe di risorse fortemente tipizzata per la ricerca di stringhe localizzate e così via. - - - - - Restituisce l'istanza di ResourceManager nella cache utilizzata da questa classe. - - - - - Esegue l'override della proprietà CurrentUICulture del thread corrente per tutte le - ricerche di risorse eseguite utilizzando questa classe di risorse fortemente tipizzata. - - - - - Cerca una risorsa localizzata di tipo System.Byte[]. - - - - diff --git a/packages/EXILED.2.2.4/lib/net472/Exiled.Updater.xml b/packages/EXILED.2.2.4/lib/net472/Exiled.Updater.xml deleted file mode 100644 index 21c3562..0000000 --- a/packages/EXILED.2.2.4/lib/net472/Exiled.Updater.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - Exiled.Updater - - - - - - - - - - - Gets a value indicating whether testing releases have to be downloaded or not. - - - - - Gets a value that indicates which assemblies should be excluded from the update. - - - - - - - - - - From 9f7360f3502103c11c2f759f0aff678dec95999f Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:29:30 -0300 Subject: [PATCH 006/146] What the fuck with the old .git xd --- .gitignore | 435 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 347 insertions(+), 88 deletions(-) diff --git a/.gitignore b/.gitignore index 4e642c8..95d160d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,95 +1,354 @@ -/.idea/.idea.EXILED/.idea/.name -/.idea/.idea.EXILED/.idea/vcs.xml -/.idea/.idea.EXILED/.idea/contentModel.xml -/EXILED_Main/.idea/.idea.ModuleLoader.dir/.idea/contentModel.xml -/.idea/.idea.EXILED/.idea/discord.xml -/EXILED_Main/.idea/.idea.ModuleLoader.dir/.idea/discord.xml -/EXILED.sln.DotSettings.user -/.idea/.idea.EXILED/.idea/indexLayout.xml -/EXILED_Main/.idea/.idea.ModuleLoader.dir/.idea/indexLayout.xml -/.idea/.idea.EXILED/.idea/modules.xml -/EXILED_Main/.idea/.idea.ModuleLoader.dir/.idea/modules.xml -/.idea/.idea.EXILED/riderModule.iml -/EXILED_Main/.idea/.idea.ModuleLoader.dir/.idea/projectSettingsUpdater.xml -/.idea/.idea.EXILED/.idea/projectSettingsUpdater.xml -/EXILED_Main/.idea/.idea.ModuleLoader.dir/riderModule.iml -/EXILED_Main/.idea/.idea.ModuleLoader.dir/.idea/workspace.xml -/.idea/.idea.EXILED/.idea/.gitignore -/bin/EXILED/0Harmony.dll -/bin/EXILED/Assembly-CSharp-firstpass.dll -/bin/EXILED/Assembly-CSharp_publicized.dll -/bin/EXILED/BouncyCastle.Crypto.dll -/bin/EXILED/DissonanceVoip.dll -/bin/EXILED/EXILED.dll -/EXILED_Main/obj/Debug/EXILED.dll -/bin/EXILED/EXILED.pdb -/EXILED_Main/obj/Debug/EXILED.pdb -/EXILED_Events/obj/Debug/EXILED_Events.csproj.CopyComplete -/EXILED_Events/obj/Debug/EXILED_Events.csproj.FileListAbsolute.txt -/bin/EXILED/EXILED_Events.dll -/EXILED_Events/obj/Debug/EXILED_Events.dll -/bin/EXILED/EXILED_Events.pdb -/EXILED_Events/obj/Debug/EXILED_Events.pdb -/EXILED_Main/obj/Debug/EXILED_Main.csproj.CopyComplete -/EXILED_Main/obj/Debug/EXILED_Main.csproj.FileListAbsolute.txt -/bin/EXILED/Facepunch.Steamworks.Win64.dll -/bin/EXILED/Mirror.Authenticators.dll -/bin/EXILED/Mirror.Components.dll -/bin/EXILED/Mirror.dll -/bin/EXILED/Mono.Nat.dll -/bin/EXILED/Newtonsoft.Json.dll -/bin/EXILED/System.Diagnostics.StackTrace.dll -/bin/EXILED/System.Globalization.Extensions.dll -/bin/EXILED/Unity.TextMeshPro.dll -/bin/EXILED/UnityEngine.AIModule.dll -/bin/EXILED/UnityEngine.AndroidJNIModule.dll -/bin/EXILED/UnityEngine.AnimationModule.dll -/bin/EXILED/UnityEngine.AssetBundleModule.dll -/bin/EXILED/UnityEngine.AudioModule.dll -/bin/EXILED/UnityEngine.CoreModule.dll -/bin/EXILED/UnityEngine.DirectorModule.dll -/bin/EXILED/UnityEngine.dll -/bin/EXILED/UnityEngine.GridModule.dll -/bin/EXILED/UnityEngine.IMGUIModule.dll -/bin/EXILED/UnityEngine.InputLegacyModule.dll -/bin/EXILED/UnityEngine.InputModule.dll -/bin/EXILED/UnityEngine.Networking.dll -/bin/EXILED/UnityEngine.ParticleSystemModule.dll -/bin/EXILED/UnityEngine.Physics2DModule.dll -/bin/EXILED/UnityEngine.PhysicsModule.dll -/bin/EXILED/UnityEngine.ScreenCaptureModule.dll -/bin/EXILED/UnityEngine.SharedInternalsModule.dll -/bin/EXILED/UnityEngine.SubsystemsModule.dll -/bin/EXILED/UnityEngine.TerrainModule.dll -/bin/EXILED/UnityEngine.TerrainPhysicsModule.dll -/bin/EXILED/UnityEngine.TextCoreModule.dll -/bin/EXILED/UnityEngine.TextRenderingModule.dll -/bin/EXILED/UnityEngine.TilemapModule.dll -/bin/EXILED/UnityEngine.UI.dll -/bin/EXILED/UnityEngine.UIElementsModule.dll -/bin/EXILED/UnityEngine.UIModule.dll -/bin/EXILED/UnityEngine.UNETModule.dll -/bin/EXILED/UnityEngine.UnityAnalyticsModule.dll -/bin/EXILED/UnityEngine.UnityWebRequestAudioModule.dll -/bin/EXILED/UnityEngine.UnityWebRequestModule.dll -/bin/EXILED/UnityEngine.UnityWebRequestWWWModule.dll -/bin/EXILED/UnityEngine.VFXModule.dll -/bin/EXILED/UnityEngine.VideoModule.dll -/bin/EXILED/UnityEngine.VRModule.dll -/bin/EXILED/UnityEngine.XRModule.dll -/bin/EXILED/zxing.unity.dll +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +# test + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory .vs/ -UltimateAFK/obj/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog -UltimateAFK/bin/Debug/ +# NVidia Nsight GPU debugger configuration file +*.nvuser -UltimateAFK/bin/Release/ -UltimateAFK/bin/* +# MFractors (Xamarin productivity tool) working folder +.mfractor/ -packages/EXILED.2.1.29/.signature.p7s +# Local History for Visual Studio +.localhistory/ -packages/EXILED.2.1.29/EXILED.2.1.29.nupkg +# BeatPulse healthcheck temp database +healthchecksdb -packages/EXILED.2.1.15/.signature.p7s +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ -packages/EXILED.2.1.29/images/Exiled_Icon.jpg +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ \ No newline at end of file From d8f30051be06b3f5cf6078b0dbf68ccda7636c91 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:33:38 -0300 Subject: [PATCH 007/146] For diff --- UltimateAFK/UltimateAFK.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 0fe4563..cba75f8 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -28,7 +28,7 @@ public class UltimateAFK : Plugin public override Version RequiredExiledVersion => new Version(5,0,0); - public override Version Version => new Version(1,0,0); + public override Version Version => new Version(5,0,0); public override void OnEnabled() { From 37bbe7a7e0c0fe3cc2996b9bd7a03ef3093a221d Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:40:28 -0300 Subject: [PATCH 008/146] Little Upsy --- UltimateAFK/Handlers/Components/AFKComponent.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 7e9cee5..acd9143 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -211,9 +211,16 @@ private void AFKChecker() } else { - //Do nothing. + MyPlayer.SetRole(RoleType.Spectator); + MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); + MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); } } + else + { + //It does nothing because the player is dead, so why detect him as an afk? + } + } } } From 3026e7e10c6fa2c18637dc1c745ddf5aafb64652 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:52:25 -0300 Subject: [PATCH 009/146] Update UltimateAFK.csproj --- UltimateAFK/UltimateAFK.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 8efd758..9eed0c2 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -35,11 +35,9 @@ - - \ No newline at end of file From 064458d1eb6b214f408242836f6414e343973017 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:55:54 -0300 Subject: [PATCH 010/146] Cleaning up unnecessary using --- UltimateAFK/API/Base/AFK.cs | 8 +------- UltimateAFK/API/Base/Handler.cs | 8 +------- UltimateAFK/Config.cs | 5 ----- UltimateAFK/Handlers/Components/AFKComponent.cs | 7 ++----- UltimateAFK/Handlers/MainHandler.cs | 4 ---- UltimateAFK/Resources/AFKData.cs | 6 +----- UltimateAFK/UltimateAFK.cs | 9 +++------ 7 files changed, 8 insertions(+), 39 deletions(-) diff --git a/UltimateAFK/API/Base/AFK.cs b/UltimateAFK/API/Base/AFK.cs index 9f75f22..b41877c 100644 --- a/UltimateAFK/API/Base/AFK.cs +++ b/UltimateAFK/API/Base/AFK.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace UltimateAFK.API.Base +namespace UltimateAFK.API.Base { internal class AFK { diff --git a/UltimateAFK/API/Base/Handler.cs b/UltimateAFK/API/Base/Handler.cs index 00f50e3..ed23ccc 100644 --- a/UltimateAFK/API/Base/Handler.cs +++ b/UltimateAFK/API/Base/Handler.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace UltimateAFK.API.Base +namespace UltimateAFK.API.Base { /// /// Base API for allow create handlers in a "easily" way. diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 4834ecd..67a5636 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -1,10 +1,5 @@ using Exiled.API.Interfaces; -using System; -using System.Collections.Generic; using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace UltimateAFK { diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index acd9143..6aedd86 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -1,13 +1,10 @@ -using UltimateAFK.Handlers; -using Exiled.API.Features; +using Exiled.API.Features; using Exiled.API.Features.Roles; using Exiled.Events.EventArgs; using MEC; using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using UnityEngine; namespace UltimateAFK.Handlers.Components @@ -225,7 +222,7 @@ private void AFKChecker() } } } - + } #endregion diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 2afd80e..41adaf7 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -3,11 +3,7 @@ using Exiled.Events.EventArgs; using Exiled.Permissions.Extensions; using MEC; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using UltimateAFK.Handlers.Components; using UltimateAFK.Resources; diff --git a/UltimateAFK/Resources/AFKData.cs b/UltimateAFK/Resources/AFKData.cs index de40761..74365f5 100644 --- a/UltimateAFK/Resources/AFKData.cs +++ b/UltimateAFK/Resources/AFKData.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; using UltimateAFK.Handlers.Components; using UnityEngine; diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index cba75f8..eda284a 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,9 +1,6 @@ using Exiled.API.Features; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace UltimateAFK { @@ -26,9 +23,9 @@ public class UltimateAFK : Plugin public override string Prefix => "Ultimate_Afk"; - public override Version RequiredExiledVersion => new Version(5,0,0); + public override Version RequiredExiledVersion => new Version(5, 0, 0); - public override Version Version => new Version(5,0,0); + public override Version Version => new Version(5, 0, 0); public override void OnEnabled() { @@ -50,6 +47,6 @@ public override void OnDisabled() base.OnDisabled(); } - + } } From cec9879bcb7c1e87b19d6ec10144fc9f63d47608 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:56:45 -0300 Subject: [PATCH 011/146] Delete AssemblyInfo.cs --- UltimateAFK/Properties/AssemblyInfo.cs | 35 -------------------------- 1 file changed, 35 deletions(-) delete mode 100644 UltimateAFK/Properties/AssemblyInfo.cs diff --git a/UltimateAFK/Properties/AssemblyInfo.cs b/UltimateAFK/Properties/AssemblyInfo.cs deleted file mode 100644 index bb455c1..0000000 --- a/UltimateAFK/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("UltimateAFK")] -[assembly: AssemblyDescription("Ultimate AFK Checker")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("KingsPlayground")] -[assembly: AssemblyProduct("UltimateAFK")] -[assembly: AssemblyCopyright("Copyright © 2020 Thomas Dick")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9280B8BA-6290-4FC8-A58F-BC24E43AA486")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.1.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file From f2593a5362b093442ee4ba2a6b5bda34a42ec5bc Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:58:21 -0300 Subject: [PATCH 012/146] Old --- UltimateAFK/app.config | 15 - UltimateAFK/bin/Release/Exiled.Bootstrap.xml | 23 -- .../bin/Release/Exiled.Permissions.xml | 283 ------------------ UltimateAFK/bin/Release/Exiled.Updater.xml | 30 -- .../bin/Release/UnityEngine.ARModule.dll | Bin 12800 -> 0 bytes UltimateAFK/bin/Release/YamlDotNet.dll | Bin 207360 -> 0 bytes UltimateAFK/bin/Release/scp035.dll | Bin 29184 -> 0 bytes UltimateAFK/bin/Release/zxing.unity.dll | Bin 449024 -> 0 bytes UltimateAFK/packages.config | 4 - 9 files changed, 355 deletions(-) delete mode 100644 UltimateAFK/app.config delete mode 100644 UltimateAFK/bin/Release/Exiled.Bootstrap.xml delete mode 100644 UltimateAFK/bin/Release/Exiled.Permissions.xml delete mode 100644 UltimateAFK/bin/Release/Exiled.Updater.xml delete mode 100644 UltimateAFK/bin/Release/UnityEngine.ARModule.dll delete mode 100644 UltimateAFK/bin/Release/YamlDotNet.dll delete mode 100644 UltimateAFK/bin/Release/scp035.dll delete mode 100644 UltimateAFK/bin/Release/zxing.unity.dll delete mode 100644 UltimateAFK/packages.config diff --git a/UltimateAFK/app.config b/UltimateAFK/app.config deleted file mode 100644 index 97df915..0000000 --- a/UltimateAFK/app.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/UltimateAFK/bin/Release/Exiled.Bootstrap.xml b/UltimateAFK/bin/Release/Exiled.Bootstrap.xml deleted file mode 100644 index 5df0458..0000000 --- a/UltimateAFK/bin/Release/Exiled.Bootstrap.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - Exiled.Bootstrap - - - - - The assembly loader class for Exiled. - - - - - Gets a value indicating whether exiled has already been loaded or not. - - - - - Internally called loading method. - - - - diff --git a/UltimateAFK/bin/Release/Exiled.Permissions.xml b/UltimateAFK/bin/Release/Exiled.Permissions.xml deleted file mode 100644 index b9bde10..0000000 --- a/UltimateAFK/bin/Release/Exiled.Permissions.xml +++ /dev/null @@ -1,283 +0,0 @@ - - - - Exiled.Permissions - - - - - Adds a permission to a group. - - - - - - - - - - - - - - - - - Adds a group to permissions. - - - - - - - - - - - - - - - - - Handles commands about permissions groups. - - - - - Initializes a new instance of the class. - - - - - - - - - - - - - - - - - - - - Removes a group to permissions. - - - - - - - - - - - - - - - - - Handles commands about permissions. - - - - - Initializes a new instance of the class. - - - - - - - - - - - - - - - - - - - - Reloads all permissions. - - - - - - - - - - - - - - - - - Removes a permission from a group. - - - - - - - - - - - - - - - - - - - - Initializes a new instance of the class. - - - - - Gets a value indicating whether the debug should be shown or not. - - - - - Gets the permissions folder path. - - - - - Gets the permissions full path. - - - - - - - - - - - Gets groups list. - - - - - Gets the default group. - - - - - Create permissions. - - - - - Reloads permissions. - - - - - Save permissions. - - - - - Checks a sender's permission. - - The sender to be checked. - The permission to be checked. - Returns a value indicating whether the user has the permission or not. - - - - Checks a sender's permission. - - The sender to be checked. - The permission to be checked. - Returns a value indicating whether the user has the permission or not. - - - - Checks a player's permission. - - The player to be checked. - The permission to be checked. - true if the player's current or native group has permissions; otherwise, false. - - - - Represents a player's group. - - - - - Gets or sets a value indicating whether group is the default one or not. - - - - - Gets or sets the group inheritance. - - - - - Gets or sets the group permissions. - - - - - Gets the combined permissions of the group plus all inherited groups. - - - - - Handles all plugin-related permissions, for executing commands, doing actions and so on. - - - - - Gets the permissions instance. - - - - - - - - - - - Classe di risorse fortemente tipizzata per la ricerca di stringhe localizzate e così via. - - - - - Restituisce l'istanza di ResourceManager nella cache utilizzata da questa classe. - - - - - Esegue l'override della proprietà CurrentUICulture del thread corrente per tutte le - ricerche di risorse eseguite utilizzando questa classe di risorse fortemente tipizzata. - - - - - Cerca una risorsa localizzata di tipo System.Byte[]. - - - - diff --git a/UltimateAFK/bin/Release/Exiled.Updater.xml b/UltimateAFK/bin/Release/Exiled.Updater.xml deleted file mode 100644 index 21c3562..0000000 --- a/UltimateAFK/bin/Release/Exiled.Updater.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - Exiled.Updater - - - - - - - - - - - Gets a value indicating whether testing releases have to be downloaded or not. - - - - - Gets a value that indicates which assemblies should be excluded from the update. - - - - - - - - - - diff --git a/UltimateAFK/bin/Release/UnityEngine.ARModule.dll b/UltimateAFK/bin/Release/UnityEngine.ARModule.dll deleted file mode 100644 index 87dd0a89cc0e93456ca34ce37da2ccbe7661e17d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12800 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 diff --git a/UltimateAFK/bin/Release/YamlDotNet.dll b/UltimateAFK/bin/Release/YamlDotNet.dll deleted file mode 100644 index ac728f938d9c0ae20613d4d3d4f2ebf5c2ca5df2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 207360 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

-L@C~_VgzHYEi zu>AKTtuk*&dM7fp29-OJLx-HBsS(PfXubR35EmU+w@fWS^C3sFB-VOcetCk|`KXmh zRI$_PO5pLxp!=g_q$m$)S`NYVp_*i zm^)(~0sfset>>$HR$$huVIjuPy(ckORj?G=_;Vq0&5fdp`^f8S#E=@Nysbj9u9So| zw(g@Se}G07Td2uGr9sRR;$a5SXx%;$s~nLg-;~5^_BBt*&PpzsITgM zS85$9Qg#tozeEbR!`)+OoLXZ)1d2P>QC0+hc@x zSo@?xQh`otA@`96fNk$u)Jl}weiEdbuHt$b}CWHYg{oe3Yz(UOFC?n5Gp^fRpBhxk4Ty7C$5Gzx}`jZ zIRliF)$2nJ*S>u_Wc$b(+y)9a5)w%@cGXP`&xQtkaJF@=^H80RO^o`8&2&ycxik&K zLAYxjw4Qb!Q`)_jDKxJ79bunMjS&JI*W9W{f6kL9D^Qtp@20t5hnux>mk@{?*OIYN zT4rS2ZrO3#Z?wzqVn4&`X3-U+#mJDGMvn7HtGPgRR)0T(FxvKNXqfUKW=HmH3>8zL z>qP<=M`x6~zQ-eg(8S@WYPZwmqEJput6>DHY{itCZZw-XGp^0q zl*G}M4Yfua@=iGKBJ%Fs0r&oW5Qi?1c6%u3cBdb7i$$5dadMoJvmdS;0ny3MVi_s} z)jkY+`@FsX5!n=1Biy6dAm_p&r*Sq!c_We226d4bA~hU5r}Z<5R(dB;1&hi`iWZH1 zlF@(a$^&x6&YodlaRWzgX5fystG2t&R zg!PGtNipMi;D`mRc1D$HSM$hJKYisTv5TwQ>|E6H8j*2nG79Y&N6!0@j-Jia*`YvJ zt@5d>Pm7&t$J`=L^2*a_N!H)nruu zPr*m3cAgcTy=ShDrR7zRL-#wrc#LK9^VV19I2(rhEOwseO>P%*INj>O}rjOcGt5a9@UdK zyYGqmz5{joj`Y9QsY67}J5@nG217$npiK2Au14x~qr)zgb2WmC2lb>BEI<`x;E+3k zNomJz4{s~uRiUZot~M`f3p$Y;eR**m1Y7E3Pj^B%i``KD%sVqwqewm{#v$oQdE8xZ z2>8b#Gobv4ifg?aL9tl%$F4rXuqO~UG3xh*JE;Wk4l)9xz`?^_2pN_pemH{KOO~jv zKt`^7QjM0ByCOh4hw7iWjAF80vv50`ErGh_s#H$F6&5M9RrEr`mFcT-)7|DegYSFR z!=w$aoPqm1R(Pd}hK(_mUTo!bj9$oa@{aKs-gF~&9sO}*fA#9r_I9TfglPhX2+knf zBvkD2g=gA98bNjwtv4=Qn-x-Ipb496jqLZN7ik5~%l>%a+vU$vS7_}l-$6ZB(-ml> z#73i(@|UtQ9A$`xs+t=R-|I}&7^bk}d!gYFH}`D?R(oIr?eJ-pVt-}$u18n+wG%~& zGHW0Tu|q4ctF8?#XT<(ECjyunft$eN5XwHfNa2GyqGbhA|H>0sUMn`z3Vpoun1n_K z=$Wgq37;c!QY+AvSDsW@LJ{|2&y~kdBu;mGw-GN?M^VgaT}*8S`xQ`;6B|6lTA8bTYaaCLdT+`#pNQB}C`K{MnBzC16qGWtoDQG=b5 zrIC$H^#`s#JrZi8^GiB#=Q_^1zS3c0V=uJ$)wdw&uE^zWHjyyNHkY)VUI8MXJfqiP z^vZxr+Waysj@yS<$IX>bptyn8!se{Y_XVi))i9w6OuMOZyQ!s~TFC^LKb*SO>78q0 zFmvlZ`|m#uhp#|#L@f0?-;Y~F^sYXDpyR!`OSBDm-HLlg?*Vps6@sqK9VT|>>ML1^ zTf@u9d@CU=4S7yQpO_3OUh5OrGFF(#11Etz$U9t&c&VMp5HqPr&)`qumfs-|{y^Q> zu_xd8u^Bbc*8r|M`bXdS(ZegPw;jPL>1!GBS~&JjP?5N)^D7-eC4$?tw--$GN4;9G zoEqcmA}UN=&Mo1&1kV;22%_hu1XUlNt+zjZQ#aLEHMb=g@+bPmVNqgnz?h2S#9? zM@n}=2D!te*xDczW7g@pxSq9}b-bO)n{YXpedhh+n9DTUh3kY@;qyyNFV?| zM{?!H!Bq~8BwtNiL}2JUw?56X;f@2|C1A(y#O)NmZmUFerYgi!4#U z;4pNB3&zV|dV$^S_%N80KXb6$h1196()V3Z4BPOtO937Xu3B^b=Duj~G}x{c&Sm~u z+Sg;0Y)~JCYi0Xs1=FJGqne!L!pXX;ctrYg3*r$lTXKvAESR!GTkk{(mbxxHo`LjH z)(dX|y&Sd=;@wTS_Ab7Hd>_d7QMu>LThOO4$5S%?W3XR%5d9vIt*QRu7#2wGql-#x zhNs}B$2;xIFF8CtUCxyk%ASa(Q5luAvmC;9*>PF>K`h)|USF>MZbjqfF`$-?DtDP- zF-LKx`Xh}+|E@c}X$*Tw(M3rKXnVut=&O3xn-)Hny1l-m?JOp^XU&s6J6xO;C z)VVw~pa8G`Au)HIB4&)4eQNW2dSCwS13!9Ovj3;P{>lH#wBRQxR;6OGj#hkiZelXd zuY}|uY}=H)KeoZd`7b7!aRGRS-h8H^ZDApP~A_NtWGz?pPGQAo)KgWC7UlC@Q>sdy|Aw?i&o zSdHQ@#uLel8`<60F$N~_Q9d~R+SZO#Jf5sRzrn0cwJ;G%za(i?z|>LUy3{Hrl3vo= zVAiBsG_%2A@$oKvbzog_d=nX6Tt)fM)qjp7gMX+{TL@Ly zM!+utaEXa8GI5>asrGU#xpWFh6Fg`Ri>v)M7Pqtw2uXUY+O}YJsa46^A9YrrO4h!P z?3T{jH&z?0vGzy!*Jfg!xugeVCub9D+vCZl^D?p7j#O+*au%(yH)yqKeJsv`TY%l* zd(|4?jWhUk996iWSuVNjrmppIt&Sbi>ZoGd#@g1Pc2Y-3V+0v$o)b*9Cg&58nF84g zTGbv)KFLKr+0MzQlWFXBq7{UY0HI;bF7)4SFrUua->fn5Ey<(F+EYo79i-VOs*^&y zxY6;ht}R`w+LF~z;;XiPEmrVt?Dd<;>KoXXr3;|f>$qW9mH<1D5h$jLIsmeXD@~9f zuzUbWxYE*f=eoX}tX}pDnBnNJv|>@!%kj2&D-a3T8sImja{uPe37R2My#B@-y zF@@n!BLZCAj@nCAEPxB)lLNhhy3)yp@I=XBAaiMP5q;faKt4;FT8mj6!RoPAOu;6w zZnE94wFsQpx-Vr~e!hY}TJ3S9@8u@{M+|QuPbBm^OKKZEWv|$GehCmE_U~l1uxNOLNKEKGs~S z$oB{2TOo767dm={qJn}-WZ)M;vhAQVK@JKmeO+?Pz-CC=!k-JlpY!KO1YRP|Ej_1n z@;vB`biyaG&mF%+$;YSs^EVt>j^X#kwFWu%{G9as)7tarY|nhUo}aTlKZm*@QeqmA zNYBra-O0Pyll9OACYIPDY))n;0koMGFn&UieM!Eb6Uh$|RDDC#$>r zi;R<9@wUWm?Ja^tqH|Mc^}mp5UO>0VxdipZw*_=P>F0NY5^jGe%W>Jo29t<(MMBq3 zTiQiCZZI7!ZOQ;&lAehfI|wVkq%*)a`(VEdmcJz%l9+{U6`Y1&j9u>8wXeiO(0 zG(>^FSSz!H^|a2$6h~K4=2yINnRYidm-Dy8Hm+$h%6hAt{r<6+9fA)a;TFiB>scXW z1CTZ^W8XG#-dL{kJ+=w|i%h-@tY1$#7%ECiU6BmbIs6~fY`F!?A~EoZzHX7^I!+Ud z)oLm}USTdqq-jbv3HZQJ0P0L*928Pd8k217pbB97TVkQUk>gTc#h6LA1sD~C@2})l z(hmitbSWjBl5}}aF=3~RVrJJ1g@YCyW$OZ7^B170mbMLM9l8!?yFTby=Ss~J6Iu0_ z36(oZFR>A|z76~cPa!Q98-b*t2oOdLY9>!>rNs-(V|jkwX~Acc)n}b#Vp`O97g6&y ztRbono|O&$8ZZ)42G9CZf)eLhl{K%UE&Jfpm-WwrLa3}cj8^-Wt;VO5?JTuYiAnr( zvfXC=@hI<}1s@A~{ssn2B3MZ6yOcn+?>flkWbKqdPC3Z4$=X{2d5aT$12Yhkw#r=5 zR((@e{03IM-YI=oLh`Dstc&`#T@;_4!nv|bc7L?a-`P5RQkESd%}ccnX0s@8+eU%5 zZ5Jy28l_~oLzngE^Cc3M)ZoJ2#0ZTGTf86(d$Vz2ix>Qke#LeK16{Q7ckHD3v>cc` zO`F@VmprXC-?BCN?8Ne*vwgzbVAS%#3>PoxOflwZiZi!1=KT^R1kG+)AZ2O_A$HOI z&x)>jcCAP@05AblHz0wTbLxL1f|!;6hPu709e88Ai>3n~ zZFtcz3;ssY7vr5v-}S2m3F-BsS>N7*x}hrVW>q55)zw7>I@{ac3iB47{!_RYanP{dRa_i|`v| z9(~yLi`}A?94puQ;!FZcQbTv}M_G-;fwClsLzIla-SPM>F}QWf#jS8*lSglBUk6mw z?&3eMD}j;h3g(mZTavXMUCDVEjd1p`>sSqF1nDhZ?WP6KM4Z!OOSte|$;CTBDFDNT zcNt6#G(6Ac0_*514%(twS=-S@f4H_oycrBe2fEgpSdEU+1;`J&ueKXQqAA$A`Vxun zBHes0&4;S}0Ez~b7?4`MwpR7BWg%f3cp1`3ZJQu-+ZydE4RAZyick8*^F#oKn2pmb zpS87SZ7n|E1!-{c1GZ~aCK;0_S;YhP$Fb12zlwhm4Qrl4uB^46w@Ekzph?Ak> zH#*x_Q2+&7_`Klu;v*OmL(7w%|MS9Hz&Egr<`7i6Va&9_kw_$;#0G&A zMLNjQr;@dk9mv;CV(2yKqxK;b;(yHM;%R6#%;mM@#cXmZ%iUO-fy=f*37XmLS~f{b z3vVOa%p@=7*@Ug;l1oRV)u?mLLEj;!sM2oCl#w;sVH(^Q?ZWwLRX|nIjo4zJx?)onE`e zpju}wmoTfKmcTLS9NE>VU!$VpMpIw$+mouZ6sy8mG!YiJa~KT_ewqnihA(c{y<{CS zKg7-A_ATvep}-`@fJnnA5QJU9MqzS^ju!MBYmxp^T*cygRHUN%qK!UsUzbK*rZx{; zvl#ZS1Shw^v?0+6!7C_~q`49)0|3e>RAZfOuT39r=d9h((PX5bP!CA{lbuWy`p<6C zi`&_4y3xd@(2Kr+|^gQOZ;52v{wYT<%%@d*lUk;&v~N zi&PQwK}~3QW{4C}@=|{SZx^4F6uclcw!&qgVAg^Xl0gg%kk)cuwCO2JhMcWltZX&X^q2p+0vS5PqierU@(7smaqrhp2a6I zu-hjjusuryn06Pe)uj($uU<_qzNWjlv~3Hk;sZ;CA}Fz?YZFQJ)%tSm0W|c|Hmnk> zgP6kHAw1&DFTT{?-Wrd0w7?Q<j(}a917zgEQb{6ZZ-02n@Qmi^)4MFZ3?yvZuwr^>j-CL9$uCYP>DF0BH)E`B??_=X6KZe-B!B^qJu;aie@UFg0XkRMxX-2?7MYy^aoT6GG6#TY|H0>_0+WOQ*nS68mP9iiAp<{v;C@L0a9q*yQ4u zA(^<*BPjhc{;6Gs1VmH_i~&UQaH6C~j8_{bJ^Fcgaox7cQa2~hsbbFETqL``#B~C5 zlBx|0?5iCt1i}(`wqK7P^&jjxPH8o~2CnN8XkN1r6m6PPlc8%}dmD&Ks)ELDYQK&R zTsNE{*@V_1^=C^9#eN4U9-Ft3N;*0L;Z&r zZ-?YHpq(H;Uc8KW@CJ}s2P_mnBegUbj_F$GD&E%7VhrS`sCJFobZD$pgL{vf67>?Jls%#aG0Bt$qh1Ys5*97F-BTpPdZiiW8*Os&6vL z$O8o}UkAm-0}0Jha#Y_`$53I52v}VQ>2ca+S!MA8ziWB7gA%7&B=I4%s#AYGG<5_$ zS@bP{>|Qe7sXzuGIU^U5tG>yq-?qK{9iE7hhGU2D}-vLzd$JiYqZvg_S z8I93_W)Z<6{=?8M2)$8qfwtCgBX~oBlaqT2i=QU{FMfsw zx(#1IDQ$uCABS6vii|YCjNo})3%+sCjQJnM;^CA59avdxJv`}->r5-wb+UsW3Wkp4 zVoHXoZib+$eH3IMmMf!)V%@?#Au`2aj<5EMnbPugPz~q>f}3`p^!2oMEC%GoPudFm zLKQB_>6SHjN$=zfXH_v69xBq2Scz6#?Lj(~wPDDoR$u_B1O-j)D-b}umH@BcX-oEl zc$Uish71)TFhhgm6iFBMCX^y$(omv23y}v{jsY~)t2hnt!Bpj)wcqf|TpcyVegi## znL_1j#5R;Zffsl>4B}hau^~=R$4Q@d$O^c>Vml`hC`G5XJ>fw{6kN;kE+XD+< zy7Vt^E!m@A^No zdFhLDZ+`4I(}&|{p8tWdLy!H}7k>Yh2h#74|Kh3+%n5VB7ccLu;uLSY`d`b~wvs=V zvFh89ssnHL$Glgev59Xrt2nz=P;a=Yz^`_Gt>)Jney!!#b^Kb#uLQrY=hu3Eb@D68 zuMPa#$gfTOx`AIe;tQ9N*5GZop4i9j-)+;G2ot2uQZ!0m+`lw%c1m`R~f8S zO1a7Tinl#ATh0_q`P}68)I(m0Rqx%^)64(2r-tV9mHCo)Z^4_#U%$?8PmRt`=5rbR zDem#&8Lx2foSQB;4v&ep&1a zjYP)VR&%I}?W~Mgq!dAnB9J9LeVSn}V5H_@N3>KUFK!Nt+liQ``L$hE>;fW$Tq0I? z`Bk^I;?mX9ws>0$3TvOiSM_Dqm}3ofDMR z;LZFRWp)?Fm}TN%&`ko{!Y*nbV!>&q$h{Y5_(lF%OgS1uNQwCu`PD5fU=^zBV2p0H zH}c#M3At=%KhN6n7zJYM0eE3hAJ&+ZnP9AmKg&7& zj7$#G{p>ni)&xvus!)*gXJK)o@KrAN5j@*44h~K_JV))cK>*)l5mvCy%!b^b5eCf?yx*5x1RdMB_Yq>-OBu`-= zH1llN&)Hs{3-|I|xR>Xmy*x)V@)VW;BEhp2)2u!%->=GD4&FhN1h0%qh>k_X8f#VUf0a)YW#^-+?Hzf8=zWPMOwWH9m*xa`QRjYih};0$Ncz^kp1FI z1VW#p1{A7fmc}KM*GwJ+52eL6Gnl0$=3JuU66amw0uoBJRirkhR>3F1W?^AYI<3d; zX2uU5S&zdTo^Dp3U*8*(d&jbTdMp?kV9*n1`Z5}@_+_{x0$IGE5IlMKX&up*z&mS5 zcue4M6uZE)qJ$idVhWIwpt{wHWe86YF3`?`>epn(a7nALD>)PRGhLVkW(T^(#auaM zMFt65gc&JG3u$r(ag#~T`e1RWc$BilIrO!rToU0HU>RJYTNw??sVI$FF ztbsC^CW+Km5%INE9oP^)Wx61(Tg(=6O&EY8pQoO^)s<34(1wa~#Is;^c216};RFvf zgeN#e0iEOIaX@Y{@6wKf=7IbEZZ}5YvDS@wz~EV1R)^G%im=p{omj8C1Y2P}`4GDbJ(F0+uj?TBI>Dph+wl#cO*9u<(>`&9l$6g~9ykpE z?}C2gUF#F{<`W&*ah`noDv5N(GjKHKl?e8qShYTGVr`(%Tks~rdQKT^@CiTlOdC`Y z2+_YS@C^15Dyw=q5Kfof^sAo~@{{9vGWAQzw5ZKl?mcp|SRJVUdDQ2_Pocr{{@Vyb zGN}GM>~CNWbCnGfTD$04E?$7z(Mnh#<*CRNN$YH$2lP7seF#6pp^(`W>N${~>wv<} zaW|qHtNNz)BTZd~lLsHWu@Dgp%?Fq5{rR|(%SqgihA!XS8*2q;SD%+*C_2=}zBqnU zZ%mfdPyjvN#}ho8#551d zZ&G+eNm@2-=@H$a&!O)))plo&q1vuBojE?%cIK{^B|i%R&BIp@8V8h|CnDgbfTXr0 zKzn+C41i2#aXXEN#q9|jxdxt@)6g;&7Ed6AzrBMeb992V%?I?cO&SoeMd+)I3b+XmjPs&g&I8UEetHqS2)BqFiI-hK3ZN4?BBPDRsZxwMH%dl; zLRnmYJ^3MI5+{)t^&D8q$}%_yw?*hp+cW6Jk!UdMh*-Uh6EV5l3?|U6vAj#VfJ@Q= z__>g)7zF6Z_xmq5k+9agT z;8>dR1Nf({&aVrDp?!vpH(3z{2{4se!5gOSIE*vBQE z@KWy@gHGY}qCmPuwRjv75oaa_2Pf-!(29sICfbo$)f+dkF;?NFthhWegAR;=;brnr zsJvFwg_a$Ro*H?Pil<5>rloFO$ScrV9K!`lPGbke6R zN@yjb*En|)2WpG2t}`T4x3mt!QuK)dSuJi+zxebTNOl@$2ohtj(d6lN>C30q zl^V94u=8uO%Ei|pV6gVrv=-TY>7bM>-OrJ@Qv8z^ugcE#u7^-nOxr@CQb9dejIv$& zsOV!(A1b4kR4OgaC@`y!QGFb_KGg>Gi{R3@4ohrnzBAQ<6qmz2(Os)1W-#lQGMq4aZd`~Vw(f$^POBF#&2DsbWkzHf-42`mV2aF=VRpb8}M7TVHo zhR_#E_mW3d|Gfl*MsB7-MbYafZ!&@V zfUigs-2FqIDim2DXgePGhTx=mq!9qiHo9g7K~9TUwl#o2orr64Bg=Vq;CG?zap+Ro!s9@s&Karklg71Bg64KYqWy znqbH&yb{Fx^Y{iG@Ee{Qzw_v(i#>QqbX>31(UZNDiGtcH?OV)K)I@Z)o7Nz!QjJQN zyYLO9x#6JRWIj&4N$I7wZX&j$Lmo)^{f3j<*n0;X)E;B9i%Cd6es@bhui+bn6>*?>oACE$er&TiegPSq4=0oY({J~=7n1{EOyJF_X@fj}_z*2~mm(ryL{Lsd| zF*7h!oLea6re`Xt?#x}O!9t~&D|o5F`N`5kDqYB?GR1-h+KZ)fs=v2)AhiRJzP-lO z(lJweH0Zcz+$&c?5%zm~d;53w^1odJ^4P1n-rjq9(GmaC^4%w|0V=_hwhdb=klz5f2*-FtTL-qXKlD&q~Hk6n{{_D&A$-8Ge-8rZXU zXLg`>YEO1@Z+h=N*`4?FPVVl@pvj(r{_Nz={&YIydAl-`yC( z>MzDj^#zg{@Rr7rOKQ@aNuSLXOQ~ErRVbcIRf?%}{#<&YobqOK6~Efqbbj6|Z%8THIrr+DQ{}Z%T#(&<1DK0UWl}q&wJU_~TS_!Aog7Wi&0$f^yXoI)%%PYWTtPo$pqzB6c{h|*_s7i8O1kkr zI`3f16Hw({ zp(cYF%6)d*y!w%tIUK5fg%LmoSL-YrGZP`?M5O{zaS;2jk^-FHGgvA?%+!JJ7APf! z@W^lhSb_8?iiqgnZOjK_W;is+5ZJxvs8^cC?vJJ+PfB<5f!y#}(Ap50Svc?ReTPcL z*}+0)M*G4a>pT`SBX6%Oa6@!vec)MpEW$&P(6I>8?z}`w?0Jb)S_gg0$~a&_K9q|z zg1ohdFEJ$DC}?ENyJP0!862!b)2{>L6we)n(9F%{JpfjZ6lQ>JNEcQ+g46P=RoevV zi?evz4WY(>et>gqfYQ=vgjVlrfMWP+ceCMRk%78n=JpUR%GQA1p(QH0$-Eb^#b)$} z#dGYsRj5+O)aD< z3v*to0KHdMbyL_u!JAE&vOOu)V_r!lawR#{DPYqW$`>o&kn-57epn> zwzWSY3IspUvL$A28Y<2gDv+^HWM*>tY{@J12n3}!;dF_zpQQOvuABq$AAk%GwjSQ6 zPZDtYJbKSLP<2h8eZrHIpd%B;gj`AV%^sF}{0Y*unbbF84g`AqDJ^tyJ2tNEN5 z$yiTsRGQK+Zfi=11!wDfzZdVOA6&b7^mSzhTH0lv+abHwx_;>DCz8cyQrxnJkDn}y zdZeUK2pYV6Z5Dq1SGQYR!1>Z8fEAB_cZs9ogH*!3 z5`b=zIrfL%gnc`OqsFn9*sm!CAzY} zkv?fdAKS_}WRWFJj9?W~n>pjr*=tV9=!QR?@o9`Fmfjh+ADS@vD>Ew7wmxY`e|l5% z`$;3VTv~a}6BZI0_4zo14AJ;<#5_#6WhpOJVJ|hFfPZl3>(Leh+3f`QZ6V~0))67XTb}}cOnY4tO zk_adjxPS^?K)3}&?e7ODidR(7Rxc=CL5q5o3R14h74<4!y8c0Us$;g_`U6DRF=Cl+RhGuBhTh*~kl`>Z5d?IQ(!fc=UO z+h$S@ee^t{H5U;X!u{*P6!Q?W1{$ZE){r1zz!b$01Pu6DF$4hvl~N2rz{v3!f`Ebc zRT6@Lk?S!80R!Gv5`ut{=P?8UBjhmz0VCgI2m%JYr*sGcMxn3c}u^Yy(z z-wXA{wN)(vql!;SkEZrr1g#+?E;?h3eZ^Ww&hYEH)xJAa5(@Ovf!&bvm+|jsJ!!jVdnh)60q5kRu(GSF0*8NwPDm|C8H*7|0YfWQ& z)L&Nb=EZN*{9d2=we5Mzd(|y#ee7zFUE9*)W7l}>s+KAryQ;lHDXD4aVL4j4Mjpp& z&}VrZTkdjHdYr@m!ck~fH6RSJMc*Z(`Vf%3O?}nAF~`$Hb1o*M6;P-9?ra#0b)dB2 zeDDrq<_XMfu9|fisevCtkrroGX#)@OYg3To=`lrHs@ws}e$Jf&0ZdZn~d zdTdj%Rqd{WAy$=p0d9?oJKrm6uBWNa(;fCy=4q8p>@+kBoW|{pTIqJ~1n=#&Gi$t1 zG^#aV->A-u-mcE(*{WuKARmESP|c*&6Ad~HCx7x(e^;l@HWe+K^u1=x8<^qsd)|CS=Ge=svjNJW%xQd zR(HEOVY#T{+br~4%Z2Tssnc4fXl!~*xyFiGV2h-dwsQx(R7^oXFbT^9Z>vfxLvyD} z0AstCAvHg@yy2a=H`%%F<+$5bmxgQ=ZtVm)?|a$MOVtSj4Lc~zCr~!`Fya^gT=8z6 zD-gk9KH-!G2+nOlY=XJs@C-0)mzBD^7;xz_M+NC8&oxj02;t_G}>bKTw0Te;#xP>HHu)c||#{KHW$F~}Crg0u>%Fo!9t#_@QEscnn_ zn||{Te)wFj>oBtiK%;wsR#sNJm_kD?HeYiK#8W{RKG0sw76rrX^Y_2;#vANIlkR|V zV3o>n)^V)ew83m@whtr74Y;TsJQiUz0rc<`U`-{q+Xv!M62Nttoh<;l?52GC@Hw2N zA89IXZnyyahW(&zHwD;KpouhySq&L~f+8dV%|dmpHq~OThto>a&}fHjbEqBa154U? zNm;@kwhuAKTK0a%Eq4I=h7bn^G~Yb{MpZ)>5~n}&tqm*eGd67TMd+Bx4npM6l>qJ_ zK>l2Wvkg}PD^NNlJ$UF4IMe?Q4Z$vd4+uyN;nsZe$3=*LE3xv`x!8(?UeMVv^g_ez zjqy&ZRRTUXI;kx_n>1ygdo@&Pt;0YoBd?>j?#Nhf)ui0&30!Hx_ag71YXBOq1!xXe z4RZslGAiBcvYOuqbeMaDE+2jl4M#@lRGmrvs8sCN6zL;TtoF`i)oCi9v@TOB z(-@R?7B27o=&99jYknMChp5>Y00#VUUIJKRHKeOd*nradYqxLj`TjSz~fK;Ca2 z8pFQSXD)a7*0Ko_bBo8xsLpTsxUA{$O(>-S%beZVgP04J>20|sfruPKG`<_l&xDkl zAv&w^a2m}K8b(70(zi0kOGEmDjLqa4FEkrAK)Gz!XQ}!Jh+~3$o{KPKWG+I;90)Wo zvh72RskvtbFhyIKCon^WrX$<=n0GL!{PRxZSgFQmO*Oaj$Sfb?q3L6|A0A6rxwnDQ zcuI@h4#L`(YRA|MXx2x-I`sDdX-xG{bthn@dl%rW#=rAvw%m_O`sl0>aPI~K2N2n* z-ju3UaYa_T_i(DLA%`)>_+vKw1FIP3wZKfB4!4 z#$LcaxetjA_p`(y#a+zYk1_WFA9o>hKhE4w__)iM`$^^=^KsFO?eza*?*I02(V6Y^ zgUo%%$3@%O>4%y7DIXW@Xs18T+(&%e^O^e@=6=@4UCG=>nfo~(cNKI0k-4AuaaS|< zG3I{3$L(P57n%DdA9pEpA7}0pKJFUkewn#n0YK;E^*lD(7RP;}S@)jsakK9Is*js> z@7H|Xtb6~-$IZI;Ngp@s-lu%rtb4!i<7VCa4Iek_-lu)stb4!d<7VCa&pvL}z2EY2 zv+jMy$IZI;Ssyp+-fs(+`)|4X9mdP0?>5$^E=M27EzY+oX2VG&N;_DDLC((x{Oe8) z+6!kgru!Vk)EUj;IZ#@4=`i2k=CQ+c(czmGF*Dio3hag*xL1m1Hf;)ArdnB78fTg1m9l0xwxstW$1pq|7!~3@Gul({nVXyPVO9^3V@-l`9)hSyHd+_POKJn{l%DL&!DZ#D?#O zp9g3-1<+X8_)fK2sdbZWK4WlcjjFWDGgjk5h1RPCuRSj`CDt+zVLlD0&E8Vg)Huh+ zm=AD^t2LXW4tNvizRVBVO}1UtlB#SpD@1T^Qn^W1ZC{fV!omA4lekk^cO%qHhb~$W z7KV;T)Jc**DnXaF8-4`oAxtQIlF#K10KL%13C=E27tGdSk=u#B{HM|7eXV^JVwIG;V8fe%AEYGb( za`O{E4&^n0fD1w|_}d7xt?oWpdAN<(XVONDs5V-9^2{4=R5t3k|0i@oqk%3?gg(WY zeC@`hYB%4|!GeUa1PhQJI<&g$!qo=P9Qa`FrUUa^T3gy%+uP8;_@p5LxU-JvoWn%l z!2QyC;9Z$iJh?Z`5)pKdCJVfayJ!Q7ECKJF3oh=&N|&gz7_hlTMm(aCr0 zx-UPEt$y8TC%A-5N9QMq5=@hIWCQLj0*CT>2i=Z~<pRg` ziRKPB*xzz++2zmmy+FEp3} zUlGoI`N=|?MoN=aHdzG>OY#@jqm=6FOG4CMn1KAhg}B^%jOWZET32wOI80ZE+14A& zitya%=9246D(M?R$E>B!infYcT2#sKcff7B$hf|wmR<%PrdQyeTDn4HG9q(};NKSb zfWXfS|4V}3Cio_S?*N=acN(Y4K2J5Mb(qT0j$fhQ@(`e9q3cU*ii?-t3p;Ik3gu3x z?-?)U+4OjjQ{O1N8@2lq{ASZ7kO@;g+#Mzpz4$lS5pa!SMp4Bq`cvI8r2ab^dKN{B z82>ox8>UwDp6T?tYSwwAlxw}NkZXMi)=r1VUdn5vM}qGQhv=o^&Z=742Y!gQgp1&f z0PGLbA}R6jp=~SbeqelHhC0zEX9y(TDC-i&pyz zY@23&rD_T-DL+*5-9pWxE|GlQcpXD~7F~+mCG?xZSL?RY9EoE}$S7i}QC5W#D$>** z(Q=P@mC0!_vZj^LvnZXZ9Cvh*QtPYtM&Qe!A*0X#0MM z{c}|<*CLDC^kn5NlW=VM5bRfZw?yE6dL{2RYc@)~R8#j`6`-O*y)XFF$|rJ6ifif* zl~007X{x#C7@k30rK!b5IiRl5)MK^Ba4vm=rk<_M0rg=`{a5ZW%(WlYR9-L#)B~C- zFFuBu;~`Bo7w3TbM@{`Fbmr2RHI-kN1M2IVx-v8yXPV#E)QzDEP(RevI;7>%X-(}y zS{}Wsse6iMV|4#kQ=ciS0QI`2{!ld=XA|Z$!smU#yy^;2`I^cgUjdbA>Im``P`##Z zES`;+YKErnF0KI8tf>#z&Bj`@KvN&9s{pk^Qxzz$nAT~ksj33hW=%!HvoTtCXlik| z0@Q9zeW-dil~S*!?t`7Bv|m$S%zYd)<)EgX&HW-~rT1#;*F}$;W%L0}8O2|uGWw9F zoSIUX=)$Rs_0ow zy&q{+^aD*@6ui%@rk6D31WTxzey*uHq}9-CnrcT{4gFbB)lKCF=DC{?5TT7g{UE27 zKB*|`sKcy6;Te)fgSju})KROZwjiyZHfgHA}x^d3$1)xDbY4q84-P0kcr z)1auM!9V0or4MUrdf9*HMCgy2isuIGY1GuHB>$L~Z%?OTP0gwf+vm_9HT9>08hZw< zpQ|Lx&4@jd9@NyFn%VX&Drr(_Hw0Vk*_6@Le-|&XQCKsl-AdoCTyD>y^_p5%vJ%us zH5IJlv>RKL;)a~?waUtUWM4O@xm37(8^pSZ=@{@I!+vn0h2sJ`(I0our zO?^GY)NxJiDC-0DPTq{t2&K$EyOlDUii2vSn}xcydS3Z*dmepCr(K2FWgZUR2h zFpae7*mSXFGYW@re07PY%7fR~3+X0J?JBy-UP6y($|)VPm(zEIdWvqa3_6c?V)sMz z6g?u;x3B>L6BfRiEVf^n0Ohr|0YLx6h|`iE~v6t7w@}x6^CJL-s0Kt*IA`Ay6BHQYEaW zOMUrP({7=zp;h!5do_JrQ>DgZb_YGIseC$auc0q%DhA25^kYqZ8q_+f;DDB{q3?jY zfSNQl-}sEZo)&59Q=mF&tx(G93u&WH`!}@Mg|vG#EeDh<)XT<$A%iZWjHVvN>~;~A zEaUQC&MT~a+`NdY6=f{0VacgFZFB7+P)$N{c{!k#X-U?(fmUjYb#9;sbw1X)fgaUq ztaAf>NvE;S4fGA6l+F$G11-rqH_{85Vx1c)hFyzi=SE5>O6=T7X`RM8H`1_B$}bz~ zdZDhNKfo^=p~0Zv75qJt}^AUk36yInnkI*B=SI~#QU~u0ppk(bl;5*R& zzhETMM^8WoI{~w)yaVeRX-sO^ax#X>Z446wl3dT1Ic&MDsyFMim^fC->zP7tTJz^+h=SSd*&EFD*k{m$5@J7b0lw@ z=xGytiQr3&NZzf+R;f{^a5^P*tKeG&zfADU6mGu1_9MngI#GJmh)S-g`OD%@7*R=` zLBA~itg*ywM7^G-(`cg^^e`gTCFab~SB;%w=kxSf-4BeI@b{Ze*)NS6^RAlLjC$eJ zi-tk-Xvv?A5%bwn(;P7~r9pGhcu%;<92ChJ(o!esU3n$&#%IeIK4H!@Z#5qe&o%SR zqCx|lt%$+zFn?OrY9_=R3CbwzC=0<@3ga!Q+tJjh3nxJ7>UG*qaG# z5_poT^S+5v&R|79VSWG+@vY{L>hGfT*UElqzG@a&FPgtae?AR8kDzzGY+hgX3&=lF zR|5GH@KT=nVD5h+lH~~Dw-_TOfDhvY$FR6B>v4_$E_j#qPa40X=F`@<1fOPY zEPT@Xp636m`jqv8#(!4w8|$YU|9oK}@GFfMmDB{vC9=u0o`KCZ*5$A^&uW03HP&`m zSYw@rZ8g?9Se0kp0efn!+h9qJ709~_um`12vv#7~8Y{2n6rf%D8^C`;DK*wP(EnTG z^Tj0?(cB7y=5hFQj&*I#!a$yRSMBn^t!AifZD5IYUG;^56XsRe?V=Z1_o4N-)N>zb ztiLoc$GoEaU4c&c)}Y^`U%fx@XX#hRjOTOj2$=K$Js1enL-a84M}V8?3l9d0=-=tH zfe3wwUP1iKa5milIEU_{Pvo^=*Y%3sg1yNv0qf}Zfag$IZVSz&dcbyS09+=pL*PZi z@1hp)FBeX?z?iV#yKk=Y)<5 z=U#!wz=?;B3w}c2*XT!daqwyI9}k@t&dUOS0{)5MtKjG4lfgAL1X{+AXlY)aaTjgK zj|gX)z&YStm%mc*RRTK%c7p%4{M~{_1$GOJgHHv+f{zHiM&J?fmlhlq{9b|g3p@t? ztp&#gKOyjI0-px|X9cGPe_7y91ilJ>eIc1*xj^&{+Ef@3e44;H;5=2hQt(v*JHaU} z+AVlgU>ux_iiQOr5qJcg!$n60zgOTfa2_i3p_1wWzlz8 zM&MC_#|54iNX48Q5x866u)w1Nj|)64FjB&jD+TTr7(|@&)xe!N!8MGBj4v237!h-| z`6=@;^GS1NpgR!9h&6CpQ;P_s9+A-$L_`rpz@M<5!U6f~mEQ!swCFj&=V}=i1{t;$ zGc2xScuR;Sy9NGZ5#xo`3~v(r-eSf-S;w%oisAAw!;g#1bGiQlJg1o9kBb=Y5&X%# zQ^0Q!7!Up!_#%Nld5qsv#_*FhEdNT)zn&H6npwC^K)!N-2LSn-Wx2p@KoeIU@F1Xx z3xgQvMK~kERS3KQ(8N^?ya>?5RSLWW(8N^+JPc^!;#0PAKoeIL@Jc`vR}JuLKoeIT z@LE6<*E@jM1Dd#|0-pkC;+h6L0*DA2^P+(>>~nyhLo|0IjJ3HW)S5QI${T#Xr=oiphK)-^%0{S(y71DnowGb;tGvFVPT1bCHY9YOj z)FL{ARNinj1DeLUfB~Zo&^FqUvJ5-N9n^^F0)YHO~w z((1B$ts(1!)+eyupR``F{*P4>xH@oaATZ3k2mj}FkO3d#{=lO+vF2S3_Xh8Co$#_W z(ft8+D(^|C`}XiynyEJISjOu+C_Lt8Kc}$o8UJa^V-6Rdwfc|P^^CXqD0Vjd)WSl) zwBTh6zO>*m3;i*G7Fle(pSq1B^d93&^t5pet|M|SHlCzE7~ci{kg-`+Z~2NPyLPp= z?rNpw7dV-1(PYf+>&!TPEB1Ko72Ugbt%;}m6VbtrL^PciS^bR+b!uNPD?vF(?jtB_5t3P@$+1kEBVpV2J z=rVv!Rz@3b8b~CfdlJs>HtJlP9O!dWn%a^W*c(rR-7%2PxP5C6ILXXJ>e2@}lR_x`AZ(?s?SJ-QTvToi2fc+*CVxb*78ri3HM6 zeL@>+EUg$`W!Agt%!(MiynxW6ZZZ>%C)-*VicZ{?Z*wzn=$3f*zJY#`lcd%qNE+)7 z*0G39rsW&m*Z@3C8`pNwHmA=$;H+Cm7o?)e4DPD@mQ`DDEOli(;lu>(i(a{Y3$5x) zI_Y?n3y0BbqM0Zhlj?JmgB#*|s4v~^rV>E*I+*6VAZIZi1j5Z%|QU{}%?%q*G(!;o3ooSC!Dm-d) z5@S(m7&TFbP`2RgumUfxO!P#QvQ>$Mdk~4+`n$I|V6BT^392KSNUU*qWFcD&L(MZ` z#lBs;R!6({;o<$dxRXGVZgQBCiNg117l`&n_fC?m8df=cqmxcgDx`+G6MGA>Npx+G z_f1mhr1~Zo-9CU36ptqS4L7lpDEZ9=);rNergvgS?Y<519;YhjKHOs{9h zL;wSw)bXgu+jQan4nrI4ta#L{Sz{jk~@bdxbww7!3$&=xnH zj$>S?HDY3aizYEhH#)I+cQkP}88!2(-jbeJM3CP2Mmh9^K=4y`4!l zNNLxuxQFr#;|aS%1Q1CMNR zyZ1SnH5k0fG$w9Lpv;t|p7Bis`y9%ytklU-i7S&Kp)&43CZ2TE!b)9mWWrG^1Xg*< zF8DID3n{)vnx5Oo@VXESaXzlpCg-5wDP~-pjAsVdCb6J9SYuL-TH};lv`?29P43&o z1h)r5Dn*>dpF9Db&8L!fR=a&)%4Paf@Ug9;Lqqeo_?r6V5Yox`u4+4#4 zQLeP`*2R;t&3oRBsrA1liGx)mLioO;!FGQR+2mf_BCP;c-)Hb~Q&SdS&-D zdyDD;XkLHRhcKXR*DiGW3~J^i9NaqGzJA0QDTxHYkFCn!U9>#|NV;Gk?xQYFVp)#& z#Ce77KpdHsTjT5**t?hcSsv705|?=^%UhLBJAHc+gWKa7zbHJGiegd0LY#3nM!S2_ zfhNh6ipHG2Xlh?JX?rvUXRX7p);I@Ue}Xc|NvScE&9L^$j2b^#mS@vC_}~&J#e*xG z*yVH&q|m9iIH|rkW?_Gtmk|MBPkiqHYA|W?8YkVIiuaFAR6ds++YnV;DOfsNuBUqo z;`M><%*2Eaw|_7d-`hKBQeS^GIhf6Z&Yi(P;xbXro_GQk%BFH}Lu7qPG%?^1R!cx{ zV%gwC4>&qHl?bS#FSgCuKj2`UipQMo(Y;8N?F*wBT%F1p(&;sdv4+V+%`7T(X40Ei zvK-xmwXrR_DZH#Q7=0N>Y4zTugmUOTo_JR;!dajAD9v%<`hk=`%_IF0@D>g}c?RZ6 zDgvW#N&6D`4Z)sHR$;dy#ElZt$iP<7jrd_B!uD)Jmf~p?^B;@L9ZTIl?Ks({jYdGC zP>UXu6^mhQ6;yeI z&`EIqK&Wjp-Q%YEq&AsRiq}&K7Sx=F5uuTO*<#Bu&N!=L7>%@b026I0iTO%wTZb?g zwy6bgT%h4q@l9oTRITM`8(Pki|7^(;wMqZv*;VS4;_WmO-t4ATp{?3 zVU)2xjxZkytY}qVpF%n&2JotDPQu?9?I&+yZ|>;oji&mQz0&HWMw;pZsCvf3)Si7q zJh`7b5>7Ow_u?C)N$d<$9KEHY-SE8}KzZEmNx5RW%wU{x!ikI4ytaprk<##L24Ni{ z1L~JJb0U92SSqb7m6913S-Y?W!;MQ;(W)1ZXbQi67ENdz!&PG{YSRQy&>HO*LsSzC zWP0&!U{;Kgfj*D`#VtOT(p_ZZpc3d7v5j%euqg}!CpCyAwi~-_kXYFKq<2t-z=V&6 zba2g-cl_jIweYbLwRFo(m$};MjUI@*1Drw5UDm{-dz08B#k@Mxfdw zq^UcLW@Fzm7Or!mnE{+y((05Ojde%UnRSWi-cb=b#7noxZB+_<*|lM}hOx%yd&1)r zpe%Kvi;&Mp^PpRka{K%JOy7Plo72BD@o`7viY(6y8L}KND#&tlERbbjXC3oR`2Ga; zYj)F}O&K$(XW4Hyfo7%7GEHU$uw!*3e9_^$o7&ECcP1;b!Rhf)%U8_bwJU?)chqql z=EF3)yAxv{H^TxB|Qb_g;vxSFD8dHFrp>mJW z@%U(8_vZYTY{<=~OO)wnjfM%MN16E2Ao?|?5SdQL_eOoF47TrxS=E30Knk5t%`l3i z<0X&9v&uMDPmhdvi}bmCp1>(`h}i^8N;o;AIX|xSE5NDm}e=Q-Ro- zChX$4eO00~SN5z%yzV1;F1!GHA+;YB;k{WBf|%BMDw7nocS6+8TPO(w&epZO5a6uo zo0BR@^+a#h@}DyM=F%29Rh0s{r+Uf-hi$SC*_>MA^h5^|%2LdbdU@aOb|i3GES2Ed zP6EWq%G0OXExvM>>mH*{KUTDK45V;2o|!DSFV~xi{RM?^i~NNJ`9+tnn73;ecAjD& zoPs3{p`Sdn*n*idL#pvGjMA9Rw;=3No|5A^X%(?YCQ^PPfgO_S%w4kQMUu?@=?dX&ohK=qy zc?Qm|D`E~uVYJW;4c+nM7Wpe-q42O% z9r?TPczFvR9Jk_8@d7-wUXI7dS=klHyI!=i{xqmKCGn*t2Qu)6lmOFFN(yOvfW>GK zSQJuRdR+1(!9S?;MnroOxl`aKz;&T7f?TXcmCyAuMiwnc<~Uu2V$P$5Ffd!$s8rz1 zsd#)!3B31HX2>4R3g_!LI^! z!{OXWF1}aB?{sye1l0woa4gl~lceEKsQxxvHvlV8rlugq4%$0vxC7Pw(&xPDa8)8G zY!Daso<2}}KyfAG=r3OWY>&dCA{tfS8(Xo6n14_l=XD5`^L#S$aXf?)9LUh(ubsFc zyzZM9eD3i6yMF)l=3kQ)AtMkmj9?BxX$7N23{0Wd+Cam@PeRJHE5gIi7zKE_u8ecl z=YsxEBOJ_)m@HD1QxU#GsVO7KUSWit93<6O2gxkmUl1NC6>jPN5E-TWl`^0~U_S!` zdO3q(mJy@zqFl40ba({m0#RbnR%lLyN{5fIkp+?5oN(MPCOA^PJ7mKng?Pip43E46 z|MB)sI1Y}|3c**_^y1$UTW9%qi4c2NK?p%GNtG zw{ndjQVJkC{9871Im^IdrpdYYGZ--KU|u9(6cj-w7>ZbC>Bt~_U@Fn?2McUM?;aW9 zsvI%GgXoQDAwe~3Aj=v+Z8&N8CcLSo#fERvazk0UAy4jBpWLk;^EMyzHjjCSk9mj3 zyxYgT+hgAAW8UjAAMi0B@R-MZ%wrz&As_Q0k9nPsd7a06#K(N31Wi0y8oAfBO}y)7B7uy80_n8{6}dUVaDj{s=;E0K~?4Lw7x0gz2MOl(I@2R@vjTuUsI$50W-Gc=Ovv191A9zB3lObB;wr{IfL8XeNOVc zJ&P7a7j!RJ*tVp7zSFvRNrkc9Ea}=8?}sKl*6Ef*H^Y|U7c}hx)6TW*aCxqt9D&Di z^(h(?AiDCOz$n1gkOv_(2jzPhPf;4j0JkwjbJ=T{bAy;V1i^^N<#{oeC+~2%zyX1S z0*BOeex17Aq;9vW+imK0hq~RZZuhF&1L}56-5yf6M=ZQLh877s0SpK;Nazh{QWLK> zGU5bfKE;X=)QwvpHlSr$kcVmx@^MQu9mE*T6|AU8hUM^oi6ED;HN0uKIw1eiP((E%`GA9hJ75l{-?={!MOj^t^I= zZ^}KGHXvt;Ts~gZR43keQaj>jI8EtP{uwc=G?&dwM zJw46y7q_)GFX^7w*1T}Z;sx{O_bgZxo#)_%YJ+myR0kv4h_^6XHm%)0`oOKp>x1}- zi$yK-U}aJHXd*v<#8#Id_Lg(5$Y@GLq?-(yt;|(NMcxj5uJ;BF?=n|MaIRGVYi*JJ@8X{Uw7fiIBgP_d=tJ`6WQ2 zEAb8gNsny*R)tpZt4b3e)KS8qcXmzKq5Z8&EzqT^ogiUuox27Pm+{mTZ>vS(J&{bW z6H!khBKTV)j(X86;-(__TO%#_86do9yB#8OUy-dS>kL{oR?#@yw1@o0#NZ{2 zC%-+54Qe0TNM1f1H*j%j{#!7SUhK#t4xY+K+zj4mK>6nxwD{~9y>}YUmV3EDi{3hS zB;D%{Bw~?+_yaT<7dvrP^b&*SPo7!Ji|0{U-7LD5VDu#hZGF?6+=--1WE)Sr72PO? z#56a%_`qv?sO>Un)0?WrBbx&b8WArw#3IoMR_1tuyJNPLgZ!Qd`f%i6G_fxYoeS|T z#L0CY#8JJ<$?b`OByGCKjSV6je#EwE@@(9U18FDHr_QU@2tba52H~X5KTN$IFli31 zzjz5}ni)NMuJi^Usx-!+$Uwiy!$`ci8t)tE(~>CnLW9wJofrKCJl~+3SBr`ilFE>^U z;-?tZJO7vAUVQ}DKIUXU_`3C&&$g~LU7uSq@Kni9@4w*A|9a1l{~Vb86ApzsmhQx} ziBx(gAAzQJrtws++u51I*<*UAiWIz?yXW0I@lK8tO*@*^(jVJHcc5_Z^I66t9yRv< zfKQHy9~-PmBsSuOarJ7p zu1k+1#zSE2eF`l8S&2(}s1s*Tn?QF0t^>}WCj!4YqrQp3CGbbx&sPM# zX`$LplX6=FXh16;_^iW$Q3Cs-P8{;|;2?@WGbGn`r1I%j8tGB&m*PlGN;=V}0||t> z25jOZNOj~iQPX;nX+^!&zxg<*f)DU*BKAiI^!4E%A1~pUSZki{OY4{N2T|Lo$Y~K; zDBp$h%3A{seA>o#^pCM~%o!Xqw<3-=Mr(Wt^5H?XuU>69#cLhCF#XGStJpJq(3ZkI z3SVTc@#}BF(OUxl__H40uj1c?z&nSMgKOH4BRaNlFOKOlkeSGf&@ptv2)^6I-*m#} z0N;vYKV|i(7U5lNAM~bhJneane_IY4Z}bUO2Db>?>iLK%_B4s7yi-cQ%`M{7Ey&Nu z&jWHaovrEEd{BdL*t0Lk%8&Qxcz-UIJgZ;>pV0Q9HzefX@~!f5dH-WS!x*eZ{GpECbO&CoQ>#Q#Sh)wFxz(%&5ap8RJ8(S6Y;`m~RDUq9vE zO_zQBl&ha}U~%H+h2+@_*FAS)|8>uMUUJLCz8fYMZhhXwf#*$Jbj8yqo|{~M!&IT* zpC(aXd9kKlw#m>Q-1*?VD(zuy^F(gbR!w`r)-+c@?SA+t$nVh40Q+@8Hvx!#{+mNQ z;Phu`bFZSL^snp6Ac64rkv?0yf_;wmsT2tV#LtAI<>7lt!PZ7I;9u#TNJ;0UF2YaB zWSY9=h8Nv}@c(>6){E*Y=~l^*W$)C&;=+Ca1Z}7rl4%dpPxo&Q-8Hpv!%Ybi60I6& zW?bJ}lkUQ9F5&wr>Q8?}o32^f|D0-QE05^fCWij{Kbv)938kC0Z|L!mruq8fD_zZA zvH;Sn3 z$f>A$JzKC5`8V$7vdOm#iFGC@Y;$7Z*!nshirk?VYCO@z7w`{j>PmsUj|%z%o%MR}XuNNkU5Ngq@rdO#jGE$_Nr|x;BM! zFbs?OB7SY(UPhrNagyCA+gx_jvqi_&9b51N5lJ+Tq9LhXf=1bv4Mnjp0Nf;uX!|dy zPNF_@e+PevR;PY!K3C8!T7O0-IQ5wbPp7>TcKrIe8+D4vMyxIvAlqSt!bWa8n7*%m zuoro6VKbS+a;h-VRRW>#Pcw~F*xEQkAzS*spmw=##6DWH>B3W?W)Evq@a6yuPHbX7 z#gl)#=!8i%I;npK(8kn1L%wXQnrv1K{20AzH+*;^c>KC+=)x!aN%h1I+32&6M^bdX z-5An|7Mm%uW$r-{5-}2w9MPheE>LK;0S#prK}5)+>q82gfMyYevvaZz+Y}j0 zQ~>fBZ7(8~CX2|1tXQXQW+dLnfh2C5B0N3V`cXy}k!qq#RNc-)4yI;Am75_+j)RJ- zTP8~Mne3zO3fqio8>XgTg+zB16dDe$>a(+DRb~_!5`*kFg+aEMz#zyIZF7|-_(m54 zx`e)I7L0D7Ax3&lkNcPr=r=xvdQj9s6hG^!{&b`euxsP`B4mVJBL+kR_{_rOAr>Ye zu`_!xHwq6iJ3|WO8P{V^0s55(DuYN;Bu`6U2Gg5yH5(YA0L>Bip>7y^N@fK&=7K`~ zESsgjmyy=O-*(!08Rw?65cziHys%yHgIr|Tu03NMo=QIss(X_xGxRpkFnbT(@?(|mhP`@J_i)8CGA8;Uf>19Nx&oZiU80SJ`_FDSH@1r0ih`p*cBl zi2P`}*9m10W6INP_HBzTsJQb`5O)SXQ_Ze;i5#vW&TRG)DduH5i_N!2H)(ms?$1i|$Zys8FCUV|p zFGE(E@#XN#R0T|(5KexK1&beFfQa}qD#H!idDlqB0ByZlcbIg2v0FR4TRRXVYvkHo zBez>4MkCTvjfBqrLAa2jvQEaG2dcX!NGQ(a7iPM&W=j+Oi(#_ea1Q4oB9Li1?~d;S zjR+lXLz86Y(V46Qgd;b+=L$Y@%Ft7hr*&WG*H6!OI3xize{%pzJ$G?OaD^UVG7$g> z0((yr9p0;hGskhi3LTs|gR^vX@NWNR`#Ph8Qzof{W1uJj^5x-4yhC3Wc-K>&YYZdh$uV zo?}zJo|8bY@Ajxr(7R!w*Hc*P_3L-E$hLmmYP~*-ob>u`Sm^blFxKn4-7mOGu!7L* z*N3?^?FLwgkDdHjF^ccy?z};DoqxV&|TNG4fJ}(OL{%G4e9kgWRYH< zMWmVtdcCL{>Gezv^m;*Ch9psoI`n!1u0yTtBk1)48|n3&pQ$0eo-8D* zdaeredP*eg)2~7bgQP)V5afxrA-z6F<5~>pi-CjmdVtKB0?_Np554|OMo+)-)A1wu zOcc+0J<)-&uds_X{V|C@i*jJ$XC!p6$neD!m??bS@?hLb2@z9qI9rtzvEU3?@W$5= zUG>2pTA%UQX}#hx(VA@tCd^YZv8JQrfCRljvw8d!Dw^jDUAq>zv3@BU!E}D1yeXai zd#cW=L=6+eX_V7w>UK@w&ebEMygQtAF)t8m-zY-^GJ+Y`5rCW#Z2i8Hw`7Q=vJ}(i z7hcpYFp$_4;;Q4@y3J&>ma&XAmC3=JBdWj^UIs#w2TSuCPVL7M^%Pb+f`iKozwDNQ zX1Eem8Urvl$c}#^!p)!P(AzET0nmq16n$MJ`79ElSINe}>Vz95&(MO&&QmSn)NhgP zUNW&f$TYs`6ivI^7f-dkqUmPB22W-Kns7GR?B>iX#T@6dQ?NuZZ#oCI9e9_hd^AXsWLWLdqT>nL(Ie`0ES?BR&r)M33+c zoL{ILX#s{ls10t4&qtCij_e)R5m<9h4fK(%ue3ob!uR9SEanIxNpu7r4YM_3bD}XlYB04`!W#?t$|IF<3&_1(+kZ%W@7@49T*1$ zzwnxP2kHtP8|y>}Ej4G4+7ql*&4c|}hKL!P?5Awid|0u#VIQas+Xq+=GPJG0)&Fbb z5~he5L75THDi6)>Y{)o+JTPPcy#PF3dCpRvZOU`D@@yv$OdC8jiD}XRNgmP*OzlC? ziz+L>T%#okOA!s}ia2ilSi+pwi^xK}M0aL(DHfM*Sdxy7iOxjwcA3M5ks|GG*`RBYTIa zV`}msvK^O~ebVH?75#{)DAsLRq|)T^Sn$Z|ven~LWc8qeL|xOhj;g0z>jLe_n_cv zy9X`N#K^HTd?Xqib`M%RXB$!3J*eas**#hkugERz9!$u(oR?ZZIR6*O`Y|gJz#*WQ zWmqzRh&qZjr-)-42(_W9-2uKSp$)kKyJ7(~qHLTgD;Dzg8QJ%)v5V{G8oRORB~j4| zB*pXD9PK=|M*eiYt`23-PxbZoIk7a;_Di@!#M4YmPIPKsR6($^E7>NzvAs+3fTMfD zR6zPc$u+Ve%W<*Qv8U)3E!&Nu9+0WbDw>%LHX?0Awh`F~i!cv$)=A?C&MlhU1Tsqp zS>$fW-9-mQ$+22=L>EkLfscAt0p#+&zv#$L66{)}$%)qUr~&T-S+u`(n&dIFo#8UU zGgGFWtD9W3GyA=b)6ps{VBDq#bx^~O7v_Q-c<|DVGj*JfXmMr7u+4T4XNEV*;I2-N zhm|$+VUoF)_99t^X|=&w%+pM}z$du`WHYec3oT2i=x0kNl4&GRrO7$F^6muf`rkNh zx(9Bo34iT=X6hBv+ojsHLQ_OpOo=N{y-XMcS)W?LylB?8=!O}i48OKhU(9X7R9pvm zD2CE%lR4hBhT=Wsf1YkweP(V2sRVb;@VQ}7(}|$IceQur0T8P?O?7EfZmg-Af+;r z$Kf}v9{A+{iifNCCH)Xo2eY|ZU%b6QWlq@S=~fks#a-}}ieh!f!2*Nnyl_V{}e zzOpA_-O-0ce6R#f#bh0-f;wZ%2r_9aK$Fe>ErL@f{UriO*=1-KAYGaJ{UDGg3n+Lj zL9_eK2o$}P3VH$<%-7>?yq$n?Xxs5-M8gEkg}xKv=TrC)mYil2i@I)xq>1!fR7#c^ zXk867f<8*_+b}K0Q%H4-iT=pzFCnPw#F#njzU|EZK*(A{TQ46f^qBHhxjblU+43w= z9zju_zFv8&^Kp3_jWfsJ|G=Tk8c40yw>fvC>P$2b;c&{?rO>0_D|@vo_OYzv%j|n%>BLXK7t=2f*J+4 z;k!=a5G&OqQMi*qm5ecyg-pKP+@;4aAZngwBswA*m#PXF2xV*W8)2U{63Ae?QI~Z- zRbj`rMHV}64;IZG+pt=+8;f>QM|^Y84!vl8{`MzR%bW>F{VEX?Cg>8s8Ol&M5n8~n zHzio5#enwjAsm#hJ@Fj$)oy{4l4FIx3=&DMTH5;%2kY{>{TSrK!cf|KFtVhhuDACJ zGUg>1;3cbQHBKv92?m)D%S_Rlp#6~qW593Z7i$*j8{2-HdzH9kaa2H704bV1-8P9B z-)^2G`wK10{e_kl47aMk*3aFN{Q;vD^WbKcdsTn172VtMV(N~we-FgiB|9iN^_OC2 z&pBFhi>~OQQqc|F-Y&XE#>qY^t6(=7chaq5IiBhiVrXn=vl2dew_MHcIynu`f zcD%&>HiG%$Y>B2>hIYI|%tc?Xjq&+9aaf2lji9U^Ig z$+FK(V4LWf=Pd29=ixc+1Iz!()Ak!^+J+g!Um^J4g!*pf>e|eYNsC00je+F zh!I43ElucT+5l*UKu?gMix~7<1cIr-r-}U%a!J0jN60!_It0MBR2Z7sMEnVjXR3R9`M9 zJ`5nw5-^*iazv*u$+nZXp(r~!#=m#rH+dcs)vhJ|@!ydU?))8>g3FfB17_{G4)uxQ zrj>ykfqsbM13<~7h@)dXzYqb*m{{}`uf-eF&QA%D<{sU1395Se%AC#>6NczhZwFd*LcEDu$PZ;5 zkASXIK$l^)MeeP6v(JQ|2%O0+j9Y3u4r3`Lm`GFvBWHoKluRtigOXi8%P2WP$*qrr zT5G=OwwXo$ZgB?rR; zqlw#@-IjicT<$8^okV~tkrY<7%5GaLEiY$S%INoXih@`r>*1>Qx0?IOl7lXp;% z`q&_T6=U=e<(_N~Z^4E!Be(~NFd_(S*DjI3KL9ZKUy+@8Bu4&##};Rzcp<`0z=xF^ zLG9$72(XURi|A2-i1heV*WyWNs#ymG%3VJ_amo7WH<8{gWzh$SFuQ3l@1#*9H+%1t z-4(P44s;Z6;p4wSJ|JQj-;O2| zkwl zr=mLB@6aFCwW}Rs3WVQ zR*+jpy?Ip>4J{e)iUZWy-`&O{@q^ixCH-RFE|>I@T0wO9)L3H_g6<@1jwkn zdZWq`e0MK+g61(9b!<(PsiNN68x>8+EN|(VYw3k>`(Wx3 z*s4VHH)u^kcvWVD*BYyE1Oi3C7(_iSZ9mb4v10@+&k$35D_AgH?Pf!$DcyQXePYcL z1OtU=YU_uHVodEsX&WnVs)u6+HrdI2sb|o%r8kQP) zmuJ_4D5bX$oPw4)2StXW=>;yA=hXyc3oT$`Z1T*Pkd}}l4YqYS5uJ@DqG_cf*Dp@% zTVCytL`o4UND|dDP)n+;*#^f1i$&%bT3fvd^}@PT)~mcruBgh`m38LtAR83y22}b6 z^&l#*5LA-6rGeE|(J9Zjm9F4oD+ife$=xY6pM}d;pn9gh3Fl*TnhyMJ`U(v*CY#F*}i$lY~5Sif}ycENNonacQ2TN_1^2qvmHwAJ{mkR zs>l|I7l9^L-Pi-w?Zpbf9sq7r0QLZINCDUbz)KW>Jpe2z0DAyfRsi;)9TbN zhG{mOaV)s!q4A)@V~iHxAJpRO;l^iBMJWA9H$4*dS?eTw)*4($#t4v?)<2iQr9ZXI z>@%^_yd1+&okNXJLvc{wwvc1Rm`ZI(g&5X*xK>;vcPMUf(2l3zV)+rXH8rO%-arI% zB!jRIoZ%KQE3g-*JwON=AesIl)7dl&!Q@$$CGEn1Cusk#8H~(AOr_2z>#dVDH!H^&y1(O`N!iNKu)#H#D%fQY4*NJMt1ArK8oc{{ouA`+UC ziSlm*aOqA43I7|2Gr%E~j9f}b<3=ampgVn7BNBn2pD(mgI-66|0+CYN{L2eO9&fv- zgYg$d`XMSQI2@Jt32%5f8rTQfLg4b@apkQXZbP0DC7c!s$gdKUUnV9m5)*xnb`8py zruIGbMu3}7j4JX%BN?v!h2KV#l<(++NScJviO9Yk9I)y5WGKiMq2wl{-GZ&hGBpe# z66nA)G^f25Wo)G~f}?tPG`@g35+1!Q{5gV4f8Mdr8gksTV5~X{D|&defEY-TEv|Fc zkZS`T5zF zgPTA82bko9yu;RkHbec!(KusGos=^OGbKcis$=j8~fr*QuC=rxyA=2#~x^ zko#E5fo9t+=i&|PL#^_g*6BT}PQPJUzfQemS+)ipKs!VA(+wNeOLdsl!x^hukd#a6 zA8b>BXU6xlIgEc`I!CxIEN-j&^7W$LzacFfaLxaf7AQ;IcDepBwu>hR3^-Q|7>pbm zFd1Upo_29h(k#CPy~@-s0v%_fe?nTL*9`k`-Y&O&xCL@$dt-3r+`BRX?=EWON+&yH zKHr0n-w60W<1@4-=zF~4pJAd;lHuM(1d{%{y1)NQ^tVa2YcUV_eP*scXgQc^tB`Yd zvuvT9X6t9Q`|HrS49o>df;b3!m>vAB|5)#XahJR}3Was7ifBDTbBsaud$I{*EwX^f zJLWLCW3xL*pykuid)E1ojm{Dk7qR@o)+8Ts*a`1|W;C8|Bv^#`x%#M+ya1lO?cNTV zJCb1K<8Q%a1C1On9f;l3zj?@a8nz>LQ|;u%hze#(_9XjszL?iZAic5sN}2@KU)XR) zwfG{X6B=yK-^6W+*A{H!9aJ7Qg^fwF^$St2Vakg%hEHj%ZW^=o&#GO~AG9Z2j3(~D zaNIu73%CRG0(BDi*8R||-*r`ZhnOl=?VZRfg)c^W4q>sP`q zyu)zB^gZvk{{W4%32Q@=Av|Gtv{vywTA=vt)Ojg|%9w_PwU;OV*1ht0_WM}n*oShJ z$^o_YFc9)Giah}QwgRx{m{f=36=05EMnBI60zH0t8ip-5J@IxE`AG+vMn{}OeUVH< zzcLblu$M@cz_Bh0pptt0EF{nC@pIr5M9OGlH`%>|tuSfVaWjFp>$ZItEo4A+c7~EU z6QwQ(xs1xQ-7%I(9-i%X@-n|dnk!`<4E6iWAExxm&31oLpYtv98clUQ6HB6^JY z#pGD=3*pep=S_-Z*aN^31z-;VZ&m>I0Ps5sz#ahJq5$k!hwGN8%naAPlnR&UEMHE3 z8k?r}eCTrLrFMOVou2SY<4XTPQY&=0Am_H9%VDs7b?_g-!vwnd=hlrE2sWRY% zWmLY7rg;fm=KT6jS`k5{!OkLB6SkKwfCNSlfaFLIfY|>o#lBwa!-{nU^{o|8c;-vF z`h1F65W*v&i%bH0i8T^X>v9r~If<92rZ=)|Lsik9@|-A*ZA zzcDBk{(mB>0HO-T0uhymH($&<1n`s(6?^0Q|lJum^y<6o5Sd+^qoY z`LPtXK@hNN$fGoVCD7|#eX;8k(XXb`eNNn_|%+*!E=r^v4Wze zW-Sb!DXTnHv&7)p40nzO5nQC08idP(h}`ZVnrz^_-0MI}*a9z;2V=_5J8q@j&Kb%3 zK(P@7B-&nuxcJo|K#yOcyvYYB-VB2HjQ}++(PQkR*n&);qd?tScpX-ZID5CI6fCjG zX!i!B8ZeDMn0?LUgM=`7KYoQyu~!p+2CC;96DtT!om#)bWW{BC+BM@oY!PBnU0rxH zib-Ar0tfW^lt>U2GAYKgDa^1Z#B=6%fcygLGxUQPVoHA5&f*yu?l>iiGdEH!97k#r z2hSqQuPemC)$9+G?MQ5-;6}gK++L*n3vS5@3-u|lQ8!!~xkY!-i4UXxm}$*+(X!f@ zF-=@XfP}F+$%oJkK|u|Fr}bv9@i>vu*&rYGpwnz}ezSL6gQ2VRubrU!Le#O(GOxx7 zAiw^2D_8Hw;U7_fO?b8mPaO1_t$*yZ?5nMO9qVCNRETFK#QZ|&eaxt>&(&V9$8@Ty zlECETH*n@zgkFcxEzm&p+EDGIMsrlbITbD-i&U>isx37cGLqjyP$8`LAgm^ga1Q!F z4?>wrBCIA^gjI>~w*`J|#B<@LG)O4#c@m+d10qaP2DKBzXOMa<+FYD32MUaHI!Na?VO*!$M-ow-2D8LRMI^ry$=OaS zS_T>E939EJx02x6qT^D)-C!S~$C{8M7ZLw9@Z!b?V0LQiLp8I3=R3qX05CN{$>J%EtL+3>tF#y=e>Z z8Mq(7#-ulHC(nXUGz-Oek!L42_JV5E_@s}(=A~UseV1H>6xj8-7*5V^7+f1$2fw}z zIH+oNc%p;o#ie9?_9-BEc$DS_+QM{wN}6z5i7*H-Ob{6AeH2S^S}d7BszIw zYUCE~#!MmhZJ<%Qm{Ed-F9Wn@=hzBbw2e4@Mpc^Q7jnLKGYs(-bKmjFGM^edK3VBA z1tf+cUTr&LX=M6U%;9IhA9d-oq=Z~1ABhYMj|KNu5|P-Vc#g}l4ddV10b~eN=EgSVpmM`<5~2;o2g;&l(Z(YlM76 z2ai6yw8R9^v0guC*x0gyf-nw< zOv1@KR=5wHO1~u&*ZQ#R@Jkj(x$t42c)KuM%w4oA?qi(b@RT#(DeuAx&Bmx8FIdwS ztV_#D=(-mLQ2JbO@#AbTsh^yVlET^kB6u1+4+O~$@Sju;!TH3l&=DV$nlzG-R_nX1 zj}<-eug?2|U=7rT`PYtyTee++6#@ecCzh$!>AB;jx1$jyKYbHz0CA7~n?C@s0n0DB zcrKF;t|Bp`lk|2#1TuUb8J39NRn+r2 z7*qRcTDRk}ZscN`NU6ZPo5fCff6f`*ifipZp;p4ODT1&WLG>r?$fGUjK@u%^xIoY+ ztK&^Sf+nF$niy2y)!q1~L<|+6AL8PMg`506;5HpH;KGCa2r4WvJA@fK?FutB1}aCG zWh4ALn1^cj>xbTf@|)Naq#9!5mDY82Sb8W(cMA|~n&9Lb{)cTjd zN_&jpnfN{M=j1$@Hdx+H9spcmbmNb}?<9}lmqetK{184qLBXp{EHt51LaLX%Z5TCL z(Hot$iynnB3hIkkB$!1EW~87QAcrwge;THZV!l(%31ZKe3ZoP5E1?OAj%<7btrzY^ zqq*a=B^qS3QU&b6yHGHSSF5XX>s@LM>b71h<#o|3gOZ{boVTCXgMxe-FhvVvO4wY{ zG;(Y}kro7q5hhqJ(;7CnMr|IK_m~TY1ZNsc)F(X0+<-RFfe*I6tDtw{-y%5|Y``+!(!hZHH-5*NP~|6s<-D!oTJFct z!izAG`A`BR=-w`jmOqZXSie>gO0EPf{tK{E z$$iM%GF0-u55cWrS^I5*LHE4aM{^7V_FA~~zaU*S4~H$d^#7^&zwH(H31KpGkwFv~ z4NSX)7JJQ_JFoY4$uz$nLN${{E$<*?E=lJ6lMV}L%8 z3+FJIVSE?!@y7t)PC}Ei{R2UJQ)4Ba)(3m=Hc~*e(A9e0b0^V$IsRkmqSY@0#zKtS zqjvKWLI;Bp6#}&){v-is2nb_?0NSnIJ@gj)V%V^J4&o>U?#3iAVfhWi#qn*k;R;<1e#nHy1T9vB zN~2wXRVV$CGl2hC3-U>YDJ)IlWqk)0Dwbtbd4i8Xr$bRxah!H9%15Y%eaq+ks&IBbe!}((S3QYvr_z<2!z#E_-lq#exXk%}{ zO*Bb26whBl_hntWC+gQgjsGs{>O1-4Jm*NkB;(d>aVnOds+Su#5 z^d1BSS5P}w_H~E(BHy_ zDuJZ+hq%N>ddb&hz3R`974k)ee5S{@Cw>Z6ZV`!gbbU|N?8x60`PtebNl)0UP;D#I z_n76`a|QY_#y%!m%ZaXM%(x2DgBjs0+B&v1W=|LStDIj*J&ZY@^Sha56L<4;jn?4Q zUy^fBkrhDpG%Y)N>7JU+%)Wm5;?}PB^X_f|Fv|0Qzit|xJ>$>fg-U5@jW@;X+j`S|kyQE6n3fr^9o zWPr>;Q~{clva#e_KZ4HRE#yP=&W#y=6nSf6{qc0P<5g6@I}i9apI@d2zy}=9@Z^!? zV|)$CZv7*Ym>nE_pf153&mW?h=tLwfR{IUyMlYI29>kUrio?1LhTl+Kc*GgZ4d$3Yh{NL|gtK{48CdE&I1|P(Lc{lQ)BA`U^s~Z!CTR)meEj z(uV=tI0S&K7v&1uIRt>r7?m>HIt1VY095ZF!wK{0ic;ekPhjU6z^u<_&g3QQoT)$7UM7J>0> zAjXjFuFT%3Y-c^ZvlBmM>m#0_|QNO5BDYE8S&5DrIxRLC=3yA3Yl zig-Tf(U)TJvHTH`i~WiE(_OGDEwEw3#BJXx{1~%ji8!+Or^G%`CgMMXhjc95Q>F;$ z|7e`4&{#i;`G{Ek9=n1izd$oY{ZFk(F1I1>-%~jGGLU0}W8hxYP1m-{x*4~gi#{XX zGj1WDFLKzNj}GM$9-DvpP%fR7{RL@g_``XTZLElwnb{`;Zj)o^`4ljT@qTPF3;`^s zz?WRhz%qc96nN<_237#9rojKan1NLQM^fN7<`_5v;Ajeb3np;9Ls6`$YnrBXB+iPd`h5^8_xW;PQR} zE)cjc1z&)9f^hc{xIYEI;D7-46L=s6zb+8q0Rk6O@MlT_TqN*d3jWEc01pzll!70q z2yltOLn(NlBfvuh9!|keM?VvR!vqdea30GH0tW={cJ(=~08?K_-Ok=~rT|lKN8Rpz z4j7P-P=80=4xiZ}z|`YWx6AGA0!)1#bvykdrwK6iderUqS76{0ZtC}_+wp$|-I~DM z^HExzpVtLAI(B@825dASxcPy*O{l?G$oMNHc4$o9OD+}2KY{bmzn|gX&+_l*`1gML6}PyBd^!u*Kn3hTL~t{=P{=8pyOI1Y zsD>HQ+e%+~fI3p$N-xaoWn6Hf)XouVfG-eAY5?|79fyJQ>4Y<;RVnUyoCrqLA!H#J z#kVAoOsjCi2EpQYk#yzbz$GA)4}1$3`?#sYP()H)Rpd8SKa|TNlA1t7Rw@EQ&7qRx zH4%ZUS{0GhN<(8KB0$ZgQoc%+IzB2Qsr80N#zf#g3Pj$x2vL)+$rlwOPegNDcMa2D zL~3d;b}=`Y8d#F=KL{9Z1vmJ_ z%=%v?$6ERtoWQq&qil-Gk%tXkm~6&)vVkF=O;2#hXyvr$+kQG}d|$Vh{#24kc!^X( zOfJJ)Q4w}KikffJn+a5$!Lta|2AqWycAIAqebwD;o7Z%s*mL+di zIQDoa7*~M+Aus{Qh(@~e08Qyn5drTAY_GhhHtRKW*c{ygyFl2Piah+z;xs{H80KDT z+oFY}MRR0C?1)%U?Vtv@t*?k)X?@Tcjl7WT_=1Ya8>K)fOQyaUovAux)DJ47B27S{ z(TWJrYXOuheg_x^@~2Ph$0R_1QG5r)DipIY;1O88V-!PlG8hZ*7-LTul@`AME;+vV zS^9;H^t(|5JyGST(2XkeL{*|F;Jds|RNoRl8A*o~Uv(sG`bIu_vn1B@!){ zjgpj9(z%Z*sdgzDQaHot%4|kzITcloCVHaET_Vd-xhJa9ZOJO6R;IfpCDnAgWlF}o zlw>M?Q#K) zv{j#onhaN!jkW@-i1AugtOU(Ek{Nm^92-{@-jvRUa?p3{U5>8nAp;tnDxe%~QDl^( z(|b~3*r`<2Xt0~ASAz5qn^r01gdSocYZbNLn)V!5y;P2F>7gS-Z6!LbOUGnSsxoQ0 zs?j9xQPZv-rI1W2mE=82wcdcmJmD*Hf3}9)4AVdVX)w!#+mI&Rqhf} zjifZDRAEo53M~voLsz1-p~u&6=v zMmaSTOok&Hk47X*xnyL0F6oQ<7419L$hx!Pi0c5SezR4rXH#?v_mjSiizE)dy-}3 z3dV=3BI>bPu%u;{yTdaou0KAD(V!y!U)S(V+6p7li&S}G^x__>G9n!FK`O(mYh-vz z6-Y^`4<8%xrS*v)j7p05Uu@j+mFW43_-eGU5%Cp8FjS?S;-FN}q;e6&OYuUIX0@)} zP39>+C(TQBL$9bQMMH}EAFpZpw6!WHvR23G&~}?{+5l~zN(}nwbrex+g>R&Xw=mrrb|nPTSnJ#OUj{9Ns&ntR*z1j#_4br zD2kUhZtrrGC~g^xZeB%kx|RaPt0hZH%2>CJ(eKSMJ6`CrTs=} ztZ5-tH4LdEZ(B{KlHye*LckV%(bk@188HrqPuT+LR+F3-it2RvqE9t=FZq~56#18L zO#Y~12I7ks^w5=&hNEg6GWi=ecn|q&Tf9sDxFVVK!gYD1990zc$r>I>N85OGqac~6 zU#+>MoI9Rr{KCeKpJkWeByAsL+J1wX2ulq`{ZrPoeM%SR7%2s*8Kfd3C0(ZRF{09` zDCHxWEHf2Fqe=?Wz*{{Pb=&@wZriU~bExr&b*ZBM)-`RPA{tlRPHlf^BhHC}F6*pS z0y4-X!C*O-zN9&V68jUIPZ&<@RX@O&1aXzMMBFV~f&TyCfv5l!u=YCL+ZIYg@ z%r<^H`Ew_&tAdS1*G(T)l zS$vB3$D>G9;O84x08@Lq?x<{BfwF3I)N@SLWi9taHPz^(Yi=y`v*d5U{82Qa$UlEA z`I%{cV`-C7e8KY5*+#-bFXcdVTz9HE^(0hhx#raM8+{@=MN$2gwN$G)6jOcL=xY~y zU8&laY4zcgR3IzwODCy7cF<0(Ux8Di&8h+~K1l^KTr;{c*I?pL<^4%}pwaDtwTr%P zN1dZ+Chl5yvfUh=IwnQtCpMNj5WX)K+(3)W4OJZ^lT5>6V&hSJdYAl@>N?r^_Hn8% zVic)Sdunu=s=&&|6&UT(j{-NWz-Wg4%_osMQyKdGjrk`vFrqo;)+hh8Xi|}X?n&6E zsAxX4!SAaRoMKDzJIu5rAgvYHjDOSAp8=qs>wPy+e_| zLOZPSKhU;3?Z@Ix7JP_dJA&A1{xbsb-s}^xmx!~1^bLb^5!Z?D!A_%b=9&0@!8w`m zBpx@VOd{T=C+Jpo%oK3(eT%r+9^+NCJKkRhB&qMVit z;&sr)pQAiJ$8z;{ zl8+D#p`aneor5VP#VOs%lAFPYqn6<;j_hN%nl8s@qEd=?T#w^!X^BHzqALo-9R(EV z;~tXO?RIwj1PAT)L)0(HAEJjExYI>}AbA&UTJwE3gWrn8X|ST9mvPM$_g3kuDY&BJ z+wr?87hcgkF?=o#A~-dB@ja9i{3*Vv`b_RC>Qa%19y6-whX`OeM-B>}w9mqMH@MaFBRBu%GC2+0%UxZ1JMY@l8a7QIOKVtxtt zTI%26hyj!aQ2K_&Npg4}^?krgr*U-?f%hYDpp0bmcKmO^k;mn5N^mxQ@bF@afLLU1 zZkOaIx>JwKt@U$oQ60xkeL7g_AtzyzNRwXxTccXEd%<4vPk@)4hn|Y8+mXEasfY47 z#z7wvI0wPMk=bP~k0lYkOGs>t!!{p68vPK758`cd0lWw(aT|{gLeqH|UAs-srM^faN?{}uaPY{;I2=3}hO^0!B5}b(^RDbT1HA%} zH)zHu0HscXr1>$tbe4sK7T*&c!;Q8foS?RnzeD7mXWt3R@!T2w_=FUGJ1#r8`|I#j zf@@v^N)FzU;nKKAi}t}?of6(8plo!hSR$BjA1L*SY=GiACnF$ef3-AFuj(Fd-{Df; z!Pr1eK#J4M7YA_ra-cX64a_`@YS6PD@&5pA2`?^FtK)#U8UHT-bh<1gwNo zauySvrwMXyt}|IO3-xJ(qvV8YidI5*Q0?TbYjU8SwxU=;B%_U~dLal9O|X>!5+~Zb8ivbBRk0M`6^2 zn(l&KsK3p3Q}*##qj&CO9fcrvWd{V6(cuq!=Z%Ag(>b_{W|$TZc@YPRcPCEE1j3Hd zIq2X&UN`S1!9#E6!9LUM9JJ><2L!wOZZMrU51KCH%-8YukH`RO?3Thwfv|&&F8-%D z1r!ufNqBYjKn@KHsY_&2tyXq)m>*5;@6o3FnIEMDM|D_|pTLU;je=ngY#N{!fCh{K zQ$?YhW#YVZL%6?SSS@21UkJgMqww^M@X{BgaJd}UCXAA6;*!RG+|Vw%xF_nBAa`)v z8D1B6{q69?CQ&iETg$hjXTzdhp2a%?MGx%okHmuF%B$zL3tli&EMmTyDr0C_xPIX| zctQgsRlXcI`xeUFM`icns<_CGD&O}g`q9x|i2BZQeU148eqcgteKSNfPPL}}h32%| z{OCd6MTZ*5JU2H_vM#V>a)@$HF520W$w}-J*zu5$o)v-O%Odxm!KTT-;HO4!m|J0N z5sO<(-$P48mg7u|}&2VU@9Dyl-!Za0dSe%ReHFR&vy;k=oXpin~(%n?;dg5^| zh+m?m5)TU5F>kgZdqo^TG}z`10-^0hGNyChA{ZX0vKOhA;{6+%bMQd`b#wb6VAU5t zjCNo80eOYRd+Be@qERZpLiv5 z;3c`r3De%&&?|J4Ta<-+*7PXBwmK5fZHp2JZS33Wd%tgKZuLUVL{V9svZGds=#2 zB_F+kXqAu5Y({eS;dAmBJ5UHQbP7b_5Rz4(mY)YQ6~*0 zM}^)7kaTWlI$c2~9?l7lC`$1_n4lETH;5N1rZG#N{Z&%i<58jntqfLbFUOm#$E2O`dAFKYpg1j_V@n}b(KBRki_6JWZKvl<*NuFLFY5mmZ zdwllzzA?N5UIypkF`DsxLWsH37dTiibb~E$1qe`piaS3YIUq{`+-ZD1quLFgz>^_7 z|GgfxJnOO2BA>#^Ih#N8*7xiXB*~?Qd&b2?Q5rgnr3(`!VGMBR&ln@R(scbqmI^Ud zm*dk!G-8-{g_mWzLEoRJ%f$2^gKOAWQPLZ*xM9}%W$k%%vi!&~BQz@bEgMz*R>EUO zH8@87Wop+UAH#UEly7=s@sOs z7F7Q^GE}-lL==w<70!@WGtJQWbGssluKpUintyIrnQ%KU^5`HrZt%B{#X2*s4x|IM zeU8_bnYNd!L)^ilq;hT9{65r#p7ms=?YryAY>r=lHJZtdwd_e(qdoNUdU6fD_G}Nm znWpDU!MuLWl!z5*<%re#3zycf4J50gWf9T=HH$*Xm|YztZl@fd>!ksNMF5- z#k2TAKmG*9O{O1-6(3taLjUIHYaV+)svN&rKg!&{aX+fkzwbWQepESrvwrN-`y2Hm z6w;nv-?$g?AT_=KMKd+TD8{C(U5Y+^4rF1*tN^juhR|gD1oeH_J`rT>6A-a>93VQi zPUt|TEAxf46&&DXVFt_De~psqxjVLUsO0f5lAL`XqHH;_$QLf_xWP%v5th||1YK%9 zqlSyDjWg3^f6Uehk!P)?!$acaBS->G9`~N5kxpbz{si#W8!-QFTW#?$k&PZPVzf^3 zQ7(g&II?(<3oVK)9&F>KZ{@rORXIR&3}<1?;Z{uR`2M?qd<-aZ^_?i_Uik1aIR{0Q zTW4e4;QG*+!lj3SR;>Jm^RcEu$EE5qt(b1uPgKIDbjqN`WK#1>Li=dOC4;RE zy4yJ<;L^W~(yeUic7>i0aNL8tXyFc~!_6V59t-?vHSSfmvoa=6)QRqZW0E2phTHlU z7WiNPv&+tN=7lmZ>`A9+Q&*_ESQ>7)YX^JV+oz&oys4ZINlPDVMHTtCr*d|EWYFfy zarq?&MdC{V&{3WFb{}<6hEC32Femrl!b5o4Fd;URM8&Ruyi(kf-qU4i+ z5#8r|cpW+Z6t^T4+xp{W-CeGGw$?@K@tBI4$J~VY7`kqU9t&qvG}gvx|4ieHg=wI& z_5In<%x$8fJqhGK#A8dcn$ThKB%|oIlBX*8cdIK{RM}}z9Nblsd<#p@S5@VpoDmH{ zCBbz=vCOI5@dS)+`d$#na1CKZ62VXg%OY47LA(tziafXn8qbUV)V0c{&F5_DqaJ&` zT2KW?mwt%CbR#^vgati3<)ceKf`^t*6hkgWkjwDyD_TFM^ybn}WGn?K6^Bs<=_s&s zvOXz`GwCglk)@v^B{WQYG>&|>!7nTvXI&GYUBC^+M*s%qpGl0$B{1G%=3M4c<1n6s zRDSEhyQpl!N4M9=MGEV#+U4BSuupbv@#Zx%-s=aWa8_L@YNgCoCeWHxkC-E+cc#nnjOh`?p=Xd3Y9TxC`cZdu< zei8`|J$|xqE+a>*q(QcvpDYcFR{8)Dy~}EfH+J#jy4eV2pwS1<>%e`t%%T5a3x?gr zUj9c^(Qgj0YbT#UH1^-nqB-4;&jj-PVnAxNy9~ZHPi%7bG%mE`Hb8jDDc^X!*@#W` zNbflLsDQ?h@`?8;KMNQ={_Ym@UVJf-V##YpgZ!o?;El&ofHOwQ*Ui|-E#HTtz4&}& zbcMq-L4-gS+VLDCe1fUo<~^HygiYW;W}h#4mMj268Z&AAk#!voM`cE}lj~ ze-bZ06#e)t3J5865MBb}z0CMZ_{r3qJeNIUEjJ1Neg^o1O-?@nW!w^aybWpS3!B++ zZQe)*Q5)Ujr@PuFhuU~@dHEmE8~A#X%O3qmZJ2f;m{)2v26gTEC?`QV3!g;G8Zq${ zz8V`p9>p~5WiSK3yGf(6sk{7zmVy^;ZFydFHZGO>^^zCQBJJ=L-7vSA@oKEhi^gw# z4If7l_uL5|rW0IvC+<1%cyrrywyZ@Y1k;J4dgo? z!&K2RD3f?^jJm~&r|?_wY`mW9yE(4d@_&GQeBDvlNb1PHfc$8R&}P=f2f_urHgs%h z(()ZQr2l^5n|e#CGzLL*_jnpJE$pkHa8xg5L1ueloSMBK;!L>y>@j z*rDFGXSSu!<~{=Cn2tn5`gZP2o}d-+zR+>;bS{q_v}Dc*lFh)v@(r)mNt~^p3eVUc zY?aQ04wT7)ObNlsFW8K?eu2-w(17aT&0`BsMM2x#)4)KkTN98_+IVsrB9#j2wtgUS zPvsWQ21R1{QvZ|9;&!;Vpk5Yq{e`78S~qw5usSr`qR9dK7{B*N zde^Pg-ds}?nZtsj9X2iSiM4n?_!R2ARew>)hcHUEbR7&(q7R$(=YW5T^x!qL9r2El zr;p?FEM9=e@=m9Af}t`v5}${3C7&KW@)u8mKlwb6;|)<^;dbdwOZMg&TN`#pGzSW? zTCXsv9iE?3C*>`@k$IVyd;w_igz2e+`S^?QCYz}ode652v4BU3X&dga=rJVu5|T8( z4V5W>B7njp6>at4rPKN#J)XG^)>8>w2>Opv zAL8hgnZM6~icBfN)rCh0oMCNx%ueWCU(&YzyVAQxH*LL4=v_4C=fQI?puVNIos2g3 zoqXL%U-P#G7{VX!6ywFh_pxYL30wx*Gx^V8nVNhV z9oKMaM-M$wGg#twffouO$z1{T=sQhM5Kxlw-J};0aAB;ECWB`JsVH5H)z_m$mO=b+ z2(mBUraL?*L|Z#~ygX=6pQ%GNQlNqkmXVo;O_4psaq`t(T)QNXMnNAQ>*E9HQ$QNf*yed3Si$UQE#9 zw{bzuv>=Wycxg1sY|eXayu!z{$udVph*Uni^-h#Y>)>-y<_l4#cwAAhIV3hQJzx7lTxL5%BVS+%q5cVJ%I#U5OhL zV^G*e`xco=k>t7hWHPqV#`hJiy(N1X`i;%cFt+d{+Qk20y4;FKtrkmp4a)obTYcsuY@9q@2S!Xpna6zYOQaDly(l2Y$gwy zZng|B04*(lUH9M0|`7iBiDArHQD70zBpK77(DoV}2IRq|~k-w2XTlV_AblN{NB z%DfZRZw^v$44*uSa&3k*L@mrw7}8%zMRR%26|Dh(b056rwg6uPe?|It!e5pC^Wh(1 ze{&YzQ3*aBr$dkk`=;@wR5+>AMH8i)R62oV>2lI?cWAVOB^3&9$Iq}O1uY59h<4QJ zo7^lXjew`TflVePHE-$;=;GUVs4sLoxnBKe^;Tm;<}+nJ-AeNr^iaQUW69dhX|;Yq z=f!mGCCGQ^Wc`K@sG~-bXQ*Wzpy}m`)+1_0_FCT(OeZ*S9^bpTsc7D`>r|A2zlV2p z;;&#B%itMv^9La4c%SO`5luRwez7Zj{CU6u_yhbO$A4;>ZTSBr{I9o7e2#pNUw@WK zzk7|!ZofVc7`a!O;q^vk7sp>>8uR$d?jEPI8{ebZmI+b5n8TIbjEvI<3riSlVou7u zN@=q)N;(_Si8ceD8~8vMkFz?Cr%ckYV+i>OLZ2+a2&D1IBXA#Be^`vX^Nl9H#^D^r zht%s1RQRK69NM!Oi=^9-6?3HJ+4w-oNT=O~VH3Nd(EBh za?(JtwfEq_#})A4bVK}9xCWg7xk>__2A4mPbTvG+8;y7`rHFSC*od#=z&u5Db=4)* zn>8%F{(`T&!g#K_}}Pmh}1 zJWdCeFl)SIW3h`*GjMEubiCwXfr^A<9ei*A@|Dhzx-u_ML|k6$H7xsJN1zWFWB#Lu zzsB$mm81}hf8dDl*^+$yQcOiV@Tz;4JUot%vnvLkgQb%2`jCeK%LEu15L6t7@w`=O zeVW%mqpc6iHu)EnP5OJ_59dUUaEemU9(D@b?m)CVI{SRu&8inGSfKcN1ifEN-g1gnBP{o{oj33g=%lpd#oJ(2Sl*BavjsAY90F z)W7cO;q~fEng6DE=0X{7GzS)b0E`R&jUVwvXCua=e9!{HHlzj)X!xvzp)=5C$aVNc zrwC5}Sm*XONKR=lMR`V?AT!>5v-;Jik%WI@GLmKD z&G#tJ1&AeJG$=8P;2O+v$(WtMPqJ_vPb7l}%DZ9X8qF|_hoHk9uaDY2@KG>1 zjWM1bgbW(*v_4rouH&U-;`H$jr^lC7SSRd)L(V74r?U^TU$>aL-8Q;qsEiB_x@C>G zzPnyt!)~va7vN0ZF_G8O$R@fX`04cJUqXBclPKBG+Q{O#N4P*9g(Iy;xRN7m6J3=T z1M_5g$7J3y&T9})aebF5UvLyFqae`ZJ!me`gmy;;0~Hpr!I&ODGfg$#kwKYiydz@A zka;(!5<)E`Yy=n+T{4Dr6coHgy4=gq27;Z+kY~adcKqdk71eS`v%bO=$4Y|M1rC4Q z1K)Lk5IsWEL(qy0$V{`_^o_ovL$9`#J4Sph%93AVfSLql{L86Y{$8TN(XPO_p}#Tt zEULOCa+A_ZAuA*-2#-yU@~fyS(Wlg13|L>*fmPRmjjTpCsuFceT%o=hkp!um#&>kf zQrGT@pehrj68%9pk-8q9CQ5HycQLIHg z-7P07DBu$_z&-^$kOB59;FTHRfC65c0Tva|%>V}#@BAk)5+8a`) zcj3~zWXD%XU`73MD5*CS>Dk)!wcT+%pxE=MG68xXR3<>rW6A{Rc}ST6J&!08fZoha zU(zK26^r|=Pll++u$F-C!^tBGlD?T-Rgm<# z_r8W>+5w>r!~Y~OJwwYL0RBq>*aN_SD*$@{_#Xve4*>tG0PJDC?0b;in8S+`SZirw zPtaa?E|i+&MOb`ou@`Rb`t|sF5F@|tzVNDS#IrIHcc&5fqBr2pK6~L$Qdk|A;ux&( zd^U|j#EpapHhh1J9^WMM{vb_IAGa4CO+!ddNq9$0Q9C@4QqJ(&`FIW9(|Ip~PZW-i zulxtK0``^Z+m~bF8+161B0{vqroO~^xddiE#3{3l_hcLAow=HxTZwNQqYqFh{u)w{n1dVVzC>41{7E*WQGV5+NRouvqHJ z*Aa(rl(@+^;K2u8Cb7)(j=AFA(jyP~ZUd{sa}f78w6u_SkG5zd7qF24Ja43SlVt}5 z;ET$S8rqF}4#XyG1^E2Sv_4#0#s`B;`$*VXeiY2ITdpaVfRFx$S55#r@pc0$D8+N4 zu(JZi(5$R%!f!Ocg40mg>RBrq^p|7lZ6B-(pzp_gEFtjm2UsdIAslrbmX}#qNuml|~YTz~D z2qsnWp;c@Se+vbp{BOhAehZeP*m2QM1mJJE<6X@pvb}=0167g9sppN6)D&QK)(X82Y>+uU=IMe7%Z}|2Y^8ZU=IK# z1z-=aD_~*LUJV{KnSJnK)G)mOpOgGQ4995jd#pO{P%`>7n@wG8k+-i7cp33)G>bu(HR$uiL$X#sotW)2@%_Wy z$ct+#1}vnO4C+Z6zz}ybcf!T=gs2lbWbd5^a314?Ipx{$dw|yua~4B)VKW^uI63G| zTQ018hDS0poPiV~RM=-W`|No}haDKYNzVvKa^mw_?XjD<&9Xlb3X&rjB4MeygfDPM&sNqo$ zWE9Oerl0x|Y5zag&ICTPs!I6P^{U>hS4)ygB~_KAI~5wz6sbzmsj#G*rMpFtO%y>$ z(zXrVY)z}BY24^9A?mmbio|6iMr}tO#bv}@Mg(UZR}^p;#f?R9*O_q}@%x{1-+lGc z)u7+^oBnm`y?5@p`*!ZRXSq*VHImm?l-@1bH9=okB+3?`F97;1Kwkj#TY$a*7_b0+ z`KT2otiF$S#41Ad1M*B%KPZo6^+WndOjkd`i|i>vD;&mC_*?P)Ye~%Mjy1o?bnxEF+PwDS% z{2HV$+P9?Or$1|$wvc{sixTLSyFt3NDtEdW z()Dtt>IPku-c94lJYbILKU3JzsrY*NG*!@&Yavau20>D`NRTvf?TIGF=xN9fOHZis zFs|cGvJq_Um%o9dofX6s8qPF>l5^F28P?p2&O$wE$enTF~>bK;Ie3%Ha0CSrf<&sY&rACI!8bnB(5%nFiI|P@E-8%3$xJ6u_*X zNs)sQ*|q%KB_LN1fU$Tg!E)dSbKil zL&TA!GqwHbu_lH`J2oubNUf^(0mOs>!4GQiQ{12^cN%%=+L;pdAr0Fa1gR%lb-1a~ zAsRM_^U~UoY_u4CfRediBXss)-vv=Xo4LnP77#TbPum*T6jEH;(kYjF>fch<(;hU*F+XGFL=&-OJQjO3_)d8m z9n}fW(uR*F>aCrwA9t42A0T#Jy4LvR#xLA*^=DFgoR#;EB)saajF9GQfb8S8G@Xq* zDQ!t)8&XQgHD5~@sSLe|zS#O2dUxuT`~VR`N_*(|X8_<`G2~q{3ox|z(ICjU_v13yw@S>UH@RpcRVA7Qo09swhZz|9*3>x&^)O0iNp0=m{f-%5tve)T>co8Ar@`r6GmN*oS^LCPH9$vWDa7wW~Q{u@N%Lb5T}S}y$Q6q z13#y0jpTol~fs{K+mLyUjo8L1X6Y4j@5a2F_4+kvE#LK z_|R!P%8Sn1^0Dw9hTaMSdJPR%yoP5r;!5oZ2T#CjO1avAo6=Y-t#V*9C9xv3snl!S z^*7DP%)z8c^`}xxvNu0G~h`p5I#iOM#A`p)VDblwzl@B*Dt10%h}JDfksaDqSIRQfT#spiO(4>r+< zOx!<{bSXWB`Jh-iJ%#Bo=GZ`b>Rw(D8C(k1o2jq!T})5i&co(^QJDY740Lt=b@HBU z<^R|q|N7L+!EWWhpD#DlpPriIg%a%5Z%VL>muLyZ7!M^llZRjF8!vPx7Gy{8uQ&CW zH1L^Ir0t{)*79fDfK}hN!|6deCZ^!O#ro1yaK$6V`8i)S2lGfWgT^+^AtN(dn9rK< zTX^`Dby^tNt!!c5Y2ahMLE|45WSX5~i<5+ zecLjLzFjv5-F#{(NQEX4_JP}YaO!3BR#;9%%1vyf)2Gv6U3}_N+Yp3l9sE`ud{YN6 zs6(U%>7WMbRW(SHH&!qXuEF!Df!}0^rVAW)*v44WR{J1N`*n17l&hV@zdCb($sQBp z6s-GCyAXq>I__2dR3>&yRbe;ul1)~-Nq;q!Yf#2=`>WI5_Z*NnXbmqozwWX*Ze60Gwq3`U2qb7N9Qx&b9!3X~e5PXJv1Gm>)Fs%*XrXhI(|Ui~eP^7J(q&_mT<@h(f1^vO1!^jRL1Uji;JSN~ul$d`$Rw5jVQ-`w2h`r zOmDfF096D+*5WR)Uf`#B4~U~aG-Rs!S6)Nuj(ZFjN2^nsZhXo`Nvis1Fyhs}=|x|-wYpX#_Z-mLnLZmIat3QFm5{6O<*y8h;BSK5gnH}MaGXjbML%D2+w zt7g+dD8~fJ|Cw0jAbIAOOiYu>aEVXh7DV%mOtkWJ;|1>A-n7lsj34dQYxiu~BDAy~ zDrD(?qFsme1;9BLpfC4?%kP)Tuja?n4HBk^ z#*r^)T5jEC;vmPI;UO+r8LM&CeOi|Fz^VGcjURLt(~|>4Pi>&WJ24AU;3wTA;$aiU z1aR~|2u0V`fyO6#P|vU#dt{I}AZxgRwP-ppB*?0ha&GG**IP)iHX=2RnY7zvG9hU%)ZwQ0^TF|3 z8c4J##Wtpx@q@KG1!Wb(#MmVA_RM*fY;YX%YP)^X(`dF|O*16X?Ks zJqK#%p|LW-RX0+W{w$U?In=9hH1FYfAyYyNk!un!pPgCmdDuY@I!m_brRUQdIn3AZ zw#KAsYEF6?{7JdNu_(*Qz+?*wrej~?; z7Brqit)pcJDx?zb@H8K&bGb94zgP7{TUW81twLz)DrA=&R7IH4)^bJ$1+yPN7bq`9E~}M+EAv@X`4A<kB$2*Vd2CyVuqy^4_)edOme+{YWvMS85E2x~}NtPA_oePBgk& z-*LVtAj<{OLi4HO(!P&MW{9L?E>Pw+cDaRqd>d8akMqhDY@(QhD7Uxig}iyTX|kR+ zOb$&2An9QX5 zwkMkJm#|(9>tugQC_z7#5P+No#jMfJ_&}<;<>aD2lw6ud2tt#{9Xg6XpPBIp{$TUV)49C z<9SP2>@Vsq>SNZEU<{fL!-u>sZh=rsm3B+GKlXFM(&*l@rpja}q3)*QBzw2N)#`s} zSmPtQ76%1uyk5q2JhqP18QRR}eyPWR3~qPfVGe3z@O|UebpT9T7@g98Qd|+`+M6~N zks~JrN>Piu#Vs3Imc84#%yMUgl2B-Ff|sWE=99;y^(`mFffu#p7-+s}jK;^smh|fK zt<|DkQGFpj7#2`pZ269(F7Kdx#VgWq4Qj*C$GvW8msh^xsr5wh3QY1|aiKX{#=Tc8 zHSYCF9QcK2e#LQLclStpBx3I-4Ol<4htx5sqV!e9ElmuHvQ9S>21ftVUJL?-EiAPA z1MC0#*Q>A2YLRG+WbFdecdqu3(2kz4C=L^~r8ml->@~7k__KPAml{XEk~NGy$#mE^ zQGPlaoz$%M84lKSmf*BQX2mykPsKh>C%#L{?WjH9FVRS56X?nGWP2zM>>CFw77*PX z$qu)AIIWpU3Lsew0dz>ULO{+~hjvGg^P9gWrTVyeHieFI^|w(7KthfgqHMx9Ahdpb z%)f+j*aum^-f? zC76U28~nJ<&k{U=rKH}mC+9PmKf=@i5CdoHPv?e_~%&wk^$y< zbItD)#b{)LYrlnN=yL78ZW`*O=di$77FRxTQjSl*I!sz-PRdgD&(fEpNlp}U172NE zV6drIhj@4NxB3;b6n7E-&C`tE+lb2;Cay$m7Q%RL^)TL(`(tp;P=N)bfys@|TJm zG3g>9kVoS`K@5fWbS=wzBv;;P`GWH$38k0o55}89;nAh>rfh}HuWD^%{yVDS8K*wI zY33<6UbPOc(Uz1HOUn%y7hk`$wCMTN{XJ554AM4*3>r?4W}Uj{=m%1_P8~ZT8ju-f zb#ry`_skYB=<43!nH{3b5KW$LLBe%1d8Cx3ei9u9WG|`E^3{tu?aWj9v3qdDK#zP=z$02 zb1WptC?0>WF5~6K%cV%VMzTD`-YaD)E|-=W&9a*#JuA~K-uzE~XXDNP;!&;BCKx5S zcC?f#HBu>MK8ZmXq-d3>$g#tx#=PKDRESAG8AI+h_C9IX>X9T6(w0Z?A)_o1e$kvv zuCs;@xsL;GI;X*Mgm!CJ2)p;m9H7x`mGgc;c2G(wE4uTTzlQ1rs|DX`OV*fb=A+d}^d`~ag- z8MUhNbf0Oclo7FkF#4fnkgRT?sv=LD6kcnL1A|~|CDWCQGqC=%HAX~gsUwkEc2i4B zdWn?MV^7TCz%)+$oL^7EO1S6A_c$Bw!D)aro$L}vBa}r%_6Hf@wsQT!BO<3nggE=&q1#vJIDxa?=Q0{MXSopz6$lo8^}*2Y{! zucgu9UqYh94{+%0fV1W+8Ed#_e3HjQvQcYQzAPfyzbB62hwPT|3qJ%#*V!<)ge*22 z+>~r^%QE*NAALjR>QdXsQp*8@6Hj^>EoWaTJ{@^>--GVl)3{XzxUHtLR$k|hoZ%E3 zT+8XwF5-5uP;|fUX5af4>5oyJgXtbD!z24!r8KZ66`k+pL{m=o8Luu|i>GsK#M6n! zA}PC(14=rIB9jmlnUyTShfH!6z9I%m(!kq^(zV8>ZCBDnZA$dI<)~G!CXt;q(;jnN zX4bnY%zJ3Df{>ZnX}VT6P3*7JHp(dsi!-{M97UI0wrJhLsEfYK=EdX{`9|oW*axNm zl`dcb8l{q5Skx0TfzKMBW=6Y8@nmrY!YJk<H26!t4S)hmxtFB611`FsLiNa!c9D! zKw&_J*e&uq*6ot2Q(Kc@ec`(_Gl_Moo{_{9BN*WVLgj|nxLFxUq#(INr}1~olfP(oie1$Gx3eMb|_Cj92Y|Z z3~8*X4=`&p;O0Y(yJRrJNT8B}r!XDVSn+V!B-npjwU#wKsduc^@FI_BPm$2)k z@LUyOWm&y#lf(CuTi+u?OQ&2D$?9%p8y1_HZ>^d;9hu@4i^bR^rQ4x?-w(+gwr zOJ^x}t>8w^0`byrG{5iGE13qxs2H8_L zSi$Jq|E8};``zYQbnTU2Wcg;(e+=&y35xKs(qgoU*w6V9&yPvl{GNDpfl6R}2A~7I zzX?Q{kz6r63j-(1qkH{LOuiWBIlRu?Neq^ODNMfJC9kaZrR93Jw^yFZzcF%g5c++~ zbryae0b55XUojQ?b)KW2MwfWX2M81hZC`ql!ZJw z_Gg*6OE5YDTcQk!UrIlFvC~2L1USao4jt2BsfujvGGzRc365r|>Ip#))z7YN5NK&; zxC<4aFi18N7HAemE~mMwuO*Vjjo)->vEtI+q>=n2Z?=)vC){lxV>w-TduhCmSUGQ# zNWw#;Syf1`EltHyT$H|6(sl*kk&|#~dM4+VC7r##Ej3PuD7F3clQWg{c&W&VBf~MH=YVzpz@pO7 zV*u^y>b=S*Y1a<7SWUt?bA(Fg-ff11ov}_{&vMh6QWkk%<9@j(m8O>)dF&9qh=zy< zX1epqvCawuyc)&}I2ij5I;I7p6T*99n+}#!D zbSIs4LLB1(gS&8h!xIj+7)wrVHt)KPPjfWfZuWv% z$|e8$byFX{<71&7oXj~3{Y0n(CzHzT8^d=z5aP;At@uF_`4Cy=qFgLpB2UqT(iZ@i zT7bTsA6_hNquU_OPv)U>HW|B!vdLM~qFh5~#pj<+d~RO%%M<%zH9RiJtBc0pY-Rm4 zR6!XVuzXCHruUsmYG$5$jc>&TW&W8UQylDSB;vjE$S8`nE=@vg0XH8n`JaS{w1kwV z4Z>!FV1T0CS1MIS?L^u!DL&F-|0?C&FNr2*1J#Udtp}=pzvSo48vP8j4Fzb8AK3_E z*l)n~e3*qMBR{cy*=ase3a=B=Yy1e?KVds;XDyqbBi(p$ejg7Cv|m1**Nh-9uWyMP zMozV_#?sGaV$zuMCSt=uyRsIpjZ;%e0^>{5niduW&n91Gz9zUt)i}x6D9=x69VL0F z3R5GQ$y_j*oIGdyYi`gTRp$SLq8?y9@;skN*lNB?9?5wG)YLQP|KTZPL_*~$!Sa-F zd20B2Y^Yrjw6)obDGCxEJ+ezV^Zx)Y&G2G*^vu7+e7#g2<@uM%qcS*;cpT5!`B%uZ zZ~m1$B>5M~Q|srbXq)U0@zS(2o!AcRIl(bqKc*YHRvAAGg5xeQr0c)P6sRS z*KL5At0-!yT|<#^oGS2vr7W0W&BqaS=${{66RYI;I*qUDc79T$1C>FqZ~H8}69VKp z^T9&NT2Ol|J~r1INJpY?c3Nv^ry1+l~yv99PAJ z4#JR;WtY1|QJi%>A>7wor&UBx}(TC?2=pVNA{I|aJ7>@ zO+VlwONYjO9U1#&U{v?gKu~C~+S6DZs$R-x7c0*tJh`wG4K)s8Pvy%R#X=hcvvIRP zPz+14uBiU;#L(Q$5TTAHq{Qaj@!bg8b6f~aQgVz0S%@^1eitRZyc>vr<~=tM(oz

+g4J9^c@*8|#1 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Ó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 diff --git a/UltimateAFK/packages.config b/UltimateAFK/packages.config deleted file mode 100644 index 5b7f9fa..0000000 --- a/UltimateAFK/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From 5d9f626989cc25d12151d4fd15aa13f147dbe420 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 05:58:47 -0300 Subject: [PATCH 013/146] Delete AFK.cs --- UltimateAFK/API/Base/AFK.cs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 UltimateAFK/API/Base/AFK.cs diff --git a/UltimateAFK/API/Base/AFK.cs b/UltimateAFK/API/Base/AFK.cs deleted file mode 100644 index b41877c..0000000 --- a/UltimateAFK/API/Base/AFK.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace UltimateAFK.API.Base -{ - internal class AFK - { - } -} From 66ddd180291d0b5d8811845c938af58e58d55caa Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 06:10:10 -0300 Subject: [PATCH 014/146] Update Cerberus.props --- Cerberus.props | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cerberus.props b/Cerberus.props index f4daa2f..0129471 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -1,4 +1,5 @@ + @@ -15,7 +16,7 @@ - 3.2.1 + 5.0.0 false From 5f8a32a7d0c3d58db264102ba9ccb93511a3b1b0 Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Mon, 2 May 2022 06:14:04 -0300 Subject: [PATCH 015/146] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c9c1cd8..4ae78ba 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +| ![Github All Releases](https://img.shields.io/github/downloads/SrLicht/Ultimate-AFK/total.svg) | Releases | Support | + # Ultimate-AFK This is an updated version of the original Ultimate AFK plugin from https://github.com/kingsplayground/Ultimate-AFK. From b257a503cb07f43faff849aced2d7165aa8ffb9a Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 06:22:54 -0300 Subject: [PATCH 016/146] Correctly eliminating things --- UltimateAFK/Handlers/MainHandler.cs | 4 ++++ UltimateAFK/UltimateAFK.cs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 41adaf7..89bdebd 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -14,6 +14,7 @@ namespace UltimateAFK.Handlers ///

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%0Dx@zu5KFq{!#WpguPO;SDO4H7S%%HpiBB2?84qFT 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

public class MainHandler : API.Base.Handler { + // A list of players who are replacing someone else public static Dictionary ReplacingPlayers = new Dictionary(); public override void Start() @@ -58,6 +59,9 @@ public override void Stop() Exiled.Events.Handlers.Scp079.GainingExperience -= OnGaingExp; Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; + + ReplacingPlayers.Clear(); + ReplacingPlayers = null; } diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index eda284a..3614680 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -43,6 +43,8 @@ public override void OnEnabled() public override void OnDisabled() { Handlers.ForEach((h) => h.Stop()); + Handlers.Clear(); + Handlers = null; Instance = null; base.OnDisabled(); } From fa4241f5fb0cd58b55f6200a8e11da195a3888a2 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 06:30:23 -0300 Subject: [PATCH 017/146] Update MainHandler.cs --- UltimateAFK/Handlers/MainHandler.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 89bdebd..10f5d59 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -36,6 +36,8 @@ public override void Start() Exiled.Events.Handlers.Scp079.GainingExperience += OnGaingExp; + Exiled.Events.Handlers.Scp079.ChangingCamera += OnChangeCamara; + Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; } @@ -58,6 +60,8 @@ public override void Stop() Exiled.Events.Handlers.Scp079.GainingExperience -= OnGaingExp; + Exiled.Events.Handlers.Scp079.ChangingCamera -= OnChangeCamara; + Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; ReplacingPlayers.Clear(); @@ -177,6 +181,11 @@ public void OnGaingExp(GainingExperienceEventArgs ev) } + public void OnChangeCamara(ChangingCameraEventArgs ev) + { + ResetAFKTime(ev.Player); + } + public void OnShooting(ShootingEventArgs ev) { ResetAFKTime(ev.Shooter); From e15f56e958ad9db66d528291a27b820b12324929 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 20:51:31 -0300 Subject: [PATCH 018/146] Supposedly it didn't work but.. I tested it myself and it works :) ME ESTAN RE CAGANDO --- UltimateAFK/Config.cs | 2 +- UltimateAFK/Handlers/Components/AFKComponent.cs | 6 +++++- UltimateAFK/Handlers/MainHandler.cs | 4 ++++ UltimateAFK/UltimateAFK.cs | 4 ++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 67a5636..468dedf 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -16,7 +16,7 @@ public class Config : IConfig [Description("The time it takes for a player to stand still before he is detected as AFK")] public int AfkTime { get; set; } = 80; - [Description("After being detected as AFK a message will appear on his face and he will be given this time to move or he will be Kicked/Moved to bystander.")] + [Description("After being detected as AFK a message will appear on his face and he will be given this time to move or he will be Kicked/Moved to spectator.")] public int GraceTime { get; set; } = 30; [Description("The number of times a player must be moved to spectator for a player to be kicked from the server. Use -1 to disable it")] diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 6aedd86..26508f8 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -100,10 +100,13 @@ private IEnumerator CheckAfkPerSecond() #region AFKChecker | CRINGE AAAAAAAAAAAAAAA private void AFKChecker() { - bool cantContinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || !Round.IsStarted; + bool cantContinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; + + //Log.Debug($"Can continue ? || Player is Dead {MyPlayer.IsDead} || Player list is low {Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers} || Player is tutorial and config says no {UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial} || Round is in lobby {Round.IsLobby}"); if (!cantContinue) { + #region Check if player is 079 or 096 Scp079Role scp079Role = MyPlayer.Role as Scp079Role; Scp096Role scp096Role = MyPlayer.Role as Scp096Role; @@ -134,6 +137,7 @@ private void AFKChecker() if (isMoving) { + this.AFKLastPosition = position; this.AFKLastAngle = vector; this.AFKTime = 0; diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 10f5d59..2d59b21 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -19,6 +19,8 @@ public class MainHandler : API.Base.Handler public override void Start() { + Log.Warn("Loading MainHandler"); + Exiled.Events.Handlers.Player.Verified += OnVerify; Exiled.Events.Handlers.Player.ChangingRole += OnChangingRole; @@ -39,6 +41,8 @@ public override void Start() Exiled.Events.Handlers.Scp079.ChangingCamera += OnChangeCamara; Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; + + Log.Warn("MainHandler Fully loaded"); } public override void Stop() diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 3614680..bf34784 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -23,9 +23,9 @@ public class UltimateAFK : Plugin public override string Prefix => "Ultimate_Afk"; - public override Version RequiredExiledVersion => new Version(5, 0, 0); + public override Version RequiredExiledVersion => new Version(5, 1, 1); - public override Version Version => new Version(5, 0, 0); + public override Version Version => new Version(5, 0, 2); public override void OnEnabled() { From dc257ae1b4e8ac850a57466ab102b5fddad039e4 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 2 May 2022 21:02:24 -0300 Subject: [PATCH 019/146] ignore this --- UltimateAFK/Handlers/Components/AFKComponent.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 26508f8..a4ae574 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -41,6 +41,7 @@ private void Awake() if (!(Player.Get(gameObject) is Player ply)) { Log.Error($"{this} Error Getting Player"); + Destroy(); return; } From b1eaa7cb70963969cd163ed645d8e8315cab1e8a Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Wed, 4 May 2022 01:07:28 -0300 Subject: [PATCH 020/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ae78ba..c9e07aa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -| ![Github All Releases](https://img.shields.io/github/downloads/SrLicht/Ultimate-AFK/total.svg) | Releases | Support | +| ![Github All Releases](https://img.shields.io/github/downloads/SrLicht/Ultimate-AFK/total.svg) | Releases | Support | # Ultimate-AFK This is an updated version of the original Ultimate AFK plugin from https://github.com/kingsplayground/Ultimate-AFK. From 8ec1d90807ecb58bef7d86d9046df41558ddaa33 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 14:37:02 -0300 Subject: [PATCH 021/146] DebugMode --- UltimateAFK/Config.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 468dedf..ff8c6dc 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -7,6 +7,9 @@ public class Config : IConfig { public bool IsEnabled { get; set; } = true; + [Description("If you have any error in the plugin operation activate this and create an Issue in Github https://github.com/SrLicht/Ultimate-AFK/issues")] + public bool DebugMode { get; set; } = false; + [Description("If the number of players is less than this the plugin will not work.")] public int MinPlayers { get; set; } = 8; From d3e75c3baecb99be9b69fa495928fa4f0f51f1af Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 14:37:04 -0300 Subject: [PATCH 022/146] Delete GlobalSuppressions.cs --- UltimateAFK/GlobalSuppressions.cs | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 UltimateAFK/GlobalSuppressions.cs diff --git a/UltimateAFK/GlobalSuppressions.cs b/UltimateAFK/GlobalSuppressions.cs deleted file mode 100644 index 92e5b5e..0000000 --- a/UltimateAFK/GlobalSuppressions.cs +++ /dev/null @@ -1,10 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; -//Shit zzzzzzz -[assembly: SuppressMessage("CodeQuality", "IDE0051:Quitar miembros privados no utilizados", Justification = "", Scope = "member", Target = "~M:UltimateAFK.Handlers.Components.AFKComponent.OnDestroy")] -[assembly: SuppressMessage("CodeQuality", "IDE0052:Quitar miembros privados no leídos", Justification = "", Scope = "member", Target = "~F:UltimateAFK.Handlers.Components.AFKComponent.CountHandler")] -[assembly: SuppressMessage("CodeQuality", "IDE0051:Quitar miembros privados no utilizados", Justification = "", Scope = "member", Target = "~M:UltimateAFK.Handlers.Components.AFKComponent.Awake")] From 133218d0ee4a0351ba1b533d3d239c5f214fd11f Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 14:37:41 -0300 Subject: [PATCH 023/146] Update Component --- .../Handlers/Components/AFKComponent.cs | 255 +++++++++--------- 1 file changed, 121 insertions(+), 134 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index a4ae574..fffce8e 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -1,39 +1,37 @@ using Exiled.API.Features; using Exiled.API.Features.Roles; +using Exiled.CustomItems.API.Features; using Exiled.Events.EventArgs; +using Exiled.Permissions.Extensions; using MEC; using System; using System.Collections.Generic; using System.Linq; +using UltimateAFK.Resources; using UnityEngine; namespace UltimateAFK.Handlers.Components { - /// - /// Cringe componente for Players. Warning doing an AFKChecker is not a pretty thing to look at but I will try to document so you understand what I am trying to do :) - /// public class AFKComponent : MonoBehaviour { - #region Variables and Stuff - public Player MyPlayer { get; set; } + #region Variables + public Player MyPlayer; + public Player ReplacementPlayer = null; /// /// If True, the component will ignore this player, but will not be destroyed. /// - public bool IsDisable { get; set; } - - public Player ReplacementPlayer { get; internal set; } - + public bool IsDisable = false; + // ------------->>>>> Sex <<<<------------ + public Vector3 LastPosition; + public Vector3 LastRotation; + // -------------- Gwa gwa --------------- + public int AFKTime = 0; + public int AFKCount = 0; + + // Coroutine handle // Using a MEC Coroutine is more optimized than using Unity methods. private CoroutineHandle CountHandler; - - public Vector3 AFKLastPosition; - - public Vector3 AFKLastAngle; - - public int AFKTime; - - public int AFKCount; #endregion private void Awake() @@ -53,112 +51,84 @@ private void Awake() // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. CountHandler = Timing.RunCoroutine(CheckAfkPerSecond().CancelWith(this).CancelWith(gameObject)); - } - private void OnDestroy() - { - Destroy(true); + if (MyPlayer.CheckPermission("uafk.ignore")) + { + Log.Debug($"The player {MyPlayer.Nickname} has the permission \"uafk.ignore\" disabling component"); + IsDisable = true; + } } - // With this you can destroy the component in a more controlled way. - public void Destroy(bool value = false) + public void Destroy() { try { - if (value) - { - // This is to avoid a loop where the component constantly calls OnDestroy because Destroy calls OnDestroy and OnDestroy calls Destroy. - Exiled.Events.Handlers.Player.Destroying -= OnDestroying; - Exiled.Events.Handlers.Player.Jumping -= OnJumping; - } - else - { - Exiled.Events.Handlers.Player.Destroying -= OnDestroying; - Exiled.Events.Handlers.Player.Jumping -= OnJumping; - Destroy(this); - } - } - catch (Exception e) - { - Log.Error($"Exception: {e}\n Couldn't destroy: {this}\nIs ReferenceHub null? {MyPlayer is null}"); - } - } - + MyPlayer = null; + ReplacementPlayer = null; - private IEnumerator CheckAfkPerSecond() - { - while (true) + Exiled.Events.Handlers.Player.Destroying -= OnDestroying; + Exiled.Events.Handlers.Player.Jumping -= OnJumping; + Destroy(this); + } + catch (Exception) { - if (!IsDisable) - { - AFKChecker(); - } - yield return Timing.WaitForSeconds(1.2f); + throw; } } - #region AFKChecker | CRINGE AAAAAAAAAAAAAAA - private void AFKChecker() + public void CheckAFK() { + var cantcontinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; - bool cantContinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; - - //Log.Debug($"Can continue ? || Player is Dead {MyPlayer.IsDead} || Player list is low {Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers} || Player is tutorial and config says no {UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial} || Round is in lobby {Round.IsLobby}"); - if (!cantContinue) + if (!cantcontinue) { - - #region Check if player is 079 or 096 - Scp079Role scp079Role = MyPlayer.Role as Scp079Role; - Scp096Role scp096Role = MyPlayer.Role as Scp096Role; - + bool isSCP079 = MyPlayer.Role is Scp079Role; + bool isSCP096 = MyPlayer.Role is Scp096Role; bool isNotCrying = false; - bool is079 = scp079Role != null; - bool is096 = scp096Role != null; + Scp079Role scp079role = null; - if (is096 || is079) + if (isSCP096 || isSCP079) { - if (is096) + if (isSCP096) { - scp096Role = MyPlayer.Role as Scp096Role; - isNotCrying = scp096Role.State == PlayableScps.Scp096PlayerState.TryNotToCry; + var role = MyPlayer.Role as Scp096Role; + isNotCrying = role.TryingNotToCry; } - else if (is079) + else if (isSCP079) { - // Unnecessary, but whatever - scp079Role = MyPlayer.Role as Scp079Role; + scp079role = MyPlayer.Role as Scp079Role; } } - #endregion Vector3 position = MyPlayer.Position; - Vector3 vector = is079 ? scp079Role.Camera.HeadPosition : (Vector3)MyPlayer.Rotation; + Vector3 vector = isSCP079 ? scp079role.Camera.HeadPosition : (Vector3)MyPlayer.Rotation; - bool isMoving = position != AFKLastPosition || vector != AFKLastAngle || isNotCrying; + bool isMoving = position != LastPosition || vector != LastRotation || isNotCrying; if (isMoving) { - - this.AFKLastPosition = position; - this.AFKLastAngle = vector; + this.LastPosition = position; + this.LastRotation = vector; this.AFKTime = 0; - this.ReplacementPlayer = null; + + if (ReplacementPlayer != null) + this.ReplacementPlayer = null; } else { - AFKTime++; - bool isNotAFK = AFKTime < UltimateAFK.Instance.Config.AfkTime; + var isAfk = AFKTime++ >= UltimateAFK.Instance.Config.AfkTime; - if (!isNotAFK) + if (isAfk) { - int num = UltimateAFK.Instance.Config.AfkTime + UltimateAFK.Instance.Config.GraceTime - this.AFKTime; + var graceNumb = UltimateAFK.Instance.Config.AfkTime + UltimateAFK.Instance.Config.GraceTime - this.AFKTime; - bool isInGrace = num > 0; + var inGraceTime = graceNumb > 0; - if (isInGrace) + if (inGraceTime) { - var message = string.Format(UltimateAFK.Instance.Config.MsgGrace, num); + var message = string.Format(UltimateAFK.Instance.Config.MsgGrace, graceNumb); MyPlayer.Broadcast(2, message, Broadcast.BroadcastFlags.Normal, true); } @@ -166,72 +136,91 @@ private void AFKChecker() { Log.Info($"{MyPlayer.Nickname} ({MyPlayer.UserId}) Detected as AFK"); - if (MyPlayer.IsAlive) + //--- I am going to save the variables that I will need later + + var items = MyPlayer.Items.ToList(); + var role = MyPlayer.Role; + var plyposition = MyPlayer.Position; + var health = MyPlayer.Health; + var ammo = MyPlayer.Ammo; + var customitems = new List(); + + foreach (var item in MyPlayer.Items) { - var items = MyPlayer.Items; - var role = MyPlayer.Role; - var plyposition = MyPlayer.Position; - var health = MyPlayer.Health; - var ammo = MyPlayer.Ammo; - ReplacementPlayer = Player.List.FirstOrDefault(p => p.Role.Type == RoleType.Spectator && !p.IsHost && !p.IsOverwatchEnabled && p != MyPlayer); - - if (ReplacementPlayer != null) + if (CustomItem.TryGet(item, out var citem)) { - MyPlayer.ClearInventory(); - MyPlayer.SetRole(RoleType.Spectator, Exiled.API.Enums.SpawnReason.ForceClass, false); - MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, false); - MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); + customitems.Add(citem.Name); + items.Remove(item); + } + } - MainHandler.ReplacingPlayers.Add(this.ReplacementPlayer, new Resources.AFKData - { - AfkComp = this, - SpawnLocation = plyposition, - Ammo = ammo, - Items = (List)items, - Is079 = is079, - Level = scp079Role != null ? scp079Role.Level : (byte)0, - Xp = scp079Role != null ? scp079Role.Experience : 0f, - Energy = scp079Role != null ? scp079Role.Energy : 0f, - Health = health, - }); - - if (UltimateAFK.Instance.Config.AfkCount != -1) - { - AFKCount++; + var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC")); + ReplacementPlayer = list.FirstOrDefault(); - if (AFKCount > UltimateAFK.Instance.Config.AfkCount) - { - MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgKick, "white"); + if (ReplacementPlayer == null) + { + Log.Debug("Unable to find replacement player moving spectator", UltimateAFK.Instance.Config.DebugMode); - MyPlayer.Kick(UltimateAFK.Instance.Config.MsgKick, "[UltimateAFK]"); - } - } + MyPlayer.SetRole(RoleType.Spectator); + MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); + MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); + } + else + { + Log.Debug($"Replacement Player found\nNickname: {ReplacementPlayer.Nickname}\nUserID: {ReplacementPlayer.UserId}\n Role: {ReplacementPlayer.Role.Type}", UltimateAFK.Instance.Config.DebugMode); + + MainHandler.ReplacingPlayers.Add(ReplacementPlayer, new AFKData + { + Position = plyposition, + Role = role, + Ammo = ammo, + Health = health, + Items = items, + CustomItems = customitems, + SCP079Role = scp079role + }); - this.ReplacementPlayer.SetRole(role); - } - else + if (UltimateAFK.Instance.Config.AfkCount != -1) { - MyPlayer.SetRole(RoleType.Spectator); - MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); - MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); + AFKCount++; + + if (AFKCount > UltimateAFK.Instance.Config.AfkCount) + { + MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgKick, "white"); + + MyPlayer.Kick(UltimateAFK.Instance.Config.MsgKick, "[UltimateAFK]"); + } } - } - else - { - //It does nothing because the player is dead, so why detect him as an afk? - } + Log.Debug("Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); + + this.ReplacementPlayer.SetRole(role); + + MyPlayer.SetRole(RoleType.Spectator); + MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); + MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); + } } } } } + } + private IEnumerator CheckAfkPerSecond() + { + while (true) + { + if (!IsDisable && Round.IsStarted) + { + CheckAFK(); + } + + yield return Timing.WaitForSeconds(1.2f); + } } - #endregion - #region Events // Technically this is not necessary since the component is destroyed when the player is destroyed but for fear of leaving a ghost component I better do this. public void OnDestroying(DestroyingEventArgs ev) { @@ -249,7 +238,5 @@ public void OnJumping(JumpingEventArgs ev) AFKTime = 0; } } - - #endregion } } From 5c8cbc4166f231aa1d572a67e239c977801c53e1 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 14:38:32 -0300 Subject: [PATCH 024/146] Now support CustomItems and Replace work well --- UltimateAFK/Handlers/MainHandler.cs | 107 +++++++++++++++++++++------- 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 2d59b21..27eee9d 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -1,7 +1,7 @@ using Exiled.API.Features; using Exiled.API.Features.Roles; +using Exiled.CustomItems.API.Features; using Exiled.Events.EventArgs; -using Exiled.Permissions.Extensions; using MEC; using System.Collections.Generic; using UltimateAFK.Handlers.Components; @@ -73,57 +73,103 @@ public override void Stop() } + // Player Join server public void OnVerify(VerifiedEventArgs ev) { - if (!ev.Player.GameObject.TryGetComponent(out var _)) + if (ev.Player.GameObject.TryGetComponent(out var com)) + { + com.Destroy(); + + ev.Player.GameObject.AddComponent(); + } + else { ev.Player.GameObject.AddComponent(); } + + } public void OnChangingRole(ChangingRoleEventArgs ev) { if (ev.Player != null && ev.Player.GameObject.TryGetComponent(out var component)) { - if (ev.Player.CheckPermission("uafk.ignore") || ev.Player.SessionVariables.ContainsKey("IsNPC")) + try { - component.IsDisable = true; - } - - if (ReplacingPlayers.TryGetValue(ev.Player, out var data)) - { - ev.Items.Clear(); - ev.Items.AddRange(data.Items); + if (ev.Player.SessionVariables.ContainsKey("IsNPC")) + { + component.Destroy(); + Log.Warn("Destroying the component to a player who was an NPC"); + return; + } - Timing.CallDelayed(0.8f, () => + if (ReplacingPlayers.TryGetValue(ev.Player, out var data)) { - data.AfkComp.ReplacementPlayer = null; + Log.Debug("Detecting player who replaces an AFK", Plugin.Config.DebugMode); - ev.Player.Position = data.SpawnLocation; - ev.Player.Broadcast(16, UltimateAFK.Instance.Config.MsgReplace, Broadcast.BroadcastFlags.Normal, true); - ev.Player.SendConsoleMessage(UltimateAFK.Instance.Config.MsgReplace, "white"); - ev.Player.Health = data.Health; - ev.Player.Inventory.UserInventory.ReserveAmmo = data.Ammo; - ev.Player.Inventory.SendAmmoNextFrame = true; + ev.Items.Clear(); - if (ev.NewRole == RoleType.Scp079 && data.Is079) + Log.Debug("Adding items from previous player", Plugin.Config.DebugMode); + foreach (var item in data.Items) { - var scprole = ev.Player.Role as Scp079Role; + ev.Items.Add(item.Type); + } - scprole.Level = data.Level; + Timing.CallDelayed(0.8f, () => + { + Log.Debug("Changing player position and HP", Plugin.Config.DebugMode); - scprole.Experience = data.Xp; + ev.Player.Position = data.Position; + ev.Player.Broadcast(16, UltimateAFK.Instance.Config.MsgReplace, Broadcast.BroadcastFlags.Normal, true); + ev.Player.SendConsoleMessage(UltimateAFK.Instance.Config.MsgReplace, "white"); + ev.Player.Health = data.Health; + Log.Debug("Adding Ammo", Plugin.Config.DebugMode); - scprole.Energy = data.Energy; - } + ev.Player.Inventory.UserInventory.ReserveAmmo = data.Ammo; + ev.Player.Inventory.SendAmmoNextFrame = true; + + + if (ev.NewRole == RoleType.Scp079 && data.SCP079Role != null) + { + Log.Debug("The new role is a SCP079, transferring level and experience.", Plugin.Config.DebugMode); - ReplacingPlayers.Remove(ev.Player); + var scprole = ev.Player.Role as Scp079Role; + scprole.Level = data.SCP079Role.Level; + scprole.Energy = data.SCP079Role.Energy; + scprole.Experience = data.SCP079Role.Experience; + } - }); + if (data.CustomItems != null) + { + Log.Debug("The AFK had CustomItems added to its replacement.", Plugin.Config.DebugMode); + foreach (var item in data.CustomItems) + { + if (CustomItem.TryGet(item, out var citem)) + { + Log.Debug($"CustomItem {citem.Name} was added to the player's inventory", Plugin.Config.DebugMode); + + citem.Give(ev.Player, false); + } + } + } + + Log.Debug("Removing the replacement player from the dictionary", Plugin.Config.DebugMode); + + ReplacingPlayers.Remove(ev.Player); + + }); + } } + catch (System.Exception e) + { + Log.Error($"Error when trying to replace a player || {e} {e.StackTrace}"); + } + } } + + public void OnRoundStarted() { ReplacingPlayers.Clear(); @@ -137,6 +183,14 @@ private void ResetAFKTime(Player ply) } } + private IEnumerator CountHandle() + { + while (true) + { + yield return Timing.WaitForSeconds(1.5f); + } + } + #region Reset AFK Timers public void OnInteractDoor(InteractingDoorEventArgs ev) @@ -195,5 +249,6 @@ public void OnShooting(ShootingEventArgs ev) ResetAFKTime(ev.Shooter); } #endregion + } } From 8bff185cdf8f9004e08cfec50d5a8f463ea3d0ec Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 14:38:38 -0300 Subject: [PATCH 025/146] Update AFKData.cs --- UltimateAFK/Resources/AFKData.cs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/UltimateAFK/Resources/AFKData.cs b/UltimateAFK/Resources/AFKData.cs index 74365f5..93870e1 100644 --- a/UltimateAFK/Resources/AFKData.cs +++ b/UltimateAFK/Resources/AFKData.cs @@ -1,27 +1,23 @@ -using System.Collections.Generic; -using UltimateAFK.Handlers.Components; +using Exiled.API.Features.Roles; +using System.Collections.Generic; using UnityEngine; namespace UltimateAFK.Resources { public struct AFKData { - public AFKComponent AfkComp { get; set; } + public Vector3 Position { get; set; } - public Vector3 SpawnLocation { get; set; } + public RoleType Role { get; set; } public Dictionary Ammo { get; set; } - public List Items { get; set; } + public List CustomItems { get; set; } - public float Health { get; set; } - - public bool Is079 { get; set; } + public List Items { get; set; } - public byte Level { get; set; } - - public float Xp { get; set; } + public float Health { get; set; } - public float Energy { get; set; } + public Scp079Role SCP079Role { get; set; } } } From e2faacbab99856bd0f7c9a209cac685718af8f70 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 14:38:42 -0300 Subject: [PATCH 026/146] Update UltimateAFK.csproj --- UltimateAFK/UltimateAFK.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 9eed0c2..9c05df2 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -23,7 +23,7 @@ - + From f116316854d835c4108887f39dab39b0d3c48531 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 15:03:01 -0300 Subject: [PATCH 027/146] Config for CustomItems Support --- UltimateAFK/Config.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index ff8c6dc..097d7fc 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -10,6 +10,9 @@ public class Config : IConfig [Description("If you have any error in the plugin operation activate this and create an Issue in Github https://github.com/SrLicht/Ultimate-AFK/issues")] public bool DebugMode { get; set; } = false; + [Description("If you have CustomItems activated and you want the player to be given the CustomItems when replacing the player, activate this.")] + public bool CustomItemsSupport { get; set; } = false; + [Description("If the number of players is less than this the plugin will not work.")] public int MinPlayers { get; set; } = 8; From d2413279076c43e9deafcba4eb59b701905553b4 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 15:03:03 -0300 Subject: [PATCH 028/146] Update AFKComponent.cs --- .../Handlers/Components/AFKComponent.cs | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index fffce8e..7a47f6f 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -145,21 +145,29 @@ public void CheckAFK() var ammo = MyPlayer.Ammo; var customitems = new List(); - foreach (var item in MyPlayer.Items) + if (UltimateAFK.Instance.Config.CustomItemsSupport) { - if (CustomItem.TryGet(item, out var citem)) + foreach (var item in MyPlayer.Items) { - customitems.Add(citem.Name); - items.Remove(item); + if (CustomItem.TryGet(item, out var citem)) + { + customitems.Add(citem.Name); + items.Remove(item); + } } } + else + { + customitems = null; + } + var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC")); ReplacementPlayer = list.FirstOrDefault(); if (ReplacementPlayer == null) { - Log.Debug("Unable to find replacement player moving spectator", UltimateAFK.Instance.Config.DebugMode); + Log.Debug("Unable to find replacement player, moving to spectator..."); MyPlayer.SetRole(RoleType.Spectator); MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); @@ -167,7 +175,7 @@ public void CheckAFK() } else { - Log.Debug($"Replacement Player found\nNickname: {ReplacementPlayer.Nickname}\nUserID: {ReplacementPlayer.UserId}\n Role: {ReplacementPlayer.Role.Type}", UltimateAFK.Instance.Config.DebugMode); + Log.Debug($"Replacement Player found\nNickname: {ReplacementPlayer.Nickname}\nUserID: {ReplacementPlayer.UserId}\n Role: {ReplacementPlayer.Role.Type}"); MainHandler.ReplacingPlayers.Add(ReplacementPlayer, new AFKData { @@ -194,7 +202,7 @@ public void CheckAFK() } } - Log.Debug("Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); + Log.Debug("Moving replacement player to the previous player's role"); this.ReplacementPlayer.SetRole(role); From 1f035dc1763cf26d04a63ff66b1ddb10f3e1b122 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 15:03:06 -0300 Subject: [PATCH 029/146] Update MainHandler.cs --- UltimateAFK/Handlers/MainHandler.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 27eee9d..d7414be 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -80,10 +80,13 @@ public void OnVerify(VerifiedEventArgs ev) { com.Destroy(); + Log.Debug($"Adding the Component to {ev.Player.Nickname}"); ev.Player.GameObject.AddComponent(); } else { + Log.Debug($"Adding the Component to {ev.Player.Nickname}"); + ev.Player.GameObject.AddComponent(); } @@ -105,17 +108,17 @@ public void OnChangingRole(ChangingRoleEventArgs ev) if (ReplacingPlayers.TryGetValue(ev.Player, out var data)) { - Log.Debug("Detecting player who replaces an AFK", Plugin.Config.DebugMode); + Log.Debug("Detecting player who replaces an AFK"); ev.Items.Clear(); - Log.Debug("Adding items from previous player", Plugin.Config.DebugMode); + Log.Debug("Adding items from previous player"); foreach (var item in data.Items) { ev.Items.Add(item.Type); } - Timing.CallDelayed(0.8f, () => + Timing.CallDelayed(1.2f, () => { Log.Debug("Changing player position and HP", Plugin.Config.DebugMode); @@ -123,7 +126,7 @@ public void OnChangingRole(ChangingRoleEventArgs ev) ev.Player.Broadcast(16, UltimateAFK.Instance.Config.MsgReplace, Broadcast.BroadcastFlags.Normal, true); ev.Player.SendConsoleMessage(UltimateAFK.Instance.Config.MsgReplace, "white"); ev.Player.Health = data.Health; - Log.Debug("Adding Ammo", Plugin.Config.DebugMode); + Log.Debug("Adding Ammo"); ev.Player.Inventory.UserInventory.ReserveAmmo = data.Ammo; ev.Player.Inventory.SendAmmoNextFrame = true; @@ -131,7 +134,7 @@ public void OnChangingRole(ChangingRoleEventArgs ev) if (ev.NewRole == RoleType.Scp079 && data.SCP079Role != null) { - Log.Debug("The new role is a SCP079, transferring level and experience.", Plugin.Config.DebugMode); + Log.Debug("The new role is a SCP079, transferring level and experience."); var scprole = ev.Player.Role as Scp079Role; scprole.Level = data.SCP079Role.Level; @@ -139,21 +142,21 @@ public void OnChangingRole(ChangingRoleEventArgs ev) scprole.Experience = data.SCP079Role.Experience; } - if (data.CustomItems != null) + if (data.CustomItems != null && Plugin.Config.CustomItemsSupport) { - Log.Debug("The AFK had CustomItems added to its replacement.", Plugin.Config.DebugMode); + Log.Debug("The AFK had CustomItems added to its replacement."); foreach (var item in data.CustomItems) { if (CustomItem.TryGet(item, out var citem)) { - Log.Debug($"CustomItem {citem.Name} was added to the player's inventory", Plugin.Config.DebugMode); + Log.Debug($"CustomItem {citem.Name} was added to the player's inventory"); citem.Give(ev.Player, false); } } } - Log.Debug("Removing the replacement player from the dictionary", Plugin.Config.DebugMode); + Log.Debug("Removing the replacement player from the dictionary"); ReplacingPlayers.Remove(ev.Player); From 1a934eae14dd58b91b4af4d8d199faf6fa8b5891 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 18:18:24 -0300 Subject: [PATCH 030/146] Update AFKComponent.cs --- UltimateAFK/Handlers/Components/AFKComponent.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 7a47f6f..e04fdcd 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -167,7 +167,7 @@ public void CheckAFK() if (ReplacementPlayer == null) { - Log.Debug("Unable to find replacement player, moving to spectator..."); + Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Instance.Config.DebugMode); MyPlayer.SetRole(RoleType.Spectator); MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); @@ -175,7 +175,7 @@ public void CheckAFK() } else { - Log.Debug($"Replacement Player found\nNickname: {ReplacementPlayer.Nickname}\nUserID: {ReplacementPlayer.UserId}\n Role: {ReplacementPlayer.Role.Type}"); + Log.Debug($"Replacement Player found\nNickname: {ReplacementPlayer.Nickname}\nUserID: {ReplacementPlayer.UserId}\n Role: {ReplacementPlayer.Role.Type}", UltimateAFK.Instance.Config.DebugMode); MainHandler.ReplacingPlayers.Add(ReplacementPlayer, new AFKData { @@ -202,7 +202,7 @@ public void CheckAFK() } } - Log.Debug("Moving replacement player to the previous player's role"); + Log.Debug("Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); this.ReplacementPlayer.SetRole(role); From 6cb9bd98219572339d51f66ab00f45db021c17de Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 18:18:26 -0300 Subject: [PATCH 031/146] Update MainHandler.cs --- UltimateAFK/Handlers/MainHandler.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index d7414be..61a6f66 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -80,12 +80,12 @@ public void OnVerify(VerifiedEventArgs ev) { com.Destroy(); - Log.Debug($"Adding the Component to {ev.Player.Nickname}"); + Log.Debug($"Adding the Component to {ev.Player.Nickname}", UltimateAFK.Instance.Config.DebugMode); ev.Player.GameObject.AddComponent(); } else { - Log.Debug($"Adding the Component to {ev.Player.Nickname}"); + Log.Debug($"Adding the Component to {ev.Player.Nickname}", UltimateAFK.Instance.Config.DebugMode); ev.Player.GameObject.AddComponent(); } @@ -108,11 +108,11 @@ public void OnChangingRole(ChangingRoleEventArgs ev) if (ReplacingPlayers.TryGetValue(ev.Player, out var data)) { - Log.Debug("Detecting player who replaces an AFK"); + Log.Debug("Detecting player who replaces an AFK", UltimateAFK.Instance.Config.DebugMode); ev.Items.Clear(); - Log.Debug("Adding items from previous player"); + Log.Debug("Adding items from previous player", UltimateAFK.Instance.Config.DebugMode); foreach (var item in data.Items) { ev.Items.Add(item.Type); @@ -126,7 +126,8 @@ public void OnChangingRole(ChangingRoleEventArgs ev) ev.Player.Broadcast(16, UltimateAFK.Instance.Config.MsgReplace, Broadcast.BroadcastFlags.Normal, true); ev.Player.SendConsoleMessage(UltimateAFK.Instance.Config.MsgReplace, "white"); ev.Player.Health = data.Health; - Log.Debug("Adding Ammo"); + + Log.Debug("Adding Ammo", UltimateAFK.Instance.Config.DebugMode); ev.Player.Inventory.UserInventory.ReserveAmmo = data.Ammo; ev.Player.Inventory.SendAmmoNextFrame = true; @@ -134,7 +135,7 @@ public void OnChangingRole(ChangingRoleEventArgs ev) if (ev.NewRole == RoleType.Scp079 && data.SCP079Role != null) { - Log.Debug("The new role is a SCP079, transferring level and experience."); + Log.Debug("The new role is a SCP079, transferring level and experience.", UltimateAFK.Instance.Config.DebugMode); var scprole = ev.Player.Role as Scp079Role; scprole.Level = data.SCP079Role.Level; @@ -144,19 +145,19 @@ public void OnChangingRole(ChangingRoleEventArgs ev) if (data.CustomItems != null && Plugin.Config.CustomItemsSupport) { - Log.Debug("The AFK had CustomItems added to its replacement."); + Log.Debug("The AFK had CustomItems added to its replacement.", UltimateAFK.Instance.Config.DebugMode); foreach (var item in data.CustomItems) { if (CustomItem.TryGet(item, out var citem)) { - Log.Debug($"CustomItem {citem.Name} was added to the player's inventory"); + Log.Debug($"CustomItem {citem.Name} was added to the player's inventory", UltimateAFK.Instance.Config.DebugMode); citem.Give(ev.Player, false); } } } - Log.Debug("Removing the replacement player from the dictionary"); + Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Instance.Config.DebugMode); ReplacingPlayers.Remove(ev.Player); From 8f571b5dd1193a1e448fa53f9b2532464bb749a3 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 18:37:31 -0300 Subject: [PATCH 032/146] Update AFKComponent.cs --- UltimateAFK/Handlers/Components/AFKComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index e04fdcd..0d98e73 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -160,7 +160,7 @@ public void CheckAFK() { customitems = null; } - + var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC")); ReplacementPlayer = list.FirstOrDefault(); From a5fb0126eb0a2e45c0e5c7c0f04d7e601f146cef Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 18:37:34 -0300 Subject: [PATCH 033/146] New version --- UltimateAFK/UltimateAFK.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index bf34784..fc1568e 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -23,7 +23,7 @@ public class UltimateAFK : Plugin public override string Prefix => "Ultimate_Afk"; - public override Version RequiredExiledVersion => new Version(5, 1, 1); + public override Version RequiredExiledVersion => new Version(5, 2, 0); public override Version Version => new Version(5, 0, 2); From 18eca16272cd9ebdc5b0a18a5f5c2be75c72cb6f Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 14 May 2022 18:40:41 -0300 Subject: [PATCH 034/146] Update MainHandler.cs --- UltimateAFK/Handlers/MainHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 61a6f66..c4f5be8 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -129,10 +129,10 @@ public void OnChangingRole(ChangingRoleEventArgs ev) Log.Debug("Adding Ammo", UltimateAFK.Instance.Config.DebugMode); + ev.Player.Inventory.UserInventory.ReserveAmmo.Clear(); ev.Player.Inventory.UserInventory.ReserveAmmo = data.Ammo; ev.Player.Inventory.SendAmmoNextFrame = true; - if (ev.NewRole == RoleType.Scp079 && data.SCP079Role != null) { Log.Debug("The new role is a SCP079, transferring level and experience.", UltimateAFK.Instance.Config.DebugMode); From dee667399b77f8fac995c2bc932517bdc2792618 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 16 May 2022 07:47:07 -0300 Subject: [PATCH 035/146] Un ejemplito para Nexus --- UltimateAFK/Config.cs | 8 +++++ UltimateAFK/Handlers/Commands/KitCommand.cs | 32 +++++++++++++++++++ .../Commands/SubCommands/CustomKit.cs | 26 +++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 UltimateAFK/Handlers/Commands/KitCommand.cs create mode 100644 UltimateAFK/Handlers/Commands/SubCommands/CustomKit.cs diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 097d7fc..7e50e56 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -39,5 +39,13 @@ public class Config : IConfig [Description("When a player replaces another player, this message will appear on the player's face and on the player console.")] public string MsgReplace { get; set; } = " You replaced an AFK player."; + + + //---------------------------> aaaaaaaaaa <-------------------------- + public string CustomCommandPrefix { get; set; } = "safe"; + + public string[] CustomCommandAliases { get; set; } = { "sf" }; + + public string CustomCommandDescription { get; set; } = "Example"; } } diff --git a/UltimateAFK/Handlers/Commands/KitCommand.cs b/UltimateAFK/Handlers/Commands/KitCommand.cs new file mode 100644 index 0000000..3d53823 --- /dev/null +++ b/UltimateAFK/Handlers/Commands/KitCommand.cs @@ -0,0 +1,32 @@ +using CommandSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UltimateAFK.Handlers.Commands.SubCommands; + +namespace UltimateAFK.Handlers.Commands +{ + internal class KitCommand : ParentCommand + { + public override string Command => "kit"; + + public override string[] Aliases => new string[] { }; + + public override string Description => "Comando de ejemplo por que soy horrible explicandome"; + + public override void LoadGeneratedCommands() + { + if(CustomKit.Instance.Command != UltimateAFK.Instance.Config.CustomCommandPrefix) + { + RegisterCommand(CustomKit.Instance); + } + } + + protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) + { + throw new NotImplementedException(); + } + } +} diff --git a/UltimateAFK/Handlers/Commands/SubCommands/CustomKit.cs b/UltimateAFK/Handlers/Commands/SubCommands/CustomKit.cs new file mode 100644 index 0000000..0ef3be1 --- /dev/null +++ b/UltimateAFK/Handlers/Commands/SubCommands/CustomKit.cs @@ -0,0 +1,26 @@ +using CommandSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UltimateAFK.Handlers.Commands.SubCommands +{ + internal class CustomKit : ICommand + { + public string Command => UltimateAFK.Instance.Config.CustomCommandPrefix; + + public string[] Aliases => UltimateAFK.Instance.Config.CustomCommandAliases; + + public string Description => UltimateAFK.Instance.Config.CustomCommandDescription; + + public static CustomKit Instance => new CustomKit(); + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + response = "Gwa gwa"; + return true; + } + } +} From db738a6051b332d3415a5b097bdae4386de17208 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 16 May 2022 08:02:11 -0300 Subject: [PATCH 036/146] adios --- UltimateAFK/Config.cs | 8 ----- UltimateAFK/Handlers/Commands/KitCommand.cs | 32 ------------------- .../Commands/SubCommands/CustomKit.cs | 26 --------------- 3 files changed, 66 deletions(-) delete mode 100644 UltimateAFK/Handlers/Commands/KitCommand.cs delete mode 100644 UltimateAFK/Handlers/Commands/SubCommands/CustomKit.cs diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 7e50e56..097d7fc 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -39,13 +39,5 @@ public class Config : IConfig [Description("When a player replaces another player, this message will appear on the player's face and on the player console.")] public string MsgReplace { get; set; } = " You replaced an AFK player."; - - - //---------------------------> aaaaaaaaaa <-------------------------- - public string CustomCommandPrefix { get; set; } = "safe"; - - public string[] CustomCommandAliases { get; set; } = { "sf" }; - - public string CustomCommandDescription { get; set; } = "Example"; } } diff --git a/UltimateAFK/Handlers/Commands/KitCommand.cs b/UltimateAFK/Handlers/Commands/KitCommand.cs deleted file mode 100644 index 3d53823..0000000 --- a/UltimateAFK/Handlers/Commands/KitCommand.cs +++ /dev/null @@ -1,32 +0,0 @@ -using CommandSystem; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UltimateAFK.Handlers.Commands.SubCommands; - -namespace UltimateAFK.Handlers.Commands -{ - internal class KitCommand : ParentCommand - { - public override string Command => "kit"; - - public override string[] Aliases => new string[] { }; - - public override string Description => "Comando de ejemplo por que soy horrible explicandome"; - - public override void LoadGeneratedCommands() - { - if(CustomKit.Instance.Command != UltimateAFK.Instance.Config.CustomCommandPrefix) - { - RegisterCommand(CustomKit.Instance); - } - } - - protected override bool ExecuteParent(ArraySegment arguments, ICommandSender sender, out string response) - { - throw new NotImplementedException(); - } - } -} diff --git a/UltimateAFK/Handlers/Commands/SubCommands/CustomKit.cs b/UltimateAFK/Handlers/Commands/SubCommands/CustomKit.cs deleted file mode 100644 index 0ef3be1..0000000 --- a/UltimateAFK/Handlers/Commands/SubCommands/CustomKit.cs +++ /dev/null @@ -1,26 +0,0 @@ -using CommandSystem; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace UltimateAFK.Handlers.Commands.SubCommands -{ - internal class CustomKit : ICommand - { - public string Command => UltimateAFK.Instance.Config.CustomCommandPrefix; - - public string[] Aliases => UltimateAFK.Instance.Config.CustomCommandAliases; - - public string Description => UltimateAFK.Instance.Config.CustomCommandDescription; - - public static CustomKit Instance => new CustomKit(); - - public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) - { - response = "Gwa gwa"; - return true; - } - } -} From c3dab04778517eaddb3cb33737d7032b32a72d12 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 31 May 2022 22:45:56 -0300 Subject: [PATCH 037/146] Update AFKComponent.cs --- UltimateAFK/Handlers/Components/AFKComponent.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 0d98e73..1055151 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -50,6 +50,7 @@ private void Awake() Exiled.Events.Handlers.Player.Jumping += OnJumping; // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. + CountHandler = Timing.RunCoroutine(CheckAfkPerSecond().CancelWith(this).CancelWith(gameObject)); if (MyPlayer.CheckPermission("uafk.ignore")) @@ -57,6 +58,8 @@ private void Awake() Log.Debug($"The player {MyPlayer.Nickname} has the permission \"uafk.ignore\" disabling component"); IsDisable = true; } + + Log.Debug($"{MyPlayer.Nickname} component fully loaded", UltimateAFK.Instance.Config.DebugMode); } public void Destroy() @@ -70,9 +73,9 @@ public void Destroy() Exiled.Events.Handlers.Player.Jumping -= OnJumping; Destroy(this); } - catch (Exception) + catch (Exception e) { - + Log.Error($"{this} " + e); throw; } } @@ -120,6 +123,8 @@ public void CheckAFK() var isAfk = AFKTime++ >= UltimateAFK.Instance.Config.AfkTime; + Log.Debug($"{MyPlayer.Nickname} is in grace time AFKTIME: {AFKTime}", UltimateAFK.Instance.Config.DebugMode); + if (isAfk) { var graceNumb = UltimateAFK.Instance.Config.AfkTime + UltimateAFK.Instance.Config.GraceTime - this.AFKTime; From 479f1eef3392558dbffdb118a74c7871ce8f3308 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 04:25:48 -0300 Subject: [PATCH 038/146] More DebugLogs and C# 9 --- Cerberus.props | 4 ++-- UltimateAFK/Handlers/Components/AFKComponent.cs | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index 0129471..1428120 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -8,7 +8,7 @@ net472 - 7.3 + 9.0 x64 false $(MSBuildThisFileDirectory)\bin\$(Configuration)\ @@ -20,7 +20,7 @@ false - 2.0.4 + 2.2.1 9.1.4 1.1.118 1.3.0 diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 1055151..7477515 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -82,10 +82,14 @@ public void Destroy() public void CheckAFK() { + Log.Debug("CheckAFK before the if call", UltimateAFK.Instance.Config.DebugMode); + var cantcontinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; if (!cantcontinue) { + Log.Debug("CheckAFK inside the if called", UltimateAFK.Instance.Config.DebugMode); + bool isSCP079 = MyPlayer.Role is Scp079Role; bool isSCP096 = MyPlayer.Role is Scp096Role; bool isNotCrying = false; @@ -111,6 +115,8 @@ public void CheckAFK() if (isMoving) { + Log.Debug("CheckAFK() player is moving, changing position variables", UltimateAFK.Instance.Config.DebugMode); + this.LastPosition = position; this.LastRotation = vector; this.AFKTime = 0; @@ -225,8 +231,10 @@ private IEnumerator CheckAfkPerSecond() { while (true) { + Log.Debug("Calling CheckAfkPerSecond() before If", UltimateAFK.Instance.Config.DebugMode); if (!IsDisable && Round.IsStarted) { + Log.Debug("Call of CheckAfkPerSecond() inside If", UltimateAFK.Instance.Config.DebugMode); CheckAFK(); } @@ -239,6 +247,7 @@ public void OnDestroying(DestroyingEventArgs ev) { if (ev.Player == MyPlayer) { + Log.Debug($"OnDestroying | My player was destroyed by DestroyingEventArg, destroying component", UltimateAFK.Instance.Config.DebugMode); Destroy(); } } @@ -248,6 +257,7 @@ public void OnJumping(JumpingEventArgs ev) { if (ev.Player != null && ev.Player == MyPlayer && !IsDisable) { + Log.Debug($"OnJumping | My player is jumping, resetting AFK counter", UltimateAFK.Instance.Config.DebugMode); AFKTime = 0; } } From 5b5ab138ba792891e5b287c308124caf63bf4fe3 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 04:27:08 -0300 Subject: [PATCH 039/146] I am tired of compiling my code on my pc --- .github/workflows/dotnet.yml | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/dotnet.yml diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..b5abcdb --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,49 @@ +name: Build + +on: + push: + workflow_dispatch: + +env: + # Para la version 10.2 el link de referencias tiene que ser este https://www.exiled.host/build_deps/Master.zip + # Pero si es para la beta https://exiled.host/build_deps/Dev.zip + EXILED_REFERENCES_URL: https://exiled.host/build_deps/Dev.zip + EXILED_REFERENCES_PATH: ${{ github.workspace }}/References + +jobs: + + build: + + runs-on: windows-latest + + steps: + + - name: Setup .NET Core SDK + uses: actions/setup-dotnet@v1.7.2 + + - name: Setup Nuget + uses: iRebbok/setup-nuget@master + + - uses: actions/checkout@v2.3.4 + + - name: Get references + shell: pwsh + run: | + Invoke-WebRequest -Uri ${{ env.EXILED_REFERENCES_URL }} -OutFile ${{ github.workspace }}/Dev.zip + Expand-Archive -Path Dev.zip -DestinationPath ${{ env.EXILED_REFERENCES_PATH }} + + - name: Build + env: + EXILED_REFERENCES: ${{ env.EXILED_REFERENCES_PATH }} + shell: pwsh + run: | + ./build.ps1 + $File = (Get-ChildItem -Path . -Include 'EXILED.*.nupkg' -Recurse).Name + Out-File -FilePath ${{ github.env }} -InputObject "PackageFile=$File" -Encoding utf-8 -Append + + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: Build Results + path: bin/Debug + retention-days: 3 \ No newline at end of file From 925f1b99bc6e26670a4cb7a4c7a936efa017f1ba Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 04:31:12 -0300 Subject: [PATCH 040/146] Forget about this :trollface: --- build.ps1 | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 build.ps1 diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..0fc3592 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,47 @@ +#! pwsh + +param ( + [Switch]$BuildNuGet +) + +$Projects = @( + 'UltimateAFK' +) + +function Execute { + param ( + [string]$Cmd + ) + + foreach ($Project in $Projects) { + Invoke-Expression ([string]::Join(' ', $Cmd, $Project, $args)) + CheckLastOperationStatus + } +} + +function CheckLastOperationStatus { + if ($? -eq $false) { + Exit 1 + } +} + +function GetSolutionVersion { + [XML]$PropsFile = Get-Content Exiled.props + $Version = $PropsFile.Project.PropertyGroup[2].Version + $Version = $Version.'#text'.Trim() + return $Version +} + +# Restore projects +Execute 'dotnet restore' +# Build projects +Execute 'dotnet build' '-c release' +# Build a NuGet package if needed +if ($BuildNuGet) { + $Version = GetSolutionVersion + $Year = [System.DateTime]::Now.ToString('yyyy') + + Write-Host "Generating NuGet package for version $Version" + + nuget pack Exiled/Exiled.nuspec -Version $Version -Properties Year=$Year +} \ No newline at end of file From 4571ff41dcdcb9aaf13750910471635ec228bce1 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 04:38:42 -0300 Subject: [PATCH 041/146] GWA GWAAAAAA --- build.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.ps1 b/build.ps1 index 0fc3592..40465a9 100644 --- a/build.ps1 +++ b/build.ps1 @@ -5,7 +5,7 @@ param ( ) $Projects = @( - 'UltimateAFK' + "UltimateAFK" ) function Execute { @@ -26,7 +26,7 @@ function CheckLastOperationStatus { } function GetSolutionVersion { - [XML]$PropsFile = Get-Content Exiled.props + [XML]$PropsFile = Get-Content Cerberus.props $Version = $PropsFile.Project.PropertyGroup[2].Version $Version = $Version.'#text'.Trim() return $Version @@ -35,7 +35,7 @@ function GetSolutionVersion { # Restore projects Execute 'dotnet restore' # Build projects -Execute 'dotnet build' '-c release' +Execute 'dotnet build' # Build a NuGet package if needed if ($BuildNuGet) { $Version = GetSolutionVersion @@ -44,4 +44,4 @@ if ($BuildNuGet) { Write-Host "Generating NuGet package for version $Version" nuget pack Exiled/Exiled.nuspec -Version $Version -Properties Year=$Year -} \ No newline at end of file +} From 4a8d349536277554bf868236073e6d3cc6587129 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 04:57:32 -0300 Subject: [PATCH 042/146] More logs --- UltimateAFK/Handlers/Components/AFKComponent.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 7477515..68b5f53 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -66,11 +66,17 @@ public void Destroy() { try { + Log.Debug($"Calling Destroy", UltimateAFK.Instance.Config.DebugMode); + + if (MyPlayer is null) + Log.Debug("Player is null"); + MyPlayer = null; ReplacementPlayer = null; Exiled.Events.Handlers.Player.Destroying -= OnDestroying; Exiled.Events.Handlers.Player.Jumping -= OnJumping; + Destroy(this); } catch (Exception e) From b9235d47f46d582fed47731fd750b54ed4947136 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 04:59:12 -0300 Subject: [PATCH 043/146] MORE --- UltimateAFK/Handlers/Components/AFKComponent.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 68b5f53..32284e6 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -90,9 +90,9 @@ public void CheckAFK() { Log.Debug("CheckAFK before the if call", UltimateAFK.Instance.Config.DebugMode); - var cantcontinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; + //var cantcontinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; - if (!cantcontinue) + if (Round.IsStarted) { Log.Debug("CheckAFK inside the if called", UltimateAFK.Instance.Config.DebugMode); From 80c68d7924db7db4b1c9102945a3ccf08768e189 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 05:12:09 -0300 Subject: [PATCH 044/146] More logs --- .../Handlers/Components/AFKComponent.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 32284e6..fb334fa 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -53,11 +53,11 @@ private void Awake() CountHandler = Timing.RunCoroutine(CheckAfkPerSecond().CancelWith(this).CancelWith(gameObject)); - if (MyPlayer.CheckPermission("uafk.ignore")) + /*if (MyPlayer.CheckPermission("uafk.ignore")) { Log.Debug($"The player {MyPlayer.Nickname} has the permission \"uafk.ignore\" disabling component"); IsDisable = true; - } + }*/ Log.Debug($"{MyPlayer.Nickname} component fully loaded", UltimateAFK.Instance.Config.DebugMode); } @@ -90,9 +90,9 @@ public void CheckAFK() { Log.Debug("CheckAFK before the if call", UltimateAFK.Instance.Config.DebugMode); - //var cantcontinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; + var cantcontinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; - if (Round.IsStarted) + if (!cantcontinue) { Log.Debug("CheckAFK inside the if called", UltimateAFK.Instance.Config.DebugMode); @@ -238,10 +238,20 @@ private IEnumerator CheckAfkPerSecond() while (true) { Log.Debug("Calling CheckAfkPerSecond() before If", UltimateAFK.Instance.Config.DebugMode); + if (!IsDisable && Round.IsStarted) { - Log.Debug("Call of CheckAfkPerSecond() inside If", UltimateAFK.Instance.Config.DebugMode); - CheckAFK(); + try + { + Log.Debug("Call of CheckAfkPerSecond() inside If", UltimateAFK.Instance.Config.DebugMode); + + CheckAFK(); + } + catch (Exception e) + { + Log.Error($"{this} Error on CheckAFK(): {e} || {e.StackTrace}"); + } + } yield return Timing.WaitForSeconds(1.2f); From d8122b0802c3e61c138fc27d6f0d91d62ea91132 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 06:36:52 -0300 Subject: [PATCH 045/146] Update AFKComponent.cs * Fix Auto-Kick * I'm going to have to clarify that you need Exiled.CustomItems.dll --- .../Handlers/Components/AFKComponent.cs | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index fb334fa..54f284d 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -53,11 +53,11 @@ private void Awake() CountHandler = Timing.RunCoroutine(CheckAfkPerSecond().CancelWith(this).CancelWith(gameObject)); - /*if (MyPlayer.CheckPermission("uafk.ignore")) + if (MyPlayer.CheckPermission("uafk.ignore")) { Log.Debug($"The player {MyPlayer.Nickname} has the permission \"uafk.ignore\" disabling component"); IsDisable = true; - }*/ + } Log.Debug($"{MyPlayer.Nickname} component fully loaded", UltimateAFK.Instance.Config.DebugMode); } @@ -186,6 +186,20 @@ public void CheckAFK() { Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Instance.Config.DebugMode); + if (UltimateAFK.Instance.Config.AfkCount != -1) + { + AFKCount++; + + if (AFKCount >= UltimateAFK.Instance.Config.AfkCount) + { + MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgKick, "white"); + + MyPlayer.Kick(UltimateAFK.Instance.Config.MsgKick, "[UltimateAFK]"); + + return; + } + } + MyPlayer.SetRole(RoleType.Spectator); MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); @@ -207,22 +221,24 @@ public void CheckAFK() }); + Log.Debug("Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); + + this.ReplacementPlayer.SetRole(role); + if (UltimateAFK.Instance.Config.AfkCount != -1) { AFKCount++; - if (AFKCount > UltimateAFK.Instance.Config.AfkCount) + if (AFKCount >= UltimateAFK.Instance.Config.AfkCount) { MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgKick, "white"); MyPlayer.Kick(UltimateAFK.Instance.Config.MsgKick, "[UltimateAFK]"); + + return; } } - Log.Debug("Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); - - this.ReplacementPlayer.SetRole(role); - MyPlayer.SetRole(RoleType.Spectator); MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); From 882ab31327f88d8f9a413d7b60da6d361ae35d25 Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Wed, 15 Jun 2022 06:41:36 -0300 Subject: [PATCH 046/146] Update README.md --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c9e07aa..ced8e9b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,14 @@ This is an updated version of the original Ultimate AFK plugin from https://gith If you give a role the `uafk.ignore` permission it will be ignored by the plugin and will never be set to afk, useful for administrators. # Installation - **[EXILED](https://github.com/galaxy119/EXILED) must be installed for this to work.** Place the "UltimateAFK.dll" file in your Plugins folder. + +# IMPORTANT + +For technical and lazy reasons you need ``Exiled.CustomItems.dll`` since I use its API regardless if the CustomItemsSupport setting is false. + +![image](https://user-images.githubusercontent.com/36207738/173796583-8f1a3287-3ab9-4d36-9aad-efbf142cb1e0.png) + +``Exiled.CustomItems`` does not contain any CustomItems and is only an API. From 2642aff9796e7845ba8c8535a611822da4d1f066 Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Wed, 15 Jun 2022 06:45:34 -0300 Subject: [PATCH 047/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ced8e9b..6ba05e8 100644 --- a/README.md +++ b/README.md @@ -24,4 +24,4 @@ For technical and lazy reasons you need ``Exiled.CustomItems.dll`` since I use i ![image](https://user-images.githubusercontent.com/36207738/173796583-8f1a3287-3ab9-4d36-9aad-efbf142cb1e0.png) -``Exiled.CustomItems`` does not contain any CustomItems and is only an API. +``Exiled.CustomItems.dll`` does not contain any CustomItems and is only an API used for spawning and creating CustomItems, if you want CustomItems you need to download this plugin https://github.com/Exiled-Team/CustomItems From e5df301081d05ca3958f5dc07e3824b033dd6d0f Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Wed, 15 Jun 2022 06:53:20 -0300 Subject: [PATCH 048/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ba05e8..bab0bf0 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Place the "UltimateAFK.dll" file in your Plugins folder. # IMPORTANT -For technical and lazy reasons you need ``Exiled.CustomItems.dll`` since I use its API regardless if the CustomItemsSupport setting is false. +For technical and Lazy reasons you have to have ``Exiled.CustomItems.dll`` in your plugins folder, this file is included in any current version of Exiled, and it doesn't matter if you have the CustomItemsSupport setting to false, you need ``Exiled.CustomItems`` ![image](https://user-images.githubusercontent.com/36207738/173796583-8f1a3287-3ab9-4d36-9aad-efbf142cb1e0.png) From c45b30aa8ae5f9e8493c407a8a8cd0c0009ae93c Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Wed, 15 Jun 2022 14:36:10 -0300 Subject: [PATCH 049/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bab0bf0..3f9a480 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Place the "UltimateAFK.dll" file in your Plugins folder. # IMPORTANT -For technical and Lazy reasons you have to have ``Exiled.CustomItems.dll`` in your plugins folder, this file is included in any current version of Exiled, and it doesn't matter if you have the CustomItemsSupport setting to false, you need ``Exiled.CustomItems`` +For technical and Lazy reasons you need to have ``Exiled.CustomItems.dll`` in your plugins folder, this file is included in any current version of Exiled, and it doesn't matter if you have the CustomItemsSupport setting to false, you need ``Exiled.CustomItems`` ![image](https://user-images.githubusercontent.com/36207738/173796583-8f1a3287-3ab9-4d36-9aad-efbf142cb1e0.png) From 15c49edbcb447cebfd46f03f8e19951a4d34e1ea Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 23:21:07 -0300 Subject: [PATCH 050/146] No more needed Exiled.CustomItems :) --- UltimateAFK/Config.cs | 4 +-- .../Handlers/Components/AFKComponent.cs | 31 +++++-------------- UltimateAFK/Handlers/MainHandler.cs | 17 +--------- UltimateAFK/UltimateAFK.cs | 2 +- 4 files changed, 12 insertions(+), 42 deletions(-) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 097d7fc..e66d9ad 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -10,8 +10,8 @@ public class Config : IConfig [Description("If you have any error in the plugin operation activate this and create an Issue in Github https://github.com/SrLicht/Ultimate-AFK/issues")] public bool DebugMode { get; set; } = false; - [Description("If you have CustomItems activated and you want the player to be given the CustomItems when replacing the player, activate this.")] - public bool CustomItemsSupport { get; set; } = false; + [Description("This bool activates the logs that easily spams the console, and normally they are not required, that's why they have this separate configuration :)")] + public bool SpamLogs { get; set; } = false; [Description("If the number of players is less than this the plugin will not work.")] public int MinPlayers { get; set; } = 8; diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 54f284d..77e7ccd 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -1,6 +1,5 @@ using Exiled.API.Features; using Exiled.API.Features.Roles; -using Exiled.CustomItems.API.Features; using Exiled.Events.EventArgs; using Exiled.Permissions.Extensions; using MEC; @@ -69,7 +68,7 @@ public void Destroy() Log.Debug($"Calling Destroy", UltimateAFK.Instance.Config.DebugMode); if (MyPlayer is null) - Log.Debug("Player is null"); + Log.Debug("Player is null in Destroy()"); MyPlayer = null; ReplacementPlayer = null; @@ -88,13 +87,13 @@ public void Destroy() public void CheckAFK() { - Log.Debug("CheckAFK before the if call", UltimateAFK.Instance.Config.DebugMode); + Log.Debug("CheckAFK before the if call", UltimateAFK.Instance.Config.DebugMode && UltimateAFK.Instance.Config.SpamLogs); var cantcontinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; if (!cantcontinue) { - Log.Debug("CheckAFK inside the if called", UltimateAFK.Instance.Config.DebugMode); + Log.Debug("CheckAFK inside the if called", UltimateAFK.Instance.Config.DebugMode && UltimateAFK.Instance.Config.SpamLogs); bool isSCP079 = MyPlayer.Role is Scp079Role; bool isSCP096 = MyPlayer.Role is Scp096Role; @@ -135,7 +134,7 @@ public void CheckAFK() var isAfk = AFKTime++ >= UltimateAFK.Instance.Config.AfkTime; - Log.Debug($"{MyPlayer.Nickname} is in grace time AFKTIME: {AFKTime}", UltimateAFK.Instance.Config.DebugMode); + Log.Debug($"{MyPlayer.Nickname} is in not moving, AFKTime: {AFKTime}", UltimateAFK.Instance.Config.DebugMode); if (isAfk) { @@ -162,21 +161,7 @@ public void CheckAFK() var ammo = MyPlayer.Ammo; var customitems = new List(); - if (UltimateAFK.Instance.Config.CustomItemsSupport) - { - foreach (var item in MyPlayer.Items) - { - if (CustomItem.TryGet(item, out var citem)) - { - customitems.Add(citem.Name); - items.Remove(item); - } - } - } - else - { - customitems = null; - } + customitems = null; var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC")); @@ -253,13 +238,13 @@ private IEnumerator CheckAfkPerSecond() { while (true) { - Log.Debug("Calling CheckAfkPerSecond() before If", UltimateAFK.Instance.Config.DebugMode); + Log.Debug("Calling CheckAfkPerSecond() before If", UltimateAFK.Instance.Config.DebugMode && UltimateAFK.Instance.Config.SpamLogs); if (!IsDisable && Round.IsStarted) { try { - Log.Debug("Call of CheckAfkPerSecond() inside If", UltimateAFK.Instance.Config.DebugMode); + Log.Debug("Call of CheckAfkPerSecond() inside If", UltimateAFK.Instance.Config.DebugMode && UltimateAFK.Instance.Config.SpamLogs); CheckAFK(); } @@ -267,7 +252,7 @@ private IEnumerator CheckAfkPerSecond() { Log.Error($"{this} Error on CheckAFK(): {e} || {e.StackTrace}"); } - + } yield return Timing.WaitForSeconds(1.2f); diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index c4f5be8..f69de68 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -1,6 +1,5 @@ using Exiled.API.Features; using Exiled.API.Features.Roles; -using Exiled.CustomItems.API.Features; using Exiled.Events.EventArgs; using MEC; using System.Collections.Generic; @@ -118,7 +117,7 @@ public void OnChangingRole(ChangingRoleEventArgs ev) ev.Items.Add(item.Type); } - Timing.CallDelayed(1.2f, () => + Timing.CallDelayed(0.8f, () => { Log.Debug("Changing player position and HP", Plugin.Config.DebugMode); @@ -143,20 +142,6 @@ public void OnChangingRole(ChangingRoleEventArgs ev) scprole.Experience = data.SCP079Role.Experience; } - if (data.CustomItems != null && Plugin.Config.CustomItemsSupport) - { - Log.Debug("The AFK had CustomItems added to its replacement.", UltimateAFK.Instance.Config.DebugMode); - foreach (var item in data.CustomItems) - { - if (CustomItem.TryGet(item, out var citem)) - { - Log.Debug($"CustomItem {citem.Name} was added to the player's inventory", UltimateAFK.Instance.Config.DebugMode); - - citem.Give(ev.Player, false); - } - } - } - Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Instance.Config.DebugMode); ReplacingPlayers.Remove(ev.Player); diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index fc1568e..1e3f53a 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -23,7 +23,7 @@ public class UltimateAFK : Plugin public override string Prefix => "Ultimate_Afk"; - public override Version RequiredExiledVersion => new Version(5, 2, 0); + public override Version RequiredExiledVersion => new Version(5, 2, 2); public override Version Version => new Version(5, 0, 2); From 14bf18aca269ed09868bb5bb1e324c16247b47fc Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 23:30:06 -0300 Subject: [PATCH 051/146] New version --- UltimateAFK/UltimateAFK.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 1e3f53a..e9c3ebd 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -23,7 +23,7 @@ public class UltimateAFK : Plugin public override string Prefix => "Ultimate_Afk"; - public override Version RequiredExiledVersion => new Version(5, 2, 2); + public override Version RequiredExiledVersion => new Version(5, 2, 3); public override Version Version => new Version(5, 0, 2); From 500e4de5d982bb7128d4efb0af803c68ecf23b6f Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 23:36:40 -0300 Subject: [PATCH 052/146] Avoiding inventory duplication --- UltimateAFK/Handlers/Components/AFKComponent.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 77e7ccd..41907cf 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -2,6 +2,7 @@ using Exiled.API.Features.Roles; using Exiled.Events.EventArgs; using Exiled.Permissions.Extensions; +using Exiled.API.Extensions; using MEC; using System; using System.Collections.Generic; @@ -223,6 +224,8 @@ public void CheckAFK() return; } } + // I do this, to avoid that when changing to spectator the items that the player had are thrown to the ground :) + MyPlayer.ClearInventory(); MyPlayer.SetRole(RoleType.Spectator); MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); From 2dfe9d3af800564a10f8d9deb8680fc8ff8e3699 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 23:39:27 -0300 Subject: [PATCH 053/146] Less retention time --- .github/workflows/dotnet.yml | 2 +- UltimateAFK/Handlers/Components/AFKComponent.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index b5abcdb..c813f65 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -46,4 +46,4 @@ jobs: with: name: Build Results path: bin/Debug - retention-days: 3 \ No newline at end of file + retention-days: 1 \ No newline at end of file diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 41907cf..8f3f896 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -206,7 +206,6 @@ public void CheckAFK() }); - Log.Debug("Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); this.ReplacementPlayer.SetRole(role); @@ -224,6 +223,7 @@ public void CheckAFK() return; } } + // I do this, to avoid that when changing to spectator the items that the player had are thrown to the ground :) MyPlayer.ClearInventory(); From 2c7e4a3be7055933825b2068847269e4fdcbb19c Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Jun 2022 23:45:50 -0300 Subject: [PATCH 054/146] A little bit higher --- UltimateAFK/Handlers/Components/AFKComponent.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 8f3f896..47335eb 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -209,6 +209,8 @@ public void CheckAFK() Log.Debug("Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); this.ReplacementPlayer.SetRole(role); + // I do this, to avoid that when changing to spectator the items that the player had are thrown to the ground :) + MyPlayer.ClearInventory(); if (UltimateAFK.Instance.Config.AfkCount != -1) { @@ -224,8 +226,7 @@ public void CheckAFK() } } - // I do this, to avoid that when changing to spectator the items that the player had are thrown to the ground :) - MyPlayer.ClearInventory(); + MyPlayer.SetRole(RoleType.Spectator); MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); From c107d681f6e638ae29f24a771666905f955942ec Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Wed, 15 Jun 2022 23:48:33 -0300 Subject: [PATCH 055/146] Update README.md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 3f9a480..728d92a 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,4 @@ Place the "UltimateAFK.dll" file in your Plugins folder. # IMPORTANT -For technical and Lazy reasons you need to have ``Exiled.CustomItems.dll`` in your plugins folder, this file is included in any current version of Exiled, and it doesn't matter if you have the CustomItemsSupport setting to false, you need ``Exiled.CustomItems`` - -![image](https://user-images.githubusercontent.com/36207738/173796583-8f1a3287-3ab9-4d36-9aad-efbf142cb1e0.png) - -``Exiled.CustomItems.dll`` does not contain any CustomItems and is only an API used for spawning and creating CustomItems, if you want CustomItems you need to download this plugin https://github.com/Exiled-Team/CustomItems +If you have CustomItem and want replacement players to have the customitems of the afk players consider using ``Ultimate-AFK-CustomItemSupport.dll`` From 81eafde4e7c2e81c4781a3dcc1874ed090079bfa Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Wed, 15 Jun 2022 23:58:29 -0300 Subject: [PATCH 056/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 728d92a..3448ff2 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,4 @@ Place the "UltimateAFK.dll" file in your Plugins folder. # IMPORTANT -If you have CustomItem and want replacement players to have the customitems of the afk players consider using ``Ultimate-AFK-CustomItemSupport.dll`` +If you have CustomItem and want replacement players to have the customitems of the afk players consider using ``UltimateAFK-CustomItemSupport.dll`` From 7c12d4c6a07dc357644e6f5607ae4f02a78173f4 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Thu, 16 Jun 2022 17:08:37 -0300 Subject: [PATCH 057/146] Update AFKComponent.cs --- UltimateAFK/Handlers/Components/AFKComponent.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 47335eb..b540bd6 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -162,9 +162,6 @@ public void CheckAFK() var ammo = MyPlayer.Ammo; var customitems = new List(); - customitems = null; - - var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC")); ReplacementPlayer = list.FirstOrDefault(); @@ -221,13 +218,10 @@ public void CheckAFK() MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgKick, "white"); MyPlayer.Kick(UltimateAFK.Instance.Config.MsgKick, "[UltimateAFK]"); - return; } } - - MyPlayer.SetRole(RoleType.Spectator); MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); From a5638a8d5ee0c2eaf1f3473e22660ca0133d77b6 Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:40:22 -0300 Subject: [PATCH 058/146] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 3448ff2..07d3573 100644 --- a/README.md +++ b/README.md @@ -19,5 +19,4 @@ If you give a role the `uafk.ignore` permission it will be ignored by the plugin Place the "UltimateAFK.dll" file in your Plugins folder. # IMPORTANT - -If you have CustomItem and want replacement players to have the customitems of the afk players consider using ``UltimateAFK-CustomItemSupport.dll`` +``UltimateAFK-CustomItemSupport.dll`` It is a separate plugin that makes replacing a player search the afk inventory for customitems and give it to the person replacing him, you only have to have 1 of the two versions enabled at the same time (``UltimateAFK-CustomItemSupport.dll`` or ``UltimateAFK.dll``). From e7638cddc24faf624393a8ff9d0f5558dd075e77 Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:43:56 -0300 Subject: [PATCH 059/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07d3573..fc41fca 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This is an updated version of the original Ultimate AFK plugin from https://gith - Works with SCP-079 by checking camera angle, and experience interactions # Permission -If you give a role the `uafk.ignore` permission it will be ignored by the plugin and will never be set to afk, useful for administrators. +If you give a group the `uafk.ignore` permission the player will be ignored to replace a player and will also never be detected as AFK. Useful for administrators # Installation **[EXILED](https://github.com/galaxy119/EXILED) must be installed for this to work.** From 3eafdd1bc190146343191059082349abd033e3c8 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Fri, 17 Jun 2022 17:02:27 -0300 Subject: [PATCH 060/146] Replace player who disconnected from the server --- UltimateAFK/Config.cs | 7 ++++ .../Handlers/Components/AFKComponent.cs | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index e66d9ad..ced59f9 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -1,4 +1,5 @@ using Exiled.API.Interfaces; +using System.Collections.Generic; using System.ComponentModel; namespace UltimateAFK @@ -13,6 +14,12 @@ public class Config : IConfig [Description("This bool activates the logs that easily spams the console, and normally they are not required, that's why they have this separate configuration :)")] public bool SpamLogs { get; set; } = false; + [Description("Should players who leave the server be replaced by spectators?")] + public bool RepleacePlayersOnLeave { get; set; } = false; + + [Description("Here is a list of the Roletypes that will not be replaced when leave the server")] + public List DisableReplacementFor { get; set; } = new List() { RoleType.Scp0492 }; + [Description("If the number of players is less than this the plugin will not work.")] public int MinPlayers { get; set; } = 8; diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index b540bd6..01da9f0 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -262,6 +262,46 @@ public void OnDestroying(DestroyingEventArgs ev) { if (ev.Player == MyPlayer) { + + if (Round.IsStarted && !Round.IsEnded && UltimateAFK.Instance.Config.RepleacePlayersOnLeave && !UltimateAFK.Instance.Config.DisableReplacementFor.Contains(ev.Player.Role)) + { + Log.Debug("OnDestroying | My Player leave the server, trying to repleace", UltimateAFK.Instance.Config.DebugMode); + var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC")); + ReplacementPlayer = list.FirstOrDefault(); + + // I don't think it will work, but I don't lose anything by trying. + if (ReplacementPlayer is not null) + { + Scp079Role scp079role = MyPlayer.Role as Scp079Role; + + var customitems = new List(); + var items = MyPlayer.Items.ToList(); + var role = MyPlayer.Role; + MyPlayer.ClearInventory(); + + MainHandler.ReplacingPlayers.Add(ReplacementPlayer, new AFKData + { + Position = MyPlayer.Position, + Role = role, + Ammo = MyPlayer.Ammo, + Health = MyPlayer.Health, + Items = items, + CustomItems = customitems, + SCP079Role = scp079role + + }); + + Log.Debug("OnDestroy| Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); + + this.ReplacementPlayer.SetRole(role); + } + else + { + Log.Debug("OnDestroying | A replacement player could not be found.", UltimateAFK.Instance.Config.DebugMode); + } + + } + Log.Debug($"OnDestroying | My player was destroyed by DestroyingEventArg, destroying component", UltimateAFK.Instance.Config.DebugMode); Destroy(); } From 985d57a01953b85f59aff2001e628d313e6071e6 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sun, 19 Jun 2022 06:08:00 -0300 Subject: [PATCH 061/146] Maybe this fix something --- UltimateAFK/Config.cs | 3 +++ UltimateAFK/Handlers/Components/AFKComponent.cs | 2 +- UltimateAFK/Handlers/MainHandler.cs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index ced59f9..8cceccc 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -20,6 +20,9 @@ public class Config : IConfig [Description("Here is a list of the Roletypes that will not be replaced when leave the server")] public List DisableReplacementFor { get; set; } = new List() { RoleType.Scp0492 }; + [Description("When a player is replaced it is called a delay, if when replacing the player the position is not updated correctly, increase this value but it must not exceed 2.5 since it would be too long.")] + public float ReplaceDelay { get; set; } = 1.4f; + [Description("If the number of players is less than this the plugin will not work.")] public int MinPlayers { get; set; } = 8; diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 01da9f0..76790b2 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -266,7 +266,7 @@ public void OnDestroying(DestroyingEventArgs ev) if (Round.IsStarted && !Round.IsEnded && UltimateAFK.Instance.Config.RepleacePlayersOnLeave && !UltimateAFK.Instance.Config.DisableReplacementFor.Contains(ev.Player.Role)) { Log.Debug("OnDestroying | My Player leave the server, trying to repleace", UltimateAFK.Instance.Config.DebugMode); - var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC")); + var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC") && !MainHandler.ReplacingPlayers.ContainsKey(p)); ReplacementPlayer = list.FirstOrDefault(); // I don't think it will work, but I don't lose anything by trying. diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index f69de68..9cdd9b8 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -117,7 +117,7 @@ public void OnChangingRole(ChangingRoleEventArgs ev) ev.Items.Add(item.Type); } - Timing.CallDelayed(0.8f, () => + Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => { Log.Debug("Changing player position and HP", Plugin.Config.DebugMode); From c08b41392c685f1a6d32e56f9b750a9531967801 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 21:30:32 -0300 Subject: [PATCH 062/146] Changed to NWAPI --- .gitignore | 3 +- Cerberus.props | 2 +- README.md | 14 +- UltimateAFK/API/Base/Handler.cs | 23 - UltimateAFK/Config.cs | 21 +- .../Handlers/Components/AFKComponent.cs | 431 +++++++++--------- UltimateAFK/Handlers/MainHandler.cs | 266 +++-------- UltimateAFK/Resources/AFKData.cs | 15 +- UltimateAFK/Resources/Extensions.cs | 38 ++ UltimateAFK/Resources/Scp079Data.cs | 13 + UltimateAFK/UltimateAFK.cs | 53 +-- UltimateAFK/UltimateAFK.csproj | 11 +- 12 files changed, 368 insertions(+), 522 deletions(-) delete mode 100644 UltimateAFK/API/Base/Handler.cs create mode 100644 UltimateAFK/Resources/Extensions.cs create mode 100644 UltimateAFK/Resources/Scp079Data.cs diff --git a/.gitignore b/.gitignore index 95d160d..9ec7174 100644 --- a/.gitignore +++ b/.gitignore @@ -351,4 +351,5 @@ healthchecksdb MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder -.ionide/ \ No newline at end of file +.ionide/ +.idea/ diff --git a/Cerberus.props b/Cerberus.props index 1428120..384563c 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -7,7 +7,7 @@ - net472 + net48 9.0 x64 false diff --git a/README.md b/README.md index fc41fca..91e0cfa 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,20 @@ -| ![Github All Releases](https://img.shields.io/github/downloads/SrLicht/Ultimate-AFK/total.svg) | Releases | Support | +| ![Github All Releases](https://img.shields.io/github/downloads/SrLicht/Ultimate-AFK/total.svg) | Releases # Ultimate-AFK -This is an updated version of the original Ultimate AFK plugin from https://github.com/kingsplayground/Ultimate-AFK. + # Features -- Detects AFK Players via in-game movement, camera movement, and in-game interactions +- Detects AFK Players via in-game movement, camera movement - Moves players to spectator after a determined AFK Time and grace period - (Optional) Kick players from the server after repeated AFK detections! - Custom broadcasts to AFK Players to indicate to them if they are AFK. -- Works with SCP-079 by checking camera angle, and experience interactions +- Works with SCP-079 # Permission If you give a group the `uafk.ignore` permission the player will be ignored to replace a player and will also never be detected as AFK. Useful for administrators # Installation -**[EXILED](https://github.com/galaxy119/EXILED) must be installed for this to work.** +**[NwPluginAPI](https://github.com/northwood-studios/NwPluginAPI) must be installed for this to work.** -Place the "UltimateAFK.dll" file in your Plugins folder. +You can install this plugin using the command ``p install SrLicht/Ultimate-AFK`` on the console or by downloading the .dll file and placing it in ``SCP Secret Laboratory/PluginAPI/plugins/global or your port`` -# IMPORTANT -``UltimateAFK-CustomItemSupport.dll`` It is a separate plugin that makes replacing a player search the afk inventory for customitems and give it to the person replacing him, you only have to have 1 of the two versions enabled at the same time (``UltimateAFK-CustomItemSupport.dll`` or ``UltimateAFK.dll``). diff --git a/UltimateAFK/API/Base/Handler.cs b/UltimateAFK/API/Base/Handler.cs deleted file mode 100644 index ed23ccc..0000000 --- a/UltimateAFK/API/Base/Handler.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace UltimateAFK.API.Base -{ - /// - /// Base API for allow create handlers in a "easily" way. - /// - public abstract class Handler - { - /// - /// Plugin Instance - /// - protected UltimateAFK Plugin => UltimateAFK.Instance; - - /// - /// Triggered when plugin is loaded - /// - public abstract void Start(); - - /// - /// Triggered when plugin is stopped or server restart. - /// - public abstract void Stop(); - } -} diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 8cceccc..3e18032 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -1,11 +1,12 @@ -using Exiled.API.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel; +using PlayerRoles; namespace UltimateAFK { - public class Config : IConfig + public class Config { + [Description("Setting this to false will stop the plugin from working in the next round.")] public bool IsEnabled { get; set; } = true; [Description("If you have any error in the plugin operation activate this and create an Issue in Github https://github.com/SrLicht/Ultimate-AFK/issues")] @@ -14,12 +15,6 @@ public class Config : IConfig [Description("This bool activates the logs that easily spams the console, and normally they are not required, that's why they have this separate configuration :)")] public bool SpamLogs { get; set; } = false; - [Description("Should players who leave the server be replaced by spectators?")] - public bool RepleacePlayersOnLeave { get; set; } = false; - - [Description("Here is a list of the Roletypes that will not be replaced when leave the server")] - public List DisableReplacementFor { get; set; } = new List() { RoleType.Scp0492 }; - [Description("When a player is replaced it is called a delay, if when replacing the player the position is not updated correctly, increase this value but it must not exceed 2.5 since it would be too long.")] public float ReplaceDelay { get; set; } = 1.4f; @@ -42,12 +37,12 @@ public class Config : IConfig public string MsgGrace { get; set; } = "[Ultimate-AFK] You will be moved to spectator if you do not move in less than {0} seconds."; [Description("This message will be sent to the player who has been moved to spectator when he is detected as AFK, it is also sent to the player's console.")] - public string MsgFspec { get; set; } = "You were detected as AFK and were moved to spectator. WAKE UP WAKE UP WAKE UP WAKE UP WAKE UP WAKE UP"; + public string MsgFspec { get; set; } = "You were detected as AFK and were moved to spectator"; [Description("This will be the reason for the Kick, due to the VSR it is obligatory to clarify that it is a plugin with flags like [UltimateAFK] or something similar.")] public string MsgKick { get; set; } = "[Ultimate-AFK] You were removed from the server for being AFK for too long.!"; - [Description("When a player replaces another player, this message will appear on the player's face and on the player console.")] - public string MsgReplace { get; set; } = " You replaced an AFK player."; + [Description("When a player replaces another player, this message will appear on the player's face and on the player console. | {0} it is the name of the player who was afk")] + public string MsgReplace { get; set; } = " You replaced {0} who was afk."; } -} +} \ No newline at end of file diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 76790b2..615a969 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -1,38 +1,47 @@ -using Exiled.API.Features; -using Exiled.API.Features.Roles; -using Exiled.Events.EventArgs; -using Exiled.Permissions.Extensions; -using Exiled.API.Extensions; -using MEC; +using MEC; +using PluginAPI.Core; using System; using System.Collections.Generic; using System.Linq; +using NWAPIPermissionSystem; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using PlayerRoles.PlayableScps.Scp096; using UltimateAFK.Resources; using UnityEngine; namespace UltimateAFK.Handlers.Components { - public class AFKComponent : MonoBehaviour + /// + /// Component that performs a constant afk check. + /// + public class AfkComponent : MonoBehaviour { - #region Variables - public Player MyPlayer; - public Player ReplacementPlayer = null; + /// + /// The player who owns this component. + /// + public Player Owner { get; private set; } + + // Position in the world + private Vector3 _ownerPosition; + + // Rotation of your head/camera + private Vector3 _cameraPosition; + + private Quaternion _cameraRotation; + + private float _graceTime = UltimateAFK.Singleton.Config.GraceTime; + + // The time the player was afk + private float _afkTime; /// - /// If True, the component will ignore this player, but will not be destroyed. + /// Number of times a player was detected as AFK /// - public bool IsDisable = false; - // ------------->>>>> Sex <<<<------------ - public Vector3 LastPosition; - public Vector3 LastRotation; - // -------------- Gwa gwa --------------- - public int AFKTime = 0; - public int AFKCount = 0; - - // Coroutine handle + public int AfkCount { get; private set; } + // Using a MEC Coroutine is more optimized than using Unity methods. - private CoroutineHandle CountHandler; - #endregion + private CoroutineHandle _checkHandle; private void Awake() { @@ -44,277 +53,247 @@ private void Awake() return; } - MyPlayer = ply; - - Exiled.Events.Handlers.Player.Destroying += OnDestroying; - Exiled.Events.Handlers.Player.Jumping += OnJumping; + Owner = ply; // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. - - CountHandler = Timing.RunCoroutine(CheckAfkPerSecond().CancelWith(this).CancelWith(gameObject)); - - if (MyPlayer.CheckPermission("uafk.ignore")) - { - Log.Debug($"The player {MyPlayer.Nickname} has the permission \"uafk.ignore\" disabling component"); - IsDisable = true; - } - - Log.Debug($"{MyPlayer.Nickname} component fully loaded", UltimateAFK.Instance.Config.DebugMode); + _checkHandle = Timing.RunCoroutine(CheckAfkPerSecond().CancelWith(this).CancelWith(gameObject)); + Log.Debug($"Component full loaded Owner: {Owner.Nickname} ({Owner.UserId})", UltimateAFK.Singleton.Config.DebugMode); } + /// + /// Destroys the component and clears the variables and events recorded in Exiled. + /// public void Destroy() { try { - Log.Debug($"Calling Destroy", UltimateAFK.Instance.Config.DebugMode); + Log.Debug($"Calling Destroy", UltimateAFK.Singleton.Config.DebugMode); - if (MyPlayer is null) - Log.Debug("Player is null in Destroy()"); + if (Owner is null) + Log.Debug("When trying to destroy the component, owner is null", UltimateAFK.Singleton.Config.DebugMode); - MyPlayer = null; - ReplacementPlayer = null; - - Exiled.Events.Handlers.Player.Destroying -= OnDestroying; - Exiled.Events.Handlers.Player.Jumping -= OnJumping; + Timing.KillCoroutines(_checkHandle); Destroy(this); } catch (Exception e) { - Log.Error($"{this} " + e); + Log.Error($"Error while trying to destroy {this} {e}"); throw; } } - public void CheckAFK() + private IEnumerator CheckAfkPerSecond() { - Log.Debug("CheckAFK before the if call", UltimateAFK.Instance.Config.DebugMode && UltimateAFK.Instance.Config.SpamLogs); - - var cantcontinue = MyPlayer.IsDead || Player.List.Count() <= UltimateAFK.Instance.Config.MinPlayers || (UltimateAFK.Instance.Config.IgnoreTut && MyPlayer.IsTutorial) || Round.IsLobby; - - if (!cantcontinue) + for (;;) { - Log.Debug("CheckAFK inside the if called", UltimateAFK.Instance.Config.DebugMode && UltimateAFK.Instance.Config.SpamLogs); + yield return Timing.WaitForSeconds(1.2f); - bool isSCP079 = MyPlayer.Role is Scp079Role; - bool isSCP096 = MyPlayer.Role is Scp096Role; - bool isNotCrying = false; - Scp079Role scp079role = null; + Log.Debug("Calling CheckAFK", UltimateAFK.Singleton.Config.DebugMode && UltimateAFK.Singleton.Config.SpamLogs); - if (isSCP096 || isSCP079) + try { - if (isSCP096) - { - var role = MyPlayer.Role as Scp096Role; - isNotCrying = role.TryingNotToCry; - } - else if (isSCP079) - { - scp079role = MyPlayer.Role as Scp079Role; - } + CheckAfk(); } + catch (Exception e) + { + Log.Error($"{this} error on CheckAFK: {e} || {e.StackTrace}"); + } + } + } - Vector3 position = MyPlayer.Position; - Vector3 vector = isSCP079 ? scp079role.Camera.HeadPosition : (Vector3)MyPlayer.Rotation; - - bool isMoving = position != LastPosition || vector != LastRotation || isNotCrying; + private void CheckAfk() + { + if (!Continue(Owner)) + return; - if (isMoving) - { - Log.Debug("CheckAFK() player is moving, changing position variables", UltimateAFK.Instance.Config.DebugMode); + var position = Owner.Position; + var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Position; + var cameraRotation = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp0792 ? new Quaternion(scp0792.CurrentCamera.HorizontalRotation, scp0792.CurrentCamera.VerticalRotation, scp0792.CurrentCamera.RollRotation, 0f ) : Owner.Camera.rotation; + + // Player is moving + if (cameraPosition != _cameraPosition || position != _ownerPosition || _cameraRotation != cameraRotation) + { + _cameraPosition = cameraPosition; + _cameraRotation = cameraRotation; + _ownerPosition = position; + _afkTime = 0f; + } + else if (!(Owner.Role == RoleTypeId.Scp096 && (Owner.ReferenceHub.roleManager.CurrentRole as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry))) + { + Log.Debug($"{Owner.Nickname} is in not moving, AFKTime: {_afkTime}", UltimateAFK.Singleton.Config.DebugMode); - this.LastPosition = position; - this.LastRotation = vector; - this.AFKTime = 0; + if(_afkTime++ < UltimateAFK.Singleton.Config.AfkTime) return; - if (ReplacementPlayer != null) - this.ReplacementPlayer = null; + var graceNumb = UltimateAFK.Singleton.Config.GraceTime - (_afkTime - UltimateAFK.Singleton.Config.AfkTime); + + if (graceNumb > 0) + { + // The player is in grace time, so let's warn him that he has been afk for too long. + Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 1, + shouldClearPrevious: true); } else { + Log.Info($"{Owner.Nickname} ({Owner.UserId}) Detected as AFK"); - var isAfk = AFKTime++ >= UltimateAFK.Instance.Config.AfkTime; - - Log.Debug($"{MyPlayer.Nickname} is in not moving, AFKTime: {AFKTime}", UltimateAFK.Instance.Config.DebugMode); - - if (isAfk) - { - var graceNumb = UltimateAFK.Instance.Config.AfkTime + UltimateAFK.Instance.Config.GraceTime - this.AFKTime; - - var inGraceTime = graceNumb > 0; - - if (inGraceTime) - { - var message = string.Format(UltimateAFK.Instance.Config.MsgGrace, graceNumb); - - MyPlayer.Broadcast(2, message, Broadcast.BroadcastFlags.Normal, true); - } - else - { - Log.Info($"{MyPlayer.Nickname} ({MyPlayer.UserId}) Detected as AFK"); - - //--- I am going to save the variables that I will need later - - var items = MyPlayer.Items.ToList(); - var role = MyPlayer.Role; - var plyposition = MyPlayer.Position; - var health = MyPlayer.Health; - var ammo = MyPlayer.Ammo; - var customitems = new List(); - - var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC")); - ReplacementPlayer = list.FirstOrDefault(); - - if (ReplacementPlayer == null) - { - Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Instance.Config.DebugMode); - - if (UltimateAFK.Instance.Config.AfkCount != -1) - { - AFKCount++; - - if (AFKCount >= UltimateAFK.Instance.Config.AfkCount) - { - MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgKick, "white"); - - MyPlayer.Kick(UltimateAFK.Instance.Config.MsgKick, "[UltimateAFK]"); - - return; - } - } - - MyPlayer.SetRole(RoleType.Spectator); - MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); - MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); - } - else - { - Log.Debug($"Replacement Player found\nNickname: {ReplacementPlayer.Nickname}\nUserID: {ReplacementPlayer.UserId}\n Role: {ReplacementPlayer.Role.Type}", UltimateAFK.Instance.Config.DebugMode); - - MainHandler.ReplacingPlayers.Add(ReplacementPlayer, new AFKData - { - Position = plyposition, - Role = role, - Ammo = ammo, - Health = health, - Items = items, - CustomItems = customitems, - SCP079Role = scp079role - - }); - - Log.Debug("Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); - - this.ReplacementPlayer.SetRole(role); - // I do this, to avoid that when changing to spectator the items that the player had are thrown to the ground :) - MyPlayer.ClearInventory(); - - if (UltimateAFK.Instance.Config.AfkCount != -1) - { - AFKCount++; - - if (AFKCount >= UltimateAFK.Instance.Config.AfkCount) - { - MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgKick, "white"); - - MyPlayer.Kick(UltimateAFK.Instance.Config.MsgKick, "[UltimateAFK]"); - return; - } - } - - MyPlayer.SetRole(RoleType.Spectator); - MyPlayer.Broadcast(30, UltimateAFK.Instance.Config.MsgFspec, Broadcast.BroadcastFlags.Normal, true); - MyPlayer.SendConsoleMessage(UltimateAFK.Instance.Config.MsgFspec, "white"); - } - } - } + Replace(Owner, Owner.Role); } } } - private IEnumerator CheckAfkPerSecond() + /// + /// Performs player replacement. + /// + /// Player to be replaced + /// This replacement happens when the player is disconnected from the server ? + public void Replace(Player player, RoleTypeId role) { - while (true) + Player replacement = GetReplacement(); + + if (replacement == null) { - Log.Debug("Calling CheckAfkPerSecond() before If", UltimateAFK.Instance.Config.DebugMode && UltimateAFK.Instance.Config.SpamLogs); + Log.Debug("Unable to find replacement player, moving to spectator...", + UltimateAFK.Singleton.Config.DebugMode); - if (!IsDisable && Round.IsStarted) + player.SetRole(RoleTypeId.Spectator); + + if (UltimateAFK.Singleton.Config.AfkCount != -1) { - try - { - Log.Debug("Call of CheckAfkPerSecond() inside If", UltimateAFK.Instance.Config.DebugMode && UltimateAFK.Instance.Config.SpamLogs); + AfkCount++; - CheckAFK(); - } - catch (Exception e) + if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) { - Log.Error($"{this} Error on CheckAFK(): {e} || {e.StackTrace}"); - } + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); + + player.Kick(UltimateAFK.Singleton.Config.MsgKick); + return; + } } - yield return Timing.WaitForSeconds(1.2f); + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + + return; } - } - // Technically this is not necessary since the component is destroyed when the player is destroyed but for fear of leaving a ghost component I better do this. - public void OnDestroying(DestroyingEventArgs ev) - { - if (ev.Player == MyPlayer) - { + Log.Debug($"Replacement Player found\nNickname: {replacement.Nickname}\nUserID: {replacement.UserId}", + UltimateAFK.Singleton.Config.DebugMode); - if (Round.IsStarted && !Round.IsEnded && UltimateAFK.Instance.Config.RepleacePlayersOnLeave && !UltimateAFK.Instance.Config.DisableReplacementFor.Contains(ev.Player.Role)) + Scp079Role scp079Role; + if (role == RoleTypeId.Scp079 && + (scp079Role = player.ReferenceHub.roleManager.CurrentRole as Scp079Role) != null && + scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager scp079TierManager) && + scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager scp079AuxManager)) + { + MainHandler.ReplacingPlayers.Add(replacement, new AFKData { - Log.Debug("OnDestroying | My Player leave the server, trying to repleace", UltimateAFK.Instance.Config.DebugMode); - var list = Player.List.Where(p => p.IsDead && p.UserId != MyPlayer.UserId && !p.IsOverwatchEnabled && !p.CheckPermission("uafk.ignore") && !p.SessionVariables.ContainsKey("IsNPC") && !MainHandler.ReplacingPlayers.ContainsKey(p)); - ReplacementPlayer = list.FirstOrDefault(); - - // I don't think it will work, but I don't lose anything by trying. - if (ReplacementPlayer is not null) + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data { - Scp079Role scp079role = MyPlayer.Role as Scp079Role; + Role = scp079Role, + Energy = scp079AuxManager.CurrentAux, + Experience = scp079TierManager.TotalExp, + } + }); - var customitems = new List(); - var items = MyPlayer.Items.ToList(); - var role = MyPlayer.Role; - MyPlayer.ClearInventory(); + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); - MainHandler.ReplacingPlayers.Add(ReplacementPlayer, new AFKData - { - Position = MyPlayer.Position, - Role = role, - Ammo = MyPlayer.Ammo, - Health = MyPlayer.Health, - Items = items, - CustomItems = customitems, - SCP079Role = scp079role + if (UltimateAFK.Singleton.Config.AfkCount != -1) + { + AfkCount++; - }); + if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) + { + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - Log.Debug("OnDestroy| Moving replacement player to the previous player's role", UltimateAFK.Instance.Config.DebugMode); + player.Kick(UltimateAFK.Singleton.Config.MsgKick); - this.ReplacementPlayer.SetRole(role); + replacement.SetRole(player.Role); + return; } - else + } + + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + + replacement.SetRole(player.Role); + } + else + { + MainHandler.ReplacingPlayers.Add(replacement, new AFKData + { + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data { - Log.Debug("OnDestroying | A replacement player could not be found.", UltimateAFK.Instance.Config.DebugMode); + Role = null, + Energy = 0f, + Experience = 0, } + }); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + if (UltimateAFK.Singleton.Config.AfkCount != -1) + { + AfkCount++; + + if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) + { + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); + + player.Kick(UltimateAFK.Singleton.Config.MsgKick); + replacement.SetRole(player.Role); + return; + } } - Log.Debug($"OnDestroying | My player was destroyed by DestroyingEventArg, destroying component", UltimateAFK.Instance.Config.DebugMode); - Destroy(); + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + + replacement.SetRole(player.Role); } } - // It is better to do it here than in MainHandler. For those who do not understand when jumping the player restarts his AFK time, since if he jumps technically he is not afk. - public void OnJumping(JumpingEventArgs ev) + /// + /// Obtains a player who qualifies for replacement. + /// + private Player GetReplacement() { - if (ev.Player != null && ev.Player == MyPlayer && !IsDisable) + foreach (var player in Player.GetPlayers()) { - Log.Debug($"OnJumping | My player is jumping, resetting AFK counter", UltimateAFK.Instance.Config.DebugMode); - AFKTime = 0; + if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer) + continue; + + return player; } + + return null; + } + + private bool Continue(Player ply) + { + var value = ply.IsAlive && Round.IsRoundStarted && Player.GetPlayers().Count >= UltimateAFK.Singleton.Config.MinPlayers && + (Owner.Role != RoleTypeId.Tutorial || !UltimateAFK.Singleton.Config.IgnoreTut); + + Log.Info($"Continue value is {value}"); + + return ply.IsAlive && Round.IsRoundStarted && Player.Count >= UltimateAFK.Singleton.Config.MinPlayers && + (Owner.Role != RoleTypeId.Tutorial || !UltimateAFK.Singleton.Config.IgnoreTut); } } -} +} \ No newline at end of file diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 9cdd9b8..c989037 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -1,243 +1,117 @@ -using Exiled.API.Features; -using Exiled.API.Features.Roles; -using Exiled.Events.EventArgs; -using MEC; +using MEC; +using PluginAPI.Core; using System.Collections.Generic; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using PluginAPI.Core.Attributes; +using PluginAPI.Enums; using UltimateAFK.Handlers.Components; using UltimateAFK.Resources; +using UnityEngine; namespace UltimateAFK.Handlers { /// - /// Class where the will be given to the and the afk counter will be reset by the player's actions. + /// Main class where players are given the AFK component and replacement players are stored. /// - public class MainHandler : API.Base.Handler + public class MainHandler { - // A list of players who are replacing someone else - public static Dictionary ReplacingPlayers = new Dictionary(); + private readonly UltimateAFK Plugin; - public override void Start() + public MainHandler(UltimateAFK plugin) { - Log.Warn("Loading MainHandler"); - - Exiled.Events.Handlers.Player.Verified += OnVerify; - Exiled.Events.Handlers.Player.ChangingRole += OnChangingRole; - - Exiled.Events.Handlers.Player.Shooting += OnShooting; - Exiled.Events.Handlers.Player.InteractingDoor += OnInteractDoor; - Exiled.Events.Handlers.Player.InteractingElevator += OnInteractElevator; - Exiled.Events.Handlers.Player.InteractingLocker += OnInteractLocker; - Exiled.Events.Handlers.Player.InteractingScp330 += OnInteract330; - - Exiled.Events.Handlers.Scp914.Activating += OnInteract914; - Exiled.Events.Handlers.Scp914.ChangingKnobSetting += OnChaningKnob914; - - Exiled.Events.Handlers.Player.DroppingItem += OnDroppingItems; - Exiled.Events.Handlers.Player.DroppingAmmo += OnDroppingAmmo; - - Exiled.Events.Handlers.Scp079.GainingExperience += OnGaingExp; - - Exiled.Events.Handlers.Scp079.ChangingCamera += OnChangeCamara; - - Exiled.Events.Handlers.Server.RoundStarted += OnRoundStarted; - - Log.Warn("MainHandler Fully loaded"); - } - - public override void Stop() - { - Exiled.Events.Handlers.Player.Verified -= OnVerify; - Exiled.Events.Handlers.Player.ChangingRole -= OnChangingRole; - - Exiled.Events.Handlers.Player.Shooting -= OnShooting; - Exiled.Events.Handlers.Player.InteractingDoor -= OnInteractDoor; - Exiled.Events.Handlers.Player.InteractingElevator -= OnInteractElevator; - Exiled.Events.Handlers.Player.InteractingLocker -= OnInteractLocker; - Exiled.Events.Handlers.Player.InteractingScp330 -= OnInteract330; - - Exiled.Events.Handlers.Scp914.Activating -= OnInteract914; - Exiled.Events.Handlers.Scp914.ChangingKnobSetting -= OnChaningKnob914; - - Exiled.Events.Handlers.Player.DroppingItem -= OnDroppingItems; - Exiled.Events.Handlers.Player.DroppingAmmo -= OnDroppingAmmo; - - Exiled.Events.Handlers.Scp079.GainingExperience -= OnGaingExp; - - Exiled.Events.Handlers.Scp079.ChangingCamera -= OnChangeCamara; - - Exiled.Events.Handlers.Server.RoundStarted -= OnRoundStarted; - - ReplacingPlayers.Clear(); - ReplacingPlayers = null; + Plugin = plugin; } + /// + /// A dictionary where replacement players are stored to give them the stats and items of the original player. + /// + public static Dictionary ReplacingPlayers = new Dictionary(); - // Player Join server - public void OnVerify(VerifiedEventArgs ev) + [PluginEvent(ServerEventType.PlayerJoined)] + private void OnPlayerJoin(Player player) { - if (ev.Player.GameObject.TryGetComponent(out var com)) + if(!Plugin.Config.IsEnabled) return; + + if (player.GameObject.TryGetComponent(out var com)) { com.Destroy(); - Log.Debug($"Adding the Component to {ev.Player.Nickname}", UltimateAFK.Instance.Config.DebugMode); - ev.Player.GameObject.AddComponent(); + Log.Debug($"Adding the Component to {player.Nickname}", UltimateAFK.Singleton.Config.DebugMode); + player.GameObject.AddComponent(); } else { - Log.Debug($"Adding the Component to {ev.Player.Nickname}", UltimateAFK.Instance.Config.DebugMode); + Log.Debug($"Adding the Component to {player.Nickname}", UltimateAFK.Singleton.Config.DebugMode); - ev.Player.GameObject.AddComponent(); + player.GameObject.AddComponent(); } - - } - public void OnChangingRole(ChangingRoleEventArgs ev) + [PluginEvent(ServerEventType.PlayerChangeRole)] + private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId newRole, RoleChangeReason reason) { - if (ev.Player != null && ev.Player.GameObject.TryGetComponent(out var component)) + try { - try - { - if (ev.Player.SessionVariables.ContainsKey("IsNPC")) - { - component.Destroy(); - Log.Warn("Destroying the component to a player who was an NPC"); - return; - } - - if (ReplacingPlayers.TryGetValue(ev.Player, out var data)) - { - Log.Debug("Detecting player who replaces an AFK", UltimateAFK.Instance.Config.DebugMode); - - ev.Items.Clear(); - - Log.Debug("Adding items from previous player", UltimateAFK.Instance.Config.DebugMode); - foreach (var item in data.Items) - { - ev.Items.Add(item.Type); - } - - Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => - { - Log.Debug("Changing player position and HP", Plugin.Config.DebugMode); - - ev.Player.Position = data.Position; - ev.Player.Broadcast(16, UltimateAFK.Instance.Config.MsgReplace, Broadcast.BroadcastFlags.Normal, true); - ev.Player.SendConsoleMessage(UltimateAFK.Instance.Config.MsgReplace, "white"); - ev.Player.Health = data.Health; - - Log.Debug("Adding Ammo", UltimateAFK.Instance.Config.DebugMode); - - ev.Player.Inventory.UserInventory.ReserveAmmo.Clear(); - ev.Player.Inventory.UserInventory.ReserveAmmo = data.Ammo; - ev.Player.Inventory.SendAmmoNextFrame = true; - - if (ev.NewRole == RoleType.Scp079 && data.SCP079Role != null) - { - Log.Debug("The new role is a SCP079, transferring level and experience.", UltimateAFK.Instance.Config.DebugMode); - - var scprole = ev.Player.Role as Scp079Role; - scprole.Level = data.SCP079Role.Level; - scprole.Energy = data.SCP079Role.Energy; - scprole.Experience = data.SCP079Role.Experience; - } - - Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Instance.Config.DebugMode); - - ReplacingPlayers.Remove(ev.Player); - - }); - } - } - catch (System.Exception e) - { - Log.Error($"Error when trying to replace a player || {e} {e.StackTrace}"); - } - - } - } - - - - public void OnRoundStarted() - { - ReplacingPlayers.Clear(); - } + if (player == null || !ReplacingPlayers.TryGetValue(player, out var data) || + !player.GameObject.TryGetComponent(out var _)) + return; - private void ResetAFKTime(Player ply) - { - if (ply != null && ply.GameObject.TryGetComponent(out var comp)) - { - comp.AFKTime = 0; + Log.Debug($"Detecting player {player.Nickname} ({player.UserId}) who replaced a player who was afk", UltimateAFK.Singleton.Config.DebugMode); + + Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => RepleaceItemsAndStats(player, data, newRole)); } - } - - private IEnumerator CountHandle() - { - while (true) + catch (System.Exception e) { - yield return Timing.WaitForSeconds(1.5f); + Log.Error($"Error on {GetType().Name} (OnChangingRole) || {e} {e.StackTrace}"); } } - #region Reset AFK Timers - - public void OnInteractDoor(InteractingDoorEventArgs ev) - { - ResetAFKTime(ev.Player); - } - - public void OnInteractElevator(InteractingElevatorEventArgs ev) - { - ResetAFKTime(ev.Player); - } - - public void OnInteractLocker(InteractingLockerEventArgs ev) + [PluginEvent(ServerEventType.RoundStart)] + private void OnRoundStarted() { - ResetAFKTime(ev.Player); + ReplacingPlayers.Clear(); } - public void OnInteract330(InteractingScp330EventArgs ev) + /// + /// Performs the change of stats and items to the replacement player + /// + private void RepleaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) { - ResetAFKTime(ev.Player); - } + Log.Debug($"Replacing {ply.Nickname} ({ply.UserId}) data", Plugin.Config.DebugMode); - public void OnInteract914(ActivatingEventArgs ev) - { - ResetAFKTime(ev.Player); - } + Scp079Role scp079Role; + if (newrole == RoleTypeId.Scp079 && data.SCP079.Role != null && + (scp079Role = ply.ReferenceHub.roleManager.CurrentRole as Scp079Role) != null + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager scp079TierManager) && + scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager scp079AuxManager)) + { + Log.Debug("The new role is a SCP079, transferring energy and experience.", UltimateAFK.Singleton.Config.DebugMode); - public void OnChaningKnob914(ChangingKnobSettingEventArgs ev) - { - ResetAFKTime(ev.Player); - } + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgReplace, 16, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgReplace, "white"); - public void OnDroppingItems(DroppingItemEventArgs ev) - { - ResetAFKTime(ev.Player); - } + scp079TierManager.TotalExp = data.SCP079.Experience; + scp079AuxManager.CurrentAux = data.SCP079.Energy; + ReplacingPlayers.Remove(ply); + return; + } - public void OnDroppingAmmo(DroppingAmmoEventArgs ev) - { - ResetAFKTime(ev.Player); - } + Log.Debug($"Changing player {ply.Nickname} ({ply.UserId}) position and HP", Plugin.Config.DebugMode); - public void OnGaingExp(GainingExperienceEventArgs ev) - { - ResetAFKTime(ev.Player); + ply.Position = data.Position; + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgReplace, 16, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgReplace, "white"); + ply.Health = data.Health; - } + Log.Debug($"Adding Ammo to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); + ply.SendAmmo(data.Ammo); + Log.Debug($"Adding items to {ply.Nickname} ({ply.UserId})"); + ply.SendItems(data.Items); - public void OnChangeCamara(ChangingCameraEventArgs ev) - { - ResetAFKTime(ev.Player); - } + Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Singleton.Config.DebugMode); - public void OnShooting(ShootingEventArgs ev) - { - ResetAFKTime(ev.Shooter); + ReplacingPlayers.Remove(ply); } - #endregion - } -} +} \ No newline at end of file diff --git a/UltimateAFK/Resources/AFKData.cs b/UltimateAFK/Resources/AFKData.cs index 93870e1..449eda1 100644 --- a/UltimateAFK/Resources/AFKData.cs +++ b/UltimateAFK/Resources/AFKData.cs @@ -1,23 +1,22 @@ -using Exiled.API.Features.Roles; -using System.Collections.Generic; +using System.Collections.Generic; +using PlayerRoles; using UnityEngine; namespace UltimateAFK.Resources { public struct AFKData { + public string NickName { get; set; } public Vector3 Position { get; set; } - public RoleType Role { get; set; } + public RoleTypeId Role { get; set; } public Dictionary Ammo { get; set; } - public List CustomItems { get; set; } - - public List Items { get; set; } + public List Items { get; set; } public float Health { get; set; } - public Scp079Role SCP079Role { get; set; } + public Scp079Data SCP079 { get; set; } } -} +} \ No newline at end of file diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs new file mode 100644 index 0000000..3f02637 --- /dev/null +++ b/UltimateAFK/Resources/Extensions.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using InventorySystem.Items; +using PluginAPI.Core; + +namespace UltimateAFK.Resources +{ + public static class Extensions + { + public static void SendItems(this Player player, List types) + { + foreach (var item in types) + { + player.AddItem(item); + } + } + + public static void SendAmmo(this Player ply, Dictionary ammo) + { + foreach (var ammoItem in ammo) + { + ply.AddAmmo(ammoItem.Key, ammoItem.Value); + } + } + + public static List GetItems(this Player ply) + { + var items = ply.ReferenceHub.inventory.UserInventory.Items; + var returnitems = new List(); + + foreach(var i in items.Values) + { + returnitems.Add(i.ItemTypeId); + } + + return returnitems; + } + } +} \ No newline at end of file diff --git a/UltimateAFK/Resources/Scp079Data.cs b/UltimateAFK/Resources/Scp079Data.cs new file mode 100644 index 0000000..1278f5e --- /dev/null +++ b/UltimateAFK/Resources/Scp079Data.cs @@ -0,0 +1,13 @@ +using PlayerRoles.PlayableScps.Scp079; + +namespace UltimateAFK.Resources +{ + public struct Scp079Data + { + public Scp079Role Role { get; set; } + + public int Experience { get; set; } + + public float Energy { get; set; } + } +} \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index e9c3ebd..6b0c778 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,54 +1,23 @@ -using Exiled.API.Features; -using System; +using System; using System.Collections.Generic; +using PluginAPI.Core.Attributes; namespace UltimateAFK { /// /// Main class where all the handlers are loaded. /// - public class UltimateAFK : Plugin + public class UltimateAFK { - // This is my programming style and if you don't like it, good for you. + public static UltimateAFK Singleton; - #region Variables - public static UltimateAFK Instance; - - public List Handlers; - #endregion - - public override string Author => "SrLicht"; - - public override string Name => "Ultimate-AFK"; - - public override string Prefix => "Ultimate_Afk"; - - public override Version RequiredExiledVersion => new Version(5, 2, 3); - - public override Version Version => new Version(5, 0, 2); - - public override void OnEnabled() + [PluginConfig] public Config Config; + + [PluginEntryPoint("UltimateAFK", "1.0.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + void OnEnabled() { - Instance = this; - - Handlers = new List() - { - new Handlers.MainHandler() - }; - - Handlers.ForEach(h => h.Start()); - base.OnEnabled(); + Singleton = this; + PluginAPI.Events.EventManager.RegisterEvents(this, new Handlers.MainHandler(Singleton)); } - - public override void OnDisabled() - { - Handlers.ForEach((h) => h.Stop()); - Handlers.Clear(); - Handlers = null; - Instance = null; - base.OnDisabled(); - } - - } -} +} \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 9c05df2..fdb3d84 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -23,11 +23,12 @@ - - + - - + + + + @@ -38,6 +39,8 @@ + + \ No newline at end of file From e636cf737ab47a7a6f673946360454f8391fe7cd Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:35:51 -0300 Subject: [PATCH 063/146] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 91e0cfa..78b09da 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,9 @@ If you give a group the `uafk.ignore` permission the player will be ignored to replace a player and will also never be detected as AFK. Useful for administrators # Installation -**[NwPluginAPI](https://github.com/northwood-studios/NwPluginAPI) must be installed for this to work.** +**This plugin only works in [NwPluginAPI](https://github.com/northwood-studios/NwPluginAPI)** + +**You need [NWAPIPermissionSystem](https://github.com/CedModV2/NWAPIPermissionSystem) for this plugin to work.** You can install this plugin using the command ``p install SrLicht/Ultimate-AFK`` on the console or by downloading the .dll file and placing it in ``SCP Secret Laboratory/PluginAPI/plugins/global or your port`` From 61c9b9aa16bb32f30ffb8ef646dc964fe806e967 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:02:58 -0300 Subject: [PATCH 064/146] Update dotnet.yml --- .github/workflows/dotnet.yml | 59 ++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index c813f65..e053696 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,14 +1,19 @@ -name: Build +name: .Net Core Master on: push: + branches: [ master ] workflow_dispatch: env: # Para la version 10.2 el link de referencias tiene que ser este https://www.exiled.host/build_deps/Master.zip # Pero si es para la beta https://exiled.host/build_deps/Dev.zip - EXILED_REFERENCES_URL: https://exiled.host/build_deps/Dev.zip - EXILED_REFERENCES_PATH: ${{ github.workspace }}/References + #EXILED_REFERENCES_URL: https://exiled.host/build_deps/Dev.zip + #EXILED_REFERENCES_PATH: ${{ github.workspace }}/References + DEPOT_DOWNLOADER_VERSION: 2.4.7 + SL_REFERENCES: D:\a\UltimateAfk\SCPSL_REFERENCES\SCPSL_Data\Managed + UNITY_REFERENCES: D:\a\UltimateAfk\SCPSL_REFERENCES\SCPSL_Data\Managed + SL_BRANCH: pluginapi-beta jobs: @@ -17,33 +22,41 @@ jobs: runs-on: windows-latest steps: + - uses: actions/checkout@v3 - - name: Setup .NET Core SDK - uses: actions/setup-dotnet@v1.7.2 - - - name: Setup Nuget - uses: iRebbok/setup-nuget@master + - name: Setup .NET + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 6.0.x - - uses: actions/checkout@v2.3.4 + - name: Download depot downloader + shell: pwsh + run: | + New-Item -ItemType Directory -Force -Path D:\a\UltimateAfk + New-Item -ItemType Directory -Force -Path D:\a\UltimateAfk\DepotDownloader + Invoke-WebRequest -Uri "https://github.com/SteamRE/DepotDownloader/releases/download/DepotDownloader_${{ env.DEPOT_DOWNLOADER_VERSION }}/depotdownloader-${{ env.DEPOT_DOWNLOADER_VERSION }}.zip" -OutFile "D:\a\UltimateAfk\depotdownloader.zip" + Expand-Archive -Path D:\a\UltimateAfk\depotdownloader.zip -PassThru -DestinationPath D:\a\UltimateAfk/DepotDownloader - - name: Get references + - name: Download SCPSL references. shell: pwsh run: | - Invoke-WebRequest -Uri ${{ env.EXILED_REFERENCES_URL }} -OutFile ${{ github.workspace }}/Dev.zip - Expand-Archive -Path Dev.zip -DestinationPath ${{ env.EXILED_REFERENCES_PATH }} + New-Item -ItemType Directory -Force -Path D:\a\UltimateAfk\SCPSL_REFERENCES + Start-Process -NoNewWindow -Wait -FilePath "D:\a\UltimateAfk\DepotDownloader\DepotDownloader.exe" -WorkingDirectory "D:\a\UltimateAfk\DepotDownloader" -ArgumentList '-app 996560','-beta nightly','-betapassword ${{ env.SL_BRANCH }}','-dir D:\a\UltimateAfk\SCPSL_REFERENCES','-filelist "${{ github.workspace }}\download-files.txt"' - - name: Build - env: - EXILED_REFERENCES: ${{ env.EXILED_REFERENCES_PATH }} + name: Download NWAPIPermissionSystem.dll shell: pwsh run: | - ./build.ps1 - $File = (Get-ChildItem -Path . -Include 'EXILED.*.nupkg' -Recurse).Name - Out-File -FilePath ${{ github.env }} -InputObject "PackageFile=$File" -Encoding utf-8 -Append + Invoke-WebRequest -Uri "https://github.com/CedModV2/NWAPIPermissionSystem/releases/download/0.0.3/NWAPIPermissionSystem.dll" -OutFile ${{ env.SL_REFERENCES }}/NWAPIPermissionSystem.dll + + - name: Restore dependencies + run: dotnet restore - - name: Upload artifacts - uses: actions/upload-artifact@v2 - with: - name: Build Results - path: bin/Debug + - name: Build + run: dotnet build --no-restore --configuration Releas + + - name: Upload + uses: actions/upload-artifact@v3 + with: + name: UltimateAFK + path: ${{ github.workspace }}/bin/Debug/UltimateAFK.dll retention-days: 1 \ No newline at end of file From 78742d002da5981454b765db35c71c4a89dc1c03 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:04:55 -0300 Subject: [PATCH 065/146] Update dotnet.yml --- .github/workflows/dotnet.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index e053696..e8e70f9 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,9 +1,8 @@ -name: .Net Core Master +name: Build Plugin NWAPI on: push: branches: [ master ] - workflow_dispatch: env: # Para la version 10.2 el link de referencias tiene que ser este https://www.exiled.host/build_deps/Master.zip From 85be45787e1807692c313282f31025e9ea6c39ea Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:07:01 -0300 Subject: [PATCH 066/146] Update dotnet.yml --- .github/workflows/dotnet.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index e8e70f9..dcbe5bf 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -15,11 +15,9 @@ env: SL_BRANCH: pluginapi-beta jobs: - build: runs-on: windows-latest - steps: - uses: actions/checkout@v3 @@ -42,7 +40,7 @@ jobs: New-Item -ItemType Directory -Force -Path D:\a\UltimateAfk\SCPSL_REFERENCES Start-Process -NoNewWindow -Wait -FilePath "D:\a\UltimateAfk\DepotDownloader\DepotDownloader.exe" -WorkingDirectory "D:\a\UltimateAfk\DepotDownloader" -ArgumentList '-app 996560','-beta nightly','-betapassword ${{ env.SL_BRANCH }}','-dir D:\a\UltimateAfk\SCPSL_REFERENCES','-filelist "${{ github.workspace }}\download-files.txt"' - name: Download NWAPIPermissionSystem.dll + - name: Download NWAPIPermissionSystem.dll shell: pwsh run: | Invoke-WebRequest -Uri "https://github.com/CedModV2/NWAPIPermissionSystem/releases/download/0.0.3/NWAPIPermissionSystem.dll" -OutFile ${{ env.SL_REFERENCES }}/NWAPIPermissionSystem.dll From 58d5f7c25e9312dcae0a2c177b2b0fa8d946c209 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:13:00 -0300 Subject: [PATCH 067/146] Update dotnet.yml --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index dcbe5bf..b15cfcb 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -49,7 +49,7 @@ jobs: run: dotnet restore - name: Build - run: dotnet build --no-restore --configuration Releas + run: dotnet build --no-restore --configuration Debug - name: Upload uses: actions/upload-artifact@v3 From 31c220ac3a77a07b87c02cfd1936ec33a382ae21 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:21:11 -0300 Subject: [PATCH 068/146] Update dotnet.yml --- .github/workflows/dotnet.yml | 58 ++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index b15cfcb..2efb196 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -3,57 +3,49 @@ name: Build Plugin NWAPI on: push: branches: [ master ] + workflow_dispatch: env: # Para la version 10.2 el link de referencias tiene que ser este https://www.exiled.host/build_deps/Master.zip # Pero si es para la beta https://exiled.host/build_deps/Dev.zip - #EXILED_REFERENCES_URL: https://exiled.host/build_deps/Dev.zip + EXILED_REFERENCES_URL: https://www.exiled.host/build_deps/EA_REFS.zip #EXILED_REFERENCES_PATH: ${{ github.workspace }}/References - DEPOT_DOWNLOADER_VERSION: 2.4.7 - SL_REFERENCES: D:\a\UltimateAfk\SCPSL_REFERENCES\SCPSL_Data\Managed - UNITY_REFERENCES: D:\a\UltimateAfk\SCPSL_REFERENCES\SCPSL_Data\Managed - SL_BRANCH: pluginapi-beta + SL_REFERENCES: ${{ github.workspace }}/References + UNITY_REFERENCES: ${{ github.workspace }}/References jobs: + build: runs-on: windows-latest + steps: - - uses: actions/checkout@v3 - - name: Setup .NET - uses: actions/setup-dotnet@v2 - with: - dotnet-version: 6.0.x + - name: Setup .NET Core SDK + uses: actions/setup-dotnet@v1.7.2 - - name: Download depot downloader - shell: pwsh - run: | - New-Item -ItemType Directory -Force -Path D:\a\UltimateAfk - New-Item -ItemType Directory -Force -Path D:\a\UltimateAfk\DepotDownloader - Invoke-WebRequest -Uri "https://github.com/SteamRE/DepotDownloader/releases/download/DepotDownloader_${{ env.DEPOT_DOWNLOADER_VERSION }}/depotdownloader-${{ env.DEPOT_DOWNLOADER_VERSION }}.zip" -OutFile "D:\a\UltimateAfk\depotdownloader.zip" - Expand-Archive -Path D:\a\UltimateAfk\depotdownloader.zip -PassThru -DestinationPath D:\a\UltimateAfk/DepotDownloader + - name: Setup Nuget + uses: iRebbok/setup-nuget@master - - name: Download SCPSL references. - shell: pwsh - run: | - New-Item -ItemType Directory -Force -Path D:\a\UltimateAfk\SCPSL_REFERENCES - Start-Process -NoNewWindow -Wait -FilePath "D:\a\UltimateAfk\DepotDownloader\DepotDownloader.exe" -WorkingDirectory "D:\a\UltimateAfk\DepotDownloader" -ArgumentList '-app 996560','-beta nightly','-betapassword ${{ env.SL_BRANCH }}','-dir D:\a\UltimateAfk\SCPSL_REFERENCES','-filelist "${{ github.workspace }}\download-files.txt"' + - uses: actions/checkout@v2.3.4 - - name: Download NWAPIPermissionSystem.dll + - name: Get references shell: pwsh run: | - Invoke-WebRequest -Uri "https://github.com/CedModV2/NWAPIPermissionSystem/releases/download/0.0.3/NWAPIPermissionSystem.dll" -OutFile ${{ env.SL_REFERENCES }}/NWAPIPermissionSystem.dll - - - name: Restore dependencies - run: dotnet restore + Invoke-WebRequest -Uri ${{ env.EXILED_REFERENCES_URL }} -OutFile ${{ github.workspace }}/References.zip + Expand-Archive -Path References.zip -DestinationPath ${{ env.SL_REFERENCES }} - name: Build - run: dotnet build --no-restore --configuration Debug + env: + shell: pwsh + run: | + ./build.ps1 + $File = (Get-ChildItem -Path . -Include 'EXILED.*.nupkg' -Recurse).Name + Out-File -FilePath ${{ github.env }} -InputObject "PackageFile=$File" -Encoding utf-8 -Append - - name: Upload - uses: actions/upload-artifact@v3 - with: - name: UltimateAFK - path: ${{ github.workspace }}/bin/Debug/UltimateAFK.dll + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: Build Results + path: bin/Debug retention-days: 1 \ No newline at end of file From 74456534eb69b654ad1b76f6d25f0016543010f4 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:21:56 -0300 Subject: [PATCH 069/146] Update dotnet.yml --- .github/workflows/dotnet.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 2efb196..34f3d57 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -36,7 +36,6 @@ jobs: Expand-Archive -Path References.zip -DestinationPath ${{ env.SL_REFERENCES }} - name: Build - env: shell: pwsh run: | ./build.ps1 From 0e8e896cd42709a4013e70cc10d18698882e6000 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:28:02 -0300 Subject: [PATCH 070/146] gwa gwa --- .github/workflows/dotnet.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 34f3d57..fcd84cc 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -21,11 +21,12 @@ jobs: steps: - - name: Setup .NET Core SDK - uses: actions/setup-dotnet@v1.7.2 - + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 3.1.101 - name: Setup Nuget - uses: iRebbok/setup-nuget@master + uses: nuget/setup-nuget@v1 - uses: actions/checkout@v2.3.4 @@ -33,6 +34,7 @@ jobs: shell: pwsh run: | Invoke-WebRequest -Uri ${{ env.EXILED_REFERENCES_URL }} -OutFile ${{ github.workspace }}/References.zip + Invoke-WebRequest -Uri "https://github.com/CedModV2/NWAPIPermissionSystem/releases/download/0.0.3/NWAPIPermissionSystem.dll" -OutFile ${{ env.SL_REFERENCES }}/NWAPIPermissionSystem.dll Expand-Archive -Path References.zip -DestinationPath ${{ env.SL_REFERENCES }} - name: Build From ae994169725fdecfb895b7190b1a9d07c144eb9b Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:35:07 -0300 Subject: [PATCH 071/146] im bad in workflow --- .github/workflows/dotnet.yml | 2 +- UltimateAFK/Config.cs | 4 +--- UltimateAFK/Handlers/MainHandler.cs | 1 - UltimateAFK/UltimateAFK.cs | 4 +--- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index fcd84cc..7d968ed 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -34,8 +34,8 @@ jobs: shell: pwsh run: | Invoke-WebRequest -Uri ${{ env.EXILED_REFERENCES_URL }} -OutFile ${{ github.workspace }}/References.zip - Invoke-WebRequest -Uri "https://github.com/CedModV2/NWAPIPermissionSystem/releases/download/0.0.3/NWAPIPermissionSystem.dll" -OutFile ${{ env.SL_REFERENCES }}/NWAPIPermissionSystem.dll Expand-Archive -Path References.zip -DestinationPath ${{ env.SL_REFERENCES }} + Invoke-WebRequest -Uri "https://github.com/CedModV2/NWAPIPermissionSystem/releases/download/0.0.3/NWAPIPermissionSystem.dll" -OutFile ${{ env.SL_REFERENCES }}/NWAPIPermissionSystem.dll - name: Build shell: pwsh diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 3e18032..53cf958 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.ComponentModel; -using PlayerRoles; +using System.ComponentModel; namespace UltimateAFK { diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index c989037..8c20ebf 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -7,7 +7,6 @@ using PluginAPI.Enums; using UltimateAFK.Handlers.Components; using UltimateAFK.Resources; -using UnityEngine; namespace UltimateAFK.Handlers { diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 6b0c778..16a5f0c 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using PluginAPI.Core.Attributes; +using PluginAPI.Core.Attributes; namespace UltimateAFK { From addad3a49a0c54ea1933eba053df3432c92b2666 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:35:52 -0300 Subject: [PATCH 072/146] new version --- UltimateAFK/UltimateAFK.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 16a5f0c..1f221f3 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -11,7 +11,7 @@ public class UltimateAFK [PluginConfig] public Config Config; - [PluginEntryPoint("UltimateAFK", "1.0.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.0.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From b4fb222bfa04b8c194c21d6cdce96dc7d66a9ec8 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:39:25 -0300 Subject: [PATCH 073/146] Fix references --- UltimateAFK/UltimateAFK.csproj | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index fdb3d84..e43d4d5 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -32,15 +32,14 @@ - - - - - - - - - - + + + + + + + + + \ No newline at end of file From 2f21a17266c38d69cd20f5b014b4a522b878d5a5 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:41:20 -0300 Subject: [PATCH 074/146] 6.0.0 --- Cerberus.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cerberus.props b/Cerberus.props index 384563c..b957fe4 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 5.0.0 + 6.0.0 false From 70835c06c35ceab7741a4a6980d81b29e438ab18 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:43:29 -0300 Subject: [PATCH 075/146] Update dotnet.yml --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 7d968ed..05bccf4 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -24,7 +24,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.1.101 + dotnet-version: 6.0.x - name: Setup Nuget uses: nuget/setup-nuget@v1 From 6a3997f4d788b17612ea4a0e1bf42bfaa24ea393 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 3 Jan 2023 22:55:24 -0300 Subject: [PATCH 076/146] No more spaming console --- UltimateAFK/Handlers/Components/AFKComponent.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 615a969..0d05200 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -287,11 +287,6 @@ private Player GetReplacement() private bool Continue(Player ply) { - var value = ply.IsAlive && Round.IsRoundStarted && Player.GetPlayers().Count >= UltimateAFK.Singleton.Config.MinPlayers && - (Owner.Role != RoleTypeId.Tutorial || !UltimateAFK.Singleton.Config.IgnoreTut); - - Log.Info($"Continue value is {value}"); - return ply.IsAlive && Round.IsRoundStarted && Player.Count >= UltimateAFK.Singleton.Config.MinPlayers && (Owner.Role != RoleTypeId.Tutorial || !UltimateAFK.Singleton.Config.IgnoreTut); } From bd019f0c286ab50c7e6387fd05e001a671f0d87b Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 4 Jan 2023 05:49:49 -0300 Subject: [PATCH 077/146] Fixing a loot of shit in the code * Now player replacement works :) * 9 hours programin... please send help --- .../Handlers/Components/AFKComponent.cs | 133 +++++++++++------- UltimateAFK/Handlers/MainHandler.cs | 48 ++++--- UltimateAFK/Resources/Extensions.cs | 51 ++++++- UltimateAFK/UltimateAFK.cs | 14 +- 4 files changed, 178 insertions(+), 68 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 0d05200..fd15e30 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -7,6 +7,7 @@ using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; using PlayerRoles.PlayableScps.Scp096; +using PluginAPI.Core.Items; using UltimateAFK.Resources; using UnityEngine; @@ -87,7 +88,7 @@ private IEnumerator CheckAfkPerSecond() { for (;;) { - yield return Timing.WaitForSeconds(1.2f); + yield return Timing.WaitForSeconds(1.3f); Log.Debug("Calling CheckAFK", UltimateAFK.Singleton.Config.DebugMode && UltimateAFK.Singleton.Config.SpamLogs); @@ -130,13 +131,14 @@ private void CheckAfk() if (graceNumb > 0) { // The player is in grace time, so let's warn him that he has been afk for too long. - Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 1, + Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, shouldClearPrevious: true); } else { Log.Info($"{Owner.Nickname} ({Owner.UserId}) Detected as AFK"); + _afkTime = 0f; Replace(Owner, Owner.Role); } } @@ -153,8 +155,7 @@ public void Replace(Player player, RoleTypeId role) if (replacement == null) { - Log.Debug("Unable to find replacement player, moving to spectator...", - UltimateAFK.Singleton.Config.DebugMode); + Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); player.SetRole(RoleTypeId.Spectator); @@ -178,32 +179,13 @@ public void Replace(Player player, RoleTypeId role) return; } - Log.Debug($"Replacement Player found\nNickname: {replacement.Nickname}\nUserID: {replacement.UserId}", - UltimateAFK.Singleton.Config.DebugMode); + Log.Debug($"Replacement Player found Nickname: {replacement.Nickname} UserID: {replacement.UserId}", UltimateAFK.Singleton.Config.DebugMode); Scp079Role scp079Role; - if (role == RoleTypeId.Scp079 && - (scp079Role = player.ReferenceHub.roleManager.CurrentRole as Scp079Role) != null && - scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager scp079TierManager) && - scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager scp079AuxManager)) + if (role == RoleTypeId.Scp079) { - MainHandler.ReplacingPlayers.Add(replacement, new AFKData - { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Role = scp079Role, - Energy = scp079AuxManager.CurrentAux, - Experience = scp079TierManager.TotalExp, - } - }); - - player.ClearInventory(); + AddData(player, replacement, true); + player.AfkClearInventory(); player.SetRole(RoleTypeId.Spectator); if (UltimateAFK.Singleton.Config.AfkCount != -1) @@ -223,30 +205,14 @@ public void Replace(Player player, RoleTypeId role) player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - - replacement.SetRole(player.Role); + replacement.SetRole(role); } else { - MainHandler.ReplacingPlayers.Add(replacement, new AFKData - { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Role = null, - Energy = 0f, - Experience = 0, - } - }); - - player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); + AddData(player, replacement, false); + player.SetRole(RoleTypeId.Scp0492); + if (UltimateAFK.Singleton.Config.AfkCount != -1) { AfkCount++; @@ -261,12 +227,79 @@ public void Replace(Player player, RoleTypeId role) return; } } - - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + player.SetRole(RoleTypeId.Spectator); + replacement.SetRole(role); + } + } - replacement.SetRole(player.Role); + /// + /// Add player data to ReplacingPlayers dictionary. + /// + private void AddData(Player player, Player replacement, bool is079 = false) + { + if (is079) + { + Scp079Role scp079Role; + if ((scp079Role = player.ReferenceHub.roleManager.CurrentRole as Scp079Role) != null && + scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager scp079TierManager) && + scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager scp079AuxManager)) + { + MainHandler.ReplacingPlayers.Add(replacement, new AFKData + { + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data + { + Role = scp079Role, + Energy = scp079AuxManager.CurrentAux, + Experience = scp079TierManager.TotalExp, + } + }); + } + return; } + + var ammo = GetAmmo(player); + MainHandler.ReplacingPlayers.Add(replacement, new AFKData + { + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = ammo, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data + { + Role = null, + Energy = 0f, + Experience = 0 + } + }); + + } + + + /// + /// NORTHWOOD FIX AMMO INVENTORY NOW! + /// + /// + private Dictionary GetAmmo(Player player) + { + var result = new Dictionary(); + + foreach (var ammo in player.ReferenceHub.inventory.UserInventory.ReserveAmmo) + { + result.Add(ammo.Key, ammo.Value); + } + + return result; } /// diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 8c20ebf..f6746f4 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; +using PlayerStatsSystem; using PluginAPI.Core.Attributes; using PluginAPI.Enums; using UltimateAFK.Handlers.Components; using UltimateAFK.Resources; +using UnityEngine; namespace UltimateAFK.Handlers { @@ -25,7 +27,7 @@ public MainHandler(UltimateAFK plugin) /// /// A dictionary where replacement players are stored to give them the stats and items of the original player. /// - public static Dictionary ReplacingPlayers = new Dictionary(); + public static Dictionary ReplacingPlayers = new(); [PluginEvent(ServerEventType.PlayerJoined)] private void OnPlayerJoin(Player player) @@ -58,7 +60,7 @@ private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId ne Log.Debug($"Detecting player {player.Nickname} ({player.UserId}) who replaced a player who was afk", UltimateAFK.Singleton.Config.DebugMode); - Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => RepleaceItemsAndStats(player, data, newRole)); + Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => ReplaceItemsAndStats(player, data, newRole)); } catch (System.Exception e) { @@ -72,13 +74,22 @@ private void OnRoundStarted() ReplacingPlayers.Clear(); } + [PluginEvent(ServerEventType.PlayerDeath)] + private void OnPlayerDeath(Player player, Player attacker, DamageHandlerBase damageHandler) + { + if (player != null && ReplacingPlayers.TryGetValue(player, out var data)) + { + ReplacingPlayers.Remove(player); + } + } + /// /// Performs the change of stats and items to the replacement player /// - private void RepleaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) + private void ReplaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) { - Log.Debug($"Replacing {ply.Nickname} ({ply.UserId}) data", Plugin.Config.DebugMode); - + Log.Debug($"Replacing player is {ply.Nickname} ({ply.UserId})", Plugin.Config.DebugMode); + Scp079Role scp079Role; if (newrole == RoleTypeId.Scp079 && data.SCP079.Role != null && (scp079Role = ply.ReferenceHub.roleManager.CurrentRole as Scp079Role) != null @@ -87,8 +98,8 @@ private void RepleaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) { Log.Debug("The new role is a SCP079, transferring energy and experience.", UltimateAFK.Singleton.Config.DebugMode); - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgReplace, 16, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgReplace, "white"); + ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); + ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); scp079TierManager.TotalExp = data.SCP079.Experience; scp079AuxManager.CurrentAux = data.SCP079.Energy; @@ -97,19 +108,24 @@ private void RepleaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) } Log.Debug($"Changing player {ply.Nickname} ({ply.UserId}) position and HP", Plugin.Config.DebugMode); - - ply.Position = data.Position; - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgReplace, 16, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgReplace, "white"); - ply.Health = data.Health; - + ply.AfkClearInventory(true); + Log.Debug($"Adding Ammo to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); + ply.SendAmmo(data.Ammo); - Log.Debug($"Adding items to {ply.Nickname} ({ply.UserId})"); - ply.SendItems(data.Items); + Timing.CallDelayed(0.3f, () => + { + ply.Position = data.Position; + ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); + ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); + ply.Health = data.Health; + Log.Debug($"Adding items to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); + ply.SendItems(data.Items); + ply.ApplyAttachments(); + }); + Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Singleton.Config.DebugMode); - ReplacingPlayers.Remove(ply); } } diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs index 3f02637..8496d3f 100644 --- a/UltimateAFK/Resources/Extensions.cs +++ b/UltimateAFK/Resources/Extensions.cs @@ -1,6 +1,11 @@ using System.Collections.Generic; +using System.Linq; +using InventorySystem; using InventorySystem.Items; +using InventorySystem.Items.Firearms; +using InventorySystem.Items.Firearms.Attachments; using PluginAPI.Core; +using UnityEngine; namespace UltimateAFK.Resources { @@ -16,10 +21,17 @@ public static void SendItems(this Player player, List types) public static void SendAmmo(this Player ply, Dictionary ammo) { + Log.Info($"Cosa inicio | ammo is empty ? {ammo.IsEmpty()}"); + foreach (var ammoItem in ammo) { - ply.AddAmmo(ammoItem.Key, ammoItem.Value); + Log.Info($"Che pasando la municion {ammoItem.Key} que son {ammoItem.Value}"); + + ply.SetAmmo(ammoItem.Key, ammoItem.Value); } + + Log.Info("Cosa final"); + } public static List GetItems(this Player ply) @@ -29,10 +41,47 @@ public static List GetItems(this Player ply) foreach(var i in items.Values) { + if(i.ItemTypeId is ItemType.Ammo9x19 or ItemType.Ammo12gauge or ItemType.Ammo44cal or ItemType.Ammo556x45 or ItemType.Ammo762x39) continue; + returnitems.Add(i.ItemTypeId); } return returnitems; } + + public static void AfkClearInventory(this Player ply, bool clearammo = false) + { + if (clearammo) + { + ply.ReferenceHub.inventory.UserInventory.ReserveAmmo.Clear(); + ply.ReferenceHub.inventory.SendAmmoNextFrame = true; + } + + while (ply.ReferenceHub.inventory.UserInventory.Items.Count > 0) + { + ply.ReferenceHub.inventory.ServerRemoveItem(ply.ReferenceHub.inventory.UserInventory.Items.ElementAt(0).Key, null); + } + + + } + + public static void ApplyAttachments(this Player ply) + { + var item = ply.Items.Where(i => i is Firearm); + + foreach (var fire in item) + { + if (fire is Firearm fireArm) + { + if (AttachmentsServerHandler.PlayerPreferences.TryGetValue(ply.ReferenceHub, out var value) && value.TryGetValue(fireArm.ItemTypeId, out var value2)) + fireArm.ApplyAttachmentsCode(value2, reValidate: true); + var firearmStatusFlags = FirearmStatusFlags.MagazineInserted; + if (fireArm.HasAdvantageFlag(AttachmentDescriptiveAdvantages.Flashlight)) + firearmStatusFlags |= FirearmStatusFlags.FlashlightEnabled; + + fireArm.Status = new FirearmStatus(fireArm.AmmoManagerModule.MaxAmmo, firearmStatusFlags, fireArm.GetCurrentAttachmentsCode()); + } + } + } } } \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 1f221f3..e5be9c8 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,4 +1,8 @@ -using PluginAPI.Core.Attributes; +using System.Collections.Generic; +using PluginAPI.Core; +using PluginAPI.Core.Attributes; +using UltimateAFK.Handlers; +using UltimateAFK.Resources; namespace UltimateAFK { @@ -16,6 +20,14 @@ void OnEnabled() { Singleton = this; PluginAPI.Events.EventManager.RegisterEvents(this, new Handlers.MainHandler(Singleton)); + //PluginAPI.Events.EventManager.RegisterEvents(this); + } + + [PluginUnload] + void OnDisable() + { + MainHandler.ReplacingPlayers.Clear(); + MainHandler.ReplacingPlayers = null; } } } \ No newline at end of file From 7132c561d8ab77364afab634e5c6f5870c066039 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 4 Jan 2023 05:54:54 -0300 Subject: [PATCH 078/146] Update UltimateAFK.csproj --- UltimateAFK/UltimateAFK.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index e43d4d5..3fd6b74 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -5,7 +5,7 @@ Library UltimateAFK true - 3.0.0 + From 6028e10169b3f634e0bf1a21d80921c12feaec9b Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 4 Jan 2023 06:07:16 -0300 Subject: [PATCH 079/146] A small warning just in case --- UltimateAFK/UltimateAFK.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index e5be9c8..1455449 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; +using GameCore; using PluginAPI.Core; using PluginAPI.Core.Attributes; using UltimateAFK.Handlers; using UltimateAFK.Resources; +using Log = PluginAPI.Core.Log; namespace UltimateAFK { @@ -20,6 +22,10 @@ void OnEnabled() { Singleton = this; PluginAPI.Events.EventManager.RegisterEvents(this, new Handlers.MainHandler(Singleton)); + if (ConfigFile.ServerConfig.GetInt("afk_time") > 0) + { + Log.Warning($"You have enabled the AFK detector of the base game, please disable it by setting &6afk_time = 0&r in &4config_gameplay.txt&r"); + } //PluginAPI.Events.EventManager.RegisterEvents(this); } From 69654344757b08a29b361d96e16995c8b116cc95 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 4 Jan 2023 06:10:52 -0300 Subject: [PATCH 080/146] Update AFKComponent.cs --- UltimateAFK/Handlers/Components/AFKComponent.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index fd15e30..a4b529c 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -180,8 +180,7 @@ public void Replace(Player player, RoleTypeId role) } Log.Debug($"Replacement Player found Nickname: {replacement.Nickname} UserID: {replacement.UserId}", UltimateAFK.Singleton.Config.DebugMode); - - Scp079Role scp079Role; + if (role == RoleTypeId.Scp079) { AddData(player, replacement, true); From 5e16127dd9202cbcd517cbfd26a91ab03f485062 Mon Sep 17 00:00:00 2001 From: SrLicht <36207738+SrLicht@users.noreply.github.com> Date: Wed, 4 Jan 2023 06:33:17 -0300 Subject: [PATCH 081/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78b09da..f3c8f5b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ | ![Github All Releases](https://img.shields.io/github/downloads/SrLicht/Ultimate-AFK/total.svg) | Releases # Ultimate-AFK - +This plugin allows AFK players to be replaced by players who are in spectator, they can also be kicked from the server after a certain number of being detected as AFK (configurable). # Features - Detects AFK Players via in-game movement, camera movement From aa109c83bde061d8a155339fe1a26ea7b91565f8 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 4 Jan 2023 15:00:36 -0300 Subject: [PATCH 082/146] Update Extensions.cs --- UltimateAFK/Resources/Extensions.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs index 8496d3f..abc3ee9 100644 --- a/UltimateAFK/Resources/Extensions.cs +++ b/UltimateAFK/Resources/Extensions.cs @@ -21,17 +21,10 @@ public static void SendItems(this Player player, List types) public static void SendAmmo(this Player ply, Dictionary ammo) { - Log.Info($"Cosa inicio | ammo is empty ? {ammo.IsEmpty()}"); - foreach (var ammoItem in ammo) { - Log.Info($"Che pasando la municion {ammoItem.Key} que son {ammoItem.Value}"); - ply.SetAmmo(ammoItem.Key, ammoItem.Value); } - - Log.Info("Cosa final"); - } public static List GetItems(this Player ply) From 6ea1abedab472fffc02f7dd32d4499a664c8afa8 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 4 Jan 2023 18:19:41 -0300 Subject: [PATCH 083/146] This should be enough Fix #3 Resolves #4 * There is now a blacklist of roles, where any RoleType that is in that list will not be replaced by another player. --- UltimateAFK/Config.cs | 7 ++- .../Handlers/Components/AFKComponent.cs | 62 +++++++++++++++++-- UltimateAFK/Handlers/MainHandler.cs | 12 ++-- 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 53cf958..921175f 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -1,4 +1,6 @@ -using System.ComponentModel; +using System.Collections.Generic; +using System.ComponentModel; +using PlayerRoles; namespace UltimateAFK { @@ -22,6 +24,9 @@ public class Config [Description("Tutorials should be ignored ?")] public bool IgnoreTut { get; set; } = true; + [Description("RoleTypes on this list will not be replaced by other players")] + public List RoleTypeBlacklist { get; set; } = new List() { RoleTypeId.Scp0492 }; + [Description("The time it takes for a player to stand still before he is detected as AFK")] public int AfkTime { get; set; } = 80; diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index a4b529c..f85d135 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -108,7 +108,9 @@ private void CheckAfk() if (!Continue(Owner)) return; + var position = Owner.Position; + // Yes.. Player.Camera does not change if you are playing a SCP that moves with the cameras :)))))))) var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Position; var cameraRotation = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp0792 ? new Quaternion(scp0792.CurrentCamera.HorizontalRotation, scp0792.CurrentCamera.VerticalRotation, scp0792.CurrentCamera.RollRotation, 0f ) : Owner.Camera.rotation; @@ -120,6 +122,7 @@ private void CheckAfk() _ownerPosition = position; _afkTime = 0f; } + // The player is not moving and is not SCP-096 with his TryToNotCry ability. else if (!(Owner.Role == RoleTypeId.Scp096 && (Owner.ReferenceHub.roleManager.CurrentRole as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry))) { Log.Debug($"{Owner.Nickname} is in not moving, AFKTime: {_afkTime}", UltimateAFK.Singleton.Config.DebugMode); @@ -151,8 +154,35 @@ private void CheckAfk() /// This replacement happens when the player is disconnected from the server ? public void Replace(Player player, RoleTypeId role) { + // Check if role is blacklisted + if (UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(role)) + { + Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); + + player.AfkClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + if (UltimateAFK.Singleton.Config.AfkCount != -1) + { + AfkCount++; + + if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) + { + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); + player.Kick(UltimateAFK.Singleton.Config.MsgKick); + return; + } + } + + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + return; + } + + // Get player replacement Player replacement = GetReplacement(); + // If replacement is null if (replacement == null) { Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); @@ -179,18 +209,23 @@ public void Replace(Player player, RoleTypeId role) return; } + // is not Log.Debug($"Replacement Player found Nickname: {replacement.Nickname} UserID: {replacement.UserId}", UltimateAFK.Singleton.Config.DebugMode); - + + // Check if role is SCP-079 to be able to pass the level and energy if (role == RoleTypeId.Scp079) { + //Adds the replacement player to the dictionary with all the necessary information AddData(player, replacement, true); - player.AfkClearInventory(); + + // Self-explanatory player.SetRole(RoleTypeId.Spectator); - + if (UltimateAFK.Singleton.Config.AfkCount != -1) { AfkCount++; + // Check if the player should be removed from the server for being too many times afk if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) { player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); @@ -202,20 +237,26 @@ public void Replace(Player player, RoleTypeId role) } } + //Send player a broadcast for being too long afk player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + + // Sends replacement to the role that had the afk replacement.SetRole(role); } else { + // Adds the replacement player to the dictionary with all the necessary information AddData(player, replacement, false); + // If you are wondering why I change it to SCP0492 and then to spectator, it is because when I change it to 0492 I clean the inventory. player.SetRole(RoleTypeId.Scp0492); if (UltimateAFK.Singleton.Config.AfkCount != -1) { AfkCount++; - + + // Check if the player should be removed from the server for being too many times afk if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) { player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); @@ -226,10 +267,12 @@ public void Replace(Player player, RoleTypeId role) return; } } - + //Send player a broadcast for being too long afk player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + // Sends player to spectator player.SetRole(RoleTypeId.Spectator); + // Sends replacement to the role that had the afk replacement.SetRole(role); } } @@ -242,6 +285,7 @@ private void AddData(Player player, Player replacement, bool is079 = false) if (is079) { Scp079Role scp079Role; + // This if is horrendous but I must get the subroutines from the player afk. if ((scp079Role = player.ReferenceHub.roleManager.CurrentRole as Scp079Role) != null && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager scp079TierManager) && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager scp079AuxManager)) @@ -251,6 +295,7 @@ private void AddData(Player player, Player replacement, bool is079 = false) NickName = player.Nickname, Position = player.Position, Role = player.Role, + // same has do ammo = null xd Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo, Health = player.Health, Items = player.GetItems(), @@ -265,6 +310,8 @@ private void AddData(Player player, Player replacement, bool is079 = false) return; } + // If I make Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo for some reason it gets buggy and ammo becomes null when changing the player to spectator. + // So I create a temporary dictionary stored in cache (ram) and then clean the information by deleting it from the ReplacingPlayers. var ammo = GetAmmo(player); MainHandler.ReplacingPlayers.Add(replacement, new AFKData { @@ -288,7 +335,6 @@ private void AddData(Player player, Player replacement, bool is079 = false) /// /// NORTHWOOD FIX AMMO INVENTORY NOW! /// - /// private Dictionary GetAmmo(Player player) { var result = new Dictionary(); @@ -317,6 +363,10 @@ private Player GetReplacement() return null; } + /// + /// Check if the player is alive, if the round has started, if the players on the server meet the requirements for check afk to work, if the player is tutorial and the configuration allows the tutorial to be detected as afk. + /// + /// True if all requirements are met private bool Continue(Player ply) { return ply.IsAlive && Round.IsRoundStarted && Player.Count >= UltimateAFK.Singleton.Config.MinPlayers && diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index f6746f4..f78ced0 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -77,6 +77,7 @@ private void OnRoundStarted() [PluginEvent(ServerEventType.PlayerDeath)] private void OnPlayerDeath(Player player, Player attacker, DamageHandlerBase damageHandler) { + // This works both in case the player is killed in the middle of the AFK detection, as well as if the player is disconnected, since when disconnected he goes through the PlayerDeath event. if (player != null && ReplacingPlayers.TryGetValue(player, out var data)) { ReplacingPlayers.Remove(player); @@ -106,22 +107,23 @@ private void ReplaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) ReplacingPlayers.Remove(ply); return; } - - Log.Debug($"Changing player {ply.Nickname} ({ply.UserId}) position and HP", Plugin.Config.DebugMode); - ply.AfkClearInventory(true); + ply.AfkClearInventory(true); Log.Debug($"Adding Ammo to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); - + // I add the ammunition first since it is the slowest thing to be done. ply.SendAmmo(data.Ammo); - + + // This call delayed is necessary Timing.CallDelayed(0.3f, () => { + Log.Debug($"Changing player {ply.Nickname} ({ply.UserId}) position and HP", Plugin.Config.DebugMode); ply.Position = data.Position; ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); ply.Health = data.Health; Log.Debug($"Adding items to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); ply.SendItems(data.Items); + // I apply the modifications of the replacement player not the afk, I could do it but I sincerely prefer this method. ply.ApplyAttachments(); }); From b63c37cfb5a00aef2b7f3962f2e100a3d6d22f70 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 4 Jan 2023 18:25:48 -0300 Subject: [PATCH 084/146] new version --- Cerberus.props | 2 +- UltimateAFK/UltimateAFK.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index b957fe4..35874f1 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.0.0 + 6.0.1 false diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 1455449..c7274ae 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; - [PluginEntryPoint("UltimateAFK", "6.0.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.0.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 99a4c0399fd3f91f15aa99e96268be87c805edca Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 4 Jan 2023 18:33:31 -0300 Subject: [PATCH 085/146] Update AFKComponent.cs --- UltimateAFK/Handlers/Components/AFKComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index f85d135..ff0d0f0 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -111,7 +111,7 @@ private void CheckAfk() var position = Owner.Position; // Yes.. Player.Camera does not change if you are playing a SCP that moves with the cameras :)))))))) - var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Position; + var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Camera.position; var cameraRotation = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp0792 ? new Quaternion(scp0792.CurrentCamera.HorizontalRotation, scp0792.CurrentCamera.VerticalRotation, scp0792.CurrentCamera.RollRotation, 0f ) : Owner.Camera.rotation; // Player is moving From 35d8c05c423b0850e76da314e72a1e0fc6fb9535 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Thu, 5 Jan 2023 01:42:43 -0300 Subject: [PATCH 086/146] Update AFKComponent.cs --- UltimateAFK/Handlers/Components/AFKComponent.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index ff0d0f0..d78212e 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -26,13 +26,12 @@ public class AfkComponent : MonoBehaviour // Position in the world private Vector3 _ownerPosition; - // Rotation of your head/camera + // Player camera position private Vector3 _cameraPosition; + // Player camera rotation private Quaternion _cameraRotation; - private float _graceTime = UltimateAFK.Singleton.Config.GraceTime; - // The time the player was afk private float _afkTime; @@ -62,7 +61,7 @@ private void Awake() } /// - /// Destroys the component and clears the variables and events recorded in Exiled. + /// Destroys the component and clears the variables /// public void Destroy() { From 02358a9832c4af1c41f4189d2046686da7339706 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 10 Jan 2023 17:12:10 -0300 Subject: [PATCH 087/146] Fixing ClearInventory * I dont test this version --- Cerberus.props | 2 +- .../Handlers/Components/AFKComponent.cs | 30 +++++++++++-------- UltimateAFK/Handlers/MainHandler.cs | 2 +- UltimateAFK/Resources/Extensions.cs | 16 ++++++++++ UltimateAFK/UltimateAFK.cs | 2 +- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index 35874f1..bff649a 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.0.1 + 6.0.2 false diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index d78212e..a28f2a4 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -157,8 +157,7 @@ public void Replace(Player player, RoleTypeId role) if (UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(role)) { Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); - - player.AfkClearInventory(); + player.ClearPlayerInventory(); player.SetRole(RoleTypeId.Spectator); if (UltimateAFK.Singleton.Config.AfkCount != -1) @@ -248,9 +247,6 @@ public void Replace(Player player, RoleTypeId role) // Adds the replacement player to the dictionary with all the necessary information AddData(player, replacement, false); - // If you are wondering why I change it to SCP0492 and then to spectator, it is because when I change it to 0492 I clean the inventory. - player.SetRole(RoleTypeId.Scp0492); - if (UltimateAFK.Singleton.Config.AfkCount != -1) { AfkCount++; @@ -266,13 +262,23 @@ public void Replace(Player player, RoleTypeId role) return; } } - //Send player a broadcast for being too long afk - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - // Sends player to spectator - player.SetRole(RoleTypeId.Spectator); - // Sends replacement to the role that had the afk - replacement.SetRole(role); + + try + { + // Clear player inventory + player.ClearPlayerInventory(); + //Send player a broadcast for being too long afk + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + // Sends player to spectator + player.SetRole(RoleTypeId.Spectator); + // Sends replacement to the role that had the afk + replacement.SetRole(role); + } + catch (Exception e) + { + Log.Error($"Error on {nameof(Replace)}: {e} -- {e.StackTrace} || player is null? {player is null}", "Ultimate-AFK"); + } } } diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index f78ced0..add082c 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -108,7 +108,7 @@ private void ReplaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) return; } - ply.AfkClearInventory(true); + ply.ClearPlayerInventory(); Log.Debug($"Adding Ammo to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); // I add the ammunition first since it is the slowest thing to be done. ply.SendAmmo(data.Ammo); diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs index abc3ee9..52ed2e9 100644 --- a/UltimateAFK/Resources/Extensions.cs +++ b/UltimateAFK/Resources/Extensions.cs @@ -58,6 +58,22 @@ public static void AfkClearInventory(this Player ply, bool clearammo = false) } + public static void ClearPlayerInventory(this Player ply, bool clearAmmo = true, bool clearItems = true) + { + if (clearAmmo) + { + ply.ReferenceHub.inventory.UserInventory.ReserveAmmo.Clear(); + } + if (clearItems) + { + var inventory = ply.ReferenceHub.inventory.UserInventory; + while (inventory.Items.Count > 0) + { + ply.ReferenceHub.inventory.ServerRemoveItem(inventory.Items.ElementAt(0).Key, null); + } + } + } + public static void ApplyAttachments(this Player ply) { var item = ply.Items.Where(i => i is Firearm); diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index c7274ae..44c62c6 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; - [PluginEntryPoint("UltimateAFK", "6.0.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.0.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From ae31a2cb4804424036e16227c0d7846cb94eb7ca Mon Sep 17 00:00:00 2001 From: SrLicht Date: Fri, 13 Jan 2023 16:33:11 -0300 Subject: [PATCH 088/146] NW release new version of NWAPI NOW! --- .../Handlers/Components/AFKComponent.cs | 13 +++-- UltimateAFK/Handlers/MainHandler.cs | 6 +-- UltimateAFK/Resources/Extensions.cs | 51 +++++++++++++++---- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index a28f2a4..c12df23 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -107,6 +107,13 @@ private void CheckAfk() if (!Continue(Owner)) return; + if (Owner == null) + { + Log.Error($"{nameof(CheckAfk)}: Player is null"); + Destroy(); + return; + } + var position = Owner.Position; // Yes.. Player.Camera does not change if you are playing a SCP that moves with the cameras :)))))))) @@ -133,7 +140,7 @@ private void CheckAfk() if (graceNumb > 0) { // The player is in grace time, so let's warn him that he has been afk for too long. - Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, + Owner.SendBroadcastToPlayer(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, shouldClearPrevious: true); } else @@ -236,7 +243,7 @@ public void Replace(Player player, RoleTypeId role) } //Send player a broadcast for being too long afk - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendBroadcastToPlayer(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); // Sends replacement to the role that had the afk @@ -268,7 +275,7 @@ public void Replace(Player player, RoleTypeId role) // Clear player inventory player.ClearPlayerInventory(); //Send player a broadcast for being too long afk - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); + player.SendBroadcastToPlayer(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); // Sends player to spectator player.SetRole(RoleTypeId.Spectator); diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index add082c..da250f5 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -99,7 +99,7 @@ private void ReplaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) { Log.Debug("The new role is a SCP079, transferring energy and experience.", UltimateAFK.Singleton.Config.DebugMode); - ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); + ply.SendBroadcastToPlayer(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); scp079TierManager.TotalExp = data.SCP079.Experience; @@ -118,12 +118,12 @@ private void ReplaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) { Log.Debug($"Changing player {ply.Nickname} ({ply.UserId}) position and HP", Plugin.Config.DebugMode); ply.Position = data.Position; - ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); + ply.SendBroadcastToPlayer(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); ply.Health = data.Health; Log.Debug($"Adding items to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); ply.SendItems(data.Items); - // I apply the modifications of the replacement player not the afk, I could do it but I sincerely prefer this method. + // I apply the modifications of the replacement player not of the afk, I could do it but I sincerely prefer this method. ply.ApplyAttachments(); }); diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs index 52ed2e9..5bff2aa 100644 --- a/UltimateAFK/Resources/Extensions.cs +++ b/UltimateAFK/Resources/Extensions.cs @@ -42,22 +42,33 @@ public static List GetItems(this Player ply) return returnitems; } - public static void AfkClearInventory(this Player ply, bool clearammo = false) + /// + /// Until NW Fix SendBroadcast. + /// + /// + /// + /// + /// + /// + public static void SendBroadcastToPlayer(this Player ply, string message, ushort duration, + Broadcast.BroadcastFlags type = Broadcast.BroadcastFlags.Normal, bool shouldClearPrevious = false) { - if (clearammo) - { - ply.ReferenceHub.inventory.UserInventory.ReserveAmmo.Clear(); - ply.ReferenceHub.inventory.SendAmmoNextFrame = true; - } + if (shouldClearPrevious) ClearBroadcasts(ply); - while (ply.ReferenceHub.inventory.UserInventory.Items.Count > 0) - { - ply.ReferenceHub.inventory.ServerRemoveItem(ply.ReferenceHub.inventory.UserInventory.Items.ElementAt(0).Key, null); - } - + Broadcast.Singleton.TargetAddElement(ply.Connection, message, duration, type); + } + private static void ClearBroadcasts(Player ply) + { + Broadcast.Singleton.TargetClearElements(ply.Connection); } + /// + /// Until NW fix clear inventory. + /// + /// + /// + /// public static void ClearPlayerInventory(this Player ply, bool clearAmmo = true, bool clearItems = true) { if (clearAmmo) @@ -74,6 +85,10 @@ public static void ClearPlayerInventory(this Player ply, bool clearAmmo = true, } } + /// + /// Applies player attachments. + /// + /// public static void ApplyAttachments(this Player ply) { var item = ply.Items.Where(i => i is Firearm); @@ -92,5 +107,19 @@ public static void ApplyAttachments(this Player ply) } } } + + public static void ReloadAllWeapons(this Player ply) + { + var item = ply.Items.Where(i => i is Firearm); + + foreach (var weapon in item) + { + if (weapon is Firearm firearm) + { + firearm.Status = new FirearmStatus(firearm.AmmoManagerModule.MaxAmmo, firearm.Status.Flags, + firearm.GetCurrentAttachmentsCode()); + } + } + } } } \ No newline at end of file From d5e09ea17e87529b058a146100e847ab32b120c0 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 05:45:12 -0300 Subject: [PATCH 089/146] Ignore Dummyts --- UltimateAFK/Handlers/Components/AFKComponent.cs | 2 +- UltimateAFK/Handlers/MainHandler.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index c12df23..8691a21 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -366,7 +366,7 @@ private Player GetReplacement() { foreach (var player in Player.GetPlayers()) { - if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer) + if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server")) continue; return player; diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index da250f5..55d268c 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -32,7 +32,7 @@ public MainHandler(UltimateAFK plugin) [PluginEvent(ServerEventType.PlayerJoined)] private void OnPlayerJoin(Player player) { - if(!Plugin.Config.IsEnabled) return; + if(!Plugin.Config.IsEnabled || player.UserId.Contains("@server")) return; if (player.GameObject.TryGetComponent(out var com)) { From f22fd16c40e049ff2d99ebe15fbf6ef2c0e80f2a Mon Sep 17 00:00:00 2001 From: MrAfitol Date: Wed, 18 Jan 2023 12:26:36 +0200 Subject: [PATCH 090/146] 6.0.3 Updated PluginAPI to 12.0.0 Fixed bug when role_type_blacklist parameter is empty --- Cerberus.props | 2 +- .../Handlers/Components/AFKComponent.cs | 35 ++++++++++--------- UltimateAFK/UltimateAFK.cs | 2 +- UltimateAFK/UltimateAFK.csproj | 4 +-- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index bff649a..0870d6d 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.0.2 + 6.0.3 false diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 8691a21..eea4c0c 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -2,12 +2,10 @@ using PluginAPI.Core; using System; using System.Collections.Generic; -using System.Linq; using NWAPIPermissionSystem; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; using PlayerRoles.PlayableScps.Scp096; -using PluginAPI.Core.Items; using UltimateAFK.Resources; using UnityEngine; @@ -161,27 +159,30 @@ private void CheckAfk() public void Replace(Player player, RoleTypeId role) { // Check if role is blacklisted - if (UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(role)) + if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Count > 0) { - Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); - player.ClearPlayerInventory(); - player.SetRole(RoleTypeId.Spectator); - - if (UltimateAFK.Singleton.Config.AfkCount != -1) + if (UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(role)) { - AfkCount++; + Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); + player.ClearPlayerInventory(); + player.SetRole(RoleTypeId.Spectator); - if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) + if (UltimateAFK.Singleton.Config.AfkCount != -1) { - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - player.Kick(UltimateAFK.Singleton.Config.MsgKick); - return; + AfkCount++; + + if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) + { + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); + player.Kick(UltimateAFK.Singleton.Config.MsgKick); + return; + } } + + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + return; } - - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - return; } // Get player replacement diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 44c62c6..4b60b4b 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; - [PluginEntryPoint("UltimateAFK", "6.0.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.0.3", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 3fd6b74..5934c9e 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -25,8 +25,8 @@ - - + + From 1f24a70daca909740d1b84793a364968578263a6 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 07:56:17 -0300 Subject: [PATCH 091/146] Pi pi rework --- UltimateAFK/Config.cs | 2 +- .../Handlers/Components/AFKComponent.cs | 1 - .../Handlers/Components/NewAFKComponent.cs | 375 ++++++++++++++++++ UltimateAFK/UltimateAFK.csproj | 4 +- 4 files changed, 378 insertions(+), 4 deletions(-) create mode 100644 UltimateAFK/Handlers/Components/NewAFKComponent.cs diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 921175f..ba94b5c 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -33,7 +33,7 @@ public class Config [Description("After being detected as AFK a message will appear on his face and he will be given this time to move or he will be Kicked/Moved to spectator.")] public int GraceTime { get; set; } = 30; - [Description("The number of times a player must be moved to spectator for a player to be kicked from the server. Use -1 to disable it")] + [Description("The number of times a player must be moved to spectator for a player to be kicked from the server.")] public int AfkCount { get; set; } = -1; [Description("When the player is detected as AFK and is in grace period this message will appear on his face. {0} represents the seconds the player has to move or be moved to spectator.")] diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/AFKComponent.cs index 8691a21..ccaa925 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AFKComponent.cs @@ -342,7 +342,6 @@ private void AddData(Player player, Player replacement, bool is079 = false) }); } - /// /// NORTHWOOD FIX AMMO INVENTORY NOW! diff --git a/UltimateAFK/Handlers/Components/NewAFKComponent.cs b/UltimateAFK/Handlers/Components/NewAFKComponent.cs new file mode 100644 index 0000000..10e6cd2 --- /dev/null +++ b/UltimateAFK/Handlers/Components/NewAFKComponent.cs @@ -0,0 +1,375 @@ +using System; +using System.Collections.Generic; +using MEC; +using NWAPIPermissionSystem; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using PlayerRoles.PlayableScps.Scp096; +using PluginAPI.Core; +using UltimateAFK.Resources; +using UnityEngine; + +namespace UltimateAFK.Handlers.Components +{ + [RequireComponent(typeof(ReferenceHub))] + public class NewAFKComponent : MonoBehaviour + { + private void Awake() + { + if (Player.Get(gameObject) is not { } ply) + { + Log.Error($"{this} Error Getting Player"); + Destroy(this); + return; + } + + Owner = ply; + // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. + _checkHandle = Timing.RunCoroutine(Check().CancelWith(this).CancelWith(gameObject)); + Log.Debug($"Component full loaded Owner: {Owner.Nickname} ({Owner.UserId})", UltimateAFK.Singleton.Config.DebugMode); + } + + private void OnDestroy() + { + Log.Debug($"Calling OnDestroy", UltimateAFK.Singleton.Config.DebugMode); + + if (Owner is null) + Log.Debug("Owner was null at the time of destroying the component", UltimateAFK.Singleton.Config.DebugMode); + + Timing.KillCoroutines(_checkHandle); + } + + private IEnumerator Check() + { + for (;;) + { + yield return Timing.WaitForSeconds(1.3f); + + Log.Debug("Calling CheckAFK", UltimateAFK.Singleton.Config.DebugMode && UltimateAFK.Singleton.Config.SpamLogs); + try + { + CheckAfk(); + } + catch (Exception e) + { + Log.Error($"Error in {nameof(Check)}: &2{e.TargetSite}&r || {e.StackTrace}"); + } + } + } + + private void CheckAfk() + { + if(!Continue(Owner)) return; + + if (Owner == null) + { + Log.Error($"{nameof(CheckAfk)}: Player is null"); + Destroy(this); + return; + } + + var ownerPosition = Owner.Position; + var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Camera.position; + var cameraRotation = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp0792 ? new Quaternion(scp0792.CurrentCamera.HorizontalRotation, scp0792.CurrentCamera.VerticalRotation, scp0792.CurrentCamera.RollRotation, 0f ) : Owner.Camera.rotation; + + // Player is moving + if (cameraPosition != _cameraPosition || ownerPosition != _ownerPosition || _cameraRotation != cameraRotation) + { + _cameraPosition = cameraPosition; + _cameraRotation = cameraRotation; + _ownerPosition = ownerPosition; + _afkTime = 0f; + } + // The player is not moving and is not SCP-096 with his TryToNotCry ability. + else if (!(Owner.Role == RoleTypeId.Scp096 && (Owner.ReferenceHub.roleManager.CurrentRole as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry))) + { + Log.Debug($"{Owner.Nickname} is in not moving, AFKTime: {_afkTime}", UltimateAFK.Singleton.Config.DebugMode); + + if(_afkTime++ < UltimateAFK.Singleton.Config.AfkTime) return; + + var graceNumb = UltimateAFK.Singleton.Config.GraceTime - (_afkTime - UltimateAFK.Singleton.Config.AfkTime); + + if (graceNumb > 0) + { + // The player is in grace time, so let's warn him that he has been afk for too long. + Owner.SendBroadcastToPlayer(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, + shouldClearPrevious: true); + } + else + { + Log.Info($"{Owner.Nickname} ({Owner.UserId}) Detected as AFK"); + _afkTime = 0f; + Replace(Owner, Owner.Role); + } + } + + } + + #region API + public static Player Owner { get; private set; } + + public RoleTypeId OwnerRoleType { get; } = Owner.Role; + + public Vector3 OwnerLastPosition { get; } = _ownerPosition; + public int AfkTimes { get; set; } + + public bool IsKickEnabled { get; set; } = UltimateAFK.Singleton.Config.AfkCount > -1; + + #endregion + + #region Private variables + + // Position in the world + private static Vector3 _ownerPosition; + + // Player camera position + private Vector3 _cameraPosition; + + // Player camera rotation + private Quaternion _cameraRotation; + + // The time the player was afk + private float _afkTime; + + // Using a MEC Coroutine is more optimized than using Unity methods. + private CoroutineHandle _checkHandle; + #endregion + + #region Private Methods + + /// + /// Check if the player is alive, if the round has started, if the players on the server meet the requirements for check afk to work, if the player is tutorial and the configuration allows the tutorial to be detected as afk. + /// + /// True if all requirements are met + private bool Continue(Player ply) + { + return ply.IsAlive && Round.IsRoundStarted && Player.Count >= UltimateAFK.Singleton.Config.MinPlayers && + (Owner.Role != RoleTypeId.Tutorial || !UltimateAFK.Singleton.Config.IgnoreTut); + } + private void Replace(Player ply, RoleTypeId roleType) + { + try + { + // Check if role is blacklisted + if (UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) + { + Log.Debug($"player {ply.Nickname} ({ply.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); + + ply.ClearInventory(); + ply.SetRole(RoleTypeId.Spectator); + + if (UltimateAFK.Singleton.Config.AfkCount != -1) + { + AfkTimes++; + + if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) + { + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); + ply.Kick(UltimateAFK.Singleton.Config.MsgKick); + return; + } + } + + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + return; + } + + // Get player replacement + Player replacement = GetReplacement(); + + // If replacement is null + if (replacement is null) + { + Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); + + ply.ClearInventory(); + ply.SetRole(RoleTypeId.Spectator); + + if (UltimateAFK.Singleton.Config.AfkCount != -1) + { + AfkTimes++; + + if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) + { + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); + + ply.Kick(UltimateAFK.Singleton.Config.MsgKick); + + return; + } + } + + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + } + else + { + // if not + Log.Debug($"Replacement Player found Nickname: {replacement.Nickname} UserID: {replacement.UserId}", UltimateAFK.Singleton.Config.DebugMode); + + // Check if AFK role is SCP-079 + if (roleType is RoleTypeId.Scp079) + { + //Adds the replacement player to the dictionary with all the necessary information + AddData(ply, replacement, true); + + // Self-explanatory + ply.SetRole(RoleTypeId.Spectator); + + if (UltimateAFK.Singleton.Config.AfkCount != -1) + { + AfkTimes++; + + // Check if the player should be removed from the server for being too many times afk + if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) + { + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); + + ply.Kick(UltimateAFK.Singleton.Config.MsgKick); + + replacement.SetRole(roleType); + return; + } + } + + //Send player a broadcast for being too long afk + ply.SendBroadcastToPlayer(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + + // Sends replacement to the role that had the afk + replacement.SetRole(roleType); + } + else + { + // Adds the replacement player to the dictionary with all the necessary information + AddData(ply, replacement, false); + + if (UltimateAFK.Singleton.Config.AfkCount != -1) + { + AfkTimes++; + + // Check if the player should be removed from the server for being too many times afk + if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) + { + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); + + ply.Kick(UltimateAFK.Singleton.Config.MsgKick); + + replacement.SetRole(roleType); + return; + } + } + + // Clear player inventory + ply.ClearPlayerInventory(); + //Send player a broadcast for being too long afk + ply.SendBroadcastToPlayer(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + // Sends player to spectator + ply.SetRole(RoleTypeId.Spectator); + // Sends replacement to the role that had the afk + replacement.SetRole(roleType); + } + } + } + catch (Exception e) + { + Log.Error($"Error on {nameof(Replace)}: IsOwnerNull: {Owner is null} || {e.Data} -- {e.StackTrace}"); + } + } + + /// + /// Obtains a player who qualifies for replacement. + /// + private Player GetReplacement() + { + foreach (var player in Player.GetPlayers()) + { + if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server")) + continue; + + return player; + } + + return null; + } + + /// + /// Add player data to ReplacingPlayers dictionary. + /// + private void AddData(Player player, Player replacement, bool is079 = false) + { + try + { + if (is079) + { + if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetComponent(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + { + MainHandler.ReplacingPlayers.Add(replacement, new AFKData + { + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = null, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data + { + Role = scp079Role, + Energy = energyManager.CurrentAux, + Experience = tierManager.TotalExp, + } + }); + } + + return; + } + + // If I make Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo for some reason it gets buggy and ammo becomes null when changing the player to spectator. + // So I create a temporary dictionary stored in cache (ram) and then clean the information by deleting it from the ReplacingPlayers. + var ammo = GetAmmo(player); + MainHandler.ReplacingPlayers.Add(replacement, new AFKData + { + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = ammo, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data + { + Role = null, + Energy = 0f, + Experience = 0 + } + }); + } + catch (Exception e) + { + Log.Error($"Error on {nameof(AddData)}: {e.Data} -- {e.StackTrace}"); + } + } + + /// + /// Cache player's ammunition + /// + /// + /// + private Dictionary GetAmmo(Player player) + { + var result = new Dictionary(); + + foreach (var ammo in player.ReferenceHub.inventory.UserInventory.ReserveAmmo) + { + result.Add(ammo.Key, ammo.Value); + } + + return result; + } + + #endregion + + } +} \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 3fd6b74..5934c9e 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -25,8 +25,8 @@ - - + + From fae0a9933902f223403ff15e8ca55405db1d2938 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 08:00:30 -0300 Subject: [PATCH 092/146] MrAfitol fix NRE Thanks dude :) --- UltimateAFK/Handlers/Components/NewAFKComponent.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UltimateAFK/Handlers/Components/NewAFKComponent.cs b/UltimateAFK/Handlers/Components/NewAFKComponent.cs index 10e6cd2..bebea0d 100644 --- a/UltimateAFK/Handlers/Components/NewAFKComponent.cs +++ b/UltimateAFK/Handlers/Components/NewAFKComponent.cs @@ -146,12 +146,18 @@ private bool Continue(Player ply) return ply.IsAlive && Round.IsRoundStarted && Player.Count >= UltimateAFK.Singleton.Config.MinPlayers && (Owner.Role != RoleTypeId.Tutorial || !UltimateAFK.Singleton.Config.IgnoreTut); } + + /// + /// Perform player replacement. + /// + /// player to replace + /// of the player afk private void Replace(Player ply, RoleTypeId roleType) { try { // Check if role is blacklisted - if (UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) + if (!UltimateAFK.Singleton.Config.RoleTypeBlacklist.IsEmpty() && UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) { Log.Debug($"player {ply.Nickname} ({ply.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); From cd6de5e8767c8dbad9f1212449ee8bcbd1184174 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 08:06:31 -0300 Subject: [PATCH 093/146] Ups --- UltimateAFK/Config.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index ba94b5c..921175f 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -33,7 +33,7 @@ public class Config [Description("After being detected as AFK a message will appear on his face and he will be given this time to move or he will be Kicked/Moved to spectator.")] public int GraceTime { get; set; } = 30; - [Description("The number of times a player must be moved to spectator for a player to be kicked from the server.")] + [Description("The number of times a player must be moved to spectator for a player to be kicked from the server. Use -1 to disable it")] public int AfkCount { get; set; } = -1; [Description("When the player is detected as AFK and is in grace period this message will appear on his face. {0} represents the seconds the player has to move or be moved to spectator.")] From a00243a595c9fd3f9070d54dd178a8517aca564e Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 08:11:48 -0300 Subject: [PATCH 094/146] using bool IsKickEnabled --- UltimateAFK/Handlers/Components/NewAFKComponent.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/UltimateAFK/Handlers/Components/NewAFKComponent.cs b/UltimateAFK/Handlers/Components/NewAFKComponent.cs index bebea0d..8f35d38 100644 --- a/UltimateAFK/Handlers/Components/NewAFKComponent.cs +++ b/UltimateAFK/Handlers/Components/NewAFKComponent.cs @@ -22,6 +22,7 @@ private void Awake() Destroy(this); return; } + Owner = ply; // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. @@ -164,7 +165,7 @@ private void Replace(Player ply, RoleTypeId roleType) ply.ClearInventory(); ply.SetRole(RoleTypeId.Spectator); - if (UltimateAFK.Singleton.Config.AfkCount != -1) + if (IsKickEnabled) { AfkTimes++; @@ -192,7 +193,7 @@ private void Replace(Player ply, RoleTypeId roleType) ply.ClearInventory(); ply.SetRole(RoleTypeId.Spectator); - if (UltimateAFK.Singleton.Config.AfkCount != -1) + if (IsKickEnabled) { AfkTimes++; @@ -212,7 +213,7 @@ private void Replace(Player ply, RoleTypeId roleType) else { // if not - Log.Debug($"Replacement Player found Nickname: {replacement.Nickname} UserID: {replacement.UserId}", UltimateAFK.Singleton.Config.DebugMode); + Log.Debug($"Replacement Player found: {replacement.Nickname} ({replacement.UserId})", UltimateAFK.Singleton.Config.DebugMode); // Check if AFK role is SCP-079 if (roleType is RoleTypeId.Scp079) @@ -223,7 +224,7 @@ private void Replace(Player ply, RoleTypeId roleType) // Self-explanatory ply.SetRole(RoleTypeId.Spectator); - if (UltimateAFK.Singleton.Config.AfkCount != -1) + if (IsKickEnabled) { AfkTimes++; @@ -251,7 +252,7 @@ private void Replace(Player ply, RoleTypeId roleType) // Adds the replacement player to the dictionary with all the necessary information AddData(ply, replacement, false); - if (UltimateAFK.Singleton.Config.AfkCount != -1) + if (IsKickEnabled) { AfkTimes++; From 9b515019ece0d7583a811d963d7a99a5c5cadcf0 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 08:49:17 -0300 Subject: [PATCH 095/146] Now it's time for the testing phase * Deleting old Extensions * Added ReloadAllWeapons * Archiving old component in case it needs to be used --- .../{NewAFKComponent.cs => AfkComponent.cs} | 21 ++- .../{AFKComponent.cs => OldAfkComponent.cs} | 12 +- UltimateAFK/Handlers/MainHandler.cs | 127 +++++++++++------- UltimateAFK/Resources/Extensions.cs | 51 +------ 4 files changed, 107 insertions(+), 104 deletions(-) rename UltimateAFK/Handlers/Components/{NewAFKComponent.cs => AfkComponent.cs} (95%) rename UltimateAFK/Handlers/Components/{AFKComponent.cs => OldAfkComponent.cs} (96%) diff --git a/UltimateAFK/Handlers/Components/NewAFKComponent.cs b/UltimateAFK/Handlers/Components/AfkComponent.cs similarity index 95% rename from UltimateAFK/Handlers/Components/NewAFKComponent.cs rename to UltimateAFK/Handlers/Components/AfkComponent.cs index 8f35d38..40f1738 100644 --- a/UltimateAFK/Handlers/Components/NewAFKComponent.cs +++ b/UltimateAFK/Handlers/Components/AfkComponent.cs @@ -11,8 +11,11 @@ namespace UltimateAFK.Handlers.Components { + /// + /// Component that performs a constant afk check. + /// [RequireComponent(typeof(ReferenceHub))] - public class NewAFKComponent : MonoBehaviour + public class AfkComponent : MonoBehaviour { private void Awake() { @@ -40,6 +43,14 @@ private void OnDestroy() Timing.KillCoroutines(_checkHandle); } + /// + /// Public method to destroy the component from the outside. + /// + public void Destroy() + { + Destroy(this); + } + private IEnumerator Check() { for (;;) @@ -93,7 +104,7 @@ private void CheckAfk() if (graceNumb > 0) { // The player is in grace time, so let's warn him that he has been afk for too long. - Owner.SendBroadcastToPlayer(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, + Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, shouldClearPrevious: true); } else @@ -241,7 +252,7 @@ private void Replace(Player ply, RoleTypeId roleType) } //Send player a broadcast for being too long afk - ply.SendBroadcastToPlayer(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); // Sends replacement to the role that had the afk @@ -269,9 +280,9 @@ private void Replace(Player ply, RoleTypeId roleType) } // Clear player inventory - ply.ClearPlayerInventory(); + ply.ClearInventory(); //Send player a broadcast for being too long afk - ply.SendBroadcastToPlayer(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); // Sends player to spectator ply.SetRole(RoleTypeId.Spectator); diff --git a/UltimateAFK/Handlers/Components/AFKComponent.cs b/UltimateAFK/Handlers/Components/OldAfkComponent.cs similarity index 96% rename from UltimateAFK/Handlers/Components/AFKComponent.cs rename to UltimateAFK/Handlers/Components/OldAfkComponent.cs index ccaa925..f49d9ea 100644 --- a/UltimateAFK/Handlers/Components/AFKComponent.cs +++ b/UltimateAFK/Handlers/Components/OldAfkComponent.cs @@ -16,7 +16,7 @@ namespace UltimateAFK.Handlers.Components /// /// Component that performs a constant afk check. /// - public class AfkComponent : MonoBehaviour + public class OldAfkComponent : MonoBehaviour { /// /// The player who owns this component. @@ -140,7 +140,7 @@ private void CheckAfk() if (graceNumb > 0) { // The player is in grace time, so let's warn him that he has been afk for too long. - Owner.SendBroadcastToPlayer(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, + Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, shouldClearPrevious: true); } else @@ -164,7 +164,7 @@ public void Replace(Player player, RoleTypeId role) if (UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(role)) { Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); - player.ClearPlayerInventory(); + player.ClearInventory(); player.SetRole(RoleTypeId.Spectator); if (UltimateAFK.Singleton.Config.AfkCount != -1) @@ -243,7 +243,7 @@ public void Replace(Player player, RoleTypeId role) } //Send player a broadcast for being too long afk - player.SendBroadcastToPlayer(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); // Sends replacement to the role that had the afk @@ -273,9 +273,9 @@ public void Replace(Player player, RoleTypeId role) try { // Clear player inventory - player.ClearPlayerInventory(); + player.ClearInventory(); //Send player a broadcast for being too long afk - player.SendBroadcastToPlayer(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); // Sends player to spectator player.SetRole(RoleTypeId.Spectator); diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 55d268c..eece5a9 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -17,6 +17,8 @@ namespace UltimateAFK.Handlers /// public class MainHandler { + #region Ignore this + private readonly UltimateAFK Plugin; public MainHandler(UltimateAFK plugin) @@ -24,11 +26,17 @@ public MainHandler(UltimateAFK plugin) Plugin = plugin; } + #endregion + /// /// A dictionary where replacement players are stored to give them the stats and items of the original player. /// public static Dictionary ReplacingPlayers = new(); + /// + /// When a player joins I give him the component. + /// + /// [PluginEvent(ServerEventType.PlayerJoined)] private void OnPlayerJoin(Player player) { @@ -49,6 +57,9 @@ private void OnPlayerJoin(Player player) } } + /// + /// When a player changes roles I make sure that if it is a replacement I give him all the things of the person who replaced. + /// [PluginEvent(ServerEventType.PlayerChangeRole)] private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId newRole, RoleChangeReason reason) { @@ -60,7 +71,7 @@ private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId ne Log.Debug($"Detecting player {player.Nickname} ({player.UserId}) who replaced a player who was afk", UltimateAFK.Singleton.Config.DebugMode); - Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => ReplaceItemsAndStats(player, data, newRole)); + Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => GiveAfkData(player, data, newRole)); } catch (System.Exception e) { @@ -68,67 +79,85 @@ private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId ne } } + /// + /// Performs the change of stats and items to the replacement player + /// + private void GiveAfkData(Player ply, AFKData data, RoleTypeId newRole) + { + Log.Debug($"Replacing player is {ply.Nickname} ({ply.UserId}) new role is {newRole}", Plugin.Config.DebugMode); + + if (newRole == RoleTypeId.Scp079) + { + Log.Debug("The new role is a SCP079, transferring energy and experience.", UltimateAFK.Singleton.Config.DebugMode); + if (ply.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + { + ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); + ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); + + tierManager.TotalExp = data.SCP079.Experience; + energyManager.CurrentAux = data.SCP079.Energy; + ReplacingPlayers.Remove(ply); + + Log.Debug($"Energy and experience transferred to the player", UltimateAFK.Singleton.Config.DebugMode); + } + else + { + Log.Error($"Error transferring experience and level to the replacement player, Player.RoleBase is not Scp079 or there was an error obtaining the subroutines."); + } + + Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Singleton.Config.DebugMode); + ReplacingPlayers.Remove(ply); + } + else + { + Log.Debug("Clearing replacement player inventory", Plugin.Config.DebugMode); + ply.ClearInventory(); + Log.Debug($"Adding Ammo to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); + // I add the ammunition first since it is the slowest thing to be done. + ply.SendAmmo(data.Ammo); + + // This call delayed is necessary. + Timing.CallDelayed(0.1f, () => + { + Log.Debug($"Changing player {ply.Nickname} ({ply.UserId}) position and HP", Plugin.Config.DebugMode); + ply.Position = data.Position; + ply.Health = data.Health; + Log.Debug($"Adding items to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); + ply.SendItems(data.Items); + // I apply the modifications of the replacement player not of the afk, I could do it but I sincerely prefer this method. + ply.ApplyAttachments(); + // I refill the ammunition of the weapons, since it is annoying to appear without a loaded weapon. + ply.ReloadAllWeapons(); + // Send the broadcast to the player + ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); + ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); + }); + + Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Singleton.Config.DebugMode); + ReplacingPlayers.Remove(ply); + } + } + + /// + /// At the beginning of a round I clean the ReplacingPlayers dictionary. + /// [PluginEvent(ServerEventType.RoundStart)] private void OnRoundStarted() { ReplacingPlayers.Clear(); } + /// + /// I make sure that no player is in the dictionary in case it has not been cleaned correctly, this event is also executed when a player disconnects. + /// [PluginEvent(ServerEventType.PlayerDeath)] private void OnPlayerDeath(Player player, Player attacker, DamageHandlerBase damageHandler) { - // This works both in case the player is killed in the middle of the AFK detection, as well as if the player is disconnected, since when disconnected he goes through the PlayerDeath event. if (player != null && ReplacingPlayers.TryGetValue(player, out var data)) { ReplacingPlayers.Remove(player); } } - - /// - /// Performs the change of stats and items to the replacement player - /// - private void ReplaceItemsAndStats(Player ply, AFKData data, RoleTypeId newrole) - { - Log.Debug($"Replacing player is {ply.Nickname} ({ply.UserId})", Plugin.Config.DebugMode); - - Scp079Role scp079Role; - if (newrole == RoleTypeId.Scp079 && data.SCP079.Role != null && - (scp079Role = ply.ReferenceHub.roleManager.CurrentRole as Scp079Role) != null - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager scp079TierManager) && - scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager scp079AuxManager)) - { - Log.Debug("The new role is a SCP079, transferring energy and experience.", UltimateAFK.Singleton.Config.DebugMode); - - ply.SendBroadcastToPlayer(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); - ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); - - scp079TierManager.TotalExp = data.SCP079.Experience; - scp079AuxManager.CurrentAux = data.SCP079.Energy; - ReplacingPlayers.Remove(ply); - return; - } - - ply.ClearPlayerInventory(); - Log.Debug($"Adding Ammo to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); - // I add the ammunition first since it is the slowest thing to be done. - ply.SendAmmo(data.Ammo); - - // This call delayed is necessary - Timing.CallDelayed(0.3f, () => - { - Log.Debug($"Changing player {ply.Nickname} ({ply.UserId}) position and HP", Plugin.Config.DebugMode); - ply.Position = data.Position; - ply.SendBroadcastToPlayer(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); - ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); - ply.Health = data.Health; - Log.Debug($"Adding items to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); - ply.SendItems(data.Items); - // I apply the modifications of the replacement player not of the afk, I could do it but I sincerely prefer this method. - ply.ApplyAttachments(); - }); - - Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Singleton.Config.DebugMode); - ReplacingPlayers.Remove(ply); - } } } \ No newline at end of file diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs index 5bff2aa..ea997cf 100644 --- a/UltimateAFK/Resources/Extensions.cs +++ b/UltimateAFK/Resources/Extensions.cs @@ -11,6 +11,9 @@ namespace UltimateAFK.Resources { public static class Extensions { + /// + /// Adds several items at the same time to a player. + /// public static void SendItems(this Player player, List types) { foreach (var item in types) @@ -19,6 +22,9 @@ public static void SendItems(this Player player, List types) } } + /// + /// Gives the ammunition you want a player to have. + /// public static void SendAmmo(this Player ply, Dictionary ammo) { foreach (var ammoItem in ammo) @@ -41,50 +47,7 @@ public static List GetItems(this Player ply) return returnitems; } - - /// - /// Until NW Fix SendBroadcast. - /// - /// - /// - /// - /// - /// - public static void SendBroadcastToPlayer(this Player ply, string message, ushort duration, - Broadcast.BroadcastFlags type = Broadcast.BroadcastFlags.Normal, bool shouldClearPrevious = false) - { - if (shouldClearPrevious) ClearBroadcasts(ply); - - Broadcast.Singleton.TargetAddElement(ply.Connection, message, duration, type); - } - - private static void ClearBroadcasts(Player ply) - { - Broadcast.Singleton.TargetClearElements(ply.Connection); - } - - /// - /// Until NW fix clear inventory. - /// - /// - /// - /// - public static void ClearPlayerInventory(this Player ply, bool clearAmmo = true, bool clearItems = true) - { - if (clearAmmo) - { - ply.ReferenceHub.inventory.UserInventory.ReserveAmmo.Clear(); - } - if (clearItems) - { - var inventory = ply.ReferenceHub.inventory.UserInventory; - while (inventory.Items.Count > 0) - { - ply.ReferenceHub.inventory.ServerRemoveItem(inventory.Items.ElementAt(0).Key, null); - } - } - } - + /// /// Applies player attachments. /// From 2a18ebc376c229dff0e67041f38a0c9a7641dac1 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 09:51:20 -0300 Subject: [PATCH 096/146] New version 6.1.0 --- Cerberus.props | 2 +- UltimateAFK/Handlers/Components/AfkComponent.cs | 15 +++++++++------ UltimateAFK/Handlers/MainHandler.cs | 4 ++++ UltimateAFK/UltimateAFK.cs | 2 +- UltimateAFK/UltimateAFK.csproj | 1 - 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index bff649a..00dfde0 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.0.2 + 6.1.0 false diff --git a/UltimateAFK/Handlers/Components/AfkComponent.cs b/UltimateAFK/Handlers/Components/AfkComponent.cs index 40f1738..6519519 100644 --- a/UltimateAFK/Handlers/Components/AfkComponent.cs +++ b/UltimateAFK/Handlers/Components/AfkComponent.cs @@ -84,6 +84,8 @@ private void CheckAfk() var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Camera.position; var cameraRotation = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp0792 ? new Quaternion(scp0792.CurrentCamera.HorizontalRotation, scp0792.CurrentCamera.VerticalRotation, scp0792.CurrentCamera.RollRotation, 0f ) : Owner.Camera.rotation; + // Set OwnerRoleType + OwnerRoleType = Owner.Role; // Player is moving if (cameraPosition != _cameraPosition || ownerPosition != _ownerPosition || _cameraRotation != cameraRotation) { @@ -91,6 +93,7 @@ private void CheckAfk() _cameraRotation = cameraRotation; _ownerPosition = ownerPosition; _afkTime = 0f; + OwnerLastPosition = ownerPosition; } // The player is not moving and is not SCP-096 with his TryToNotCry ability. else if (!(Owner.Role == RoleTypeId.Scp096 && (Owner.ReferenceHub.roleManager.CurrentRole as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry))) @@ -118,21 +121,21 @@ private void CheckAfk() } #region API - public static Player Owner { get; private set; } + public Player Owner { get; private set; } - public RoleTypeId OwnerRoleType { get; } = Owner.Role; + public RoleTypeId OwnerRoleType; - public Vector3 OwnerLastPosition { get; } = _ownerPosition; + public Vector3 OwnerLastPosition; + public int AfkTimes { get; set; } - + public bool IsKickEnabled { get; set; } = UltimateAFK.Singleton.Config.AfkCount > -1; - #endregion #region Private variables // Position in the world - private static Vector3 _ownerPosition; + private Vector3 _ownerPosition; // Player camera position private Vector3 _cameraPosition; diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index eece5a9..0652d9d 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -42,6 +42,10 @@ private void OnPlayerJoin(Player player) { if(!Plugin.Config.IsEnabled || player.UserId.Contains("@server")) return; + Log.Debug($"Adding the Component to {player.Nickname} | Player already have component: {player.GameObject.TryGetComponent(out _)}", UltimateAFK.Singleton.Config.DebugMode); + + player.GameObject.AddComponent(); + return; if (player.GameObject.TryGetComponent(out var com)) { com.Destroy(); diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 44c62c6..19fc358 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; - [PluginEntryPoint("UltimateAFK", "6.0.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 5934c9e..5a9e94e 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -5,7 +5,6 @@ Library UltimateAFK true - From e37d6fe557cbcfa6968c685145aca35cc48f5c8d Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 09:56:06 -0300 Subject: [PATCH 097/146] IsEmpty not work --- UltimateAFK/Handlers/Components/AfkComponent.cs | 2 +- UltimateAFK/UltimateAFK.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/UltimateAFK/Handlers/Components/AfkComponent.cs b/UltimateAFK/Handlers/Components/AfkComponent.cs index 6519519..ebbdfa4 100644 --- a/UltimateAFK/Handlers/Components/AfkComponent.cs +++ b/UltimateAFK/Handlers/Components/AfkComponent.cs @@ -172,7 +172,7 @@ private void Replace(Player ply, RoleTypeId roleType) try { // Check if role is blacklisted - if (!UltimateAFK.Singleton.Config.RoleTypeBlacklist.IsEmpty() && UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) + if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Count > 0 && UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) { Log.Debug($"player {ply.Nickname} ({ply.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 19fc358..53d1ac5 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -22,6 +22,7 @@ void OnEnabled() { Singleton = this; PluginAPI.Events.EventManager.RegisterEvents(this, new Handlers.MainHandler(Singleton)); + if (ConfigFile.ServerConfig.GetInt("afk_time") > 0) { Log.Warning($"You have enabled the AFK detector of the base game, please disable it by setting &6afk_time = 0&r in &4config_gameplay.txt&r"); From 253af9b7a0a2f76c266bdcf7da1f7f8f9c7f084c Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 09:57:55 -0300 Subject: [PATCH 098/146] Update Config.cs --- UltimateAFK/Config.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 921175f..97bf7af 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -25,7 +25,7 @@ public class Config public bool IgnoreTut { get; set; } = true; [Description("RoleTypes on this list will not be replaced by other players")] - public List RoleTypeBlacklist { get; set; } = new List() { RoleTypeId.Scp0492 }; + public List RoleTypeBlacklist { get; set; } = new() { RoleTypeId.Scp0492 }; [Description("The time it takes for a player to stand still before he is detected as AFK")] public int AfkTime { get; set; } = 80; From 4d0857c324c50bf736d8f568a8e3512f12c97931 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 10:01:24 -0300 Subject: [PATCH 099/146] zzzzz --- Cerberus.props | 2 +- UltimateAFK/UltimateAFK.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index 00dfde0..bff649a 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.0 + 6.0.2 false diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 53d1ac5..44c62c6 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,12 +17,11 @@ public class UltimateAFK [PluginConfig] public Config Config; - [PluginEntryPoint("UltimateAFK", "6.1.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.0.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; PluginAPI.Events.EventManager.RegisterEvents(this, new Handlers.MainHandler(Singleton)); - if (ConfigFile.ServerConfig.GetInt("afk_time") > 0) { Log.Warning($"You have enabled the AFK detector of the base game, please disable it by setting &6afk_time = 0&r in &4config_gameplay.txt&r"); From 67e9240dad4ee1f4059d42be65e313afe7cac70a Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 10:02:17 -0300 Subject: [PATCH 100/146] i dont whan to use GitMarge force --- UltimateAFK/Handlers/Components/AfkComponent.cs | 2 +- UltimateAFK/Handlers/Components/OldAfkComponent.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UltimateAFK/Handlers/Components/AfkComponent.cs b/UltimateAFK/Handlers/Components/AfkComponent.cs index ebbdfa4..4e2ce3c 100644 --- a/UltimateAFK/Handlers/Components/AfkComponent.cs +++ b/UltimateAFK/Handlers/Components/AfkComponent.cs @@ -15,7 +15,7 @@ namespace UltimateAFK.Handlers.Components /// Component that performs a constant afk check. /// [RequireComponent(typeof(ReferenceHub))] - public class AfkComponent : MonoBehaviour + public class NewAfkComponent : MonoBehaviour { private void Awake() { diff --git a/UltimateAFK/Handlers/Components/OldAfkComponent.cs b/UltimateAFK/Handlers/Components/OldAfkComponent.cs index f49d9ea..86fe778 100644 --- a/UltimateAFK/Handlers/Components/OldAfkComponent.cs +++ b/UltimateAFK/Handlers/Components/OldAfkComponent.cs @@ -16,7 +16,7 @@ namespace UltimateAFK.Handlers.Components /// /// Component that performs a constant afk check. /// - public class OldAfkComponent : MonoBehaviour + public class AfkComponent : MonoBehaviour { /// /// The player who owns this component. From b1881b579745150214ef3add3dea5b216e128462 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 10:06:08 -0300 Subject: [PATCH 101/146] fuck you github --- Cerberus.props | 2 +- UltimateAFK/Handlers/Components/AfkComponent.cs | 2 +- UltimateAFK/Handlers/Components/OldAfkComponent.cs | 2 +- UltimateAFK/Handlers/MainHandler.cs | 14 -------------- UltimateAFK/UltimateAFK.cs | 4 +++- 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index bff649a..00dfde0 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.0.2 + 6.1.0 false diff --git a/UltimateAFK/Handlers/Components/AfkComponent.cs b/UltimateAFK/Handlers/Components/AfkComponent.cs index 4e2ce3c..ebbdfa4 100644 --- a/UltimateAFK/Handlers/Components/AfkComponent.cs +++ b/UltimateAFK/Handlers/Components/AfkComponent.cs @@ -15,7 +15,7 @@ namespace UltimateAFK.Handlers.Components /// Component that performs a constant afk check. /// [RequireComponent(typeof(ReferenceHub))] - public class NewAfkComponent : MonoBehaviour + public class AfkComponent : MonoBehaviour { private void Awake() { diff --git a/UltimateAFK/Handlers/Components/OldAfkComponent.cs b/UltimateAFK/Handlers/Components/OldAfkComponent.cs index 86fe778..f49d9ea 100644 --- a/UltimateAFK/Handlers/Components/OldAfkComponent.cs +++ b/UltimateAFK/Handlers/Components/OldAfkComponent.cs @@ -16,7 +16,7 @@ namespace UltimateAFK.Handlers.Components /// /// Component that performs a constant afk check. /// - public class AfkComponent : MonoBehaviour + public class OldAfkComponent : MonoBehaviour { /// /// The player who owns this component. diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 0652d9d..d114afa 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -45,20 +45,6 @@ private void OnPlayerJoin(Player player) Log.Debug($"Adding the Component to {player.Nickname} | Player already have component: {player.GameObject.TryGetComponent(out _)}", UltimateAFK.Singleton.Config.DebugMode); player.GameObject.AddComponent(); - return; - if (player.GameObject.TryGetComponent(out var com)) - { - com.Destroy(); - - Log.Debug($"Adding the Component to {player.Nickname}", UltimateAFK.Singleton.Config.DebugMode); - player.GameObject.AddComponent(); - } - else - { - Log.Debug($"Adding the Component to {player.Nickname}", UltimateAFK.Singleton.Config.DebugMode); - - player.GameObject.AddComponent(); - } } /// diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 44c62c6..41096d2 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -2,6 +2,7 @@ using GameCore; using PluginAPI.Core; using PluginAPI.Core.Attributes; +using PluginAPI.Enums; using UltimateAFK.Handlers; using UltimateAFK.Resources; using Log = PluginAPI.Core.Log; @@ -17,7 +18,8 @@ public class UltimateAFK [PluginConfig] public Config Config; - [PluginEntryPoint("UltimateAFK", "6.0.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginPriority(LoadPriority.High)] + [PluginEntryPoint("UltimateAFK", "6.1.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From fa40dacb094156e3243e76df0ef013aa18716ae4 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 16:13:35 -0300 Subject: [PATCH 102/146] Fixing base game afk warning --- UltimateAFK/UltimateAFK.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 41096d2..b6ed159 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -24,7 +24,8 @@ void OnEnabled() { Singleton = this; PluginAPI.Events.EventManager.RegisterEvents(this, new Handlers.MainHandler(Singleton)); - if (ConfigFile.ServerConfig.GetInt("afk_time") > 0) + + if (ConfigFile.ServerConfig.GetFloat("afk_time", 90f) > 0) { Log.Warning($"You have enabled the AFK detector of the base game, please disable it by setting &6afk_time = 0&r in &4config_gameplay.txt&r"); } From f6a1c83b72e86375f572ba78b120870e8d650dc1 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 16:14:34 -0300 Subject: [PATCH 103/146] =?UTF-8?q?pi=20pi=20pi=20hablo=20espa=C3=B1ol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UltimateAFK/UltimateAFK.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index b6ed159..e1570a6 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -27,7 +27,7 @@ void OnEnabled() if (ConfigFile.ServerConfig.GetFloat("afk_time", 90f) > 0) { - Log.Warning($"You have enabled the AFK detector of the base game, please disable it by setting &6afk_time = 0&r in &4config_gameplay.txt&r"); + Log.Warning($"You have enabled the AFK detector of the base game, please disable it by config &6afk_time = 0&r in &4config_gameplay.txt&r"); } //PluginAPI.Events.EventManager.RegisterEvents(this); } From 567a53d2b6649ea2e01bab4f8291b59fbed82847 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 18 Jan 2023 16:26:54 -0300 Subject: [PATCH 104/146] Update UltimateAFK.cs --- UltimateAFK/UltimateAFK.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index e1570a6..78f0c13 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -29,6 +29,7 @@ void OnEnabled() { Log.Warning($"You have enabled the AFK detector of the base game, please disable it by config &6afk_time = 0&r in &4config_gameplay.txt&r"); } + //PluginAPI.Events.EventManager.RegisterEvents(this); } From 4eca8841c5c1141cca9a7c36f07d47e09b673dcd Mon Sep 17 00:00:00 2001 From: SrLicht Date: Fri, 27 Jan 2023 06:52:41 -0300 Subject: [PATCH 105/146] 6.1.1 * Now if the configuration allows it, players can use a command to be considered afk and be moved to spectator and have a player replace them. * Changed the method of how experience is given to SCP-079. I had reports of a bug that made the lockdown skill have 575 seconds of cooldown, I don't know if this is my problem but just in case. --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 202 ++++++++++++++++++++++++++++ UltimateAFK/Config.cs | 27 ++++ UltimateAFK/Handlers/MainHandler.cs | 4 +- UltimateAFK/UltimateAFK.cs | 6 +- 5 files changed, 236 insertions(+), 5 deletions(-) create mode 100644 UltimateAFK/Command/AfkCommand.cs diff --git a/Cerberus.props b/Cerberus.props index 00dfde0..f08e96b 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.0 + 6.1.1 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs new file mode 100644 index 0000000..ab91a46 --- /dev/null +++ b/UltimateAFK/Command/AfkCommand.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using CommandSystem; +using NWAPIPermissionSystem; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using PluginAPI.Commands; +using PluginAPI.Core; +using PluginAPI.Core.Attributes; +using UltimateAFK.Handlers; +using UltimateAFK.Resources; + +namespace UltimateAFK.Command +{ + [CommandHandler(typeof(ClientCommandHandler))] + public class AfkCommand : ICommand + { + public string Command { get; } = "afk"; + public string[] Aliases { get; } + public string Description { get; } = "By using this command you will be moved to spectator and if the server allows it a player will replace you."; + + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) + { + try + { + if (!UltimateAFK.Singleton.Config.CommandConfig.IsEnabled) + { + response = UltimateAFK.Singleton.Config.CommandConfig.TextOnDisable; + } + + var ply = Player.Get(sender); + + GoAfk(ply, ply.Role); + response = UltimateAFK.Singleton.Config.CommandConfig.TextOnSucces; + return true; + } + catch (Exception e) + { + response = $"Error: {e.Data} -- {e}"; + return false; + } + } + + private void GoAfk(Player ply, RoleTypeId roleType) + { + // Check if role is blacklisted + if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Count > 0 && UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) + { + Log.Debug($"In the command | player {ply.Nickname} ({ply.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); + + ply.ClearInventory(); + ply.SetRole(RoleTypeId.Spectator); + + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + return; + } + + // Get player replacement + Player replacement = null; + + if (UltimateAFK.Singleton.Config.CommandConfig.Replace) + replacement = FindReplacement(ply); + + // If replacement is null + if (replacement is null) + { + Log.Debug("In the command | Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); + + ply.ClearInventory(); + ply.SetRole(RoleTypeId.Spectator); + + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + } + else + { + // if not + Log.Debug($"In the command | Replacement Player found: {replacement.Nickname} ({replacement.UserId})", UltimateAFK.Singleton.Config.DebugMode); + + // Check if AFK role is SCP-079 + if (roleType is RoleTypeId.Scp079) + { + //Adds the replacement player to the dictionary with all the necessary information + AddData(ply, replacement, true); + + // Self-explanatory + ply.SetRole(RoleTypeId.Spectator); + + //Send player a broadcast for being too long afk + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + + // Sends replacement to the role that had the afk + replacement.SetRole(roleType); + } + else + { + // Adds the replacement player to the dictionary with all the necessary information + AddData(ply, replacement, false); + + // Clear player inventory + ply.ClearInventory(); + //Send player a broadcast for being too long afk + ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + // Sends player to spectator + ply.SetRole(RoleTypeId.Spectator); + // Sends replacement to the role that had the afk + replacement.SetRole(roleType); + } + } + } + + private Player FindReplacement(Player afk) + { + foreach (var player in Player.GetPlayers()) + { + if (player.IsAlive || player == afk || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server")) + continue; + + return player; + } + + return null; + } + + /// + /// Add player data to ReplacingPlayers dictionary. + /// + private void AddData(Player player, Player replacement, bool is079 = false) + { + try + { + if (is079) + { + if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetComponent(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + { + MainHandler.ReplacingPlayers.Add(replacement, new AFKData + { + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = null, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data + { + Role = scp079Role, + Energy = energyManager.CurrentAux, + Experience = tierManager.TotalExp, + } + }); + } + + return; + } + + // If I make Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo for some reason it gets buggy and ammo becomes null when changing the player to spectator. + // So I create a temporary dictionary stored in cache (ram) and then clean the information by deleting it from the ReplacingPlayers. + var ammo = GetAmmo(player); + MainHandler.ReplacingPlayers.Add(replacement, new AFKData + { + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = ammo, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data + { + Role = null, + Energy = 0f, + Experience = 0 + } + }); + } + catch (Exception e) + { + Log.Error($"Error on {nameof(AddData)}: {e.Data} -- {e.StackTrace}"); + } + } + + /// + /// Cache player's ammunition + /// + /// + /// + private Dictionary GetAmmo(Player player) + { + var result = new Dictionary(); + + foreach (var ammo in player.ReferenceHub.inventory.UserInventory.ReserveAmmo) + { + result.Add(ammo.Key, ammo.Value); + } + + return result; + } + } +} \ No newline at end of file diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 97bf7af..c474feb 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -47,5 +47,32 @@ public class Config [Description("When a player replaces another player, this message will appear on the player's face and on the player console. | {0} it is the name of the player who was afk")] public string MsgReplace { get; set; } = " You replaced {0} who was afk."; + + [Description("All configuration related with the command")] + public CommandConfig CommandConfig { get; set; } = new(); + } + + public class CommandConfig + { + [Description("Is the command enabled on this server ?")] + public bool IsEnabled { get; set; } = false; + + // Maybe one day... Commands load before singleton is created so i can get config + /*[Description("the prefix to be used by the players .afk or whatever you like")] + public string Command { get; set; } = "afk"; + + [Description("I recommend you not to leave this empty since a bug crashes the server if you register commands with empty aliases.")] + public string[] Aliases { get; set; } = new[] { "uafk" }; + + [Description("The command description")] + public string Description { get; set; } = "By using this command you will be moved to spectator and if the server allows it a player will replace you."; + */ + + [Description("Players who use this command will be replaced by players who are in spectator")] + public bool Replace { get; set; } = true; + + public string TextOnDisable = "This command is disabled"; + + public string TextOnSucces = "You were moved to spectator because you considered yourself AFK."; } } \ No newline at end of file diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index d114afa..f0a8f9c 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -84,8 +84,8 @@ private void GiveAfkData(Player ply, AFKData data, RoleTypeId newRole) { ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); - - tierManager.TotalExp = data.SCP079.Experience; + + tierManager.ServerGrantExperience(data.SCP079.Experience, Scp079HudTranslation.Experience); energyManager.CurrentAux = data.SCP079.Energy; ReplacingPlayers.Remove(ply); diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 78f0c13..131ec56 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +using System.Linq; using GameCore; +using Interactables.Interobjects.DoorUtils; using PluginAPI.Core; using PluginAPI.Core.Attributes; using PluginAPI.Enums; @@ -19,13 +21,13 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; PluginAPI.Events.EventManager.RegisterEvents(this, new Handlers.MainHandler(Singleton)); - if (ConfigFile.ServerConfig.GetFloat("afk_time", 90f) > 0) + if (ConfigFile.ServerConfig.GetFloat("afk_time") > 1) { Log.Warning($"You have enabled the AFK detector of the base game, please disable it by config &6afk_time = 0&r in &4config_gameplay.txt&r"); } From 48ed9af31ea08758f8c4ddcebe48f5b3ecde5ad5 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 28 Jan 2023 00:04:03 -0300 Subject: [PATCH 106/146] 6.1.2 --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 2 +- UltimateAFK/Config.cs | 6 ++++-- UltimateAFK/UltimateAFK.cs | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index f08e96b..aafe2e6 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.1 + 6.1.2 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index ab91a46..514c0e1 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -31,7 +31,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s var ply = Player.Get(sender); GoAfk(ply, ply.Role); - response = UltimateAFK.Singleton.Config.CommandConfig.TextOnSucces; + response = UltimateAFK.Singleton.Config.CommandConfig.TextOnSuccess; return true; } catch (Exception e) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index c474feb..5624dcb 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -71,8 +71,10 @@ public class CommandConfig [Description("Players who use this command will be replaced by players who are in spectator")] public bool Replace { get; set; } = true; - public string TextOnDisable = "This command is disabled"; + [Description("The message given to the player attempting to use the command when it is disabled.")] + public string TextOnDisable {get; set;} = "This command is disabled"; - public string TextOnSucces = "You were moved to spectator because you considered yourself AFK."; + [Description("The message that will be sent to the player when they are moved to spectator")] + public string TextOnSuccess { get; set; } = "You were moved to spectator because you considered yourself AFK."; } } \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 131ec56..6bfdd9e 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,7 +21,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From ac34e8c2bccef1b495f90e414dd84199e565facd Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sun, 29 Jan 2023 17:16:54 -0300 Subject: [PATCH 107/146] 6.1.3 * Now the command will not be executed if the player has the ServereHands or Cardiac Arrest effect. * Added configurable cooldown to .afk command --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 33 ++++++++++++++++++++++++++++++- UltimateAFK/Config.cs | 12 +++++++++++ UltimateAFK/UltimateAFK.cs | 2 +- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index aafe2e6..6d348c6 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.2 + 6.1.3 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 514c0e1..949b7b1 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using CommandSystem; +using CustomPlayerEffects; using NWAPIPermissionSystem; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; @@ -9,6 +10,7 @@ using PluginAPI.Core.Attributes; using UltimateAFK.Handlers; using UltimateAFK.Resources; +using UnityEngine; namespace UltimateAFK.Command { @@ -18,6 +20,7 @@ public class AfkCommand : ICommand public string Command { get; } = "afk"; public string[] Aliases { get; } public string Description { get; } = "By using this command you will be moved to spectator and if the server allows it a player will replace you."; + public Dictionary InCooldown = new(); public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) { @@ -27,10 +30,38 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s { response = UltimateAFK.Singleton.Config.CommandConfig.TextOnDisable; } - var ply = Player.Get(sender); + if (ply.EffectsManager.TryGetEffect(out _)) + { + response = UltimateAFK.Singleton.Config.CommandConfig.TextOnSevereHands; + return false; + } + if (ply.EffectsManager.TryGetEffect(out _)) + { + response = UltimateAFK.Singleton.Config.CommandConfig.TextOnHearthAttack; + return false; + } + if (InCooldown.TryGetValue(ply.UserId, out var cooldown)) + { + // In cooldown + if (cooldown >= Time.time) + { + var cooldownTimer = (int)(cooldown - Time.time); + + response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.TextOnCooldown, + cooldownTimer); + return false; + } + else + { + // Not in cooldown + InCooldown.Remove(ply.UserId); + } + } + GoAfk(ply, ply.Role); + InCooldown.Add(ply.UserId, Time.time + UltimateAFK.Singleton.Config.CommandConfig.Cooldown); response = UltimateAFK.Singleton.Config.CommandConfig.TextOnSuccess; return true; } diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 5624dcb..550a750 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -71,10 +71,22 @@ public class CommandConfig [Description("Players who use this command will be replaced by players who are in spectator")] public bool Replace { get; set; } = true; + [Description("The coldown of the command when using it")] + public float Cooldown { get; set; } = 40f; + [Description("The message given to the player attempting to use the command when it is disabled.")] public string TextOnDisable {get; set;} = "This command is disabled"; [Description("The message that will be sent to the player when they are moved to spectator")] public string TextOnSuccess { get; set; } = "You were moved to spectator because you considered yourself AFK."; + + [Description("The message that will be given to the player when trying to use the command when it has the effect of taking more than 2 candies.")] + public string TextOnSevereHands { get; set; } = $"You cannot use this command if you have no hands"; + + [Description("When a player attempts to use the command when it has the effect given by SCP-049")] + public string TextOnHearthAttack { get; set; } = $"You cannot use this command if you have a heart attack."; + + [Description("When a player tries to use the command when he still has cooldown, {0} is the number of seconds the player has to wait.")] + public string TextOnCooldown { get; set; } = "You cannot use the command yet, you have to wait {0} seconds."; } } \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 6bfdd9e..3809de3 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,7 +21,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.3", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 6d74368be16a21eeee0c838dc7705b667ac16172 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 30 Jan 2023 19:14:55 -0300 Subject: [PATCH 108/146] 6.1.4 * I broke all translations with this update, sorry. * Now if a player is in the pocket dimension they will not be able to use the command. * If a player is dead, they will not be able to use the command * It is now possible to configure a list of RoleTypes that cannot use the command * It is now possible to make it so that only a list of UserGroups can use the command (useful if you only want your VIPs to be able to use the command) * Fixed a small bug where disable command didn't work --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 43 +++++++++++++++++---- UltimateAFK/Config.cs | 64 ++++++++++++++++++++++++------- UltimateAFK/UltimateAFK.cs | 2 +- 4 files changed, 89 insertions(+), 22 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index 6d348c6..ce51cd0 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.3 + 6.1.4 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 949b7b1..afa78c8 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using CommandSystem; using CustomPlayerEffects; +using MapGeneration; using NWAPIPermissionSystem; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; @@ -28,18 +29,46 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s { if (!UltimateAFK.Singleton.Config.CommandConfig.IsEnabled) { - response = UltimateAFK.Singleton.Config.CommandConfig.TextOnDisable; + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnDisable; + return false; + } + if (!Round.IsRoundStarted) + { + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnRoundIsNotStarted; + return false; } - var ply = Player.Get(sender); + var ply = Player.Get(sender); + if (!ply.IsAlive) + { + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPlayerIsDead; + return false; + } + if (ply.Zone == FacilityZone.Other) + { + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPocketDimension; + return false; + } + if (UltimateAFK.Singleton.Config.CommandConfig.DisableForCertainRole && UltimateAFK.Singleton.Config.CommandConfig.RoleTypeIdBlackList.Contains(ply.Role)) + { + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnBlackListedRole; + return false; + } + + if (UltimateAFK.Singleton.Config.CommandConfig.ExclusiveForGroups && + !UltimateAFK.Singleton.Config.CommandConfig.UserGroupsAllowed.Contains(ply.RoleName)) + { + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnGroupExclusive; + return false; + } if (ply.EffectsManager.TryGetEffect(out _)) { - response = UltimateAFK.Singleton.Config.CommandConfig.TextOnSevereHands; + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnSevereHands; return false; } if (ply.EffectsManager.TryGetEffect(out _)) { - response = UltimateAFK.Singleton.Config.CommandConfig.TextOnHearthAttack; + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnHearthAttack; return false; } if (InCooldown.TryGetValue(ply.UserId, out var cooldown)) @@ -49,7 +78,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s { var cooldownTimer = (int)(cooldown - Time.time); - response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.TextOnCooldown, + response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnCooldown, cooldownTimer); return false; } @@ -62,12 +91,12 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s GoAfk(ply, ply.Role); InCooldown.Add(ply.UserId, Time.time + UltimateAFK.Singleton.Config.CommandConfig.Cooldown); - response = UltimateAFK.Singleton.Config.CommandConfig.TextOnSuccess; + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnSuccess; return true; } catch (Exception e) { - response = $"Error: {e.Data} -- {e}"; + response = $"Error: {e} -- {e.Message}"; return false; } } diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 550a750..2305839 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -57,7 +57,7 @@ public class CommandConfig [Description("Is the command enabled on this server ?")] public bool IsEnabled { get; set; } = false; - // Maybe one day... Commands load before singleton is created so i can get config + // Maybe one day... Commands load before singleton is created so i cant get config /*[Description("the prefix to be used by the players .afk or whatever you like")] public string Command { get; set; } = "afk"; @@ -73,20 +73,58 @@ public class CommandConfig [Description("The coldown of the command when using it")] public float Cooldown { get; set; } = 40f; + + [Description("The command can only be used by players who have a group that is on the list ?")] + public bool ExclusiveForGroups { get; set; } = false; + + [Description("List of groups.")] + public List UserGroupsAllowed { get; set; } = new() + { + "someGroup", + }; + + [Description("The command is disabled for certain RoleTypes?")] + public bool DisableForCertainRole { get; set; } = false; + + [Description("List of RoleTypes that cannot use the command")] + public List RoleTypeIdBlackList { get; set; } = new() + { + RoleTypeId.None, + }; + + public Responses Responses { get; set; } = new(); + } - [Description("The message given to the player attempting to use the command when it is disabled.")] - public string TextOnDisable {get; set;} = "This command is disabled"; - - [Description("The message that will be sent to the player when they are moved to spectator")] - public string TextOnSuccess { get; set; } = "You were moved to spectator because you considered yourself AFK."; - - [Description("The message that will be given to the player when trying to use the command when it has the effect of taking more than 2 candies.")] - public string TextOnSevereHands { get; set; } = $"You cannot use this command if you have no hands"; + public class Responses + { + [Description("Response given to the player when trying to use the command when it is disabled.")] + public string OnDisable { get; set; } = "This command is disabled"; - [Description("When a player attempts to use the command when it has the effect given by SCP-049")] - public string TextOnHearthAttack { get; set; } = $"You cannot use this command if you have a heart attack."; + [Description("Response given to the player when successfully executing the command.")] + public string OnSuccess { get; set; } = "You were moved to spectator because you considered yourself AFK."; + + [Description("Response given to the player when he has no hands")] + public string OnSevereHands { get; set; } = "You cannot use this command if you have no hands"; - [Description("When a player tries to use the command when he still has cooldown, {0} is the number of seconds the player has to wait.")] - public string TextOnCooldown { get; set; } = "You cannot use the command yet, you have to wait {0} seconds."; + [Description("Response given to the player when affected by Cardiact Arrest (Effect of SCP-049)")] + public string OnHearthAttack { get; set; } = "You cannot use this command if you have a heart attack."; + + [Description("Response given to the player when trying to use the command when in the pocket dimension.")] + public string OnPocketDimension { get; set; } = "There is no easy escape from the pocket dimension."; + + [Description("Response given to the player when he still has cooldown to use the command. {0} is the number of seconds the player has to wait.")] + public string OnCooldown { get; set; } = "You cannot use the command yet, you have to wait {0} seconds."; + + [Description("Response given when a player tries to use the command with a role in the blacklist")] + public string OnBlackListedRole { get; set; } = "You cannot use this command when you are {0}"; + + [Description("Response given to the player when not in the group list")] + public string OnGroupExclusive { get; set; } = "Your current group is not in the list of allowed groups."; + + [Description("Response given to the player when he tries to use the command when the round has not started.")] + public string OnRoundIsNotStarted { get; set; } = "The round has not started yet, you cannot use the command."; + + [Description("Response given to the player when trying to use the command while is dead.")] + public string OnPlayerIsDead { get; set; } = "You cannot use the command if you are dead."; } } \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 3809de3..5062131 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,7 +21,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.3", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.4", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 934b17fa2a48627990432c2f4eac6cc9486b46f9 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 30 Jan 2023 23:34:36 -0300 Subject: [PATCH 109/146] ups --- UltimateAFK/Command/AfkCommand.cs | 3 ++- UltimateAFK/UltimateAFK.cs | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index afa78c8..2c33dba 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -51,7 +51,8 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } if (UltimateAFK.Singleton.Config.CommandConfig.DisableForCertainRole && UltimateAFK.Singleton.Config.CommandConfig.RoleTypeIdBlackList.Contains(ply.Role)) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnBlackListedRole; + response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnBlackListedRole, + ply.Role); return false; } diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 5062131..f409b33 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -31,8 +31,6 @@ void OnEnabled() { Log.Warning($"You have enabled the AFK detector of the base game, please disable it by config &6afk_time = 0&r in &4config_gameplay.txt&r"); } - - //PluginAPI.Events.EventManager.RegisterEvents(this); } [PluginUnload] From 63745f7b58ce0152009aa2ab05d3f3f4921fb217 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 6 Feb 2023 13:03:58 -0300 Subject: [PATCH 110/146] 6.1.5 * Making sure the effect is active --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 4 ++-- UltimateAFK/UltimateAFK.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index ce51cd0..d744c4e 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.4 + 6.1.5 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 2c33dba..a75d2b6 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -62,12 +62,12 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnGroupExclusive; return false; } - if (ply.EffectsManager.TryGetEffect(out _)) + if (ply.EffectsManager.TryGetEffect(out var severedHands) && severedHands.IsEnabled) { response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnSevereHands; return false; } - if (ply.EffectsManager.TryGetEffect(out _)) + if (ply.EffectsManager.TryGetEffect(out var cardiacArrest) && cardiacArrest.IsEnabled) { response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnHearthAttack; return false; diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index f409b33..160a01e 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,7 +21,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.4", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.5", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From a721ff736b68caf9f37ad34f775ddbac3b9bba1d Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 7 Mar 2023 22:06:38 -0300 Subject: [PATCH 111/146] 6.1.6 * Now players in overwatch mode will be ignored to replace players using the .afk command (configurable). --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 15 ++++++++++----- UltimateAFK/Config.cs | 3 +++ UltimateAFK/UltimateAFK.cs | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index d744c4e..12bafa6 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.5 + 6.1.6 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index a75d2b6..23ce1b0 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using CommandSystem; using CustomPlayerEffects; using MapGeneration; @@ -12,6 +13,8 @@ using UltimateAFK.Handlers; using UltimateAFK.Resources; using UnityEngine; +using Utils.NonAllocLINQ; +using Random = UnityEngine.Random; namespace UltimateAFK.Command { @@ -175,15 +178,17 @@ private void GoAfk(Player ply, RoleTypeId roleType) private Player FindReplacement(Player afk) { + var players = new List(); foreach (var player in Player.GetPlayers()) { - if (player.IsAlive || player == afk || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server")) + if (player.IsAlive || player == afk || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") + || (UltimateAFK.Singleton.Config.CommandConfig.IgnoreOverwatch && player.IsOverwatchEnabled)) continue; - - return player; + + players.Add(player); } - - return null; + + return players.Any() ? players.ElementAtOrDefault(Random.Range(0, players.Count)) : null; } /// diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 2305839..34e0dd4 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -92,6 +92,9 @@ public class CommandConfig RoleTypeId.None, }; + [Description("Players who are in overwatch mode will not be considered replacements for AFKs.")] + public bool IgnoreOverwatch { get; set; } = true; + public Responses Responses { get; set; } = new(); } diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 160a01e..a4a24e1 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,7 +21,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.5", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.6", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 05c422987395caa476656e68d83488d6482dc78a Mon Sep 17 00:00:00 2001 From: SrLicht Date: Fri, 24 Mar 2023 18:54:29 -0300 Subject: [PATCH 112/146] 6.1.7 * Improve plugin logic --- .github/workflows/dotnet.yml | 52 --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 5 +- .../Handlers/Components/AfkComponent.cs | 104 +++-- .../Handlers/Components/OldAfkComponent.cs | 387 ------------------ UltimateAFK/UltimateAFK.cs | 2 +- 6 files changed, 67 insertions(+), 485 deletions(-) delete mode 100644 .github/workflows/dotnet.yml delete mode 100644 UltimateAFK/Handlers/Components/OldAfkComponent.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml deleted file mode 100644 index 05bccf4..0000000 --- a/.github/workflows/dotnet.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Build Plugin NWAPI - -on: - push: - branches: [ master ] - workflow_dispatch: - -env: - # Para la version 10.2 el link de referencias tiene que ser este https://www.exiled.host/build_deps/Master.zip - # Pero si es para la beta https://exiled.host/build_deps/Dev.zip - EXILED_REFERENCES_URL: https://www.exiled.host/build_deps/EA_REFS.zip - #EXILED_REFERENCES_PATH: ${{ github.workspace }}/References - SL_REFERENCES: ${{ github.workspace }}/References - UNITY_REFERENCES: ${{ github.workspace }}/References - -jobs: - - build: - - runs-on: windows-latest - - steps: - - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 6.0.x - - name: Setup Nuget - uses: nuget/setup-nuget@v1 - - - uses: actions/checkout@v2.3.4 - - - name: Get references - shell: pwsh - run: | - Invoke-WebRequest -Uri ${{ env.EXILED_REFERENCES_URL }} -OutFile ${{ github.workspace }}/References.zip - Expand-Archive -Path References.zip -DestinationPath ${{ env.SL_REFERENCES }} - Invoke-WebRequest -Uri "https://github.com/CedModV2/NWAPIPermissionSystem/releases/download/0.0.3/NWAPIPermissionSystem.dll" -OutFile ${{ env.SL_REFERENCES }}/NWAPIPermissionSystem.dll - - - name: Build - shell: pwsh - run: | - ./build.ps1 - $File = (Get-ChildItem -Path . -Include 'EXILED.*.nupkg' -Recurse).Name - Out-File -FilePath ${{ github.env }} -InputObject "PackageFile=$File" -Encoding utf-8 -Append - - - name: Upload artifacts - uses: actions/upload-artifact@v2 - with: - name: Build Results - path: bin/Debug - retention-days: 1 \ No newline at end of file diff --git a/Cerberus.props b/Cerberus.props index 12bafa6..8394c29 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.6 + 6.1.7 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 23ce1b0..0d534fd 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -100,7 +100,8 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } catch (Exception e) { - response = $"Error: {e} -- {e.Message}"; + Log.Error($"Error on {nameof(AfkCommand)}: {e}"); + response = $"Error: {e}"; return false; } } @@ -182,7 +183,7 @@ private Player FindReplacement(Player afk) foreach (var player in Player.GetPlayers()) { if (player.IsAlive || player == afk || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || (UltimateAFK.Singleton.Config.CommandConfig.IgnoreOverwatch && player.IsOverwatchEnabled)) + || (UltimateAFK.Singleton.Config.CommandConfig.IgnoreOverwatch && player.IsOverwatchEnabled) || player is null) continue; players.Add(player); diff --git a/UltimateAFK/Handlers/Components/AfkComponent.cs b/UltimateAFK/Handlers/Components/AfkComponent.cs index ebbdfa4..0b799e5 100644 --- a/UltimateAFK/Handlers/Components/AfkComponent.cs +++ b/UltimateAFK/Handlers/Components/AfkComponent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using MEC; using NWAPIPermissionSystem; using PlayerRoles; @@ -8,6 +9,7 @@ using PluginAPI.Core; using UltimateAFK.Resources; using UnityEngine; +using Random = UnityEngine.Random; namespace UltimateAFK.Handlers.Components { @@ -96,7 +98,7 @@ private void CheckAfk() OwnerLastPosition = ownerPosition; } // The player is not moving and is not SCP-096 with his TryToNotCry ability. - else if (!(Owner.Role == RoleTypeId.Scp096 && (Owner.ReferenceHub.roleManager.CurrentRole as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry))) + else if (!(Owner.Role == RoleTypeId.Scp096 && (Owner.RoleBase as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry))) { Log.Debug($"{Owner.Nickname} is in not moving, AFKTime: {_afkTime}", UltimateAFK.Singleton.Config.DebugMode); @@ -119,37 +121,7 @@ private void CheckAfk() } } - - #region API - public Player Owner { get; private set; } - - public RoleTypeId OwnerRoleType; - - public Vector3 OwnerLastPosition; - - public int AfkTimes { get; set; } - public bool IsKickEnabled { get; set; } = UltimateAFK.Singleton.Config.AfkCount > -1; - #endregion - - #region Private variables - - // Position in the world - private Vector3 _ownerPosition; - - // Player camera position - private Vector3 _cameraPosition; - - // Player camera rotation - private Quaternion _cameraRotation; - - // The time the player was afk - private float _afkTime; - - // Using a MEC Coroutine is more optimized than using Unity methods. - private CoroutineHandle _checkHandle; - #endregion - #region Private Methods /// @@ -232,19 +204,23 @@ private void Replace(Player ply, RoleTypeId roleType) // Check if AFK role is SCP-079 if (roleType is RoleTypeId.Scp079) { + Log.Debug($"Player {ply.Nickname} is Scp079, adding data to dictionary",UltimateAFK.Singleton.Config.DebugMode); //Adds the replacement player to the dictionary with all the necessary information AddData(ply, replacement, true); + Log.Debug($"Changing player {ply.Nickname} role to spectator", UltimateAFK.Singleton.Config.DebugMode); // Self-explanatory ply.SetRole(RoleTypeId.Spectator); if (IsKickEnabled) { + Log.Debug($"Kick is enabled, adding afk times to player {ply.Nickname}", UltimateAFK.Singleton.Config.DebugMode); AfkTimes++; - // Check if the player should be removed from the server for being too many times afk if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) { + Log.Debug($"The player {ply.Nickname} reached the maximum number of times he can be AFK, kicking from the server.", UltimateAFK.Singleton.Config.DebugMode); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); ply.Kick(UltimateAFK.Singleton.Config.MsgKick); @@ -258,21 +234,25 @@ private void Replace(Player ply, RoleTypeId roleType) ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); // Sends replacement to the role that had the afk replacement.SetRole(roleType); } else { + Log.Debug($"Player is not Scp079, adding data of player {ply.Nickname}", UltimateAFK.Singleton.Config.DebugMode); // Adds the replacement player to the dictionary with all the necessary information AddData(ply, replacement, false); if (IsKickEnabled) { + Log.Debug($"Kick is enabled, adding afk times to player {ply.Nickname}", UltimateAFK.Singleton.Config.DebugMode); AfkTimes++; - // Check if the player should be removed from the server for being too many times afk if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) { + Log.Debug($"The player {ply.Nickname} reached the maximum number of times he can be AFK, kicking from the server.", UltimateAFK.Singleton.Config.DebugMode); + ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); ply.Kick(UltimateAFK.Singleton.Config.MsgKick); @@ -282,21 +262,25 @@ private void Replace(Player ply, RoleTypeId roleType) } } + Log.Debug($"Cleaning player {ply.Nickname} inventory", UltimateAFK.Singleton.Config.DebugMode); // Clear player inventory ply.ClearInventory(); //Send player a broadcast for being too long afk ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + + Log.Debug($"Changing player {ply.Nickname} to spectator", UltimateAFK.Singleton.Config.DebugMode); // Sends player to spectator ply.SetRole(RoleTypeId.Spectator); // Sends replacement to the role that had the afk + Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); replacement.SetRole(roleType); } } } catch (Exception e) { - Log.Error($"Error on {nameof(Replace)}: IsOwnerNull: {Owner is null} || {e.Data} -- {e.StackTrace}"); + Log.Error($"Error on {nameof(Replace)}: IsOwnerNull: {Owner is null} || {e}"); } } @@ -305,15 +289,18 @@ private void Replace(Player ply, RoleTypeId roleType) /// private Player GetReplacement() { + var players = new List(); + foreach (var player in Player.GetPlayers()) { - if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server")) + if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") + || (UltimateAFK.Singleton.Config.CommandConfig.IgnoreOverwatch && player.IsOverwatchEnabled)) continue; - - return player; + + players.Add(player); } - return null; + return players.Any() ? players.ElementAtOrDefault(Random.Range(0, players.Count)) : null; } /// @@ -323,10 +310,16 @@ private void AddData(Player player, Player replacement, bool is079 = false) { try { + if (player is null || replacement is null) + { + Log.Error($"Error on {nameof(AddData)}: player or replacement is null"); + return; + } + if (is079) { if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetComponent(out Scp079TierManager tierManager) - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) { MainHandler.ReplacingPlayers.Add(replacement, new AFKData { @@ -369,15 +362,13 @@ private void AddData(Player player, Player replacement, bool is079 = false) } catch (Exception e) { - Log.Error($"Error on {nameof(AddData)}: {e.Data} -- {e.StackTrace}"); + Log.Error($"Error on {nameof(AddData)}: {e}"); } } /// /// Cache player's ammunition /// - /// - /// private Dictionary GetAmmo(Player player) { var result = new Dictionary(); @@ -392,5 +383,34 @@ private Dictionary GetAmmo(Player player) #endregion + #region API + public Player Owner { get; private set; } + + public RoleTypeId OwnerRoleType; + + public Vector3 OwnerLastPosition; + + public int AfkTimes { get; set; } + + public bool IsKickEnabled { get; set; } = UltimateAFK.Singleton.Config.AfkCount > -1; + #endregion + + #region Private variables + + // Position in the world + private Vector3 _ownerPosition; + + // Player camera position + private Vector3 _cameraPosition; + + // Player camera rotation + private Quaternion _cameraRotation; + + // The time the player was afk + private float _afkTime; + + // Using a MEC Coroutine is more optimized than using Unity methods. + private CoroutineHandle _checkHandle; + #endregion } } \ No newline at end of file diff --git a/UltimateAFK/Handlers/Components/OldAfkComponent.cs b/UltimateAFK/Handlers/Components/OldAfkComponent.cs deleted file mode 100644 index f49d9ea..0000000 --- a/UltimateAFK/Handlers/Components/OldAfkComponent.cs +++ /dev/null @@ -1,387 +0,0 @@ -using MEC; -using PluginAPI.Core; -using System; -using System.Collections.Generic; -using System.Linq; -using NWAPIPermissionSystem; -using PlayerRoles; -using PlayerRoles.PlayableScps.Scp079; -using PlayerRoles.PlayableScps.Scp096; -using PluginAPI.Core.Items; -using UltimateAFK.Resources; -using UnityEngine; - -namespace UltimateAFK.Handlers.Components -{ - /// - /// Component that performs a constant afk check. - /// - public class OldAfkComponent : MonoBehaviour - { - /// - /// The player who owns this component. - /// - public Player Owner { get; private set; } - - // Position in the world - private Vector3 _ownerPosition; - - // Player camera position - private Vector3 _cameraPosition; - - // Player camera rotation - private Quaternion _cameraRotation; - - // The time the player was afk - private float _afkTime; - - /// - /// Number of times a player was detected as AFK - /// - public int AfkCount { get; private set; } - - // Using a MEC Coroutine is more optimized than using Unity methods. - private CoroutineHandle _checkHandle; - - private void Awake() - { - if (!(Player.Get(gameObject) is Player ply)) - { - Log.Error($"{this} Error Getting Player"); - - Destroy(); - return; - } - - Owner = ply; - - // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. - _checkHandle = Timing.RunCoroutine(CheckAfkPerSecond().CancelWith(this).CancelWith(gameObject)); - Log.Debug($"Component full loaded Owner: {Owner.Nickname} ({Owner.UserId})", UltimateAFK.Singleton.Config.DebugMode); - } - - /// - /// Destroys the component and clears the variables - /// - public void Destroy() - { - try - { - Log.Debug($"Calling Destroy", UltimateAFK.Singleton.Config.DebugMode); - - if (Owner is null) - Log.Debug("When trying to destroy the component, owner is null", UltimateAFK.Singleton.Config.DebugMode); - - Timing.KillCoroutines(_checkHandle); - - Destroy(this); - } - catch (Exception e) - { - Log.Error($"Error while trying to destroy {this} {e}"); - throw; - } - } - - private IEnumerator CheckAfkPerSecond() - { - for (;;) - { - yield return Timing.WaitForSeconds(1.3f); - - Log.Debug("Calling CheckAFK", UltimateAFK.Singleton.Config.DebugMode && UltimateAFK.Singleton.Config.SpamLogs); - - try - { - CheckAfk(); - } - catch (Exception e) - { - Log.Error($"{this} error on CheckAFK: {e} || {e.StackTrace}"); - } - } - } - - private void CheckAfk() - { - if (!Continue(Owner)) - return; - - if (Owner == null) - { - Log.Error($"{nameof(CheckAfk)}: Player is null"); - Destroy(); - return; - } - - - var position = Owner.Position; - // Yes.. Player.Camera does not change if you are playing a SCP that moves with the cameras :)))))))) - var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Camera.position; - var cameraRotation = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp0792 ? new Quaternion(scp0792.CurrentCamera.HorizontalRotation, scp0792.CurrentCamera.VerticalRotation, scp0792.CurrentCamera.RollRotation, 0f ) : Owner.Camera.rotation; - - // Player is moving - if (cameraPosition != _cameraPosition || position != _ownerPosition || _cameraRotation != cameraRotation) - { - _cameraPosition = cameraPosition; - _cameraRotation = cameraRotation; - _ownerPosition = position; - _afkTime = 0f; - } - // The player is not moving and is not SCP-096 with his TryToNotCry ability. - else if (!(Owner.Role == RoleTypeId.Scp096 && (Owner.ReferenceHub.roleManager.CurrentRole as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry))) - { - Log.Debug($"{Owner.Nickname} is in not moving, AFKTime: {_afkTime}", UltimateAFK.Singleton.Config.DebugMode); - - if(_afkTime++ < UltimateAFK.Singleton.Config.AfkTime) return; - - var graceNumb = UltimateAFK.Singleton.Config.GraceTime - (_afkTime - UltimateAFK.Singleton.Config.AfkTime); - - if (graceNumb > 0) - { - // The player is in grace time, so let's warn him that he has been afk for too long. - Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, - shouldClearPrevious: true); - } - else - { - Log.Info($"{Owner.Nickname} ({Owner.UserId}) Detected as AFK"); - - _afkTime = 0f; - Replace(Owner, Owner.Role); - } - } - } - - /// - /// Performs player replacement. - /// - /// Player to be replaced - /// This replacement happens when the player is disconnected from the server ? - public void Replace(Player player, RoleTypeId role) - { - // Check if role is blacklisted - if (UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(role)) - { - Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); - player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); - - if (UltimateAFK.Singleton.Config.AfkCount != -1) - { - AfkCount++; - - if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) - { - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - player.Kick(UltimateAFK.Singleton.Config.MsgKick); - return; - } - } - - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - return; - } - - // Get player replacement - Player replacement = GetReplacement(); - - // If replacement is null - if (replacement == null) - { - Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); - - player.SetRole(RoleTypeId.Spectator); - - if (UltimateAFK.Singleton.Config.AfkCount != -1) - { - AfkCount++; - - if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) - { - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - - player.Kick(UltimateAFK.Singleton.Config.MsgKick); - - return; - } - } - - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - - return; - } - - // is not - Log.Debug($"Replacement Player found Nickname: {replacement.Nickname} UserID: {replacement.UserId}", UltimateAFK.Singleton.Config.DebugMode); - - // Check if role is SCP-079 to be able to pass the level and energy - if (role == RoleTypeId.Scp079) - { - //Adds the replacement player to the dictionary with all the necessary information - AddData(player, replacement, true); - - // Self-explanatory - player.SetRole(RoleTypeId.Spectator); - - if (UltimateAFK.Singleton.Config.AfkCount != -1) - { - AfkCount++; - - // Check if the player should be removed from the server for being too many times afk - if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) - { - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - - player.Kick(UltimateAFK.Singleton.Config.MsgKick); - - replacement.SetRole(player.Role); - return; - } - } - - //Send player a broadcast for being too long afk - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - - // Sends replacement to the role that had the afk - replacement.SetRole(role); - } - else - { - // Adds the replacement player to the dictionary with all the necessary information - AddData(player, replacement, false); - - if (UltimateAFK.Singleton.Config.AfkCount != -1) - { - AfkCount++; - - // Check if the player should be removed from the server for being too many times afk - if (AfkCount >= UltimateAFK.Singleton.Config.AfkCount) - { - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - - player.Kick(UltimateAFK.Singleton.Config.MsgKick); - - replacement.SetRole(player.Role); - return; - } - } - - try - { - // Clear player inventory - player.ClearInventory(); - //Send player a broadcast for being too long afk - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - // Sends player to spectator - player.SetRole(RoleTypeId.Spectator); - // Sends replacement to the role that had the afk - replacement.SetRole(role); - } - catch (Exception e) - { - Log.Error($"Error on {nameof(Replace)}: {e} -- {e.StackTrace} || player is null? {player is null}", "Ultimate-AFK"); - } - } - } - - /// - /// Add player data to ReplacingPlayers dictionary. - /// - private void AddData(Player player, Player replacement, bool is079 = false) - { - if (is079) - { - Scp079Role scp079Role; - // This if is horrendous but I must get the subroutines from the player afk. - if ((scp079Role = player.ReferenceHub.roleManager.CurrentRole as Scp079Role) != null && - scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager scp079TierManager) && - scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager scp079AuxManager)) - { - MainHandler.ReplacingPlayers.Add(replacement, new AFKData - { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - // same has do ammo = null xd - Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Role = scp079Role, - Energy = scp079AuxManager.CurrentAux, - Experience = scp079TierManager.TotalExp, - } - }); - } - return; - } - - // If I make Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo for some reason it gets buggy and ammo becomes null when changing the player to spectator. - // So I create a temporary dictionary stored in cache (ram) and then clean the information by deleting it from the ReplacingPlayers. - var ammo = GetAmmo(player); - MainHandler.ReplacingPlayers.Add(replacement, new AFKData - { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = ammo, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Role = null, - Energy = 0f, - Experience = 0 - } - }); - - } - - /// - /// NORTHWOOD FIX AMMO INVENTORY NOW! - /// - private Dictionary GetAmmo(Player player) - { - var result = new Dictionary(); - - foreach (var ammo in player.ReferenceHub.inventory.UserInventory.ReserveAmmo) - { - result.Add(ammo.Key, ammo.Value); - } - - return result; - } - - /// - /// Obtains a player who qualifies for replacement. - /// - private Player GetReplacement() - { - foreach (var player in Player.GetPlayers()) - { - if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server")) - continue; - - return player; - } - - return null; - } - - /// - /// Check if the player is alive, if the round has started, if the players on the server meet the requirements for check afk to work, if the player is tutorial and the configuration allows the tutorial to be detected as afk. - /// - /// True if all requirements are met - private bool Continue(Player ply) - { - return ply.IsAlive && Round.IsRoundStarted && Player.Count >= UltimateAFK.Singleton.Config.MinPlayers && - (Owner.Role != RoleTypeId.Tutorial || !UltimateAFK.Singleton.Config.IgnoreTut); - } - } -} \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index a4a24e1..a30fe14 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,7 +21,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.6", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.7", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 9aa2f7aa3cf01477351d9672afad34f112acaa8d Mon Sep 17 00:00:00 2001 From: SrLicht Date: Fri, 24 Mar 2023 19:05:36 -0300 Subject: [PATCH 113/146] i forgot to pull this (6.1.8) --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 2 +- UltimateAFK/Config.cs | 17 +++-------------- UltimateAFK/Handlers/Components/AfkComponent.cs | 2 +- UltimateAFK/UltimateAFK.cs | 2 +- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index 8394c29..ab80cc3 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.7 + 6.1.8 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 0d534fd..3a67661 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -183,7 +183,7 @@ private Player FindReplacement(Player afk) foreach (var player in Player.GetPlayers()) { if (player.IsAlive || player == afk || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || (UltimateAFK.Singleton.Config.CommandConfig.IgnoreOverwatch && player.IsOverwatchEnabled) || player is null) + || (UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled)) continue; players.Add(player); diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 34e0dd4..2d13c49 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -23,6 +23,9 @@ public class Config [Description("Tutorials should be ignored ?")] public bool IgnoreTut { get; set; } = true; + + [Description("Players in overwatch mode will be ignored for replace a afk player")] + public bool IgnoreOverwatch { get; set; } = true; [Description("RoleTypes on this list will not be replaced by other players")] public List RoleTypeBlacklist { get; set; } = new() { RoleTypeId.Scp0492 }; @@ -57,17 +60,6 @@ public class CommandConfig [Description("Is the command enabled on this server ?")] public bool IsEnabled { get; set; } = false; - // Maybe one day... Commands load before singleton is created so i cant get config - /*[Description("the prefix to be used by the players .afk or whatever you like")] - public string Command { get; set; } = "afk"; - - [Description("I recommend you not to leave this empty since a bug crashes the server if you register commands with empty aliases.")] - public string[] Aliases { get; set; } = new[] { "uafk" }; - - [Description("The command description")] - public string Description { get; set; } = "By using this command you will be moved to spectator and if the server allows it a player will replace you."; - */ - [Description("Players who use this command will be replaced by players who are in spectator")] public bool Replace { get; set; } = true; @@ -92,9 +84,6 @@ public class CommandConfig RoleTypeId.None, }; - [Description("Players who are in overwatch mode will not be considered replacements for AFKs.")] - public bool IgnoreOverwatch { get; set; } = true; - public Responses Responses { get; set; } = new(); } diff --git a/UltimateAFK/Handlers/Components/AfkComponent.cs b/UltimateAFK/Handlers/Components/AfkComponent.cs index 0b799e5..a7efd03 100644 --- a/UltimateAFK/Handlers/Components/AfkComponent.cs +++ b/UltimateAFK/Handlers/Components/AfkComponent.cs @@ -294,7 +294,7 @@ private Player GetReplacement() foreach (var player in Player.GetPlayers()) { if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || (UltimateAFK.Singleton.Config.CommandConfig.IgnoreOverwatch && player.IsOverwatchEnabled)) + || (UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled)) continue; players.Add(player); diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index a30fe14..4f8de73 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,7 +21,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.7", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.8", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 1506528613326501958b90986e8e83fe02572c32 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 10 Apr 2023 21:14:04 -0300 Subject: [PATCH 114/146] 6.1.9 * Fixing pocked dimension check --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 2 +- UltimateAFK/UltimateAFK.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index ab80cc3..50e9adf 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.8 + 6.1.9 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 3a67661..12c6551 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -47,7 +47,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPlayerIsDead; return false; } - if (ply.Zone == FacilityZone.Other) + if (ply.Zone == FacilityZone.Other || ply.EffectsManager.GetEffect().IsEnabled) { response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPocketDimension; return false; diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 4f8de73..43beeb5 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,7 +21,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.8", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.1.9", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 4b41c314210f58835486497ae7a18778178721f2 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 15 May 2023 19:06:37 -0300 Subject: [PATCH 115/146] 6.2.0 * Now the dictionary with the afk data saves the UserId instead of the player (allowing for faster checks). * Re-Made the AFK component, I needed it. * Updated to version of SCP.SL 13.0 * Fixed a bug where replacing SCP-079 would not transfer Experience --- UltimateAFK/Command/AfkCommand.cs | 219 +++++---- .../Handlers/Components/AfkComponent.cs | 416 ------------------ UltimateAFK/Handlers/MainHandler.cs | 138 +++--- .../Resources/Component/AFKComponent.cs | 304 +++++++++++++ UltimateAFK/Resources/Extensions.cs | 15 +- UltimateAFK/UltimateAFK.cs | 13 +- UltimateAFK/UltimateAFK.csproj | 1 + 7 files changed, 494 insertions(+), 612 deletions(-) delete mode 100644 UltimateAFK/Handlers/Components/AfkComponent.cs create mode 100644 UltimateAFK/Resources/Component/AFKComponent.cs diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 12c6551..e28f912 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -23,9 +23,11 @@ public class AfkCommand : ICommand { public string Command { get; } = "afk"; public string[] Aliases { get; } - public string Description { get; } = "By using this command you will be moved to spectator and if the server allows it a player will replace you."; + public string Description { get; } = "By using this command you will be moved to spectator and if the server allows it a player will replace you."; + public Dictionary InCooldown = new(); - + private readonly UltimateAFK Plugin = UltimateAFK.Singleton; + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) { try @@ -92,8 +94,8 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s InCooldown.Remove(ply.UserId); } } - - GoAfk(ply, ply.Role); + + Replace(ply, ply.Role); InCooldown.Add(ply.UserId, Time.time + UltimateAFK.Singleton.Config.CommandConfig.Cooldown); response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnSuccess; return true; @@ -106,84 +108,67 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } } - private void GoAfk(Player ply, RoleTypeId roleType) + + private void Replace(Player player, RoleTypeId roleType) { - // Check if role is blacklisted - if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Count > 0 && UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) - { - Log.Debug($"In the command | player {ply.Nickname} ({ply.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); - - ply.ClearInventory(); - ply.SetRole(RoleTypeId.Spectator); - - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - return; - } - - // Get player replacement - Player replacement = null; + // Check if role is blacklisted + if (Plugin.Config.RoleTypeBlacklist?.Count > 0 && Plugin.Config.RoleTypeBlacklist.Contains(roleType)) + { + Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", Plugin.Config.DebugMode); - if (UltimateAFK.Singleton.Config.CommandConfig.Replace) - replacement = FindReplacement(ply); - - // If replacement is null - if (replacement is null) - { - Log.Debug("In the command | Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); - - ply.ClearInventory(); - ply.SetRole(RoleTypeId.Spectator); + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - } - else - { - // if not - Log.Debug($"In the command | Replacement Player found: {replacement.Nickname} ({replacement.UserId})", UltimateAFK.Singleton.Config.DebugMode); + player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + return; + } - // Check if AFK role is SCP-079 - if (roleType is RoleTypeId.Scp079) - { - //Adds the replacement player to the dictionary with all the necessary information - AddData(ply, replacement, true); - - // Self-explanatory - ply.SetRole(RoleTypeId.Spectator); - - //Send player a broadcast for being too long afk - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - - // Sends replacement to the role that had the afk - replacement.SetRole(roleType); - } - else - { - // Adds the replacement player to the dictionary with all the necessary information - AddData(ply, replacement, false); - - // Clear player inventory - ply.ClearInventory(); - //Send player a broadcast for being too long afk - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - // Sends player to spectator - ply.SetRole(RoleTypeId.Spectator); - // Sends replacement to the role that had the afk - replacement.SetRole(roleType); - } - } + // Get player replacement + Player replacement = FindReplacement(player.UserId); + + // If no replacement player is found, I change the player's role to spectator + if (replacement == null) + { + Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + } + else + { + Log.Debug($"Replacement Player found: {replacement.Nickname} ({replacement.UserId})", Plugin.Config.DebugMode); + Log.Debug($"Saving data of player {player.Nickname} in the dictionary.", Plugin.Config.DebugMode); + + SaveData(player, replacement.UserId, roleType == RoleTypeId.Scp079); + + Log.Debug($"Cleaning player {player.Nickname} inventory", Plugin.Config.DebugMode); + // Clear player inventory + player.ClearInventory(); + //Send player a broadcast for being too long afk + player.SendBroadcast(Plugin.Config.MsgFspec, 25, shouldClearPrevious: true); + player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + + Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); + // Sends player to spectator + player.SetRole(RoleTypeId.Spectator); + // Sends replacement to the role that had the afk + Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", Plugin.Config.DebugMode); + replacement.SetRole(roleType); + + } } - private Player FindReplacement(Player afk) + private Player FindReplacement(string afkUserId) { var players = new List(); foreach (var player in Player.GetPlayers()) { - if (player.IsAlive || player == afk || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || (UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled)) + if (player.IsAlive || player.UserId == afkUserId || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") + || UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) continue; players.Add(player); @@ -191,62 +176,56 @@ private Player FindReplacement(Player afk) return players.Any() ? players.ElementAtOrDefault(Random.Range(0, players.Count)) : null; } - - /// - /// Add player data to ReplacingPlayers dictionary. - /// - private void AddData(Player player, Player replacement, bool is079 = false) + + private void SaveData(Player player, string replacementUserId, bool isScp079 = false) { - try + if (isScp079) { - if (is079) + if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) { - if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetComponent(out Scp079TierManager tierManager) - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + + var afkData = new AFKData() { - MainHandler.ReplacingPlayers.Add(replacement, new AFKData + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = null, + Health = player.Health, + Items = null, + SCP079 = new Scp079Data { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = null, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Role = scp079Role, - Energy = energyManager.CurrentAux, - Experience = tierManager.TotalExp, - } - }); - } - - return; + Role = scp079Role, + Energy = energyManager.CurrentAux, + Experience = tierManager.TotalExp, + } + }; + + MainHandler.ReplacingPlayersData.Add(replacementUserId, afkData); } - - // If I make Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo for some reason it gets buggy and ammo becomes null when changing the player to spectator. - // So I create a temporary dictionary stored in cache (ram) and then clean the information by deleting it from the ReplacingPlayers. - var ammo = GetAmmo(player); - MainHandler.ReplacingPlayers.Add(replacement, new AFKData - { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = ammo, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Role = null, - Energy = 0f, - Experience = 0 - } - }); + + return; } - catch (Exception e) + + var ammo = Extensions.GetAmmo(player); + + var data = new AFKData() { - Log.Error($"Error on {nameof(AddData)}: {e.Data} -- {e.StackTrace}"); - } + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = ammo, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data + { + Role = null, + Energy = 0f, + Experience = 0 + } + }; + + MainHandler.ReplacingPlayersData.Add(replacementUserId, data); } /// diff --git a/UltimateAFK/Handlers/Components/AfkComponent.cs b/UltimateAFK/Handlers/Components/AfkComponent.cs deleted file mode 100644 index a7efd03..0000000 --- a/UltimateAFK/Handlers/Components/AfkComponent.cs +++ /dev/null @@ -1,416 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using MEC; -using NWAPIPermissionSystem; -using PlayerRoles; -using PlayerRoles.PlayableScps.Scp079; -using PlayerRoles.PlayableScps.Scp096; -using PluginAPI.Core; -using UltimateAFK.Resources; -using UnityEngine; -using Random = UnityEngine.Random; - -namespace UltimateAFK.Handlers.Components -{ - /// - /// Component that performs a constant afk check. - /// - [RequireComponent(typeof(ReferenceHub))] - public class AfkComponent : MonoBehaviour - { - private void Awake() - { - if (Player.Get(gameObject) is not { } ply) - { - Log.Error($"{this} Error Getting Player"); - Destroy(this); - return; - } - - - Owner = ply; - // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. - _checkHandle = Timing.RunCoroutine(Check().CancelWith(this).CancelWith(gameObject)); - Log.Debug($"Component full loaded Owner: {Owner.Nickname} ({Owner.UserId})", UltimateAFK.Singleton.Config.DebugMode); - } - - private void OnDestroy() - { - Log.Debug($"Calling OnDestroy", UltimateAFK.Singleton.Config.DebugMode); - - if (Owner is null) - Log.Debug("Owner was null at the time of destroying the component", UltimateAFK.Singleton.Config.DebugMode); - - Timing.KillCoroutines(_checkHandle); - } - - /// - /// Public method to destroy the component from the outside. - /// - public void Destroy() - { - Destroy(this); - } - - private IEnumerator Check() - { - for (;;) - { - yield return Timing.WaitForSeconds(1.3f); - - Log.Debug("Calling CheckAFK", UltimateAFK.Singleton.Config.DebugMode && UltimateAFK.Singleton.Config.SpamLogs); - try - { - CheckAfk(); - } - catch (Exception e) - { - Log.Error($"Error in {nameof(Check)}: &2{e.TargetSite}&r || {e.StackTrace}"); - } - } - } - - private void CheckAfk() - { - if(!Continue(Owner)) return; - - if (Owner == null) - { - Log.Error($"{nameof(CheckAfk)}: Player is null"); - Destroy(this); - return; - } - - var ownerPosition = Owner.Position; - var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Camera.position; - var cameraRotation = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp0792 ? new Quaternion(scp0792.CurrentCamera.HorizontalRotation, scp0792.CurrentCamera.VerticalRotation, scp0792.CurrentCamera.RollRotation, 0f ) : Owner.Camera.rotation; - - // Set OwnerRoleType - OwnerRoleType = Owner.Role; - // Player is moving - if (cameraPosition != _cameraPosition || ownerPosition != _ownerPosition || _cameraRotation != cameraRotation) - { - _cameraPosition = cameraPosition; - _cameraRotation = cameraRotation; - _ownerPosition = ownerPosition; - _afkTime = 0f; - OwnerLastPosition = ownerPosition; - } - // The player is not moving and is not SCP-096 with his TryToNotCry ability. - else if (!(Owner.Role == RoleTypeId.Scp096 && (Owner.RoleBase as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry))) - { - Log.Debug($"{Owner.Nickname} is in not moving, AFKTime: {_afkTime}", UltimateAFK.Singleton.Config.DebugMode); - - if(_afkTime++ < UltimateAFK.Singleton.Config.AfkTime) return; - - var graceNumb = UltimateAFK.Singleton.Config.GraceTime - (_afkTime - UltimateAFK.Singleton.Config.AfkTime); - - if (graceNumb > 0) - { - // The player is in grace time, so let's warn him that he has been afk for too long. - Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, - shouldClearPrevious: true); - } - else - { - Log.Info($"{Owner.Nickname} ({Owner.UserId}) Detected as AFK"); - _afkTime = 0f; - Replace(Owner, Owner.Role); - } - } - - } - - #region Private Methods - - /// - /// Check if the player is alive, if the round has started, if the players on the server meet the requirements for check afk to work, if the player is tutorial and the configuration allows the tutorial to be detected as afk. - /// - /// True if all requirements are met - private bool Continue(Player ply) - { - return ply.IsAlive && Round.IsRoundStarted && Player.Count >= UltimateAFK.Singleton.Config.MinPlayers && - (Owner.Role != RoleTypeId.Tutorial || !UltimateAFK.Singleton.Config.IgnoreTut); - } - - /// - /// Perform player replacement. - /// - /// player to replace - /// of the player afk - private void Replace(Player ply, RoleTypeId roleType) - { - try - { - // Check if role is blacklisted - if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Count > 0 && UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) - { - Log.Debug($"player {ply.Nickname} ({ply.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); - - ply.ClearInventory(); - ply.SetRole(RoleTypeId.Spectator); - - if (IsKickEnabled) - { - AfkTimes++; - - if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) - { - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - ply.Kick(UltimateAFK.Singleton.Config.MsgKick); - return; - } - } - - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - return; - } - - // Get player replacement - Player replacement = GetReplacement(); - - // If replacement is null - if (replacement is null) - { - Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); - - ply.ClearInventory(); - ply.SetRole(RoleTypeId.Spectator); - - if (IsKickEnabled) - { - AfkTimes++; - - if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) - { - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - - ply.Kick(UltimateAFK.Singleton.Config.MsgKick); - - return; - } - } - - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - } - else - { - // if not - Log.Debug($"Replacement Player found: {replacement.Nickname} ({replacement.UserId})", UltimateAFK.Singleton.Config.DebugMode); - - // Check if AFK role is SCP-079 - if (roleType is RoleTypeId.Scp079) - { - Log.Debug($"Player {ply.Nickname} is Scp079, adding data to dictionary",UltimateAFK.Singleton.Config.DebugMode); - //Adds the replacement player to the dictionary with all the necessary information - AddData(ply, replacement, true); - - Log.Debug($"Changing player {ply.Nickname} role to spectator", UltimateAFK.Singleton.Config.DebugMode); - // Self-explanatory - ply.SetRole(RoleTypeId.Spectator); - - if (IsKickEnabled) - { - Log.Debug($"Kick is enabled, adding afk times to player {ply.Nickname}", UltimateAFK.Singleton.Config.DebugMode); - AfkTimes++; - // Check if the player should be removed from the server for being too many times afk - if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) - { - Log.Debug($"The player {ply.Nickname} reached the maximum number of times he can be AFK, kicking from the server.", UltimateAFK.Singleton.Config.DebugMode); - - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - - ply.Kick(UltimateAFK.Singleton.Config.MsgKick); - - replacement.SetRole(roleType); - return; - } - } - - //Send player a broadcast for being too long afk - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - - Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); - // Sends replacement to the role that had the afk - replacement.SetRole(roleType); - } - else - { - Log.Debug($"Player is not Scp079, adding data of player {ply.Nickname}", UltimateAFK.Singleton.Config.DebugMode); - // Adds the replacement player to the dictionary with all the necessary information - AddData(ply, replacement, false); - - if (IsKickEnabled) - { - Log.Debug($"Kick is enabled, adding afk times to player {ply.Nickname}", UltimateAFK.Singleton.Config.DebugMode); - AfkTimes++; - // Check if the player should be removed from the server for being too many times afk - if (AfkTimes >= UltimateAFK.Singleton.Config.AfkCount) - { - Log.Debug($"The player {ply.Nickname} reached the maximum number of times he can be AFK, kicking from the server.", UltimateAFK.Singleton.Config.DebugMode); - - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgKick, "white"); - - ply.Kick(UltimateAFK.Singleton.Config.MsgKick); - - replacement.SetRole(roleType); - return; - } - } - - Log.Debug($"Cleaning player {ply.Nickname} inventory", UltimateAFK.Singleton.Config.DebugMode); - // Clear player inventory - ply.ClearInventory(); - //Send player a broadcast for being too long afk - ply.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); - ply.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - - Log.Debug($"Changing player {ply.Nickname} to spectator", UltimateAFK.Singleton.Config.DebugMode); - // Sends player to spectator - ply.SetRole(RoleTypeId.Spectator); - // Sends replacement to the role that had the afk - Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); - replacement.SetRole(roleType); - } - } - } - catch (Exception e) - { - Log.Error($"Error on {nameof(Replace)}: IsOwnerNull: {Owner is null} || {e}"); - } - } - - /// - /// Obtains a player who qualifies for replacement. - /// - private Player GetReplacement() - { - var players = new List(); - - foreach (var player in Player.GetPlayers()) - { - if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || (UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled)) - continue; - - players.Add(player); - } - - return players.Any() ? players.ElementAtOrDefault(Random.Range(0, players.Count)) : null; - } - - /// - /// Add player data to ReplacingPlayers dictionary. - /// - private void AddData(Player player, Player replacement, bool is079 = false) - { - try - { - if (player is null || replacement is null) - { - Log.Error($"Error on {nameof(AddData)}: player or replacement is null"); - return; - } - - if (is079) - { - if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetComponent(out Scp079TierManager tierManager) - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) - { - MainHandler.ReplacingPlayers.Add(replacement, new AFKData - { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = null, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Role = scp079Role, - Energy = energyManager.CurrentAux, - Experience = tierManager.TotalExp, - } - }); - } - - return; - } - - // If I make Ammo = player.ReferenceHub.inventory.UserInventory.ReserveAmmo for some reason it gets buggy and ammo becomes null when changing the player to spectator. - // So I create a temporary dictionary stored in cache (ram) and then clean the information by deleting it from the ReplacingPlayers. - var ammo = GetAmmo(player); - MainHandler.ReplacingPlayers.Add(replacement, new AFKData - { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = ammo, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Role = null, - Energy = 0f, - Experience = 0 - } - }); - } - catch (Exception e) - { - Log.Error($"Error on {nameof(AddData)}: {e}"); - } - } - - /// - /// Cache player's ammunition - /// - private Dictionary GetAmmo(Player player) - { - var result = new Dictionary(); - - foreach (var ammo in player.ReferenceHub.inventory.UserInventory.ReserveAmmo) - { - result.Add(ammo.Key, ammo.Value); - } - - return result; - } - - #endregion - - #region API - public Player Owner { get; private set; } - - public RoleTypeId OwnerRoleType; - - public Vector3 OwnerLastPosition; - - public int AfkTimes { get; set; } - - public bool IsKickEnabled { get; set; } = UltimateAFK.Singleton.Config.AfkCount > -1; - #endregion - - #region Private variables - - // Position in the world - private Vector3 _ownerPosition; - - // Player camera position - private Vector3 _cameraPosition; - - // Player camera rotation - private Quaternion _cameraRotation; - - // The time the player was afk - private float _afkTime; - - // Using a MEC Coroutine is more optimized than using Unity methods. - private CoroutineHandle _checkHandle; - #endregion - } -} \ No newline at end of file diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index f0a8f9c..42ed97c 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -6,9 +6,8 @@ using PlayerStatsSystem; using PluginAPI.Core.Attributes; using PluginAPI.Enums; -using UltimateAFK.Handlers.Components; using UltimateAFK.Resources; -using UnityEngine; +using UltimateAFK.Resources.Component; namespace UltimateAFK.Handlers { @@ -31,7 +30,7 @@ public MainHandler(UltimateAFK plugin) /// /// A dictionary where replacement players are stored to give them the stats and items of the original player. /// - public static Dictionary ReplacingPlayers = new(); + public static Dictionary ReplacingPlayersData = new(); /// /// When a player joins I give him the component. @@ -42,9 +41,9 @@ private void OnPlayerJoin(Player player) { if(!Plugin.Config.IsEnabled || player.UserId.Contains("@server")) return; - Log.Debug($"Adding the Component to {player.Nickname} | Player already have component: {player.GameObject.TryGetComponent(out _)}", UltimateAFK.Singleton.Config.DebugMode); + Log.Debug($"Adding the Component to {player.Nickname}", Plugin.Config.DebugMode); - player.GameObject.AddComponent(); + player.GameObject.AddComponent(); } /// @@ -55,87 +54,85 @@ private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId ne { try { - if (player == null || !ReplacingPlayers.TryGetValue(player, out var data) || - !player.GameObject.TryGetComponent(out var _)) + if (player == null || newRole == RoleTypeId.Spectator || !ReplacingPlayersData.TryGetValue(player.UserId, out var data)) return; - Log.Debug($"Detecting player {player.Nickname} ({player.UserId}) who replaced a player who was afk", UltimateAFK.Singleton.Config.DebugMode); + Log.Debug($"Detecting player {player.Nickname} ({player.UserId}) who replaced a player {data.NickName} who was afk", UltimateAFK.Singleton.Config.DebugMode); - Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => GiveAfkData(player, data, newRole)); + Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => GiveData(player, data, newRole)); } catch (System.Exception e) { - Log.Error($"Error on {GetType().Name} (OnChangingRole) || {e} {e.StackTrace}"); + Log.Error($"{nameof(OnChangingRole)}: {e}"); } } - /// - /// Performs the change of stats and items to the replacement player - /// - private void GiveAfkData(Player ply, AFKData data, RoleTypeId newRole) + private void GiveData(Player player, AFKData data, RoleTypeId roleType) { - Log.Debug($"Replacing player is {ply.Nickname} ({ply.UserId}) new role is {newRole}", Plugin.Config.DebugMode); - - if (newRole == RoleTypeId.Scp079) + try { - Log.Debug("The new role is a SCP079, transferring energy and experience.", UltimateAFK.Singleton.Config.DebugMode); - if (ply.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + Log.Debug($"Replacing player is {player.Nickname} ({player.UserId}) new role is {roleType}", Plugin.Config.DebugMode); + + if (roleType == RoleTypeId.Scp079) { - ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); - ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); - - tierManager.ServerGrantExperience(data.SCP079.Experience, Scp079HudTranslation.Experience); - energyManager.CurrentAux = data.SCP079.Energy; - ReplacingPlayers.Remove(ply); - - Log.Debug($"Energy and experience transferred to the player", UltimateAFK.Singleton.Config.DebugMode); + Log.Debug("The new role is a SCP-079, transferring energy and experience.", Plugin.Config.DebugMode); + + if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + { + player.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); + player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); + + tierManager.TotalExp = data.SCP079.Experience; + energyManager.CurrentAux = data.SCP079.Energy; + ReplacingPlayersData.Remove(player.UserId); + + Log.Debug($"Energy and experience transferred to the player", UltimateAFK.Singleton.Config.DebugMode); + } + else + { + Log.Error($"Error transferring experience and level to the replacement player, Player.RoleBase is not Scp079 or there was an error obtaining the subroutines."); + } } else { - Log.Error($"Error transferring experience and level to the replacement player, Player.RoleBase is not Scp079 or there was an error obtaining the subroutines."); + // Clear default role inventory. + player.ClearInventory(); + // I add the ammunition first since it is the slowest thing to be done. + player.SendAmmo(data.Ammo); + + // This call delayed is necessary. + Timing.CallDelayed(0.3f, () => + { + // Teleport the player to the afk position, please don't fall off the map. + player.Position = data.Position; + // Set player health to afk health. + player.Health = data.Health; + // Give afk items + player.SendItems(data.Items); + // I apply the modifications of the replacement player not of the afk, I could do it but I sincerely prefer this method. + player.ApplyAttachments(); + // I refill the ammunition of the weapons, since it is annoying to appear without a loaded weapon. + player.ReloadAllWeapons(); + // Send the broadcast to the player + player.SendBroadcast(string.Format(Plugin.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); + player.SendConsoleMessage(string.Format(Plugin.Config.MsgReplace, data.NickName), "white"); + + ReplacingPlayersData.Remove(player.UserId); + }); } - - Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Singleton.Config.DebugMode); - ReplacingPlayers.Remove(ply); } - else + catch (System.Exception e) { - Log.Debug("Clearing replacement player inventory", Plugin.Config.DebugMode); - ply.ClearInventory(); - Log.Debug($"Adding Ammo to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); - // I add the ammunition first since it is the slowest thing to be done. - ply.SendAmmo(data.Ammo); - - // This call delayed is necessary. - Timing.CallDelayed(0.1f, () => - { - Log.Debug($"Changing player {ply.Nickname} ({ply.UserId}) position and HP", Plugin.Config.DebugMode); - ply.Position = data.Position; - ply.Health = data.Health; - Log.Debug($"Adding items to {ply.Nickname} ({ply.UserId})", UltimateAFK.Singleton.Config.DebugMode); - ply.SendItems(data.Items); - // I apply the modifications of the replacement player not of the afk, I could do it but I sincerely prefer this method. - ply.ApplyAttachments(); - // I refill the ammunition of the weapons, since it is annoying to appear without a loaded weapon. - ply.ReloadAllWeapons(); - // Send the broadcast to the player - ply.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); - ply.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); - }); - - Log.Debug("Removing the replacement player from the dictionary", UltimateAFK.Singleton.Config.DebugMode); - ReplacingPlayers.Remove(ply); + Log.Error($"{nameof(GiveData)}: {e}"); } } - /// - /// At the beginning of a round I clean the ReplacingPlayers dictionary. - /// - [PluginEvent(ServerEventType.RoundStart)] - private void OnRoundStarted() + + [PluginEvent(ServerEventType.MapGenerated)] + private void OnMapGenerated() { - ReplacingPlayers.Clear(); + ReplacingPlayersData.Clear(); } /// @@ -144,9 +141,18 @@ private void OnRoundStarted() [PluginEvent(ServerEventType.PlayerDeath)] private void OnPlayerDeath(Player player, Player attacker, DamageHandlerBase damageHandler) { - if (player != null && ReplacingPlayers.TryGetValue(player, out var data)) + if (player != null && ReplacingPlayersData.TryGetValue(player.UserId, out _)) + { + ReplacingPlayersData.Remove(player.UserId); + } + } + + [PluginEvent(ServerEventType.PlayerLeft)] + private void OnDisconnect(Player player) + { + if (player != null && ReplacingPlayersData.TryGetValue(player.UserId, out _)) { - ReplacingPlayers.Remove(player); + ReplacingPlayersData.Remove(player.UserId); } } } diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs new file mode 100644 index 0000000..92f41b0 --- /dev/null +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -0,0 +1,304 @@ +using MEC; +using NWAPIPermissionSystem; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using PlayerRoles.PlayableScps.Scp096; +using PluginAPI.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UltimateAFK.Handlers; +using UltimateAFK.Resources; +using UnityEngine; + +namespace UltimateAFK.Resources.Component +{ + [RequireComponent(typeof(ReferenceHub))] + public class AFKComponent : MonoBehaviour + { + private void Awake() + { + // Gets PluginAPI.Core.Player from the gameobject. + if (Player.Get(gameObject) is not { } ply) + { + Log.Error($"{this} Error Getting Player"); + Destroy(this); + return; + } + + // Sets owner variable + Owner = ply; + _ownerId = ply.UserId; + + // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. + _checkHandle = Timing.RunCoroutine(Check().CancelWith(this).CancelWith(gameObject)); + } + + private void OnDestroy() + { + Timing.KillCoroutines(_checkHandle); + } + + private IEnumerator Check() + { + for (; ;) + { + yield return Timing.WaitForSeconds(1.3f); + + Log.Debug("Calling CheckAFK", Plugin.Config.DebugMode && Plugin.Config.SpamLogs); + try + { + AfkCheck(); + } + catch (Exception e) + { + Log.Error($"Error in {nameof(Check)}: &2{e}&r"); + } + } + } + + private void AfkCheck() + { + if (!UglyCheck()) + return; + + if (Owner.Role is RoleTypeId.Tutorial && Plugin.Config.IgnoreTut) + return; + + // save current player position + var worldPosition = Owner.Position; + var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Camera.position; + var cameraRotation = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp0792 ? new Quaternion(scp0792.CurrentCamera.HorizontalRotation, scp0792.CurrentCamera.VerticalRotation, scp0792.CurrentCamera.RollRotation, 0f) : Owner.Camera.rotation; + + // Player is moving. + if (cameraPosition != _lastCameraPosition || worldPosition != _lastPosition || _lastCameraRotation != cameraRotation) + { + _lastCameraPosition = cameraPosition; + _lastCameraRotation = cameraRotation; + _lastPosition = worldPosition; + _afkTime = 0f; + } + else + { + // Check if the player role is Scp096 and current state is in TryNotToCry if it is return. + if (Owner.Role == RoleTypeId.Scp096 && (Owner.RoleBase as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry)) + return; + + Log.Debug($"{Owner.Nickname} is in not moving, AFKTime: {_afkTime}", UltimateAFK.Singleton.Config.DebugMode); + // If the time the player is afk is less than the limit, return. + if (_afkTime++ < UltimateAFK.Singleton.Config.AfkTime) return; + + // Get grace time + var graceNumb = UltimateAFK.Singleton.Config.GraceTime - (_afkTime - UltimateAFK.Singleton.Config.AfkTime); + + if (graceNumb > 0) + { + // The player is being considered afk and is being warned that they have X time to move again or they will be kicked/replaced + Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, + shouldClearPrevious: true); + } + else + { + Log.Info($"{Owner.Nickname} ({Owner.UserId}) was detected as AFK"); + _afkTime = 0f; + Replace(Owner, Owner.Role); + } + } + } + + private void Replace(Player player, RoleTypeId roleType) + { + // Check if role is blacklisted + if (Plugin.Config.RoleTypeBlacklist?.Count > 0 && Plugin.Config.RoleTypeBlacklist.Contains(roleType)) + { + Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", Plugin.Config.DebugMode); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + if (Plugin.Config.AfkCount > -1) + { + AfkTimes++; + + if (AfkTimes >= Plugin.Config.AfkCount) + { + player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); + player.Kick(Plugin.Config.MsgKick); + return; + } + } + + player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + return; + } + + // Get player replacement + Player replacement = GetReplacement(); + + // If no replacement player is found, I change the player's role to spectator + if (replacement == null) + { + Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + if (Plugin.Config.AfkCount > -1) + { + AfkTimes++; + + if (AfkTimes >= Plugin.Config.AfkCount) + { + player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); + + player.Kick(Plugin.Config.MsgKick); + + return; + } + } + + player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + } + else + { + Log.Debug($"Replacement Player found: {replacement.Nickname} ({replacement.UserId})", Plugin.Config.DebugMode); + Log.Debug($"Saving data of player {player.Nickname} in the dictionary.", Plugin.Config.DebugMode); + SaveData(replacement.UserId, roleType is RoleTypeId.Scp079); + + if (Plugin.Config.AfkCount > -1) + { + AfkTimes++; + + if (AfkTimes >= Plugin.Config.AfkCount) + { + player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); + + player.Kick(Plugin.Config.MsgKick); + + replacement.SetRole(roleType); + return; + } + } + + Log.Debug($"Cleaning player {player.Nickname} inventory", Plugin.Config.DebugMode); + // Clear player inventory + player.ClearInventory(); + //Send player a broadcast for being too long afk + player.SendBroadcast(Plugin.Config.MsgFspec, 25, shouldClearPrevious: true); + player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + + Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); + // Sends player to spectator + player.SetRole(RoleTypeId.Spectator); + // Sends replacement to the role that had the afk + Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", Plugin.Config.DebugMode); + replacement.SetRole(roleType); + + } + } + + private void SaveData(string replacementUserId, bool isScp079 = false) + { + if (isScp079) + { + if (Owner.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + { + var afkData = new AFKData() + { + NickName = Owner.Nickname, + Position = Owner.Position, + Role = Owner.Role, + Ammo = null, + Health = Owner.Health, + Items = null, + SCP079 = new Scp079Data + { + Role = scp079Role, + Energy = energyManager.CurrentAux, + Experience = tierManager.TotalExp, + } + }; + Log.Info($"Datos guardados: {afkData.SCP079.Experience} | {afkData.SCP079.Experience}"); + MainHandler.ReplacingPlayersData.Add(replacementUserId, afkData); + } + + return; + } + + var ammo = Extensions.GetAmmo(Owner); + + var data = new AFKData() + { + NickName = Owner.Nickname, + Position = Owner.Position, + Role = Owner.Role, + Ammo = ammo, + Health = Owner.Health, + Items = Owner.GetItems(), + SCP079 = new Scp079Data + { + Role = null, + Energy = 0f, + Experience = 0 + } + }; + + MainHandler.ReplacingPlayersData.Add(replacementUserId, data); + } + + /// + /// Obtains a player who qualifies for replacement. + /// + private Player GetReplacement() + { + var players = new List(); + + foreach (var player in Player.GetPlayers()) + { + if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") + || Plugin.Config.IgnoreOverwatch && player.IsOverwatchEnabled || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + continue; + + players.Add(player); + } + + return players.Any() ? players.ElementAtOrDefault(UnityEngine.Random.Range(0, players.Count)) : null; + } + + private bool UglyCheck() + { + return Owner.IsAlive && Round.IsRoundStarted && Player.Count >= UltimateAFK.Singleton.Config.MinPlayers; + } + + // Owner PluginAPI.Core.Player + private Player Owner; + + // How many times was the owner afk + private int AfkTimes; + + // Owner UserID + private string _ownerId; + + // Position in the world + private Vector3 _lastPosition; + + // Player camera position + private Vector3 _lastCameraPosition; + + // Player camera rotation + private Quaternion _lastCameraRotation; + + // The time the player was afk + private float _afkTime; + + // Using a MEC Coroutine is more optimized than using Unity methods. + private CoroutineHandle _checkHandle; + + private readonly UltimateAFK Plugin = UltimateAFK.Singleton; + } +} diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs index ea997cf..30557f2 100644 --- a/UltimateAFK/Resources/Extensions.cs +++ b/UltimateAFK/Resources/Extensions.cs @@ -33,6 +33,7 @@ public static void SendAmmo(this Player ply, Dictionary ammo) } } + public static List GetItems(this Player ply) { var items = ply.ReferenceHub.inventory.UserInventory.Items; @@ -47,7 +48,19 @@ public static List GetItems(this Player ply) return returnitems; } - + + public static Dictionary GetAmmo(Player player) + { + var result = new Dictionary(); + + foreach (var ammo in player.ReferenceHub.inventory.UserInventory.ReserveAmmo) + { + result.Add(ammo.Key, ammo.Value); + } + + return result; + } + /// /// Applies player attachments. /// diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 43beeb5..775d114 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,23 +21,18 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.1.9", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.2.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; - PluginAPI.Events.EventManager.RegisterEvents(this, new Handlers.MainHandler(Singleton)); - - if (ConfigFile.ServerConfig.GetFloat("afk_time") > 1) - { - Log.Warning($"You have enabled the AFK detector of the base game, please disable it by config &6afk_time = 0&r in &4config_gameplay.txt&r"); - } + PluginAPI.Events.EventManager.RegisterEvents(this, new MainHandler(Singleton)); } [PluginUnload] void OnDisable() { - MainHandler.ReplacingPlayers.Clear(); - MainHandler.ReplacingPlayers = null; + MainHandler.ReplacingPlayersData.Clear(); + MainHandler.ReplacingPlayersData = null; } } } \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 5a9e94e..c2ea9cd 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -40,5 +40,6 @@ + \ No newline at end of file From 913dfc58d82198dcdcb81b9056f5665f3aec0e51 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 15 May 2023 19:12:10 -0300 Subject: [PATCH 116/146] TryGetEffect instead of GetEffect --- UltimateAFK/Command/AfkCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index e28f912..3736e91 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -49,7 +49,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPlayerIsDead; return false; } - if (ply.Zone == FacilityZone.Other || ply.EffectsManager.GetEffect().IsEnabled) + if (ply.Zone == FacilityZone.Other || ply.EffectsManager.TryGetEffect(out var corriding) && corriding.IsEnabled) { response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPocketDimension; return false; From 0d91ab53a53e6123062f2b011a20daf7ae280b6a Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 16 May 2023 15:52:00 -0300 Subject: [PATCH 117/146] 6.2.1 * Fixed .afk command * Added a check to avoid giving the component to null players * Removed unnecessary using --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 72 ++++++++----------- UltimateAFK/Config.cs | 30 ++++---- UltimateAFK/Handlers/MainHandler.cs | 14 ++-- UltimateAFK/Resources/AFKData.cs | 4 +- .../Resources/Component/AFKComponent.cs | 5 +- UltimateAFK/Resources/Extensions.cs | 19 +++-- UltimateAFK/UltimateAFK.cs | 13 +--- 8 files changed, 65 insertions(+), 94 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index 50e9adf..ed3067c 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.1.9 + 6.2.1 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 3736e91..11d374e 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -1,20 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; using CommandSystem; using CustomPlayerEffects; using MapGeneration; using NWAPIPermissionSystem; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; -using PluginAPI.Commands; using PluginAPI.Core; -using PluginAPI.Core.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; using UltimateAFK.Handlers; using UltimateAFK.Resources; using UnityEngine; using Utils.NonAllocLINQ; -using Random = UnityEngine.Random; namespace UltimateAFK.Command { @@ -26,7 +23,7 @@ public class AfkCommand : ICommand public string Description { get; } = "By using this command you will be moved to spectator and if the server allows it a player will replace you."; public Dictionary InCooldown = new(); - private readonly UltimateAFK Plugin = UltimateAFK.Singleton; + private UltimateAFK Plugin = UltimateAFK.Singleton; public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) { @@ -42,7 +39,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnRoundIsNotStarted; return false; } - + var ply = Player.Get(sender); if (!ply.IsAlive) { @@ -111,21 +108,24 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s private void Replace(Player player, RoleTypeId roleType) { + Log.Info($"Player is null: {player is null}"); + Log.Info($"Plugin is null: {UltimateAFK.Singleton is null}"); // Check if role is blacklisted - if (Plugin.Config.RoleTypeBlacklist?.Count > 0 && Plugin.Config.RoleTypeBlacklist.Contains(roleType)) + if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Count > 0 && UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) { - Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", Plugin.Config.DebugMode); + Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); player.ClearInventory(); player.SetRole(RoleTypeId.Spectator); - player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); return; } + Log.Info("Getting replacement"); // Get player replacement - Player replacement = FindReplacement(player.UserId); + Player replacement = GetReplacement(player.UserId); // If no replacement player is found, I change the player's role to spectator if (replacement == null) @@ -135,46 +135,47 @@ private void Replace(Player player, RoleTypeId roleType) player.ClearInventory(); player.SetRole(RoleTypeId.Spectator); - player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); } else { - Log.Debug($"Replacement Player found: {replacement.Nickname} ({replacement.UserId})", Plugin.Config.DebugMode); - Log.Debug($"Saving data of player {player.Nickname} in the dictionary.", Plugin.Config.DebugMode); + Log.Debug($"Replacement Player found: {replacement.Nickname} ({replacement.UserId})", UltimateAFK.Singleton.Config.DebugMode); + Log.Debug($"Saving data of player {player.Nickname} in the dictionary.", UltimateAFK.Singleton.Config.DebugMode); - SaveData(player, replacement.UserId, roleType == RoleTypeId.Scp079); + SaveData(player, replacement.UserId, roleType is RoleTypeId.Scp079); - Log.Debug($"Cleaning player {player.Nickname} inventory", Plugin.Config.DebugMode); + Log.Debug($"Cleaning player {player.Nickname} inventory", UltimateAFK.Singleton.Config.DebugMode); // Clear player inventory player.ClearInventory(); //Send player a broadcast for being too long afk - player.SendBroadcast(Plugin.Config.MsgFspec, 25, shouldClearPrevious: true); - player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); + Log.Debug($"Changing player {player.Nickname} to spectator", UltimateAFK.Singleton.Config.DebugMode); // Sends player to spectator player.SetRole(RoleTypeId.Spectator); // Sends replacement to the role that had the afk - Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", Plugin.Config.DebugMode); + Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); replacement.SetRole(roleType); } } - private Player FindReplacement(string afkUserId) + private Player GetReplacement(string afkUserId) { var players = new List(); + foreach (var player in Player.GetPlayers()) { if (player.IsAlive || player.UserId == afkUserId || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) continue; - + players.Add(player); } - - return players.Any() ? players.ElementAtOrDefault(Random.Range(0, players.Count)) : null; + + return players.Any() ? players.ElementAtOrDefault(UnityEngine.Random.Range(0, players.Count)) : null; } private void SaveData(Player player, string replacementUserId, bool isScp079 = false) @@ -227,22 +228,5 @@ private void SaveData(Player player, string replacementUserId, bool isScp079 = f MainHandler.ReplacingPlayersData.Add(replacementUserId, data); } - - /// - /// Cache player's ammunition - /// - /// - /// - private Dictionary GetAmmo(Player player) - { - var result = new Dictionary(); - - foreach (var ammo in player.ReferenceHub.inventory.UserInventory.ReserveAmmo) - { - result.Add(ammo.Key, ammo.Value); - } - - return result; - } } } \ No newline at end of file diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 2d13c49..f69bee3 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using PlayerRoles; +using System.Collections.Generic; using System.ComponentModel; -using PlayerRoles; namespace UltimateAFK { @@ -23,7 +23,7 @@ public class Config [Description("Tutorials should be ignored ?")] public bool IgnoreTut { get; set; } = true; - + [Description("Players in overwatch mode will be ignored for replace a afk player")] public bool IgnoreOverwatch { get; set; } = true; @@ -65,7 +65,7 @@ public class CommandConfig [Description("The coldown of the command when using it")] public float Cooldown { get; set; } = 40f; - + [Description("The command can only be used by players who have a group that is on the list ?")] public bool ExclusiveForGroups { get; set; } = false; @@ -74,7 +74,7 @@ public class CommandConfig { "someGroup", }; - + [Description("The command is disabled for certain RoleTypes?")] public bool DisableForCertainRole { get; set; } = false; @@ -83,7 +83,7 @@ public class CommandConfig { RoleTypeId.None, }; - + public Responses Responses { get; set; } = new(); } @@ -94,28 +94,28 @@ public class Responses [Description("Response given to the player when successfully executing the command.")] public string OnSuccess { get; set; } = "You were moved to spectator because you considered yourself AFK."; - + [Description("Response given to the player when he has no hands")] - public string OnSevereHands { get; set; } = "You cannot use this command if you have no hands"; + public string OnSevereHands { get; set; } = "You cannot use this command if you have no hands"; [Description("Response given to the player when affected by Cardiact Arrest (Effect of SCP-049)")] public string OnHearthAttack { get; set; } = "You cannot use this command if you have a heart attack."; - + [Description("Response given to the player when trying to use the command when in the pocket dimension.")] public string OnPocketDimension { get; set; } = "There is no easy escape from the pocket dimension."; - + [Description("Response given to the player when he still has cooldown to use the command. {0} is the number of seconds the player has to wait.")] - public string OnCooldown { get; set; } = "You cannot use the command yet, you have to wait {0} seconds."; - + public string OnCooldown { get; set; } = "You cannot use the command yet, you have to wait {0} seconds."; + [Description("Response given when a player tries to use the command with a role in the blacklist")] public string OnBlackListedRole { get; set; } = "You cannot use this command when you are {0}"; - + [Description("Response given to the player when not in the group list")] public string OnGroupExclusive { get; set; } = "Your current group is not in the list of allowed groups."; - + [Description("Response given to the player when he tries to use the command when the round has not started.")] public string OnRoundIsNotStarted { get; set; } = "The round has not started yet, you cannot use the command."; - + [Description("Response given to the player when trying to use the command while is dead.")] public string OnPlayerIsDead { get; set; } = "You cannot use the command if you are dead."; } diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 42ed97c..fb73632 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -1,11 +1,11 @@ using MEC; -using PluginAPI.Core; -using System.Collections.Generic; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; using PlayerStatsSystem; +using PluginAPI.Core; using PluginAPI.Core.Attributes; using PluginAPI.Enums; +using System.Collections.Generic; using UltimateAFK.Resources; using UltimateAFK.Resources.Component; @@ -26,7 +26,7 @@ public MainHandler(UltimateAFK plugin) } #endregion - + /// /// A dictionary where replacement players are stored to give them the stats and items of the original player. /// @@ -39,8 +39,8 @@ public MainHandler(UltimateAFK plugin) [PluginEvent(ServerEventType.PlayerJoined)] private void OnPlayerJoin(Player player) { - if(!Plugin.Config.IsEnabled || player.UserId.Contains("@server")) return; - + if (!Plugin.Config.IsEnabled || player is null || player.UserId.Contains("@server")) return; + Log.Debug($"Adding the Component to {player.Nickname}", Plugin.Config.DebugMode); player.GameObject.AddComponent(); @@ -58,7 +58,7 @@ private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId ne return; Log.Debug($"Detecting player {player.Nickname} ({player.UserId}) who replaced a player {data.NickName} who was afk", UltimateAFK.Singleton.Config.DebugMode); - + Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => GiveData(player, data, newRole)); } catch (System.Exception e) @@ -127,7 +127,7 @@ private void GiveData(Player player, AFKData data, RoleTypeId roleType) Log.Error($"{nameof(GiveData)}: {e}"); } } - + [PluginEvent(ServerEventType.MapGenerated)] private void OnMapGenerated() diff --git a/UltimateAFK/Resources/AFKData.cs b/UltimateAFK/Resources/AFKData.cs index 449eda1..edadc22 100644 --- a/UltimateAFK/Resources/AFKData.cs +++ b/UltimateAFK/Resources/AFKData.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using PlayerRoles; +using PlayerRoles; +using System.Collections.Generic; using UnityEngine; namespace UltimateAFK.Resources diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs index 92f41b0..9915385 100644 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -7,10 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using UltimateAFK.Handlers; -using UltimateAFK.Resources; using UnityEngine; namespace UltimateAFK.Resources.Component @@ -43,7 +40,7 @@ private void OnDestroy() private IEnumerator Check() { - for (; ;) + for (; ; ) { yield return Timing.WaitForSeconds(1.3f); diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs index 30557f2..7ac5f23 100644 --- a/UltimateAFK/Resources/Extensions.cs +++ b/UltimateAFK/Resources/Extensions.cs @@ -1,11 +1,8 @@ -using System.Collections.Generic; -using System.Linq; -using InventorySystem; -using InventorySystem.Items; using InventorySystem.Items.Firearms; using InventorySystem.Items.Firearms.Attachments; using PluginAPI.Core; -using UnityEngine; +using System.Collections.Generic; +using System.Linq; namespace UltimateAFK.Resources { @@ -32,17 +29,17 @@ public static void SendAmmo(this Player ply, Dictionary ammo) ply.SetAmmo(ammoItem.Key, ammoItem.Value); } } - + public static List GetItems(this Player ply) { var items = ply.ReferenceHub.inventory.UserInventory.Items; var returnitems = new List(); - - foreach(var i in items.Values) + + foreach (var i in items.Values) { - if(i.ItemTypeId is ItemType.Ammo9x19 or ItemType.Ammo12gauge or ItemType.Ammo44cal or ItemType.Ammo556x45 or ItemType.Ammo762x39) continue; - + if (i.ItemTypeId is ItemType.Ammo9x19 or ItemType.Ammo12gauge or ItemType.Ammo44cal or ItemType.Ammo556x45 or ItemType.Ammo762x39) continue; + returnitems.Add(i.ItemTypeId); } @@ -88,7 +85,7 @@ public static void ReloadAllWeapons(this Player ply) { var item = ply.Items.Where(i => i is Firearm); - foreach (var weapon in item) + foreach (var weapon in item) { if (weapon is Firearm firearm) { diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 775d114..33de616 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,13 +1,6 @@ -using System.Collections.Generic; -using System.Linq; -using GameCore; -using Interactables.Interobjects.DoorUtils; -using PluginAPI.Core; -using PluginAPI.Core.Attributes; +using PluginAPI.Core.Attributes; using PluginAPI.Enums; using UltimateAFK.Handlers; -using UltimateAFK.Resources; -using Log = PluginAPI.Core.Log; namespace UltimateAFK { @@ -19,9 +12,9 @@ public class UltimateAFK public static UltimateAFK Singleton; [PluginConfig] public Config Config; - + [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.2.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.2.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 5f964290a8ddef3e713ffb05635009c69d936a99 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 16 May 2023 16:03:53 -0300 Subject: [PATCH 118/146] 6.2.2 * Remove Log.Info in the code (sorry) --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 3 --- UltimateAFK/UltimateAFK.cs | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index ed3067c..a29a8ce 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.2.1 + 6.2.2 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 11d374e..9af03dc 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -108,8 +108,6 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s private void Replace(Player player, RoleTypeId roleType) { - Log.Info($"Player is null: {player is null}"); - Log.Info($"Plugin is null: {UltimateAFK.Singleton is null}"); // Check if role is blacklisted if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Count > 0 && UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) { @@ -123,7 +121,6 @@ private void Replace(Player player, RoleTypeId roleType) return; } - Log.Info("Getting replacement"); // Get player replacement Player replacement = GetReplacement(player.UserId); diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 33de616..8078580 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -14,7 +14,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.2.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.2.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 8dfc9bd63c1c75d358c08b4b53446742819c1575 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 16 May 2023 16:15:59 -0300 Subject: [PATCH 119/146] Man i need sleep --- UltimateAFK/Command/AfkCommand.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 9af03dc..531060d 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -41,6 +41,12 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } var ply = Player.Get(sender); + + if(ply == null) + { + response = "Player is null"; + return false; + } if (!ply.IsAlive) { response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPlayerIsDead; From 8cb4db376ee356c7fb60f26a150715f8a8905c9f Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 24 May 2023 21:21:25 -0300 Subject: [PATCH 120/146] 6.2.3 * Fixed a bug that for some reason the command searched for null players * Removed unnecessary event --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 20 +++++++------------- UltimateAFK/Handlers/MainHandler.cs | 9 --------- UltimateAFK/UltimateAFK.cs | 2 +- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index a29a8ce..fa43dab 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.2.2 + 6.2.3 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 531060d..0f863f2 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -52,7 +52,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPlayerIsDead; return false; } - if (ply.Zone == FacilityZone.Other || ply.EffectsManager.TryGetEffect(out var corriding) && corriding.IsEnabled) + if (ply.Zone == FacilityZone.Other || ply.EffectsManager.TryGetEffect(out var corroding) && corroding.IsEnabled) { response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPocketDimension; return false; @@ -105,8 +105,8 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } catch (Exception e) { - Log.Error($"Error on {nameof(AfkCommand)}: {e}"); - response = $"Error: {e}"; + Log.Error($"Error on {GetType().Name}.{nameof(Execute)}: {e}"); + response = $"Error on {nameof(Execute)}: {e}"; return false; } } @@ -126,18 +126,16 @@ private void Replace(Player player, RoleTypeId roleType) player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); return; } - + // Get player replacement Player replacement = GetReplacement(player.UserId); - + // If no replacement player is found, I change the player's role to spectator if (replacement == null) { Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); - player.ClearInventory(); player.SetRole(RoleTypeId.Spectator); - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); } @@ -147,7 +145,6 @@ private void Replace(Player player, RoleTypeId roleType) Log.Debug($"Saving data of player {player.Nickname} in the dictionary.", UltimateAFK.Singleton.Config.DebugMode); SaveData(player, replacement.UserId, roleType is RoleTypeId.Scp079); - Log.Debug($"Cleaning player {player.Nickname} inventory", UltimateAFK.Singleton.Config.DebugMode); // Clear player inventory player.ClearInventory(); @@ -161,7 +158,6 @@ private void Replace(Player player, RoleTypeId roleType) // Sends replacement to the role that had the afk Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); replacement.SetRole(roleType); - } } @@ -171,7 +167,7 @@ private Player GetReplacement(string afkUserId) foreach (var player in Player.GetPlayers()) { - if (player.IsAlive || player.UserId == afkUserId || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") + if (player is null || player.IsAlive || player.UserId == afkUserId || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) continue; @@ -185,10 +181,8 @@ private void SaveData(Player player, string replacementUserId, bool isScp079 = f { if (isScp079) { - if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) { - var afkData = new AFKData() { NickName = player.Nickname, diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index fb73632..999c5d2 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -146,14 +146,5 @@ private void OnPlayerDeath(Player player, Player attacker, DamageHandlerBase dam ReplacingPlayersData.Remove(player.UserId); } } - - [PluginEvent(ServerEventType.PlayerLeft)] - private void OnDisconnect(Player player) - { - if (player != null && ReplacingPlayersData.TryGetValue(player.UserId, out _)) - { - ReplacingPlayersData.Remove(player.UserId); - } - } } } \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 8078580..e8fa5f7 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -14,7 +14,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.2.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.2.3", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 8e30a85f726e2fe87656af70469d5336818335bf Mon Sep 17 00:00:00 2001 From: ThijsNameIsTaken Date: Wed, 31 May 2023 17:50:49 +0200 Subject: [PATCH 121/146] Fix 079 overcharge --- UltimateAFK/Resources/Component/AFKComponent.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs index 9915385..f1da6a9 100644 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -188,12 +188,12 @@ private void Replace(Player player, RoleTypeId roleType) player.SendBroadcast(Plugin.Config.MsgFspec, 25, shouldClearPrevious: true); player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); - Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); - // Sends player to spectator - player.SetRole(RoleTypeId.Spectator); // Sends replacement to the role that had the afk Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", Plugin.Config.DebugMode); replacement.SetRole(roleType); + // Sends player to spectator + Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); + player.SetRole(RoleTypeId.Spectator); } } From 4e796f900c973eff6ab67ef5bee40eef242e442b Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 31 May 2023 13:07:26 -0300 Subject: [PATCH 122/146] Change version --- Cerberus.props | 2 +- UltimateAFK/UltimateAFK.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index fa43dab..84e11ec 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.2.3 + 6.2.4 false diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index e8fa5f7..6df1775 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -14,7 +14,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.2.3", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.2.4", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 32f8c93b2fa98ed722afafd701b2e7a5929519d3 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 24 Jun 2023 19:31:11 -0300 Subject: [PATCH 123/146] Rebuild for 13.1 Maybe this fix the error on GetReplacement --- Cerberus.props | 2 +- UltimateAFK/UltimateAFK.cs | 2 +- UltimateAFK/UltimateAFK.csproj | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index 84e11ec..45c49d8 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.2.4 + 6.3.0 false diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 6df1775..13ffa63 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -14,7 +14,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.2.4", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.3.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index c2ea9cd..b283a9b 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -24,13 +24,12 @@ - - + From 0e0203387ee8555eabc0a6d095a710665861c5a5 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sun, 25 Jun 2023 16:08:11 -0300 Subject: [PATCH 124/146] Finally it works * A message will now be sent on the console to tell the player who replaced him. * NWAPIPermissionSystem version 0.0.4 is required. --- Cerberus.props | 2 +- UltimateAFK/Command/AfkCommand.cs | 1 + UltimateAFK/Config.cs | 3 +++ UltimateAFK/Resources/Component/AFKComponent.cs | 2 +- UltimateAFK/UltimateAFK.cs | 7 +++++-- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index 45c49d8..6a446e3 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.3.0 + 6.3.2 false diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 0f863f2..d91d442 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -158,6 +158,7 @@ private void Replace(Player player, RoleTypeId roleType) // Sends replacement to the role that had the afk Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); replacement.SetRole(roleType); + player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplaced, replacement.Nickname), "white"); } } diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index f69bee3..3d3b4a9 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -45,6 +45,9 @@ public class Config [Description("This message will be sent to the player who has been moved to spectator when he is detected as AFK, it is also sent to the player's console.")] public string MsgFspec { get; set; } = "You were detected as AFK and were moved to spectator"; + [Description("When a player is replaced by another player, this message will be sent to his console.")] + public string MsgReplaced { get; set; } = "\nyou were replaced by {0}"; + [Description("This will be the reason for the Kick, due to the VSR it is obligatory to clarify that it is a plugin with flags like [UltimateAFK] or something similar.")] public string MsgKick { get; set; } = "[Ultimate-AFK] You were removed from the server for being AFK for too long.!"; diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs index f1da6a9..7e0ded1 100644 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -194,7 +194,7 @@ private void Replace(Player player, RoleTypeId roleType) // Sends player to spectator Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); player.SetRole(RoleTypeId.Spectator); - + player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplaced, replacement.Nickname), "white"); } } diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 13ffa63..26484ab 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,5 +1,8 @@ -using PluginAPI.Core.Attributes; +using PluginAPI.Core; +using PluginAPI.Core.Attributes; using PluginAPI.Enums; +using System.Linq; +using System.Reflection; using UltimateAFK.Handlers; namespace UltimateAFK @@ -14,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.3.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.3.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 9bb22fd4e0f9198e2bf0ccebcb58fd9c42b646db Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 3 Jul 2023 15:38:11 -0300 Subject: [PATCH 125/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f3c8f5b..fc0a03e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -| ![Github All Releases](https://img.shields.io/github/downloads/SrLicht/Ultimate-AFK/total.svg) | Releases +![Github All Releases](https://img.shields.io/github/downloads/SrLicht/Ultimate-AFK/total.svg) Releases # Ultimate-AFK This plugin allows AFK players to be replaced by players who are in spectator, they can also be kicked from the server after a certain number of being detected as AFK (configurable). From 8eba3bffb0377131d011a61fab7e8b5b5efe2b85 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 3 Jul 2023 15:41:04 -0300 Subject: [PATCH 126/146] 6.3.3 * Fixing error when trying to check permissions of null player --- Cerberus.props | 2 +- .../Resources/Component/AFKComponent.cs | 28 +++++++++++++------ UltimateAFK/UltimateAFK.cs | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index 6a446e3..d45156f 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.3.2 + 6.3.3 false diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs index 7e0ded1..d1c7276 100644 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -253,18 +253,28 @@ private void SaveData(string replacementUserId, bool isScp079 = false) /// private Player GetReplacement() { - var players = new List(); - - foreach (var player in Player.GetPlayers()) + try { - if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || Plugin.Config.IgnoreOverwatch && player.IsOverwatchEnabled || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) - continue; + var players = new List(); - players.Add(player); - } + foreach (var player in Player.GetPlayers()) + { + if(player is null) continue; - return players.Any() ? players.ElementAtOrDefault(UnityEngine.Random.Range(0, players.Count)) : null; + if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") + || Plugin.Config.IgnoreOverwatch && player.IsOverwatchEnabled || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + continue; + + players.Add(player); + } + + return players.Any() ? players.ElementAtOrDefault(UnityEngine.Random.Range(0, players.Count)) : null; + } + catch (Exception e) + { + Log.Error($"Error in {nameof(GetReplacement)} of type {e.GetType()}: {e} "); + return null; + } } private bool UglyCheck() diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 26484ab..1c4a840 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.3.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.3.3", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From a634bdae6125b24e14a89e9f17c90d9b34455bca Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 3 Jul 2023 16:47:23 -0300 Subject: [PATCH 127/146] 13.1.1 Recompile --- Cerberus.props | 2 +- UltimateAFK/UltimateAFK.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index d45156f..587ac8b 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.3.3 + 6.3.4 false diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 1c4a840..96578c8 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.3.3", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.3.4", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 844430540e0e3aab691923b64857def9350a2b36 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 26 Jul 2023 21:01:45 -0300 Subject: [PATCH 128/146] 6.3.5 * Preventing log error for players with userId emptys --- UltimateAFK/Handlers/MainHandler.cs | 9 ++++++--- UltimateAFK/UltimateAFK.cs | 2 +- UltimateAFK/UltimateAFK.csproj | 1 - 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 999c5d2..eb43cbf 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -39,7 +39,7 @@ public MainHandler(UltimateAFK plugin) [PluginEvent(ServerEventType.PlayerJoined)] private void OnPlayerJoin(Player player) { - if (!Plugin.Config.IsEnabled || player is null || player.UserId.Contains("@server")) return; + if (!Plugin.Config.IsEnabled || player is null || player.UserId.Contains("@server") || !player.IsReady) return; Log.Debug($"Adding the Component to {player.Nickname}", Plugin.Config.DebugMode); @@ -54,7 +54,7 @@ private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId ne { try { - if (player == null || newRole == RoleTypeId.Spectator || !ReplacingPlayersData.TryGetValue(player.UserId, out var data)) + if (player == null || !player.IsReady || newRole == RoleTypeId.Spectator || !ReplacingPlayersData.TryGetValue(player.UserId, out var data)) return; Log.Debug($"Detecting player {player.Nickname} ({player.UserId}) who replaced a player {data.NickName} who was afk", UltimateAFK.Singleton.Config.DebugMode); @@ -141,7 +141,10 @@ private void OnMapGenerated() [PluginEvent(ServerEventType.PlayerDeath)] private void OnPlayerDeath(Player player, Player attacker, DamageHandlerBase damageHandler) { - if (player != null && ReplacingPlayersData.TryGetValue(player.UserId, out _)) + if (!player.IsReady) + return; + + if (ReplacingPlayersData.TryGetValue(player.UserId, out _)) { ReplacingPlayersData.Remove(player.UserId); } diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 96578c8..80e0a5a 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.3.4", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.3.5", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index b283a9b..bccb3da 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -29,7 +29,6 @@ - From 567b5408eb930e4c9d2d1ba31486fd27a8e21621 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Thu, 27 Jul 2023 09:22:23 -0300 Subject: [PATCH 129/146] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index fc0a03e..92ef77f 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,14 @@ If you give a group the `uafk.ignore` permission the player will be ignored to r You can install this plugin using the command ``p install SrLicht/Ultimate-AFK`` on the console or by downloading the .dll file and placing it in ``SCP Secret Laboratory/PluginAPI/plugins/global or your port`` +# For devs +You can disable a player from being detected as afk or using the command by setting ``uafk_disable`` in his TemporaryData. +```cs +// Add the temporary data +player.TemporaryData.StoredData.Add("uafk_disable", true); + +// Remove the temporary data +player.TemporaryData.StoredData.Remove("uafk_disable"); +// It is also cleaned at the end of a round or when a player is disconnected. +``` +You can also prevent it from being detected as afk by adding the value ``uafk_disable_check``. From 567dd1f89348d4879dad05e1527f52b722e18ec9 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Thu, 27 Jul 2023 09:23:00 -0300 Subject: [PATCH 130/146] 6.3.6 * Added some utilities for the Devs in case they want to deactivate the command and can be replaced. * Replace command no longer clears player inventory --- UltimateAFK/Command/AfkCommand.cs | 16 ++++++++++------ UltimateAFK/Config.cs | 3 +++ UltimateAFK/Resources/Component/AFKComponent.cs | 8 ++++++-- UltimateAFK/UltimateAFK.cs | 2 +- UltimateAFK/UltimateAFK.csproj | 1 + 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index d91d442..43da591 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -63,9 +63,8 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s ply.Role); return false; } - if (UltimateAFK.Singleton.Config.CommandConfig.ExclusiveForGroups && - !UltimateAFK.Singleton.Config.CommandConfig.UserGroupsAllowed.Contains(ply.RoleName)) + !UltimateAFK.Singleton.Config.CommandConfig.UserGroupsAllowed.Contains(ply.ReferenceHub.serverRoles.Group?.BadgeText)) { response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnGroupExclusive; return false; @@ -80,6 +79,11 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnHearthAttack; return false; } + if(ply.TemporaryData.StoredData.ContainsKey("uafk_disable")) + { + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnUafkDisable; + return false; + } if (InCooldown.TryGetValue(ply.UserId, out var cooldown)) { // In cooldown @@ -131,10 +135,10 @@ private void Replace(Player player, RoleTypeId roleType) Player replacement = GetReplacement(player.UserId); // If no replacement player is found, I change the player's role to spectator - if (replacement == null) + if (replacement is null) { Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); - player.ClearInventory(); + //player.ClearInventory(); player.SetRole(RoleTypeId.Spectator); player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); @@ -168,8 +172,8 @@ private Player GetReplacement(string afkUserId) foreach (var player in Player.GetPlayers()) { - if (player is null || player.IsAlive || player.UserId == afkUserId || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + if (player is null || !player.IsReady || player.IsAlive || player.UserId == afkUserId || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") + || UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) continue; players.Add(player); diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 3d3b4a9..3a1bc5a 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -121,5 +121,8 @@ public class Responses [Description("Response given to the player when trying to use the command while is dead.")] public string OnPlayerIsDead { get; set; } = "You cannot use the command if you are dead."; + + [Description("If a player has the value \"afk disable\" (bool) set to true in his Temporary Storage, trying to use the command will give him this message")] + public string OnUafkDisable { get; set; } = "You can't use this command because you have a subclass or some plugin temporarily removed your access to the command"; } } \ No newline at end of file diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs index d1c7276..89ee2c8 100644 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -64,6 +64,10 @@ private void AfkCheck() if (Owner.Role is RoleTypeId.Tutorial && Plugin.Config.IgnoreTut) return; + // With this you can temporarily deactivate a player's check. + if (Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) + return; + // save current player position var worldPosition = Owner.Position; var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Camera.position; @@ -261,8 +265,8 @@ private Player GetReplacement() { if(player is null) continue; - if (player.IsAlive || player == Owner || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || Plugin.Config.IgnoreOverwatch && player.IsOverwatchEnabled || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + if (player.IsAlive || player == Owner || !player.IsReady || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") + || Plugin.Config.IgnoreOverwatch && player.IsOverwatchEnabled || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) continue; players.Add(player); diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 80e0a5a..e0e040d 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.3.5", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.3.6", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index bccb3da..fc442fa 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -24,6 +24,7 @@ + From db1814271d10bdde73ead7898a53ca9a562b689d Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sun, 30 Jul 2023 19:30:12 -0300 Subject: [PATCH 131/146] 6.3.7 * Now players will not be able to use the .afk command if they are in a moving elevator. * This fixes a bug where if a player used the command while the elevator was moving the replacement player would fall into the void. --- UltimateAFK/Command/AfkCommand.cs | 5 +++++ UltimateAFK/Config.cs | 3 +++ UltimateAFK/Handlers/MainHandler.cs | 2 ++ UltimateAFK/Resources/Extensions.cs | 25 +++++++++++++++++++++++++ UltimateAFK/UltimateAFK.cs | 4 +++- 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 43da591..fb358f7 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -79,6 +79,11 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnHearthAttack; return false; } + if (ply.InElevator()) + { + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnElevatorMoving; + return false; + } if(ply.TemporaryData.StoredData.ContainsKey("uafk_disable")) { response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnUafkDisable; diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 3a1bc5a..b9b5ad2 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -124,5 +124,8 @@ public class Responses [Description("If a player has the value \"afk disable\" (bool) set to true in his Temporary Storage, trying to use the command will give him this message")] public string OnUafkDisable { get; set; } = "You can't use this command because you have a subclass or some plugin temporarily removed your access to the command"; + + [Description("If a player is inside an elevator he will not be able to use the command because his replacement will fall into the void if the elevator is moving.")] + public string OnElevatorMoving { get; set; } = " You cannot use this command while in a moving elevator."; } } \ No newline at end of file diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index eb43cbf..4531771 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -6,6 +6,7 @@ using PluginAPI.Core.Attributes; using PluginAPI.Enums; using System.Collections.Generic; +using System.Linq; using UltimateAFK.Resources; using UltimateAFK.Resources.Component; @@ -133,6 +134,7 @@ private void GiveData(Player player, AFKData data, RoleTypeId roleType) private void OnMapGenerated() { ReplacingPlayersData.Clear(); + Extensions.AllElevators = Map.Elevators.ToList(); } /// diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs index 7ac5f23..c836f25 100644 --- a/UltimateAFK/Resources/Extensions.cs +++ b/UltimateAFK/Resources/Extensions.cs @@ -1,3 +1,4 @@ +using Interactables.Interobjects; using InventorySystem.Items.Firearms; using InventorySystem.Items.Firearms.Attachments; using PluginAPI.Core; @@ -8,6 +9,7 @@ namespace UltimateAFK.Resources { public static class Extensions { + public static List AllElevators = new(Map.Elevators); /// /// Adds several items at the same time to a player. /// @@ -94,5 +96,28 @@ public static void ReloadAllWeapons(this Player ply) } } } + + /// + /// Check if a is un bounds of a + /// + /// + /// + public static bool InElevator(this Player player) + { + foreach(var elevator in AllElevators) + { + if (elevator.WorldspaceBounds.Contains(player.Position) && elevator.IsMoving()) + return true; + } + + return false; + } + + /// + /// Check if the current is moving. + /// + /// + /// + public static bool IsMoving(this ElevatorChamber elevator) => elevator._curSequence is ElevatorChamber.ElevatorSequence.MovingAway or ElevatorChamber.ElevatorSequence.Arriving; } } \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index e0e040d..e4c39b3 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using UltimateAFK.Handlers; +using UltimateAFK.Resources; namespace UltimateAFK { @@ -17,7 +18,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.3.6", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.3.7", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; @@ -29,6 +30,7 @@ void OnDisable() { MainHandler.ReplacingPlayersData.Clear(); MainHandler.ReplacingPlayersData = null; + Extensions.AllElevators.Clear(); } } } \ No newline at end of file From c3d650988a5a982a31ae43f54f66f52e0b7a915a Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 1 Aug 2023 06:05:16 -0300 Subject: [PATCH 132/146] Rework in progress * New afk component * More legible * It is no longer possible to have players in Overwatch be considered to replace a player * Minor optimization by caching the player's RoleBase instead of invoking it every 1 second * * Events for when a player is detected AFK --- UltimateAFK/API/AfkEvents.cs | 38 ++ UltimateAFK/Command/AfkCommand.cs | 2 +- UltimateAFK/Config.cs | 5 +- .../Resources/Component/AFKComponent.cs | 2 +- .../Resources/Component/NewComponent.cs | 428 ++++++++++++++++++ 5 files changed, 469 insertions(+), 6 deletions(-) create mode 100644 UltimateAFK/API/AfkEvents.cs create mode 100644 UltimateAFK/Resources/Component/NewComponent.cs diff --git a/UltimateAFK/API/AfkEvents.cs b/UltimateAFK/API/AfkEvents.cs new file mode 100644 index 0000000..41c6338 --- /dev/null +++ b/UltimateAFK/API/AfkEvents.cs @@ -0,0 +1,38 @@ +using PluginAPI.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UltimateAFK.API +{ + public class AfkEvents + { + // Lazy event system... Sorry i dont know well doing events in a simple way. + private static AfkEvents _instance; + + public static AfkEvents Instance + { + get + { + _instance ??= new AfkEvents(); + return _instance; + } + } + + public void InvokePlayerAfkDetected(Player player, bool isForCommand) + { + PlayerAfkDetectedEvent?.Invoke(player, isForCommand); + } + + public delegate void PlayerAfkDetected(Player player, bool isForCommand); + + public event PlayerAfkDetected PlayerAfkDetectedEvent; + + + // Prevent extarnal instances. + private AfkEvents() { } + + } +} diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index fb358f7..d38cc9f 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -178,7 +178,7 @@ private Player GetReplacement(string afkUserId) foreach (var player in Player.GetPlayers()) { if (player is null || !player.IsReady || player.IsAlive || player.UserId == afkUserId || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + || /*UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled ||*/ player.TemporaryData.StoredData.ContainsKey("uafk_disable") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) continue; players.Add(player); diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index b9b5ad2..860e2ca 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -24,13 +24,10 @@ public class Config [Description("Tutorials should be ignored ?")] public bool IgnoreTut { get; set; } = true; - [Description("Players in overwatch mode will be ignored for replace a afk player")] - public bool IgnoreOverwatch { get; set; } = true; - [Description("RoleTypes on this list will not be replaced by other players")] public List RoleTypeBlacklist { get; set; } = new() { RoleTypeId.Scp0492 }; - [Description("The time it takes for a player to stand still before he is detected as AFK")] + [Description("How long a player can remain motionless before being detected as AFK")] public int AfkTime { get; set; } = 80; [Description("After being detected as AFK a message will appear on his face and he will be given this time to move or he will be Kicked/Moved to spectator.")] diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs index 89ee2c8..2cd2b05 100644 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -266,7 +266,7 @@ private Player GetReplacement() if(player is null) continue; if (player.IsAlive || player == Owner || !player.IsReady || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || Plugin.Config.IgnoreOverwatch && player.IsOverwatchEnabled || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + || /*Plugin.Config.IgnoreOverwatch && player.IsOverwatchEnabled ||*/ player.TemporaryData.StoredData.ContainsKey("uafk_disable") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) continue; players.Add(player); diff --git a/UltimateAFK/Resources/Component/NewComponent.cs b/UltimateAFK/Resources/Component/NewComponent.cs new file mode 100644 index 0000000..f082775 --- /dev/null +++ b/UltimateAFK/Resources/Component/NewComponent.cs @@ -0,0 +1,428 @@ +using MEC; +using NWAPIPermissionSystem; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using PlayerRoles.PlayableScps.Scp096; +using PlayerRoles.Spectating; +using PluginAPI.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UltimateAFK.API; +using UltimateAFK.Handlers; +using UnityEngine; +using static MapGeneration.ImageGenerator; + +namespace UltimateAFK.Resources.Component +{ + public class NewComponent : MonoBehaviour + { + // ---------- Fields -------- + private Player Owner; + private Scp079Role Scp079Role = null; + private Scp096Role Scp096Role = null; + private readonly UltimateAFK Plugin = UltimateAFK.Singleton; + + // Seconds a player was not moving + private int secondsNotMoving; + // Position in the world + private Vector3 _lastPosition; + // Player camera position + private Vector3 _lastCameraPosition; + // Player camera rotation + private Quaternion _lastCameraRotation; + // Number of times that the player was detected as AFK + private int afkTimes = 0; + // Coroutine that handles afk checking. + private CoroutineHandle checkCoroutine; + + // Cached owner user id + private string ownerUserId; + // ----------- Unity Methods --------------- + + private void Start() + { + if(!Player.TryGet(gameObject, out Owner)) + { + Log.Error($"Error on {nameof(NewComponent)}::{nameof(Start)}: Error on try get player"); + Destroy(this); + return; + } + + // Check if the player is not fully connected to the server. + if (!Owner.IsReady) + { + Log.Debug("Destroying a component because the player is not fully connected to the server", UltimateAFK.Singleton.Config.DebugMode); + Destroy(this); + return; + } + + // Cached Rolebase + switch (Owner.RoleBase) + { + case Scp079Role scp079Role: + { + Scp079Role = scp079Role; + break; + } + case Scp096Role scp096Role: + { + Scp096Role = scp096Role; + break; + } + } + ownerUserId = Owner.UserId; + // Starts the coroutine that checks if the player is moving, the coroutine is cancelled if the gameobject (the player) becomes null or the component is destroyed. + checkCoroutine = Timing.RunCoroutine(AfkStatusChecker().CancelWith(gameObject).CancelWith(this)); + } + + private void OnDestroy() + { + Timing.KillCoroutines(checkCoroutine); + } + + // ---------------- Main method -------------- + + private void CheckAfkStatus() + { + if (!Round.IsRoundStarted || Player.Count < Plugin.Config.MinPlayers || Owner.Role == RoleTypeId.Tutorial && Plugin.Config.IgnoreTut + || Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) + return; + + Vector3 worldPosition = Owner.Position; + Vector3 cameraPosition = Owner.Camera.position; + Quaternion cameraRotation = Owner.Camera.rotation; + + switch (Owner.Role) + { + case RoleTypeId.Scp096: + { + Scp096Role ??= Owner.RoleBase as Scp096Role; + + // Player is moving or its crying o a wall/door + if (Scp096Role.IsAbilityState(Scp096AbilityState.TryingNotToCry) || IsMoving(worldPosition, cameraPosition, cameraRotation)) + { + // Do nothing player is moving + secondsNotMoving = 0; + } + else + { + Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); + + HandleAfkState(); + } + + break; + } + case RoleTypeId.Scp079: + { + Scp079Role ??= Owner.RoleBase as Scp079Role; + cameraPosition = Scp079Role.CameraPosition; + cameraRotation = new Quaternion(Scp079Role.CurrentCamera.HorizontalRotation, Scp079Role.CurrentCamera.VerticalRotation, Scp079Role.CurrentCamera.RollRotation, 0f); + if(IsMoving(worldPosition, cameraPosition, cameraRotation)) + { + // Do nothing player is moving + secondsNotMoving = 0; + } + else + { + Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); + + HandleAfkState(); + } + break; + } + case RoleTypeId.Overwatch: + case RoleTypeId.Filmmaker: + case RoleTypeId.None: + case RoleTypeId.Spectator: + { + // Do nothing player is not alive. + secondsNotMoving = 0; + break; + } + default: + { + // Player is human or a SCP with more simple mechanic arround camera/position + + if(IsMoving(worldPosition, cameraPosition, cameraRotation)) + { + // Do nothing player is moving + secondsNotMoving = 0; + } + else + { + Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); + + HandleAfkState(); + } + break; + } + } + } + + // ---------------- Private methods -------------- + + private bool IsMoving(Vector3 worlPosition, Vector3 cameraPosition, Quaternion cameraRotation) + { + if(worlPosition != _lastPosition || cameraPosition != _lastCameraPosition || cameraRotation != _lastCameraRotation) + { + _lastPosition = worlPosition; + _lastCameraPosition = cameraPosition; + _lastCameraRotation = cameraRotation; + return true; + } + + return false; + } + + private void HandleAfkState() + { + if (secondsNotMoving++ < UltimateAFK.Singleton.Config.AfkTime) return; + + // Calculate grace time + var graceTimeRemaining = UltimateAFK.Singleton.Config.GraceTime - (secondsNotMoving - UltimateAFK.Singleton.Config.AfkTime); + + if (graceTimeRemaining > 0) + { + // The player is being considered afk and is being warned that they have X time to move again or they will be kicked/replaced + Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceTimeRemaining), 2, + shouldClearPrevious: true); + } + else + { + Log.Info($"{Owner.LogName} was detected as AFK"); + secondsNotMoving = 0; + API.AfkEvents.Instance.InvokePlayerAfkDetected(Owner, false); + Replace(Owner, Owner.Role); + } + } + + private void Replace(Player player, RoleTypeId roleType) + { + // Check if role is blacklisted + if (Plugin.Config.RoleTypeBlacklist?.Contains(roleType) == true) + { + Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", Plugin.Config.DebugMode); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + if (Plugin.Config.AfkCount > -1) + { + afkTimes++; + + if (afkTimes >= Plugin.Config.AfkCount) + { + player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); + player.Kick(Plugin.Config.MsgKick); + return; + } + } + + player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + return; + } + + // Get a replacement player + Player replacement = GetReplacement(); + + if(replacement == null) + { + Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + if (Plugin.Config.AfkCount > -1) + { + afkTimes++; + + if (afkTimes >= Plugin.Config.AfkCount) + { + player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); + + player.Kick(Plugin.Config.MsgKick); + + return; + } + } + + player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + } + else + { + Log.Debug($"Replacement Player found: {replacement.LogName}", Plugin.Config.DebugMode); + + SaveData(replacement.UserId, roleType is RoleTypeId.Scp079); + + if (Plugin.Config.AfkCount > -1) + { + afkTimes++; + + if (afkTimes >= Plugin.Config.AfkCount) + { + player.ClearInventory(); + player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); + player.Kick(Plugin.Config.MsgKick); + replacement.SetRole(roleType); + return; + } + } + + Log.Debug($"Cleaning player {player.Nickname} inventory", Plugin.Config.DebugMode); + // Clear player inventory + player.ClearInventory(); + //Send player a broadcast for being too long afk + player.SendBroadcast(Plugin.Config.MsgFspec, 25, shouldClearPrevious: true); + player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); + + // Sends replacement to the role that had the afk + Log.Debug($"Changing replacement player {replacement.LogName} role to {roleType}", Plugin.Config.DebugMode); + replacement.SetRole(roleType); + // Sends player to spectator + Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); + player.SetRole(RoleTypeId.Spectator); + player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplaced, replacement.Nickname), "white"); + } + } + + /// + /// Searches and returns the player with the longest active time in spectator mode. + /// If no valid player is found, it returns null. + /// + /// The player with the longest active time or null if none is found. + private Player GetReplacement() + { + try + { + Player longestSpectator = null; + float maxActiveTime = 0f; + + foreach (var player in Player.GetPlayers()) + { + if (IgnorePlayer(player)) + continue; + + // It is faster to compare an enum than to compare a class. + if (player.Role == RoleTypeId.Spectator && player.RoleBase is SpectatorRole role) + { + if (role.ActiveTime > maxActiveTime) + { + maxActiveTime = role.ActiveTime; + longestSpectator = player; + } + } + } + + return longestSpectator; + } + catch (Exception e) + { + Log.Error($"Error on {nameof(NewComponent)}::{nameof(GetReplacement)}: {e.Message} || typeof {e.GetType()}"); + return null; + } + } + + /// + /// Checks if the provided player should be ignored in the AFK replacement process. + /// + /// The player to check. + /// True if the player should be ignored, otherwise false. + private bool IgnorePlayer(Player player) + { + if (!player.IsReady || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || player.UserId == ownerUserId || player.IsAlive || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || player.UserId.Contains("@npc") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + return true; + + return false; + } + + /// + /// Saves the relevant data of a player for potential AFK replacement. + /// + /// The user ID of the player who will be the replacement. + /// Specifies if the player is SCP-079 (True if SCP-079, False if not). + private void SaveData(string replacementUserId, bool isScp079 = false) + { + AFKData data; + + if (isScp079) + { + if (Owner.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + { + data = new AFKData() + { + NickName = Owner.Nickname, + Position = Owner.Position, + Role = Owner.Role, + Ammo = null, + Health = Owner.Health, + Items = null, + SCP079 = new Scp079Data + { + Role = scp079Role, + Energy = energyManager.CurrentAux, + Experience = tierManager.TotalExp, + } + }; + } + else + { + // Return early if SCP-079 data cannot be obtained + return; + } + } + else + { + var ammo = Extensions.GetAmmo(Owner); + + data = new AFKData() + { + NickName = Owner.Nickname, + Position = Owner.Position, + Role = Owner.Role, + Ammo = ammo, + Health = Owner.Health, + Items = Owner.GetItems(), + SCP079 = new Scp079Data + { + Role = null, + Energy = 0f, + Experience = 0 + } + }; + } + + MainHandler.ReplacingPlayersData.Add(replacementUserId, data); + } + + + // ---------------- Coroutines ------------------- + + /// +/// Coroutine that continuously checks and updates the AFK status of players. +/// + private IEnumerator AfkStatusChecker() + { + + while (true) + { + try + { + CheckAfkStatus(); + } + catch (Exception e) + { + Log.Error($"Error on {nameof(NewComponent)}::{nameof(AfkStatusChecker)}: {e.Message} || typeof {e.GetType()}"); + } + + yield return Timing.WaitForSeconds(1); + } + } + } +} From 145d5eb9a888703feb384ecce0cf26f8eb2e6d1d Mon Sep 17 00:00:00 2001 From: SrLicht Date: Thu, 3 Aug 2023 17:24:25 -0300 Subject: [PATCH 133/146] 6.4.0 * Now when using the .afk command the player will have to stay still for X amount of time, if he moves or dies the command will be cancelled. * It is possible to put a limit of uses to the .afk command, the number of uses increases when the command is successfully executed (when you are moved to spectator). * Now when looking for a replacement, we will look for the player in spectator who has been in that role the longest. * It is no longer possible for players in Overwatch to be chosen as replacements * It is recommended to delete the ``on_success`` configuration due to the changes made, it is not mandatory. --- Cerberus.props | 4 +- UltimateAFK/API/AfkEvents.cs | 7 +- UltimateAFK/Command/AfkCommand.cs | 284 ++++++++---- UltimateAFK/Config.cs | 26 +- UltimateAFK/Handlers/MainHandler.cs | 9 +- .../Resources/Component/AFKComponent.cs | 414 ++++++++++------- .../Resources/Component/NewComponent.cs | 428 ------------------ UltimateAFK/Resources/Extensions.cs | 2 +- UltimateAFK/Resources/Scp079Data.cs | 4 - UltimateAFK/UltimateAFK.cs | 15 +- 10 files changed, 503 insertions(+), 690 deletions(-) delete mode 100644 UltimateAFK/Resources/Component/NewComponent.cs diff --git a/Cerberus.props b/Cerberus.props index 587ac8b..af561cf 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,14 +16,14 @@ - 6.3.4 + 6.4.0 false 2.2.1 9.1.4 1.1.118 - 1.3.0 + 6.4.0 Copyright © $(Authors) 2020 - $([System.DateTime]::Now.ToString("yyyy")) Git diff --git a/UltimateAFK/API/AfkEvents.cs b/UltimateAFK/API/AfkEvents.cs index 41c6338..84aa287 100644 --- a/UltimateAFK/API/AfkEvents.cs +++ b/UltimateAFK/API/AfkEvents.cs @@ -1,9 +1,4 @@ using PluginAPI.Core; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace UltimateAFK.API { @@ -33,6 +28,6 @@ public void InvokePlayerAfkDetected(Player player, bool isForCommand) // Prevent extarnal instances. private AfkEvents() { } - + } } diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index d38cc9f..b3ec990 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -1,17 +1,19 @@ using CommandSystem; using CustomPlayerEffects; using MapGeneration; +using MEC; using NWAPIPermissionSystem; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; +using PlayerRoles.Spectating; using PluginAPI.Core; using System; using System.Collections.Generic; -using System.Linq; +using UltimateAFK.API; using UltimateAFK.Handlers; using UltimateAFK.Resources; +using UltimateAFK.Resources.Component; using UnityEngine; -using Utils.NonAllocLINQ; namespace UltimateAFK.Command { @@ -19,11 +21,14 @@ namespace UltimateAFK.Command public class AfkCommand : ICommand { public string Command { get; } = "afk"; - public string[] Aliases { get; } + public string[] Aliases { get; } = Array.Empty(); public string Description { get; } = "By using this command you will be moved to spectator and if the server allows it a player will replace you."; public Dictionary InCooldown = new(); - private UltimateAFK Plugin = UltimateAFK.Singleton; + + public static Dictionary CommandUses = new(); + public static List InProcess = new(); + public static List AllCoroutines = new(); public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) { @@ -41,10 +46,9 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } var ply = Player.Get(sender); - - if(ply == null) + if (ply == null || !ply.IsReady) { - response = "Player is null"; + response = "The player is not fully connected to the server. you cannot use this command, please try to reconnect to the server."; return false; } if (!ply.IsAlive) @@ -84,11 +88,21 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnElevatorMoving; return false; } - if(ply.TemporaryData.StoredData.ContainsKey("uafk_disable")) + if (ply.TemporaryData.StoredData.ContainsKey("uafk_disable")) { response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnUafkDisable; return false; } + if (CommandUses.TryGetValue(ply.UserId, out var uses) && UltimateAFK.Singleton.Config.CommandConfig.UseLimitsPerRound > 0 && uses > UltimateAFK.Singleton.Config.CommandConfig.UseLimitsPerRound) + { + response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnLimit; + return false; + } + if (InProcess.Contains(ply.UserId)) + { + response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnTryToExecute, UltimateAFK.Singleton.Config.CommandConfig.SecondsStill); + return false; + } if (InCooldown.TryGetValue(ply.UserId, out var cooldown)) { // In cooldown @@ -107,93 +121,177 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } } - Replace(ply, ply.Role); + AllCoroutines.Add(Timing.RunCoroutine(AfkCountdown(ply, ply.UserId, ply.Role, ply.Position))); InCooldown.Add(ply.UserId, Time.time + UltimateAFK.Singleton.Config.CommandConfig.Cooldown); - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnSuccess; + response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnSuccess, UltimateAFK.Singleton.Config.CommandConfig.SecondsStill); return true; } catch (Exception e) { - Log.Error($"Error on {GetType().Name}.{nameof(Execute)}: {e}"); - response = $"Error on {nameof(Execute)}: {e}"; + Log.Error($"Error on {nameof(AfkCommand)}::{nameof(Execute)}: {e} || Typeof {e.GetType()}"); + response = $"Error on {nameof(AfkCommand)}::{nameof(Execute)}: {e.Message}"; return false; } } - private void Replace(Player player, RoleTypeId roleType) + private IEnumerator AfkCountdown(Player player, string userid, RoleTypeId roleType, Vector3 position) { - // Check if role is blacklisted - if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Count > 0 && UltimateAFK.Singleton.Config.RoleTypeBlacklist.Contains(roleType)) + InProcess.Add(userid); + + for (int i = UltimateAFK.Singleton.Config.CommandConfig.SecondsStill; i >= 0; i--) { - Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); + yield return Timing.WaitForSeconds(1); - player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); + if (player == null || !player.IsAlive || !Round.IsRoundStarted) + { + Log.Info("Killing coroutine in first if"); + InProcess.Remove(userid); + yield break; + } + + if (position != player.Position) + { + InProcess.Remove(userid); + player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnMoving, UltimateAFK.Singleton.Config.CommandConfig.SecondsStill), "red"); + player.ReceiveHint(string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnMoving, UltimateAFK.Singleton.Config.CommandConfig.SecondsStill), 5); + yield break; + } - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - return; + string text = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnWaitingForAfk, i); + player.ReceiveHint(text, 1); + player.SendConsoleMessage(text, "red"); } - - // Get player replacement - Player replacement = GetReplacement(player.UserId); - - // If no replacement player is found, I change the player's role to spectator - if (replacement is null) + + Replace(player, roleType); + AfkEvents.Instance.InvokePlayerAfkDetected(player, true); + if (UltimateAFK.Singleton.Config.CommandConfig.UseLimitsPerRound > 0) + AddUse(userid); + InProcess.Remove(userid); + } + + private void Replace(Player player, RoleTypeId roleType) + { + try { - Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); - //player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + // Check if role is blacklisted + if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Contains(roleType) == true) + { + Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + return; + } + + // Get a replacement player + Player replacement = GetReplacement(player.UserId); + if (replacement == null) + { + Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + } + else + { + Log.Debug($"Replacement Player found: {replacement.LogName}", UltimateAFK.Singleton.Config.DebugMode); + + SaveData(player, replacement.UserId, roleType is RoleTypeId.Scp079); + + Log.Debug($"Cleaning player {player.Nickname} inventory", UltimateAFK.Singleton.Config.DebugMode); + // Clear player inventory + player.ClearInventory(); + //Send player a broadcast for being too long afk + player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); + player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); + + // Sends replacement to the role that had the afk + Log.Debug($"Changing replacement player {replacement.LogName} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); + replacement.SetRole(roleType); + // Sends player to spectator + Log.Debug($"Changing player {player.Nickname} to spectator", UltimateAFK.Singleton.Config.DebugMode); + player.SetRole(RoleTypeId.Spectator); + player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplaced, replacement.Nickname), "white"); + } } - else + catch (Exception e) { - Log.Debug($"Replacement Player found: {replacement.Nickname} ({replacement.UserId})", UltimateAFK.Singleton.Config.DebugMode); - Log.Debug($"Saving data of player {player.Nickname} in the dictionary.", UltimateAFK.Singleton.Config.DebugMode); - - SaveData(player, replacement.UserId, roleType is RoleTypeId.Scp079); - Log.Debug($"Cleaning player {player.Nickname} inventory", UltimateAFK.Singleton.Config.DebugMode); - // Clear player inventory - player.ClearInventory(); - //Send player a broadcast for being too long afk - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - - Log.Debug($"Changing player {player.Nickname} to spectator", UltimateAFK.Singleton.Config.DebugMode); - // Sends player to spectator - player.SetRole(RoleTypeId.Spectator); - // Sends replacement to the role that had the afk - Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); - replacement.SetRole(roleType); - player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplaced, replacement.Nickname), "white"); + Log.Error($"Error on {nameof(AfkCommand)}::{nameof(Replace)}: {e} || Typeof {e.GetType()}"); } } - private Player GetReplacement(string afkUserId) + /// + /// Searches and returns the player with the longest active time in spectator mode. + /// If no valid player is found, it returns null. + /// + /// The player with the longest active time or null if none is found. + private Player GetReplacement(string userid) { - var players = new List(); - - foreach (var player in Player.GetPlayers()) + try { - if (player is null || !player.IsReady || player.IsAlive || player.UserId == afkUserId || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || /*UltimateAFK.Singleton.Config.IgnoreOverwatch && player.IsOverwatchEnabled ||*/ player.TemporaryData.StoredData.ContainsKey("uafk_disable") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) - continue; + Player longestSpectator = null; + float maxActiveTime = 0f; - players.Add(player); + foreach (var player in Player.GetPlayers()) + { + if (IgnorePlayer(player, userid)) + continue; + + // It is faster to compare an enum than to compare a class. + if (player.Role == RoleTypeId.Spectator && player.RoleBase is SpectatorRole role) + { + if (role.ActiveTime > maxActiveTime) + { + maxActiveTime = role.ActiveTime; + longestSpectator = player; + } + } + } + + return longestSpectator; } + catch (Exception e) + { + Log.Error($"Error on {nameof(AfkComponent)}::{nameof(GetReplacement)}: {e.Message} || typeof {e.GetType()}"); + return null; + } + } + + /// + /// Checks if the provided player should be ignored in the AFK replacement process. + /// + /// The player to check. + /// True if the player should be ignored, otherwise false. + private bool IgnorePlayer(Player player, string ignoreUserId) + { + if (!player.IsReady || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || player.UserId == ignoreUserId || player.IsAlive || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || player.UserId.Contains("@npc") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + return true; - return players.Any() ? players.ElementAtOrDefault(UnityEngine.Random.Range(0, players.Count)) : null; + return false; } + /// + /// Saves the relevant data of a player for potential AFK replacement. + /// + /// The user ID of the player who will be the replacement. + /// Specifies if the player is SCP-079 (True if SCP-079, False if not). private void SaveData(Player player, string replacementUserId, bool isScp079 = false) { + AFKData data; + if (isScp079) { - if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) { - var afkData = new AFKData() + data = new AFKData() { NickName = player.Nickname, Position = player.Position, @@ -203,37 +301,59 @@ private void SaveData(Player player, string replacementUserId, bool isScp079 = f Items = null, SCP079 = new Scp079Data { - Role = scp079Role, Energy = energyManager.CurrentAux, Experience = tierManager.TotalExp, } }; - - MainHandler.ReplacingPlayersData.Add(replacementUserId, afkData); } + else + { + // Return early if SCP-079 data cannot be obtained + return; + } + } + else + { + var ammo = Extensions.GetAmmo(player); - return; + data = new AFKData() + { + NickName = player.Nickname, + Position = player.Position, + Role = player.Role, + Ammo = ammo, + Health = player.Health, + Items = player.GetItems(), + SCP079 = new Scp079Data + { + Energy = 0f, + Experience = 0 + } + }; } - var ammo = Extensions.GetAmmo(player); + MainHandler.ReplacingPlayersData.Add(replacementUserId, data); + } - var data = new AFKData() + private void AddUse(string userID) + { + if (CommandUses.TryGetValue(userID, out var uses)) { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = ammo, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Role = null, - Energy = 0f, - Experience = 0 - } - }; + CommandUses[userID] = uses + 1; + } + else + { + CommandUses.Add(userID, 1); + } + } - MainHandler.ReplacingPlayersData.Add(replacementUserId, data); + + public static void ClearAllCachedData() + { + AllCoroutines.ForEach(c => Timing.KillCoroutines(c)); + AllCoroutines.Clear(); + InProcess.Clear(); + CommandUses.Clear(); } } } \ No newline at end of file diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 860e2ca..24a00ef 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -12,11 +12,8 @@ public class Config [Description("If you have any error in the plugin operation activate this and create an Issue in Github https://github.com/SrLicht/Ultimate-AFK/issues")] public bool DebugMode { get; set; } = false; - [Description("This bool activates the logs that easily spams the console, and normally they are not required, that's why they have this separate configuration :)")] - public bool SpamLogs { get; set; } = false; - [Description("When a player is replaced it is called a delay, if when replacing the player the position is not updated correctly, increase this value but it must not exceed 2.5 since it would be too long.")] - public float ReplaceDelay { get; set; } = 1.4f; + public float ReplaceDelay { get; set; } = 1.3f; [Description("If the number of players is less than this the plugin will not work.")] public int MinPlayers { get; set; } = 8; @@ -60,8 +57,11 @@ public class CommandConfig [Description("Is the command enabled on this server ?")] public bool IsEnabled { get; set; } = false; - [Description("Players who use this command will be replaced by players who are in spectator")] - public bool Replace { get; set; } = true; + [Description("When a player uses the command, he must stand still for this amount of seconds to be moved to spectator.")] + public int SecondsStill { get; set; } = 10; + + [Description("The number of times a player can be this command per round")] + public int UseLimitsPerRound { get; set; } = 3; [Description("The coldown of the command when using it")] public float Cooldown { get; set; } = 40f; @@ -93,7 +93,7 @@ public class Responses public string OnDisable { get; set; } = "This command is disabled"; [Description("Response given to the player when successfully executing the command.")] - public string OnSuccess { get; set; } = "You were moved to spectator because you considered yourself AFK."; + public string OnSuccess { get; set; } = "You will be moved to spectator in {0} seconds, stand still."; [Description("Response given to the player when he has no hands")] public string OnSevereHands { get; set; } = "You cannot use this command if you have no hands"; @@ -124,5 +124,17 @@ public class Responses [Description("If a player is inside an elevator he will not be able to use the command because his replacement will fall into the void if the elevator is moving.")] public string OnElevatorMoving { get; set; } = " You cannot use this command while in a moving elevator."; + + [Description("When a player uses the command he will have to wait X seconds to be spectator.")] + public string OnWaitingForAfk { get; set; } = "You will be moved to spectator in {0} seconds."; + + [Description("If a player moves within the time limit this message will be sent to the player's console.")] + public string OnMoving { get; set; } = "You moved, you have to be still for {0} seconds to be moved to spectator."; + + [Description("If a player tries to execute the command when he has already reached his limit per round this message will be sent to the console.")] + public string OnLimit { get; set; } = "You have reached the limit of uses of the command per round."; + + [Description("If a player tries to execute the command while it is being processed to move to spectator he will get this message")] + public string OnTryToExecute { get; set; } = "You are already being processed to move to spectator, you have to stand still for {0} seconds."; } } \ No newline at end of file diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/Handlers/MainHandler.cs index 4531771..22cc530 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/Handlers/MainHandler.cs @@ -7,6 +7,7 @@ using PluginAPI.Enums; using System.Collections.Generic; using System.Linq; +using UltimateAFK.Command; using UltimateAFK.Resources; using UltimateAFK.Resources.Component; @@ -42,9 +43,9 @@ private void OnPlayerJoin(Player player) { if (!Plugin.Config.IsEnabled || player is null || player.UserId.Contains("@server") || !player.IsReady) return; - Log.Debug($"Adding the Component to {player.Nickname}", Plugin.Config.DebugMode); + Log.Debug($"Adding the Component to {player.LogName}", Plugin.Config.DebugMode); - player.GameObject.AddComponent(); + player.GameObject.AddComponent(); } /// @@ -58,7 +59,7 @@ private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId ne if (player == null || !player.IsReady || newRole == RoleTypeId.Spectator || !ReplacingPlayersData.TryGetValue(player.UserId, out var data)) return; - Log.Debug($"Detecting player {player.Nickname} ({player.UserId}) who replaced a player {data.NickName} who was afk", UltimateAFK.Singleton.Config.DebugMode); + Log.Debug($"Detecting player {player.LogName} who replaced a player {data.NickName} who was afk", UltimateAFK.Singleton.Config.DebugMode); Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => GiveData(player, data, newRole)); } @@ -129,11 +130,11 @@ private void GiveData(Player player, AFKData data, RoleTypeId roleType) } } - [PluginEvent(ServerEventType.MapGenerated)] private void OnMapGenerated() { ReplacingPlayersData.Clear(); + AfkCommand.ClearAllCachedData(); Extensions.AllElevators = Map.Elevators.ToList(); } diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs index 2cd2b05..2bfc601 100644 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -3,116 +3,204 @@ using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; using PlayerRoles.PlayableScps.Scp096; +using PlayerRoles.Spectating; using PluginAPI.Core; using System; using System.Collections.Generic; -using System.Linq; using UltimateAFK.Handlers; using UnityEngine; namespace UltimateAFK.Resources.Component { - [RequireComponent(typeof(ReferenceHub))] - public class AFKComponent : MonoBehaviour + public class AfkComponent : MonoBehaviour { - private void Awake() + // ---------- Fields -------- + private Player Owner; + private Scp079Role Scp079Role = null; + private Scp096Role Scp096Role = null; + private readonly UltimateAFK Plugin = UltimateAFK.Singleton; + + // Seconds a player was not moving + private int secondsNotMoving; + // Position in the world + private Vector3 _lastPosition; + // Player camera position + private Vector3 _lastCameraPosition; + // Player camera rotation + private Quaternion _lastCameraRotation; + // Number of times that the player was detected as AFK + private int afkTimes = 0; + // Coroutine that handles afk checking. + private CoroutineHandle checkCoroutine; + // Cached owner user id + private string ownerUserId; + + // ----------- Unity Methods --------------- + + private void Start() { - // Gets PluginAPI.Core.Player from the gameobject. - if (Player.Get(gameObject) is not { } ply) + if (!Player.TryGet(gameObject, out Owner)) { - Log.Error($"{this} Error Getting Player"); + Log.Error($"Error on {nameof(AfkComponent)}::{nameof(Start)}: Error on try get player"); Destroy(this); return; } - // Sets owner variable - Owner = ply; - _ownerId = ply.UserId; + // Check if the player is not fully connected to the server. + if (!Owner.IsReady) + { + Log.Debug("Destroying a component because the player is not fully connected to the server", UltimateAFK.Singleton.Config.DebugMode); + Destroy(this); + return; + } + + // Cached Rolebase + switch (Owner.RoleBase) + { + case Scp079Role scp079Role: + { + Scp079Role = scp079Role; + break; + } + case Scp096Role scp096Role: + { + Scp096Role = scp096Role; + break; + } + } - // Coroutine dies when the component or the ReferenceHub (Player) is destroyed. - _checkHandle = Timing.RunCoroutine(Check().CancelWith(this).CancelWith(gameObject)); + ownerUserId = Owner.UserId; + // Starts the coroutine that checks if the player is moving, the coroutine is cancelled if the gameobject (the player) becomes null or the component is destroyed. + checkCoroutine = Timing.RunCoroutine(AfkStatusChecker().CancelWith(gameObject).CancelWith(this)); } private void OnDestroy() { - Timing.KillCoroutines(_checkHandle); + Timing.KillCoroutines(checkCoroutine); } - private IEnumerator Check() + // ---------------- Main method -------------- + + private void CheckAfkStatus() { - for (; ; ) + if (!Round.IsRoundStarted || Player.Count < Plugin.Config.MinPlayers || Owner.Role == RoleTypeId.Tutorial && Plugin.Config.IgnoreTut + || Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) + return; + + Vector3 worldPosition = Owner.Position; + Vector3 cameraPosition = Owner.Camera.position; + Quaternion cameraRotation = Owner.Camera.rotation; + + switch (Owner.Role) { - yield return Timing.WaitForSeconds(1.3f); + case RoleTypeId.Scp096: + { + Scp096Role ??= Owner.RoleBase as Scp096Role; - Log.Debug("Calling CheckAFK", Plugin.Config.DebugMode && Plugin.Config.SpamLogs); - try - { - AfkCheck(); - } - catch (Exception e) - { - Log.Error($"Error in {nameof(Check)}: &2{e}&r"); - } + // Player is moving or its crying on a wall/door + if (Scp096Role.IsAbilityState(Scp096AbilityState.TryingNotToCry) || IsMoving(worldPosition, cameraPosition, cameraRotation)) + { + // Do nothing player is moving + secondsNotMoving = 0; + } + else + { + Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); + + HandleAfkState(); + } + + break; + } + case RoleTypeId.Scp079: + { + Scp079Role ??= Owner.RoleBase as Scp079Role; + + cameraPosition = Scp079Role.CameraPosition; + cameraRotation = new Quaternion(Scp079Role.CurrentCamera.HorizontalRotation, Scp079Role.CurrentCamera.VerticalRotation, Scp079Role.CurrentCamera.RollRotation, 0f); + if (IsMoving(worldPosition, cameraPosition, cameraRotation)) + { + // Do nothing player is moving + secondsNotMoving = 0; + } + else + { + Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); + + HandleAfkState(); + } + break; + } + case RoleTypeId.Overwatch: + case RoleTypeId.Filmmaker: + case RoleTypeId.None: + case RoleTypeId.Spectator: + { + // Do nothing player is not alive. + secondsNotMoving = 0; + break; + } + default: + { + // Player is human or a SCP with more simple mechanic arround camera/position + + if (IsMoving(worldPosition, cameraPosition, cameraRotation)) + { + // Do nothing player is moving + secondsNotMoving = 0; + } + else + { + Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); + + HandleAfkState(); + } + break; + } } } - private void AfkCheck() + private void HandleAfkState() { - if (!UglyCheck()) - return; - - if (Owner.Role is RoleTypeId.Tutorial && Plugin.Config.IgnoreTut) - return; - - // With this you can temporarily deactivate a player's check. - if (Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) - return; + if (secondsNotMoving++ < UltimateAFK.Singleton.Config.AfkTime) return; - // save current player position - var worldPosition = Owner.Position; - var cameraPosition = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp079 ? scp079.CurrentCamera.CameraPosition : Owner.Camera.position; - var cameraRotation = Owner.ReferenceHub.roleManager.CurrentRole is Scp079Role scp0792 ? new Quaternion(scp0792.CurrentCamera.HorizontalRotation, scp0792.CurrentCamera.VerticalRotation, scp0792.CurrentCamera.RollRotation, 0f) : Owner.Camera.rotation; + // Calculate grace time + var graceTimeRemaining = UltimateAFK.Singleton.Config.GraceTime - (secondsNotMoving - UltimateAFK.Singleton.Config.AfkTime); - // Player is moving. - if (cameraPosition != _lastCameraPosition || worldPosition != _lastPosition || _lastCameraRotation != cameraRotation) + if (graceTimeRemaining > 0) { - _lastCameraPosition = cameraPosition; - _lastCameraRotation = cameraRotation; - _lastPosition = worldPosition; - _afkTime = 0f; + // The player is being considered afk and is being warned that they have X time to move again or they will be kicked/replaced + Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceTimeRemaining), 2, + shouldClearPrevious: true); } else { - // Check if the player role is Scp096 and current state is in TryNotToCry if it is return. - if (Owner.Role == RoleTypeId.Scp096 && (Owner.RoleBase as Scp096Role).IsAbilityState(Scp096AbilityState.TryingNotToCry)) - return; - - Log.Debug($"{Owner.Nickname} is in not moving, AFKTime: {_afkTime}", UltimateAFK.Singleton.Config.DebugMode); - // If the time the player is afk is less than the limit, return. - if (_afkTime++ < UltimateAFK.Singleton.Config.AfkTime) return; + Log.Info($"{Owner.LogName} was detected as AFK"); + secondsNotMoving = 0; + API.AfkEvents.Instance.InvokePlayerAfkDetected(Owner, false); + Replace(Owner, Owner.Role); + } + } - // Get grace time - var graceNumb = UltimateAFK.Singleton.Config.GraceTime - (_afkTime - UltimateAFK.Singleton.Config.AfkTime); + // ---------------- Private methods -------------- - if (graceNumb > 0) - { - // The player is being considered afk and is being warned that they have X time to move again or they will be kicked/replaced - Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceNumb), 2, - shouldClearPrevious: true); - } - else - { - Log.Info($"{Owner.Nickname} ({Owner.UserId}) was detected as AFK"); - _afkTime = 0f; - Replace(Owner, Owner.Role); - } + private bool IsMoving(Vector3 worlPosition, Vector3 cameraPosition, Quaternion cameraRotation) + { + if (worlPosition != _lastPosition || cameraPosition != _lastCameraPosition || cameraRotation != _lastCameraRotation) + { + _lastPosition = worlPosition; + _lastCameraPosition = cameraPosition; + _lastCameraRotation = cameraRotation; + return true; } + + return false; } private void Replace(Player player, RoleTypeId roleType) { // Check if role is blacklisted - if (Plugin.Config.RoleTypeBlacklist?.Count > 0 && Plugin.Config.RoleTypeBlacklist.Contains(roleType)) + if (Plugin.Config.RoleTypeBlacklist?.Contains(roleType) == true) { Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", Plugin.Config.DebugMode); @@ -121,9 +209,9 @@ private void Replace(Player player, RoleTypeId roleType) if (Plugin.Config.AfkCount > -1) { - AfkTimes++; + afkTimes++; - if (AfkTimes >= Plugin.Config.AfkCount) + if (afkTimes >= Plugin.Config.AfkCount) { player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); player.Kick(Plugin.Config.MsgKick); @@ -136,10 +224,9 @@ private void Replace(Player player, RoleTypeId roleType) return; } - // Get player replacement + // Get a replacement player Player replacement = GetReplacement(); - // If no replacement player is found, I change the player's role to spectator if (replacement == null) { Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); @@ -149,9 +236,9 @@ private void Replace(Player player, RoleTypeId roleType) if (Plugin.Config.AfkCount > -1) { - AfkTimes++; + afkTimes++; - if (AfkTimes >= Plugin.Config.AfkCount) + if (afkTimes >= Plugin.Config.AfkCount) { player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); @@ -166,20 +253,19 @@ private void Replace(Player player, RoleTypeId roleType) } else { - Log.Debug($"Replacement Player found: {replacement.Nickname} ({replacement.UserId})", Plugin.Config.DebugMode); - Log.Debug($"Saving data of player {player.Nickname} in the dictionary.", Plugin.Config.DebugMode); + Log.Debug($"Replacement Player found: {replacement.LogName}", Plugin.Config.DebugMode); + SaveData(replacement.UserId, roleType is RoleTypeId.Scp079); if (Plugin.Config.AfkCount > -1) { - AfkTimes++; + afkTimes++; - if (AfkTimes >= Plugin.Config.AfkCount) + if (afkTimes >= Plugin.Config.AfkCount) { + player.ClearInventory(); player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); - player.Kick(Plugin.Config.MsgKick); - replacement.SetRole(roleType); return; } @@ -193,7 +279,7 @@ private void Replace(Player player, RoleTypeId roleType) player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); // Sends replacement to the role that had the afk - Log.Debug($"Changing replacement player {replacement.Nickname} role to {roleType}", Plugin.Config.DebugMode); + Log.Debug($"Changing replacement player {replacement.LogName} role to {roleType}", Plugin.Config.DebugMode); replacement.SetRole(roleType); // Sends player to spectator Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); @@ -202,14 +288,71 @@ private void Replace(Player player, RoleTypeId roleType) } } + /// + /// Searches and returns the player with the longest active time in spectator mode. + /// If no valid player is found, it returns null. + /// + /// The player with the longest active time or null if none is found. + private Player GetReplacement() + { + try + { + Player longestSpectator = null; + float maxActiveTime = 0f; + + foreach (var player in Player.GetPlayers()) + { + if (IgnorePlayer(player)) + continue; + + // It is faster to compare an enum than to compare a class. + if (player.Role == RoleTypeId.Spectator && player.RoleBase is SpectatorRole role) + { + if (role.ActiveTime > maxActiveTime) + { + maxActiveTime = role.ActiveTime; + longestSpectator = player; + } + } + } + + return longestSpectator; + } + catch (Exception e) + { + Log.Error($"Error on {nameof(AfkComponent)}::{nameof(GetReplacement)}: {e.Message} || typeof {e.GetType()}"); + return null; + } + } + + /// + /// Checks if the provided player should be ignored in the AFK replacement process. + /// + /// The player to check. + /// True if the player should be ignored, otherwise false. + private bool IgnorePlayer(Player player) + { + if (!player.IsReady || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || player.UserId == ownerUserId || player.IsAlive || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || player.UserId.Contains("@npc") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + return true; + + return false; + } + + /// + /// Saves the relevant data of a player for potential AFK replacement. + /// + /// The user ID of the player who will be the replacement. + /// Specifies if the player is SCP-079 (True if SCP-079, False if not). private void SaveData(string replacementUserId, bool isScp079 = false) { + AFKData data; + if (isScp079) { if (Owner.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) { - var afkData = new AFKData() + data = new AFKData() { NickName = Owner.Nickname, Position = Owner.Position, @@ -219,97 +362,62 @@ private void SaveData(string replacementUserId, bool isScp079 = false) Items = null, SCP079 = new Scp079Data { - Role = scp079Role, Energy = energyManager.CurrentAux, Experience = tierManager.TotalExp, } }; - Log.Info($"Datos guardados: {afkData.SCP079.Experience} | {afkData.SCP079.Experience}"); - MainHandler.ReplacingPlayersData.Add(replacementUserId, afkData); } - - return; + else + { + // Return early if SCP-079 data cannot be obtained + return; + } } - - var ammo = Extensions.GetAmmo(Owner); - - var data = new AFKData() + else { - NickName = Owner.Nickname, - Position = Owner.Position, - Role = Owner.Role, - Ammo = ammo, - Health = Owner.Health, - Items = Owner.GetItems(), - SCP079 = new Scp079Data + var ammo = Extensions.GetAmmo(Owner); + + data = new AFKData() { - Role = null, - Energy = 0f, - Experience = 0 - } - }; + NickName = Owner.Nickname, + Position = Owner.Position, + Role = Owner.Role, + Ammo = ammo, + Health = Owner.Health, + Items = Owner.GetItems(), + SCP079 = new Scp079Data + { + Energy = 0f, + Experience = 0 + } + }; + } MainHandler.ReplacingPlayersData.Add(replacementUserId, data); } + + // ---------------- Coroutines ------------------- + /// - /// Obtains a player who qualifies for replacement. + /// Coroutine that continuously checks and updates the AFK status of players. /// - private Player GetReplacement() + private IEnumerator AfkStatusChecker() { - try - { - var players = new List(); - foreach (var player in Player.GetPlayers()) + while (true) + { + try { - if(player is null) continue; - - if (player.IsAlive || player == Owner || !player.IsReady || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") - || /*Plugin.Config.IgnoreOverwatch && player.IsOverwatchEnabled ||*/ player.TemporaryData.StoredData.ContainsKey("uafk_disable") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) - continue; - - players.Add(player); + CheckAfkStatus(); + } + catch (Exception e) + { + Log.Error($"Error on {nameof(AfkComponent)}::{nameof(AfkStatusChecker)}: {e.Message} || typeof {e.GetType()}"); } - return players.Any() ? players.ElementAtOrDefault(UnityEngine.Random.Range(0, players.Count)) : null; - } - catch (Exception e) - { - Log.Error($"Error in {nameof(GetReplacement)} of type {e.GetType()}: {e} "); - return null; + yield return Timing.WaitForSeconds(1); } } - - private bool UglyCheck() - { - return Owner.IsAlive && Round.IsRoundStarted && Player.Count >= UltimateAFK.Singleton.Config.MinPlayers; - } - - // Owner PluginAPI.Core.Player - private Player Owner; - - // How many times was the owner afk - private int AfkTimes; - - // Owner UserID - private string _ownerId; - - // Position in the world - private Vector3 _lastPosition; - - // Player camera position - private Vector3 _lastCameraPosition; - - // Player camera rotation - private Quaternion _lastCameraRotation; - - // The time the player was afk - private float _afkTime; - - // Using a MEC Coroutine is more optimized than using Unity methods. - private CoroutineHandle _checkHandle; - - private readonly UltimateAFK Plugin = UltimateAFK.Singleton; } } diff --git a/UltimateAFK/Resources/Component/NewComponent.cs b/UltimateAFK/Resources/Component/NewComponent.cs deleted file mode 100644 index f082775..0000000 --- a/UltimateAFK/Resources/Component/NewComponent.cs +++ /dev/null @@ -1,428 +0,0 @@ -using MEC; -using NWAPIPermissionSystem; -using PlayerRoles; -using PlayerRoles.PlayableScps.Scp079; -using PlayerRoles.PlayableScps.Scp096; -using PlayerRoles.Spectating; -using PluginAPI.Core; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UltimateAFK.API; -using UltimateAFK.Handlers; -using UnityEngine; -using static MapGeneration.ImageGenerator; - -namespace UltimateAFK.Resources.Component -{ - public class NewComponent : MonoBehaviour - { - // ---------- Fields -------- - private Player Owner; - private Scp079Role Scp079Role = null; - private Scp096Role Scp096Role = null; - private readonly UltimateAFK Plugin = UltimateAFK.Singleton; - - // Seconds a player was not moving - private int secondsNotMoving; - // Position in the world - private Vector3 _lastPosition; - // Player camera position - private Vector3 _lastCameraPosition; - // Player camera rotation - private Quaternion _lastCameraRotation; - // Number of times that the player was detected as AFK - private int afkTimes = 0; - // Coroutine that handles afk checking. - private CoroutineHandle checkCoroutine; - - // Cached owner user id - private string ownerUserId; - // ----------- Unity Methods --------------- - - private void Start() - { - if(!Player.TryGet(gameObject, out Owner)) - { - Log.Error($"Error on {nameof(NewComponent)}::{nameof(Start)}: Error on try get player"); - Destroy(this); - return; - } - - // Check if the player is not fully connected to the server. - if (!Owner.IsReady) - { - Log.Debug("Destroying a component because the player is not fully connected to the server", UltimateAFK.Singleton.Config.DebugMode); - Destroy(this); - return; - } - - // Cached Rolebase - switch (Owner.RoleBase) - { - case Scp079Role scp079Role: - { - Scp079Role = scp079Role; - break; - } - case Scp096Role scp096Role: - { - Scp096Role = scp096Role; - break; - } - } - ownerUserId = Owner.UserId; - // Starts the coroutine that checks if the player is moving, the coroutine is cancelled if the gameobject (the player) becomes null or the component is destroyed. - checkCoroutine = Timing.RunCoroutine(AfkStatusChecker().CancelWith(gameObject).CancelWith(this)); - } - - private void OnDestroy() - { - Timing.KillCoroutines(checkCoroutine); - } - - // ---------------- Main method -------------- - - private void CheckAfkStatus() - { - if (!Round.IsRoundStarted || Player.Count < Plugin.Config.MinPlayers || Owner.Role == RoleTypeId.Tutorial && Plugin.Config.IgnoreTut - || Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) - return; - - Vector3 worldPosition = Owner.Position; - Vector3 cameraPosition = Owner.Camera.position; - Quaternion cameraRotation = Owner.Camera.rotation; - - switch (Owner.Role) - { - case RoleTypeId.Scp096: - { - Scp096Role ??= Owner.RoleBase as Scp096Role; - - // Player is moving or its crying o a wall/door - if (Scp096Role.IsAbilityState(Scp096AbilityState.TryingNotToCry) || IsMoving(worldPosition, cameraPosition, cameraRotation)) - { - // Do nothing player is moving - secondsNotMoving = 0; - } - else - { - Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); - - HandleAfkState(); - } - - break; - } - case RoleTypeId.Scp079: - { - Scp079Role ??= Owner.RoleBase as Scp079Role; - cameraPosition = Scp079Role.CameraPosition; - cameraRotation = new Quaternion(Scp079Role.CurrentCamera.HorizontalRotation, Scp079Role.CurrentCamera.VerticalRotation, Scp079Role.CurrentCamera.RollRotation, 0f); - if(IsMoving(worldPosition, cameraPosition, cameraRotation)) - { - // Do nothing player is moving - secondsNotMoving = 0; - } - else - { - Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); - - HandleAfkState(); - } - break; - } - case RoleTypeId.Overwatch: - case RoleTypeId.Filmmaker: - case RoleTypeId.None: - case RoleTypeId.Spectator: - { - // Do nothing player is not alive. - secondsNotMoving = 0; - break; - } - default: - { - // Player is human or a SCP with more simple mechanic arround camera/position - - if(IsMoving(worldPosition, cameraPosition, cameraRotation)) - { - // Do nothing player is moving - secondsNotMoving = 0; - } - else - { - Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); - - HandleAfkState(); - } - break; - } - } - } - - // ---------------- Private methods -------------- - - private bool IsMoving(Vector3 worlPosition, Vector3 cameraPosition, Quaternion cameraRotation) - { - if(worlPosition != _lastPosition || cameraPosition != _lastCameraPosition || cameraRotation != _lastCameraRotation) - { - _lastPosition = worlPosition; - _lastCameraPosition = cameraPosition; - _lastCameraRotation = cameraRotation; - return true; - } - - return false; - } - - private void HandleAfkState() - { - if (secondsNotMoving++ < UltimateAFK.Singleton.Config.AfkTime) return; - - // Calculate grace time - var graceTimeRemaining = UltimateAFK.Singleton.Config.GraceTime - (secondsNotMoving - UltimateAFK.Singleton.Config.AfkTime); - - if (graceTimeRemaining > 0) - { - // The player is being considered afk and is being warned that they have X time to move again or they will be kicked/replaced - Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceTimeRemaining), 2, - shouldClearPrevious: true); - } - else - { - Log.Info($"{Owner.LogName} was detected as AFK"); - secondsNotMoving = 0; - API.AfkEvents.Instance.InvokePlayerAfkDetected(Owner, false); - Replace(Owner, Owner.Role); - } - } - - private void Replace(Player player, RoleTypeId roleType) - { - // Check if role is blacklisted - if (Plugin.Config.RoleTypeBlacklist?.Contains(roleType) == true) - { - Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", Plugin.Config.DebugMode); - - player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); - - if (Plugin.Config.AfkCount > -1) - { - afkTimes++; - - if (afkTimes >= Plugin.Config.AfkCount) - { - player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); - player.Kick(Plugin.Config.MsgKick); - return; - } - } - - player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); - return; - } - - // Get a replacement player - Player replacement = GetReplacement(); - - if(replacement == null) - { - Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); - - player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); - - if (Plugin.Config.AfkCount > -1) - { - afkTimes++; - - if (afkTimes >= Plugin.Config.AfkCount) - { - player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); - - player.Kick(Plugin.Config.MsgKick); - - return; - } - } - - player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); - } - else - { - Log.Debug($"Replacement Player found: {replacement.LogName}", Plugin.Config.DebugMode); - - SaveData(replacement.UserId, roleType is RoleTypeId.Scp079); - - if (Plugin.Config.AfkCount > -1) - { - afkTimes++; - - if (afkTimes >= Plugin.Config.AfkCount) - { - player.ClearInventory(); - player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); - player.Kick(Plugin.Config.MsgKick); - replacement.SetRole(roleType); - return; - } - } - - Log.Debug($"Cleaning player {player.Nickname} inventory", Plugin.Config.DebugMode); - // Clear player inventory - player.ClearInventory(); - //Send player a broadcast for being too long afk - player.SendBroadcast(Plugin.Config.MsgFspec, 25, shouldClearPrevious: true); - player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); - - // Sends replacement to the role that had the afk - Log.Debug($"Changing replacement player {replacement.LogName} role to {roleType}", Plugin.Config.DebugMode); - replacement.SetRole(roleType); - // Sends player to spectator - Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); - player.SetRole(RoleTypeId.Spectator); - player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplaced, replacement.Nickname), "white"); - } - } - - /// - /// Searches and returns the player with the longest active time in spectator mode. - /// If no valid player is found, it returns null. - /// - /// The player with the longest active time or null if none is found. - private Player GetReplacement() - { - try - { - Player longestSpectator = null; - float maxActiveTime = 0f; - - foreach (var player in Player.GetPlayers()) - { - if (IgnorePlayer(player)) - continue; - - // It is faster to compare an enum than to compare a class. - if (player.Role == RoleTypeId.Spectator && player.RoleBase is SpectatorRole role) - { - if (role.ActiveTime > maxActiveTime) - { - maxActiveTime = role.ActiveTime; - longestSpectator = player; - } - } - } - - return longestSpectator; - } - catch (Exception e) - { - Log.Error($"Error on {nameof(NewComponent)}::{nameof(GetReplacement)}: {e.Message} || typeof {e.GetType()}"); - return null; - } - } - - /// - /// Checks if the provided player should be ignored in the AFK replacement process. - /// - /// The player to check. - /// True if the player should be ignored, otherwise false. - private bool IgnorePlayer(Player player) - { - if (!player.IsReady || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || player.UserId == ownerUserId || player.IsAlive || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || player.UserId.Contains("@npc") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) - return true; - - return false; - } - - /// - /// Saves the relevant data of a player for potential AFK replacement. - /// - /// The user ID of the player who will be the replacement. - /// Specifies if the player is SCP-079 (True if SCP-079, False if not). - private void SaveData(string replacementUserId, bool isScp079 = false) - { - AFKData data; - - if (isScp079) - { - if (Owner.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) - { - data = new AFKData() - { - NickName = Owner.Nickname, - Position = Owner.Position, - Role = Owner.Role, - Ammo = null, - Health = Owner.Health, - Items = null, - SCP079 = new Scp079Data - { - Role = scp079Role, - Energy = energyManager.CurrentAux, - Experience = tierManager.TotalExp, - } - }; - } - else - { - // Return early if SCP-079 data cannot be obtained - return; - } - } - else - { - var ammo = Extensions.GetAmmo(Owner); - - data = new AFKData() - { - NickName = Owner.Nickname, - Position = Owner.Position, - Role = Owner.Role, - Ammo = ammo, - Health = Owner.Health, - Items = Owner.GetItems(), - SCP079 = new Scp079Data - { - Role = null, - Energy = 0f, - Experience = 0 - } - }; - } - - MainHandler.ReplacingPlayersData.Add(replacementUserId, data); - } - - - // ---------------- Coroutines ------------------- - - /// -/// Coroutine that continuously checks and updates the AFK status of players. -/// - private IEnumerator AfkStatusChecker() - { - - while (true) - { - try - { - CheckAfkStatus(); - } - catch (Exception e) - { - Log.Error($"Error on {nameof(NewComponent)}::{nameof(AfkStatusChecker)}: {e.Message} || typeof {e.GetType()}"); - } - - yield return Timing.WaitForSeconds(1); - } - } - } -} diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs index c836f25..081e452 100644 --- a/UltimateAFK/Resources/Extensions.cs +++ b/UltimateAFK/Resources/Extensions.cs @@ -104,7 +104,7 @@ public static void ReloadAllWeapons(this Player ply) /// public static bool InElevator(this Player player) { - foreach(var elevator in AllElevators) + foreach (var elevator in AllElevators) { if (elevator.WorldspaceBounds.Contains(player.Position) && elevator.IsMoving()) return true; diff --git a/UltimateAFK/Resources/Scp079Data.cs b/UltimateAFK/Resources/Scp079Data.cs index 1278f5e..88de869 100644 --- a/UltimateAFK/Resources/Scp079Data.cs +++ b/UltimateAFK/Resources/Scp079Data.cs @@ -1,11 +1,7 @@ -using PlayerRoles.PlayableScps.Scp079; - namespace UltimateAFK.Resources { public struct Scp079Data { - public Scp079Role Role { get; set; } - public int Experience { get; set; } public float Energy { get; set; } diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index e4c39b3..7661e57 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,8 +1,7 @@ using PluginAPI.Core; using PluginAPI.Core.Attributes; using PluginAPI.Enums; -using System.Linq; -using System.Reflection; +using UltimateAFK.API; using UltimateAFK.Handlers; using UltimateAFK.Resources; @@ -18,11 +17,12 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.3.7", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.4.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; PluginAPI.Events.EventManager.RegisterEvents(this, new MainHandler(Singleton)); + AfkEvents.Instance.PlayerAfkDetectedEvent += OnPlayerIsDetectedAfk; } [PluginUnload] @@ -31,6 +31,15 @@ void OnDisable() MainHandler.ReplacingPlayersData.Clear(); MainHandler.ReplacingPlayersData = null; Extensions.AllElevators.Clear(); + AfkEvents.Instance.PlayerAfkDetectedEvent -= OnPlayerIsDetectedAfk; + } + + public void OnPlayerIsDetectedAfk(Player player, bool isForCommand) + { + if (isForCommand) + { + Log.Info($"{player.LogName} use the command to be moved to spectator"); + } } } } \ No newline at end of file From 30e011b105671ffa9e40bf7c97cabe6420678fbe Mon Sep 17 00:00:00 2001 From: SrLicht Date: Fri, 1 Sep 2023 15:28:17 -0300 Subject: [PATCH 134/146] 6.4.1 * Recompile for SL 13.2.0 --- UltimateAFK/UltimateAFK.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 7661e57..acf81a2 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.4.0", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.4.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 3924869540d3f7a6abad661a2ed4af8c77061686 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 2 Sep 2023 16:40:49 -0300 Subject: [PATCH 135/146] 6.4.1.1 Rebuild for pluginapi-beta branch --- UltimateAFK/UltimateAFK.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index acf81a2..7dd0451 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,10 +17,11 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.4.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.4.1.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; + Log.Warning("This version of the plugin is only compatible with the \"plugin-beta\" branch of NWAPI."); PluginAPI.Events.EventManager.RegisterEvents(this, new MainHandler(Singleton)); AfkEvents.Instance.PlayerAfkDetectedEvent += OnPlayerIsDetectedAfk; } From 70e51fedc94009821eb83e51dba8a116c3fd438b Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 2 Sep 2023 16:42:03 -0300 Subject: [PATCH 136/146] Update UltimateAFK.cs --- UltimateAFK/UltimateAFK.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 7dd0451..cc95f36 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -21,7 +21,7 @@ public class UltimateAFK void OnEnabled() { Singleton = this; - Log.Warning("This version of the plugin is only compatible with the \"plugin-beta\" branch of NWAPI."); + Log.Warning("This version of the plugin is only compatible with the \"pluginapi-beta\" branch of NWAPI."); PluginAPI.Events.EventManager.RegisterEvents(this, new MainHandler(Singleton)); AfkEvents.Instance.PlayerAfkDetectedEvent += OnPlayerIsDetectedAfk; } From d5788180bad6dc5ab95439e9ea6dc86712794009 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sat, 28 Oct 2023 19:13:28 -0300 Subject: [PATCH 137/146] 13.3.0 --- UltimateAFK/UltimateAFK.cs | 2 +- UltimateAFK/UltimateAFK.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index cc95f36..3cfe9dd 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -17,7 +17,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.4.1.1", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.4.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index fc442fa..0163541 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -24,12 +24,12 @@ - - + + From 6838ea9365348eb06d56efbf7c736207001f3b90 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sun, 29 Oct 2023 10:06:17 -0300 Subject: [PATCH 138/146] 6.4.3 * Removing log.warning on plugin load. * Trying to fix the bug that when a SCP is replaced the termination announcement is played. --- Cerberus.props | 2 +- UltimateAFK/Resources/Component/AFKComponent.cs | 13 +++++++++++-- UltimateAFK/UltimateAFK.cs | 6 +++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Cerberus.props b/Cerberus.props index af561cf..2e53af4 100644 --- a/Cerberus.props +++ b/Cerberus.props @@ -16,7 +16,7 @@ - 6.4.0 + 6.4.3 false diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs index 2bfc601..871b604 100644 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -7,6 +7,7 @@ using PluginAPI.Core; using System; using System.Collections.Generic; +using System.Drawing; using UltimateAFK.Handlers; using UnityEngine; @@ -232,7 +233,11 @@ private void Replace(Player player, RoleTypeId roleType) Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); + + if (player.IsSCP) + player.ReferenceHub.roleManager.ServerSetRole(RoleTypeId.Spectator, RoleChangeReason.RemoteAdmin, RoleSpawnFlags.None); + else + player.SetRole(RoleTypeId.Spectator); if (Plugin.Config.AfkCount > -1) { @@ -283,7 +288,11 @@ private void Replace(Player player, RoleTypeId roleType) replacement.SetRole(roleType); // Sends player to spectator Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); - player.SetRole(RoleTypeId.Spectator); + + if (player.IsSCP) + player.ReferenceHub.roleManager.ServerSetRole(RoleTypeId.Spectator, RoleChangeReason.RemoteAdmin, RoleSpawnFlags.None); + else + player.SetRole(RoleTypeId.Spectator); player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplaced, replacement.Nickname), "white"); } } diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 3cfe9dd..457418b 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -1,4 +1,5 @@ -using PluginAPI.Core; +using PluginAPI; +using PluginAPI.Core; using PluginAPI.Core.Attributes; using PluginAPI.Enums; using UltimateAFK.API; @@ -17,11 +18,10 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.4.2", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.4.3", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; - Log.Warning("This version of the plugin is only compatible with the \"pluginapi-beta\" branch of NWAPI."); PluginAPI.Events.EventManager.RegisterEvents(this, new MainHandler(Singleton)); AfkEvents.Instance.PlayerAfkDetectedEvent += OnPlayerIsDetectedAfk; } From 03e8f0c725fd4e8fdc6b74fc95044299d93a2d08 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Mon, 30 Oct 2023 14:42:34 -0300 Subject: [PATCH 139/146] 6.4.4 * Added UserIdIgnored * Changed how i get the SCP-079 camera rotation --- UltimateAFK/Config.cs | 6 ++++++ UltimateAFK/Resources/Component/AFKComponent.cs | 7 ++++--- UltimateAFK/UltimateAFK.cs | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 24a00ef..279ec44 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -48,6 +48,12 @@ public class Config [Description("When a player replaces another player, this message will appear on the player's face and on the player console. | {0} it is the name of the player who was afk")] public string MsgReplace { get; set; } = " You replaced {0} who was afk."; + [Description("Similar to give the permission of \"uafk.ignore\". This is more focused on Exiled users.")] + public List UserIdIgnored { get; set; } = new() + { + "11111111111@steam" + }; + [Description("All configuration related with the command")] public CommandConfig CommandConfig { get; set; } = new(); } diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs index 871b604..ec17ef5 100644 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ b/UltimateAFK/Resources/Component/AFKComponent.cs @@ -85,7 +85,7 @@ private void OnDestroy() private void CheckAfkStatus() { if (!Round.IsRoundStarted || Player.Count < Plugin.Config.MinPlayers || Owner.Role == RoleTypeId.Tutorial && Plugin.Config.IgnoreTut - || Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) + || Plugin.Config.UserIdIgnored.Contains(Owner.UserId) || Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) return; Vector3 worldPosition = Owner.Position; @@ -118,7 +118,8 @@ private void CheckAfkStatus() Scp079Role ??= Owner.RoleBase as Scp079Role; cameraPosition = Scp079Role.CameraPosition; - cameraRotation = new Quaternion(Scp079Role.CurrentCamera.HorizontalRotation, Scp079Role.CurrentCamera.VerticalRotation, Scp079Role.CurrentCamera.RollRotation, 0f); + cameraRotation = Scp079Role.CurrentCamera._cameraAnchor.transform.rotation; + if (IsMoving(worldPosition, cameraPosition, cameraRotation)) { // Do nothing player is moving @@ -341,7 +342,7 @@ private Player GetReplacement() /// True if the player should be ignored, otherwise false. private bool IgnorePlayer(Player player) { - if (!player.IsReady || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || player.UserId == ownerUserId || player.IsAlive || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || player.UserId.Contains("@npc") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) + if (!player.IsReady || Plugin.Config.UserIdIgnored.Contains(Owner.UserId) || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || player.UserId == ownerUserId || player.IsAlive || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || player.UserId.Contains("@npc") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) return true; return false; diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs index 457418b..8a3f8c0 100644 --- a/UltimateAFK/UltimateAFK.cs +++ b/UltimateAFK/UltimateAFK.cs @@ -18,7 +18,7 @@ public class UltimateAFK [PluginConfig] public Config Config; [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.4.3", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + [PluginEntryPoint("UltimateAFK", "6.4.4", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] void OnEnabled() { Singleton = this; From 408ec1a85b89bf66640e76655fdab6bfa3e32c8e Mon Sep 17 00:00:00 2001 From: SrLicht Date: Tue, 14 Nov 2023 16:54:37 -0300 Subject: [PATCH 140/146] Update to SL 13.3.1 * Rework of the component and EventArgs --- UltimateAFK/API/AfkEvents.cs | 33 -- .../API/Components/AfkCheckComponent.cs | 480 ++++++++++++++++++ .../EventArgs/DetectedAfkPlayerEventArgs.cs | 31 ++ UltimateAFK/API/EventArgs/Events.cs | 26 + UltimateAFK/API/Extensions.cs | 399 +++++++++++++++ UltimateAFK/API/Structs/AfkData.cs | 69 +++ UltimateAFK/API/Structs/Scp079RoleData.cs | 30 ++ UltimateAFK/Command/AfkCommand.cs | 231 ++------- UltimateAFK/Config.cs | 153 +++++- UltimateAFK/EntryPoint.cs | 57 +++ UltimateAFK/{Handlers => }/MainHandler.cs | 150 +++--- UltimateAFK/Resources/AFKData.cs | 22 - .../Resources/Component/AFKComponent.cs | 433 ---------------- UltimateAFK/Resources/Extensions.cs | 123 ----- UltimateAFK/Resources/Scp079Data.cs | 9 - UltimateAFK/UltimateAFK.cs | 46 -- UltimateAFK/UltimateAFK.csproj | 5 +- build.ps1 | 47 -- 18 files changed, 1353 insertions(+), 991 deletions(-) delete mode 100644 UltimateAFK/API/AfkEvents.cs create mode 100644 UltimateAFK/API/Components/AfkCheckComponent.cs create mode 100644 UltimateAFK/API/EventArgs/DetectedAfkPlayerEventArgs.cs create mode 100644 UltimateAFK/API/EventArgs/Events.cs create mode 100644 UltimateAFK/API/Extensions.cs create mode 100644 UltimateAFK/API/Structs/AfkData.cs create mode 100644 UltimateAFK/API/Structs/Scp079RoleData.cs create mode 100644 UltimateAFK/EntryPoint.cs rename UltimateAFK/{Handlers => }/MainHandler.cs (51%) delete mode 100644 UltimateAFK/Resources/AFKData.cs delete mode 100644 UltimateAFK/Resources/Component/AFKComponent.cs delete mode 100644 UltimateAFK/Resources/Extensions.cs delete mode 100644 UltimateAFK/Resources/Scp079Data.cs delete mode 100644 UltimateAFK/UltimateAFK.cs delete mode 100644 build.ps1 diff --git a/UltimateAFK/API/AfkEvents.cs b/UltimateAFK/API/AfkEvents.cs deleted file mode 100644 index 84aa287..0000000 --- a/UltimateAFK/API/AfkEvents.cs +++ /dev/null @@ -1,33 +0,0 @@ -using PluginAPI.Core; - -namespace UltimateAFK.API -{ - public class AfkEvents - { - // Lazy event system... Sorry i dont know well doing events in a simple way. - private static AfkEvents _instance; - - public static AfkEvents Instance - { - get - { - _instance ??= new AfkEvents(); - return _instance; - } - } - - public void InvokePlayerAfkDetected(Player player, bool isForCommand) - { - PlayerAfkDetectedEvent?.Invoke(player, isForCommand); - } - - public delegate void PlayerAfkDetected(Player player, bool isForCommand); - - public event PlayerAfkDetected PlayerAfkDetectedEvent; - - - // Prevent extarnal instances. - private AfkEvents() { } - - } -} diff --git a/UltimateAFK/API/Components/AfkCheckComponent.cs b/UltimateAFK/API/Components/AfkCheckComponent.cs new file mode 100644 index 0000000..0d2d882 --- /dev/null +++ b/UltimateAFK/API/Components/AfkCheckComponent.cs @@ -0,0 +1,480 @@ +using MEC; +using NWAPIPermissionSystem; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using PlayerRoles.PlayableScps.Scp096; +using PlayerRoles.Spectating; +using PluginAPI.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using UltimateAFK.API.Structs; +using UnityEngine; + +namespace UltimateAFK.API.Components +{ + /// + /// Player component who check if the player is not moving. + /// + public class AfkCheckComponent : MonoBehaviour + { + // Main fields + + /// + /// Player who owns this component. + /// + private protected Player Owner = null!; + + /// + /// Cached of the + /// + private protected string OwnerUserId = string.Empty; + + /// + /// Plugin config instance. + /// + private Config PluginConfig => EntryPoint.Instance.Config; + + private void Start() + { + if (!TryGetOwner(out Owner)) + { + Log.Debug($"{GetType().Name}::{nameof(Start)}: Error on getting player or player is not ready | {Owner is null}", PluginConfig.DebugMode); + Disable(); + return; + } + + OwnerUserId = Owner.UserId; + // Starts the coroutine that checks if the player is moving, the coroutine is cancelled if the gameobject (the player) becomes null or the component is destroyed. + CoroutineHandle = Timing.RunCoroutine(AfkStatusChecker().CancelWith(gameObject).CancelWith(this)); + Log.Debug($"Component {GetType().Name} fully loaded for {Owner.LogName}", PluginConfig.DebugMode, "UltimateAfk"); + } + + private void OnDestroy() + { + if (CoroutineHandle.IsRunning) + CoroutineHandle.IsRunning = false; // Using this is the same as using Timing.KillCoroutine(CoroutineHandle); | Inspect CoroutineHandle struc for more info. + } + + /// + /// Destroy the component. + /// + public void Disable() + { + Destroy(this); + } + + /// + /// Coroutine that continuously checks and updates the AFK status of players. + /// + private IEnumerator AfkStatusChecker() + { + + while (true) + { + try + { + CheckAfk(); + } + catch (Exception e) + { + Log.Error($"Error on {nameof(AfkCheckComponent)}::{nameof(AfkStatusChecker)}: {e.Message} || typeof {e.GetType()}"); + } + + yield return Timing.WaitForSeconds(1); + } + } + + /// + /// Gets how many times this player has detected has AFK. + /// + /// + public int GetAfkTimes() => afkTimes; + + /// + /// Sets how many times this player has detected has AFK. + /// + /// + public void SetAfkTimes(int times) => afkTimes = times; + + // Main Method + + /// + /// Checks the AFK status of the player and takes appropriate actions. + /// + private void CheckAfk() + { + if (!Round.IsRoundStarted || Player.Count < PluginConfig.MinPlayers || Owner.Role == RoleTypeId.Tutorial && PluginConfig.IgnoreTut + || PluginConfig.UserIdIgnored.Contains(Owner.UserId) || Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) + return; + + // Retrieve player information. + Vector3 worldPosition = Owner.Position; + Vector3 cameraPosition = Owner.Camera.position; + Quaternion cameraRotation = Owner.Camera.rotation; + + switch (Owner.Role) + { + case RoleTypeId.Scp096: + { + Scp096Role ??= Owner.RoleBase as Scp096Role; + if (Scp096Role is null) + break; + + if (Scp096Role.IsAbilityState(Scp096AbilityState.TryingNotToCry) || IsMoving(worldPosition, cameraPosition, cameraRotation)) + { + // set afk time to 0 player is moving. + secondsNotMoving = 0; + } + else + { + Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", PluginConfig.DebugMode); + HandleAfkState(); + } + + break; + } + case RoleTypeId.Scp079: + { + Scp079Role ??= Owner.RoleBase as Scp079Role; + + if (Scp079Role is null) + break; + + cameraPosition = Scp079Role.CameraPosition; + cameraRotation = Scp079Role.CurrentCamera._cameraAnchor.transform.rotation; + + if (IsMoving(worldPosition, cameraPosition, cameraRotation)) + { + // Do nothing player is moving + secondsNotMoving = 0; + } + else + { + Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", PluginConfig.DebugMode); + HandleAfkState(); + } + + break; + } + case RoleTypeId.Overwatch: + case RoleTypeId.Filmmaker: + case RoleTypeId.None: + case RoleTypeId.Spectator: + { + // Do nothing player is not alive. + secondsNotMoving = 0; + break; + } + default: + { + // Player is human or a SCP with more simple mechanic arround camera/position + if (IsMoving(worldPosition, cameraPosition, cameraRotation)) + { + // Do nothing player is moving + secondsNotMoving = 0; + } + else + { + Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", PluginConfig.DebugMode, "UltimateAfk"); + + HandleAfkState(); + } + break; + } + } + } + + /// + /// Replaces the specified player with a suitable replacement based on the specified role type. + /// + /// The player to be replaced. + /// The role type of the player to be replaced. + private void Replace(Player player, RoleTypeId roleType) + { + // Check if role is blacklisted + if (PluginConfig.RoleTypeBlacklist?.Contains(roleType) == true) + { + Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", PluginConfig.DebugMode, "UltimateAfk"); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + if (PluginConfig.AfkCount > -1) + { + if (++afkTimes >= PluginConfig.AfkCount) + { + player.SendConsoleMessage(PluginConfig.MsgKick, "white"); + player.Kick(PluginConfig.MsgKick); + return; + } + } + + player.SendBroadcast(PluginConfig.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(PluginConfig.MsgFspec, "white"); + return; + } + + // Get a replacement player + Player? replacement = GetReplacement(); + + if (replacement == null) + { + Log.Debug("Unable to find replacement player, moving to spectator...", PluginConfig.DebugMode, "UltimateAfk"); + + player.ClearInventory(); + + if (player.IsSCP) + player.ReferenceHub.roleManager.ServerSetRole(RoleTypeId.Spectator, RoleChangeReason.RemoteAdmin, RoleSpawnFlags.None); + else + player.SetRole(RoleTypeId.Spectator); + + if (PluginConfig.AfkCount > -1) + { + if (++afkTimes >= PluginConfig.AfkCount) + { + player.SendConsoleMessage(PluginConfig.MsgKick, "white"); + + player.Kick(PluginConfig.MsgKick); + + return; + } + } + + player.SendBroadcast(PluginConfig.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(PluginConfig.MsgFspec, "white"); + + API.EventArgs.Events.OnDetectedAfkPlayer(new(player, false)); + } + else + { + Log.Debug($"Replacement Player found: {replacement.LogName}", PluginConfig.DebugMode, "UltimateAfk"); + + SaveData(replacement.UserId, roleType is RoleTypeId.Scp079); + + if (PluginConfig.AfkCount > -1) + { + if (++afkTimes >= PluginConfig.AfkCount) + { + player.ClearInventory(); + player.SendConsoleMessage(PluginConfig.MsgKick, "white"); + player.Kick(PluginConfig.MsgKick); + replacement.SetRole(roleType); + return; + } + } + + Log.Debug($"Cleaning player {player.Nickname} inventory", PluginConfig.DebugMode, "UltimateAfk"); + // Clear player inventory + player.ClearInventory(); + //Send player a broadcast for being too long afk + player.SendBroadcast(PluginConfig.MsgFspec, 25, shouldClearPrevious: true); + player.SendConsoleMessage(PluginConfig.MsgFspec, "white"); + + // Sends replacement to the role that had the afk + Log.Debug($"Changing replacement player {replacement.LogName} role to {roleType}", PluginConfig.DebugMode, "UltimateAfk"); + replacement.SetRole(roleType); + // Sends player to spectator + Log.Debug($"Changing player {player.Nickname} to spectator", PluginConfig.DebugMode, "UltimateAfk"); + + if (player.IsSCP) + player.ReferenceHub.roleManager.ServerSetRole(RoleTypeId.Spectator, RoleChangeReason.RemoteAdmin, RoleSpawnFlags.None); + else + player.SetRole(RoleTypeId.Spectator); + + player.SendConsoleMessage(string.Format(PluginConfig.MsgReplaced, replacement.Nickname), "white"); + + API.EventArgs.Events.OnDetectedAfkPlayer(new(player, false)); + } + } + + /// + /// Saves data for the specified replacement player. + /// + /// The user ID of the replacement player. + /// Flag indicating whether the replacement player is SCP-079. + private void SaveData(string replacementUserId, bool isScp079 = false) + { + Log.Debug($"Saving data for {replacementUserId}", PluginConfig.DebugMode, "UltimateAfk"); + + AfkData data; + + if (isScp079) + { + Log.Debug($"Saving data: Player is SCP-079", PluginConfig.DebugMode, "UltimateAfk"); + if (Owner.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + { + data = new AfkData(Owner.Nickname, Owner.Position, Owner.Role, null, new(), Owner.Health, new(tierManager.TotalExp, energyManager.CurrentAux)); + } + else + { + data = new AfkData(Owner.Nickname, Owner.Position, Owner.Role, null, new(), Owner.Health, null); + // Return early if SCP-079 data cannot be obtained + return; + } + } + else + { + data = new AfkData(Owner.Nickname, Owner.Position, Owner.Role, Owner.GetAmmo(), Owner.GetItemTypes(), Owner.Health, null); + } + + Log.Debug($"Saving data: Saving in ReplacingPlayersData", PluginConfig.DebugMode, "UltimateAfk"); + MainHandler.ReplacingPlayersData.Add(replacementUserId, data); + } + + /// + /// Handles the AFK state for the player. + /// + private void HandleAfkState() + { + // Increment the time the player has not been moving + if (secondsNotMoving++ < PluginConfig.AfkTime) + return; + + // Calculate remaining grace time + var graceTimeRemaining = PluginConfig.GraceTime - (secondsNotMoving - PluginConfig.AfkTime); + + if (graceTimeRemaining > 0) + { + // The player is being considered AFK and is being warned that they have X time to move again or they will be kicked/replaced + Owner.SendBroadcast(string.Format(PluginConfig.MsgGrace, graceTimeRemaining), 2, + shouldClearPrevious: true); + } + else + { + // The player has exceeded the grace time and is now officially AFK + Log.Info($"{Owner.LogName} was detected as AFK"); + + secondsNotMoving = 0; + // Optionally invoke custom event or action when a player is detected as AFK + // API.AfkEvents.Instance.InvokePlayerAfkDetected(Owner, false); + Replace(Owner, Owner.Role); + } + } + + /// + /// Gets the player to be used as a replacement, typically the longest-active spectator. + /// + /// The player to be used as a replacement, or null if no suitable replacement is found. + private Player? GetReplacement() + { + try + { + Player? longestSpectator = null; + float maxActiveTime = 0f; + + Log.Info($"Player user id using PluginAPI is " + Owner.UserId); + Log.Info($"Player user id using manual method is " + Owner.ReferenceHub.authManager.UserId); + + // Find the longest-active spectator among non-ignored players + foreach (var player in Player.GetPlayers().Where(p => !IgnorePlayer(p))) + { + if (player.Role == RoleTypeId.Spectator && player.RoleBase is SpectatorRole spectatorRole) + { + // Update the longest spectator if the current one has a longer active time + if (spectatorRole.ActiveTime > maxActiveTime) + { + maxActiveTime = spectatorRole.ActiveTime; + longestSpectator = player; + } + } + } + + return longestSpectator; + } + catch (Exception e) + { + // Log and handle any exceptions that occur during the process + Log.Error($"Error in {nameof(GetReplacement)}: {e} || Type: {e.GetType()}"); + return null; + } + } + + /// + /// Determines whether the specified player should be ignored in the context of AFK checks. + /// + /// The player to check. + /// + /// true if the player should be ignored; otherwise, false. + /// + private bool IgnorePlayer(Player player) + { + // Check various conditions to determine if the player should be ignored + if (!player.IsReady || // Player is not ready + PluginConfig.UserIdIgnored.Contains(OwnerUserId) || // Player's user ID is in the ignored list + player.TemporaryData.StoredData.ContainsKey("uafk_disable") || // Player has AFK checking disabled + player.UserId == OwnerUserId || // Player is the same as the owner + player.IsAlive || // Player is alive + player.CheckPermission("uafk.ignore") || // Player has the uafk.ignore permission + player.IsServer || // Player is a server + player.UserId.Contains("@server") || // Player's user ID contains "@server" + player.UserId.Contains("@npc") || // Player's user ID contains "@npc" + MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) // Player is being replaced + { + return true; + } + + return false; + } + + + /// + /// Determines whether an object is moving based on its world position, camera position, and camera rotation. + /// + /// The world position of the object. + /// The position of the camera. + /// The rotation of the camera. + /// + /// true if the object is moving; otherwise, false. + /// + private bool IsMoving(Vector3 worldPosition, Vector3 cameraPosition, Quaternion cameraRotation) + { + // Check if any of the parameters have changed since the last check + bool hasChanged = worldPosition != _lastPosition || + cameraPosition != _lastCameraPosition || + cameraRotation != _lastCameraRotation; + + // Update the last recorded values + if (hasChanged) + { + _lastPosition = worldPosition; + _lastCameraPosition = cameraPosition; + _lastCameraRotation = cameraRotation; + } + + // Return true if there is a change, indicating movement + return hasChanged; + } + + + /// + /// Try get the Owner of this component, if fails the component is destroyed. + /// + /// + /// + protected bool TryGetOwner(out Player player) + { + player = Player.Get(gameObject); + return player != null && player.IsReady; + } + // fields // + + // Seconds a player was not moving + private int secondsNotMoving; + // Position in the world + private Vector3 _lastPosition; + // Player camera position + private Vector3 _lastCameraPosition; + // Player camera rotation + private Quaternion _lastCameraRotation; + // Number of times that the player was detected as AFK + private int afkTimes = 0; + // Coroutine that handles afk checking. + private CoroutineHandle CoroutineHandle; + // Cached Scp079Role + private Scp079Role? Scp079Role = null; + // Cached Scp096Role + private Scp096Role? Scp096Role = null; + } +} diff --git a/UltimateAFK/API/EventArgs/DetectedAfkPlayerEventArgs.cs b/UltimateAFK/API/EventArgs/DetectedAfkPlayerEventArgs.cs new file mode 100644 index 0000000..ab8491f --- /dev/null +++ b/UltimateAFK/API/EventArgs/DetectedAfkPlayerEventArgs.cs @@ -0,0 +1,31 @@ +using PluginAPI.Core; + +namespace UltimateAFK.API.EventArgs +{ + /// + /// Represents the event arguments for detecting an AFK player. + /// + public sealed class DetectedAfkPlayerEventArgs + { + /// + /// Initializes a new instance of the . + /// + /// The AFK player that has been detected. + /// Indicates whether the detection is associated with a specific command. + public DetectedAfkPlayerEventArgs(Player player, bool isForCommand) + { + Player = player; + IsForCommand = isForCommand; + } + + /// + /// Gets the player who is detected as AFK player. + /// + public Player Player { get; } + + /// + /// Gets if the detection if for the command. + /// + public bool IsForCommand { get; } + } +} diff --git a/UltimateAFK/API/EventArgs/Events.cs b/UltimateAFK/API/EventArgs/Events.cs new file mode 100644 index 0000000..f00d6c3 --- /dev/null +++ b/UltimateAFK/API/EventArgs/Events.cs @@ -0,0 +1,26 @@ +using System; + +namespace UltimateAFK.API.EventArgs +{ + /// + /// A static class that defines events related to detecting AFK players. + /// + public class Events + { +#nullable disable + + /// + /// Event triggered when an AFK player is detected. + /// + public static event Action DetectedAfkPlayer; + + /// + /// Invokes the event. + /// + /// The event arguments containing information about the detected AFK player. + public static void OnDetectedAfkPlayer(DetectedAfkPlayerEventArgs ev) => DetectedAfkPlayer?.Invoke(ev); + +#nullable enable + } + +} diff --git a/UltimateAFK/API/Extensions.cs b/UltimateAFK/API/Extensions.cs new file mode 100644 index 0000000..8c44e5c --- /dev/null +++ b/UltimateAFK/API/Extensions.cs @@ -0,0 +1,399 @@ +using Interactables.Interobjects; +using InventorySystem; +using InventorySystem.Items; +using InventorySystem.Items.Firearms; +using InventorySystem.Items.Firearms.Attachments; +using NWAPIPermissionSystem; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using PlayerRoles.Spectating; +using PluginAPI.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using UltimateAFK.API.Components; +using UltimateAFK.API.Structs; + +namespace UltimateAFK.API +{ + /// + /// A collection of extension methods. + /// + public static class Extensions + { + /// + /// Gets a list of all Elevators in the facility. + /// + public static List AllElevators = new(Map.Elevators); + + /// + /// Determines whether the round has ended. + /// + /// + /// true if the round has ended; otherwise, false. + /// + public static bool IsRoundEnded() + { + return !Round.IsRoundStarted && Round.Duration.TotalSeconds > 0; + } + + /// + /// Obtains a list of ItemType instances representing the items held by the player, excluding ammo. + /// + /// The player whose items are to be retrieved. + /// A list of ItemType instances representing the non-ammo items held by the player. + public static List GetItemTypes(this Player player) + { + // Retrieve the items from the player's inventory + var playerItem = player.ReferenceHub.inventory.UserInventory.Items.Values; + + // Create a list to store the resulting ItemType + List result = new List(); + + foreach (var item in playerItem) + { + // Exclude items with the Ammo category + if (item.Category == ItemCategory.Ammo) + continue; + // Add the ItemType of the item to the result list + result.Add(item.ItemTypeId); + } + + // Return the list of non-ammo ItemType + return result; + } + + /// + /// Retrieves a dictionary representing the ammunition held by the player. + /// + /// The player whose ammunition is to be retrieved. + /// A dictionary where keys are ItemType instances and values are the corresponding ammunition amounts. + public static Dictionary GetAmmo(this Player player) + { + // Retrieve the ammunition from the player's inventory and create a copy using ToDictionary + Dictionary result = player.ReferenceHub.inventory.UserInventory.ReserveAmmo.ToDictionary(entry => entry.Key, entry => entry.Value); + + return result; + } + + /// + /// Replaces the player with another. + /// + /// The player to be replaced. + /// The role type of the player to be replaced. + /// If this is true will be counted for kick if reach the max afk times + public static void Replace(this Player player, RoleTypeId role, bool countForKicl = false) + { + if (player.TryGetComponent(out var component)) + { + string ownerUserId = player.UserId; + int afkTimes = component.GetAfkTimes(); + + // Check if role is blacklisted + if (EntryPoint.Instance.Config.RoleTypeBlacklist?.Contains(role) == true) + { + Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", EntryPoint.Instance.Config.DebugMode); + + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + + if (EntryPoint.Instance.Config.AfkCount > -1 && countForKicl) + { + + if (++afkTimes >= EntryPoint.Instance.Config.AfkCount) + { + player.SendConsoleMessage(EntryPoint.Instance.Config.MsgKick, "white"); + player.Kick(EntryPoint.Instance.Config.MsgKick); + return; + } + } + + component.SetAfkTimes(afkTimes); + + player.SendBroadcast(EntryPoint.Instance.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(EntryPoint.Instance.Config.MsgFspec, "white"); + return; + } + + // Get a replacement player + Player? replacement = GetReplacement(ownerUserId); + + if (replacement == null) + { + Log.Debug("Unable to find replacement player, moving to spectator...", EntryPoint.Instance.Config.DebugMode); + + player.ClearInventory(); + + if (player.IsSCP) + player.ReferenceHub.roleManager.ServerSetRole(RoleTypeId.Spectator, RoleChangeReason.RemoteAdmin, RoleSpawnFlags.None); + else + player.SetRole(RoleTypeId.Spectator); + + if (EntryPoint.Instance.Config.AfkCount > -1 && countForKicl) + { + afkTimes++; + + if (afkTimes >= EntryPoint.Instance.Config.AfkCount) + { + player.SendConsoleMessage(EntryPoint.Instance.Config.MsgKick, "white"); + + player.Kick(EntryPoint.Instance.Config.MsgKick); + + return; + } + } + + player.SendBroadcast(EntryPoint.Instance.Config.MsgFspec, 30, shouldClearPrevious: true); + player.SendConsoleMessage(EntryPoint.Instance.Config.MsgFspec, "white"); + } + else + { + Log.Debug($"Replacement Player found: {replacement.LogName}", EntryPoint.Instance.Config.DebugMode); + + SaveData(player, replacement.UserId, role is RoleTypeId.Scp079); + + if (EntryPoint.Instance.Config.AfkCount > -1 && countForKicl) + { + + if (++afkTimes >= EntryPoint.Instance.Config.AfkCount) + { + player.ClearInventory(); + player.SendConsoleMessage(EntryPoint.Instance.Config.MsgKick, "white"); + player.Kick(EntryPoint.Instance.Config.MsgKick); + replacement.SetRole(role); + return; + } + } + + component.SetAfkTimes(afkTimes); + Log.Debug($"Cleaning player {player.Nickname} inventory", EntryPoint.Instance.Config.DebugMode); + // Clear player inventory + player.ClearInventory(); + //Send player a broadcast for being too long afk + player.SendBroadcast(EntryPoint.Instance.Config.MsgFspec, 25, shouldClearPrevious: true); + player.SendConsoleMessage(EntryPoint.Instance.Config.MsgFspec, "white"); + + // Sends replacement to the role that had the afk + Log.Debug($"Changing replacement player {replacement.LogName} role to {role}", EntryPoint.Instance.Config.DebugMode); + replacement.SetRole(role); + // Sends player to spectator + Log.Debug($"Changing player {player.Nickname} to spectator", EntryPoint.Instance.Config.DebugMode); + + if (player.IsSCP) + player.ReferenceHub.roleManager.ServerSetRole(RoleTypeId.Spectator, RoleChangeReason.RemoteAdmin, RoleSpawnFlags.None); + else + player.SetRole(RoleTypeId.Spectator); + + player.SendConsoleMessage(string.Format(EntryPoint.Instance.Config.MsgReplaced, replacement.Nickname), "white"); + } + } + } + + /// + /// Checks if the specified is ammo. + /// + /// The to check. + /// if the is ammo; otherwise, . + public static bool IsAmmo(this ItemType type) + { + var itemBase = type.GetItemBase(); + return itemBase?.Category == ItemCategory.Ammo; + } + + /// + /// Retrieves the base item associated with a specific item type. + /// + /// The item type for which the base item is requested. + /// + /// The base item associated with the specified item type if available; otherwise, returns null. + /// + public static ItemBase? GetItemBase(this ItemType type) + { + if (!InventoryItemLoader.AvailableItems.TryGetValue(type, out ItemBase @base)) + return null; + + return @base; + } + + /// + /// Gives the ammunition you want a player to have. + /// + public static void SendAmmo(this Player ply, Dictionary ammo) + { + foreach (var ammoItem in ammo) + { + ply.SetAmmo(ammoItem.Key, ammoItem.Value); + } + } + + /// + /// Reloads all player 's + /// + /// + public static void ReloadAllWeapons(this Player ply) + { + var item = ply.Items.Where(i => i is Firearm); + + foreach (var weapon in item) + { + if (weapon is Firearm firearm) + { + firearm.Status = new FirearmStatus(firearm.AmmoManagerModule.MaxAmmo, firearm.Status.Flags, + firearm.Status.Attachments); + } + } + } + + /// + /// Adds several items at the same time to a player. + /// + public static void SendItems(this Player player, List types) + { + if (types.IsEmpty()) + return; + + foreach (var item in types) + { + player.AddItem(item); + } + } + + /// + /// Applies player attachments preferences. + /// + /// + public static void ApplyAttachments(this Player ply) + { + var item = ply.Items.Where(i => i is Firearm); + + foreach (var fire in item) + { + if (fire is Firearm fireArm) + { + if (AttachmentsServerHandler.PlayerPreferences.TryGetValue(ply.ReferenceHub, out var value) && value.TryGetValue(fireArm.ItemTypeId, out var value2)) + fireArm.ApplyAttachmentsCode(value2, reValidate: true); + + var firearmStatusFlags = FirearmStatusFlags.MagazineInserted; + + if (fireArm.HasAdvantageFlag(AttachmentDescriptiveAdvantages.Flashlight)) + firearmStatusFlags |= FirearmStatusFlags.FlashlightEnabled; + + fireArm.Status = new FirearmStatus(fireArm.AmmoManagerModule.MaxAmmo, firearmStatusFlags, fireArm.GetCurrentAttachmentsCode()); + } + } + } + + /// + /// Gets if the current player is in a elevator. + /// + /// + /// + public static bool InElevator(this Player player) + { + foreach (var elevator in AllElevators) + { + if (elevator.WorldspaceBounds.Contains(player.Position) && elevator.IsMoving()) + return true; + } + + return false; + } + + /// + /// Check if the current is moving. + /// + /// + /// + public static bool IsMoving(this ElevatorChamber elevator) => elevator._curSequence is ElevatorChamber.ElevatorSequence.MovingAway or ElevatorChamber.ElevatorSequence.Arriving; + + + /// + /// Gets the player to be used as a replacement, typically the longest-active spectator. + /// + /// The player to be used as a replacement, or null if no suitable replacement is found. + private static Player? GetReplacement(string ownerUserId) + { + try + { + Player? longestSpectator = null; + float maxActiveTime = 0f; + + // Find the longest-active spectator among non-ignored players + foreach (var player in Player.GetPlayers().Where(p => !IgnorePlayer(p, ownerUserId))) + { + if (player.Role == RoleTypeId.Spectator && player.RoleBase is SpectatorRole spectatorRole) + { + // Update the longest spectator if the current one has a longer active time + if (spectatorRole.ActiveTime > maxActiveTime) + { + maxActiveTime = spectatorRole.ActiveTime; + longestSpectator = player; + } + } + } + + return longestSpectator; + } + catch (Exception e) + { + // Log and handle any exceptions that occur during the process + Log.Error($"Error in {nameof(GetReplacement)}: {e.Message} || Type: {e.GetType()}"); + return null; + } + } + + /// + /// Determines whether the specified player should be ignored in the context of AFK checks. + /// + /// The player to check. + /// + /// + /// true if the player should be ignored; otherwise, false. + /// + private static bool IgnorePlayer(Player player, string ownerUserId) + { + // Check various conditions to determine if the player should be ignored + if (!player.IsReady || // Player is not ready + EntryPoint.Instance.Config.UserIdIgnored.Contains(ownerUserId) || // Player's user ID is in the ignored list + player.TemporaryData.StoredData.ContainsKey("uafk_disable") || // Player has AFK checking disabled + player.UserId == ownerUserId || // Player is the same as the owner + player.IsAlive || // Player is alive + player.CheckPermission("uafk.ignore") || // Player has the uafk.ignore permission + player.IsServer || // Player is a server + player.UserId.Contains("@server") || // Player's user ID contains "@server" + player.UserId.Contains("@npc") || // Player's user ID contains "@npc" + MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) // Player is being replaced + { + return true; + } + + return false; + } + + private static void SaveData(Player Owner, string replacementUserId, bool isScp079 = false) + { + AfkData data; + + if (isScp079) + { + if (Owner.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) + && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) + { + data = new AfkData(Owner.Nickname, Owner.Position, Owner.Role, null, new(), Owner.Health, new(tierManager.TotalExp, energyManager.CurrentAux)); + } + else + { + data = new AfkData(Owner.Nickname, Owner.Position, Owner.Role, null, new(), Owner.Health, null); + // Return early if SCP-079 data cannot be obtained + return; + } + } + else + { + data = new AfkData(Owner.Nickname, Owner.Position, Owner.Role, Owner.GetAmmo(), Owner.GetItemTypes(), Owner.Health, null); + } + + MainHandler.ReplacingPlayersData.Add(replacementUserId, data); + } + } +} diff --git a/UltimateAFK/API/Structs/AfkData.cs b/UltimateAFK/API/Structs/AfkData.cs new file mode 100644 index 0000000..a02ea7a --- /dev/null +++ b/UltimateAFK/API/Structs/AfkData.cs @@ -0,0 +1,69 @@ +using PlayerRoles; +using System.Collections.Generic; +using UnityEngine; + +namespace UltimateAFK.API.Structs +{ + /// + /// Represents data related to an AFK (Away From Keyboard) player. + /// + public readonly struct AfkData + { + /// + /// Initializes a new instance of the . + /// + /// The nickname of the AFK player. + /// The position of the AFK player in the game. + /// The role type of the AFK player. + /// A dictionary representing the ammunition of the AFK player (nullable). + /// A list of item types associated with the AFK player. + /// The health points of the AFK player. + /// SCP-079 role data of the AFK player (nullable). + public AfkData(string nickname, Vector3 position, RoleTypeId roleType, Dictionary? ammo, List items, float hp, Scp079RoleData? data) + { + this.Nickname = nickname; + this.Position = position; + this.RoleType = roleType; + this.Ammo = ammo; + this.Items = items; + this.Health = hp; + this.Scp079Data = data; + } + + /// + /// Gets the nickname of the AFK player. + /// + public string Nickname { get; } + + /// + /// Gets the position of the AFK player in the game. + /// + public Vector3 Position { get; } + + /// + /// Gets the role type of the AFK player. + /// + public RoleTypeId RoleType { get; } + + /// + /// Gets the ammunition dictionary of the AFK player (nullable). + /// + public Dictionary? Ammo { get; } + + /// + /// Gets the list of item types associated with the AFK player. + /// + public List Items { get; } + + /// + /// Gets the health points of the AFK player. + /// + public float Health { get; } + + /// + /// Gets the SCP-079 role data of the AFK player (nullable). + /// + public Scp079RoleData? Scp079Data { get; } + } + +} diff --git a/UltimateAFK/API/Structs/Scp079RoleData.cs b/UltimateAFK/API/Structs/Scp079RoleData.cs new file mode 100644 index 0000000..9f59ca1 --- /dev/null +++ b/UltimateAFK/API/Structs/Scp079RoleData.cs @@ -0,0 +1,30 @@ +namespace UltimateAFK.API.Structs +{ + /// + /// Represents data related to the role of SCP-079. + /// + public readonly struct Scp079RoleData + { + /// + /// Initializes a new instance of the . + /// + /// The experience of SCP-079. + /// The energy level of SCP-079. + public Scp079RoleData(int experience, float energy) + { + this.Experience = experience; + this.Energy = energy; + } + + /// + /// Gets the experience SCP-079. + /// + public int Experience { get; } + + /// + /// Gets the energy SCP-079. + /// + public float Energy { get; } + } + +} diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index b3ec990..33bfd83 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -2,23 +2,17 @@ using CustomPlayerEffects; using MapGeneration; using MEC; -using NWAPIPermissionSystem; using PlayerRoles; -using PlayerRoles.PlayableScps.Scp079; -using PlayerRoles.Spectating; using PluginAPI.Core; using System; using System.Collections.Generic; using UltimateAFK.API; -using UltimateAFK.Handlers; -using UltimateAFK.Resources; -using UltimateAFK.Resources.Component; using UnityEngine; namespace UltimateAFK.Command { [CommandHandler(typeof(ClientCommandHandler))] - public class AfkCommand : ICommand + internal sealed class AfkCommand : ICommand { public string Command { get; } = "afk"; public string[] Aliases { get; } = Array.Empty(); @@ -30,18 +24,20 @@ public class AfkCommand : ICommand public static List InProcess = new(); public static List AllCoroutines = new(); + private Config PluginConfig => EntryPoint.Instance.Config; + public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) { try { - if (!UltimateAFK.Singleton.Config.CommandConfig.IsEnabled) + if (!PluginConfig.CommandConfig.IsEnabled) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnDisable; + response = PluginConfig.CommandConfig.Responses.OnDisable; return false; } if (!Round.IsRoundStarted) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnRoundIsNotStarted; + response = PluginConfig.CommandConfig.Responses.OnRoundIsNotStarted; return false; } @@ -53,54 +49,54 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } if (!ply.IsAlive) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPlayerIsDead; + response = PluginConfig.CommandConfig.Responses.OnPlayerIsDead; return false; } if (ply.Zone == FacilityZone.Other || ply.EffectsManager.TryGetEffect(out var corroding) && corroding.IsEnabled) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnPocketDimension; + response = PluginConfig.CommandConfig.Responses.OnPocketDimension; return false; } - if (UltimateAFK.Singleton.Config.CommandConfig.DisableForCertainRole && UltimateAFK.Singleton.Config.CommandConfig.RoleTypeIdBlackList.Contains(ply.Role)) + if (PluginConfig.CommandConfig.DisableForCertainRole && PluginConfig.CommandConfig.RoleTypeIdBlackList.Contains(ply.Role)) { - response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnBlackListedRole, + response = string.Format(PluginConfig.CommandConfig.Responses.OnBlackListedRole, ply.Role); return false; } - if (UltimateAFK.Singleton.Config.CommandConfig.ExclusiveForGroups && - !UltimateAFK.Singleton.Config.CommandConfig.UserGroupsAllowed.Contains(ply.ReferenceHub.serverRoles.Group?.BadgeText)) + if (PluginConfig.CommandConfig.ExclusiveForGroups && + !PluginConfig.CommandConfig.UserGroupsAllowed.Contains(ply.ReferenceHub.serverRoles.Group.BadgeText)) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnGroupExclusive; + response = PluginConfig.CommandConfig.Responses.OnGroupExclusive; return false; } if (ply.EffectsManager.TryGetEffect(out var severedHands) && severedHands.IsEnabled) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnSevereHands; + response = PluginConfig.CommandConfig.Responses.OnSevereHands; return false; } if (ply.EffectsManager.TryGetEffect(out var cardiacArrest) && cardiacArrest.IsEnabled) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnHearthAttack; + response = PluginConfig.CommandConfig.Responses.OnHearthAttack; return false; } if (ply.InElevator()) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnElevatorMoving; + response = PluginConfig.CommandConfig.Responses.OnElevatorMoving; return false; } if (ply.TemporaryData.StoredData.ContainsKey("uafk_disable")) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnUafkDisable; + response = PluginConfig.CommandConfig.Responses.OnUafkDisable; return false; } - if (CommandUses.TryGetValue(ply.UserId, out var uses) && UltimateAFK.Singleton.Config.CommandConfig.UseLimitsPerRound > 0 && uses > UltimateAFK.Singleton.Config.CommandConfig.UseLimitsPerRound) + if (CommandUses.TryGetValue(ply.UserId, out var uses) && PluginConfig.CommandConfig.UseLimitsPerRound > 0 && uses > PluginConfig.CommandConfig.UseLimitsPerRound) { - response = UltimateAFK.Singleton.Config.CommandConfig.Responses.OnLimit; + response = PluginConfig.CommandConfig.Responses.OnLimit; return false; } if (InProcess.Contains(ply.UserId)) { - response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnTryToExecute, UltimateAFK.Singleton.Config.CommandConfig.SecondsStill); + response = string.Format(PluginConfig.CommandConfig.Responses.OnTryToExecute, PluginConfig.CommandConfig.SecondsStill); return false; } if (InCooldown.TryGetValue(ply.UserId, out var cooldown)) @@ -110,7 +106,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s { var cooldownTimer = (int)(cooldown - Time.time); - response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnCooldown, + response = string.Format(PluginConfig.CommandConfig.Responses.OnCooldown, cooldownTimer); return false; } @@ -122,8 +118,8 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } AllCoroutines.Add(Timing.RunCoroutine(AfkCountdown(ply, ply.UserId, ply.Role, ply.Position))); - InCooldown.Add(ply.UserId, Time.time + UltimateAFK.Singleton.Config.CommandConfig.Cooldown); - response = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnSuccess, UltimateAFK.Singleton.Config.CommandConfig.SecondsStill); + InCooldown.Add(ply.UserId, Time.time + PluginConfig.CommandConfig.Cooldown); + response = string.Format(PluginConfig.CommandConfig.Responses.OnSuccess, PluginConfig.CommandConfig.SecondsStill); return true; } catch (Exception e) @@ -139,7 +135,7 @@ private IEnumerator AfkCountdown(Player player, string userid, RoleTypeId { InProcess.Add(userid); - for (int i = UltimateAFK.Singleton.Config.CommandConfig.SecondsStill; i >= 0; i--) + for (int i = PluginConfig.CommandConfig.SecondsStill; i >= 0; i--) { yield return Timing.WaitForSeconds(1); @@ -153,186 +149,24 @@ private IEnumerator AfkCountdown(Player player, string userid, RoleTypeId if (position != player.Position) { InProcess.Remove(userid); - player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnMoving, UltimateAFK.Singleton.Config.CommandConfig.SecondsStill), "red"); - player.ReceiveHint(string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnMoving, UltimateAFK.Singleton.Config.CommandConfig.SecondsStill), 5); + player.SendConsoleMessage(string.Format(PluginConfig.CommandConfig.Responses.OnMoving, PluginConfig.CommandConfig.SecondsStill), "red"); + player.ReceiveHint(string.Format(PluginConfig.CommandConfig.Responses.OnMoving, PluginConfig.CommandConfig.SecondsStill), 5); yield break; } - string text = string.Format(UltimateAFK.Singleton.Config.CommandConfig.Responses.OnWaitingForAfk, i); + string text = string.Format(PluginConfig.CommandConfig.Responses.OnWaitingForAfk, i); player.ReceiveHint(text, 1); player.SendConsoleMessage(text, "red"); } - Replace(player, roleType); - AfkEvents.Instance.InvokePlayerAfkDetected(player, true); - if (UltimateAFK.Singleton.Config.CommandConfig.UseLimitsPerRound > 0) - AddUse(userid); - InProcess.Remove(userid); - } - - private void Replace(Player player, RoleTypeId roleType) - { - try - { - // Check if role is blacklisted - if (UltimateAFK.Singleton.Config.RoleTypeBlacklist?.Contains(roleType) == true) - { - Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", UltimateAFK.Singleton.Config.DebugMode); - - player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); - - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - return; - } - - // Get a replacement player - Player replacement = GetReplacement(player.UserId); - if (replacement == null) - { - Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); - - player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); - - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - } - else - { - Log.Debug($"Replacement Player found: {replacement.LogName}", UltimateAFK.Singleton.Config.DebugMode); - - SaveData(player, replacement.UserId, roleType is RoleTypeId.Scp079); - - Log.Debug($"Cleaning player {player.Nickname} inventory", UltimateAFK.Singleton.Config.DebugMode); - // Clear player inventory - player.ClearInventory(); - //Send player a broadcast for being too long afk - player.SendBroadcast(UltimateAFK.Singleton.Config.MsgFspec, 25, shouldClearPrevious: true); - player.SendConsoleMessage(UltimateAFK.Singleton.Config.MsgFspec, "white"); - - // Sends replacement to the role that had the afk - Log.Debug($"Changing replacement player {replacement.LogName} role to {roleType}", UltimateAFK.Singleton.Config.DebugMode); - replacement.SetRole(roleType); - // Sends player to spectator - Log.Debug($"Changing player {player.Nickname} to spectator", UltimateAFK.Singleton.Config.DebugMode); - player.SetRole(RoleTypeId.Spectator); - player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplaced, replacement.Nickname), "white"); - } - } - catch (Exception e) - { - Log.Error($"Error on {nameof(AfkCommand)}::{nameof(Replace)}: {e} || Typeof {e.GetType()}"); - } - } - - /// - /// Searches and returns the player with the longest active time in spectator mode. - /// If no valid player is found, it returns null. - /// - /// The player with the longest active time or null if none is found. - private Player GetReplacement(string userid) - { - try - { - Player longestSpectator = null; - float maxActiveTime = 0f; - - foreach (var player in Player.GetPlayers()) - { - if (IgnorePlayer(player, userid)) - continue; - - // It is faster to compare an enum than to compare a class. - if (player.Role == RoleTypeId.Spectator && player.RoleBase is SpectatorRole role) - { - if (role.ActiveTime > maxActiveTime) - { - maxActiveTime = role.ActiveTime; - longestSpectator = player; - } - } - } - - return longestSpectator; - } - catch (Exception e) - { - Log.Error($"Error on {nameof(AfkComponent)}::{nameof(GetReplacement)}: {e.Message} || typeof {e.GetType()}"); - return null; - } - } - - /// - /// Checks if the provided player should be ignored in the AFK replacement process. - /// - /// The player to check. - /// True if the player should be ignored, otherwise false. - private bool IgnorePlayer(Player player, string ignoreUserId) - { - if (!player.IsReady || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || player.UserId == ignoreUserId || player.IsAlive || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || player.UserId.Contains("@npc") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) - return true; - - return false; - } + player.Replace(roleType); - /// - /// Saves the relevant data of a player for potential AFK replacement. - /// - /// The user ID of the player who will be the replacement. - /// Specifies if the player is SCP-079 (True if SCP-079, False if not). - private void SaveData(Player player, string replacementUserId, bool isScp079 = false) - { - AFKData data; + API.EventArgs.Events.OnDetectedAfkPlayer(new(player, true)); - if (isScp079) - { - if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) - { - data = new AFKData() - { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = null, - Health = player.Health, - Items = null, - SCP079 = new Scp079Data - { - Energy = energyManager.CurrentAux, - Experience = tierManager.TotalExp, - } - }; - } - else - { - // Return early if SCP-079 data cannot be obtained - return; - } - } - else - { - var ammo = Extensions.GetAmmo(player); - - data = new AFKData() - { - NickName = player.Nickname, - Position = player.Position, - Role = player.Role, - Ammo = ammo, - Health = player.Health, - Items = player.GetItems(), - SCP079 = new Scp079Data - { - Energy = 0f, - Experience = 0 - } - }; - } + if (PluginConfig.CommandConfig.UseLimitsPerRound > 0) + AddUse(userid); - MainHandler.ReplacingPlayersData.Add(replacementUserId, data); + InProcess.Remove(userid); } private void AddUse(string userID) @@ -347,7 +181,6 @@ private void AddUse(string userID) } } - public static void ClearAllCachedData() { AllCoroutines.ForEach(c => Timing.KillCoroutines(c)); diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index 279ec44..a218184 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -1,146 +1,289 @@ -using PlayerRoles; +using CustomPlayerEffects; +using PlayerRoles; using System.Collections.Generic; using System.ComponentModel; namespace UltimateAFK { + /// + /// Plugin config class. + /// public class Config { - [Description("Setting this to false will stop the plugin from working in the next round.")] + /// + /// Gets or sets if the plugin is enabled. + /// + [Description("Set if the plugin is enabled. If false the plugin will not load any events.")] public bool IsEnabled { get; set; } = true; - [Description("If you have any error in the plugin operation activate this and create an Issue in Github https://github.com/SrLicht/Ultimate-AFK/issues")] + /// + /// Gets or sets if the plugin is in debug mode. + /// + [Description("set if the plugin is in debug mode. enabling this will activate log debugs in the code, this is useful to identify issues and report them in Github.")] public bool DebugMode { get; set; } = false; + // copy from repository + + /// + /// Gets or sets the deplay for replacing players. + /// [Description("When a player is replaced it is called a delay, if when replacing the player the position is not updated correctly, increase this value but it must not exceed 2.5 since it would be too long.")] public float ReplaceDelay { get; set; } = 1.3f; + /// + /// Gets or sets the minimun player to the works. + /// [Description("If the number of players is less than this the plugin will not work.")] public int MinPlayers { get; set; } = 8; + /// + /// Gets or sets if you should ignore the players in tutorial. + /// [Description("Tutorials should be ignored ?")] public bool IgnoreTut { get; set; } = true; + /// + /// Gets or sets the list of RoleTypes that should be ignored for the afk check. + /// [Description("RoleTypes on this list will not be replaced by other players")] public List RoleTypeBlacklist { get; set; } = new() { RoleTypeId.Scp0492 }; + /// + /// Gets or sets how long a player can be afk for. + /// [Description("How long a player can remain motionless before being detected as AFK")] public int AfkTime { get; set; } = 80; + /// + /// Gets or sets the length of time the player will be on grace period + /// [Description("After being detected as AFK a message will appear on his face and he will be given this time to move or he will be Kicked/Moved to spectator.")] public int GraceTime { get; set; } = 30; + /// + /// The number of times a player has to be afk before being kicked from the server. + /// [Description("The number of times a player must be moved to spectator for a player to be kicked from the server. Use -1 to disable it")] public int AfkCount { get; set; } = -1; + /// + /// Gets or sets the grace time broadcast message. + /// [Description("When the player is detected as AFK and is in grace period this message will appear on his face. {0} represents the seconds the player has to move or be moved to spectator.")] public string MsgGrace { get; set; } = "[Ultimate-AFK] You will be moved to spectator if you do not move in less than {0} seconds."; + /// + /// Gets or sets the broadcast when a player is moved to espectator. + /// [Description("This message will be sent to the player who has been moved to spectator when he is detected as AFK, it is also sent to the player's console.")] public string MsgFspec { get; set; } = "You were detected as AFK and were moved to spectator"; + /// + /// Gets or sets the replace message sended to the player console. + /// [Description("When a player is replaced by another player, this message will be sent to his console.")] public string MsgReplaced { get; set; } = "\nyou were replaced by {0}"; + /// + /// Gets or sets the message that will be sent to the player when is kicked from the server. + /// [Description("This will be the reason for the Kick, due to the VSR it is obligatory to clarify that it is a plugin with flags like [UltimateAFK] or something similar.")] public string MsgKick { get; set; } = "[Ultimate-AFK] You were removed from the server for being AFK for too long.!"; + /// + /// Gets or sets the broadcast message to be sent to the replacement player + /// [Description("When a player replaces another player, this message will appear on the player's face and on the player console. | {0} it is the name of the player who was afk")] public string MsgReplace { get; set; } = " You replaced {0} who was afk."; + /// + /// Gets or sets the user ids ignored by afk check. + /// [Description("Similar to give the permission of \"uafk.ignore\". This is more focused on Exiled users.")] public List UserIdIgnored { get; set; } = new() { "11111111111@steam" }; + /// + /// Gets or sets all configuracion related with the commands. + /// [Description("All configuration related with the command")] public CommandConfig CommandConfig { get; set; } = new(); } + /// + /// All settings related to the command + /// public class CommandConfig { + /// + /// Gets or sets if the command is enabled. + /// [Description("Is the command enabled on this server ?")] public bool IsEnabled { get; set; } = false; + /// + /// Gets or sets the amount of time a player must stand still to be replaced/sent to a spectator + /// [Description("When a player uses the command, he must stand still for this amount of seconds to be moved to spectator.")] public int SecondsStill { get; set; } = 10; + /// + /// Gets or sets the limit of times a player can use this command per round + /// [Description("The number of times a player can be this command per round")] public int UseLimitsPerRound { get; set; } = 3; + /// + /// + /// + [Description("If this is true and afk_count is greater than 1 and the player reaches the maximum afk_count he will be kicked from the server.")] + public bool CountForKick { get; set; } = false; + + /// + /// Gets or sets the cooldown that the player will have when using the command + /// [Description("The coldown of the command when using it")] public float Cooldown { get; set; } = 40f; + /// + /// Gets or sets if the command is exclusive to certain groups. + /// [Description("The command can only be used by players who have a group that is on the list ?")] public bool ExclusiveForGroups { get; set; } = false; + /// + /// Gets or sets the list of groups that can use the command + /// [Description("List of groups.")] public List UserGroupsAllowed { get; set; } = new() { "someGroup", }; + /// + /// Gets or sets if the command is disabled for certain + /// [Description("The command is disabled for certain RoleTypes?")] public bool DisableForCertainRole { get; set; } = false; + /// + /// Gets or sets the list of that cannot use the command + /// [Description("List of RoleTypes that cannot use the command")] public List RoleTypeIdBlackList { get; set; } = new() { RoleTypeId.None, + RoleTypeId.Scp0492 }; + /// + /// Gets or sets command responses. + /// + [Description("all the responses given by the command")] public Responses Responses { get; set; } = new(); } + /// + /// All respones for the command. + /// public class Responses { + /// + /// Gets or sets the response of the command when it is deactivated. + /// [Description("Response given to the player when trying to use the command when it is disabled.")] public string OnDisable { get; set; } = "This command is disabled"; + /// + /// Gets or sets the response of the command when successfully executed + /// [Description("Response given to the player when successfully executing the command.")] public string OnSuccess { get; set; } = "You will be moved to spectator in {0} seconds, stand still."; + /// + /// Gets or sets the response of the command when the player has no hands + /// [Description("Response given to the player when he has no hands")] public string OnSevereHands { get; set; } = "You cannot use this command if you have no hands"; - [Description("Response given to the player when affected by Cardiact Arrest (Effect of SCP-049)")] + /// + /// Gets or sets the response of the command when the player has the effect of + /// + [Description("Response given to the player when affected by Cardiac Arrest (Effect of SCP-049)")] public string OnHearthAttack { get; set; } = "You cannot use this command if you have a heart attack."; + /// + /// Gets or sets the response of the command when the player is in the pocket dimension + /// [Description("Response given to the player when trying to use the command when in the pocket dimension.")] public string OnPocketDimension { get; set; } = "There is no easy escape from the pocket dimension."; + /// + /// Gets or sets the response of the command when the player is in cooldown + /// [Description("Response given to the player when he still has cooldown to use the command. {0} is the number of seconds the player has to wait.")] public string OnCooldown { get; set; } = "You cannot use the command yet, you have to wait {0} seconds."; + /// + /// Gets or sets the response of the command when the player has a roletype that cannot use the command + /// [Description("Response given when a player tries to use the command with a role in the blacklist")] public string OnBlackListedRole { get; set; } = "You cannot use this command when you are {0}"; + /// + /// Gets or sets the response of the command when the player does not have the group required to use the command + /// [Description("Response given to the player when not in the group list")] public string OnGroupExclusive { get; set; } = "Your current group is not in the list of allowed groups."; + /// + /// Gets or sets the command response when the round is not started + /// [Description("Response given to the player when he tries to use the command when the round has not started.")] public string OnRoundIsNotStarted { get; set; } = "The round has not started yet, you cannot use the command."; + /// + /// Gets or sets the response of the command when the player is dead + /// [Description("Response given to the player when trying to use the command while is dead.")] public string OnPlayerIsDead { get; set; } = "You cannot use the command if you are dead."; + /// + /// Gets or sets the response of the command when the player has the command disabled due to "afk disable". + /// [Description("If a player has the value \"afk disable\" (bool) set to true in his Temporary Storage, trying to use the command will give him this message")] public string OnUafkDisable { get; set; } = "You can't use this command because you have a subclass or some plugin temporarily removed your access to the command"; + /// + /// Gets or sets the command response when the player is in a moving elevator + /// [Description("If a player is inside an elevator he will not be able to use the command because his replacement will fall into the void if the elevator is moving.")] public string OnElevatorMoving { get; set; } = " You cannot use this command while in a moving elevator."; + /// + /// Gets or sets the command response when the player is being processed to go afk + /// [Description("When a player uses the command he will have to wait X seconds to be spectator.")] public string OnWaitingForAfk { get; set; } = "You will be moved to spectator in {0} seconds."; + /// + /// Gets or sets the response of the command when the player moved when processed to go afk + /// [Description("If a player moves within the time limit this message will be sent to the player's console.")] public string OnMoving { get; set; } = "You moved, you have to be still for {0} seconds to be moved to spectator."; + /// + /// Gets or sets the response of the command when the player reached the maximum number of times the command can be used per round + /// [Description("If a player tries to execute the command when he has already reached his limit per round this message will be sent to the console.")] public string OnLimit { get; set; } = "You have reached the limit of uses of the command per round."; + /// + /// Gets or sets the response of the command when the player tries to use the command 2 or more times in a row + /// [Description("If a player tries to execute the command while it is being processed to move to spectator he will get this message")] public string OnTryToExecute { get; set; } = "You are already being processed to move to spectator, you have to stand still for {0} seconds."; } -} \ No newline at end of file +} diff --git a/UltimateAFK/EntryPoint.cs b/UltimateAFK/EntryPoint.cs new file mode 100644 index 0000000..f0babfb --- /dev/null +++ b/UltimateAFK/EntryPoint.cs @@ -0,0 +1,57 @@ +using GameCore; +using PluginAPI.Core; +using PluginAPI.Core.Attributes; + +namespace UltimateAFK +{ + /// + /// Plugin main class + /// + public class EntryPoint + { + /// + /// Gets the singleton instance of the plugin. + /// + public static EntryPoint Instance { get; private set; } = null!; + + /// + /// Plugin config + /// + [PluginConfig] + public Config Config = null!; + + /// + /// Gets the plugin version. + /// + public const string Version = "7.0.0"; + + /// + /// Called when loading the plugin + /// + [PluginPriority(PluginAPI.Enums.LoadPriority.High)] + [PluginEntryPoint("UltimateAFK", Version, "Plugin that checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] + private void OnLoad() + { + Instance = this; + + if (!Config.IsEnabled) + { + PluginAPI.Core.Log.Warning($"UltimateAfk was disabled through configuration."); + return; + } + + PluginAPI.Events.EventManager.RegisterEvents(Instance, new MainHandler()); + + PluginAPI.Core.Log.Info($"UltimateAfk {Version} fully loaded."); + } + + /// + /// Called when unloading the plugin + /// + [PluginUnload] + private void OnUnload() + { + PluginAPI.Events.EventManager.UnregisterEvents(Instance); + } + } +} diff --git a/UltimateAFK/Handlers/MainHandler.cs b/UltimateAFK/MainHandler.cs similarity index 51% rename from UltimateAFK/Handlers/MainHandler.cs rename to UltimateAFK/MainHandler.cs index 22cc530..24fe52d 100644 --- a/UltimateAFK/Handlers/MainHandler.cs +++ b/UltimateAFK/MainHandler.cs @@ -1,95 +1,124 @@ using MEC; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; -using PlayerStatsSystem; using PluginAPI.Core; using PluginAPI.Core.Attributes; -using PluginAPI.Enums; +using PluginAPI.Events; using System.Collections.Generic; using System.Linq; +using UltimateAFK.API; +using UltimateAFK.API.Components; +using UltimateAFK.API.Structs; using UltimateAFK.Command; -using UltimateAFK.Resources; -using UltimateAFK.Resources.Component; -namespace UltimateAFK.Handlers +namespace UltimateAFK { /// - /// Main class where players are given the AFK component and replacement players are stored. + /// Main handler where players are given the AFK component and replacement players are stored. /// public class MainHandler { - #region Ignore this - - private readonly UltimateAFK Plugin; - - public MainHandler(UltimateAFK plugin) - { - Plugin = plugin; - } - - #endregion + private Config PluginConfig => EntryPoint.Instance.Config; /// /// A dictionary where replacement players are stored to give them the stats and items of the original player. /// - public static Dictionary ReplacingPlayersData = new(); + public static Dictionary ReplacingPlayersData = new(); /// - /// When a player joins I give him the component. + /// When a player join the server i give him the /// - /// - [PluginEvent(ServerEventType.PlayerJoined)] - private void OnPlayerJoin(Player player) + /// + [PluginEvent] + private void OnPlayerJoined(PlayerJoinedEvent ev) { - if (!Plugin.Config.IsEnabled || player is null || player.UserId.Contains("@server") || !player.IsReady) return; + if (!PluginConfig.IsEnabled || ev.Player is null || ev.Player.UserId.Contains("@server") || !ev.Player.IsReady) + return; - Log.Debug($"Adding the Component to {player.LogName}", Plugin.Config.DebugMode); + ev.Player.GameObject.AddComponent(); + } - player.GameObject.AddComponent(); + /// + /// On map generation clear al cached data and update cached elevators. + /// + /// + [PluginEvent] + private void OnMapGenerated(MapGeneratedEvent _) + { + API.EventArgs.Events.DetectedAfkPlayer += OnDetectAfk; + ReplacingPlayersData.Clear(); + AfkCommand.ClearAllCachedData(); + Extensions.AllElevators = Map.Elevators.ToList(); } /// - /// When a player changes roles I make sure that if it is a replacement I give him all the things of the person who replaced. + /// Im lazy... /// - [PluginEvent(ServerEventType.PlayerChangeRole)] - private void OnChangingRole(Player player, PlayerRoleBase oldRole, RoleTypeId newRole, RoleChangeReason reason) + /// + [PluginEvent] + private void OnRoundEnd(RoundEndEvent _) { - try + API.EventArgs.Events.DetectedAfkPlayer -= OnDetectAfk; + } + + private void OnDetectAfk(API.EventArgs.DetectedAfkPlayerEventArgs ev) + { + if (ev.IsForCommand) { - if (player == null || !player.IsReady || newRole == RoleTypeId.Spectator || !ReplacingPlayersData.TryGetValue(player.UserId, out var data)) - return; + Log.Info($"{ev.Player.LogName} use the command to be moved to spectator"); + } + } - Log.Debug($"Detecting player {player.LogName} who replaced a player {data.NickName} who was afk", UltimateAFK.Singleton.Config.DebugMode); + /// + /// I make sure that no player is in the dictionary in case it has not been cleaned correctly, this event is also executed when a player disconnects. + /// + [PluginEvent] + private void OnPlayerDeath(PlayerDeathEvent ev) + { + if (!ev.Player.IsReady) + return; - Timing.CallDelayed(Plugin.Config.ReplaceDelay, () => GiveData(player, data, newRole)); - } - catch (System.Exception e) + if (ReplacingPlayersData.TryGetValue(ev.Player.UserId, out _)) { - Log.Error($"{nameof(OnChangingRole)}: {e}"); + ReplacingPlayersData.Remove(ev.Player.UserId); } } - private void GiveData(Player player, AFKData data, RoleTypeId roleType) + private void OnChangeRole(PlayerChangeRoleEvent ev) + { + if (ev.Player is null || !ev.Player.IsReady || ev.NewRole == PlayerRoles.RoleTypeId.Spectator || !ReplacingPlayersData.TryGetValue(ev.Player.UserId, out var data)) + return; + + Log.Debug($"Detecting player {ev.Player.LogName} who replaced a player {data.Nickname} who was afk", PluginConfig.DebugMode); + + Timing.CallDelayed(PluginConfig.ReplaceDelay, () => GiveData(ev.Player, data, ev.NewRole)); + } + + private void GiveData(Player player, AfkData data, RoleTypeId roleType) { try { - Log.Debug($"Replacing player is {player.Nickname} ({player.UserId}) new role is {roleType}", Plugin.Config.DebugMode); + Log.Debug($"Replacing player is {player.Nickname} ({player.UserId}) new role is {roleType}", PluginConfig.DebugMode); if (roleType == RoleTypeId.Scp079) { - Log.Debug("The new role is a SCP-079, transferring energy and experience.", Plugin.Config.DebugMode); + Log.Debug("The new role is a SCP-079, transferring energy and experience.", PluginConfig.DebugMode); if (player.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) { - player.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); - player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplace, data.NickName), "white"); + player.SendBroadcast(string.Format(PluginConfig.MsgReplace, data.Nickname), 16, shouldClearPrevious: true); + player.SendConsoleMessage(string.Format(PluginConfig.MsgReplace, data.Nickname), "white"); - tierManager.TotalExp = data.SCP079.Experience; - energyManager.CurrentAux = data.SCP079.Energy; - ReplacingPlayersData.Remove(player.UserId); + if (data.Scp079Data.HasValue) + { + tierManager.TotalExp = data.Scp079Data.Value.Experience; + energyManager.CurrentAux = data.Scp079Data.Value.Energy; + Log.Debug($"Energy and experience transferred to the player", PluginConfig.DebugMode); + } - Log.Debug($"Energy and experience transferred to the player", UltimateAFK.Singleton.Config.DebugMode); + Log.Debug($"Removing player from diccionary.", PluginConfig.DebugMode); + ReplacingPlayersData.Remove(player.UserId); } else { @@ -100,8 +129,10 @@ private void GiveData(Player player, AFKData data, RoleTypeId roleType) { // Clear default role inventory. player.ClearInventory(); + // I add the ammunition first since it is the slowest thing to be done. - player.SendAmmo(data.Ammo); + if (data.Ammo != null) + player.SendAmmo(data.Ammo); // This call delayed is necessary. Timing.CallDelayed(0.3f, () => @@ -117,8 +148,8 @@ private void GiveData(Player player, AFKData data, RoleTypeId roleType) // I refill the ammunition of the weapons, since it is annoying to appear without a loaded weapon. player.ReloadAllWeapons(); // Send the broadcast to the player - player.SendBroadcast(string.Format(Plugin.Config.MsgReplace, data.NickName), 16, shouldClearPrevious: true); - player.SendConsoleMessage(string.Format(Plugin.Config.MsgReplace, data.NickName), "white"); + player.SendBroadcast(string.Format(PluginConfig.MsgReplace, data.Nickname), 16, shouldClearPrevious: true); + player.SendConsoleMessage(string.Format(PluginConfig.MsgReplace, data.Nickname), "white"); ReplacingPlayersData.Remove(player.UserId); }); @@ -129,28 +160,5 @@ private void GiveData(Player player, AFKData data, RoleTypeId roleType) Log.Error($"{nameof(GiveData)}: {e}"); } } - - [PluginEvent(ServerEventType.MapGenerated)] - private void OnMapGenerated() - { - ReplacingPlayersData.Clear(); - AfkCommand.ClearAllCachedData(); - Extensions.AllElevators = Map.Elevators.ToList(); - } - - /// - /// I make sure that no player is in the dictionary in case it has not been cleaned correctly, this event is also executed when a player disconnects. - /// - [PluginEvent(ServerEventType.PlayerDeath)] - private void OnPlayerDeath(Player player, Player attacker, DamageHandlerBase damageHandler) - { - if (!player.IsReady) - return; - - if (ReplacingPlayersData.TryGetValue(player.UserId, out _)) - { - ReplacingPlayersData.Remove(player.UserId); - } - } } -} \ No newline at end of file +} diff --git a/UltimateAFK/Resources/AFKData.cs b/UltimateAFK/Resources/AFKData.cs deleted file mode 100644 index edadc22..0000000 --- a/UltimateAFK/Resources/AFKData.cs +++ /dev/null @@ -1,22 +0,0 @@ -using PlayerRoles; -using System.Collections.Generic; -using UnityEngine; - -namespace UltimateAFK.Resources -{ - public struct AFKData - { - public string NickName { get; set; } - public Vector3 Position { get; set; } - - public RoleTypeId Role { get; set; } - - public Dictionary Ammo { get; set; } - - public List Items { get; set; } - - public float Health { get; set; } - - public Scp079Data SCP079 { get; set; } - } -} \ No newline at end of file diff --git a/UltimateAFK/Resources/Component/AFKComponent.cs b/UltimateAFK/Resources/Component/AFKComponent.cs deleted file mode 100644 index ec17ef5..0000000 --- a/UltimateAFK/Resources/Component/AFKComponent.cs +++ /dev/null @@ -1,433 +0,0 @@ -using MEC; -using NWAPIPermissionSystem; -using PlayerRoles; -using PlayerRoles.PlayableScps.Scp079; -using PlayerRoles.PlayableScps.Scp096; -using PlayerRoles.Spectating; -using PluginAPI.Core; -using System; -using System.Collections.Generic; -using System.Drawing; -using UltimateAFK.Handlers; -using UnityEngine; - -namespace UltimateAFK.Resources.Component -{ - public class AfkComponent : MonoBehaviour - { - // ---------- Fields -------- - private Player Owner; - private Scp079Role Scp079Role = null; - private Scp096Role Scp096Role = null; - private readonly UltimateAFK Plugin = UltimateAFK.Singleton; - - // Seconds a player was not moving - private int secondsNotMoving; - // Position in the world - private Vector3 _lastPosition; - // Player camera position - private Vector3 _lastCameraPosition; - // Player camera rotation - private Quaternion _lastCameraRotation; - // Number of times that the player was detected as AFK - private int afkTimes = 0; - // Coroutine that handles afk checking. - private CoroutineHandle checkCoroutine; - // Cached owner user id - private string ownerUserId; - - // ----------- Unity Methods --------------- - - private void Start() - { - if (!Player.TryGet(gameObject, out Owner)) - { - Log.Error($"Error on {nameof(AfkComponent)}::{nameof(Start)}: Error on try get player"); - Destroy(this); - return; - } - - // Check if the player is not fully connected to the server. - if (!Owner.IsReady) - { - Log.Debug("Destroying a component because the player is not fully connected to the server", UltimateAFK.Singleton.Config.DebugMode); - Destroy(this); - return; - } - - // Cached Rolebase - switch (Owner.RoleBase) - { - case Scp079Role scp079Role: - { - Scp079Role = scp079Role; - break; - } - case Scp096Role scp096Role: - { - Scp096Role = scp096Role; - break; - } - } - - ownerUserId = Owner.UserId; - // Starts the coroutine that checks if the player is moving, the coroutine is cancelled if the gameobject (the player) becomes null or the component is destroyed. - checkCoroutine = Timing.RunCoroutine(AfkStatusChecker().CancelWith(gameObject).CancelWith(this)); - } - - private void OnDestroy() - { - Timing.KillCoroutines(checkCoroutine); - } - - // ---------------- Main method -------------- - - private void CheckAfkStatus() - { - if (!Round.IsRoundStarted || Player.Count < Plugin.Config.MinPlayers || Owner.Role == RoleTypeId.Tutorial && Plugin.Config.IgnoreTut - || Plugin.Config.UserIdIgnored.Contains(Owner.UserId) || Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) - return; - - Vector3 worldPosition = Owner.Position; - Vector3 cameraPosition = Owner.Camera.position; - Quaternion cameraRotation = Owner.Camera.rotation; - - switch (Owner.Role) - { - case RoleTypeId.Scp096: - { - Scp096Role ??= Owner.RoleBase as Scp096Role; - - // Player is moving or its crying on a wall/door - if (Scp096Role.IsAbilityState(Scp096AbilityState.TryingNotToCry) || IsMoving(worldPosition, cameraPosition, cameraRotation)) - { - // Do nothing player is moving - secondsNotMoving = 0; - } - else - { - Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); - - HandleAfkState(); - } - - break; - } - case RoleTypeId.Scp079: - { - Scp079Role ??= Owner.RoleBase as Scp079Role; - - cameraPosition = Scp079Role.CameraPosition; - cameraRotation = Scp079Role.CurrentCamera._cameraAnchor.transform.rotation; - - if (IsMoving(worldPosition, cameraPosition, cameraRotation)) - { - // Do nothing player is moving - secondsNotMoving = 0; - } - else - { - Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); - - HandleAfkState(); - } - break; - } - case RoleTypeId.Overwatch: - case RoleTypeId.Filmmaker: - case RoleTypeId.None: - case RoleTypeId.Spectator: - { - // Do nothing player is not alive. - secondsNotMoving = 0; - break; - } - default: - { - // Player is human or a SCP with more simple mechanic arround camera/position - - if (IsMoving(worldPosition, cameraPosition, cameraRotation)) - { - // Do nothing player is moving - secondsNotMoving = 0; - } - else - { - Log.Debug($"Player {Owner.LogName} is not moving. Seconds not moving: {secondsNotMoving}", UltimateAFK.Singleton.Config.DebugMode); - - HandleAfkState(); - } - break; - } - } - } - - private void HandleAfkState() - { - if (secondsNotMoving++ < UltimateAFK.Singleton.Config.AfkTime) return; - - // Calculate grace time - var graceTimeRemaining = UltimateAFK.Singleton.Config.GraceTime - (secondsNotMoving - UltimateAFK.Singleton.Config.AfkTime); - - if (graceTimeRemaining > 0) - { - // The player is being considered afk and is being warned that they have X time to move again or they will be kicked/replaced - Owner.SendBroadcast(string.Format(UltimateAFK.Singleton.Config.MsgGrace, graceTimeRemaining), 2, - shouldClearPrevious: true); - } - else - { - Log.Info($"{Owner.LogName} was detected as AFK"); - secondsNotMoving = 0; - API.AfkEvents.Instance.InvokePlayerAfkDetected(Owner, false); - Replace(Owner, Owner.Role); - } - } - - // ---------------- Private methods -------------- - - private bool IsMoving(Vector3 worlPosition, Vector3 cameraPosition, Quaternion cameraRotation) - { - if (worlPosition != _lastPosition || cameraPosition != _lastCameraPosition || cameraRotation != _lastCameraRotation) - { - _lastPosition = worlPosition; - _lastCameraPosition = cameraPosition; - _lastCameraRotation = cameraRotation; - return true; - } - - return false; - } - - private void Replace(Player player, RoleTypeId roleType) - { - // Check if role is blacklisted - if (Plugin.Config.RoleTypeBlacklist?.Contains(roleType) == true) - { - Log.Debug($"player {player.Nickname} ({player.UserId}) has a role that is blacklisted so he will not be searched for a replacement player", Plugin.Config.DebugMode); - - player.ClearInventory(); - player.SetRole(RoleTypeId.Spectator); - - if (Plugin.Config.AfkCount > -1) - { - afkTimes++; - - if (afkTimes >= Plugin.Config.AfkCount) - { - player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); - player.Kick(Plugin.Config.MsgKick); - return; - } - } - - player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); - return; - } - - // Get a replacement player - Player replacement = GetReplacement(); - - if (replacement == null) - { - Log.Debug("Unable to find replacement player, moving to spectator...", UltimateAFK.Singleton.Config.DebugMode); - - player.ClearInventory(); - - if (player.IsSCP) - player.ReferenceHub.roleManager.ServerSetRole(RoleTypeId.Spectator, RoleChangeReason.RemoteAdmin, RoleSpawnFlags.None); - else - player.SetRole(RoleTypeId.Spectator); - - if (Plugin.Config.AfkCount > -1) - { - afkTimes++; - - if (afkTimes >= Plugin.Config.AfkCount) - { - player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); - - player.Kick(Plugin.Config.MsgKick); - - return; - } - } - - player.SendBroadcast(Plugin.Config.MsgFspec, 30, shouldClearPrevious: true); - player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); - } - else - { - Log.Debug($"Replacement Player found: {replacement.LogName}", Plugin.Config.DebugMode); - - SaveData(replacement.UserId, roleType is RoleTypeId.Scp079); - - if (Plugin.Config.AfkCount > -1) - { - afkTimes++; - - if (afkTimes >= Plugin.Config.AfkCount) - { - player.ClearInventory(); - player.SendConsoleMessage(Plugin.Config.MsgKick, "white"); - player.Kick(Plugin.Config.MsgKick); - replacement.SetRole(roleType); - return; - } - } - - Log.Debug($"Cleaning player {player.Nickname} inventory", Plugin.Config.DebugMode); - // Clear player inventory - player.ClearInventory(); - //Send player a broadcast for being too long afk - player.SendBroadcast(Plugin.Config.MsgFspec, 25, shouldClearPrevious: true); - player.SendConsoleMessage(Plugin.Config.MsgFspec, "white"); - - // Sends replacement to the role that had the afk - Log.Debug($"Changing replacement player {replacement.LogName} role to {roleType}", Plugin.Config.DebugMode); - replacement.SetRole(roleType); - // Sends player to spectator - Log.Debug($"Changing player {player.Nickname} to spectator", Plugin.Config.DebugMode); - - if (player.IsSCP) - player.ReferenceHub.roleManager.ServerSetRole(RoleTypeId.Spectator, RoleChangeReason.RemoteAdmin, RoleSpawnFlags.None); - else - player.SetRole(RoleTypeId.Spectator); - player.SendConsoleMessage(string.Format(UltimateAFK.Singleton.Config.MsgReplaced, replacement.Nickname), "white"); - } - } - - /// - /// Searches and returns the player with the longest active time in spectator mode. - /// If no valid player is found, it returns null. - /// - /// The player with the longest active time or null if none is found. - private Player GetReplacement() - { - try - { - Player longestSpectator = null; - float maxActiveTime = 0f; - - foreach (var player in Player.GetPlayers()) - { - if (IgnorePlayer(player)) - continue; - - // It is faster to compare an enum than to compare a class. - if (player.Role == RoleTypeId.Spectator && player.RoleBase is SpectatorRole role) - { - if (role.ActiveTime > maxActiveTime) - { - maxActiveTime = role.ActiveTime; - longestSpectator = player; - } - } - } - - return longestSpectator; - } - catch (Exception e) - { - Log.Error($"Error on {nameof(AfkComponent)}::{nameof(GetReplacement)}: {e.Message} || typeof {e.GetType()}"); - return null; - } - } - - /// - /// Checks if the provided player should be ignored in the AFK replacement process. - /// - /// The player to check. - /// True if the player should be ignored, otherwise false. - private bool IgnorePlayer(Player player) - { - if (!player.IsReady || Plugin.Config.UserIdIgnored.Contains(Owner.UserId) || player.TemporaryData.StoredData.ContainsKey("uafk_disable") || player.UserId == ownerUserId || player.IsAlive || player.CheckPermission("uafk.ignore") || player.IsServer || player.UserId.Contains("@server") || player.UserId.Contains("@npc") || MainHandler.ReplacingPlayersData.TryGetValue(player.UserId, out _)) - return true; - - return false; - } - - /// - /// Saves the relevant data of a player for potential AFK replacement. - /// - /// The user ID of the player who will be the replacement. - /// Specifies if the player is SCP-079 (True if SCP-079, False if not). - private void SaveData(string replacementUserId, bool isScp079 = false) - { - AFKData data; - - if (isScp079) - { - if (Owner.RoleBase is Scp079Role scp079Role && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager) - && scp079Role.SubroutineModule.TryGetSubroutine(out Scp079AuxManager energyManager)) - { - data = new AFKData() - { - NickName = Owner.Nickname, - Position = Owner.Position, - Role = Owner.Role, - Ammo = null, - Health = Owner.Health, - Items = null, - SCP079 = new Scp079Data - { - Energy = energyManager.CurrentAux, - Experience = tierManager.TotalExp, - } - }; - } - else - { - // Return early if SCP-079 data cannot be obtained - return; - } - } - else - { - var ammo = Extensions.GetAmmo(Owner); - - data = new AFKData() - { - NickName = Owner.Nickname, - Position = Owner.Position, - Role = Owner.Role, - Ammo = ammo, - Health = Owner.Health, - Items = Owner.GetItems(), - SCP079 = new Scp079Data - { - Energy = 0f, - Experience = 0 - } - }; - } - - MainHandler.ReplacingPlayersData.Add(replacementUserId, data); - } - - - // ---------------- Coroutines ------------------- - - /// - /// Coroutine that continuously checks and updates the AFK status of players. - /// - private IEnumerator AfkStatusChecker() - { - - while (true) - { - try - { - CheckAfkStatus(); - } - catch (Exception e) - { - Log.Error($"Error on {nameof(AfkComponent)}::{nameof(AfkStatusChecker)}: {e.Message} || typeof {e.GetType()}"); - } - - yield return Timing.WaitForSeconds(1); - } - } - } -} diff --git a/UltimateAFK/Resources/Extensions.cs b/UltimateAFK/Resources/Extensions.cs deleted file mode 100644 index 081e452..0000000 --- a/UltimateAFK/Resources/Extensions.cs +++ /dev/null @@ -1,123 +0,0 @@ -using Interactables.Interobjects; -using InventorySystem.Items.Firearms; -using InventorySystem.Items.Firearms.Attachments; -using PluginAPI.Core; -using System.Collections.Generic; -using System.Linq; - -namespace UltimateAFK.Resources -{ - public static class Extensions - { - public static List AllElevators = new(Map.Elevators); - /// - /// Adds several items at the same time to a player. - /// - public static void SendItems(this Player player, List types) - { - foreach (var item in types) - { - player.AddItem(item); - } - } - - /// - /// Gives the ammunition you want a player to have. - /// - public static void SendAmmo(this Player ply, Dictionary ammo) - { - foreach (var ammoItem in ammo) - { - ply.SetAmmo(ammoItem.Key, ammoItem.Value); - } - } - - - public static List GetItems(this Player ply) - { - var items = ply.ReferenceHub.inventory.UserInventory.Items; - var returnitems = new List(); - - foreach (var i in items.Values) - { - if (i.ItemTypeId is ItemType.Ammo9x19 or ItemType.Ammo12gauge or ItemType.Ammo44cal or ItemType.Ammo556x45 or ItemType.Ammo762x39) continue; - - returnitems.Add(i.ItemTypeId); - } - - return returnitems; - } - - public static Dictionary GetAmmo(Player player) - { - var result = new Dictionary(); - - foreach (var ammo in player.ReferenceHub.inventory.UserInventory.ReserveAmmo) - { - result.Add(ammo.Key, ammo.Value); - } - - return result; - } - - /// - /// Applies player attachments. - /// - /// - public static void ApplyAttachments(this Player ply) - { - var item = ply.Items.Where(i => i is Firearm); - - foreach (var fire in item) - { - if (fire is Firearm fireArm) - { - if (AttachmentsServerHandler.PlayerPreferences.TryGetValue(ply.ReferenceHub, out var value) && value.TryGetValue(fireArm.ItemTypeId, out var value2)) - fireArm.ApplyAttachmentsCode(value2, reValidate: true); - var firearmStatusFlags = FirearmStatusFlags.MagazineInserted; - if (fireArm.HasAdvantageFlag(AttachmentDescriptiveAdvantages.Flashlight)) - firearmStatusFlags |= FirearmStatusFlags.FlashlightEnabled; - - fireArm.Status = new FirearmStatus(fireArm.AmmoManagerModule.MaxAmmo, firearmStatusFlags, fireArm.GetCurrentAttachmentsCode()); - } - } - } - - public static void ReloadAllWeapons(this Player ply) - { - var item = ply.Items.Where(i => i is Firearm); - - foreach (var weapon in item) - { - if (weapon is Firearm firearm) - { - firearm.Status = new FirearmStatus(firearm.AmmoManagerModule.MaxAmmo, firearm.Status.Flags, - firearm.GetCurrentAttachmentsCode()); - } - } - } - - /// - /// Check if a is un bounds of a - /// - /// - /// - public static bool InElevator(this Player player) - { - foreach (var elevator in AllElevators) - { - if (elevator.WorldspaceBounds.Contains(player.Position) && elevator.IsMoving()) - return true; - } - - return false; - } - - /// - /// Check if the current is moving. - /// - /// - /// - public static bool IsMoving(this ElevatorChamber elevator) => elevator._curSequence is ElevatorChamber.ElevatorSequence.MovingAway or ElevatorChamber.ElevatorSequence.Arriving; - } -} \ No newline at end of file diff --git a/UltimateAFK/Resources/Scp079Data.cs b/UltimateAFK/Resources/Scp079Data.cs deleted file mode 100644 index 88de869..0000000 --- a/UltimateAFK/Resources/Scp079Data.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UltimateAFK.Resources -{ - public struct Scp079Data - { - public int Experience { get; set; } - - public float Energy { get; set; } - } -} \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.cs b/UltimateAFK/UltimateAFK.cs deleted file mode 100644 index 8a3f8c0..0000000 --- a/UltimateAFK/UltimateAFK.cs +++ /dev/null @@ -1,46 +0,0 @@ -using PluginAPI; -using PluginAPI.Core; -using PluginAPI.Core.Attributes; -using PluginAPI.Enums; -using UltimateAFK.API; -using UltimateAFK.Handlers; -using UltimateAFK.Resources; - -namespace UltimateAFK -{ - /// - /// Main class where all the handlers are loaded. - /// - public class UltimateAFK - { - public static UltimateAFK Singleton; - - [PluginConfig] public Config Config; - - [PluginPriority(LoadPriority.High)] - [PluginEntryPoint("UltimateAFK", "6.4.4", "Checks if a player is afk for too long and if detected as afk will be replaced by a spectator.", "SrLicht")] - void OnEnabled() - { - Singleton = this; - PluginAPI.Events.EventManager.RegisterEvents(this, new MainHandler(Singleton)); - AfkEvents.Instance.PlayerAfkDetectedEvent += OnPlayerIsDetectedAfk; - } - - [PluginUnload] - void OnDisable() - { - MainHandler.ReplacingPlayersData.Clear(); - MainHandler.ReplacingPlayersData = null; - Extensions.AllElevators.Clear(); - AfkEvents.Instance.PlayerAfkDetectedEvent -= OnPlayerIsDetectedAfk; - } - - public void OnPlayerIsDetectedAfk(Player player, bool isForCommand) - { - if (isForCommand) - { - Log.Info($"{player.LogName} use the command to be moved to spectator"); - } - } - } -} \ No newline at end of file diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 0163541..87f7db2 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -5,6 +5,7 @@ Library UltimateAFK true + Enable @@ -17,13 +18,11 @@ - - - + diff --git a/build.ps1 b/build.ps1 deleted file mode 100644 index 40465a9..0000000 --- a/build.ps1 +++ /dev/null @@ -1,47 +0,0 @@ -#! pwsh - -param ( - [Switch]$BuildNuGet -) - -$Projects = @( - "UltimateAFK" -) - -function Execute { - param ( - [string]$Cmd - ) - - foreach ($Project in $Projects) { - Invoke-Expression ([string]::Join(' ', $Cmd, $Project, $args)) - CheckLastOperationStatus - } -} - -function CheckLastOperationStatus { - if ($? -eq $false) { - Exit 1 - } -} - -function GetSolutionVersion { - [XML]$PropsFile = Get-Content Cerberus.props - $Version = $PropsFile.Project.PropertyGroup[2].Version - $Version = $Version.'#text'.Trim() - return $Version -} - -# Restore projects -Execute 'dotnet restore' -# Build projects -Execute 'dotnet build' -# Build a NuGet package if needed -if ($BuildNuGet) { - $Version = GetSolutionVersion - $Year = [System.DateTime]::Now.ToString('yyyy') - - Write-Host "Generating NuGet package for version $Version" - - nuget pack Exiled/Exiled.nuspec -Version $Version -Properties Year=$Year -} From da11039137aa92baab500e98c13905678352c3d8 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 15 Nov 2023 11:53:47 -0300 Subject: [PATCH 141/146] 7.0.1 * Fixing crash with dummys * Removing unnecesary Log.Info --- UltimateAFK/API/Components/AfkCheckComponent.cs | 6 ++---- UltimateAFK/EntryPoint.cs | 2 +- UltimateAFK/MainHandler.cs | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/UltimateAFK/API/Components/AfkCheckComponent.cs b/UltimateAFK/API/Components/AfkCheckComponent.cs index 0d2d882..d122674 100644 --- a/UltimateAFK/API/Components/AfkCheckComponent.cs +++ b/UltimateAFK/API/Components/AfkCheckComponent.cs @@ -113,6 +113,7 @@ private void CheckAfk() Vector3 cameraPosition = Owner.Camera.position; Quaternion cameraRotation = Owner.Camera.rotation; + Log.Debug($"{nameof(CheckAfk)}: Owner role is {Owner.Role}", PluginConfig.DebugMode); switch (Owner.Role) { case RoleTypeId.Scp096: @@ -364,9 +365,6 @@ private void HandleAfkState() Player? longestSpectator = null; float maxActiveTime = 0f; - Log.Info($"Player user id using PluginAPI is " + Owner.UserId); - Log.Info($"Player user id using manual method is " + Owner.ReferenceHub.authManager.UserId); - // Find the longest-active spectator among non-ignored players foreach (var player in Player.GetPlayers().Where(p => !IgnorePlayer(p))) { @@ -456,7 +454,7 @@ private bool IsMoving(Vector3 worldPosition, Vector3 cameraPosition, Quaternion protected bool TryGetOwner(out Player player) { player = Player.Get(gameObject); - return player != null && player.IsReady; + return player != null && player.ReferenceHub.authManager.InstanceMode is CentralAuth.ClientInstanceMode.ReadyClient; } // fields // diff --git a/UltimateAFK/EntryPoint.cs b/UltimateAFK/EntryPoint.cs index f0babfb..9ae3bad 100644 --- a/UltimateAFK/EntryPoint.cs +++ b/UltimateAFK/EntryPoint.cs @@ -23,7 +23,7 @@ public class EntryPoint /// /// Gets the plugin version. /// - public const string Version = "7.0.0"; + public const string Version = "7.0.1"; /// /// Called when loading the plugin diff --git a/UltimateAFK/MainHandler.cs b/UltimateAFK/MainHandler.cs index 24fe52d..c764130 100644 --- a/UltimateAFK/MainHandler.cs +++ b/UltimateAFK/MainHandler.cs @@ -32,7 +32,7 @@ public class MainHandler [PluginEvent] private void OnPlayerJoined(PlayerJoinedEvent ev) { - if (!PluginConfig.IsEnabled || ev.Player is null || ev.Player.UserId.Contains("@server") || !ev.Player.IsReady) + if (!PluginConfig.IsEnabled || ev.Player is null || ev.Player.UserId.Contains("@server") || ev.Player.ReferenceHub.authManager.InstanceMode != CentralAuth.ClientInstanceMode.ReadyClient) return; ev.Player.GameObject.AddComponent(); From fa907c78b1c0786fd90fe9bb9e28eb1e850d6c37 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Thu, 30 Nov 2023 22:16:31 -0300 Subject: [PATCH 142/146] 7.0.2 * Fixed an issue where player replacement was not working properly. --- UltimateAFK/API/Extensions.cs | 3 +-- UltimateAFK/API/Structs/AfkData.cs | 2 ++ UltimateAFK/Command/AfkCommand.cs | 1 - UltimateAFK/EntryPoint.cs | 2 +- UltimateAFK/MainHandler.cs | 5 +++++ 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/UltimateAFK/API/Extensions.cs b/UltimateAFK/API/Extensions.cs index 8c44e5c..c9aaa89 100644 --- a/UltimateAFK/API/Extensions.cs +++ b/UltimateAFK/API/Extensions.cs @@ -383,9 +383,8 @@ private static void SaveData(Player Owner, string replacementUserId, bool isScp0 } else { + // SCP-079 data cannot be obtained data = new AfkData(Owner.Nickname, Owner.Position, Owner.Role, null, new(), Owner.Health, null); - // Return early if SCP-079 data cannot be obtained - return; } } else diff --git a/UltimateAFK/API/Structs/AfkData.cs b/UltimateAFK/API/Structs/AfkData.cs index a02ea7a..a298b21 100644 --- a/UltimateAFK/API/Structs/AfkData.cs +++ b/UltimateAFK/API/Structs/AfkData.cs @@ -64,6 +64,8 @@ public AfkData(string nickname, Vector3 position, RoleTypeId roleType, Dictionar /// Gets the SCP-079 role data of the AFK player (nullable). /// public Scp079RoleData? Scp079Data { get; } + + public override string ToString() => $"{Nickname} | {RoleType} | {Position} | {Health} | {Items.Count} | {Scp079Data is null} | {Ammo is null} |"; } } diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 33bfd83..1f0a085 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -141,7 +141,6 @@ private IEnumerator AfkCountdown(Player player, string userid, RoleTypeId if (player == null || !player.IsAlive || !Round.IsRoundStarted) { - Log.Info("Killing coroutine in first if"); InProcess.Remove(userid); yield break; } diff --git a/UltimateAFK/EntryPoint.cs b/UltimateAFK/EntryPoint.cs index 9ae3bad..7657b04 100644 --- a/UltimateAFK/EntryPoint.cs +++ b/UltimateAFK/EntryPoint.cs @@ -23,7 +23,7 @@ public class EntryPoint /// /// Gets the plugin version. /// - public const string Version = "7.0.1"; + public const string Version = "7.0.2"; /// /// Called when loading the plugin diff --git a/UltimateAFK/MainHandler.cs b/UltimateAFK/MainHandler.cs index c764130..ce87398 100644 --- a/UltimateAFK/MainHandler.cs +++ b/UltimateAFK/MainHandler.cs @@ -84,6 +84,11 @@ private void OnPlayerDeath(PlayerDeathEvent ev) } } + /// + /// Here i detec if the player who is changing role is any replacement player. + /// + /// + [PluginEvent] private void OnChangeRole(PlayerChangeRoleEvent ev) { if (ev.Player is null || !ev.Player.IsReady || ev.NewRole == PlayerRoles.RoleTypeId.Spectator || !ReplacingPlayersData.TryGetValue(ev.Player.UserId, out var data)) From b8e8763aff2192c685b1075520ea1c53cc5c7bfc Mon Sep 17 00:00:00 2001 From: SrLicht Date: Sun, 3 Dec 2023 15:54:47 -0300 Subject: [PATCH 143/146] 7.0.3 * Fixing a bug with the component dont checking the 'uafk.ignore' for detecting afk players --- .../API/Components/AfkCheckComponent.cs | 21 +++++++++++++++++-- UltimateAFK/EntryPoint.cs | 2 +- UltimateAFK/UltimateAFK.csproj | 1 - 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/UltimateAFK/API/Components/AfkCheckComponent.cs b/UltimateAFK/API/Components/AfkCheckComponent.cs index d122674..c3213f1 100644 --- a/UltimateAFK/API/Components/AfkCheckComponent.cs +++ b/UltimateAFK/API/Components/AfkCheckComponent.cs @@ -35,6 +35,21 @@ public class AfkCheckComponent : MonoBehaviour /// private Config PluginConfig => EntryPoint.Instance.Config; + /// + /// Checks if the owner have the 'uafk.ignore' permission. + /// + private bool IsIgnored = false; + + /// + /// Check if the owner have the 'uafk_disable_check' temporary data. + /// + private bool IsTemporaryIgnored => Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check") || Owner.Role == RoleTypeId.Tutorial && PluginConfig.IgnoreTut; + + /// + /// Checks if the current is ended. + /// + private bool IsRoundEnded => Round.Duration.TotalSeconds > 0 && !Round.IsRoundStarted; + private void Start() { if (!TryGetOwner(out Owner)) @@ -45,6 +60,9 @@ private void Start() } OwnerUserId = Owner.UserId; + + IsIgnored = Owner.CheckPermission("uafk.ignore") || PluginConfig.UserIdIgnored.Contains(Owner.UserId); + // Starts the coroutine that checks if the player is moving, the coroutine is cancelled if the gameobject (the player) becomes null or the component is destroyed. CoroutineHandle = Timing.RunCoroutine(AfkStatusChecker().CancelWith(gameObject).CancelWith(this)); Log.Debug($"Component {GetType().Name} fully loaded for {Owner.LogName}", PluginConfig.DebugMode, "UltimateAfk"); @@ -104,8 +122,7 @@ private IEnumerator AfkStatusChecker() /// private void CheckAfk() { - if (!Round.IsRoundStarted || Player.Count < PluginConfig.MinPlayers || Owner.Role == RoleTypeId.Tutorial && PluginConfig.IgnoreTut - || PluginConfig.UserIdIgnored.Contains(Owner.UserId) || Owner.TemporaryData.StoredData.ContainsKey("uafk_disable_check")) + if (!Round.IsRoundStarted || IsRoundEnded || Player.Count < PluginConfig.MinPlayers || IsIgnored || IsTemporaryIgnored) return; // Retrieve player information. diff --git a/UltimateAFK/EntryPoint.cs b/UltimateAFK/EntryPoint.cs index 7657b04..3473060 100644 --- a/UltimateAFK/EntryPoint.cs +++ b/UltimateAFK/EntryPoint.cs @@ -23,7 +23,7 @@ public class EntryPoint /// /// Gets the plugin version. /// - public const string Version = "7.0.2"; + public const string Version = "7.0.3"; /// /// Called when loading the plugin diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 87f7db2..f7b9a2d 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -23,7 +23,6 @@ - From 68a5e2ca3c7285a84d9c184c846464a3f0adc332 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 13 Dec 2023 15:06:35 -0300 Subject: [PATCH 144/146] Update UltimateAFK.csproj --- UltimateAFK/UltimateAFK.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index f7b9a2d..061d994 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -22,7 +22,6 @@ - From 906820b56ee32649f0bb02425124baff2462d3dd Mon Sep 17 00:00:00 2001 From: SrLicht Date: Wed, 10 Jan 2024 01:34:36 -0300 Subject: [PATCH 145/146] 7.0.4 * Added disable replacement in the config file * The permission 'uafk.detectiondisabled' has been added and it will disable the detection of afk but not the replacement, players with uafk.detectiondisabled can be replace players afk --- .../API/Components/AfkCheckComponent.cs | 18 ++++++++++++++++-- UltimateAFK/API/Extensions.cs | 10 +++++++++- UltimateAFK/Config.cs | 7 +++++++ UltimateAFK/EntryPoint.cs | 2 +- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/UltimateAFK/API/Components/AfkCheckComponent.cs b/UltimateAFK/API/Components/AfkCheckComponent.cs index c3213f1..01930ce 100644 --- a/UltimateAFK/API/Components/AfkCheckComponent.cs +++ b/UltimateAFK/API/Components/AfkCheckComponent.cs @@ -50,6 +50,11 @@ public class AfkCheckComponent : MonoBehaviour /// private bool IsRoundEnded => Round.Duration.TotalSeconds > 0 && !Round.IsRoundStarted; + /// + /// If is disable the player will not be replaced. + /// + private bool IsReplacedDisable = false; + private void Start() { if (!TryGetOwner(out Owner)) @@ -61,7 +66,9 @@ private void Start() OwnerUserId = Owner.UserId; - IsIgnored = Owner.CheckPermission("uafk.ignore") || PluginConfig.UserIdIgnored.Contains(Owner.UserId); + IsIgnored = Owner.CheckPermission("uafk.ignore") || Owner.CheckPermission("uafk.detectiondisabled") || PluginConfig.UserIdIgnored.Contains(Owner.UserId); + + IsReplacedDisable = PluginConfig.DisableReplacement; // Starts the coroutine that checks if the player is moving, the coroutine is cancelled if the gameobject (the player) becomes null or the component is destroyed. CoroutineHandle = Timing.RunCoroutine(AfkStatusChecker().CancelWith(gameObject).CancelWith(this)); @@ -238,7 +245,10 @@ private void Replace(Player player, RoleTypeId roleType) if (replacement == null) { - Log.Debug("Unable to find replacement player, moving to spectator...", PluginConfig.DebugMode, "UltimateAfk"); + if(IsReplacedDisable) + Log.Debug("AFK player replacement is disabled by the plugin configuration.", PluginConfig.DebugMode, "UltimateAfk"); + else + Log.Debug("Unable to find replacement player, moving to spectator...", PluginConfig.DebugMode, "UltimateAfk"); player.ClearInventory(); @@ -379,6 +389,10 @@ private void HandleAfkState() { try { + + if (IsReplacedDisable) + return null; + Player? longestSpectator = null; float maxActiveTime = 0f; diff --git a/UltimateAFK/API/Extensions.cs b/UltimateAFK/API/Extensions.cs index c9aaa89..62455ac 100644 --- a/UltimateAFK/API/Extensions.cs +++ b/UltimateAFK/API/Extensions.cs @@ -8,6 +8,7 @@ using PlayerRoles.PlayableScps.Scp079; using PlayerRoles.Spectating; using PluginAPI.Core; +using PluginAPI.Core.Attributes; using System; using System.Collections.Generic; using System.Linq; @@ -120,7 +121,10 @@ public static void Replace(this Player player, RoleTypeId role, bool countForKic if (replacement == null) { - Log.Debug("Unable to find replacement player, moving to spectator...", EntryPoint.Instance.Config.DebugMode); + if (EntryPoint.Instance.Config.DisableReplacement) + Log.Debug("AFK player replacement is disabled by the plugin configuration.", EntryPoint.Instance.Config.DebugMode); + else + Log.Debug("Unable to find replacement player, moving to spectator...", EntryPoint.Instance.Config.DebugMode); player.ClearInventory(); @@ -315,6 +319,10 @@ public static bool InElevator(this Player player) { try { + // Return null if replacement is disable + if (EntryPoint.Instance.Config.DisableReplacement) + return null; + Player? longestSpectator = null; float maxActiveTime = 0f; diff --git a/UltimateAFK/Config.cs b/UltimateAFK/Config.cs index a218184..0cc96fb 100644 --- a/UltimateAFK/Config.cs +++ b/UltimateAFK/Config.cs @@ -24,6 +24,13 @@ public class Config // copy from repository + + /// + /// Gets or sets if the replacement of afk player is disabled. + /// + [Description("Setting this to true will cause players who are detected afk not to be replaced but only moved to spectator/kicked of the server.")] + public bool DisableReplacement { get; set; } = false; + /// /// Gets or sets the deplay for replacing players. /// diff --git a/UltimateAFK/EntryPoint.cs b/UltimateAFK/EntryPoint.cs index 3473060..3fdee7b 100644 --- a/UltimateAFK/EntryPoint.cs +++ b/UltimateAFK/EntryPoint.cs @@ -23,7 +23,7 @@ public class EntryPoint /// /// Gets the plugin version. /// - public const string Version = "7.0.3"; + public const string Version = "7.0.4"; /// /// Called when loading the plugin From 9d8caa39aaf2d401aa6e161c9e2046a8f3a33881 Mon Sep 17 00:00:00 2001 From: SrLicht Date: Fri, 7 Jun 2024 19:55:55 -0300 Subject: [PATCH 146/146] 7.0.5 Updated for SL13.5 --- UltimateAFK/Command/AfkCommand.cs | 1 + UltimateAFK/EntryPoint.cs | 2 +- UltimateAFK/UltimateAFK.csproj | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/UltimateAFK/Command/AfkCommand.cs b/UltimateAFK/Command/AfkCommand.cs index 1f0a085..449593b 100644 --- a/UltimateAFK/Command/AfkCommand.cs +++ b/UltimateAFK/Command/AfkCommand.cs @@ -14,6 +14,7 @@ namespace UltimateAFK.Command [CommandHandler(typeof(ClientCommandHandler))] internal sealed class AfkCommand : ICommand { + public bool SanitizeResponse => false; public string Command { get; } = "afk"; public string[] Aliases { get; } = Array.Empty(); public string Description { get; } = "By using this command you will be moved to spectator and if the server allows it a player will replace you."; diff --git a/UltimateAFK/EntryPoint.cs b/UltimateAFK/EntryPoint.cs index 3fdee7b..6483c10 100644 --- a/UltimateAFK/EntryPoint.cs +++ b/UltimateAFK/EntryPoint.cs @@ -23,7 +23,7 @@ public class EntryPoint /// /// Gets the plugin version. /// - public const string Version = "7.0.4"; + public const string Version = "7.0.5"; /// /// Called when loading the plugin diff --git a/UltimateAFK/UltimateAFK.csproj b/UltimateAFK/UltimateAFK.csproj index 061d994..7e722dc 100644 --- a/UltimateAFK/UltimateAFK.csproj +++ b/UltimateAFK/UltimateAFK.csproj @@ -35,7 +35,7 @@ - - + + \ No newline at end of file