using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using FishNet; using FishNet.Broadcast; using FishNet.Connection; using FishNet.Object; using FishNet.Serializing; using FishNet.Transporting; using HarmonyLib; using Microsoft.CodeAnalysis; using NativeStatusUI; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ArceDev.StatusCore")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("ArceDev.StatusCore")] [assembly: AssemblyTitle("Status Core")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] [Embedded] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] [Embedded] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace Microsoft.CodeAnalysis { [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace StatusCore { [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("ArceDev.StatusCore", "Status Core", "0.1.0")] public class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(Player), "OnStartClient")] private static class PlayerStartPatch { private static void Postfix() { StatusApi.RegisterClient(); } } [HarmonyPatch(typeof(Server), "OnStartClient")] private static class ServerStartPatch { private static void Postfix() { StatusApi.RegisterClient(); } } [HarmonyPatch(typeof(Server), "OnStopClient")] private static class ServerStopPatch { private static void Prefix() { StatusApi.ResetSession(); } } public const string Id = "ArceDev.StatusCore"; internal static ManualLogSource Log { get; private set; } public static string Name => "Status Core"; public static string Version => "0.1.0"; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; StatusApi.InitializeNetwork(); StatusApi.Validate(); Harmony.CreateAndPatchAll(typeof(Plugin).Assembly, "ArceDev.StatusCore"); Log.LogInfo((object)("Plugin " + Name + " is loaded!")); } private void Update() { StatusApi.UpdateServer(); } } public enum StatusRemovalReason : byte { Expired, Removed, TargetUnavailable, SessionEnded } public sealed class StatusDefinition { private readonly byte[] _iconPng; public string Id { get; } public string DisplayName { get; } public Color Color { get; } public float TickInterval { get; } public bool RemoveOnDeath { get; } internal Action? OnApplied { get; } internal Action? OnTick { get; } internal Action? OnRemoved { get; } internal byte[] IconPng => _iconPng; public StatusDefinition(string id, string displayName, byte[] iconPng, Color color, float tickInterval = 0f, Action? onApplied = null, Action? onTick = null, Action? onRemoved = null, bool removeOnDeath = true) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) Id = Required(id, "id"); DisplayName = Required(displayName, "displayName"); if (iconPng == null || iconPng.Length <= 0) { throw new ArgumentException("A status icon PNG is required.", "iconPng"); } _iconPng = (byte[])iconPng.Clone(); Color = color; if (!(tickInterval >= 0f)) { throw new ArgumentOutOfRangeException("tickInterval"); } TickInterval = tickInterval; OnApplied = onApplied; OnTick = onTick; OnRemoved = onRemoved; RemoveOnDeath = removeOnDeath; } private static string Required(string value, string parameter) { if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentException("A non-empty value is required.", parameter); } return value; } } public readonly record struct StatusContext(string StatusId, Player Target, Player? Source, float Duration, float Remaining); public readonly record struct StatusSnapshot(string StatusId, int TargetObjectId, int SourceObjectId, float Duration, float Remaining); internal readonly record struct StatusChangeBroadcast(string StatusId, float Duration, bool Active) : IBroadcast; public static class StatusApi { private readonly record struct StatusKey(int TargetObjectId, string StatusId); private readonly record struct LocalStatus(float Duration, float ExpiresAt); private sealed class ActiveStatus { internal Player Target { get; } internal Player? Source { get; } internal float Duration { get; } internal float ExpiresAt { get; } internal float NextTick { get; set; } internal ActiveStatus(Player target, Player? source, float duration, float expiresAt, float nextTick) { Target = target; Source = source; Duration = duration; ExpiresAt = expiresAt; NextTick = nextTick; } } private static readonly Dictionary Definitions = new Dictionary(StringComparer.Ordinal); private static readonly List Ordered = new List(); private static readonly Dictionary ServerStatuses = new Dictionary(); private static readonly Dictionary LocalStatuses = new Dictionary(StringComparer.Ordinal); private static readonly Action ClientHandler = OnClientChange; private static bool _networkInitialized; private static bool _clientRegistered; public static IReadOnlyList Catalog => Ordered.AsReadOnly(); public static void Register(StatusDefinition definition) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (definition == null) { throw new ArgumentNullException("definition"); } if (!Definitions.TryAdd(definition.Id, definition)) { throw new InvalidOperationException("Status '" + definition.Id + "' is already registered."); } Ordered.Add(definition); NativeStatus.Register(definition.Id, definition.IconPng, definition.Color); } public static void Apply(Player target, string statusId, float duration, Player? source = null) { EnsureServer(); if (!Object.op_Implicit((Object)(object)target) || ((NetworkBehaviour)target).IsDeinitializing) { throw new ArgumentNullException("target"); } if (duration <= 0f) { throw new ArgumentOutOfRangeException("duration"); } StatusDefinition statusDefinition = Definition(statusId); float time = Time.time; ActiveStatus activeStatus = new ActiveStatus(target, source, duration, time + duration, (statusDefinition.TickInterval > 0f) ? (time + statusDefinition.TickInterval) : 0f); ServerStatuses[new StatusKey(((NetworkBehaviour)target).ObjectId, statusId)] = activeStatus; statusDefinition.OnApplied?.Invoke(Context(statusId, activeStatus, time)); Send(target, new StatusChangeBroadcast(statusId, duration, Active: true)); } public static bool Remove(Player target, string statusId) { EnsureServer(); if (Object.op_Implicit((Object)(object)target)) { return Remove(new StatusKey(((NetworkBehaviour)target).ObjectId, statusId), StatusRemovalReason.Removed, notify: true); } return false; } public static bool IsActive(Player target, string statusId) { if (!Object.op_Implicit((Object)(object)target) || !Definitions.ContainsKey(statusId)) { return false; } ActiveStatus active; if (InstanceFinder.IsServerStarted) { return TryGetServer(new StatusKey(((NetworkBehaviour)target).ObjectId, statusId), out active); } LocalStatus local; float remaining; if (((NetworkBehaviour)target).IsOwner) { return TryGetLocal(statusId, out local, out remaining); } return false; } public static bool TryGet(Player target, string statusId, out StatusSnapshot snapshot) { snapshot = default(StatusSnapshot); if (!Object.op_Implicit((Object)(object)target) || !Definitions.ContainsKey(statusId)) { return false; } if (InstanceFinder.IsServerStarted && TryGetServer(new StatusKey(((NetworkBehaviour)target).ObjectId, statusId), out ActiveStatus active)) { snapshot = Snapshot(statusId, active, Time.time); return true; } if (!((NetworkBehaviour)target).IsOwner || !TryGetLocal(statusId, out var local, out var remaining)) { return false; } snapshot = new StatusSnapshot(statusId, ((NetworkBehaviour)target).ObjectId, -1, local.Duration, remaining); return true; } internal static void InitializeNetwork() { if (!_networkInitialized) { GenericWriter.SetWrite((Action)WriteChange); GenericReader.SetRead((Func)ReadChange); _networkInitialized = true; } } internal static void RegisterClient() { if (!_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager)) { InstanceFinder.ClientManager.RegisterBroadcast(ClientHandler); _clientRegistered = true; } } internal static void UpdateServer() { if (InstanceFinder.IsServerStarted && ServerStatuses.Count != 0) { float time = Time.time; StatusKey[] array = ServerStatuses.Keys.ToArray(); foreach (StatusKey key in array) { UpdateStatus(key, time); } } } internal static void ResetSession() { if (_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager)) { InstanceFinder.ClientManager.UnregisterBroadcast(ClientHandler); } StatusKey[] array = ServerStatuses.Keys.ToArray(); foreach (StatusKey key in array) { Remove(key, StatusRemovalReason.SessionEnded, notify: false); } string[] array2 = LocalStatuses.Keys.ToArray(); foreach (string text in array2) { NativeStatus.Hide(text); } LocalStatuses.Clear(); _clientRegistered = false; } internal static void Validate() { if (Remaining(10f, 15f, 7f) != 8f || Remaining(10f, 5f, 7f) != 0f) { throw new InvalidOperationException("Status Core duration validation failed."); } } private static void UpdateStatus(StatusKey key, float now) { ActiveStatus activeStatus = ServerStatuses[key]; StatusDefinition statusDefinition = Definitions[key.StatusId]; StatusRemovalReason? statusRemovalReason = RemovalReason(activeStatus, statusDefinition, now); if (statusRemovalReason.HasValue) { Remove(key, statusRemovalReason.Value, statusRemovalReason != StatusRemovalReason.TargetUnavailable); } else if (!(statusDefinition.TickInterval <= 0f) && !(now < activeStatus.NextTick)) { statusDefinition.OnTick?.Invoke(Context(key.StatusId, activeStatus, now)); activeStatus.NextTick = now + statusDefinition.TickInterval; } } private static StatusRemovalReason? RemovalReason(ActiveStatus active, StatusDefinition definition, float now) { if (!Object.op_Implicit((Object)(object)active.Target) || ((NetworkBehaviour)active.Target).IsDeinitializing) { return StatusRemovalReason.TargetUnavailable; } if (now >= active.ExpiresAt) { return StatusRemovalReason.Expired; } if (!definition.RemoveOnDeath || !active.Target.Dying.IsDead) { return null; } return StatusRemovalReason.TargetUnavailable; } private static bool Remove(StatusKey key, StatusRemovalReason reason, bool notify) { if (!ServerStatuses.TryGetValue(key, out ActiveStatus value)) { return false; } ServerStatuses.Remove(key); Definitions[key.StatusId].OnRemoved?.Invoke(Context(key.StatusId, value, Time.time), reason); if (notify && Object.op_Implicit((Object)(object)value.Target)) { Send(value.Target, new StatusChangeBroadcast(key.StatusId, 0f, Active: false)); } return true; } private static bool TryGetServer(StatusKey key, out ActiveStatus active) { if (!ServerStatuses.TryGetValue(key, out active)) { return false; } StatusRemovalReason? statusRemovalReason = RemovalReason(active, Definitions[key.StatusId], Time.time); if (!statusRemovalReason.HasValue) { return true; } Remove(key, statusRemovalReason.Value, notify: true); active = null; return false; } private static bool TryGetLocal(string statusId, out LocalStatus local, out float remaining) { if (LocalStatuses.TryGetValue(statusId, out local)) { remaining = Mathf.Max(0f, local.ExpiresAt - Time.time); if (remaining > 0f) { return true; } LocalStatuses.Remove(statusId); NativeStatus.Hide(statusId); } local = default(LocalStatus); remaining = 0f; return false; } private static void OnClientChange(StatusChangeBroadcast message, Channel channel) { if (Definitions.ContainsKey(message.StatusId)) { if (message.Active && message.Duration > 0f) { LocalStatuses[message.StatusId] = new LocalStatus(message.Duration, Time.time + message.Duration); NativeStatus.Activate(message.StatusId, message.Duration); } else { LocalStatuses.Remove(message.StatusId); NativeStatus.Hide(message.StatusId); } } } private static void Send(Player target, StatusChangeBroadcast message) { NetworkConnection owner = ((NetworkBehaviour)target).Owner; if (owner != null && owner.IsValid && Object.op_Implicit((Object)(object)InstanceFinder.ServerManager)) { InstanceFinder.ServerManager.Broadcast(owner, message, true, (Channel)0); } } private static StatusDefinition Definition(string id) { if (!Definitions.TryGetValue(id, out StatusDefinition value)) { throw new KeyNotFoundException("Status '" + id + "' is not registered."); } return value; } private static void EnsureServer() { if (!InstanceFinder.IsServerStarted) { throw new InvalidOperationException("Statuses can only be applied by the server."); } } private static StatusContext Context(string id, ActiveStatus active, float now) { return new StatusContext(id, active.Target, active.Source, active.Duration, Remaining(active.Duration, active.ExpiresAt, now)); } private static StatusSnapshot Snapshot(string id, ActiveStatus active, float now) { return new StatusSnapshot(id, ((NetworkBehaviour)active.Target).ObjectId, Object.op_Implicit((Object)(object)active.Source) ? ((NetworkBehaviour)active.Source).ObjectId : (-1), active.Duration, Remaining(active.Duration, active.ExpiresAt, now)); } private static float Remaining(float duration, float expiresAt, float now) { if (!(duration > 0f)) { return 0f; } return Mathf.Clamp(expiresAt - now, 0f, duration); } private static void WriteChange(Writer writer, StatusChangeBroadcast message) { writer.WriteString(message.StatusId); writer.WriteSingle(message.Duration); writer.WriteBoolean(message.Active); } private static StatusChangeBroadcast ReadChange(Reader reader) { return new StatusChangeBroadcast(reader.ReadStringAllocated(), reader.ReadSingle(), reader.ReadBoolean()); } } } namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ConstantExpectedAttribute : Attribute { public object? Min { get; set; } public object? Max { get; set; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ExperimentalAttribute : Attribute { public string DiagnosticId { get; } public string? UrlFormat { get; set; } public ExperimentalAttribute(string diagnosticId) { DiagnosticId = diagnosticId; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] internal sealed class MemberNotNullAttribute : Attribute { public string[] Members { get; } public MemberNotNullAttribute(string member) { Members = new string[1] { member }; } public MemberNotNullAttribute(params string[] members) { Members = members; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] internal sealed class MemberNotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public string[] Members { get; } public MemberNotNullWhenAttribute(bool returnValue, string member) { ReturnValue = returnValue; Members = new string[1] { member }; } public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { ReturnValue = returnValue; Members = members; } } [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class SetsRequiredMembersAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class StringSyntaxAttribute : Attribute { public const string CompositeFormat = "CompositeFormat"; public const string DateOnlyFormat = "DateOnlyFormat"; public const string DateTimeFormat = "DateTimeFormat"; public const string EnumFormat = "EnumFormat"; public const string GuidFormat = "GuidFormat"; public const string Json = "Json"; public const string NumericFormat = "NumericFormat"; public const string Regex = "Regex"; public const string TimeOnlyFormat = "TimeOnlyFormat"; public const string TimeSpanFormat = "TimeSpanFormat"; public const string Uri = "Uri"; public const string Xml = "Xml"; public string Syntax { get; } public object?[] Arguments { get; } public StringSyntaxAttribute(string syntax) { Syntax = syntax; Arguments = new object[0]; } public StringSyntaxAttribute(string syntax, params object?[] arguments) { Syntax = syntax; Arguments = arguments; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class UnscopedRefAttribute : Attribute { } } namespace System.Runtime.Versioning { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class RequiresPreviewFeaturesAttribute : Attribute { public string? Message { get; } public string? Url { get; set; } public RequiresPreviewFeaturesAttribute() { } public RequiresPreviewFeaturesAttribute(string? message) { Message = message; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CallerArgumentExpressionAttribute : Attribute { public string ParameterName { get; } public CallerArgumentExpressionAttribute(string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CollectionBuilderAttribute : Attribute { public Type BuilderType { get; } public string MethodName { get; } public CollectionBuilderAttribute(Type builderType, string methodName) { BuilderType = builderType; MethodName = methodName; } } [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CompilerFeatureRequiredAttribute : Attribute { public const string RefStructs = "RefStructs"; public const string RequiredMembers = "RequiredMembers"; public string FeatureName { get; } public bool IsOptional { get; set; } public CompilerFeatureRequiredAttribute(string featureName) { FeatureName = featureName; } } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute { public string[] Arguments { get; } public InterpolatedStringHandlerArgumentAttribute(string argument) { Arguments = new string[1] { argument }; } public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) { Arguments = arguments; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class InterpolatedStringHandlerAttribute : Attribute { } [EditorBrowsable(EditorBrowsableState.Never)] [ExcludeFromCodeCoverage] internal static class IsExternalInit { } [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ModuleInitializerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class OverloadResolutionPriorityAttribute : Attribute { public int Priority { get; } public OverloadResolutionPriorityAttribute(int priority) { Priority = priority; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] [ExcludeFromCodeCoverage] internal sealed class ParamCollectionAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class RequiredMemberAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [EditorBrowsable(EditorBrowsableState.Never)] [ExcludeFromCodeCoverage] internal sealed class RequiresLocationAttribute : Attribute { } [AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class SkipLocalsInitAttribute : Attribute { } }