Decompiled source of The Archive UNSTABLE TEST ONLY v0.0.7
BepInEx/plugins/TheArchive.Core/plugins/TheArchive.Core.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using AK; using Agents; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP.Hook; using BepInEx.Unity.IL2CPP.Utils; using CellMenu; using Clonesoft.Json; using Clonesoft.Json.Converters; using Clonesoft.Json.Serialization; using CullingSystem; using Enemies; using GameData; using GameEvent; using Gear; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.Runtime; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.IO; using Il2CppSystem.Reflection; using InControl; using JetBrains.Annotations; using LevelGeneration; using Localization; using Microsoft.CodeAnalysis; using Mono.Cecil; using Player; using SNetwork; using SemanticVersioning; using TMPro; using TheArchive.Core; using TheArchive.Core.Attributes; using TheArchive.Core.Attributes.Feature; using TheArchive.Core.Attributes.Feature.Members; using TheArchive.Core.Attributes.Feature.Patches; using TheArchive.Core.Attributes.Feature.Settings; using TheArchive.Core.Bootstrap; using TheArchive.Core.Definitions; using TheArchive.Core.Definitions.Data; using TheArchive.Core.Exceptions; using TheArchive.Core.FeaturesAPI; using TheArchive.Core.FeaturesAPI.Components; using TheArchive.Core.FeaturesAPI.Groups; using TheArchive.Core.FeaturesAPI.Settings; using TheArchive.Core.Interop; using TheArchive.Core.Localization; using TheArchive.Core.Localization.Data; using TheArchive.Core.Localization.Services; using TheArchive.Core.Managers; using TheArchive.Core.Models; using TheArchive.Core.ModulesAPI; using TheArchive.Core.Settings; using TheArchive.Interfaces; using TheArchive.Loader; using TheArchive.Utilities; using UnityEngine; using UnityEngine.Analytics; using UnityEngine.CrashReportHandler; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyFileVersion("0.0.838")] [assembly: AssemblyInformationalVersion("0.0.838-doing-things+a4eb55a")] [assembly: ModSettingsDisplayName("TheArchive")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("TheArchive.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyProduct("TheArchive.Core")] [assembly: AssemblyTitle("TheArchive.Core")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.838.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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; } } } internal class ThisAssembly { public class Git { public class BaseVersion { public const string Major = "0"; public const string Minor = "0"; public const string Patch = "0"; } public class SemVer { public const string Major = "0"; public const string Minor = "0"; public const string Patch = "838"; public const string Label = ""; public const string DashLabel = ""; public const string Source = "Default"; } public const bool IsDirty = false; public const string IsDirtyString = "false"; public const string RepositoryUrl = "https://github.com/Tuesday1028/GTFO_TheArchive"; public const string Branch = "doing-things"; public const string Commit = "a4eb55a"; public const string Sha = "a4eb55a7e97569a44df6831acc6074b0f084b6eb"; public const string CommitDate = "2026-05-07T19:14:52+08:00"; public const string Commits = "838"; public const string Tag = ""; public const string BaseTag = ""; } } internal class ManifestInfo { internal const string TSName = "TheArchive_Core"; internal const string TSDescription = "The main Archive mod, adds the ModSettings menu."; internal const string TSVersion = ""; internal const string TSAuthor = "AuriRex"; internal const string TSWebsite = "https://github.com/AuriRex/GTFO_TheArchive"; } namespace JetBrains.Annotations { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.GenericParameter)] internal sealed class CanBeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.GenericParameter)] internal sealed class NotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)] internal sealed class ItemNotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)] internal sealed class ItemCanBeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate)] internal sealed class StringFormatMethodAttribute : Attribute { [NotNull] public string FormatParameterName { get; } public StringFormatMethodAttribute([NotNull] string formatParameterName) { FormatParameterName = formatParameterName; } } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class StructuredMessageTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true)] internal sealed class ValueProviderAttribute : Attribute { [NotNull] public string Name { get; } public ValueProviderAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate, AllowMultiple = true)] internal sealed class ValueRangeAttribute : Attribute { public object From { get; } public object To { get; } public ValueRangeAttribute(long from, long to) { From = from; To = to; } public ValueRangeAttribute(ulong from, ulong to) { From = from; To = to; } public ValueRangeAttribute(long value) { From = (To = value); } public ValueRangeAttribute(ulong value) { From = (To = value); } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)] internal sealed class NonNegativeValueAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class InvokerParameterNameAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute { [CanBeNull] public string ParameterName { get; } public NotifyPropertyChangedInvocatorAttribute() { } public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] internal sealed class ContractAnnotationAttribute : Attribute { [NotNull] public string Contract { get; } public bool ForceFullStates { get; } public ContractAnnotationAttribute([NotNull] string contract) : this(contract, forceFullStates: false) { } public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) { Contract = contract; ForceFullStates = forceFullStates; } } [AttributeUsage(AttributeTargets.All)] internal sealed class LocalizationRequiredAttribute : Attribute { public bool Required { get; } public LocalizationRequiredAttribute() : this(required: true) { } public LocalizationRequiredAttribute(bool required) { Required = required; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter)] internal sealed class DefaultEqualityUsageAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] [BaseTypeRequired(typeof(Attribute))] internal sealed class BaseTypeRequiredAttribute : Attribute { [NotNull] public Type BaseType { get; } public BaseTypeRequiredAttribute([NotNull] Type baseType) { BaseType = baseType; } } [AttributeUsage(AttributeTargets.All)] internal sealed class UsedImplicitlyAttribute : Attribute { public ImplicitUseKindFlags UseKindFlags { get; } public ImplicitUseTargetFlags TargetFlags { get; } public string Reason { get; set; } public UsedImplicitlyAttribute() : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) : this(useKindFlags, ImplicitUseTargetFlags.Default) { } public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) : this(ImplicitUseKindFlags.Default, targetFlags) { } public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { UseKindFlags = useKindFlags; TargetFlags = targetFlags; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter | AttributeTargets.GenericParameter)] internal sealed class MeansImplicitUseAttribute : Attribute { [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } public MeansImplicitUseAttribute() : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) : this(useKindFlags, ImplicitUseTargetFlags.Default) { } public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) : this(ImplicitUseKindFlags.Default, targetFlags) { } public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { UseKindFlags = useKindFlags; TargetFlags = targetFlags; } } [Flags] internal enum ImplicitUseKindFlags { Default = 7, Access = 1, Assign = 2, InstantiatedWithFixedConstructorSignature = 4, InstantiatedNoFixedConstructorSignature = 8 } [Flags] internal enum ImplicitUseTargetFlags { Default = 1, Itself = 1, Members = 2, WithInheritors = 4, WithMembers = 3 } [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] [AttributeUsage(AttributeTargets.All, Inherited = false)] internal sealed class PublicAPIAttribute : Attribute { [CanBeNull] public string Comment { get; } public PublicAPIAttribute() { } public PublicAPIAttribute([NotNull] string comment) { Comment = comment; } } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class InstantHandleAttribute : Attribute { public bool RequireAwait { get; set; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class PureAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class MustUseReturnValueAttribute : Attribute { [CanBeNull] public string Justification { get; } public bool IsFluentBuilderMethod { get; set; } public MustUseReturnValueAttribute() { } public MustUseReturnValueAttribute([NotNull] string justification) { Justification = justification; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Parameter)] internal sealed class MustDisposeResourceAttribute : Attribute { public bool Value { get; } public MustDisposeResourceAttribute() { Value = true; } public MustDisposeResourceAttribute(bool value) { Value = value; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class HandlesResourceDisposalAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class RequireStaticDelegateAttribute : Attribute { public bool IsError { get; set; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.GenericParameter)] internal sealed class ProvidesContextAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class PathReferenceAttribute : Attribute { [CanBeNull] public string BasePath { get; } public PathReferenceAttribute() { } public PathReferenceAttribute([NotNull][PathReference] string basePath) { BasePath = basePath; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class SourceTemplateAttribute : Attribute { public SourceTemplateTargetExpression Target { get; set; } } internal enum SourceTemplateTargetExpression { Inner, Outer } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = true)] internal sealed class MacroAttribute : Attribute { [CanBeNull] public string Expression { get; set; } public int Editable { get; set; } [CanBeNull] public string Target { get; set; } } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.ReturnValue)] internal sealed class CollectionAccessAttribute : Attribute { public CollectionAccessType CollectionAccessType { get; } public CollectionAccessAttribute(CollectionAccessType collectionAccessType) { CollectionAccessType = collectionAccessType; } } [Flags] internal enum CollectionAccessType { None = 0, Read = 1, ModifyExistingContent = 2, UpdatedContent = 6 } [AttributeUsage(AttributeTargets.Method)] internal sealed class AssertionMethodAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AssertionConditionAttribute : Attribute { public AssertionConditionType ConditionType { get; } public AssertionConditionAttribute(AssertionConditionType conditionType) { ConditionType = conditionType; } } internal enum AssertionConditionType { IS_TRUE, IS_FALSE, IS_NULL, IS_NOT_NULL } [Obsolete("Use [ContractAnnotation('=> halt')] instead")] [AttributeUsage(AttributeTargets.Method)] internal sealed class TerminatesProgramAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class LinqTunnelAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class NoEnumerationAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class RegexPatternAttribute : Attribute { } internal enum InjectedLanguage { CSS, HTML, JAVASCRIPT, JSON, XML } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue)] internal sealed class LanguageInjectionAttribute : Attribute { public InjectedLanguage InjectedLanguage { get; } [CanBeNull] public string InjectedLanguageName { get; } [CanBeNull] public string Prefix { get; set; } [CanBeNull] public string Suffix { get; set; } public LanguageInjectionAttribute(InjectedLanguage injectedLanguage) { InjectedLanguage = injectedLanguage; } public LanguageInjectionAttribute([NotNull] string injectedLanguage) { InjectedLanguageName = injectedLanguage; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface, AllowMultiple = true)] internal sealed class NoReorderAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] internal sealed class CodeTemplateAttribute : Attribute { public string SearchTemplate { get; } public string Message { get; set; } public string ReplaceTemplate { get; set; } public string ReplaceMessage { get; set; } public bool FormatAfterReplace { get; set; } = true; public bool MatchSimilarConstructs { get; set; } public bool ShortenReferences { get; set; } public string SuppressionKey { get; set; } public CodeTemplateAttribute(string searchTemplate) { SearchTemplate = searchTemplate; } } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class IgnoreSpellingAndGrammarErrorsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] internal sealed class AspChildControlTypeAttribute : Attribute { [NotNull] public string TagName { get; } [NotNull] public Type ControlType { get; } public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) { TagName = tagName; ControlType = controlType; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)] internal sealed class AspDataFieldAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)] internal sealed class AspDataFieldsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] internal sealed class AspMethodPropertyAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] internal sealed class AspRequiredAttributeAttribute : Attribute { [NotNull] public string Attribute { get; } public AspRequiredAttributeAttribute([NotNull] string attribute) { Attribute = attribute; } } [AttributeUsage(AttributeTargets.Property)] internal sealed class AspTypePropertyAttribute : Attribute { public bool CreateConstructorReferences { get; } public AspTypePropertyAttribute(bool createConstructorReferences) { CreateConstructorReferences = createConstructorReferences; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcAreaViewComponentViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaViewComponentViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcMasterLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcMasterLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcViewComponentViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcViewComponentViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcActionAttribute : Attribute { [CanBeNull] public string AnonymousProperty { get; } public AspMvcActionAttribute() { } public AspMvcActionAttribute([NotNull] string anonymousProperty) { AnonymousProperty = anonymousProperty; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcAreaAttribute : Attribute { [CanBeNull] public string AnonymousProperty { get; } public AspMvcAreaAttribute() { } public AspMvcAreaAttribute([NotNull] string anonymousProperty) { AnonymousProperty = anonymousProperty; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcControllerAttribute : Attribute { [CanBeNull] public string AnonymousProperty { get; } public AspMvcControllerAttribute() { } public AspMvcControllerAttribute([NotNull] string anonymousProperty) { AnonymousProperty = anonymousProperty; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcMasterAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AspMvcModelTypeAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcPartialViewAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] internal sealed class AspMvcSuppressViewErrorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcDisplayTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcEditorTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcViewAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcViewComponentAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcViewComponentViewAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] internal sealed class AspMvcActionSelectorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class RouteTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] internal sealed class RouteParameterConstraintAttribute : Attribute { [NotNull] public string ConstraintName { get; } [CanBeNull] public Type ProposedType { get; set; } public RouteParameterConstraintAttribute([NotNull] string constraintName) { ConstraintName = constraintName; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class UriStringAttribute : Attribute { [CanBeNull] public string HttpVerb { get; } public UriStringAttribute() { } public UriStringAttribute(string httpVerb) { HttpVerb = httpVerb; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class AspRouteConventionAttribute : Attribute { [CanBeNull] public string PredefinedPattern { get; } public AspRouteConventionAttribute() { } public AspRouteConventionAttribute(string predefinedPattern) { PredefinedPattern = predefinedPattern; } } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AspDefaultRouteValuesAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AspRouteValuesConstraintsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] internal sealed class AspRouteOrderAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] internal sealed class AspRouteVerbsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] internal sealed class AspAttributeRoutingAttribute : Attribute { public string HttpVerb { get; set; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class AspMinimalApiDeclarationAttribute : Attribute { public string HttpVerb { get; set; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class AspMinimalApiGroupAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AspMinimalApiHandlerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] internal sealed class AspMinimalApiImplicitEndpointDeclarationAttribute : Attribute { public string HttpVerb { get; set; } public string RouteTemplate { get; set; } public Type BodyType { get; set; } public string QueryParameters { get; set; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class HtmlElementAttributesAttribute : Attribute { [CanBeNull] public string Name { get; } public HtmlElementAttributesAttribute() { } public HtmlElementAttributesAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class HtmlAttributeValueAttribute : Attribute { [NotNull] public string Name { get; } public HtmlAttributeValueAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)] internal sealed class RazorSectionAttribute : Attribute { } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorImportNamespaceAttribute : Attribute { [NotNull] public string Name { get; } public RazorImportNamespaceAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorInjectionAttribute : Attribute { [NotNull] public string Type { get; } [NotNull] public string FieldName { get; } public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) { Type = type; FieldName = fieldName; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorDirectiveAttribute : Attribute { [NotNull] public string Directive { get; } public RazorDirectiveAttribute([NotNull] string directive) { Directive = directive; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorPageBaseTypeAttribute : Attribute { [NotNull] public string BaseType { get; } [CanBeNull] public string PageName { get; } public RazorPageBaseTypeAttribute([NotNull] string baseType) { BaseType = baseType; } public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) { BaseType = baseType; PageName = pageName; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class RazorHelperCommonAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] internal sealed class RazorLayoutAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class RazorWriteLiteralMethodAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class RazorWriteMethodAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class RazorWriteMethodParameterAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] internal sealed class XamlItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] internal sealed class XamlItemStyleOfItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] internal sealed class XamlOneWayBindingModeByDefaultAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] internal sealed class XamlTwoWayBindingModeByDefaultAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Interface, AllowMultiple = true)] internal sealed class TestSubjectAttribute : Attribute { [NotNull] public Type Subject { get; } public TestSubjectAttribute([NotNull] Type subject) { Subject = subject; } } [AttributeUsage(AttributeTargets.GenericParameter)] internal sealed class MeansTestSubjectAttribute : Attribute { } } namespace TheArchive { [HideInModSettings] [DisallowInGameToggle] [DoNotSaveToConfig] [EnableFeatureByDefault] internal class ArchiveBootstrap : Feature { [ArchivePatch(typeof(GameDataInit), "Initialize", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class GameDataInit__Initialize__Patch { public static void Postfix() { try { InvokeGameDataInitialized(); } catch (ReflectionTypeLoadException ex) { ArchiveLogger.Error("Exception thrown in ArchiveBootstrap"); ArchiveLogger.Msg(ConsoleColor.Green, "Oh no, seems like someone's referencing game types from an older/newer game version that do not exist anymore! :c"); ArchiveLogger.Exception(ex); ArchiveLogger.Warning($"{ex.Types.Length} Types loaded."); ArchiveLogger.Notice("Exceptions:"); Exception[] loaderExceptions = ex.LoaderExceptions; foreach (Exception ex2 in loaderExceptions) { if (ex2 != null) { ArchiveLogger.Error(ex2.Message); } } } catch (Exception ex3) { ArchiveLogger.Error("Exception thrown in ArchiveBootstrap"); ArchiveLogger.Exception(ex3); if (ex3.InnerException != null) { ArchiveLogger.Exception(ex3?.InnerException); } } } } [RundownConstraint(Utils.RundownFlags.RundownSix, Utils.RundownFlags.Latest)] [ArchivePatch("Setup", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class LocalizationManager__Setup__Patch { public static Type Type() { return typeof(LocalizationManager); } public static void Postfix() { InvokeDataBlocksReady(); } } [ArchivePatch(typeof(GameStateManager), "ChangeState", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class GameStateManager__ChangeState__Patch { public static void Postfix(eGameStateName nextState) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected I4, but got Unknown ArchiveMod.InvokeGameStateChanged((int)nextState); } } [RundownConstraint(Utils.RundownFlags.RundownAltOne, Utils.RundownFlags.Latest)] [ArchivePatch("OnBtnPress", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class CM_RundownSelection__OnBtnPress__Patch { public static Type Type() { return typeof(CM_RundownSelection); } public static void Postfix(CM_RundownSelection __instance) { ArchiveMod.CurrentlySelectedRundownKey = __instance.RundownKey; } } [RundownConstraint(Utils.RundownFlags.RundownAltOne, Utils.RundownFlags.Latest)] [ArchivePatch(typeof(CM_PageRundown_New), "Setup", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class CM_PageRundown_New__Setup__Patch { public static void Postfix(CM_PageRundown_New __instance) { __instance.m_selectRundownButton.AddCMItemEvents(delegate { ArchiveMod.CurrentlySelectedRundownKey = string.Empty; }); } } [ArchivePatch(typeof(InControlManager), "OnApplicationFocus", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class EventSystem__OnApplicationFocus__Patch { public static void Postfix(bool focusState) { ArchiveMod.InvokeApplicationFocusChanged(focusState); } } public override string Name => "ArchiveBootstrap"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Hooks into a bunch of important game code in order for this mod to work."; public override bool RequiresRestart => true; private static void InvokeGameDataInitialized() { ArchiveMod.InvokeGameDataInitialized(); if (ArchiveMod.CurrentRundown.IsIncludedIn(Utils.RundownFlags.RundownFour | Utils.RundownFlags.RundownFive)) { InvokeDataBlocksReady(); } } private static void InvokeDataBlocksReady() { if (SharedUtils.TryGetRundownDataBlock(out var block)) { ArchiveMod.CurrentlySelectedRundownKey = $"Local_{((GameDataBlockBase<RundownDataBlock>)(object)block).persistentID}"; } ArchiveMod.InvokeDataBlocksReady(); } } public static class ArchiveMod { public const string GUID = "dev.AuriRex.gtfo.TheArchive"; public const string MOD_NAME = "TheArchive"; public const string ABBREVIATION = "Ar"; public const string AUTHOR = "AuriRex"; public const string VERSION_STRING = "0.0.838"; public const string GITHUB_REPOSITORY_NAME = "GTFO_TheArchive"; public const string GITHUB_OWNER_NAME = "AuriRex"; public const string GITHUB_LINK = "https://github.com/AuriRex/GTFO_TheArchive"; public static readonly bool GIT_IS_DIRTY = false; public const string GIT_COMMIT_SHORT_HASH = "a4eb55a"; public const string GIT_COMMIT_DATE = "2026-05-07T19:14:52+08:00"; public const string GIT_BASE_TAG = ""; public const uint GTFO_STEAM_APPID = 493520u; public const string MTFO_GUID = "com.dak.MTFO"; public static readonly string CORE_PATH = Assembly.GetAssembly(typeof(ArchiveMod)).Location; private static JsonSerializerSettings _jsonSerializerSettings = null; private static string _currentlySelectedRundownKey = string.Empty; private static IArchiveModule _mainModule; private static readonly HashSet<Type> _typesToInitOnDataBlocksReady = new HashSet<Type>(); private static readonly HashSet<Type> _typesToInitOnGameDataInit = new HashSet<Type>(); private static readonly HashSet<Assembly> _moduleAssemblies = new HashSet<Assembly>(); private static readonly List<Type> _moduleTypes = new List<Type>(); private const string ARCHIVE_SETTINGS_FILE = "TheArchive_Settings.json"; private static Harmony _harmonyInstance; internal static ArchiveSettings Settings { get; private set; } = new ArchiveSettings(); internal static JsonSerializerSettings JsonSerializerSettings { get { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown if (_jsonSerializerSettings != null) { return _jsonSerializerSettings; } _jsonSerializerSettings = new JsonSerializerSettings { Formatting = (Formatting)1 }; _jsonSerializerSettings.Converters.Add((JsonConverter)new StringEnumConverter()); _jsonSerializerSettings.ContractResolver = (IContractResolver)(object)ArchiveContractResolver.Instance; return _jsonSerializerSettings; } } public static bool IsPlayingModded { get; private set; } = false; public static Utils.RundownID CurrentRundown { get; private set; } = Utils.RundownID.RundownUnitialized; public static GameBuildInfo CurrentBuildInfo { get; private set; } public static int CurrentGameState { get; private set; } public static bool IsOnALTBuild { get; private set; } private static bool IsInitialized { get; set; } public static string CurrentlySelectedRundownKey { get { return _currentlySelectedRundownKey; } internal set { _currentlySelectedRundownKey = value; ArchiveLogger.Debug($"Setting {"CurrentlySelectedRundownKey"} to \"{_currentlySelectedRundownKey}\"."); if (string.IsNullOrEmpty(_currentlySelectedRundownKey)) { CurrentlySelectedRundownPersistentID = 0u; return; } try { CurrentlySelectedRundownPersistentID = uint.Parse(_currentlySelectedRundownKey.Replace("Local_", "")); } catch (Exception ex) { ArchiveLogger.Error($"Failed to parse selected rundown persistentId from {"CurrentlySelectedRundownKey"} \"{CurrentlySelectedRundownKey}\"!"); ArchiveLogger.Exception(ex); } } } public static uint CurrentlySelectedRundownPersistentID { get; set; } = 0u; public static HashSet<IArchiveModule> Modules { get; } = new HashSet<IArchiveModule>(); public static Type IL2CPP_BaseType { get; private set; } = null; public static event Action<Utils.RundownID> GameDataInitialized; public static event Action DataBlocksReady; public static event Action<int> GameStateChanged; public static event Action<bool> ApplicationFocusStateChanged; internal static event Action<IArchiveModule> OnNewModuleRegistered; internal static void OnApplicationStart(IArchiveLogger logger, Harmony harmonyInstance) { ArchiveLogger.Logger = logger; _harmonyInstance = harmonyInstance; if (GIT_IS_DIRTY) { ArchiveLogger.Warning("Git is dirty, this is a development build!"); } if (LoaderWrapper.IsGameIL2CPP()) { IL2CPP_BaseType = ImplementationManager.FindTypeInCurrentAppDomain("Il2CppSystem.Object", exactMatch: true); ArchiveLogger.Debug("IL2CPP_BaseType: " + IL2CPP_BaseType?.FullName); if (IL2CPP_BaseType == null) { ArchiveLogger.Error("IL2CPP base type \"Il2CppSystem.Object\" could not be resolved!"); } } LoadConfig(); if (LoaderWrapper.IsModInstalled("com.dak.MTFO")) { IsPlayingModded = true; } GTFOLogger.Logger = LoaderWrapper.CreateLoggerInstance("GTFO-Internals", ConsoleColor.DarkGray); CurrentRundown = BuildDB.GetCurrentRundownID(BuildDB.BuildNumber); ArchiveLogger.Msg(ConsoleColor.DarkMagenta, $"Current game revision determined to be {BuildDB.BuildNumber}! ({CurrentRundown})"); GameBuildInfo currentBuildInfo = default(GameBuildInfo); currentBuildInfo.BuildNumber = BuildDB.BuildNumber; currentBuildInfo.Rundown = CurrentRundown; CurrentBuildInfo = currentBuildInfo; IsOnALTBuild = CurrentRundown.IsIncludedIn(Utils.RundownFlags.RundownAltOne.ToLatest()); string path = Path.Combine(LoaderWrapper.GameDirectory, "steam_appid.txt"); if (!File.Exists(path)) { ArchiveLogger.Notice("Creating \"steam_appid.txt\" in GTFO folder ..."); File.WriteAllText(path, $"{493520}"); } AddInternalAttribution(); FeatureManager.Internal_Init(); try { _mainModule = CreateAndInitModule(typeof(MainArchiveModule)); } catch (ReflectionTypeLoadException ex) { ArchiveLogger.Error("Failed loading main module!!"); ArchiveLogger.Exception(ex); ArchiveLogger.Notice($"Loader Exceptions ({ex.LoaderExceptions.Length}):"); Exception[] loaderExceptions = ex.LoaderExceptions; foreach (Exception ex2 in loaderExceptions) { if (ex2 != null) { ArchiveLogger.Warning(ex2.Message); ArchiveLogger.Debug(ex2.StackTrace); } } ArchiveLogger.Info("-------------"); } InitializeArchiveModuleChainloader(); } internal static void OnApplicationQuit() { InitSingletonBase<FeatureManager>.Instance.OnApplicationQuit(); CustomSettingManager.OnApplicationQuit(); } private static void LoadConfig() { LoadConfig(Path.Combine(LocalFiles.ModLocalLowPath, "TheArchive_Settings.json")); } private static void LoadConfig(string path) { try { ArchiveLogger.Info("Loading config file ... [" + path + "]"); if (File.Exists(path)) { Settings = JsonConvert.DeserializeObject<ArchiveSettings>(File.ReadAllText(path), JsonSerializerSettings); } SaveConfig(path); } catch (Exception ex) { ArchiveLogger.Exception(ex); } } private static void SaveConfig(string path) { ArchiveLogger.Debug("Saving config file ... [" + path + "]"); File.WriteAllText(path, JsonConvert.SerializeObject((object)Settings, JsonSerializerSettings)); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static bool RegisterArchiveModule(Assembly asm) { return RegisterArchiveModule(asm.GetTypes().FirstOrDefault((Type t) => typeof(IArchiveModule).IsAssignableFrom(t))); } public static bool RegisterArchiveModule(Type moduleType) { if (moduleType == null) { throw new ArgumentException("Module can't be null!"); } if (_moduleTypes.Contains(moduleType)) { throw new ArgumentException("Module \"" + moduleType.Name + "\" is already registered!"); } if (!typeof(IArchiveModule).IsAssignableFrom(moduleType)) { throw new ArgumentException($"Type \"{moduleType.Name}\" does not implement {"IArchiveModule"}!"); } IArchiveModule archiveModule = CreateAndInitModule(moduleType); Utils.SafeInvoke(ArchiveMod.OnNewModuleRegistered, archiveModule); if (CurrentRundown != Utils.RundownID.RundownUnitialized) { return true; } return false; } private static void InitializeArchiveModuleChainloader() { ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Finished += ArchiveModuleChainloader.Initialize; } internal static void InvokeGameDataInitialized() { if (IsInitialized) { ArchiveLogger.Info("Reload triggered, skipping init."); return; } IsInitialized = true; ArchiveLogger.Info("GameData has been initialized, invoking event."); foreach (Type item in _typesToInitOnGameDataInit) { try { InitInitializables(item, out var _); } catch (Exception ex) { ArchiveLogger.Error("Trying to Init \"" + item.FullName + "\" threw an exception:"); ArchiveLogger.Exception(ex); } } InitSingletonBase<FeatureManager>.Instance.OnGameDataInitialized(); Utils.SafeInvoke(ArchiveMod.GameDataInitialized, CurrentRundown); } internal static void InvokeDataBlocksReady() { try { DataBlockManager.Setup(); } catch (Exception ex) { ArchiveLogger.Exception(ex); } ArchiveLogger.Info("DataBlocks should be ready to be interacted with, invoking event."); foreach (Type item in _typesToInitOnDataBlocksReady) { try { InitInitializables(item, out var _); } catch (Exception ex2) { ArchiveLogger.Error("Trying to Init \"" + item.FullName + "\" threw an exception:"); ArchiveLogger.Exception(ex2); } } InitSingletonBase<FeatureManager>.Instance.OnDatablocksReady(); CustomSettingManager.OnGameDataInited(); Interop.OnDataBlocksReady(); Utils.SafeInvoke(ArchiveMod.DataBlocksReady); } internal static void InvokeGameStateChanged(int eGameState_state) { CurrentGameState = eGameState_state; Utils.SafeInvoke(ArchiveMod.GameStateChanged, eGameState_state); } internal static void InvokeApplicationFocusChanged(bool focus) { Utils.SafeInvoke(ArchiveMod.ApplicationFocusStateChanged, focus); } private static void InitInitializables(Type type, out IInitializable initializable) { ArchiveLogger.Debug("Creating instance of: \"" + type.FullName + "\"."); IInitializable initializable2 = (initializable = (IInitializable)Activator.CreateInstance(type)); bool flag = true; Type type2 = typeof(InitSingletonBase<>).MakeGenericType(type); bool flag2 = type2.IsAssignableFrom(type); if (flag2) { type2.GetProperty("Instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SetValue(null, initializable2); } if (typeof(IInitCondition).IsAssignableFrom(type)) { IInitCondition initCondition = (IInitCondition)initializable2; try { flag = initCondition.InitCondition(); } catch (Exception ex) { ArchiveLogger.Error("InitCondition method on Type \"" + type.FullName + "\" failed!"); ArchiveLogger.Warning("This IInitializable won't be initialized."); ArchiveLogger.Exception(ex); flag = false; } } if (!flag) { return; } try { initializable2.Init(); if (flag2) { type2.GetProperty("HasBeenInitialized", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SetValue(null, true); } } catch (Exception ex2) { ArchiveLogger.Error("Init method on Type \"" + type.FullName + "\" failed!"); ArchiveLogger.Exception(ex2); } } private static void InspectType(Type type, IArchiveModule module) { if (typeof(Feature).IsAssignableFrom(type) && type != typeof(Feature)) { InitSingletonBase<FeatureManager>.Instance.InitFeature(type, module); return; } if (typeof(IInitImmediately).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) { try { InitInitializables(type, out var _); return; } catch (Exception ex) { ArchiveLogger.Error("Trying to Init \"" + type.FullName + "\" (immediately) threw an exception:"); ArchiveLogger.Exception(ex); return; } } if (typeof(IInitAfterGameDataInitialized).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) { _typesToInitOnGameDataInit.Add(type); } else if (typeof(IInitAfterDataBlocksReady).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) { _typesToInitOnDataBlocksReady.Add(type); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] internal static IArchiveModule CreateAndInitModule(Type moduleType) { if (moduleType == null) { throw new ArgumentException("Parameter moduleType can not be null!"); } Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); _moduleTypes.Add(moduleType); ArchiveLogger.Info("Initializing module \"" + moduleType.FullName + "\" ..."); IArchiveModule archiveModule = (IArchiveModule)Activator.CreateInstance(moduleType); DefinitionManager.LoadModuleDefinitions(archiveModule); IArchiveLogger archiveLogger2 = (archiveModule.Logger = LoaderWrapper.CreateLoggerInstance(moduleType.Assembly.GetName().Name, ConsoleColor.DarkMagenta)); IArchiveLogger logger = archiveLogger2; ModuleLocalizationService moduleLocalizationService = (ModuleLocalizationService)(archiveModule.LocalizationService = new ModuleLocalizationService(archiveModule, moduleType, logger)); try { moduleLocalizationService.Setup(); } catch (Exception ex) { ArchiveLogger.Error("Error while trying to setup module localization for \"" + moduleType.FullName + "\"!"); ArchiveLogger.Exception(ex); } try { archiveModule.Init(); } catch (Exception ex2) { ArchiveLogger.Error("Error while trying to init \"" + moduleType.FullName + "\"!"); ArchiveLogger.Exception(ex2); } Type[] types = moduleType.Assembly.GetTypes(); for (int i = 0; i < types.Length; i++) { InspectType(types[i], archiveModule); } _moduleAssemblies.Add(moduleType.Assembly); Modules.Add(archiveModule); stopwatch.Stop(); ArchiveLogger.Debug($"Creation of \"{moduleType.FullName}\" took {stopwatch.Elapsed:ss\\.fff} seconds."); return archiveModule; } internal static void OnUpdate() { InitSingletonBase<FeatureManager>.Instance.OnUpdate(); } internal static void OnLateUpdate() { InitSingletonBase<FeatureManager>.Instance.OnLateUpdate(); } private static void AddInternalAttribution() { try { string @string = Encoding.UTF8.GetString(Utils.LoadFromResource("TheArchive.Resources.LICENSE")); Attribution.Add(new Attribution.AttributionInfo("TheArchive License", @string ?? "") { Origin = "TheArchive.Core", Comment = "<color=orange><b>Huge thanks to everyone that has contributed!</b> - Check out the repo on GitHub!</color>" }); string string2 = Encoding.UTF8.GetString(Utils.LoadFromResource("TheArchive.Resources.LICENSE_BepInEx")); Attribution.Add(new Attribution.AttributionInfo("BepInEx Info + License", ("This project contains parts of BepInEx code, denoted in source files.\n\nLICENSE (Truncated, see repository):\n\n" + string2).Substring(0, 619) + "\n\n[...]") { Origin = "TheArchive.Core" }); string content = "<color=orange>Material Symbols</color> used in ThunderStore mod icons licensed under <color=orange>Apache License Version 2.0</color>\n\n> https://github.com/google/material-design-icons\n> https://www.apache.org/licenses/LICENSE-2.0.txt"; Attribution.Add(new Attribution.AttributionInfo("Mod Icon(s) Info + License", content) { Origin = "TheArchive.Core" }); string string3 = Encoding.UTF8.GetString(Utils.LoadFromResource("TheArchive.Resources.LICENSE_JBA")); Attribution.Add(new Attribution.AttributionInfo("JetBrains.Annotations License", string3 ?? "") { Origin = "TheArchive.Core" }); } catch (Exception ex) { ArchiveLogger.Error("Error while trying to add internal AttributionInfos"); ArchiveLogger.Exception(ex); } } } [ArchiveModule("dev.AuriRex.gtfo.TheArchive", "TheArchive", "0.0.838")] internal class MainArchiveModule : IArchiveModule { public ILocalizationService LocalizationService { get; set; } public IArchiveLogger Logger { get; set; } static MainArchiveModule() { ImplementationManagerExtensions.RegisterSelf(typeof(EnemyDataBlock)); ImplementationManagerExtensions.RegisterSelf(typeof(GameDataBlockBase<>)); ImplementationManagerExtensions.RegisterSelf(typeof(GameDataBlockWrapper<>)); ImplementationManagerExtensions.RegisterSelf(typeof(eGameStateName)); ImplementationManagerExtensions.RegisterSelf(typeof(LG_Area)); typeof(List<>).RegisterForIdentifier("GenericList"); } public void Init() { ArchiveLocalizationService.Setup(LocalizationService); CrashReportHandler.SetUserMetadata("Modded", "true"); CrashReportHandler.enableCaptureExceptions = false; } } } namespace TheArchive.Utilities { public static class AccessorExtensions { public static IValueAccessor<T, MT> OrAlternative<T, MT>(this IValueAccessor<T, MT> self, Func<IValueAccessor<T, MT>> func) { if (self != null && self.HasMember) { return self; } if (func == null) { throw new NullReferenceException("Parameter func may not be null!"); } return func(); } } public interface IValueAccessor<T, MT> { bool CanGet { get; } bool CanSet { get; } bool HasMember { get; } MT Get(T instance); void Set(T instance, MT value); } public interface IStaticValueAccessor<T, MT> : IValueAccessor<T, MT> { bool IsStatic { get; } MT GetStaticValue(); void SetStaticValue(MT value); } public abstract class AccessorBase { protected static readonly Dictionary<string, AccessorBase> Accessors = new Dictionary<string, AccessorBase>(); protected static object[] NoParams { get; } = Array.Empty<object>(); protected static BindingFlags AnyBindingFlags => BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; public string Identifier { get; private set; } public bool IgnoreErrors { get; private set; } public abstract bool HasMemberBeenFound { get; } protected AccessorBase(string identifier, bool ignoreErrors) { Identifier = identifier; IgnoreErrors = ignoreErrors; } public static IValueAccessor<T, MT> GetValueAccessor<T, MT>(string memberName, bool throwOnError = false) { if (LoaderWrapper.IsGameIL2CPP() && LoaderWrapper.IsIL2CPPType(typeof(T))) { return PropertyAccessor<T, MT>.GetAccessor(memberName, !throwOnError); } MemberInfo memberInfo = typeof(T).GetMember(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(); if (!(memberInfo is PropertyInfo)) { if (memberInfo is FieldInfo) { return FieldAccessor<T, MT>.GetAccessor(memberName, !throwOnError); } if (throwOnError) { throw new ArgumentException($"Member with name \"{memberName}\" could not be found in type \"{typeof(T).Name}\" or isn't {"IValueAccessor"} compatible.", "memberName"); } return null; } return PropertyAccessor<T, MT>.GetAccessor(memberName, !throwOnError); } public static IStaticValueAccessor<T, MT> GetStaticValueAccessor<T, MT>(string memberName, bool throwOnError = false) { IValueAccessor<T, MT> valueAccessor = GetValueAccessor<T, MT>(memberName, throwOnError); if (valueAccessor != null) { IStaticValueAccessor<T, MT> staticValueAccessor = valueAccessor as IStaticValueAccessor<T, MT>; if (throwOnError && !staticValueAccessor.IsStatic) { throw new ArgumentException("Member with name \"" + memberName + "\" is not static!", "memberName"); } return staticValueAccessor; } return null; } } public class FieldAccessor<T, FT> : AccessorBase, IValueAccessor<T, FT>, IStaticValueAccessor<T, FT> { private readonly FieldInfo _field; public override bool HasMemberBeenFound => _field != null; public bool CanGet => true; public bool CanSet => true; public bool HasMember => HasMemberBeenFound; public bool IsStatic => _field?.IsStatic ?? false; public static FieldAccessor<T, FT> GetAccessor(string fieldName, bool ignoreErrors = false) { string text = "Field_" + typeof(T).FullName + "_" + fieldName; if (AccessorBase.Accessors.TryGetValue(text, out var value)) { return (FieldAccessor<T, FT>)value; } value = new FieldAccessor<T, FT>(text, fieldName, ignoreErrors); AccessorBase.Accessors.Add(text, value); return (FieldAccessor<T, FT>)value; } private FieldAccessor(string identifier, string fieldName, bool ignoreErrors = false) : base(identifier, ignoreErrors) { _field = typeof(T).GetField(fieldName, AccessorBase.AnyBindingFlags); } public FT Get(T instance) { try { return (FT)_field.GetValue(instance); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (!HasMemberBeenFound) { ArchiveLogger.Warning($"NullReferenceException while getting {"FieldAccessor"} field \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); return default(FT); } ArchiveLogger.Error($"NullReferenceException while getting {"FieldAccessor"} field \"{base.Identifier}\"!"); throw; } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while getting {"FieldAccessor"} field \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } return default(FT); } public void Set(T instance, FT value) { try { _field.SetValue(instance, value); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (HasMemberBeenFound) { ArchiveLogger.Error($"NullReferenceException while setting {"FieldAccessor"} field \"{base.Identifier}\"!"); throw; } ArchiveLogger.Warning($"NullReferenceException while setting {"FieldAccessor"} field \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while setting {"FieldAccessor"} field \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } } public FT GetStaticValue() { return Get(default(T)); } public void SetStaticValue(FT value) { Set(default(T), value); } } public class PropertyAccessor<T, PT> : AccessorBase, IValueAccessor<T, PT>, IStaticValueAccessor<T, PT> { private readonly PropertyInfo _property; public override bool HasMemberBeenFound => _property != null; public bool CanGet => _property?.GetGetMethod(nonPublic: true) != null; public bool CanSet => _property?.GetSetMethod(nonPublic: true) != null; public bool HasMember => HasMemberBeenFound; public bool IsStatic => (_property?.GetGetMethod(nonPublic: true) ?? _property?.GetSetMethod(nonPublic: true))?.IsStatic ?? false; public static PropertyAccessor<T, PT> GetAccessor(string propertyName, bool ignoreErrors = false) { string text = "Property_" + typeof(T).FullName + "_" + propertyName; if (AccessorBase.Accessors.TryGetValue(text, out var value)) { return (PropertyAccessor<T, PT>)value; } value = new PropertyAccessor<T, PT>(text, propertyName, ignoreErrors); AccessorBase.Accessors.Add(text, value); return (PropertyAccessor<T, PT>)value; } private PropertyAccessor(string identifier, string propertyName, bool ignoreErrors = false) : base(identifier, ignoreErrors) { _property = typeof(T).GetProperty(propertyName, AccessorBase.AnyBindingFlags); } public PT Get(T instance) { try { return (PT)_property.GetValue(instance); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (!HasMemberBeenFound) { ArchiveLogger.Warning($"NullReferenceException while getting {"PropertyAccessor"} property \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); return default(PT); } ArchiveLogger.Error($"NullReferenceException while getting {"PropertyAccessor"} property \"{base.Identifier}\"!"); throw; } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while getting {"PropertyAccessor"} property \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } return default(PT); } public void Set(T instance, PT value) { try { _property.SetValue(instance, value); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (HasMemberBeenFound) { ArchiveLogger.Error($"NullReferenceException while setting {"PropertyAccessor"} property \"{base.Identifier}\"!"); throw; } ArchiveLogger.Warning($"NullReferenceException while setting {"PropertyAccessor"} property \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while setting {"PropertyAccessor"} property \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } } public PT GetStaticValue() { return Get(default(T)); } public void SetStaticValue(PT value) { Set(default(T), value); } } public class MethodAccessor<T, RT> : AccessorBase { private readonly MethodInfo _method; public bool IsMethodStatic => _method.IsStatic; public override bool HasMemberBeenFound => _method != null; public static MethodAccessor<T, RT> GetAccessor(string methodName, Type[] parameterTypes = null, bool ignoreErrors = false) { string text = $"Method_{typeof(T).FullName}_{typeof(RT)}_{methodName}"; if (parameterTypes != null) { text = text + "_" + string.Join("_", parameterTypes.Select((Type pt) => pt.Name)); } if (AccessorBase.Accessors.TryGetValue(text, out var value)) { return (MethodAccessor<T, RT>)value; } value = new MethodAccessor<T, RT>(text, methodName, parameterTypes, ignoreErrors); AccessorBase.Accessors.Add(text, value); return (MethodAccessor<T, RT>)value; } private MethodAccessor(string identifier, string methodName, Type[] parameterTypes, bool ignoreErrors = false) : base(identifier, ignoreErrors) { try { if (parameterTypes == null) { _method = typeof(T).GetMethod(methodName, AccessorBase.AnyBindingFlags); } else { _method = typeof(T).GetMethod(methodName, AccessorBase.AnyBindingFlags, null, parameterTypes, null); } } catch (Exception ex) { ArchiveLogger.Error($"Method \"{methodName}\" in Type {typeof(T).FullName} could not be resolved on {ex.Source}!"); ArchiveLogger.Exception(ex); } } public RT Invoke(T instance, params object[] parameters) { try { object obj = _method.Invoke(instance, parameters ?? AccessorBase.NoParams); if (obj == null) { return default(RT); } return (RT)obj; } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (!HasMemberBeenFound) { ArchiveLogger.Warning($"NullReferenceException while calling {"MethodAccessor"} method \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); return default(RT); } ArchiveLogger.Error($"NullReferenceException while calling {"MethodAccessor"} method \"{base.Identifier}\"!"); throw; } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while calling {"MethodAccessor"} method \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } return default(RT); } public RT Invoke(T instance) { return Invoke(instance, null); } } public class MethodAccessor<T> : AccessorBase { private readonly MethodInfo _method; public bool IsMethodStatic => _method.IsStatic; public override bool HasMemberBeenFound => _method != null; public int ParameterCount { get; private set; } public static MethodAccessor<T> GetAccessor(string methodName, Type[] parameterTypes = null, bool ignoreErrors = false) { string text = "Method_" + typeof(T).FullName + "_void_" + methodName; if (parameterTypes != null && parameterTypes != Array.Empty<Type>()) { text = text + "_" + string.Join("_", parameterTypes.Select((Type pt) => pt.Name)); } if (AccessorBase.Accessors.TryGetValue(text, out var value)) { return (MethodAccessor<T>)value; } value = new MethodAccessor<T>(text, methodName, parameterTypes, ignoreErrors); AccessorBase.Accessors.Add(text, value); return (MethodAccessor<T>)value; } private MethodAccessor(string identifier, string methodName, Type[] parameterTypes, bool ignoreErrors = false) : base(identifier, ignoreErrors) { try { if (parameterTypes == null) { _method = typeof(T).GetMethod(methodName, AccessorBase.AnyBindingFlags); } else { _method = typeof(T).GetMethod(methodName, AccessorBase.AnyBindingFlags, null, parameterTypes, null); } if (!ignoreErrors && _method == null) { throw new Exception("Method not found!"); } if (_method != null) { ParameterCount = _method.GetParameters().Length; } } catch (Exception ex) { if (parameterTypes == null) { parameterTypes = Array.Empty<Type>(); } ArchiveLogger.Error($"Method \"{methodName}\" in Type {typeof(T).FullName} could not be resolved on {ex.Source}!"); ArchiveLogger.Exception(ex); ArchiveLogger.Debug($"Constructor debug data:\nidentifier:{identifier}\nmethodName:{methodName}\nparameterTypes:{string.Join(", ", parameterTypes.Select((Type p) => p.FullName))}"); StackFrame frame = new StackTrace().GetFrame(2); ArchiveLogger.Debug($"FileName:{frame.GetFileName()} {frame.GetFileLineNumber()}\nMethod:{frame.GetMethod()?.DeclaringType?.FullName ?? "Unknown"}:{frame.GetMethod()?.Name ?? "Unknown"}"); PrintDebug(); } } private void PrintDebug() { if (!(_method == null)) { ArchiveLogger.Debug($"Method debug data:\nName:{_method.Name}\nDeclaringType:{_method.DeclaringType?.FullName ?? "Unknown"}\nReturnType:{_method.ReturnType}\nParameter Count:{_method.GetParameters().Length}\nParameters:{string.Join(", ", from p in _method.GetParameters() select p.ParameterType.FullName)}"); } } public void Invoke(T instance, params object[] parameters) { try { if (parameters != null && parameters.Length > ParameterCount) { parameters = parameters.Take(ParameterCount).ToArray(); } _method.Invoke(instance, parameters ?? AccessorBase.NoParams); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (HasMemberBeenFound) { ArchiveLogger.Error($"NullReferenceException while calling {"MethodAccessor"} method \"{base.Identifier}\"!"); throw; } ArchiveLogger.Warning($"NullReferenceException while calling {"MethodAccessor"} method \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while calling {"MethodAccessor"} method \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); PrintDebug(); } } public void Invoke(T instance) { Invoke(instance, null); } } internal static class ArchiveLogger { internal static IArchiveLogger Logger; public static void Success(string msg) { Logger.Msg(ConsoleColor.Green, msg); } public static void Notice(string msg) { Logger.Msg(ConsoleColor.Cyan, msg); } public static void Msg(ConsoleColor col, string msg) { Logger.Msg(col, msg); } public static void Debug(string msg) { Logger.Msg(ConsoleColor.DarkGray, msg); } public static void Info(string msg) { Logger.Info(msg); } public static void Warning(string msg) { Logger.Msg(ConsoleColor.DarkYellow, msg); } public static void Error(string msg) { Logger.Error(msg); } public static void Msg(string v) { Info(v); } public static void Exception(Exception ex) { Error($"{ex}: {ex.Message}\n{ex.StackTrace}"); } } public static class EnumExtensions { [CompilerGenerated] private sealed class <GetFlags>d__2<T> : IEnumerable<T>, IEnumerable, IEnumerator<T>, IEnumerator, IDisposable where T : struct, Enum { private int <>1__state; private T <>2__current; private int <>l__initialThreadId; private T value; public T <>3__value; private ulong <enumValue>5__2; private IEnumerator <>7__wrap2; T IEnumerator<T>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetFlags>d__2(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap2 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <enumValue>5__2 = Convert.ToUInt64(value); <>7__wrap2 = Enum.GetValues(typeof(T)).GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } while (<>7__wrap2.MoveNext()) { T val = (T)<>7__wrap2.Current; ulong num = Convert.ToUInt64(val); if (num != 0L && (<enumValue>5__2 & num) == num) { <>2__current = val; <>1__state = 1; return true; } } <>m__Finally1(); <>7__wrap2 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap2 is IDisposable disposable) { disposable.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<T> IEnumerable<T>.GetEnumerator() { <GetFlags>d__2<T> <GetFlags>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <GetFlags>d__ = this; } else { <GetFlags>d__ = new <GetFlags>d__2<T>(0); } <GetFlags>d__.value = <>3__value; return <GetFlags>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<T>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <GetFlags>d__3 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private int <>l__initialThreadId; private object value; public object <>3__value; private ulong <enumValue>5__2; private IEnumerator <>7__wrap2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetFlags>d__3(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap2 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; if (value == null) { throw new ArgumentNullException("value"); } <enumValue>5__2 = Convert.ToUInt64(value); Type type = value.GetType(); <>7__wrap2 = Enum.GetValues(type).GetEnumerator(); <>1__state = -3; break; } case 1: <>1__state = -3; break; } while (<>7__wrap2.MoveNext()) { object current = <>7__wrap2.Current; ulong num = Convert.ToUInt64(current); if (num != 0L && (<enumValue>5__2 & num) == num) { <>2__current = current; <>1__state = 1; return true; } } <>m__Finally1(); <>7__wrap2 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap2 is IDisposable disposable) { disposable.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<object> IEnumerable<object>.GetEnumerator() { <GetFlags>d__3 <GetFlags>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <GetFlags>d__ = this; } else { <GetFlags>d__ = new <GetFlags>d__3(0); } <GetFlags>d__.value = <>3__value; return <GetFlags>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<object>)this).GetEnumerator(); } } public static T ToFlags<T>(this List<T> enums) where T : struct, Enum { ulong num = 0uL; foreach (T @enum in enums) { num |= Convert.ToUInt64(@enum); } return (T)Enum.ToObject(typeof(T), num); } public static T GetHighestLevel<T>(this T value) where T : struct, Enum { ulong num = Convert.ToUInt64(value); if (num == 0L) { return value; } ulong value2 = 0uL; for (ulong num2 = num; num2 != 0L; num2 &= num2 - 1) { value2 = num2 & (~num2 + 1); } return (T)Enum.ToObject(typeof(T), value2); } [IteratorStateMachine(typeof(<GetFlags>d__2<>))] public static IEnumerable<T> GetFlags<T>(this T value) where T : struct, Enum { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetFlags>d__2<T>(-2) { <>3__value = value }; } [IteratorStateMachine(typeof(<GetFlags>d__3))] public static IEnumerable<object> GetFlags(object value) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetFlags>d__3(-2) { <>3__value = value }; } } internal static class GTFOLogger { private static readonly HashSet<string> _ignoreListExact = new HashSet<string> { "show crosshair", "Setting and getting Body Position/Rotation, IK Goals, Lookat and BoneLocalRotation should only be done in OnAnimatorIK or OnStateIK" }; private static readonly HashSet<string> _ignoreListStartsWith = new HashSet<string> { "Wielding new item in slot", "Backend.", "BE.OnGameEvent" }; internal static IArchiveLogger Logger { private get; set; } public static void Ignore(string str) { _ignoreListExact.Add(str); } public static void Log(string message) { if (!_ignoreListExact.Contains(message) && !_ignoreListStartsWith.Any((string s) => message.StartsWith(s))) { Logger.Info(message); } } public static void Warn(string message) { if (!_ignoreListExact.Contains(message) && !_ignoreListStartsWith.Any((string s) => message.StartsWith(s))) { Logger.Warning(message); } } public static void Error(string message) { if (!_ignoreListExact.Contains(message) && !_ignoreListStartsWith.Any((string s) => message.StartsWith(s))) { Logger.Error(message); } } } internal static class ImplementationManagerExtensions { public static void RegisterForIdentifier(this Type type, string identifier) { ImplementationManager.RegisterGameType(identifier, type); } public static void RegisterSelf<T>(this T type) where T : Type { if (type.IsGenericTypeDefinition) { type.RegisterForIdentifier(type.Name.Split('`')[0] + "<" + ((type.GenericTypeArguments.Length > 1) ? string.Join(",", new string[type.GenericTypeArguments.Length - 1]) : string.Empty) + ">"); } else { type.RegisterForIdentifier(type.Name); } } } public static class LocalFiles { public const string GTFO_SETTINGS_JSON = "GTFO_Settings.json"; public const string GTFO_FAVORITES_JSON = "GTFO_Favorites.json"; public const string GTFO_BOT_FAVORITES_JSON = "GTFO_BotFavorites.json"; private static string _modLocalLowPath; private static string _modDefaultGameLogsAndCachePath; private static string _modDefaultSaveDataPath; private static string _dataBlockDumpPath; private static string _savePath; private static string _gameLogsAndCacheSavePath; private static string _versionSpecificLogsAndCachePath; private static string _versionSpecificSavePath; private static string _otherConfigsPath; private static string _featureConfigsPath; private static string _filesPath; private static string _settingsPath; private static string _favoritesPath; private static string _botFavoritesPath; private static readonly FileStreamOptions Options = new FileStreamOptions { Access = FileAccess.Write, Mode = FileMode.Create, Options = FileOptions.WriteThrough }; public static string ModLocalLowPath { get { if (_modLocalLowPath != null) { return _modLocalLowPath; } _modLocalLowPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow", "GTFO_TheArchive"); if (!Directory.Exists(_modLocalLowPath)) { Directory.CreateDirectory(_modLocalLowPath); } return _modLocalLowPath; } } public static string ModDefaultGameLogsAndCachePath { get { if (_modDefaultGameLogsAndCachePath != null) { return _modDefaultGameLogsAndCachePath; } _modDefaultGameLogsAndCachePath = Path.Combine(ModLocalLowPath, "GameLogsAndCache"); if (!Directory.Exists(_modDefaultGameLogsAndCachePath)) { Directory.CreateDirectory(_modDefaultGameLogsAndCachePath); } return _modDefaultGameLogsAndCachePath; } } public static string ModDefaultSaveDataPath { get { if (_modDefaultSaveDataPath != null) { return _modDefaultSaveDataPath; } _modDefaultSaveDataPath = Path.Combine(ModLocalLowPath, "SaveData"); if (!Directory.Exists(_modLocalLowPath)) { Directory.CreateDirectory(_modLocalLowPath); } return _modDefaultSaveDataPath; } } public static string DataBlockDumpPath { get { if (!string.IsNullOrEmpty(_dataBlockDumpPath)) { return _dataBlockDumpPath; } _dataBlockDumpPath = Path.Combine(SaveDirectoryPath, "DataBlocks", $"Build_{BuildDB.BuildNumber}_{ArchiveMod.CurrentRundown}"); if (!Directory.Exists(_dataBlockDumpPath)) { Directory.CreateDirectory(_dataBlockDumpPath); } return _dataBlockDumpPath; } } public static string SaveDirectoryPath { get { if (!string.IsNullOrEmpty(_savePath)) { return _savePath; } _savePath = (string.IsNullOrWhiteSpace(ArchiveMod.Settings.CustomFileSaveLocation) ? ModDefaultSaveDataPath : ArchiveMod.Settings.CustomFileSaveLocation); if (!Directory.Exists(_savePath)) { Directory.CreateDirectory(_savePath); } return _savePath; } } public static string GameLogsAndCachePath { get { if (_gameLogsAndCacheSavePath != null) { return _gameLogsAndCacheSavePath; } _gameLogsAndCacheSavePath = (string.IsNullOrWhiteSpace(ArchiveMod.Settings.CustomLogsAndCacheLocation) ? ModDefaultGameLogsAndCachePath : ArchiveMod.Settings.CustomLogsAndCacheLocation); if (!Directory.Exists(_gameLogsAndCacheSavePath)) { Directory.CreateDirectory(_gameLogsAndCacheSavePath); } return _gameLogsAndCacheSavePath; } } public static string VersionSpecificLogsAndCachePath { get { if (!string.IsNullOrEmpty(_versionSpecificLogsAndCachePath)) { return _versionSpecificLogsAndCachePath; } _versionSpecificLogsAndCachePath = Path.Combine(GameLogsAndCachePath, $"{((int)ArchiveMod.CurrentRundown).ToString().PadLeft(2, '0')}_{ArchiveMod.CurrentRundown}_Data", "appdata"); if (!Directory.Exists(_versionSpecificLogsAndCachePath)) { Directory.CreateDirectory(_versionSpecificLogsAndCachePath); } return _versionSpecificLogsAndCachePath; } } public static string VersionSpecificSaveDirectoryPath { get { if (!string.IsNullOrEmpty(_versionSpecificSavePath)) { return _versionSpecificSavePath; } _versionSpecificSavePath = GetVersionSpecificSaveDirectoryPath(ArchiveMod.CurrentRundown); if (Directory.Exists(_versionSpecificSavePath)) { return _versionSpecificSavePath; } Directory.CreateDirectory(_versionSpecificSavePath); try { if (!CopyMostRecentSaveFiles(ArchiveMod.CurrentRundown - 1, ArchiveMod.CurrentRundown)) { ArchiveLogger.Notice("Creating new game settings file(s)!"); } } catch (Exception ex) { ArchiveLogger.Warning($"Caught an exception while trying to copy over older settings files: {ex}: {ex.Message}"); ArchiveLogger.Debug(ex.StackTrace); } return _versionSpecificSavePath; } } public static string OtherConfigsDirectoryPath { get { if (!string.IsNullOrEmpty(_otherConfigsPath)) { return _otherConfigsPath; } _otherConfigsPath = Path.Combine(SaveDirectoryPath, "OtherConfigs"); if (!Directory.Exists(_otherConfigsPath)) { Directory.CreateDirectory(_otherConfigsPath); } return _otherConfigsPath; } } public static string FeatureConfigsDirectoryPath { get { if (!string.IsNullOrEmpty(_featureConfigsPath)) { return _featureConfigsPath; } _featureConfigsPath = Path.Combine(SaveDirectoryPath, "FeatureSettings"); if (!Directory.Exists(_featureConfigsPath)) { Directory.CreateDirectory(_featureConfigsPath); } return _featureConfigsPath; } } [Obsolete("Legacy path.")] public static string FilesDirectoryPath { get { if (!string.IsNullOrEmpty(_filesPath)) { return _filesPath; } _filesPath = Path.Combine(VersionSpecificSaveDirectoryPath, "Files"); if (!Directory.Exists(_filesPath)) { Directory.CreateDirectory(_filesPath); } return _filesPath; } } public static string SettingsPath { get { if (!string.IsNullOrEmpty(_settingsPath)) { return _settingsPath; } _settingsPath = Path.Combine(VersionSpecificSaveDirectoryPath, "GTFO_Settings.json"); return _settingsPath; } } public static string FavoritesPath { get { if (string.IsNullOrEmpty(_favoritesPath)) { _favoritesPath = Path.Combine(VersionSpecificSaveDirectoryPath, "GTFO_Favorites.json"); } return _favoritesPath; } } public static string BotFavoritesPath { get { if (string.IsNullOrEmpty(_botFavoritesPath)) { _botFavoritesPath = Path.Combine(VersionSpecificSaveDirectoryPath, "GTFO_BotFavorites.json"); } return _botFavoritesPath; } } private static bool CopyFromBaseGameLocation(Utils.RundownID copyTo) { string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow", "10 Chambers Collective", "GTFO"); string text = Path.Combine(path, "GTFO_Settings.txt"); string text2 = Path.Combine(path, "GTFO_Favorites.txt"); string text3 = Path.Combine(path, "GTFO_BotFavorites.txt"); if (!File.Exists(text)) { return false; } string settingsPath = GetSettingsPath(copyTo); ArchiveLogger.Debug($"Copying vanilla game settings file! (\"{text}\" -> \"{settingsPath}\")"); File.Copy(text, settingsPath); string favoritesPath = GetFavoritesPath(copyTo); string botFavoritesPath = GetBotFavoritesPath(copyTo); if (File.Exists(text2)) { ArchiveLogger.Debug($"Copying vanilla game favorites file! (\"{text2}\" -> \"{favoritesPath}\")"); File.Copy(text2, favoritesPath); } if (File.Exists(text3)) { ArchiveLogger.Debug($"Copying vanilla game bot favorites file! (\"{text3}\" -> \"{botFavoritesPath}\")"); File.Copy(text3, botFavoritesPath); } return true; } private static bool CopyMostRecentSaveFiles(Utils.RundownID copyFrom, Utils.RundownID copyTo, int maxStep = 3) { if (copyFrom < Utils.RundownID.RundownOne) { return CopyFromBaseGameLocation(copyTo); } string settingsPath = GetSettingsPath(copyFrom); if (!File.Exists(settingsPath)) { if (maxStep <= 1) { return CopyFromBaseGameLocation(copyTo); } return CopyMostRecentSaveFiles(copyFrom - 1, copyTo, maxStep - 1); } string settingsPath2 = GetSettingsPath(copyTo); ArchiveLogger.Debug($"Copying most recent settings file! (\"{settingsPath}\" -> \"{settingsPath2}\")"); File.Copy(settingsPath, settingsPath2); if (ArchiveMod.IsPlayingModded) { return true; } string favoritesPath = GetFavoritesPath(copyTo); string favoritesPath2 = GetFavoritesPath(copyFrom); if (File.Exists(favoritesPath2)) { ArchiveLogger.Debug($"Copying most recent favorites file! (\"{favoritesPath2}\" -> \"{favoritesPath}\")"); File.Copy(favoritesPath2, favoritesPath); } string botFavoritesPath = GetBotFavoritesPath(copyTo); string botFavoritesPath2 = GetBotFavoritesPath(copyFrom); if (File.Exists(botFavoritesPath2)) { ArchiveLogger.Debug($"Copying most recent bot favorites file! (\"{botFavoritesPath2}\" -> \"{botFavoritesPath}\")"); File.Copy(botFavoritesPath2, botFavoritesPath); } return true; } public static string GetVersionSpecificSaveDirectoryPath(Utils.RundownID rundown) { string saveDirectoryPath = SaveDirectoryPath; DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(6, 2); int num = (int)rundown; defaultInterpolatedStringHandler.AppendFormatted(num.ToString().PadLeft(2, '0')); defaultInterpolatedStringHandler.AppendLiteral("_"); defaultInterpolatedStringHandler.AppendFormatted(rundown); defaultInterpolatedStringHandler.AppendLiteral("_Data"); return Path.Combine(saveDirectoryPath, defaultInterpolatedStringHandler.ToStringAndClear()); } public static string GetSettingsPath(Utils.RundownID rundown) { return Path.Combine(GetVersionSpecificSaveDirectoryPath(rundown), "GTFO_Settings.json"); } public static string GetFavoritesPath(Utils.RundownID rundown) { return Path.Combine(GetVersionSpecificSaveDirectoryPath(rundown), "GTFO_Favorites.json"); } public static string GetBotFavoritesPath(Utils.RundownID rundown) { return Path.Combine(GetVersionSpecificSaveDirectoryPath(rundown), "GTFO_BotFavorites.json"); } public static T LoadConfig<T>(out bool fileExists, bool saveIfNonExistent = true) where T : new() { string path = Path.Combine(OtherConfigsDirectoryPath, typeof(T).Name + ".json"); try { if (!File.Exists(path)) { T val = new T(); if (saveIfNonExistent) { SaveConfig(val); } fileExists = false; return val; } fileExists = true; return JsonConvert.DeserializeObject<T>(File.ReadAllText(path), ArchiveMod.JsonSerializerSettings); } catch (Exception ex) { ArchiveLogger.Error("An error occured while loading config file " + typeof(T).Name + ".json"); ArchiveLogger.Exception(ex); } fileExists = false; return new T(); } public static T LoadConfig<T>(bool saveIfNonExistent = true) where T : new() { bool fileExists; return LoadConfig<T>(out fileExists, saveIfNonExistent); } public static void SaveConfig<T>(T config) { try { File.WriteAllText(Path.Combine(OtherConfigsDirectoryPath, typeof(T).Name + ".json"), JsonConvert.SerializeObject((object)config, ArchiveMod.JsonSerializerSettings)); } catch (Exception ex) { ArchiveLogger.Error("An error occured while saving config file " + typeof(T).Name + ".json"); ArchiveLogger.Exception(ex); } } private static object LoadFeatureConfig(string moduleIdentifier, string featureIdentifier, Type configType, out bool fileExists, bool saveIfNonExistent = true) { if (string.IsNullOrWhiteSpace(featureIdentifier)) { throw new ArgumentException("Parameter featureIdentifier may not be null or whitespace."); } if (configType == null) { throw new ArgumentNullException("configType"); } string text = Path.Combine(FeatureConfigsDirectoryPath, moduleIdentifier); if (!Directory.Exists(text)) { if (moduleIdentifier == "TheArchive.Essentials") { string text2 = Path.Combine(FeatureConfigsDirectoryPath, "TheArchive.IL2CPP"); if (Directory.Exists(text2)) { ArchiveLogger.Msg(ConsoleColor.Green, $"Migrating old config path from \"{text2}\" to \"{text}\""); Directory.Move(text2, text); } else { Directory.CreateDirectory(text); ArchiveLogger.Msg(ConsoleColor.Green, $"Migrating old config files from \"{FeatureConfigsDirectoryPath}\" to \"{text}\""); foreach (string item in Directory.EnumerateFiles(FeatureConfigsDirectoryPath, "*.json", SearchOption.TopDirectoryOnly)) { string text3 = Path.Combine(text, Path.GetFileName(item)); ArchiveLogger.Debug($"Copying \"{item}\" -> \"{text3}\""); File.Copy(item, text3); } } } else { Directory.CreateDirectory(text); } } string path = Path.Combine(text, featureIdentifier + "_" + configType.Name + ".json"); if (!File.Exists(path)) { object obj = Activator.CreateInstance(configType); if (saveIfNonExistent) { SaveFeatureConfig(moduleIdentifier, featureIdentifier, configType, obj); } fileExists = false; return obj; } fileExists = true; try { return JsonConvert.DeserializeObject(File.ReadAllText(path), configType, ArchiveMod.JsonSerializerSettings); } catch { object obj2 = Activator.CreateInstance(configType); if (saveIfNonExistent) { SaveFeatureConfig(moduleIdentifier, featureIdentifier, configType, obj2); } fileExists = false; return obj2; } } internal static object LoadFeatureConfig(string moduleIdentifier, string featureIdentifier, Type configType, bool saveIfNonExistent = true) { bool fileExists; return LoadFeatureConfig(moduleIdentifier, featureIdentifier, configType, out fileExists, saveIfNonExistent); } internal static void SaveFeatureConfig(string moduleIdentifier, string featureIdentifier, Type configType, object configInstance) { if (string.IsNullOrWhiteSpace(featureIdentifier)) { throw new ArgumentException("Parameter featureIdentifier may not be null or whitespace."); } if (configType == null) { throw new ArgumentNullException("configType"); } if (configInstance == null) { throw new ArgumentNullException("configInstance"); } string text = Path.Combine(FeatureConfigsDirectoryPath, moduleIdentifier); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } string text2 = Path.Combine(text, featureIdentifier + "_" + configType.Name + ".json"); ArchiveLogger.Debug("Saving Feature Setting to: " + text2); string value = JsonConvert.SerializeObject(configInstance, ArchiveMod.JsonSerializerSettings); try { using StreamWriter streamWriter = new StreamWriter(text2, Encoding.UTF8, Options); streamWriter.Write(value); streamWriter.Flush(); } catch (Exception ex) { ArchiveLogger.Error("Threw an exception while trying to save file '" + text2 + "'."); ArchiveLogger.Exception(ex); } } } public static class MetadataHelper { internal static IEnumerable<CustomAttribute> GetCustomAttributes<T>(TypeDefinition td, bool inherit) where T : Attribute { List<CustomAttribute> list = new List<CustomAttribute>(); Type type = typeof(T); TypeDefinition val = td; do { list.AddRange(((IEnumerable<CustomAttribute>)val.CustomAttributes).Where((CustomAttribute ca) => ((MemberReference)ca.AttributeType).FullName == type.FullName)); TypeReference baseType = val.BaseType; val = ((baseType != null) ? baseType.Resolve() : null); } while (inherit && ((val != null) ? ((MemberReference)val).FullName : null) != typeof(object).FullName); return list; } public static ArchiveModule GetMetadata(Type moduleType) { object[] customAttributes = moduleType.GetCustomAttributes(typeof(ArchiveModule), inherit: false); if (customAttributes.Length == 0) { return null; } return (ArchiveModule)customAttributes[0]; } public static ArchiveModule GetMetadata(object module) { return GetMetadata(module.GetType()); } public static T[] GetAttributes<T>(Type moduleType) where T : Attribute { return (T[])moduleType.GetCustomAttributes(typeof(T), inherit: true); } public static T[] GetAttributes<T>(Assembly assembly) where T : Attribute { return (T[])assembly.GetCustomAttributes(typeof(T), inherit: true); } public static IEnumerable<T> GetAttributes<T>(object module) where T : Attribute { return GetAttributes<T>(module.GetType()); } public static T[] GetAttributes<T>(MemberInfo member) where T : Attribute { return (T[])member.GetCustomAttributes(typeof(T), inherit: true); } public static IEnumerable<ArchiveDependency> GetDependencies(Type module) { return module.GetCustomAttributes(typeof(ArchiveDependency), inherit: true).Cast<ArchiveDependency>(); } } public static class PresenceFormatter { [AttributeUsage(AttributeTargets.Property)] public class PresenceFormatProvider : Attribute { private PropertyInfo _propertyInfo; public string Identifier { get; private set; } public bool IsValid => _propertyInfo != null; public Type PropertyType => PropertyInfo.PropertyType; public string DebugIdentifier => $"{PropertyInfo.DeclaringType.Name}.{PropertyInfo.Name} (ASM:{PropertyInfo.DeclaringType.Assembly.GetName().Name})"; internal PropertyInfo PropertyInfo => _propertyInfo ?? throw new Exception($"PropertyInfo not set on {"PresenceFormatProvider"} with ID \"{Identifier}\"!"); public PresenceFormatProvider([CallerMemberName] string identifier = "") { Identifier = identifier; } internal void SetPropertyInfo(PropertyInfo propertyInfo) { if (propertyInfo == null) { throw new ArgumentException($"Provided {"PresenceFormatProvider"} {"PropertyInfo"} may not be null! (ID:{Identifier})"); } MethodInfo? getMethod = propertyInfo.GetGetMethod(); if ((object)getMethod == null || !getMethod.IsStatic) { throw new ArgumentException($"Provided {"PresenceFormatProvider"} {"PropertyInfo"} has to implement a static get method! (ID:{Identifier})"); } _propertyInfo = propertyInfo; } public object GetValue() { return PropertyInfo.GetValue(null); } } [AttributeUsage(AttributeTargets.Property)] public class FallbackPresenceFormatProvider : PresenceFormatProvider { public bool NoNotImplementedWarning { get; } public FallbackPresenceFormatProvider(string identifier, bool noNotImplementedWarning = false) : base(identifier) { NoNotImplementedWarning = noNotImplementedWarning; } } private static readonly Dictionary<string, PresenceFormatProvider> _formatters = new Dictionary<string, PresenceFormatProvider>(); private static readonly List<Type> _typesToCheckForProviders = new List<Type>(); private static IArchiveLogger _logger; private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateArSubLoggerInstance("PresenceFormatter", ConsoleColor.DarkMagenta)); public static void Setup() { Logger.Debug("Setting up providers ..."); foreach (Type typesToCheckForProvider in _typesToCheckForProviders) { CheckTypeForProviders(typesToCheckForProvider); } foreach (KeyValuePair<string, PresenceFormatProvider> item in _formatters.Where((KeyValuePair<string, PresenceFormatProvider> kvp) => kvp.Value is FallbackPresenceFormatProvider fallbackPresenceFormatProvider && !fallbackPresenceFormatProvider.NoNotImplementedWarning)) { Logger.Warning("Identifier \"" + item.Key + "\" has not been implemented! Using Fallback default values!"); } } private static void CheckTypeForProviders(Type type) { PropertyInfo[] properties = type.GetProperties(); foreach (PropertyInfo propertyInfo in properties) { try { PresenceFormatProvider customAttribute = propertyInfo.GetCustomAttribute<PresenceFormatProvider>(); if (customAttribute != null) { customAttribute.SetPropertyInfo(propertyInfo); RegisterFormatter(customAttribute); } } catch (Exception ex) { Logger.Exception(ex); } } } public static void RegisterAllPresenceFormatProviders(this Type type, bool throwOnDuplicate = true) { if (type == null) { throw new ArgumentException("Type must not be null!"); } if (_typesToCheckForProviders.Contains(type)) { if (throwOnDuplicate) { throw new ArgumentException("Duplicate Type registered: \"" + type.FullName + "\""); } return; } _typesToCheckForProviders.Add(type); if (_formatters.Count > 0) { Logger.Debug($"Late call of {"RegisterAllPresenceFormatProviders"} for {type.FullName} - running checks now."); CheckTypeForProviders(type); } } private static void RegisterFormatter(PresenceFormatProvider pfp) { if (!pfp.IsValid) { return; } bool flag = false; if (_formatters.TryGetValue(pfp.Identifier, out var value)) { if (pfp is FallbackPresenceFormatProvider) { return; } if (!(value is FallbackPresenceFormatProvider)) { throw new ArgumentException($"Duplicate formatter identifier: \"{pfp.Identifier}\" (\"{pfp.DebugIdentifier}\")"); } _formatters.Remove(pfp.Identifier); flag = true; } _formatters.Add(pfp.Identifier, pfp); Logger.Debug($"{(flag ? " (Fallback Overridden)" : ((pfp is FallbackPresenceFormatProvider) ? " (Fallback)" : string.Empty))} Registered: \"{pfp.Identifier}\" => {pfp.DebugIdentifier}"); } public static object Get(string identifier) { _formatters.TryGetValue(identifier, out var value); return value?.GetValue(); } public static T Get<T>(string identifier) { _formatters.TryGetValue(identifier, out var value); if (value == null) { return default(T); } if (!value.PropertyType.IsAssignableFrom(typeof(T)) && typeof(T) != typeof(string)) { throw new ArgumentException($"The property at identifier \"{identifier}\" is not declared as Type \"{typeof(T).Name}\"!"); } return (T)value.GetValue(); } public static string Format(this string formatString, params (string search, string replace)[] extraFormatters) { return FormatPresenceString(formatString, extraFormatters); } public static string FormatPresenceString(string formatString) { return FormatPresenceString(formatString, null); } public static string FormatPresenceString(string formatString, params (string search, string replace)[] extraFormatters) { return FormatPresenceString(formatString, stripAllTMPTags: true, extraFormatters); } public static string FormatPresenceString(string formatString, bool stripAllTMPTags = true, params (string search, string replace)[] extraFormatters) { string text = formatString; foreach (KeyValuePair<string, PresenceFormatProvider> formatter in _formatters) { if (text.Contains("%" + formatter.Key + "%")) { text = text.ReplaceCaseInsensitive("%" + formatter.Key + "%", formatter.Value.GetValue()?.ToString() ?? "null"); } } if (extraFormatters != null) { for (int i = 0; i < extraFormatters.Length; i++) { (string, string) tuple = extraFormatters[i]; if (text.Contains("%" + tuple.Item1 + "%")) { text = text.ReplaceCaseInsensitive("%" + tuple.Item1 + "%", tuple.Item2); } } } if (stripAllTMPTags) { return Utils.StripTMPTagsRegex(text.Trim()); } return text.Trim(); } } public static class RundownFlagsExtensions { private static IEnumerable<Utils.RundownFlags> _allFlagsOrdered; public static IEnumerable<Utils.RundownFlags> AllFlagsOrdered { get { if (_allFlagsOrdered == null) { _allFlagsOrdered = (from Utils.RundownFlags x in Enum.GetValues(typeof(Utils.RundownFlags)) orderby x select x).Skip(2); } return _allFlagsOrdered; } } public static bool IsIncludedIn(this Utils.RundownID rundownID, Utils.RundownFlags flags) { return Utils.FlagsContain(flags, r
BepInEx/plugins/TheArchive.Essentials/plugins/TheArchive.Essentials.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using AK; using Agents; using AirParticleSystem; using BepInEx.Unity.IL2CPP.Utils.Collections; using BoosterImplants; using CellMenu; using ChainedPuzzles; using Clonesoft.Json; using Clonesoft.Json.Linq; using Enemies; using FX_EffectSystem; using GameData; using Gear; using HarmonyLib; using IRF; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.Runtime; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using LevelGeneration; using Localization; using Microsoft.CodeAnalysis; using Player; using SNetwork; using StateMachines; using Steamworks; using TMPro; using TheArchive.Core; using TheArchive.Core.Attributes; using TheArchive.Core.Attributes.Feature; using TheArchive.Core.Attributes.Feature.Members; using TheArchive.Core.Attributes.Feature.Patches; using TheArchive.Core.Attributes.Feature.Settings; using TheArchive.Core.FeaturesAPI; using TheArchive.Core.FeaturesAPI.Components; using TheArchive.Core.FeaturesAPI.Groups; using TheArchive.Core.FeaturesAPI.Settings; using TheArchive.Core.Localization; using TheArchive.Core.Managers; using TheArchive.Core.Models; using TheArchive.Features.Accessibility; using TheArchive.Features.Dev; using TheArchive.Interfaces; using TheArchive.Loader; using TheArchive.Utilities; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyFileVersion("2025.2.0")] [assembly: AssemblyInformationalVersion("2025.2.0")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("TheArchive.Essentials")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyProduct("TheArchive.Essentials")] [assembly: AssemblyTitle("TheArchive.Essentials")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2025.2.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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; } } } internal class ThisAssembly { public class Git { public class BaseVersion { public const string Major = "0"; public const string Minor = "0"; public const string Patch = "0"; } public class SemVer { public const string Major = "0"; public const string Minor = "0"; public const string Patch = "838"; public const string Label = ""; public const string DashLabel = ""; public const string Source = "Default"; } public const bool IsDirty = false; public const string IsDirtyString = "false"; public const string RepositoryUrl = "https://github.com/Tuesday1028/GTFO_TheArchive"; public const string Branch = "doing-things"; public const string Commit = "a4eb55a"; public const string Sha = "a4eb55a7e97569a44df6831acc6074b0f084b6eb"; public const string CommitDate = "2026-05-07T19:14:52+08:00"; public const string Commits = "838"; public const string Tag = ""; public const string BaseTag = ""; } } internal class ManifestInfo { internal const string TSName = "TheArchive_Essentials"; internal const string TSDescription = "That one massive GTFO Quality of Life mod."; internal const string TSVersion = "2025.2.0"; internal const string TSAuthor = "AuriRex"; internal const string TSWebsite = "https://github.com/AuriRex/GTFO_TheArchive"; } namespace TheArchive { [ArchiveModule("dev.AuriRex.gtfo.TheArchive.Essentials", "TheArchive_Essentials", "2025.2.0")] public class ArchiveEssentialsModule : IArchiveModule { public const string GUID = "dev.AuriRex.gtfo.TheArchive.Essentials"; public const string MOD_NAME = "TheArchive_Essentials"; public const string VERSION = "2025.2.0"; public ILocalizationService LocalizationService { get; set; } public IArchiveLogger Logger { get; set; } public void Init() { } } } namespace TheArchive.Features.Special { [RundownConstraint(/*Could not decode attribute arguments.*/)] public class AdBlock : Feature { [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class CM_PageRundown_New_ABC_Patch { public static void Postfix(CM_PageRundown_New __instance) { ToggleDOWImage(__instance); } } public override string Name => "AdBlock"; public override GroupBase Group => (GroupBase)(object)GroupManager.Special; public override string Description => "Removes the Den of Wolves button from the rundown screen."; public static bool IsEnabled { get; set; } public override void OnEnable() { if (Feature.DataBlocksReady) { ToggleDOWImage(MainMenuGuiLayer.Current.PageRundownNew); } } public override void OnDisable() { if (!Feature.IsApplicationQuitting) { ToggleDOWImage(MainMenuGuiLayer.Current.PageRundownNew, setActive: true); } } private static void ToggleDOWImage(CM_PageRundown_New __instance, bool setActive = false) { RectTransform movingContentHolder = ((CM_PageBase)__instance).m_movingContentHolder; object obj; if (movingContentHolder == null) { obj = null; } else { Transform childWithExactName = SharedUtils.GetChildWithExactName((Transform)(object)movingContentHolder, "PasteAndJoinOnLobbyID"); if (childWithExactName == null) { obj = null; } else { Transform childWithExactName2 = SharedUtils.GetChildWithExactName(childWithExactName, "ButtonGIF"); obj = ((childWithExactName2 != null) ? ((Component)childWithExactName2).gameObject : null); } } GameObject val = (GameObject)obj; if (!((Object)(object)val == (Object)null)) { OnEnabledListener component = val.GetComponent<OnEnabledListener>(); if ((Object)(object)component != (Object)null && setActive) { Object.Destroy((Object)(object)component); } else if ((Object)(object)component == (Object)null) { component = val.AddComponent<OnEnabledListener>(); OnEnabledListener obj2 = component; obj2.OnEnabledSelf = (Action<GameObject>)Delegate.Combine(obj2.OnEnabledSelf, new Action<GameObject>(OnButtonEnabled)); } val.SetActive(setActive); } } private static void OnButtonEnabled(GameObject go) { if (IsEnabled) { go.SetActive(false); } } } [EnableFeatureByDefault] internal class AltTabCounter : Feature { public class AltTabCounterSettings { public class AltTabCounterSettingsForReal { [FSHeader("ALT + TABs <3", true)] [FSReadOnly(true)] [FSDisplayName("Total Count")] [FSDescription("All time total of ALT + TABs")] public int TotalCount { get; set; } [FSReadOnly(true)] [FSDisplayName("This Session Count")] [FSDescription("ALT + TABs accumulated this session")] public int CurrentSessionCount { get; set; } [FSReadOnly(true)] [FSDisplayName("This Level Count")] [FSDescription("ALT + TABs accumulated this level")] public int CurrentWhileInLevelCount { get; set; } } [FSUseDynamicSubmenu] [FSDisplayName("ALT + TAB Counts")] public AltTabCounterSettingsForReal AltTabCounts { get; set; } = new AltTabCounterSettingsForReal(); } public override string Name => "Alt Tab Counter"; public override GroupBase Group => (GroupBase)(object)GroupManager.Special; public override string Description => "Counts the amount of times that the game went out of focus. (ALT + TAB)"; [FeatureConfig] public static AltTabCounterSettings Settings { get; set; } public static bool InLevel { get; private set; } public override void Init() { Settings.AltTabCounts.CurrentSessionCount = 0; } public override void OnApplicationFocusChanged(bool focus) { if (!focus) { Settings.AltTabCounts.TotalCount++; Settings.AltTabCounts.CurrentSessionCount++; if (InLevel) { Settings.AltTabCounts.CurrentWhileInLevelCount++; } Feature.MarkSettingsAsDirty<AltTabCounterSettings>(Settings); } } public void OnGameStateChanged(eGameStateName state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 InLevel = (int)state == 10; if (InLevel) { Settings.AltTabCounts.CurrentWhileInLevelCount = 0; Feature.MarkSettingsAsDirty<AltTabCounterSettings>(Settings); } } } public class HideFirstPersonItem : Feature { public class HideFirstPersonItemSettings { [FSDisplayName("Model Toggle Key")] [FSDescription("Key used to toggle the model.")] public KeyCode Key { get; set; } = (KeyCode)283; } public override string Name => "Weapon Model Toggle"; public override GroupBase Group => (GroupBase)(object)GroupManager.Special; public override string Description => "Forces the held item to be hidden.\nIntended for taking pictures.\n<color=orange>(Warning! This makes you unable to use or switch items until unhidden!)</color>"; [FeatureConfig] public static HideFirstPersonItemSettings Settings { get; set; } public override void Update() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_000e: Unknown result type (might be due to invalid IL or missing references) if ((int)FocusStateManager.CurrentState == 4 && Input.GetKeyDown(Settings.Key)) { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); if ((Object)(object)localPlayerAgent != (Object)null && (Object)(object)localPlayerAgent.FPItemHolder != (Object)null) { localPlayerAgent.FPItemHolder.ForceItemHidden = !localPlayerAgent.FPItemHolder.ForceItemHidden; } } } } [RundownConstraint(/*Could not decode attribute arguments.*/)] public class MuteSpeak : Feature { private static PlayerAgent _localPlayerAgent; public override string Name => "Mute Speak"; public override GroupBase Group => (GroupBase)(object)GroupManager.Special; public override string Description => "Binds a few voice lines to keyboard keys.\n\nArrow keys\n[P, L, K, J, H] toggleable by hitting F8; off by default\nHold [Right Control] for alternate lines"; public static IArchiveLogger FeatureLogger { get; set; } public static bool EnableOtherVoiceBinds { get; set; } public static void IfKeySay(KeyCode key, uint soundId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(key)) { PlayerVoiceManager.WantToSay(_localPlayerAgent.CharacterID, soundId); } } public static void IfKeySay(KeyCode key, uint soundId, int characterID) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(key)) { PlayerVoiceManager.WantToSay(characterID, soundId); } } public override void Update() { if (Input.GetKeyDown((KeyCode)289)) { EnableOtherVoiceBinds = !EnableOtherVoiceBinds; FeatureLogger.Notice($"Voice binds enabled: {EnableOtherVoiceBinds}"); } if (PlayerChatManager.InChatMode || SharedUtils.LocalPlayerIsInTerminal || !PlayerManager.TryGetLocalPlayerAgent(ref _localPlayerAgent) || (Object)(object)_localPlayerAgent == (Object)null) { return; } if (Input.GetKey((KeyCode)305)) { IfKeySay((KeyCode)303, SoundEventCache.Resolve("PLAY_BIGSPACEENTER01", false)); IfKeySay((KeyCode)273, SoundEventCache.Resolve("PLAY_CL_NORTH", false)); IfKeySay((KeyCode)275, SoundEventCache.Resolve("PLAY_CL_EAST", false)); IfKeySay((KeyCode)274, SoundEventCache.Resolve("PLAY_CL_SOUTH", false)); IfKeySay((KeyCode)276, SoundEventCache.Resolve("PLAY_CL_WEST", false)); if (EnableOtherVoiceBinds) { IfKeySay((KeyCode)112, SoundEventCache.Resolve("PLAY_CL_WELLDONE", false)); IfKeySay((KeyCode)108, SoundEventCache.Resolve("PLAY_CL_SHH", false)); IfKeySay((KeyCode)107, SoundEventCache.Resolve("PLAY_CL_WILLDO", false)); IfKeySay((KeyCode)106, SoundEventCache.Resolve("PLAY_CL_YOUTAKE", false)); IfKeySay((KeyCode)104, SoundEventCache.Resolve("PLAY_FALLDAMAGEGRUNT02_5A", false), 2); } } else { IfKeySay((KeyCode)276, SoundEventCache.Resolve("PLAY_CL_LEFT", false)); IfKeySay((KeyCode)275, SoundEventCache.Resolve("PLAY_CL_RIGHT", false)); IfKeySay((KeyCode)273, SoundEventCache.Resolve("PLAY_CL_YES", false)); IfKeySay((KeyCode)274, SoundEventCache.Resolve("PLAY_CL_NO", false)); if (EnableOtherVoiceBinds) { IfKeySay((KeyCode)112, SoundEventCache.Resolve("PLAY_CL_SORRY", false)); IfKeySay((KeyCode)108, SoundEventCache.Resolve("PLAY_CL_HURRY", false)); IfKeySay((KeyCode)106, SoundEventCache.Resolve("PLAY_CL_THREETWOONEGO", false)); IfKeySay((KeyCode)107, SoundEventCache.Resolve("PLAY_CL_SYNCHRONIZE", false)); IfKeySay((KeyCode)104, SoundEventCache.Resolve("PLAY_FALLDAMAGEGRUNT02_5A", false)); } } } } [EnableFeatureByDefault] public class ProcessPriority : Feature { public class ProcessPrioritySettings { [Localized] public enum PriorityClass { High, AboveNormal, Normal, BelowNormal } [FSDisplayName("Priority")] public PriorityClass Priority { get; set; } = PriorityClass.AboveNormal; } private static readonly Dictionary<ProcessPrioritySettings.PriorityClass, ProcessPriorityClass> _prioMap = new Dictionary<ProcessPrioritySettings.PriorityClass, ProcessPriorityClass> { { ProcessPrioritySettings.PriorityClass.High, ProcessPriorityClass.High }, { ProcessPrioritySettings.PriorityClass.AboveNormal, ProcessPriorityClass.AboveNormal }, { ProcessPrioritySettings.PriorityClass.Normal, ProcessPriorityClass.Normal }, { ProcessPrioritySettings.PriorityClass.BelowNormal, ProcessPriorityClass.BelowNormal } }; public override string Name => "Process Priority"; public override GroupBase Group => (GroupBase)(object)GroupManager.Special; public override string Description => $"Set the games process priority.\n\nThis does the same thing as opening up <color=orange>Taskmanager</color>, going into the 'Details' tab and right clicking on GTFO.exe > [Set Priority]\n\nWarning! Your system might lag / stutter while the game is loading if set to <color=orange>{1}</color> or higher!"; [FeatureConfig] public static ProcessPrioritySettings Settings { get; set; } public override void OnEnable() { SetPriority(Settings.Priority); } public override void OnDisable() { SetPriority(ProcessPrioritySettings.PriorityClass.Normal); } public override void OnFeatureSettingChanged(FeatureSetting setting) { SetPriority(Settings.Priority); } public void SetPriority(ProcessPrioritySettings.PriorityClass settingsPriority) { if (_prioMap.TryGetValue(settingsPriority, out var value)) { using (Process process = Process.GetCurrentProcess()) { process.PriorityClass = value; } ((Feature)this).FeatureLogger.Success($"Set ProcessPriority to {settingsPriority}!"); } } } [RundownConstraint(/*Could not decode attribute arguments.*/)] public class RemoveChatRestrictions : Feature { [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class PlayerChatManager_SetupPatch { public static void Postfix(ref PlayerChatManager __instance) { try { SetValues(__instance); } catch (Exception ex) { FeatureLogger.Exception(ex); } } } private static PropertyAccessor<PlayerChatManager, Il2CppStructArray<int>> A_PlayerChatManager_m_forbiddenChars; private readonly int[] _forbiddenChars = new int[3] { 60, 61, 62 }; public override string Name => "Remove Chat Restrictions"; public override GroupBase Group => (GroupBase)(object)GroupManager.Special; public override string Description => "Allows the usage of '>' and '<' characters in chat.\n\n(Also enables TextMeshPro RichText tags to be used in chat, don't do stupid things!)"; public static IArchiveLogger FeatureLogger { get; set; } public override void OnEnable() { SetValues(PlayerChatManager.Current); } public override void OnDisable() { SetValues(PlayerChatManager.Current, _forbiddenChars); } public override void Init() { A_PlayerChatManager_m_forbiddenChars = PropertyAccessor<PlayerChatManager, Il2CppStructArray<int>>.GetAccessor("m_forbiddenChars", false); } public static void SetValues(PlayerChatManager instance, int[] values = null) { if (!((Object)(object)instance == (Object)null)) { A_PlayerChatManager_m_forbiddenChars.Set(instance, Il2CppStructArray<int>.op_Implicit(values ?? Array.Empty<int>())); } } } } namespace TheArchive.Features.Security { [EnableFeatureByDefault] public class AntiBoosterHack : Feature { [ArchivePatch(/*Could not decode attribute arguments.*/)] private class BoosterImplantManager__OnSyncBoosterImplants__Patch { private static void Postfix(SNet_Player player, pBoosterImplantsWithOwner pBoosterImplantsWithOwner) { SNet_Player player2 = default(SNet_Player); if (SNet.IsMaster && pBoosterImplantsWithOwner != null && (player == null || !player.IsLocal) && SNet.Replication.TryGetLastSender(ref player2, false) && ((pBoosterImplantsWithOwner.BasicImplant.BoosterEffectCount != 0 && !BoosterImplantTemplateManager.TryGetBoosterImplantTemplate(pBoosterImplantsWithOwner.BasicImplant, (BoosterImplantCategory)0)) || (pBoosterImplantsWithOwner.AdvancedImplant.BoosterEffectCount != 0 && !BoosterImplantTemplateManager.TryGetBoosterImplantTemplate(pBoosterImplantsWithOwner.AdvancedImplant, (BoosterImplantCategory)1)) || (pBoosterImplantsWithOwner.SpecializedImplant.BoosterEffectCount != 0 && !BoosterImplantTemplateManager.TryGetBoosterImplantTemplate(pBoosterImplantsWithOwner.SpecializedImplant, (BoosterImplantCategory)2)))) { PunishPlayer(player2); } } } public static class BoosterImplantTemplateManager { public class LocalizedTextJsonConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { writer.WriteValue(string.Empty); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return BoosterImplantTemplateDataBlock.UNKNOWN_BLOCK.PublicName; } public override bool CanConvert(Type objectType) { return objectType == typeof(LocalizedText); } } public class ListOfTConverter<T> : JsonConverter<List<T>> { public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 JToken val = JToken.Load(reader); if ((int)val.Type == 2) { return SharedUtils.ToIL2CPPListIfNecessary<T>(val.ToObject<List<T>>(serializer)); } return null; } public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer) { JToken.FromObject((object)value).WriteTo(writer, Array.Empty<JsonConverter>()); } public ListOfTConverter() { ((JsonConverter<List<List<T>>>)(object)this)..ctor(); } } public class ListOfListOfTConverter<T> : JsonConverter<List<List<T>>> { public override List<List<T>> ReadJson(JsonReader reader, Type objectType, List<List<T>> existingValue, bool hasExistingValue, JsonSerializer serializer) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) JToken val = JToken.Load(reader); List<List<T>> val2 = (List<List<T>>)(object)new List<List<List<T>>>(); if ((int)val.Type == 2) { foreach (JToken item in val.Children()) { if ((int)item.Type != 2) { continue; } List<T> val3 = new List<T>(); foreach (JToken item2 in item.Children()) { T val4 = item2.ToObject<T>(serializer); val3.Add(val4); } ((List<List<List<T>>>)(object)val2).Add((List<List<T>>)(object)val3); } } return val2; } public override void WriteJson(JsonWriter writer, List<List<T>> value, JsonSerializer serializer) { JToken.FromObject((object)value).WriteTo(writer, Array.Empty<JsonConverter>()); } public ListOfListOfTConverter() { ((JsonConverter<List<List<List<List<T>>>>>)(object)this)..ctor(); } } public class BoosterImplantEffectTemplate { public uint BoosterImplantEffect { get; set; } public float EffectMaxValue { get; set; } public float EffectMinValue { get; set; } public BoosterImplantEffectTemplate(BoosterImplantEffectInstance effect) { EffectMaxValue = effect.MaxValue; EffectMinValue = effect.MinValue; BoosterImplantEffect = effect.BoosterImplantEffect; } } public class BoosterImplantTemplate { public uint BoosterImplantID { get; set; } public BoosterImplantCategory ImplantCategory { get; set; } [JsonIgnore] public List<BoosterImplantEffectTemplate> Effects { get; set; } = new List<BoosterImplantEffectTemplate>(); [JsonIgnore] public List<List<BoosterImplantEffectTemplate>> RandomEffects { get; set; } = new List<List<BoosterImplantEffectTemplate>>(); [JsonIgnore] public List<uint> Conditions { get; set; } = new List<uint>(); [JsonIgnore] public List<uint> RandomConditions { get; set; } = new List<uint>(); [JsonIgnore] public BoosterImplantTemplateDataBlock TemplateDataBlock { get; private set; } public List<List<BoosterImplantEffectTemplate>> EffectGroups { get; set; } = new List<List<BoosterImplantEffectTemplate>>(); public List<List<uint>> ConditionGroups { get; set; } = new List<List<uint>>(); public BoosterImplantTemplate(BoosterImplantTemplateDataBlock block) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) TemplateDataBlock = block; BoosterImplantID = ((GameDataBlockBase<BoosterImplantTemplateDataBlock>)(object)block).persistentID; ImplantCategory = block.ImplantCategory; for (int i = 0; i < block.Effects.Count; i++) { Effects.Add(new BoosterImplantEffectTemplate(block.Effects[i])); } for (int j = 0; j < block.RandomEffects.Count; j++) { List<BoosterImplantEffectTemplate> list = new List<BoosterImplantEffectTemplate>(); for (int k = 0; k < block.RandomEffects[j].Count; k++) { list.Add(new BoosterImplantEffectTemplate(block.RandomEffects[j][k])); } RandomEffects.Add(list); } Conditions.AddRange((IEnumerable<uint>)block.Conditions.ToArray()); RandomConditions.AddRange((IEnumerable<uint>)block.RandomConditions.ToArray()); EffectGroups = GenerateEffectGroups(); ConditionGroups = GenerateConditionGroups(); } private List<List<BoosterImplantEffectTemplate>> GenerateEffectGroups() { List<List<BoosterImplantEffectTemplate>> list = new List<List<BoosterImplantEffectTemplate>>(); List<List<BoosterImplantEffectTemplate>> nElementCombinations = GetNElementCombinations(RandomEffects); for (int i = 0; i < nElementCombinations.Count; i++) { List<BoosterImplantEffectTemplate> list2 = Effects.ToList(); list2.AddRange(nElementCombinations[i]); list.Add(list2); } return list; } private List<List<uint>> GenerateConditionGroups() { return GetNElementCombinations(new List<List<uint>> { Conditions, RandomConditions }); } } private static string _r5BoosterTemplatesJson; private static List<BoosterImplantTemplateDataBlock> OldBoosterImplantTemplateDataBlocks = new List<BoosterImplantTemplateDataBlock>(); public static List<BoosterImplantTemplate> BoosterImplantTemplates { get; } = new List<BoosterImplantTemplate>(); private static string R5BoosterTemplatesJson { get { if (!string.IsNullOrWhiteSpace(_r5BoosterTemplatesJson)) { return _r5BoosterTemplatesJson; } byte[] bytes = Utils.LoadFromResource("TheArchive.Resources.RundownFiveBoosterTemplates.json"); _r5BoosterTemplatesJson = Encoding.UTF8.GetString(bytes); return _r5BoosterTemplatesJson; } } public static void LoadTemplateData() { OldBoosterImplantTemplateDataBlocks.Clear(); OldBoosterImplantTemplateDataBlocks.AddRange(JsonConvert.DeserializeObject<List<BoosterImplantTemplateDataBlock>>(R5BoosterTemplatesJson, (JsonConverter[])(object)new JsonConverter[4] { new LocalizedTextJsonConverter(), (JsonConverter)new ListOfTConverter<uint>(), (JsonConverter)new ListOfTConverter<BoosterImplantEffectInstance>(), (JsonConverter)new ListOfListOfTConverter<BoosterImplantEffectInstance>() })); BoosterImplantTemplates.Clear(); Il2CppArrayBase<BoosterImplantTemplateDataBlock> allBlocksForEditor = GameDataBlockBase<BoosterImplantTemplateDataBlock>.GetAllBlocksForEditor(); for (int i = 0; i < allBlocksForEditor.Count; i++) { BoosterImplantTemplates.Add(new BoosterImplantTemplate(allBlocksForEditor[i])); } for (int j = 0; j < OldBoosterImplantTemplateDataBlocks.Count; j++) { BoosterImplantTemplates.Add(new BoosterImplantTemplate(OldBoosterImplantTemplateDataBlocks[j])); } } public static bool TryGetBoosterImplantTemplate(pBoosterImplantData boosterImplant, BoosterImplantCategory category) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) if (boosterImplant == null) { return false; } uint persistenID = boosterImplant.BoosterImplantID; List<BoosterImplantTemplate> list = BoosterImplantTemplates.FindAll((BoosterImplantTemplate p) => p.BoosterImplantID == persistenID && p.ImplantCategory == category); for (int k = 0; k < list.Count; k++) { BoosterImplantTemplate boosterImplantTemplate = list[k]; if (boosterImplantTemplate == null || boosterImplantTemplate.TemplateDataBlock == null) { continue; } List<List<uint>> conditionGroups = boosterImplantTemplate.ConditionGroups; int conditionCount = boosterImplant.ConditionCount; bool flag = false; IEnumerable<uint> conditions = ((IEnumerable<uint>)boosterImplant.Conditions).Take(conditionCount); int j; for (j = 0; j < conditionGroups.Count; j++) { if (conditionCount != conditionGroups[j].Count) { continue; } bool num = conditions.All((uint p) => conditionGroups[j].Any((uint q) => q == p)); bool flag2 = conditionGroups[j].All((uint p) => conditions.Any((uint q) => q == p)); if (num && flag2) { flag = true; break; } } if (!flag) { continue; } int boosterEffectCount = boosterImplant.BoosterEffectCount; bool flag3 = false; List<List<BoosterImplantEffectTemplate>> effectGroups = boosterImplantTemplate.EffectGroups; IEnumerable<pBoosterEffectData> effects = ((IEnumerable<pBoosterEffectData>)boosterImplant.BoosterEffectDatas).Take(boosterEffectCount); int i; for (i = 0; i < effectGroups.Count; i++) { if (effectGroups[i].Count != boosterEffectCount) { continue; } for (int l = 0; l < effectGroups[i].Count; l++) { bool num2 = effects.All((pBoosterEffectData p) => effectGroups[i].Any((BoosterImplantEffectTemplate q) => q.BoosterImplantEffect == p.BoosterEffectID && p.EffectValue >= q.EffectMinValue && p.EffectValue <= q.EffectMaxValue)); bool flag4 = effectGroups[i].All((BoosterImplantEffectTemplate p) => effects.Any((pBoosterEffectData q) => q.BoosterEffectID == p.BoosterImplantEffect && q.EffectValue >= p.EffectMinValue && q.EffectValue <= p.EffectMaxValue)); if (num2 && flag4) { flag3 = true; break; } } if (flag3) { break; } } if (flag3) { bool flag5 = boosterImplant.UseCount <= (int)boosterImplantTemplate.TemplateDataBlock.DurationRange.y && boosterImplant.UseCount >= 0; if (flag && flag3 && flag5) { return true; } } } return false; } private static List<List<T>> GetNElementCombinations<T>(List<List<T>> lists) { List<List<T>> list = new List<List<T>>(); GetNElementCombinationsHelper(lists, new List<T>(), 0, list); return list; } private static void GetNElementCombinationsHelper<T>(List<List<T>> lists, List<T> currentCombination, int currentIndex, List<List<T>> combinations) { if (currentIndex == lists.Count) { combinations.Add(new List<T>(currentCombination)); return; } List<T> list = lists[currentIndex]; if (list.Count == 0) { GetNElementCombinationsHelper(lists, currentCombination, currentIndex + 1, combinations); return; } foreach (T item in list) { currentCombination.Add(item); GetNElementCombinationsHelper(lists, currentCombination, currentIndex + 1, combinations); currentCombination.RemoveAt(currentCombination.Count - 1); } } } public override string Name => "Anti Booster Hack"; public override string Description => "Prevents clients from using modified boosters."; public override GroupBase Group => (GroupBase)(object)GroupManager.Security; public override Type[] ExternalLocalizedTypes => new Type[1] { typeof(BasicPunishmentSettings) }; public static IArchiveLogger FeatureLogger { get; set; } [FeatureConfig] public static BasicPunishmentSettings Settings { get; set; } public override bool ShouldInit() { if (ArchiveMod.IsPlayingModded) { ((Feature)this).RequestDisable("Playing Modded"); return false; } return true; } public override void OnGameDataInitialized() { BoosterImplantTemplateManager.LoadTemplateData(); } public static bool PunishPlayer(SNet_Player player) { if ((Object)(object)player == (Object)null) { return true; } if (SharedUtils.IsFriend(player) && !Settings.PunishFriends) { FeatureLogger.Notice($"Friend \"{player.NickName}\" \"{player.Lookup}\" is using modified boosters!"); return false; } switch (Settings.Punishment) { case BasicPunishmentSettings.PunishmentMode.KickAndBan: PlayerLobbyManagement.BanPlayer(player); break; case BasicPunishmentSettings.PunishmentMode.Kick: PlayerLobbyManagement.KickPlayer(player); break; } FeatureLogger.Notice($"Player \"{player.NickName}\" \"{player.Lookup}\" is using modified boosters! ({Settings.Punishment})"); return true; } } [EnableFeatureByDefault] public class AntiGearHack : Feature { [ArchivePatch(/*Could not decode attribute arguments.*/)] private class PlayerBackpackManager__ReceiveInventorySync__Patch { private static void Prefix(pInventorySync data) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) //IL_005f: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown if (!SNet.IsMaster) { return; } pPlayer sourcePlayer = data.sourcePlayer; SNet_Player val = default(SNet_Player); SNet_Player val2 = default(SNet_Player); if (!((pPlayer)(ref sourcePlayer)).TryGetPlayer(ref val) || val.IsLocal || val.IsBot || !SNet.Replication.TryGetLastSender(ref val2, false) || val2.IsLocal || val2.IsBot) { return; } foreach (GearIDRange item in new List<GearIDRange> { new GearIDRange(data.gearStandard), new GearIDRange(data.gearSpecial), new GearIDRange(data.gearMelee), new GearIDRange(data.gearClass), new GearIDRange(data.gearHacking) }) { if (!CheckGearIDRange(item)) { PunishPlayer(val2); break; } } } } private static Dictionary<string, string> GearCompsHashLookup = new Dictionary<string, string>(); public override string Name => "Anti Gear Hack"; public override string Description => "Prevents clients from using modified gear."; public override GroupBase Group => (GroupBase)(object)GroupManager.Security; public override Type[] ExternalLocalizedTypes => new Type[1] { typeof(BasicPunishmentSettings) }; public static IArchiveLogger FeatureLogger { get; set; } [FeatureConfig] public static BasicPunishmentSettings Settings { get; set; } public override bool ShouldInit() { if (ArchiveMod.IsPlayingModded) { ((Feature)this).RequestDisable("Playing Modded"); return false; } return true; } public override void OnGameDataInitialized() { LoadData(); } public static bool PunishPlayer(SNet_Player player) { if ((Object)(object)player == (Object)null) { return true; } if (SharedUtils.IsFriend(player) && !Settings.PunishFriends) { FeatureLogger.Notice($"Friend \"{player.NickName}\" \"{player.Lookup}\" is using modified gears!"); return false; } switch (Settings.Punishment) { case BasicPunishmentSettings.PunishmentMode.KickAndBan: PlayerLobbyManagement.BanPlayer(player); break; case BasicPunishmentSettings.PunishmentMode.Kick: PlayerLobbyManagement.KickPlayer(player); break; } FeatureLogger.Notice($"Player \"{player.NickName}\" \"{player.Lookup}\" is using modified gears! ({Settings.Punishment})"); return true; } public static bool CheckIsValidWeaponGearIDRangeDataForPlayer(SNet_Player player) { if (!player.HasCharacterSlot) { return false; } Enumerator<GearIDRange> enumerator = ((Il2CppArrayBase<List<GearIDRange>>)(object)GearManager.Current.m_gearPerSlot)[player.PlayerSlotIndex()].GetEnumerator(); while (enumerator.MoveNext()) { if (CheckGearIDRange(enumerator.Current)) { return false; } } return true; } public static bool CheckGearIDRange(GearIDRange gearIDRange) { string input = gearIDRange.ToJSON(); string pattern = "(?<=Comps\":)(.*?)(?=,\"MatTrans\")"; string key = Utils.HashString(Regex.Match(input, pattern).Value); return GearCompsHashLookup.ContainsKey(key); } public static void LoadData() { GearCompsHashLookup.Clear(); foreach (PlayerOfflineGearDataBlock allBlock in GameDataBlockBase<PlayerOfflineGearDataBlock>.GetAllBlocks()) { string gearJSON = allBlock.GearJSON; string pattern = "(?<=Comps\":)(.*?)(?=,\"MatTrans\")"; string key = Utils.HashString(Regex.Match(gearJSON, pattern).Value); GearCompsHashLookup[key] = gearJSON; } } } [EnableFeatureByDefault] [RundownConstraint(/*Could not decode attribute arguments.*/)] public class AntiSpawn : Feature { public unsafe delegate void Original_InternalSpawnRequestFromSlaveCallback(IntPtr self, IntPtr spawnData, Il2CppMethodInfo* methodInfo); private static bool _hasBeenPatched = false; private static Original_InternalSpawnRequestFromSlaveCallback _originalMethod_InternalSpawnRequestFromSlaveCallback_pEnemySpawnData; private unsafe static Original_InternalSpawnRequestFromSlaveCallback _detourMethod_InternalSpawnRequestFromSlaveCallback_pEnemySpawnData = InternalSpawnRequestFromSlaveCallback_pEnemySpawnData_Replacement; private static Original_InternalSpawnRequestFromSlaveCallback _originalMethod_InternalSpawnRequestFromSlaveCallback_pEnemyGroupSpawnData; private unsafe static Original_InternalSpawnRequestFromSlaveCallback _detourMethod_InternalSpawnRequestFromSlaveCallback_pEnemyGroupSpawnData = InternalSpawnRequestFromSlaveCallback_pEnemyGroupSpawnData_Replacement; public override string Name => "Anti Spawn"; public override string Description => "Prevents clients from spawning in enemies."; public override GroupBase Group => (GroupBase)(object)GroupManager.Security; public override Type[] ExternalLocalizedTypes => new Type[1] { typeof(BasicPunishmentSettings) }; public static IArchiveLogger FeatureLogger { get; set; } public static bool IsEnabled { get; set; } [FeatureConfig] public static BasicPunishmentSettings Settings { get; set; } public override void OnEnable() { OneTimePatch(); } private static void OneTimePatch() { if (!_hasBeenPatched) { LoaderWrapper.ApplyNativeHook<SNet_ReplicationManager<pEnemySpawnData, EnemyReplicator>, Original_InternalSpawnRequestFromSlaveCallback>("InternalSpawnRequestFromSlaveCallback", typeof(void).FullName, new string[1] { typeof(pEnemySpawnData).FullName }, _detourMethod_InternalSpawnRequestFromSlaveCallback_pEnemySpawnData, ref _originalMethod_InternalSpawnRequestFromSlaveCallback_pEnemySpawnData); LoaderWrapper.ApplyNativeHook<SNet_ReplicationManager<pEnemyGroupSpawnData, SNet_DynamicReplicator<pEnemyGroupSpawnData>>, Original_InternalSpawnRequestFromSlaveCallback>("InternalSpawnRequestFromSlaveCallback", typeof(void).FullName, new string[1] { typeof(pEnemyGroupSpawnData).FullName }, _detourMethod_InternalSpawnRequestFromSlaveCallback_pEnemyGroupSpawnData, ref _originalMethod_InternalSpawnRequestFromSlaveCallback_pEnemyGroupSpawnData); _hasBeenPatched = true; } } public unsafe static void InternalSpawnRequestFromSlaveCallback_pEnemyGroupSpawnData_Replacement(IntPtr self, IntPtr spawnData, Il2CppMethodInfo* methodInfo) { if (IsEnabled && SNet.IsMaster && !SNet.Capture.IsCheckpointRecall) { bool flag = true; SNet_Player player = default(SNet_Player); if (SNet.Replication.TryGetLastSender(ref player, false)) { flag = PunishPlayer(player); } if (flag) { FeatureLogger.Fail("Cancelled enemy spawn!"); return; } } _originalMethod_InternalSpawnRequestFromSlaveCallback_pEnemyGroupSpawnData(self, spawnData, methodInfo); } public unsafe static void InternalSpawnRequestFromSlaveCallback_pEnemySpawnData_Replacement(IntPtr self, IntPtr spawnData, Il2CppMethodInfo* methodInfo) { if (IsEnabled && SNet.IsMaster && !SNet.Capture.IsCheckpointRecall) { bool flag = true; SNet_Player player = default(SNet_Player); if (SNet.Replication.TryGetLastSender(ref player, false)) { flag = PunishPlayer(player); } if (flag) { FeatureLogger.Fail("Cancelled enemy spawn!"); return; } } _originalMethod_InternalSpawnRequestFromSlaveCallback_pEnemySpawnData(self, spawnData, methodInfo); } public static bool PunishPlayer(SNet_Player player) { if ((Object)(object)player == (Object)null) { return true; } if (SharedUtils.IsFriend(player) && !Settings.PunishFriends) { FeatureLogger.Notice("Friend \"" + player.NickName + "\" is spawning something in!"); return false; } switch (Settings.Punishment) { case BasicPunishmentSettings.PunishmentMode.KickAndBan: PlayerLobbyManagement.BanPlayer(player); break; case BasicPunishmentSettings.PunishmentMode.Kick: PlayerLobbyManagement.KickPlayer(player); break; } FeatureLogger.Notice($"Player \"{player.NickName}\" tried to spawn something! ({Settings.Punishment})"); return true; } } public class BasicPunishmentSettings { [Localized] public enum PunishmentMode { NoneAndLog, Kick, KickAndBan } [FSDisplayName("Punish Friends")] [FSDescription("If (Steam) Friends should be affected as well.")] public bool PunishFriends { get; set; } [FSDisplayName("Punishment")] [FSDescription("What to do with griefers.")] public PunishmentMode Punishment { get; set; } = PunishmentMode.Kick; } [EnableFeatureByDefault] public class PlayerLobbyManagement : Feature { public class LobbyManagementSettings { public class LobbyColorSettings { [FSDisplayName("Color the Square")] [FSDescription("Use the colors/settings below to color the square on the loadout screen next to the player names.")] public bool ColorizeLobbyBullet { get; set; } = true; [FSDisplayName("Use Nickname Color for Self")] public bool UseNicknameColorForSelf { get; set; } = true; [FSDisplayName("Random Colors for Self")] public bool RainbowPukeSelf { get; set; } [FSDisplayName("Random Colors for Others")] public bool RainbowPukeFriends { get; set; } [FSHeader("Colors", true)] [FSDisplayName("Default")] public SColor Default { get; set; } = new SColor(1f, 1f, 1f, (float?)null); [FSDisplayName("Friends")] public SColor Friends { get; set; } = new SColor(0.964f, 0.921f, 0.227f, (float?)null); [FSDisplayName("Bots")] public SColor Bots { get; set; } = new SColor(0.949f, 0.58f, 0f, (float?)null); [FSDisplayName("Banned")] public SColor Banned { get; set; } = new SColor(0.545f, 0f, 0f, (float?)null); [FSDisplayName("Self")] public SColor Self { get; set; } = new SColor(1f, 1f, 1f, (float?)null); } public class BanListEntry { [FSSeparator] [FSReadOnly(true)] [FSDisplayName("Name")] public string Name { get; set; } [FSReadOnly(true)] [FSDisplayName("SteamID")] public ulong SteamID { get; set; } [FSReadOnly(true)] [FSTimestamp("U")] [FSDisplayName("Banned on:")] public long Timestamp { get; set; } } public class RecentlyPlayedWithEntry { [FSSeparator] [FSReadOnly(true)] [FSDisplayName("Name")] public string Name { get; set; } [FSReadOnly(true)] [FSDisplayName("SteamID")] public ulong SteamID { get; set; } [FSReadOnly(true)] [FSTimestamp("U")] [FSDisplayName("First time played with:")] public long TimestampFirst { get; set; } [FSReadOnly(true)] [FSTimestamp("U")] [FSDisplayName("Last time played with:")] public long TimestampLast { get; set; } } [FSDisplayName("Names on Map open Steam Profile")] [FSDescription("If clicking a players name on the map screen should open their Steam Profile in your default browser.")] public bool NamesOnMapOpenSteamProfile { get; set; } [FSDisplayName("Open Profiles in Steam Overlay")] [FSDescription("Wheter to open profile links in the overlay or in the default OS browser.")] public bool PreferOpeningProfileLinksInSteamOverlay { get; set; } = true; [FSHeader("Lobby Color Settings", true)] [FSDisplayName("Lobby Colors")] public LobbyColorSettings LobbyColors { get; set; } = new LobbyColorSettings(); [FSHeader("Other", true)] [FSDisplayName("Recently Played With")] public List<RecentlyPlayedWithEntry> RecentlyPlayedWith { get; set; } = new List<RecentlyPlayedWithEntry>(); [FSDisplayName("Banned Players")] [FSDescription("Players who are on this list will not be able to join any games <b>you host</b>.")] public List<BanListEntry> BanList { get; set; } = new List<BanListEntry>(); } [Localized] public enum PlayerRelationShip { None, Self, Friend, Banned, Bot } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class SNet_SessionHub_SlaveWantsToJoin_Patch { public static bool Prefix(SNet_Player player) { if (SNet.IsMaster && IsPlayerBanned(player.Lookup)) { FeatureLogger.Notice("Banned player \"" + player.GetName() + "\" tried to join, their join request has been ignored!"); KickPlayer(player); return false; } return true; } } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class SNet_OnPlayerJoinedSessionHub_Patch { public static void Postfix(SNet_Player player) { AddRecentlyPlayedWith(player, joined: true); } } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class SNet_OnPlayerLeftSessionHub_Patch { public static void Prefix(SNet_Player player) { AddRecentlyPlayedWith(player, joined: false); } } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class CM_PlayerLobbyBar_UpdatePlayer_Patch { internal static Dictionary<int, BoxCollider2D> colliderMap = new Dictionary<int, BoxCollider2D>(); private static MethodAccessor<GUIX_ElementSprite> A_Unregister; private static MethodAccessor<GUIX_ElementSprite> A_Start; private static IValueAccessor<GUIX_ElementSprite, GUIX_Layer> A_layer; public static void Init() { A_Start = MethodAccessor<GUIX_ElementSprite>.GetAccessor("Start", (Type[])null, false); A_Unregister = MethodAccessor<GUIX_ElementSprite>.GetAccessor("Unregister", (Type[])null, false); A_layer = AccessorBase.GetValueAccessor<GUIX_ElementSprite, GUIX_Layer>("layer", false); } public static void ColorizeNickNameGUIX(GameObject nickNameGuix, PlayerRelationShip rel = PlayerRelationShip.None) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)nickNameGuix == (Object)null) { return; } foreach (Transform item in SharedUtils.DirectChildren(nickNameGuix.transform)) { GUIX_ElementSprite component = ((Component)item).GetComponent<GUIX_ElementSprite>(); SpriteRenderer component2 = ((Component)item).GetComponent<SpriteRenderer>(); if ((Object)(object)component2 != (Object)null) { if (!Settings.LobbyColors.ColorizeLobbyBullet) { component2.color = Color.white; } else if (rel != PlayerRelationShip.Self) { if (rel != PlayerRelationShip.Friend || !Settings.LobbyColors.RainbowPukeFriends) { goto IL_00cf; } component2.color = Random.ColorHSV(); } else if (Settings.LobbyColors.RainbowPukeSelf) { component2.color = Random.ColorHSV(); } else { if (!Settings.LobbyColors.UseNicknameColorForSelf || !FeatureManager.IsFeatureEnabled<Nickname>() || !Nickname.IsNicknameColorEnabled) { goto IL_00cf; } component2.color = Nickname.CurrentNicknameColor; } } goto IL_00db; IL_00cf: component2.color = GetRelationshipColor(rel); goto IL_00db; IL_00db: if (!((Object)(object)A_layer.Get(component) == (Object)null) && ((Component)component).gameObject.activeInHierarchy) { A_Unregister.Invoke(component); component.DynamicIndex = -1; A_layer.Set(component, (GUIX_Layer)null); A_Start.Invoke(component); } } } public static void Postfix(CM_PlayerLobbyBar __instance, SNet_Player player) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) if (player == null) { return; } SNet_Slot characterSlot = player.CharacterSlot; if (characterSlot == null) { return; } _ = characterSlot.index; if (false) { return; } int playerLobbyBarIndex = SharedUtils.GetPlayerLobbyBarIndex(__instance); GameObject gameObject = ((Component)__instance.m_nickText).gameObject; CM_Item CM_Item = gameObject.GetComponent<CM_Item>(); if ((Object)(object)CM_Item == (Object)null) { FeatureLogger.Debug($"Setting up player name button for slot index {playerLobbyBarIndex}"); BoxCollider2D val = gameObject.AddComponent<BoxCollider2D>(); val.size = new Vector2(447.2f, 52.8f); ((Collider2D)val).offset = new Vector2(160f, 1.6f); if (colliderMap.ContainsKey(playerLobbyBarIndex)) { colliderMap.Remove(playerLobbyBarIndex); } colliderMap.Add(playerLobbyBarIndex, val); CM_Item = gameObject.AddComponent<CM_Item>(); CM_Item.ID = playerLobbyBarIndex + 1; SharedUtils.SetCMItemEvents(CM_Item, (Action<int>)delegate(int id) { if (!((RectTransformComp)PopupWindow).IsVisible || ((CM_PopupOverlay)PopupWindow).ID != id) { SetupAndPlaceWindow(id, ((Component)CM_Item).transform); } else { ((RectTransformComp)PopupWindow).SetVisible(false); } }, (Action<int, bool>)null); } ColorizeNickNameGUIX(__instance.m_nickNameGuix, GetRelationship(player)); } } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class PUI_Inventory_UpdateAllSlots_Patch { internal static Dictionary<int, BoxCollider2D> colliderMap = new Dictionary<int, BoxCollider2D>(); public static void Postfix(PUI_Inventory __instance, SNet_Player player) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) if (player == null) { return; } SNet_Slot characterSlot = player.CharacterSlot; if (characterSlot == null) { return; } _ = characterSlot.index; if (false) { return; } int index = player.CharacterSlot.index; GameObject headerRoot = __instance.m_headerRoot; if ((Object)(object)headerRoot.GetComponent<CM_Item>() == (Object)null) { FeatureLogger.Debug($"Setting up player name button (map) for index {player.CharacterSlot.index}"); BoxCollider2D val = headerRoot.AddComponent<BoxCollider2D>(); val.size = new Vector2(400f, 40f); ((Collider2D)val).offset = new Vector2(-200f, 0f); if (colliderMap.ContainsKey(index)) { colliderMap.Remove(index); } colliderMap.Add(index, val); CM_Item obj = headerRoot.AddComponent<CM_Item>(); obj.ID = player.CharacterSlot.index + 1; SharedUtils.SetCMItemEvents(obj, (Action<int>)OnMapNameButtonPressed, (Action<int, bool>)null); } } } private static Dictionary<PlayerRelationShip, Color> _colorMap = new Dictionary<PlayerRelationShip, Color>(); private static CM_ScrollWindow _popupWindow = null; public override string Name => "Player Lobby Management"; public override GroupBase Group => (GroupBase)(object)GroupManager.Security; public override string Description => "Allows you to open a players steam profile by clicking on their name as well as kick and ban players as host."; public static ILocalizationService Localization { get; set; } public static IArchiveLogger FeatureLogger { get; set; } [FeatureConfig] public static LobbyManagementSettings Settings { get; set; } private static CM_ScrollWindow PopupWindow { get { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_popupWindow == (Object)null) { FeatureLogger.Debug("Creating PopupWindow ..."); _popupWindow = Object.Instantiate<CM_ScrollWindow>(PageSettingsData.PopupWindow, (Transform)(object)((CM_PageBase)CM_PageLoadout.Current).m_movingContentHolder); Object.DontDestroyOnLoad((Object)(object)_popupWindow); ((Object)_popupWindow).name = "PlayerLobbyManagement_PopupWindow_PlayerManagement"; ((RectTransformComp)PopupWindow).Setup(); OpenSteamItem = CreatePopupItem(Localization.GetById(1u, (string)null), OnNameButtonPressed); SharedUtils.ChangeColorCMItem(OpenSteamItem, ModSettings.GREEN, (Color?)null); SharedUtils.SetHoldDuration(SharedUtils.TryCastTo<CM_TimedButton>((Il2CppObjectBase)(object)OpenSteamItem), 0.5f); IsFriendItem = CreatePopupItem(Localization.GetById(2u, (string)null), delegate { }); SharedUtils.ChangeColorCMItem(IsFriendItem, GetRelationshipColor(PlayerRelationShip.Friend), (Color?)null); SharedUtils.SetHoldDuration(SharedUtils.TryCastTo<CM_TimedButton>((Il2CppObjectBase)(object)IsFriendItem), 100f); ((Behaviour)((Component)IsFriendItem).GetComponent<Collider2D>()).enabled = false; KickPlayerItem = CreatePopupItem(Localization.GetById(3u, (string)null), KickPlayerButtonPressed); SharedUtils.ChangeColorCMItem(KickPlayerItem, ModSettings.ORANGE, (Color?)null); SharedUtils.SetHoldDuration(SharedUtils.TryCastTo<CM_TimedButton>((Il2CppObjectBase)(object)KickPlayerItem), 2f); BanPlayerItem = CreatePopupItem(Localization.GetById(4u, (string)null), BanPlayerButtonPressed); SharedUtils.ChangeColorCMItem(BanPlayerItem, ModSettings.RED, (Color?)null); SharedUtils.SetHoldDuration(SharedUtils.TryCastTo<CM_TimedButton>((Il2CppObjectBase)(object)BanPlayerItem), 4f); CM_Item val = CreatePopupItem("Spacer, should not be visible", delegate { }); List<iScrollWindowContent> val2 = SharedUtils.NewListForGame<iScrollWindowContent>(); val2.Add(((Component)OpenSteamItem).GetComponentInChildren<iScrollWindowContent>()); val2.Add(((Component)IsFriendItem).GetComponentInChildren<iScrollWindowContent>()); val2.Add(((Component)val).GetComponentInChildren<iScrollWindowContent>()); val2.Add(((Component)KickPlayerItem).GetComponentInChildren<iScrollWindowContent>()); val2.Add(((Component)BanPlayerItem).GetComponentInChildren<iScrollWindowContent>()); PopupWindow.SetContentItems(val2, 5f); ((Component)val).gameObject.SetActive(false); ((Component)val).transform.position = new Vector3(-5000f, 0f, 0f); ((RectTransformComp)PopupWindow).SetSize(new Vector2(350f, (float)(58 * val2.Count))); ((RectTransformComp)PopupWindow).SetVisible(false); PopupWindow.SetHeader("Player options"); } return _popupWindow; } } internal static CM_Item OpenSteamItem { get; set; } internal static CM_Item IsFriendItem { get; set; } internal static CM_Item KickPlayerItem { get; set; } internal static CM_Item BanPlayerItem { get; set; } public override void OnEnable() { foreach (BoxCollider2D value in CM_PlayerLobbyBar_UpdatePlayer_Patch.colliderMap.Values) { if ((Object)(object)value != (Object)null) { ((Behaviour)value).enabled = true; } } foreach (BoxCollider2D value2 in PUI_Inventory_UpdateAllSlots_Patch.colliderMap.Values) { if ((Object)(object)value2 != (Object)null) { ((Behaviour)value2).enabled = true; } } SetupColorMap(); } public override void OnDisable() { foreach (BoxCollider2D value in CM_PlayerLobbyBar_UpdatePlayer_Patch.colliderMap.Values) { if ((Object)(object)value != (Object)null) { ((Behaviour)value).enabled = false; } } foreach (BoxCollider2D value2 in PUI_Inventory_UpdateAllSlots_Patch.colliderMap.Values) { if ((Object)(object)value2 != (Object)null) { ((Behaviour)value2).enabled = false; } } } public override void OnFeatureSettingChanged(FeatureSetting setting) { SetupColorMap(); } public static void SetupColorMap() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) _colorMap.Clear(); PlayerRelationShip[] array = Enum.GetValues(typeof(PlayerRelationShip)) as PlayerRelationShip[]; foreach (PlayerRelationShip playerRelationShip in array) { _colorMap.Add(playerRelationShip, GetRelationshipColor(playerRelationShip)); } } public static PlayerRelationShip GetRelationship(PlayerAgent player) { return GetRelationship((player != null) ? player.Owner : null); } public static PlayerRelationShip GetRelationship(SNet_Player player) { if ((Object)(object)player == (Object)null) { return PlayerRelationShip.None; } if (SharedUtils.SafeIsBot(player)) { return PlayerRelationShip.Bot; } if (player.IsLocal) { return PlayerRelationShip.Self; } if (IsPlayerBanned(player.Lookup)) { return PlayerRelationShip.Banned; } if (SharedUtils.IsFriend(player)) { return PlayerRelationShip.Friend; } return PlayerRelationShip.None; } public static Color GetRelationshipColor(PlayerRelationShip rel) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (_colorMap.TryGetValue(rel, out var value)) { return value; } return (Color)(rel switch { PlayerRelationShip.Self => SColorExtensions.ToUnityColor(Settings.LobbyColors.Self), PlayerRelationShip.Bot => SColorExtensions.ToUnityColor(Settings.LobbyColors.Bots), PlayerRelationShip.Friend => SColorExtensions.ToUnityColor(Settings.LobbyColors.Friends), PlayerRelationShip.Banned => SColorExtensions.ToUnityColor(Settings.LobbyColors.Banned), _ => SColorExtensions.ToUnityColor(Settings.LobbyColors.Default), }); } public static bool IsPlayerBanned(ulong lookup) { return Settings.BanList.Any((LobbyManagementSettings.BanListEntry bp) => bp.SteamID == lookup); } internal static void OnMapNameButtonPressed(int id) { if (Settings.NamesOnMapOpenSteamProfile) { SNet_Player player = default(SNet_Player); if (!SharedUtils.TryGetPlayerByCharacterIndex(id, ref player)) { FeatureLogger.Debug($"No player found for character index {id - 1}."); } else { OpenSteamProfileFor(player); } } } internal static void OnNameButtonPressed(int id) { ((RectTransformComp)PopupWindow).SetVisible(false); SNet_Player player = default(SNet_Player); if (!SharedUtils.TryGetPlayerByPlayerLobbyBarIndex(id - 1, ref player)) { FeatureLogger.Debug($"No player found for index {id - 1}."); } else { OpenSteamProfileFor(player); } } public static void OpenSteamProfileFor(SNet_Player player) { if ((Object)(object)player == (Object)null) { FeatureLogger.Debug("Player was null!"); return; } if (SharedUtils.SafeIsBot(player)) { FeatureLogger.Notice("The Bot has no steam profile!"); return; } FeatureLogger.Info($"Opening Steam profile for player \"{player.NickName}\" ({player.Lookup})"); string text = $"https://steamcommunity.com/profiles/{player.Lookup}"; if (Settings.PreferOpeningProfileLinksInSteamOverlay && SteamUtils.IsOverlayEnabled()) { SteamFriends.ActivateGameOverlayToWebPage(text, (EActivateGameOverlayToWebPageMode)0); } else { Application.OpenURL(text); } } internal static void KickPlayerButtonPressed(int playerID) { ((RectTransformComp)PopupWindow).SetVisible(false); SNet_Player val = default(SNet_Player); if (SNet.IsMaster && SharedUtils.TryGetPlayerByPlayerLobbyBarIndex(playerID - 1, ref val)) { if (SharedUtils.SafeIsBot(val)) { FeatureLogger.Notice("Use the built in button below to remove bots!"); return; } FeatureLogger.Notice($"Kicking player \"{val.GetName()}\" Nick:\"{val.NickName}\" ..."); KickPlayer(val); } } internal static void BanPlayerButtonPressed(int playerID) { SNet_Player val = default(SNet_Player); if (!SharedUtils.TryGetPlayerByPlayerLobbyBarIndex(playerID - 1, ref val)) { return; } if (val.IsLocal) { BanPlayerItem.SetText(" You can't ban yourself, silly!"); return; } ((RectTransformComp)PopupWindow).SetVisible(false); if (SharedUtils.SafeIsBot(val)) { FeatureLogger.Notice("You can't ban a bot!"); } else if (!IsPlayerBanned(val.Lookup)) { BanPlayer(val); } else { UnbanPlayer(val); } } public static bool BanPlayer(SNet_Player player, bool kickPlayer = true) { if ((Object)(object)player == (Object)null) { return false; } if (!IsPlayerBanned(player.Lookup)) { Settings.BanList.Add(new LobbyManagementSettings.BanListEntry { Name = player.GetName(), SteamID = player.Lookup, Timestamp = DateTime.UtcNow.Ticks }); FeatureLogger.Fail($"Player has been added to list of banned players: Name:\"{player.GetName()}\" SteamID:\"{player.Lookup}\""); Feature.MarkSettingsAsDirty<LobbyManagementSettings>(Settings); if (kickPlayer) { KickPlayer(player); } return true; } return false; } public static bool UnbanPlayer(SNet_Player player) { return UnbanPlayer((player != null) ? player.Lookup : ulong.MaxValue); } public static bool UnbanPlayer(ulong playerID) { if (playerID == ulong.MaxValue) { return false; } LobbyManagementSettings.BanListEntry banListEntry = Settings.BanList.FirstOrDefault((LobbyManagementSettings.BanListEntry entry) => entry.SteamID == playerID); if (banListEntry != null) { Settings.BanList.Remove(banListEntry); FeatureLogger.Success($"Player has been removed from the list of banned players: Name:\"{banListEntry.Name}\" SteamID:\"{banListEntry.SteamID}\""); Feature.MarkSettingsAsDirty<LobbyManagementSettings>(Settings); return true; } return false; } public static void KickPlayer(SNet_Player player) { if (SNet.IsMaster) { SNet.Sync.KickPlayer(player, (SNet_PlayerEventReason)2); } } private static CM_Item CreatePopupItem(string text, Action<int> onClick, int id = 0) { CM_Item val = SharedUtils.TryCastTo<CM_Item>((Il2CppObjectBase)(object)GOUtil.SpawnChildAndGetComp<iScrollWindowContent>(UIHelper.PopupItemPrefab, ((Component)_popupWindow).transform)); val.SetupCMItem(); if (id != 0) { val.ID = id; } val.SetText(text); SharedUtils.SetCMItemEvents(val, onClick, (Action<int, bool>)null); ((RectTransformComp)val).ForcePopupLayer(true, (GameObject)null); return val; } public static void SetupAndPlaceWindow(int playerID, Transform pos) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) SNet_Player val = default(SNet_Player); if (!SharedUtils.TryGetPlayerByPlayerLobbyBarIndex(playerID - 1, ref val)) { return; } string name = val.GetName(); bool flag = SharedUtils.SafeIsBot(val); ((Component)KickPlayerItem).gameObject.SetActive(!flag); ((Component)BanPlayerItem).gameObject.SetActive(!flag); ((Component)OpenSteamItem).gameObject.SetActive(!flag); if (flag) { ((Component)IsFriendItem).gameObject.SetActive(true); IsFriendItem.SetText(Localization.GetById(6u, (string)null)); SharedUtils.ChangeColorCMItem(IsFriendItem, GetRelationshipColor(PlayerRelationShip.Bot), (Color?)null); ShowWindow(name, pos, playerID); return; } ((CM_PopupOverlay)PopupWindow).SetupFromButton(new iCellMenuPopupController(((Il2CppObjectBase)CM_PageLoadout.Current).Pointer), (CM_PageBase)(object)CM_PageLoadout.Current); SNet_Friend val2 = default(SNet_Friend); bool flag2 = SNet.Friends.TryGetFriend(val.Lookup, ref val2); bool flag3 = IsPlayerBanned(val.Lookup); ((Component)IsFriendItem).gameObject.SetActive(flag2 || flag3); if (flag3) { IsFriendItem.SetText(Localization.GetById(5u, (string)null)); SharedUtils.ChangeColorCMItem(IsFriendItem, GetRelationshipColor(PlayerRelationShip.Banned), (Color?)null); } else if (flag2) { IsFriendItem.SetText(Localization.GetById(2u, (string)null)); SharedUtils.ChangeColorCMItem(IsFriendItem, GetRelationshipColor(PlayerRelationShip.Friend), (Color?)null); } KickPlayerItem.SetText(Localization.Format(7u, " Kick {0}", new object[1] { name })); if (val.IsMaster || !SNet.IsMaster) { ((Behaviour)((Component)KickPlayerItem).GetComponent<Collider2D>()).enabled = false; SharedUtils.ChangeColorCMItem(KickPlayerItem, ModSettings.DISABLED, (Color?)null); BanPlayerItem.SetText(Localization.Format(8u, " Ban {0}", new object[1] { name })); } else { ((Behaviour)((Component)KickPlayerItem).GetComponent<Collider2D>()).enabled = true; SharedUtils.ChangeColorCMItem(KickPlayerItem, ModSettings.ORANGE, (Color?)null); BanPlayerItem.SetText(Localization.Format(9u, " Ban and kick {0}", new object[1] { name })); } if (val.IsLocal) { SharedUtils.ChangeColorCMItem(BanPlayerItem, ModSettings.DISABLED, (Color?)null); } else if (flag3) { BanPlayerItem.SetText(Localization.Format(10u, " Unban {0}", new object[1] { name })); SharedUtils.ChangeColorCMItem(BanPlayerItem, ModSettings.GREEN, (Color?)null); } else { SharedUtils.ChangeColorCMItem(BanPlayerItem, ModSettings.RED, (Color?)null); } ShowWindow(name, pos, playerID); } private static void ShowWindow(string header, Transform pos, int playerID) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) PopupWindow.SetHeader(header); ((CM_PopupOverlay)PopupWindow).ID = playerID; ((Component)PopupWindow).transform.position = pos.position + new Vector3(200f, 0f, 0f); OpenSteamItem.ID = playerID; KickPlayerItem.ID = playerID; BanPlayerItem.ID = playerID; ((RectTransformComp)PopupWindow).SetVisible(true); } private static void AddRecentlyPlayedWith(SNet_Player player, bool joined) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) LobbyManagementSettings.RecentlyPlayedWithEntry recentlyPlayedWithEntry = Settings.RecentlyPlayedWith.FirstOrDefault((LobbyManagementSettings.RecentlyPlayedWithEntry entry) => entry.SteamID == player.Lookup); if (recentlyPlayedWithEntry != null) { if (joined) { if (!player.IsBot) { SteamFriends.SetPlayedWith(new CSteamID(player.Lookup)); } FeatureLogger.Notice($"{player.NickName} joined Session, last time played with them {DateTime.UtcNow - new DateTime(recentlyPlayedWithEntry.TimestampLast):d' day(s) and 'hh':'mm':'ss} ago."); } else { FeatureLogger.Debug(player.NickName + " left Session, saving Timestamp."); } recentlyPlayedWithEntry.TimestampLast = DateTime.UtcNow.Ticks; Feature.MarkSettingsAsDirty<LobbyManagementSettings>(Settings); } else { long ticks = DateTime.UtcNow.Ticks; Settings.RecentlyPlayedWith.Add(new LobbyManagementSettings.RecentlyPlayedWithEntry { Name = player.NickName, SteamID = player.Lookup, TimestampFirst = ticks, TimestampLast = ticks }); Feature.MarkSettingsAsDirty<LobbyManagementSettings>(Settings); } } } } namespace TheArchive.Features.QoL { public class IntroSkip : Feature { [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class CM_PageIntro_Update_Patch { private static bool _injectPressed; private static MethodAccessor<CM_PageIntro> _onSkip; public static void Init() { _onSkip = MethodAccessor<CM_PageIntro>.GetAccessor("OnSkip", (Type[])null, false); } [IsPostfix] [RundownConstraint(/*Could not decode attribute arguments.*/)] public static void PostfixR4OrLater(CM_PageIntro __instance) { CheckAndSkipIfReady(__instance, __instance.m_startupLoaded, __instance.m_enemiesLoaded, __instance.m_sharedLoaded); } public static void CheckAndSkipIfReady(CM_PageIntro __instance, bool ___m_startupLoaded, bool ___m_enemiesLoaded, bool ___m_sharedLoaded) { if (___m_startupLoaded && ___m_enemiesLoaded && ___m_sharedLoaded && ItemSpawnManager.ReadyToSpawnItems && IsProgressionFileReady() && !_injectPressed) { FeatureLogger.Notice("Automatically pressing the Inject button ..."); _onSkip.Invoke(__instance); __instance.EXT_PressInject(-1); _injectPressed = true; } } } public override string Name => "Skip Intro"; public override GroupBase Group => (GroupBase)(object)GroupManager.QualityOfLife; public override string Description => "Automatically presses inject at the start of the game"; public static IArchiveLogger FeatureLogger { get; set; } public static bool IsProgressionFileReady() { return RundownManager.RundownProgressionReady; } } public class L4DStylePacks : Feature { [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class ResourcePackFirstPerson_UpdateInteraction_Patch { private static MethodAccessor<ResourcePackFirstPerson> A_UpdateInteractionActionName; private static IValueAccessor<Interact_Timed, PlayerAgent> A_m_interactionSourceAgent; private static IValueAccessor<Interact_Timed, float> A_m_interactionTimer; public static void Init() { A_UpdateInteractionActionName = MethodAccessor<ResourcePackFirstPerson>.GetAccessor("UpdateInteractionActionName", (Type[])null, false); if (!Is.R6OrLater) { A_m_interactionSourceAgent = AccessorBase.GetValueAccessor<Interact_Timed, PlayerAgent>("m_interactionSourceAgent", false); A_m_interactionTimer = AccessorBase.GetValueAccessor<Interact_Timed, float>("m_interactionTimer", false); } } public static bool Prefix(ResourcePackFirstPerson __instance) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) iResourcePackReceiver val = __instance.m_actionReceiver; iResourcePackReceiver lastActionReceiver = __instance.m_lastActionReceiver; eResourceContainerSpawnType packType = __instance.m_packType; Interact_ManualTimedWithCallback interactApplyResource = __instance.m_interactApplyResource; bool flag = false; bool flag2 = false; InputAction input = (InputAction)0; if (InputMapper.GetButtonDown.Invoke((InputAction)8, ((Item)__instance).Owner.InputFilter)) { flag = true; if (!((Interact_Timed)interactApplyResource).TimerIsActive) { val = (__instance.m_actionReceiver = SharedUtils.CastTo<iResourcePackReceiver>((Il2CppObjectBase)(object)((Item)__instance).Owner)); } input = (InputAction)8; } if (InputMapper.GetButton.Invoke((InputAction)7, ((Item)__instance).Owner.InputFilter)) { flag2 = true; flag = true; RaycastHit val3 = default(RaycastHit); if (!((Interact_Timed)interactApplyResource).TimerIsActive && Physics.Raycast(((Item)__instance).Owner.FPSCamera.Position, ((Item)__instance).Owner.FPSCamera.Forward, ref val3, 2.4f, LayerManager.MASK_GIVE_RESOURCE_PACK)) { iResourcePackReceiver componentInParent = ((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent<iResourcePackReceiver>(); if (componentInParent != null) { val = (__instance.m_actionReceiver = componentInParent); } } input = ((!val.IsLocallyOwned) ? ((InputAction)7) : ((InputAction)8)); } if (val == null || (!((Interact_Timed)interactApplyResource).TimerIsActive && !flag)) { val = (__instance.m_actionReceiver = SharedUtils.CastTo<iResourcePackReceiver>((Il2CppObjectBase)(object)((Item)__instance).Owner)); if (Is.R6OrLater) { OnInteractorStateChangedR6Plus(interactApplyResource, ((Item)__instance).Owner, state: false); } ((Interact_Base)interactApplyResource).PlayerSetSelected(false, ((Item)__instance).Owner); } bool flag3 = NeedsResource(val, packType); if (!((Interact_Timed)interactApplyResource).TimerIsActive && val != lastActionReceiver) { if (val.IsLocallyOwned) { A_UpdateInteractionActionName.Invoke(__instance, new object[2] { Localization.GetById(7u, (string)null), true }); interactApplyResource.m_input = input; } else { A_UpdateInteractionActionName.Invoke(__instance, new object[2] { val.InteractionName, false }); interactApplyResource.m_input = input; if (flag3) { if (Is.R6OrLater) { OnInteractorStateChangedR6Plus(interactApplyResource, ((Item)__instance).Owner, state: true); } else { ActivateTimerR5AndBelow(interactApplyResource, ((Item)__instance).Owner); } } } __instance.m_lastActionReceiver = val; } bool timerIsActive = ((Interact_Timed)interactApplyResource).TimerIsActive; interactApplyResource.ManualUpdateWithCondition(flag3, ((Item)__instance).Owner, flag3 && !val.IsLocallyOwned); bool timerIsActive2 = ((Interact_Timed)interactApplyResource).TimerIsActive; if (!timerIsActive && timerIsActive2 && Is.R6OrLater) { SendGenericInteractR6Plus(__instance, val); } if (timerIsActive && !timerIsActive2) { PlayerAgent owner = ((Item)__instance).Owner; __instance.m_actionReceiver = ((owner != null) ? SharedUtils.CastTo<iResourcePackReceiver>((Il2CppObjectBase)(object)owner) : null); } if (!flag3 && flag && __instance.m_lastButtonDown != flag && (!flag2 || !val.IsLocallyOwned)) { SharedUtils.SafePost(((ItemEquippable)__instance).Sound, EVENTS.BUTTONGENERICBLIPDENIED, true); ShowDoesNotNeedResourcePrompt(val, packType); } __instance.m_lastButtonDown = flag; return false; } private static void ActivateTimerR5AndBelow(Interact_ManualTimedWithCallback timer, PlayerAgent agent) { A_m_interactionSourceAgent.Set((Interact_Timed)(object)timer, agent); ((Interact_Timed)timer).SetTimerActive(true); A_m_interactionTimer.Set((Interact_Timed)(object)timer, Clock.Delta); } [MethodImpl(MethodImplOptions.NoInlining)] private static void SendGenericInteractR6Plus(ResourcePackFirstPerson __instance, iResourcePackReceiver packReceiver) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) TypeEnum val = (TypeEnum)(packReceiver.IsLocallyOwned ? 6 : 5); ((Item)__instance).Owner.Sync.SendGenericInteract(val, false); } [MethodImpl(MethodImplOptions.NoInlining)] private static void OnInteractorStateChangedR6Plus(Interact_ManualTimedWithCallback timer, PlayerAgent sourceAgent, bool state) { ((Interact_Timed)timer).OnInteractorStateChanged(sourceAgent, state); } } private static MethodAccessor<InteractionGuiLayer> A_SetTimedMessage; public override string Name => "L4D Style Resource Packs"; public override GroupBase Group => (GroupBase)(object)GroupManager.QualityOfLife; public override string Description => "Use left and right mouse buttons to apply resource packs instead of E.\n\nLeft mouse = yourself\nRight mouse = other players\n\n<color=orange>[R4+]</color> You're able to hold down M2 and it will start applying to a receiver under your croshair if in range automatically\n\n<#f00><u>/!\\</u> Make sure to <color=orange><u>disable</u></color> the vanilla game setting <color=orange>Gameplay > Separate Use Keybinds</color> for this Feature to work!</color>"; public static ILocalizationService Localization { get; set; } public static IArchiveLogger FeatureLogger { get; set; } public override void Init() { if (!Is.R2OrLater) { A_SetTimedMessage = MethodAccessor<InteractionGuiLayer>.GetAccessor("SetTimedMessage", (Type[])null, false); } } public static bool NeedsResource(iResourcePackReceiver receiver, eResourceContainerSpawnType packType) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected I4, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 if (receiver == null) { return false; } switch ((int)packType) { default: if ((int)packType == 9) { if (Is.R2OrLater) { return NeedsDisinfect(receiver); } return false; } return false; case 0: return receiver.NeedHealth(); case 1: return receiver.NeedWeaponAmmo(); case 2: return receiver.NeedToolAmmo(); } } [MethodImpl(MethodImplOptions.NoInlining)] private static bool NeedsDisinfect(iResourcePackReceiver receiver) { return receiver.NeedDisinfection(); } public static void ShowDoesNotNeedResourcePrompt(iResourcePackReceiver receiver, eResourceContainerSpawnType packType) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected I4, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 string text = (receiver.IsLocallyOwned ? Localization.GetById(1u, (string)null) : Localization.Format(2u, "{0} DOES", new object[1] { receiver.InteractionName })); switch ((int)packType) { default: if ((int)packType == 9) { text += Localization.GetById(6u, (string)null); } break; case 1: text += Localization.GetById(3u, (string)null); break; case 2: text += Localization.GetById(4u, (string)null); break; case 0: text += Localization.GetById(5u, (string)null); break; } SetTimedInteractionPrompt(text, 1.4f); } private static void SetTimedInteractionPrompt(string text, float time) { if (Is.R2OrLater) { TimedInteractionPromptR2Plus(text, time); return; } A_SetTimedMessage.Invoke(GuiManager.InteractionLayer, new object[3] { text, time, (object)(ePUIMessageStyle)1 }); } [MethodImpl(MethodImplOptions.NoInlining)] private static void TimedInteractionPromptR2Plus(string text, float time) { GuiManager.InteractionLayer.SetTimedInteractionPrompt(text, time, (ePUIMessageStyle)0); } } public class LastUsedGearSwitcher : Feature { public class LastUsedGearSwitcherSettings { [FSDisplayName("Quick Swap Key")] [FSDescription("Press this key to switch to the previously wielded gear.")] public KeyCode QuickSwitchKey { get; set; } = (KeyCode)120; [FSHide] [FSDisplayName("Prints Debug Info")] [FSDescription("Prints debug info to the console")] public bool DebugLog { get; set; } } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class PlayerSync_WantsToWieldSlot_Patch { public static void Prefix(PlayerSync __instance, InventorySlot slot) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) IReplicator replicator = __instance.Replicator; bool? obj; if (replicator == null) { obj = null; } else { SNet_Player owningPlayer = replicator.OwningPlayer; obj = ((owningPlayer != null) ? new bool?(owningPlayer.IsLocal) : null); } bool? flag = obj; if (flag.GetValueOrDefault()) { InventorySlot wieldedSlot = __instance.GetWieldedSlot(); if (Settings.DebugLog) { FeatureLogger.Debug($"PlayerSync.WantsToWieldSlot: currentSlot:{wieldedSlot} -> slot:{slot} | _previousInventorySlot:{_previousInventorySlot}"); } if (wieldedSlot != slot) { _previousInventorySlot = wieldedSlot; } } } } private static readonly eFocusState _eFocusState_FPS = Utils.GetEnumFromName<eFocusState>("FPS"); private static eFocusState _eFocusState_FPS_CommunicationDialog; private static InventorySlot _previousInventorySlot = (InventorySlot)10; public override string Name => "Last Used Gear Switcher"; public override GroupBase Group => (GroupBase)(object)GroupManager.QualityOfLife; public override string Description => "Allows you to swap between the last two used weapons via a keypress"; public static IArchiveLogger FeatureLogger { get; set; } [FeatureConfig] public static LastUsedGearSwitcherSettings Settings { get; set; } public override void Init() { if (Is.R6OrLater) { Utils.TryGetEnumFromName<eFocusState>("FPS_CommunicationDialog", ref _eFocusState_FPS_CommunicationDialog); } } public override void Update() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if ((FocusStateManager.CurrentState == _eFocusState_FPS || (Is.R6OrLater && FocusStateManager.CurrentState == _eFocusState_FPS_CommunicationDialog)) && Input.GetKeyDown(Settings.QuickSwitchKey)) { SwitchToPreviousSlot(); } } public static void SwitchToPreviousSlot() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); InventorySlot previousInventorySlot = _previousInventorySlot; InventorySlot wieldedSlot = localPlayerAgent.Inventory.WieldedSlot; if (Settings.DebugLog) { FeatureLogger.Debug($"Switching to previous slot: {wieldedSlot} -> {previousInventorySlot}"); } localPlayerAgent.Sync.WantsToWieldSlot(previousInventorySlot, false); } } public class LoadoutRandomizer : Feature { public class LoadoutRandomizerSettings { [Localized] public enum InventorySlots { Primary, Special, Tool, Melee } [Localized] public enum RandomizerMode { True, NoDuplicate } [FSDisplayName("Do not randomize")] public List<InventorySlots> ExcludedSlots { get; set; } = new List<InventorySlots>(); [FSDisplayName("Mode")] public RandomizerMode Mode { get; set; } = RandomizerMode.NoDuplicate; } [ArchivePatch(/*Could not decode attribute arguments.*/)] public static class CM_PageLoadout_SetupPatch { public static void Postfix(CM_PageLoadout __instance) { SetupViaInstance(__instance); } } private static CM_TimedButton _loadoutRandomizerButton; private static bool _hasBeenSetup = false; private static eGameStateName _eGameStateName_Lobby; private static readonly Dictionary<InventorySlot, LoadoutRandomizerSettings.InventorySlots> _invSlotMap = new Dictionary<InventorySlot, LoadoutRandomizerSettings.InventorySlots> { { Utils.GetEnumFromName<InventorySlot>("GearMelee"), LoadoutRandomizerSettings.InventorySlots.Melee }, { Utils.GetEnumFromName<InventorySlot>("GearStandard"), LoadoutRandomizerSettings.InventorySlots.Primary }, { Utils.GetEnumFromName<InventorySlot>("GearSpecial"), LoadoutRandomizerSettings.InventorySlots.Special }, { Utils.GetEnumFromName<InventorySlot>("GearClass"), LoadoutRandomizerSettings.InventorySlots.Tool } }; public override string Name => "Loadout Randomizer"; public override GroupBase Group => (GroupBase)(object)GroupManager.QualityOfLife; public override string Description => "Adds a Loadout Randomizer button onto the loadout screen.\nSelect which gear to randomize via the settings below."; public static IArchiveLogger FeatureLogger { get; set; } [FeatureConfig] public static LoadoutRandomizerSettings Config { get; set; } public static bool IsEnabled { get; set; } public override void Init() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) _eGameStateName_Lobby = Utils.GetEnumFromName<eGameStateName>("Lobby"); } public override void OnEnable() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between I4 and Unknown if (!_hasBeenSetup) { SetupViaInstance(CM_PageLoadout.Current); } if ((byte)Feature.CurrentGameState == (int)_eGameStateName_Lobby) { SetButtonActive(); } } public override void OnDisable() { CM_TimedButton loadoutRandomizerButton = _loadoutRandomizerButton; if (loadoutRandomizerButton != null) { GameObject gameObject = ((Component)loadoutRandomizerButton).gameObject; if (gameObject != null) { gameObject.SetActive(false); } } } public void OnGameStateChanged(eGameStateName state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (state == _eGameStateName_Lobby) { SetButtonActive(); } else { SetButtonInactive(); } } public static void SetupViaInstance(CM_PageLoadout pageLoadout) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)pageLoadout == (Object)null)) { CM_TimedButton readyButton = pageLoadout.m_readyButton; CM_Item changeLoadoutButton = null; GameBuildInfo buildInfo = Feature.BuildInfo; if (RundownFlagsExtensions.IsIncludedIn(((GameBuildInfo)(ref buildInfo)).Rundown, RundownFlagsExtensions.ToLatest((RundownFlags)32))) { GetChangeLoadoutButtonR6Plus(pageLoadout, out changeLoadoutButton); } SetupButton(readyButton, changeLoadoutButton); } } [MethodImpl(MethodImplOptions.NoInlining)] public static void GetChangeLoadoutButtonR6Plus(CM_PageLoadout pageLoadout, out CM_Item changeLoadoutButton) { changeLoadoutButton = pageLoadout.m_changeLoadoutButton; } private static void SetupButton(CM_TimedButton readyUpButton, CM_Item changeLoadoutButton = null) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (_hasBeenSetup) { return; } GameBuildInfo buildInfo = Feature.BuildInfo; if (RundownFlagsExtensions.IsIncludedIn(((GameBuildInfo)(ref buildInfo)).Rundown, RundownFlagsExtensions.ToLatest((RundownFlags)32))) { SharedUtils.AddCMItemEvents((CM_Item)(object)readyUpButton, (Action<int>)SetButtonInactive, (Action<int, bool>)null); if (changeLoadoutButton != null) { SharedUtils.AddCMItemEvents(changeLoadoutButton, (Action<int>)SetButtonActive, (Action<int, bool>)null); } } CreateButton(readyUpButton); _hasBeenSetup = true; } private static CM_TimedButton CreateButton(CM_TimedButton prefab) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) _loadoutRandomizerButton = Object.Instantiate<GameObject>(((Component)prefab).gameObject, ((Component)prefab).transform.parent).GetComponent<CM_TimedButton>(); SharedUtils.SetCMItemEvents((CM_Item)(object)_loadoutRandomizerButton, (Action<int>)OnRandomizeLoadoutButtonPressed, (Action<int, bool>)OnButtonHoverChanged); ((Component)_loadoutRandomizerButton).gameObject.transform.Translate(new Vector3(-500f, 0f, 0f)); BoxCollider2D component = ((Component)_loadoutRandomizerButton).GetComponent<BoxCollider2D>(); if ((Object)(object)component != (Object)null) { ((Collider2D)component).offset = new Vector2(0f, -40f); } ((CM_Item)_loadoutRandomizerButton).SetText("Randomize Loadout"); SharedUtils.ChangeColorTimedExpeditionButton(_loadoutRandomizerButton, new Color(1f, 1f, 1f, 0.5f)); SetButtonActive(); return _loadoutRandomizerButton; } private static void SetButtonActive(int _ = 0) { CM_TimedButton loadoutRandomizerButton = _loadoutRandomizerButton; if (loadoutRandomizerButton != null) { GameObject gameObject = ((Component)loadoutRandomizerButton).gameObject; if (gameObject != null) { gameObject.SetActive(IsEnabled); } } } public static void SetButtonInactive(int _ = 0) { CM_TimedButton loadoutRandomizerButton = _loadoutRandomizerButton; if (loadoutRandomizerButton != null) { GameObject gameObject = ((Component)loadoutRandomizerButton).gameObject; if (gameObject != null) { gameObject.SetActive(false); } } } public static void OnRandomizeLoadoutButtonPressed(int _) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) FeatureLogger.Notice("Randomizer Button has been pressed!"); CM_PlayerLobbyBar val = null; val = ((IEnumerable<CM_PlayerLobbyBar>)((IEnumerable<CM_PlayerLobbyBar>)CM_PageLoadout.Current.m_playerLobbyBars).ToArray()).FirstOrDefault((Func<CM_PlayerLobbyBar, bool>)delegate(CM_PlayerLobbyBar plb) { SNet_Player player = plb.m_player; int? num = ((player != null) ? new int?(player.CharacterIndex) : null); PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); return num == ((localPlayerAgent != null) ? new int?(localPlayerAgent.CharacterID) : null); }); if ((Object)(object)val == (Object)null) { FeatureLogger.Error("Couldn't find the local players CM_PlayerLobbyBar, aborting randomization of loadout!"); return; } Enumerator<InventorySlot, CM_InventorySlotItem> enumerator = val.m_inventorySlotItems.GetEnumerator(); BackpackItem val2 = default(BackpackItem); while (enumerator.MoveNext()) { KeyValuePair<InventorySlot, CM_InventorySlotItem> current = enumerator.Current; InventorySlot key = current.Key; if (!_invSlotMap.TryGetValue(key, out var value) || !Config.ExcludedSlots.Contains(value)) { GearIDRange[] array = Il2CppArrayBase<GearIDRange>.op_Implicit((Il2CppArrayBase<GearIDRange>)(object)GearManager.GetAllGearForSlot(key)); PlayerBackpackManager.LocalBackpack.TryGetBackpackItem(key, ref val2); GearIDRange currentGearIdForSlot = val2.GearIDRange; LoadoutRandomizerSettings.RandomizerMode mode = Config.Mode; GearIDRange val3 = ((mode == LoadoutRandomizerSettings.RandomizerMode.True || mode != LoadoutRandomizerSettings.RandomizerMode.NoDuplicate) ? Utils.PickRandom<GearIDRange>(array) : Utils.PickRandomExcept<GearIDRange>(array, (Func<GearIDRange, bool>)((GearIDRange random) => !currentGearIdForSlot.GetChecksum().Equals(random.GetChecksum())))); if (val3 == null) { FeatureLogger.Error($"Tried to randomize Gear for slot {key} but received null!"); continue; } FeatureLogger.Notice($"Picked random gear \"{val3.PublicGearName}\" for slot {key}!"); PlayerBackpackManager.ResetLocalAmmoStorage(false); PlayerBackpackManager.EquipLocalGear(val3); GearManager.RegisterGearInSlotAsEquipped(val3.PlayfabItemInstanceId, key); } } } public static void OnButtonHoverChanged(int i, bool b) { } } [RundownConstraint(/*Could not decode attribute arguments.*/)] internal class NoDroppedMagazineSounds : Feature { public override string Name => "No Magazine Drop Sound"; public override GroupBase Group => (GroupBase)(object)GroupManager.QualityOfLife; public override string Description => "Removes the <i>globally audible</i> sound whenever a magazine drops on the floor after a reload."; public override bool RequiresRestart => true; public override void OnGameDataInitialized() { if (!((Feature)this).Enabled) { return; } foreach (GearMagPartDataBlock allBlock in GameDataBlockBase<GearMagPartDataBlock>.GetAllBlocks()) { allBlock.DropSoundType = (MagazineDropSoundType)0; } } } [EnableFeatureByDefault] public class NoNavMarkerHideInChat : Feature { [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class GuiManager_OnFocusStateChanged_Patch { private static eFocusState _eFocusState_FPS_TypingInChat; public static void Init() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) _eFocusState_FPS_TypingInChat = Utils.GetEnumFromName<eFocusState>("FPS_TypingInChat"); } public static void Postfix(eFocusState state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (state == _eFocusState_FPS_TypingInChat) { ((GuiLayer)GuiManager.NavMarkerLayer).SetVisible(true); } } } public override string Name => "See NavMarkers in Chat"; public override GroupBase Group => (GroupBase)(object)GroupManager.QualityOfLife; public override string Description => "Prevent enemy pings from hiding whenever the chat is open."; } [RundownConstraint(/*Could not decode attribute arguments.*/)] public class NoStoryDialog : Feature { [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class WardenObjectiveManager_CheckAndExecuteEventsOnTrigger_Patch { [IsPrefix] [RundownConstraint(/*Could not decode attribute arguments.*/)] public static void PrefixR7(WardenObjectiveEventData eventToTrigger) { if (eventToTrigger.SoundSubtitle.HasValue) { FeatureLogger.Notice($"SoundEvent about to execute was: {eventToTrigger.SoundID} ({SoundEventCache.ReverseResolve(eventToTrigger.SoundID, false)})"); eventToTrigger.SoundID = 0u; } } [IsPrefix] [RundownConstraint(/*Could not decode attribute arguments.*/)] public static void PrefixR6(WardenObjectiveEventData eventToTrigger) { if (eventToTrigger.SoundID != 0) { FeatureLogger.Notice($"SoundEvent about to execute was: {eventToTrigger.SoundID} ({SoundEventCache.ReverseResolve(eventToTrigger.SoundID, false)})"); eventToTrigger.SoundID = 0u; } } } public override string Name => "Remove Story Dialog"; public override GroupBase Group => (GroupBase)(object)GroupManager.QualityOfLife; public override string Description => "Removes all level-based voice events that come with subtitles.\naka Schaeffer-be-gone"; public static IArchiveLogger FeatureLogger { get; set; } } public class ReloadSoundCue : Feature { public class ReloadSoundCueSettings { [FSDisplayName("Test Sound Event")] [FSDescription("Plays the sound event below,\nit's a little janky and might not work depending on the sound, sorry!")] public FButton TestSoundButton { get; set; } = new FButton("Play Sound", (string)null, (Action)null, false); [FSDisplayName
BepInEx/plugins/TheArchive.RichPresence/plugins/DiscordRPC.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using Clonesoft.Json; using Clonesoft.Json.Linq; using DiscordRPC.Converters; using DiscordRPC.Events; using DiscordRPC.Exceptions; using DiscordRPC.Helper; using DiscordRPC.IO; using DiscordRPC.Logging; using DiscordRPC.Message; using DiscordRPC.RPC; using DiscordRPC.RPC.Commands; using DiscordRPC.RPC.Payload; using DiscordRPC.Registry; using Microsoft.Win32; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Discord RPC")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Discord RPC")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("819d20d6-8d88-45c1-a4d2-aa21f10abd19")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("1.0.0.0")] namespace DiscordRPC { public class Configuration { [JsonProperty("api_endpoint")] public string ApiEndpoint { get; set; } [JsonProperty("cdn_host")] public string CdnHost { get; set; } [JsonProperty("environment")] public string Environment { get; set; } } public sealed class DiscordRpcClient : IDisposable { private ILogger _logger; private RpcConnection connection; private bool _shutdownOnly = true; private object _sync = new object(); public bool HasRegisteredUriScheme { get; private set; } public string ApplicationID { get; private set; } public string SteamID { get; private set; } public int ProcessID { get; private set; } public int MaxQueueSize { get; private set; } public bool IsDisposed { get; private set; } public ILogger Logger { get { return _logger; } set { _logger = value; if (connection != null) { connection.Logger = value; } } } public bool AutoEvents { get; private set; } public bool SkipIdenticalPresence { get; set; } public int TargetPipe { get; private set; } public RichPresence CurrentPresence { get; private set; } public EventType Subscription { get; private set; } public User CurrentUser { get; private set; } public Configuration Configuration { get; private set; } public bool IsInitialized { get; private set; } public bool ShutdownOnly { get { return _shutdownOnly; } set { _shutdownOnly = value; if (connection != null) { connection.ShutdownOnly = value; } } } public event OnReadyEvent OnReady; public event OnCloseEvent OnClose; public event OnErrorEvent OnError; public event OnPresenceUpdateEvent OnPresenceUpdate; public event OnSubscribeEvent OnSubscribe; public event OnUnsubscribeEvent OnUnsubscribe; public event OnJoinEvent OnJoin; public event OnSpectateEvent OnSpectate; public event OnJoinRequestedEvent OnJoinRequested; public event OnConnectionEstablishedEvent OnConnectionEstablished; public event OnConnectionFailedEvent OnConnectionFailed; public event OnRpcMessageEvent OnRpcMessage; public DiscordRpcClient(string applicationID) : this(applicationID, -1, null, autoEvents: true, null) { } public DiscordRpcClient(string applicationID, int pipe = -1, ILogger logger = null, bool autoEvents = true, INamedPipeClient client = null) { if (string.IsNullOrEmpty(applicationID)) { throw new ArgumentNullException("applicationID"); } ApplicationID = applicationID.Trim(); TargetPipe = pipe; ProcessID = Process.GetCurrentProcess().Id; HasRegisteredUriScheme = false; AutoEvents = autoEvents; SkipIdenticalPresence = true; _logger = logger ?? new NullLogger(); connection = new RpcConnection(ApplicationID, ProcessID, TargetPipe, client ?? new ManagedNamedPipeClient(), (!autoEvents) ? 128u : 0u) { ShutdownOnly = _shutdownOnly, Logger = _logger }; connection.OnRpcMessage += delegate(object sender, IMessage msg) { if (this.OnRpcMessage != null) { this.OnRpcMessage(this, msg); } if (AutoEvents) { ProcessMessage(msg); } }; } public IMessage[] Invoke() { if (AutoEvents) { Logger.Error("Cannot Invoke client when AutomaticallyInvokeEvents has been set."); return new IMessage[0]; } IMessage[] array = connection.DequeueMessages(); foreach (IMessage message in array) { ProcessMessage(message); } return array; } private void ProcessMessage(IMessage message) { if (message == null) { return; } switch (message.Type) { case MessageType.PresenceUpdate: lock (_sync) { if (message is PresenceMessage presenceMessage) { if (presenceMessage.Presence == null) { CurrentPresence = null; } else if (CurrentPresence == null) { CurrentPresence = new RichPresence().Merge(presenceMessage.Presence); } else { CurrentPresence.Merge(presenceMessage.Presence); } presenceMessage.Presence = CurrentPresence; } } if (this.OnPresenceUpdate != null) { this.OnPresenceUpdate(this, message as PresenceMessage); } break; case MessageType.Ready: if (message is ReadyMessage readyMessage) { lock (_sync) { Configuration = readyMessage.Configuration; CurrentUser = readyMessage.User; } SynchronizeState(); } if (this.OnReady != null) { this.OnReady(this, message as ReadyMessage); } break; case MessageType.Close: if (this.OnClose != null) { this.OnClose(this, message as CloseMessage); } break; case MessageType.Error: if (this.OnError != null) { this.OnError(this, message as ErrorMessage); } break; case MessageType.JoinRequest: if (Configuration != null && message is JoinRequestMessage joinRequestMessage) { joinRequestMessage.User.SetConfiguration(Configuration); } if (this.OnJoinRequested != null) { this.OnJoinRequested(this, message as JoinRequestMessage); } break; case MessageType.Subscribe: lock (_sync) { SubscribeMessage subscribeMessage = message as SubscribeMessage; Subscription |= subscribeMessage.Event; } if (this.OnSubscribe != null) { this.OnSubscribe(this, message as SubscribeMessage); } break; case MessageType.Unsubscribe: lock (_sync) { UnsubscribeMessage unsubscribeMessage = message as UnsubscribeMessage; Subscription &= ~unsubscribeMessage.Event; } if (this.OnUnsubscribe != null) { this.OnUnsubscribe(this, message as UnsubscribeMessage); } break; case MessageType.Join: if (this.OnJoin != null) { this.OnJoin(this, message as JoinMessage); } break; case MessageType.Spectate: if (this.OnSpectate != null) { this.OnSpectate(this, message as SpectateMessage); } break; case MessageType.ConnectionEstablished: if (this.OnConnectionEstablished != null) { this.OnConnectionEstablished(this, message as ConnectionEstablishedMessage); } break; case MessageType.ConnectionFailed: if (this.OnConnectionFailed != null) { this.OnConnectionFailed(this, message as ConnectionFailedMessage); } break; default: Logger.Error("Message was queued with no appropriate handle! {0}", message.Type); break; } } public void Respond(JoinRequestMessage request, bool acceptRequest) { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } if (!IsInitialized) { throw new UninitializedException(); } connection.EnqueueCommand(new RespondCommand { Accept = acceptRequest, UserID = request.User.ID.ToString() }); } public void SetPresence(RichPresence presence) { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } if (!IsInitialized) { Logger.Warning("The client is not yet initialized, storing the presence as a state instead."); } if (presence == null) { if (!SkipIdenticalPresence || CurrentPresence != null) { connection.EnqueueCommand(new PresenceCommand { PID = ProcessID, Presence = null }); } } else { if (presence.HasSecrets() && !HasRegisteredUriScheme) { throw new BadPresenceException("Cannot send a presence with secrets as this object has not registered a URI scheme. Please enable the uri scheme registration in the DiscordRpcClient constructor."); } if (presence.HasParty() && presence.Party.Max < presence.Party.Size) { throw new BadPresenceException("Presence maximum party size cannot be smaller than the current size."); } if (presence.HasSecrets() && !presence.HasParty()) { Logger.Warning("The presence has set the secrets but no buttons will show as there is no party available."); } if (!SkipIdenticalPresence || !presence.Matches(CurrentPresence)) { connection.EnqueueCommand(new PresenceCommand { PID = ProcessID, Presence = presence.Clone() }); } } lock (_sync) { CurrentPresence = presence?.Clone(); } } public RichPresence Update(Action<RichPresence> func) { if (!IsInitialized) { throw new UninitializedException(); } RichPresence richPresence; lock (_sync) { richPresence = ((CurrentPresence == null) ? new RichPresence() : CurrentPresence.Clone()); } func(richPresence); SetPresence(richPresence); return richPresence; } public RichPresence UpdateType(ActivityType type) { return Update(delegate(RichPresence p) { p.Type = type; }); } public RichPresence UpdateButtons(Button[] buttons = null) { return Update(delegate(RichPresence p) { p.Buttons = buttons; }); } public RichPresence SetButton(Button button, int index = 0) { return Update(delegate(RichPresence p) { p.Buttons[index] = button; }); } public RichPresence UpdateDetails(string details) { return Update(delegate(RichPresence p) { p.Details = details; }); } public RichPresence UpdateState(string state) { return Update(delegate(RichPresence p) { p.State = state; }); } public RichPresence UpdateParty(Party party) { return Update(delegate(RichPresence p) { p.Party = party; }); } public RichPresence UpdatePartySize(int size) { return Update(delegate(RichPresence p) { if (p.Party == null) { throw new BadPresenceException("Cannot set the size of the party if the party does not exist"); } p.Party.Size = size; }); } public RichPresence UpdatePartySize(int size, int max) { return Update(delegate(RichPresence p) { if (p.Party == null) { throw new BadPresenceException("Cannot set the size of the party if the party does not exist"); } p.Party.Size = size; p.Party.Max = max; }); } public RichPresence UpdateLargeAsset(string key = null, string tooltip = null) { return Update(delegate(RichPresence p) { if (p.Assets == null) { p.Assets = new Assets(); } p.Assets.LargeImageKey = key ?? p.Assets.LargeImageKey; p.Assets.LargeImageText = tooltip ?? p.Assets.LargeImageText; }); } public RichPresence UpdateSmallAsset(string key = null, string tooltip = null) { return Update(delegate(RichPresence p) { if (p.Assets == null) { p.Assets = new Assets(); } p.Assets.SmallImageKey = key ?? p.Assets.SmallImageKey; p.Assets.SmallImageText = tooltip ?? p.Assets.SmallImageText; }); } public RichPresence UpdateSecrets(Secrets secrets) { return Update(delegate(RichPresence p) { p.Secrets = secrets; }); } public RichPresence UpdateStartTime() { return UpdateStartTime(DateTime.UtcNow); } public RichPresence UpdateStartTime(DateTime time) { return Update(delegate(RichPresence p) { if (p.Timestamps == null) { p.Timestamps = new Timestamps(); } p.Timestamps.Start = time; }); } public RichPresence UpdateEndTime() { return UpdateEndTime(DateTime.UtcNow); } public RichPresence UpdateEndTime(DateTime time) { return Update(delegate(RichPresence p) { if (p.Timestamps == null) { p.Timestamps = new Timestamps(); } p.Timestamps.End = time; }); } public RichPresence UpdateClearTime() { return Update(delegate(RichPresence p) { p.Timestamps = null; }); } public void ClearPresence() { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (!IsInitialized) { throw new UninitializedException(); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } SetPresence(null); } public bool RegisterUriScheme(string steamAppID = null, string executable = null) { UriSchemeRegister uriSchemeRegister = new UriSchemeRegister(_logger, ApplicationID, steamAppID, executable); return HasRegisteredUriScheme = uriSchemeRegister.RegisterUriScheme(); } public void Subscribe(EventType type) { SetSubscription(Subscription | type); } [Obsolete("Replaced with Unsubscribe", true)] public void Unubscribe(EventType type) { SetSubscription(Subscription & ~type); } public void Unsubscribe(EventType type) { SetSubscription(Subscription & ~type); } public void SetSubscription(EventType type) { if (IsInitialized) { SubscribeToTypes(Subscription & ~type, isUnsubscribe: true); SubscribeToTypes(~Subscription & type, isUnsubscribe: false); } else { Logger.Warning("Client has not yet initialized, but events are being subscribed too. Storing them as state instead."); } lock (_sync) { Subscription = type; } } private void SubscribeToTypes(EventType type, bool isUnsubscribe) { if (type != 0) { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (!IsInitialized) { throw new UninitializedException(); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } if (!HasRegisteredUriScheme) { throw new InvalidConfigurationException("Cannot subscribe/unsubscribe to an event as this application has not registered a URI Scheme. Call RegisterUriScheme()."); } if ((type & EventType.Spectate) == EventType.Spectate) { connection.EnqueueCommand(new SubscribeCommand { Event = ServerEvent.ActivitySpectate, IsUnsubscribe = isUnsubscribe }); } if ((type & EventType.Join) == EventType.Join) { connection.EnqueueCommand(new SubscribeCommand { Event = ServerEvent.ActivityJoin, IsUnsubscribe = isUnsubscribe }); } if ((type & EventType.JoinRequest) == EventType.JoinRequest) { connection.EnqueueCommand(new SubscribeCommand { Event = ServerEvent.ActivityJoinRequest, IsUnsubscribe = isUnsubscribe }); } } } public void SynchronizeState() { if (!IsInitialized) { throw new UninitializedException(); } SetPresence(CurrentPresence); if (HasRegisteredUriScheme) { SubscribeToTypes(Subscription, isUnsubscribe: false); } } public bool Initialize() { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (IsInitialized) { throw new UninitializedException("Cannot initialize a client that is already initialized"); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } return IsInitialized = connection.AttemptConnection(); } public void Deinitialize() { if (!IsInitialized) { throw new UninitializedException("Cannot deinitialize a client that has not been initalized."); } connection.Close(); IsInitialized = false; } public void Dispose() { if (!IsDisposed) { if (IsInitialized) { Deinitialize(); } IsDisposed = true; } } } [Flags] public enum EventType { None = 0, Spectate = 1, Join = 2, JoinRequest = 4 } [Serializable] [JsonObject(/*Could not decode attribute arguments.*/)] public class BaseRichPresence { protected internal string _state; protected internal string _details; [JsonProperty(/*Could not decode attribute arguments.*/)] public string State { get { return _state; } set { if (!ValidateString(value, out _state, 128, Encoding.UTF8)) { throw new StringOutOfRangeException("State", 0, 128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string Details { get { return _details; } set { if (!ValidateString(value, out _details, 128, Encoding.UTF8)) { throw new StringOutOfRangeException(128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public Timestamps Timestamps { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public Assets Assets { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public Party Party { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public Secrets Secrets { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public ActivityType Type { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] [Obsolete("This was going to be used, but was replaced by JoinSecret instead")] private bool Instance { get; set; } public bool HasTimestamps() { return Timestamps != null && (Timestamps.Start.HasValue || Timestamps.End.HasValue); } public bool HasAssets() { return Assets != null; } public bool HasParty() { return Party != null && Party.ID != null; } public bool HasSecrets() { return Secrets != null && (Secrets.JoinSecret != null || Secrets.SpectateSecret != null); } internal static bool ValidateString(string str, out string result, int bytes, Encoding encoding) { result = str; if (str == null) { return true; } string str2 = str.Trim(); if (!str2.WithinLength(bytes, encoding)) { return false; } result = str2.GetNullOrString(); return true; } public static implicit operator bool(BaseRichPresence presesnce) { return presesnce != null; } internal virtual bool Matches(RichPresence other) { if (other == null) { return false; } if (State != other.State || Details != other.Details || Type != other.Type) { return false; } if (Timestamps != null) { if (other.Timestamps == null || other.Timestamps.StartUnixMilliseconds != Timestamps.StartUnixMilliseconds || other.Timestamps.EndUnixMilliseconds != Timestamps.EndUnixMilliseconds) { return false; } } else if (other.Timestamps != null) { return false; } if (Secrets != null) { if (other.Secrets == null || other.Secrets.JoinSecret != Secrets.JoinSecret || other.Secrets.MatchSecret != Secrets.MatchSecret || other.Secrets.SpectateSecret != Secrets.SpectateSecret) { return false; } } else if (other.Secrets != null) { return false; } if (Party != null) { if (other.Party == null || other.Party.ID != Party.ID || other.Party.Max != Party.Max || other.Party.Size != Party.Size || other.Party.Privacy != Party.Privacy) { return false; } } else if (other.Party != null) { return false; } if (Assets != null) { if (other.Assets == null || other.Assets.LargeImageKey != Assets.LargeImageKey || other.Assets.LargeImageText != Assets.LargeImageText || other.Assets.SmallImageKey != Assets.SmallImageKey || other.Assets.SmallImageText != Assets.SmallImageText) { return false; } } else if (other.Assets != null) { return false; } return Instance == other.Instance; } public RichPresence ToRichPresence() { RichPresence richPresence = new RichPresence(); richPresence.State = State; richPresence.Details = Details; richPresence.Type = Type; richPresence.Party = ((!HasParty()) ? Party : null); richPresence.Secrets = ((!HasSecrets()) ? Secrets : null); if (HasAssets()) { richPresence.Assets = new Assets { SmallImageKey = Assets.SmallImageKey, SmallImageText = Assets.SmallImageText, LargeImageKey = Assets.LargeImageKey, LargeImageText = Assets.LargeImageText }; } if (HasTimestamps()) { richPresence.Timestamps = new Timestamps(); if (Timestamps.Start.HasValue) { richPresence.Timestamps.Start = Timestamps.Start; } if (Timestamps.End.HasValue) { richPresence.Timestamps.End = Timestamps.End; } } return richPresence; } } [Serializable] public class Secrets { private string _matchSecret; private string _joinSecret; private string _spectateSecret; [Obsolete("This feature has been deprecated my Mason in issue #152 on the offical library. Was originally used as a Notify Me feature, it has been replaced with Join / Spectate.")] [JsonProperty(/*Could not decode attribute arguments.*/)] public string MatchSecret { get { return _matchSecret; } set { if (!BaseRichPresence.ValidateString(value, out _matchSecret, 128, Encoding.UTF8)) { throw new StringOutOfRangeException(128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string JoinSecret { get { return _joinSecret; } set { if (!BaseRichPresence.ValidateString(value, out _joinSecret, 128, Encoding.UTF8)) { throw new StringOutOfRangeException(128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string SpectateSecret { get { return _spectateSecret; } set { if (!BaseRichPresence.ValidateString(value, out _spectateSecret, 128, Encoding.UTF8)) { throw new StringOutOfRangeException(128); } } } public static Encoding Encoding => Encoding.UTF8; public static int SecretLength => 128; public static string CreateSecret(Random random) { byte[] array = new byte[SecretLength]; random.NextBytes(array); return Encoding.GetString(array); } public static string CreateFriendlySecret(Random random) { string text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < SecretLength; i++) { stringBuilder.Append(text[random.Next(text.Length)]); } return stringBuilder.ToString(); } } [Serializable] public class Assets { private string _largeimagekey; private bool _islargeimagekeyexternal; private string _largeimagetext; private string _smallimagekey; private bool _issmallimagekeyexternal; private string _smallimagetext; private ulong? _largeimageID; private ulong? _smallimageID; [JsonProperty(/*Could not decode attribute arguments.*/)] public string LargeImageKey { get { return _largeimagekey; } set { if (!BaseRichPresence.ValidateString(value, out _largeimagekey, 256, Encoding.UTF8)) { throw new StringOutOfRangeException(256); } _islargeimagekeyexternal = _largeimagekey?.StartsWith("mp:external/") ?? false; _largeimageID = null; } } [JsonIgnore] public bool IsLargeImageKeyExternal => _islargeimagekeyexternal; [JsonProperty(/*Could not decode attribute arguments.*/)] public string LargeImageText { get { return _largeimagetext; } set { if (!BaseRichPresence.ValidateString(value, out _largeimagetext, 128, Encoding.UTF8)) { throw new StringOutOfRangeException(128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string SmallImageKey { get { return _smallimagekey; } set { if (!BaseRichPresence.ValidateString(value, out _smallimagekey, 256, Encoding.UTF8)) { throw new StringOutOfRangeException(256); } _issmallimagekeyexternal = _smallimagekey?.StartsWith("mp:external/") ?? false; _smallimageID = null; } } [JsonIgnore] public bool IsSmallImageKeyExternal => _issmallimagekeyexternal; [JsonProperty(/*Could not decode attribute arguments.*/)] public string SmallImageText { get { return _smallimagetext; } set { if (!BaseRichPresence.ValidateString(value, out _smallimagetext, 128, Encoding.UTF8)) { throw new StringOutOfRangeException(128); } } } [JsonIgnore] public ulong? LargeImageID => _largeimageID; [JsonIgnore] public ulong? SmallImageID => _smallimageID; internal void Merge(Assets other) { _smallimagetext = other._smallimagetext; _largeimagetext = other._largeimagetext; if (ulong.TryParse(other._largeimagekey, out var result)) { _largeimageID = result; } else { _largeimagekey = other._largeimagekey; _largeimageID = null; } if (ulong.TryParse(other._smallimagekey, out var result2)) { _smallimageID = result2; return; } _smallimagekey = other._smallimagekey; _smallimageID = null; } } [Serializable] public class Timestamps { public static Timestamps Now => new Timestamps(DateTime.UtcNow); [JsonIgnore] public DateTime? Start { get; set; } [JsonIgnore] public DateTime? End { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public ulong? StartUnixMilliseconds { get { return Start.HasValue ? new ulong?(ToUnixMilliseconds(Start.Value)) : null; } set { Start = (value.HasValue ? new DateTime?(FromUnixMilliseconds(value.Value)) : null); } } [JsonProperty(/*Could not decode attribute arguments.*/)] public ulong? EndUnixMilliseconds { get { return End.HasValue ? new ulong?(ToUnixMilliseconds(End.Value)) : null; } set { End = (value.HasValue ? new DateTime?(FromUnixMilliseconds(value.Value)) : null); } } public static Timestamps FromTimeSpan(double seconds) { return FromTimeSpan(TimeSpan.FromSeconds(seconds)); } public static Timestamps FromTimeSpan(TimeSpan timespan) { return new Timestamps { Start = DateTime.UtcNow, End = DateTime.UtcNow + timespan }; } public Timestamps() { Start = null; End = null; } public Timestamps(DateTime start) { Start = start; End = null; } public Timestamps(DateTime start, DateTime end) { Start = start; End = end; } public static DateTime FromUnixMilliseconds(ulong unixTime) { return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(Convert.ToDouble(unixTime)); } public static ulong ToUnixMilliseconds(DateTime date) { DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); return Convert.ToUInt64((date - dateTime).TotalMilliseconds); } } [Serializable] public class Party { public enum PrivacySetting { Private, Public } private string _partyid; [JsonProperty(/*Could not decode attribute arguments.*/)] public string ID { get { return _partyid; } set { _partyid = value.GetNullOrString(); } } [JsonIgnore] public int Size { get; set; } [JsonIgnore] public int Max { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public PrivacySetting Privacy { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] private int[] _size { get { int num = Math.Max(1, Size); return new int[2] { num, Math.Max(num, Max) }; } set { if (value.Length != 2) { Size = 0; Max = 0; } else { Size = value[0]; Max = value[1]; } } } } public class Button { private string _label; private string _url; [JsonProperty("label")] public string Label { get { return _label; } set { if (!BaseRichPresence.ValidateString(value, out _label, 32, Encoding.UTF8)) { throw new StringOutOfRangeException(32); } } } [JsonProperty("url")] public string Url { get { return _url; } set { if (!BaseRichPresence.ValidateString(value, out _url, 512, Encoding.UTF8)) { throw new StringOutOfRangeException(512); } if (!Uri.TryCreate(_url, UriKind.Absolute, out Uri _)) { throw new ArgumentException("Url must be a valid URI"); } } } } public enum ActivityType { Playing = 0, Listening = 2, Watching = 3, Competing = 5 } public sealed class RichPresence : BaseRichPresence { [JsonProperty(/*Could not decode attribute arguments.*/)] public Button[] Buttons { get; set; } public bool HasButtons() { return Buttons != null && Buttons.Length != 0; } public RichPresence WithState(string state) { base.State = state; return this; } public RichPresence WithDetails(string details) { base.Details = details; return this; } public RichPresence WithType(ActivityType type) { base.Type = type; return this; } public RichPresence WithTimestamps(Timestamps timestamps) { base.Timestamps = timestamps; return this; } public RichPresence WithAssets(Assets assets) { base.Assets = assets; return this; } public RichPresence WithParty(Party party) { base.Party = party; return this; } public RichPresence WithSecrets(Secrets secrets) { base.Secrets = secrets; return this; } public RichPresence Clone() { RichPresence richPresence = new RichPresence(); richPresence.State = ((_state != null) ? (_state.Clone() as string) : null); richPresence.Details = ((_details != null) ? (_details.Clone() as string) : null); richPresence.Type = base.Type; richPresence.Buttons = ((!HasButtons()) ? null : (Buttons.Clone() as Button[])); richPresence.Secrets = ((!HasSecrets()) ? null : new Secrets { JoinSecret = ((base.Secrets.JoinSecret != null) ? (base.Secrets.JoinSecret.Clone() as string) : null), SpectateSecret = ((base.Secrets.SpectateSecret != null) ? (base.Secrets.SpectateSecret.Clone() as string) : null) }); richPresence.Timestamps = ((!HasTimestamps()) ? null : new Timestamps { Start = base.Timestamps.Start, End = base.Timestamps.End }); richPresence.Assets = ((!HasAssets()) ? null : new Assets { LargeImageKey = ((base.Assets.LargeImageKey != null) ? (base.Assets.LargeImageKey.Clone() as string) : null), LargeImageText = ((base.Assets.LargeImageText != null) ? (base.Assets.LargeImageText.Clone() as string) : null), SmallImageKey = ((base.Assets.SmallImageKey != null) ? (base.Assets.SmallImageKey.Clone() as string) : null), SmallImageText = ((base.Assets.SmallImageText != null) ? (base.Assets.SmallImageText.Clone() as string) : null) }); richPresence.Party = ((!HasParty()) ? null : new Party { ID = base.Party.ID, Size = base.Party.Size, Max = base.Party.Max, Privacy = base.Party.Privacy }); return richPresence; } internal RichPresence Merge(BaseRichPresence presence) { _state = presence.State; _details = presence.Details; base.Type = presence.Type; base.Party = presence.Party; base.Timestamps = presence.Timestamps; base.Secrets = presence.Secrets; if (presence.HasAssets()) { if (!HasAssets()) { base.Assets = presence.Assets; } else { base.Assets.Merge(presence.Assets); } } else { base.Assets = null; } return this; } internal override bool Matches(RichPresence other) { if (!base.Matches(other)) { return false; } if ((Buttons == null) ^ (other.Buttons == null)) { return false; } if (Buttons != null) { if (Buttons.Length != other.Buttons.Length) { return false; } for (int i = 0; i < Buttons.Length; i++) { Button button = Buttons[i]; Button button2 = other.Buttons[i]; if (button.Label != button2.Label || button.Url != button2.Url) { return false; } } } return true; } public static implicit operator bool(RichPresence presesnce) { return presesnce != null; } } internal sealed class RichPresenceResponse : BaseRichPresence { [JsonProperty("application_id")] public string ClientID { get; private set; } [JsonProperty("name")] public string Name { get; private set; } } public class User { public enum AvatarFormat { PNG, JPEG, WebP, GIF } public enum AvatarSize { x16 = 0x10, x32 = 0x20, x64 = 0x40, x128 = 0x80, x256 = 0x100, x512 = 0x200, x1024 = 0x400, x2048 = 0x800 } [Flags] public enum Flag { None = 0, Employee = 1, Partner = 2, HypeSquad = 4, BugHunter = 8, HouseBravery = 0x40, HouseBrilliance = 0x80, HouseBalance = 0x100, EarlySupporter = 0x200, TeamUser = 0x400 } public enum PremiumType { None, NitroClassic, Nitro } [JsonProperty("id")] public ulong ID { get; private set; } [JsonProperty("username")] public string Username { get; private set; } [JsonProperty("discriminator")] [Obsolete("Discord no longer uses discriminators.")] public int Discriminator { get; private set; } [JsonProperty("global_name")] public string DisplayName { get; private set; } [JsonProperty("avatar")] public string Avatar { get; private set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public Flag Flags { get; private set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public PremiumType Premium { get; private set; } public string CdnEndpoint { get; private set; } internal User() { CdnEndpoint = "cdn.discordapp.com"; } internal void SetConfiguration(Configuration configuration) { CdnEndpoint = configuration.CdnHost; } public string GetAvatarURL(AvatarFormat format) { return GetAvatarURL(format, AvatarSize.x128); } public string GetAvatarURL(AvatarFormat format, AvatarSize size) { string text = $"/avatars/{ID}/{Avatar}"; if (string.IsNullOrEmpty(Avatar)) { if (format != 0) { throw new BadImageFormatException("The user has no avatar and the requested format " + format.ToString() + " is not supported. (Only supports PNG)."); } int value = (int)((ID >> 22) % 6); if (Discriminator > 0) { value = Discriminator % 5; } text = $"/embed/avatars/{value}"; } return $"https://{CdnEndpoint}{text}{GetAvatarExtension(format)}?size={(int)size}"; } public string GetAvatarExtension(AvatarFormat format) { return "." + format.ToString().ToLowerInvariant(); } public override string ToString() { if (!string.IsNullOrEmpty(DisplayName)) { return DisplayName; } if (Discriminator != 0) { return Username + "#" + Discriminator.ToString("D4"); } return Username; } } } namespace DiscordRPC.RPC { internal class RpcConnection : IDisposable { public static readonly int VERSION = 1; public static readonly int POLL_RATE = 1000; private static readonly bool CLEAR_ON_SHUTDOWN = true; private static readonly bool LOCK_STEP = false; private ILogger _logger; private RpcState _state; private readonly object l_states = new object(); private Configuration _configuration = null; private readonly object l_config = new object(); private volatile bool aborting = false; private volatile bool shutdown = false; private string applicationID; private int processID; private long nonce; private Thread thread; private INamedPipeClient namedPipe; private int targetPipe; private readonly object l_rtqueue = new object(); private readonly uint _maxRtQueueSize; private Queue<ICommand> _rtqueue; private readonly object l_rxqueue = new object(); private readonly uint _maxRxQueueSize; private Queue<IMessage> _rxqueue; private AutoResetEvent queueUpdatedEvent = new AutoResetEvent(initialState: false); private BackoffDelay delay; public ILogger Logger { get { return _logger; } set { _logger = value; if (namedPipe != null) { namedPipe.Logger = value; } } } public RpcState State { get { lock (l_states) { return _state; } } } public Configuration Configuration { get { Configuration result = null; lock (l_config) { result = _configuration; } return result; } } public bool IsRunning => thread != null; public bool ShutdownOnly { get; set; } public event OnRpcMessageEvent OnRpcMessage; public RpcConnection(string applicationID, int processID, int targetPipe, INamedPipeClient client, uint maxRxQueueSize = 128u, uint maxRtQueueSize = 512u) { this.applicationID = applicationID; this.processID = processID; this.targetPipe = targetPipe; namedPipe = client; ShutdownOnly = true; Logger = new ConsoleLogger(); delay = new BackoffDelay(500, 60000); _maxRtQueueSize = maxRtQueueSize; _rtqueue = new Queue<ICommand>((int)(_maxRtQueueSize + 1)); _maxRxQueueSize = maxRxQueueSize; _rxqueue = new Queue<IMessage>((int)(_maxRxQueueSize + 1)); nonce = 0L; } private long GetNextNonce() { nonce++; return nonce; } internal void EnqueueCommand(ICommand command) { Logger.Trace("Enqueue Command: {0}", command.GetType().FullName); if (aborting || shutdown) { return; } lock (l_rtqueue) { if (_rtqueue.Count == _maxRtQueueSize) { Logger.Error("Too many enqueued commands, dropping oldest one. Maybe you are pushing new presences to fast?"); _rtqueue.Dequeue(); } _rtqueue.Enqueue(command); } } private void EnqueueMessage(IMessage message) { try { if (this.OnRpcMessage != null) { this.OnRpcMessage(this, message); } } catch (Exception ex) { Logger.Error("Unhandled Exception while processing event: {0}", ex.GetType().FullName); Logger.Error(ex.Message); Logger.Error(ex.StackTrace); } if (_maxRxQueueSize == 0) { Logger.Trace("Enqueued Message, but queue size is 0."); return; } Logger.Trace("Enqueue Message: {0}", message.Type); lock (l_rxqueue) { if (_rxqueue.Count == _maxRxQueueSize) { Logger.Warning("Too many enqueued messages, dropping oldest one."); _rxqueue.Dequeue(); } _rxqueue.Enqueue(message); } } internal IMessage DequeueMessage() { lock (l_rxqueue) { if (_rxqueue.Count == 0) { return null; } return _rxqueue.Dequeue(); } } internal IMessage[] DequeueMessages() { lock (l_rxqueue) { IMessage[] result = _rxqueue.ToArray(); _rxqueue.Clear(); return result; } } private void MainLoop() { Logger.Info("RPC Connection Started"); if (Logger.Level <= LogLevel.Trace) { Logger.Trace("============================"); Logger.Trace("Assembly: " + Assembly.GetAssembly(typeof(RichPresence)).FullName); Logger.Trace("Pipe: " + namedPipe.GetType().FullName); Logger.Trace("Platform: " + Environment.OSVersion.ToString()); Logger.Trace("applicationID: " + applicationID); Logger.Trace("targetPipe: " + targetPipe); Logger.Trace("POLL_RATE: " + POLL_RATE); Logger.Trace("_maxRtQueueSize: " + _maxRtQueueSize); Logger.Trace("_maxRxQueueSize: " + _maxRxQueueSize); Logger.Trace("============================"); } while (!aborting && !shutdown) { try { if (namedPipe == null) { Logger.Error("Something bad has happened with our pipe client!"); aborting = true; return; } Logger.Trace("Connecting to the pipe through the {0}", namedPipe.GetType().FullName); if (namedPipe.Connect(targetPipe)) { Logger.Trace("Connected to the pipe. Attempting to establish handshake..."); EnqueueMessage(new ConnectionEstablishedMessage { ConnectedPipe = namedPipe.ConnectedPipe }); EstablishHandshake(); Logger.Trace("Connection Established. Starting reading loop..."); bool flag = true; while (flag && !aborting && !shutdown && namedPipe.IsConnected) { if (namedPipe.ReadFrame(out var frame)) { Logger.Trace("Read Payload: {0}", frame.Opcode); switch (frame.Opcode) { case Opcode.Close: { ClosePayload @object = frame.GetObject<ClosePayload>(); Logger.Warning("We have been told to terminate by discord: ({0}) {1}", @object.Code, @object.Reason); EnqueueMessage(new CloseMessage { Code = @object.Code, Reason = @object.Reason }); flag = false; break; } case Opcode.Ping: Logger.Trace("PING"); frame.Opcode = Opcode.Pong; namedPipe.WriteFrame(frame); break; case Opcode.Pong: Logger.Trace("PONG"); break; case Opcode.Frame: { if (shutdown) { Logger.Warning("Skipping frame because we are shutting down."); break; } if (frame.Data == null) { Logger.Error("We received no data from the frame so we cannot get the event payload!"); break; } EventPayload eventPayload = null; try { eventPayload = frame.GetObject<EventPayload>(); } catch (Exception ex) { Logger.Error("Failed to parse event! {0}", ex.Message); Logger.Error("Data: {0}", frame.Message); } try { if (eventPayload != null) { ProcessFrame(eventPayload); } } catch (Exception ex2) { Logger.Error("Failed to process event! {0}", ex2.Message); Logger.Error("Data: {0}", frame.Message); } break; } default: Logger.Error("Invalid opcode: {0}", frame.Opcode); flag = false; break; } } if (!aborting && namedPipe.IsConnected) { ProcessCommandQueue(); queueUpdatedEvent.WaitOne(POLL_RATE); } } Logger.Trace("Left main read loop for some reason. Aborting: {0}, Shutting Down: {1}", aborting, shutdown); } else { Logger.Error("Failed to connect for some reason."); EnqueueMessage(new ConnectionFailedMessage { FailedPipe = targetPipe }); } if (!aborting && !shutdown) { long num = delay.NextDelay(); Logger.Trace("Waiting {0}ms before attempting to connect again", num); Thread.Sleep(delay.NextDelay()); } } catch (Exception ex3) { Logger.Error("Unhandled Exception: {0}", ex3.GetType().FullName); Logger.Error(ex3.Message); Logger.Error(ex3.StackTrace); } finally { if (namedPipe.IsConnected) { Logger.Trace("Closing the named pipe."); namedPipe.Close(); } SetConnectionState(RpcState.Disconnected); } } Logger.Trace("Left Main Loop"); if (namedPipe != null) { namedPipe.Dispose(); } Logger.Info("Thread Terminated, no longer performing RPC connection."); } private void ProcessFrame(EventPayload response) { //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Expected O, but got Unknown Logger.Info("Handling Response. Cmd: {0}, Event: {1}", response.Command, response.Event); if (response.Event.HasValue && response.Event.Value == ServerEvent.Error) { Logger.Error("Error received from the RPC"); ErrorMessage @object = response.GetObject<ErrorMessage>(); Logger.Error("Server responded with an error message: ({0}) {1}", @object.Code.ToString(), @object.Message); EnqueueMessage(@object); } else if (State == RpcState.Connecting && response.Command == Command.Dispatch && response.Event.HasValue && response.Event.Value == ServerEvent.Ready) { Logger.Info("Connection established with the RPC"); SetConnectionState(RpcState.Connected); delay.Reset(); ReadyMessage object2 = response.GetObject<ReadyMessage>(); lock (l_config) { _configuration = object2.Configuration; object2.User.SetConfiguration(_configuration); } EnqueueMessage(object2); } else if (State == RpcState.Connected) { switch (response.Command) { case Command.Dispatch: ProcessDispatch(response); break; case Command.SetActivity: { if (response.Data == null) { EnqueueMessage(new PresenceMessage()); break; } RichPresenceResponse object3 = response.GetObject<RichPresenceResponse>(); EnqueueMessage(new PresenceMessage(object3)); break; } case Command.Subscribe: case Command.Unsubscribe: { JsonSerializer val = new JsonSerializer(); ((Collection<JsonConverter>)(object)val.Converters).Add((JsonConverter)(object)new EnumSnakeCaseConverter()); ServerEvent value = response.GetObject<EventPayload>().Event.Value; if (response.Command == Command.Subscribe) { EnqueueMessage(new SubscribeMessage(value)); } else { EnqueueMessage(new UnsubscribeMessage(value)); } break; } case Command.SendActivityJoinInvite: Logger.Trace("Got invite response ack."); break; case Command.CloseActivityJoinRequest: Logger.Trace("Got invite response reject ack."); break; default: Logger.Error("Unkown frame was received! {0}", response.Command); break; } } else { Logger.Trace("Received a frame while we are disconnected. Ignoring. Cmd: {0}, Event: {1}", response.Command, response.Event); } } private void ProcessDispatch(EventPayload response) { if (response.Command == Command.Dispatch && response.Event.HasValue) { switch (response.Event.Value) { case ServerEvent.ActivitySpectate: { SpectateMessage object3 = response.GetObject<SpectateMessage>(); EnqueueMessage(object3); break; } case ServerEvent.ActivityJoin: { JoinMessage object2 = response.GetObject<JoinMessage>(); EnqueueMessage(object2); break; } case ServerEvent.ActivityJoinRequest: { JoinRequestMessage @object = response.GetObject<JoinRequestMessage>(); EnqueueMessage(@object); break; } default: Logger.Warning("Ignoring {0}", response.Event.Value); break; } } } private void ProcessCommandQueue() { if (State != RpcState.Connected) { return; } if (aborting) { Logger.Warning("We have been told to write a queue but we have also been aborted."); } bool flag = true; ICommand command = null; while (flag && namedPipe.IsConnected) { lock (l_rtqueue) { flag = _rtqueue.Count > 0; if (!flag) { break; } command = _rtqueue.Peek(); } if (shutdown || (!aborting && LOCK_STEP)) { flag = false; } IPayload payload = command.PreparePayload(GetNextNonce()); Logger.Trace("Attempting to send payload: {0}", payload.Command); PipeFrame frame = default(PipeFrame); if (command is CloseCommand) { SendHandwave(); Logger.Trace("Handwave sent, ending queue processing."); lock (l_rtqueue) { _rtqueue.Dequeue(); break; } } if (aborting) { Logger.Warning("- skipping frame because of abort."); lock (l_rtqueue) { _rtqueue.Dequeue(); } continue; } frame.SetObject(Opcode.Frame, payload); Logger.Trace("Sending payload: {0}", payload.Command); if (namedPipe.WriteFrame(frame)) { Logger.Trace("Sent Successfully."); lock (l_rtqueue) { _rtqueue.Dequeue(); } continue; } Logger.Warning("Something went wrong during writing!"); break; } } private void EstablishHandshake() { Logger.Trace("Attempting to establish a handshake..."); if (State != 0) { Logger.Error("State must be disconnected in order to start a handshake!"); return; } Logger.Trace("Sending Handshake..."); if (!namedPipe.WriteFrame(new PipeFrame(Opcode.Handshake, new Handshake { Version = VERSION, ClientID = applicationID }))) { Logger.Error("Failed to write a handshake."); } else { SetConnectionState(RpcState.Connecting); } } private void SendHandwave() { Logger.Info("Attempting to wave goodbye..."); if (State == RpcState.Disconnected) { Logger.Error("State must NOT be disconnected in order to send a handwave!"); } else if (!namedPipe.WriteFrame(new PipeFrame(Opcode.Close, new Handshake { Version = VERSION, ClientID = applicationID }))) { Logger.Error("failed to write a handwave."); } } public bool AttemptConnection() { Logger.Info("Attempting a new connection"); if (thread != null) { Logger.Error("Cannot attempt a new connection as the previous connection thread is not null!"); return false; } if (State != 0) { Logger.Warning("Cannot attempt a new connection as the previous connection hasn't changed state yet."); return false; } if (aborting) { Logger.Error("Cannot attempt a new connection while aborting!"); return false; } thread = new Thread(MainLoop); thread.Name = "Discord IPC Thread"; thread.IsBackground = true; thread.Start(); return true; } private void SetConnectionState(RpcState state) { Logger.Trace("Setting the connection state to {0}", state.ToString().ToSnakeCase().ToUpperInvariant()); lock (l_states) { _state = state; } } public void Shutdown() { Logger.Trace("Initiated shutdown procedure"); shutdown = true; lock (l_rtqueue) { _rtqueue.Clear(); if (CLEAR_ON_SHUTDOWN) { _rtqueue.Enqueue(new PresenceCommand { PID = processID, Presence = null }); } _rtqueue.Enqueue(new CloseCommand()); } queueUpdatedEvent.Set(); } public void Close() { if (thread == null) { Logger.Error("Cannot close as it is not available!"); return; } if (aborting) { Logger.Error("Cannot abort as it has already been aborted"); return; } if (ShutdownOnly) { Shutdown(); return; } Logger.Trace("Updating Abort State..."); aborting = true; queueUpdatedEvent.Set(); } public void Dispose() { ShutdownOnly = false; Close(); } } internal enum RpcState { Disconnected, Connecting, Connected } } namespace DiscordRPC.RPC.Payload { internal class ClosePayload : IPayload { [JsonProperty("code")] public int Code { get; set; } [JsonProperty("message")] public string Reason { get; set; } [JsonConstructor] public ClosePayload() { Code = -1; Reason = ""; } } internal enum Command { [EnumValue("DISPATCH")] Dispatch, [EnumValue("SET_ACTIVITY")] SetActivity, [EnumValue("SUBSCRIBE")] Subscribe, [EnumValue("UNSUBSCRIBE")] Unsubscribe, [EnumValue("SEND_ACTIVITY_JOIN_INVITE")] SendActivityJoinInvite, [EnumValue("CLOSE_ACTIVITY_JOIN_REQUEST")] CloseActivityJoinRequest, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] Authorize, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] Authenticate, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetGuild, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetGuilds, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetChannel, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetChannels, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] SetUserVoiceSettings, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] SelectVoiceChannel, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetSelectedVoiceChannel, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] SelectTextChannel, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetVoiceSettings, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] SetVoiceSettings, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] CaptureShortcut } internal abstract class IPayload { [JsonProperty("cmd")] [JsonConverter(typeof(EnumSnakeCaseConverter))] public Command Command { get; set; } [JsonProperty("nonce")] public string Nonce { get; set; } protected IPayload() { } protected IPayload(long nonce) { Nonce = nonce.ToString(); } public override string ToString() { return $"Payload || Command: {Command}, Nonce: {Nonce}"; } } internal class ArgumentPayload : IPayload { [JsonProperty(/*Could not decode attribute arguments.*/)] public JObject Arguments { get; set; } public ArgumentPayload() { Arguments = null; } public ArgumentPayload(long nonce) : base(nonce) { Arguments = null; } public ArgumentPayload(object args, long nonce) : base(nonce) { SetObject(args); } public void SetObject(object obj) { Arguments = JObject.FromObject(obj); } public T GetObject<T>() { return ((JToken)Arguments).ToObject<T>(); } public override string ToString() { return "Argument " + base.ToString(); } } internal class EventPayload : IPayload { [JsonProperty(/*Could not decode attribute arguments.*/)] public JObject Data { get; set; } [JsonProperty("evt")] [JsonConverter(typeof(EnumSnakeCaseConverter))] public ServerEvent? Event { get; set; } public EventPayload() { Data = null; } public EventPayload(long nonce) : base(nonce) { Data = null; } public T GetObject<T>() { if (Data == null) { return default(T); } return ((JToken)Data).ToObject<T>(); } public override string ToString() { return "Event " + base.ToString() + ", Event: " + (Event.HasValue ? Event.ToString() : "N/A"); } } internal enum ServerEvent { [EnumValue("READY")] Ready, [EnumValue("ERROR")] Error, [EnumValue("ACTIVITY_JOIN")] ActivityJoin, [EnumValue("ACTIVITY_SPECTATE")] ActivitySpectate, [EnumValue("ACTIVITY_JOIN_REQUEST")] ActivityJoinRequest } } namespace DiscordRPC.RPC.Commands { internal class CloseCommand : ICommand { [JsonProperty("close_reason")] public string value = "Unity 5.5 doesn't handle thread aborts. Can you please close me discord?"; [JsonProperty("pid")] public int PID { get; set; } public IPayload PreparePayload(long nonce) { return new ArgumentPayload { Command = Command.Dispatch, Nonce = null, Arguments = null }; } } internal interface ICommand { IPayload PreparePayload(long nonce); } internal class PresenceCommand : ICommand { [JsonProperty("pid")] public int PID { get; set; } [JsonProperty("activity")] public RichPresence Presence { get; set; } public IPayload PreparePayload(long nonce) { return new ArgumentPayload(this, nonce) { Command = Command.SetActivity }; } } internal class RespondCommand : ICommand { [JsonProperty("user_id")] public string UserID { get; set; } [JsonIgnore] public bool Accept { get; set; } public IPayload PreparePayload(long nonce) { return new ArgumentPayload(this, nonce) { Command = (Accept ? Command.SendActivityJoinInvite : Command.CloseActivityJoinRequest) }; } } internal class SubscribeCommand : ICommand { public ServerEvent Event { get; set; } public bool IsUnsubscribe { get; set; } public IPayload PreparePayload(long nonce) { return new EventPayload(nonce) { Command = (IsUnsubscribe ? Command.Unsubscribe : Command.Subscribe), Event = Event }; } } } namespace DiscordRPC.Registry { internal interface IUriSchemeCreator { bool RegisterUriScheme(UriSchemeRegister register); } internal class MacUriSchemeCreator : IUriSchemeCreator { private ILogger logger; public MacUriSchemeCreator(ILogger logger) { this.logger = logger; } public bool RegisterUriScheme(UriSchemeRegister register) { string executablePath = register.ExecutablePath; if (string.IsNullOrEmpty(executablePath)) { logger.Error("Failed to register because the application could not be located."); return false; } logger.Trace("Registering Steam Command"); string text = executablePath; if (register.UsingSteamApp) { text = "steam://rungameid/" + register.SteamAppID; } else { logger.Warning("This library does not fully support MacOS URI Scheme Registration."); } string text2 = "~/Library/Application Support/discord/games"; DirectoryInfo directoryInfo = Directory.CreateDirectory(text2); if (!directoryInfo.Exists) { logger.Error("Failed to register because {0} does not exist", text2); return false; } string text3 = text2 + "/" + register.ApplicationID + ".json"; File.WriteAllText(text3, "{ \"command\": \"" + text + "\" }"); logger.Trace("Registered {0}, {1}", text3, text); return true; } } internal class UnixUriSchemeCreator : IUriSchemeCreator { private ILogger logger; public UnixUriSchemeCreator(ILogger logger) { this.logger = logger; } public bool RegisterUriScheme(UriSchemeRegister register) { string environmentVariable = Environment.GetEnvironmentVariable("HOME"); if (string.IsNullOrEmpty(environmentVariable)) { logger.Error("Failed to register because the HOME variable was not set."); return false; } string executablePath = register.ExecutablePath; if (string.IsNullOrEmpty(executablePath)) { logger.Error("Failed to register because the application was not located."); return false; } string text = null; text = ((!register.UsingSteamApp) ? executablePath : ("xdg-open steam://rungameid/" + register.SteamAppID)); string format = "[Desktop Entry]\r\nName=Game {0}\r\nExec={1} %u\r\nType=Application\r\nNoDisplay=true\r\nCategories=Discord;Games;\r\nMimeType=x-scheme-handler/discord-{2}"; string text2 = string.Format(format, register.ApplicationID, text, register.ApplicationID); string text3 = "/discord-" + register.ApplicationID + ".desktop"; string text4 = environmentVariable + "/.local/share/applications"; DirectoryInfo directoryInfo = Directory.CreateDirectory(text4); if (!directoryInfo.Exists) { logger.Error("Failed to register because {0} does not exist", text4); return false; } File.WriteAllText(text4 + text3, text2); if (!RegisterMime(register.ApplicationID)) { logger.Error("Failed to register because the Mime failed."); return false; } logger.Trace("Registered {0}, {1}, {2}", text4 + text3, text2, text); return true; } private bool RegisterMime(string appid) { string format = "default discord-{0}.desktop x-scheme-handler/discord-{0}"; string arguments = string.Format(format, appid); Process process = Process.Start("xdg-mime", arguments); process.WaitForExit(); return process.ExitCode >= 0; } } internal class UriSchemeRegister { private ILogger _logger; public string ApplicationID { get; set; } public string SteamAppID { get; set; } public bool UsingSteamApp => !string.IsNullOrEmpty(SteamAppID) && SteamAppID != ""; public string ExecutablePath { get; set; } public UriSchemeRegister(ILogger logger, string applicationID, string steamAppID = null, string executable = null) { _logger = logger; ApplicationID = applicationID.Trim(); SteamAppID = steamAppID?.Trim(); ExecutablePath = executable ?? GetApplicationLocation(); } public bool RegisterUriScheme() { IUriSchemeCreator uriSchemeCreator = null; switch (Environment.OSVersion.Platform) { case PlatformID.Win32S: case PlatformID.Win32Windows: case PlatformID.Win32NT: case PlatformID.WinCE: _logger.Trace("Creating Windows Scheme Creator"); uriSchemeCreator = new WindowsUriSchemeCreator(_logger); break; case PlatformID.Unix: _logger.Trace("Creating Unix Scheme Creator"); uriSchemeCreator = new UnixUriSchemeCreator(_logger); break; case PlatformID.MacOSX: _logger.Trace("Creating MacOSX Scheme Creator"); uriSchemeCreator = new MacUriSchemeCreator(_logger); break; default: _logger.Error("Unkown Platform: {0}", Environment.OSVersion.Platform); throw new PlatformNotSupportedException("Platform does not support registration."); } if (uriSchemeCreator.RegisterUriScheme(this)) { _logger.Info("URI scheme registered."); return true; } return false; } public static string GetApplicationLocation() { return Process.GetCurrentProcess().MainModule.FileName; } } internal class WindowsUriSchemeCreator : IUriSchemeCreator { private ILogger logger; public WindowsUriSchemeCreator(ILogger logger) { this.logger = logger; } public bool RegisterUriScheme(UriSchemeRegister register) { if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX) { throw new PlatformNotSupportedException("URI schemes can only be registered on Windows"); } string executablePath = register.ExecutablePath; if (executablePath == null) { logger.Error("Failed to register application because the location was null."); return false; } string scheme = "discord-" + register.ApplicationID; string friendlyName = "Run game " + register.ApplicationID + " protocol"; string defaultIcon = executablePath; string command = executablePath; if (register.UsingSteamApp) { string steamLocation = GetSteamLocation(); if (steamLocation != null) { command = $"\"{steamLocation}\" steam://rungameid/{register.SteamAppID}"; } } CreateUriScheme(scheme, friendlyName, defaultIcon, command); return true; } private void CreateUriScheme(string scheme, string friendlyName, string defaultIcon, string command) { using (RegistryKey registryKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\" + scheme)) { registryKey.SetValue("", "URL:" + friendlyName); registryKey.SetValue("URL Protocol", ""); using (RegistryKey registryKey2 = registryKey.CreateSubKey("DefaultIcon")) { registryKey2.SetValue("", defaultIcon); } using RegistryKey registryKey3 = registryKey.CreateSubKey("shell\\open\\command"); registryKey3.SetValue("", command); } logger.Trace("Registered {0}, {1}, {2}", scheme, friendlyName, command); } public string GetSteamLocation() { using RegistryKey registryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Valve\\Steam"); if (registryKey == null) { return null; } return registryKey.GetValue("SteamExe") as string; } } } namespace DiscordRPC.Message { public class CloseMessage : IMessage { public override MessageType Type => MessageType.Close; public string Reason { get; internal set; } public int Code { get; internal set; } internal CloseMessage() { } internal CloseMessage(string reason) { Reason = reason; } } public class ConnectionEstablishedMessage : IMessage { public override MessageType Type => MessageType.ConnectionEstablished; public int ConnectedPipe { get; internal set; } } public class ConnectionFailedMessage : IMessage { public override MessageType Type => MessageType.ConnectionFailed; public int FailedPipe { get; internal set; } } public class ErrorMessage : IMessage { public override MessageType Type => MessageType.Error; [JsonProperty("code")] public ErrorCode Code { get; internal set; } [JsonProperty("message")] public string Message { get; internal set; } } public enum ErrorCode { Success = 0, PipeException = 1, ReadCorrupt = 2, NotImplemented = 10, UnkownError = 1000, InvalidPayload = 4000, InvalidCommand = 4002, InvalidEvent = 4004 } public abstract class IMessage { private DateTime _timecreated; public abstract MessageType Type { get; } public DateTime TimeCreated => _timecreated; public IMessage() { _timecreated = DateTime.Now; } } public class JoinMessage : IMessage { public override MessageType Type => MessageType.Join; [JsonProperty("secret")] public string Secret { get; internal set; } } public class JoinRequestMessage : IMessage { public override MessageType Type => MessageType.JoinRequest; [JsonProperty("user")] public User User { get; internal set; } } public enum MessageType { Ready, Close, Error, PresenceUpdate, Subscribe, Unsubscribe, Join, Spectate, JoinRequest, ConnectionEstablished, ConnectionFailed } public class PresenceMessage : IMessage { public override MessageType Type => MessageType.PresenceUpdate; public BaseRichPresence Presence { get; internal set; } public string Name { get; internal set; } public string ApplicationID { get; internal set; } internal PresenceMessage() : this(null) { } internal PresenceMessage(RichPresenceResponse rpr) { if (rpr == null) { Presence = null; Name = "No Rich Presence"; ApplicationID = ""; } else { Presence = rpr; Name = rpr.Name; ApplicationID = rpr.ClientID; } } } public class ReadyMessage : IMessage { public override MessageType Type => MessageType.Ready; [JsonProperty("config")] public Configuration Configuration { get; set; } [JsonProperty("user")] public User User { get; set; } [JsonProperty("v")] public int Version { get; set; } } public class SpectateMessage : JoinMessage { public override MessageType Type => MessageType.Spectate; } public class SubscribeMessage : IMessage { public override MessageType Type => MessageType.Subscribe; public EventType Event { get; internal set; } internal SubscribeMessage(ServerEvent evt) { switch (evt) { default: Event = EventType.Join; break; case ServerEvent.ActivityJoinRequest: Event = EventType.JoinRequest; break; case ServerEvent.ActivitySpectate: Event = EventType.Spectate; break; } } } public class UnsubscribeMessage : IMessage { public override MessageType Type => MessageType.Unsubscribe; public EventType Event { get; internal set; } internal UnsubscribeMessage(ServerEvent evt) { switch (evt) { default: Event = EventType.Join; break; case ServerEvent.ActivityJoinRequest: Event = EventType.JoinRequest; break; case ServerEvent.ActivitySpectate: Event = EventType.Spectate; break; } } } } namespace DiscordRPC.Logging { public class ConsoleLogger : ILogger { public LogLevel Level { get; set; } public bool Coloured { get; set; } [Obsolete("Use Coloured")] public bool Colored { get { return Coloured; } set { Coloured = value; } } public ConsoleLogger() { Level = LogLevel.Info; Coloured = false; } public ConsoleLogger(LogLevel level) : this() { Level = level; } public ConsoleLogger(LogLevel level, bool coloured) { Level = level; Coloured = coloured; } public void Trace(string message, params object[] args) { if (Level <= LogLevel.Trace) { if (Coloured) { Console.ForegroundColor = ConsoleColor.Gray; } string text = "TRACE: " + message; if (args.Length != 0) { Console.WriteLine(text, args); } else { Console.WriteLine(text); } } } public void Info(string message, params object[] args) { if (Level <= LogLevel.Info) { if (Coloured) { Console.ForegroundColor = ConsoleColor.White; } string text = "INFO: " + message; if (args.Length != 0) { Console.WriteLine(text, args); } else { Console.WriteLine(text); } } } public void Warning(string message, params object[] args) { if (Level <= LogLevel.Warning) { if (Coloured) { Console.ForegroundColor = ConsoleColor.Yellow; } string text = "WARN: " + message; if (args.Length != 0) { Console.WriteLine(text, args); } else { Console.WriteLine(text); } } } public void Error(string message, params object[] args) { if (Level <= LogLevel.Error) { if (Coloured) { Console.ForegroundColor = ConsoleColor.Red; } string text = "ERR : " + message; if (args.Length != 0) { Console.WriteLine(text, args); } else { Console.WriteLine(text); } } } } public class FileLogger : ILogger { private object filelock; public LogLevel Level { get; set; } public string File { get; set; } public FileLogger(string path) : this(path, LogLevel.Info) { } public FileLogger(string path, LogLevel level) { Level = level; File = path; filelock = new object(); } public void Trace(string message, params object[] args) { if (Level > LogLevel.Trace) { return; } lock (filelock) { System.IO.File.AppendAllText(File, "\r\nTRCE: " + ((args.Length != 0) ? string.Format(message, args) : message)); } } public void Info(string message, params object[] args) { if (Level > LogLevel.Info) { return; } lock (filelock) { System.IO.File.AppendAllText(File, "\r\nINFO: " + ((args.Length != 0) ? string.Format(message, args) : message)); } } public void Warning(string message, params object[] args) { if (Level > LogLevel.Warning) { return; } lock (filelock) { System.IO.File.AppendAllText(File, "\r\nWARN: " + ((args.Length != 0) ? string.Format(message, args) : message)); } } public void Error(string message, params object[] args) { if (Level > LogLevel.Error) { return; } lock (filelock) { System.IO.File.AppendAllText(File, "\r\nERR : " + ((args.Length != 0) ? string.Format(message, args) : message)); } } } public interface ILogger { LogLevel Level { get; set; } void Trace(string message, params object[] args); void Info(string message, params object[] args); void Warning(string message, params object[] args); void Error(string message, params object[] args); } public enum LogLevel { Trace = 1, Info = 2, Warning = 3, Error = 4, None = 256 } public class NullLogger : ILogger { public LogLevel Level { get; set; } public void Trace(string message, params object[] args) { } public void Info(string message, params object[] args) { } public void Warning(string message, params object[] args) { } public void Error(string message, params object[] args) { } } } namespace DiscordRPC.IO { internal class Handshake { [JsonProperty("v")] public int Version { get; set; } [JsonProperty("client_id")] public string ClientID { get; set; } } public interface INamedPipeClient : IDisposable { ILogger Logger { get; set; } bool IsConnected { get; } int ConnectedPipe { get; } bool Connect(int pipe); bool ReadFrame(out PipeFrame frame); bool WriteFrame(PipeFrame frame); void Close(); } public sealed class ManagedNamedPipeClient : INamedPipeClient, IDisposable { private const string PIPE_NAME = "discord-ipc-{0}"; private int _connectedPipe; private NamedPipeClientStream _stream; private byte[] _buffer = new byte[PipeFrame.MAX_SIZE]; private Queue<PipeFrame> _framequeue = new Queue<PipeFrame>(); private object _framequeuelock = new object(); private volatile bool _isDisposed = false; private volatile bool _isClosed = true; private object l_stream = new object(); public ILogger Logger { get; set; } public bool IsConnected { get { if (_isClosed) { return false; } lock (l_stream) { return _stream != null && _stream.IsConnected; } } } public int ConnectedPipe => _connectedPipe; public ManagedNamedPipeClient() { _buffer = new byte[PipeFrame.MAX_SIZE]; Logger = new NullLogger(); _stream = null; } public bool Connect(int pipe) { Logger.Trace("ManagedNamedPipeClient.Connection({0})", pipe); if (_isDisposed) { throw new ObjectDisposedException("NamedPipe"); } if (pipe > 9) { throw new ArgumentOutOfRangeException("pipe", "Argument cannot be greater than 9"); } if (pipe < 0) { for (int i = 0; i < 10; i++) { if (AttemptConnection(i) || AttemptConnection(i, isSandbox: true)) { BeginReadStream(); return true; } } } else if (AttemptConnection(pipe) || AttemptConnection(pipe, isSandbox: true)) { BeginReadStream(); return true; } return false; } private bool AttemptConnection(int pipe, bool isSandbox = false) { if (_isDisposed) { throw new ObjectDisposedException("_stream"); } string text = (isSandbox ? GetPipeSandbox() : ""); if (isSandbox && text == null) { Logger.Trace("Skipping sandbox connection."); return false; } Logger.Trace("Connection Attempt {0} ({1})", pipe, text); string pipeName = GetPipeName(pipe, text); try { lock (l_stream) { Logger.Info("Attempting to connect to '{0}'", pipeName); _stream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); _stream.Connect(0); Logger.Trace("Waiting for connection..."); do { Thread.Sleep(10); } while (!_stream.IsConnected); } Logger.Info("Connected to '{0}'", pipeName); _connectedPipe = pipe; _isClosed = false; } catch (Exception ex) { Logger.Error("Failed connection to {0}. {1}", pipeName, ex.Message); Close(); } Logger.Trace("Done. Result: {0}", _isClosed); return !_isClosed; } private void BeginReadStream() { if (_isClosed) { return; } try { lock (l_stream) { if (_stream != null && _stream.IsConnected) { Logger.Trace("Begining Read of {0} bytes", _buffer.Length); _stream.BeginRead(_buffer, 0, _buffer.Length, EndReadStream, _stream.IsConnected); } } } catch (ObjectDisposedException) { Logger.Warning("Attempted to start reading from a disposed pipe"); } catch (InvalidOperationException) { Logger.Warning("Attempted to start reading from a closed pipe"); } catch (Exception ex3) { Logger.Error("An exception occured while starting to read a stream: {0}", ex3.Message); Logger.Error(ex3.StackTrace); } } private void EndReadStream(IAsyncResult callback) { Logger.Trace("Ending Read"); int num = 0; try { lock (l_stream) { if (_stream == null || !_stream.IsConnected) { return; } num = _stream.EndRead(callback); } } catch (IOException) { Logger.Warning("Attempted to end reading from a closed pipe"); return; } catch (NullReferenceException) { Logger.Warning("Attempted to read from a null pipe"); return; } catch (ObjectDisposedException) { Logger.Warning("Attemped to end reading from a disposed pipe"); return; } catch (Exception ex4) { Logger.Error("An exception occured while ending a read of a stream: {0}", ex4.Message); Logger.Error(ex4.StackTrace); return; } Logger.Trace("Read {0} bytes", num); if (num > 0) { using MemoryStream stream = new MemoryStream(_buffer, 0, num); try { PipeFrame item = default(PipeFrame); if (item.ReadStream(stream)) { Logger.Trace("Read a frame: {0}", item.Opcode); lock (_framequeuelock) { _framequeue.Enqueue(item); } } else { Logger.Error("Pipe failed to read from the data received by the stream."); Close(); } } catch (Exception ex5) { Logger.Error("A exception has occured while trying to parse the pipe data: {0}", ex5.Message); Close(); } } else if (IsUnix()) { Logger.Error("Empty frame was read on {0}, aborting.", Environment.OSVersion); Close(); } else { Logger.Warning("Empty frame was read. Please send report to Lachee."); } if (!_isClosed && IsConnected) { Logger.Trace("Starting another read"); BeginReadStream(); } } public bool ReadFrame(out PipeFrame frame) { if (_isDisposed) { throw new ObjectDisposedException("_stream"); } lock (_framequeuelock) { if (_framequeue.Count == 0) { frame = default(PipeFrame); return false; } frame = _framequeue.Dequeue(); return true; } } public bool WriteFrame(PipeFrame frame) { if (_isDisposed) { throw new ObjectDisposedException("_stream"); } if (_isClosed || !IsConnected) { Logger.Error("Failed to write frame because the stream is closed"); return false; } try { frame.WriteStream(_stream); return true; } catch (IOException ex) { Logger.Error("Failed to write frame because of a IO Exception: {0}", ex.Message); } catch (ObjectDisposedException) { Logger.Warning("Failed to write frame as the stream was already disposed"); } catch (InvalidOperationException) { Logger.Warning("Failed to write frame because of a invalid operation"); } return false; } public void Close() { if (_isClosed) { Logger.Warning("Tried to close a already closed pipe."); return; } try { lock (l_stream) { if (_stream != null) { try { _stream.Flush(); _stream.Dispose(); } catch (Exception) { } _stream = null; _isClosed = true; } else { Logger.Warning("Stream was closed, but no stream was available to begin with!"); } } } catch (ObjectDisposedException) { Logger.Warning("Tried to dispose already disposed stream"); } finally { _isClosed = true; _connectedPipe = -1; } } public void Dispose() { if (_isDisposed) { return; } if (!_isClosed) { Close(); } lock (l_stream) { if (_stream != null) { _stream.Dispose(); _stream = null; } } _isDisposed = true; } public static string GetPipeName(int pipe, string sandbox) { if (!IsUnix()) { return sandbox + $"discord-ipc-{pipe}"; } return Path.Combine(GetTemporaryDirectory(), sandbox + $"discord-ipc-{pipe}"); } public static string GetPipeName(int pipe) { return GetPipeName(pipe, ""); } public static string GetPipeSandbox() { PlatformID platform = Environment.OSVersion.Platform; PlatformID platformID = platform; if (platformID != PlatformID.Unix) { return null; } return "snap.discord/"; } private static string GetTemporaryDirectory() { string text = null; text = text ?? Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR"); text = text ?? Environment.GetEnvironmentVariable("TMPDIR"); text = text ?? Environment.GetEnvironmentVariable("TMP"); text = text ?? Environment.GetEnvironmentVariable("TEMP"); return text ?? "/tmp"; } public static bool IsUnix() { PlatformID platform = Environment.OSVersion.Platform; PlatformID platformID = platform; if (platformID != PlatformID.Unix && platformID != PlatformID.MacOSX) { return false; } return true; } } public enum Opcode : uint { Handshake, Frame, Close, Ping, Pong } public struct PipeFrame : IEquatable<PipeFrame> { public static readonly int MAX_SIZE = 16384; public Opcode Opcode { get; set; } public uint Length => (uint)Data.Length; public byte[] Data { get; set; } public string Message { get { return GetMessage(); } set { SetMessage(value); } } public Encoding MessageEncoding => Encoding.UTF8; public PipeFrame(Opcode opcode, object data) { Opcode = opcode; Data = null; SetObject(data); } private void SetMessage(string str) { Data = MessageEncoding.GetBytes(str); } private string GetMessage() { return MessageEncoding.GetString(Data); } public void SetObject(object obj) { string message = JsonConvert.SerializeObject(obj); SetMessage(message); } public void SetObject(Opcode opcode, object obj) { Opcode = opcode; SetObject(obj); } public T GetObject<T>() { string message = GetMessage(); return JsonConvert.DeserializeObject<T>(message); } public bool ReadStream(Stream stream) { if (!TryReadUInt32(stream, out var value)) { return false; } if (!TryReadUInt32(stream, out var value2)) { return false; } uint num = value2; using MemoryStream memoryStream = new MemoryStream(); uint num2 = (uint)Min(2048, value2); byte[] array = new byte[num2]; int count; while ((count = stream.Read(array, 0, Min(array.Length, num))) > 0) { num -= num2; memoryStream.Write(array, 0, count); } byte[] array2 = memoryStream.ToArray(); if (array2.LongLength != value2) { return false; } Opcode = (Opcode)value; Data = array2; return true; } private int Min(int a, uint b) { if (b >= a) { return a; } return (int)b; } private bool TryReadUInt32(Stream stream, out uint value) { byte[] array = new byte[4]; int num = stream.Read(array, 0, array.Length); if (num != 4) { value = 0u; return false; } value = BitConverter.ToUInt32(array, 0); return true; } public void WriteStream(Stream stream) { byte[] bytes = BitConverter.GetBytes((uint)Opcode); byte[] bytes2 = BitConverter.GetBytes(Length); byte[] array = new byte[bytes.Length + bytes2.Length + Data.Length]; bytes.CopyTo(array, 0); bytes2.CopyTo(array, bytes.Length); Data.CopyTo(array, bytes.Length + bytes2.Length); stream.Write(array, 0, array.Length); } public bool Equals(PipeFrame other) { return Opcode == other.Opcode && Length == other.Length && Data == other.Data; } } } namespace DiscordRPC.Helper { internal class BackoffDelay { private int _current; private int _fails; public int Maximum { get; private set; } public int Minimum { get; private set; } public int Current => _current; public int Fails => _fails; public Random Random { get; set; } private BackoffDelay() { } public BackoffDelay(int min, int max) : this(min, max, new Random()) { } public BackoffDelay(int min, int max, Random random) { Minimum = min; Maximum = max; _current = min; _fails = 0; Random = random; } public void Reset() { _fails = 0; _current = Minimum; } public int NextDelay() { _fails++; double num = (float)(Maximum - Minimum) / 100f; _current = (int)Math.Floor(num * (double)_fails) + Minimum; return Math.Min(Math.Max(_current, Minimum), Maximum); } } public static class StringTools { public static string GetNullOrString(this string str) { return (str.Length == 0 || string.IsNullOrEmpty(str.Trim())) ? null : str; } public static bool WithinLength(this string str, int bytes) { return str.WithinLength(bytes, Encoding.UTF8); } public static bool WithinLength(this string str, int bytes, Encoding encoding) { return encoding.GetByteCount(str) <= bytes; } public static string ToCamelCase(this string str) { return (from s in str?.ToLowerInvariant().Split(new string[2] { "_", " " }, StringSplitOptions.RemoveEmptyEntries) select char.ToUpper(s[0]) + s.Substring(1, s.Length - 1)).Aggregate(string.Empty, (string s1, string s2) => s1 + s2); } public static string ToSnakeCase(this string str) { if (str == null) { return null; } string text = string.Concat(str.Select((char x, int i) => (i > 0 && char.IsUpper(x)) ? ("_" + x) : x.ToString()).ToArray()); return text.ToUpperInvariant(); } } } namespace DiscordRPC.Exceptions { public class BadPresenceException : Exception { internal BadPresenceException(string message) : base(message) { } } public class InvalidConfigurationException : Exception { internal InvalidConfigurationException(string message) : base(message) { } } [Obsolete("Not actually used anywhere")] public class InvalidPipeException : Exception { internal InvalidPipeException(string message) : base(message) { } } public class StringOutOfRangeException : Exception { public int MaximumLength { get; private set; } public int MinimumLength { get; private set; } internal StringOutOfRangeException(string message, int min, int max) : base(message) { MinimumLength = min; MaximumLength = max; } internal StringOutOfRangeException(int minumum, int max) : this($"Length of string is out of range. Expected a value between {minumum} and {max}", minumum, max) { } internal StringOutOfRangeException(int max) : this($"Length of string is out of range. Expected a value with a maximum length of {max}", 0, max) { } } public class UninitializedException : Exception { internal UninitializedException(string message) : base(message) { } internal UninitializedException() : this("Cannot perform action because the client has not been initialized yet or has been deinitialized.") { } } } namespace DiscordRPC.Events { public delegate void OnReadyEvent(object sender, ReadyMessage args); public delegate void OnCloseEvent(object sender, CloseMessage args); public delegate void OnErrorEvent(object sender, ErrorMessage args); public delegate void OnPresenceUpdateEvent(object sender, PresenceMessage args); public delegate void OnSubscribeEvent(object sender, SubscribeMessage args); public delegate void OnUnsubscribeEvent(object sender, UnsubscribeMessage args); public delegate void OnJoinEvent(object sender, JoinMessage args); public delegate void OnSpectateEvent(object sender, SpectateMessage args); public delegate void OnJoinRequestedEvent(object sender, JoinRequestMessage args); public delegate void OnConnectionEstablishedEvent(object sender, ConnectionEstablishedMessage args); public delegate void OnConnectionFailedEvent(object sender, ConnectionFailedMessage args); public delegate void OnRpcMessageEvent(object sender, IMessage msg); } namespace DiscordRPC.Converters { internal class EnumSnakeCaseConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType.IsEnum; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.Value == null) { return null; } object obj = null; if (TryParseEnum(objectType, (string)reader.Value, out obj)) { return obj; } return existingValue; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { Type type = value.GetType(); string text = Enum.GetName(type, value); MemberInfo[] members = type.GetMembers(BindingFlags.Static | BindingFlags.Public); MemberInfo[] array = members; foreach (MemberInfo memberInfo in array) { if (memberInfo.Name.Equals(text)) { object[] customAttributes = memberInfo.GetCustomAttributes(typeof(EnumValueAttribute), inherit: true); if (customAttributes.Length != 0) { text = ((EnumValueAttribute)customAttributes[0]).Value; } } } writer.WriteValue(text); } public bool TryParseEnum(Type enumType, string str, out object obj) { if (str == null) { obj = null; return false; } Type type = enumType; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { type = type.GetGenericArguments().First(); } if (!type.IsEnum) { obj = null; return false; } MemberInfo[] members = type.GetMembers(BindingFlags.Static | BindingFlags.Public); MemberInfo[] array = members; foreach (MemberInfo memberInfo in array) { object[] customAttributes = memberInfo.GetCustomAttributes(typeof(EnumValueAttribute), inherit: true); object[] array2 = customAttributes; foreach (object obj2 in array2) { EnumValueAttribute enumValueAttribute = (EnumValueAttribute)obj2; if (str.Equals(enumValueAttribute.Value)) { obj = Enum.Parse(type, memberInfo.Name, ignoreCase: true); return true; } } } obj = null; return false; } } internal class EnumValueAttribute : Attribute { public string Value { get; set; } public EnumValueAttribute(string value) { Value = value; } } }
BepInEx/plugins/TheArchive.RichPresence/plugins/TheArchive.RichPresence.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using AIGraph; using Agents; using CellMenu; using Clonesoft.Json; using DiscordRPC; using DiscordRPC.Events; using DiscordRPC.IO; using DiscordRPC.Logging; using DiscordRPC.Message; using GameData; using Gear; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using LevelGeneration; using Player; using SNetwork; using TheArchive.Core; using TheArchive.Core.Attributes; using TheArchive.Core.Attributes.Feature; using TheArchive.Core.Attributes.Feature.Members; using TheArchive.Core.Attributes.Feature.Patches; using TheArchive.Core.Attributes.Feature.Settings; using TheArchive.Core.FeaturesAPI; using TheArchive.Core.FeaturesAPI.Groups; using TheArchive.Core.Localization; using TheArchive.Core.Managers; using TheArchive.Core.Models; using TheArchive.Core.Settings; using TheArchive.Interfaces; using TheArchive.Loader; using TheArchive.Utilities; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyFileVersion("2025.2.0")] [assembly: AssemblyInformationalVersion("2025.2.0")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("TheArchive.RichPresence")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyProduct("TheArchive.RichPresence")] [assembly: AssemblyTitle("TheArchive.RichPresence")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2025.2.0.0")] [module: UnverifiableCode] internal class ThisAssembly { public class Git { public class BaseVersion { public const string Major = "0"; public const string Minor = "0"; public const string Patch = "0"; } public class SemVer { public const string Major = "0"; public const string Minor = "0"; public const string Patch = "838"; public const string Label = ""; public const string DashLabel = ""; public const string Source = "Default"; } public const bool IsDirty = false; public const string IsDirtyString = "false"; public const string RepositoryUrl = "https://github.com/Tuesday1028/GTFO_TheArchive"; public const string Branch = "doing-things"; public const string Commit = "a4eb55a"; public const string Sha = "a4eb55a7e97569a44df6831acc6074b0f084b6eb"; public const string CommitDate = "2026-05-07T19:14:52+08:00"; public const string Commits = "838"; public const string Tag = ""; public const string BaseTag = ""; } } internal class ManifestInfo { internal const string TSName = "TheArchive_RichPresence"; internal const string TSDescription = "Custom Discord Rich Presence Add-on for TheArchive."; internal const string TSVersion = "2025.2.0"; internal const string TSAuthor = "AuriRex"; internal const string TSWebsite = "https://github.com/AuriRex/GTFO_TheArchive"; } namespace TheArchive { [ArchiveModule("dev.AuriRex.gtfo.TheArchive.RichPresence", "TheArchive_RichPresence", "2025.2.0")] public class ArchiveRichPresenceModule : IArchiveModule { public const string GUID = "dev.AuriRex.gtfo.TheArchive.RichPresence"; public const string MOD_NAME = "TheArchive_RichPresence"; public const string VERSION = "2025.2.0"; public ILocalizationService LocalizationService { get; set; } public IArchiveLogger Logger { get; set; } public void Init() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown ArchiveMod.GameDataInitialized += OnGameDataInitialized; try { string @string = Encoding.UTF8.GetString(Utils.GetResource(Assembly.GetExecutingAssembly(), "TheArchive.DiscordGameSDK.LICENSE_DiscordRPC")); AttributionInfo val = new AttributionInfo("DiscordRPC License", @string, "", ""); val.set_Comment("<#fff18e>Huge shoutouts to <#88e2e9>Lachee</color> for her re-implementation of the discord_game_sdks functions in C#!</color>"); val.set_Origin("TheArchive_RichPresence".Replace("_", ".")); Attribution.Add(val); } catch (Exception ex) { Logger.Error("Something went wrong while trying to add Attribution."); Logger.Exception(ex); } } private void OnGameDataInitialized(RundownID _) { try { PresenceFormatter.RegisterAllPresenceFormatProviders(typeof(PresenceManager), false); PresenceFormatter.Setup(); } catch (Exception ex) { Logger.Exception(ex); } } } } namespace TheArchive.Features.Presence { [EnableFeatureByDefault] public class CustomLobbyCodeString : Feature { public class CustomLobbyCodeStringConfig { [FSMaxLength(120)] [FSDisplayName("Format")] public string Format { get; set; } = "LF%OpenSlots% %Rundown%%Expedition% \"%ExpeditionName%\": `%LobbyID%`"; } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class CM_PageSettings_SetupPatch { public static void Postfix(CM_PageSettings __instance) { SetupViaInstance(__instance); } } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class CM_PageLoadout_SetupPatch { public static void Postfix(CM_PageLoadout __instance) { SetupViaInstance(__instance); } } public const string DefaultFormat = "LF%OpenSlots% %Rundown%%Expedition% \"%ExpeditionName%\": `%LobbyID%`"; private static CM_Item _CM_PageLoadout_coppyLobbyIDButton; private static CM_Item _CM_PageSettings_coppyLobbyIDButton; public override string Name => "Copy Lobby ID Format"; public override GroupBase Group => (GroupBase)(object)GroupManager.Presence; public override string Description => "Customize copied lobby code from the 'Copy Lobby ID'-Button on the loadout and settings screens with a custom format."; public static IArchiveLogger FeatureLogger { get; set; } [FeatureConfig] public static CustomLobbyCodeStringConfig Config { get; set; } [SetEnabledStatus] public static bool IsEnabled { get; set; } public override void OnEnable() { if ((Object)(object)_CM_PageLoadout_coppyLobbyIDButton == (Object)null) { SetupViaInstance(CM_PageLoadout.Current); } } public static void CopyLobbyIdToClipboard(int _) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!IsEnabled) { string text2 = (GUIUtility.systemCopyBuffer = PresenceFormatter.FormatPresenceString("%LobbyID%")); FeatureLogger.Notice("Copied lobby id to clipboard: " + text2); return; } GameBuildInfo buildInfo = Feature.BuildInfo; if ((!RundownFlagsExtensions.IsIncludedIn(((GameBuildInfo)(ref buildInfo)).Rundown, RundownFlagsExtensions.ToLatest((RundownFlags)16)) || !IsExternalMatchMakingActive()) && SNet.IsInLobby) { if (string.IsNullOrWhiteSpace(Config.Format)) { Config.Format = "LF%OpenSlots% %Rundown%%Expedition% \"%ExpeditionName%\": `%LobbyID%`"; } string text4 = (GUIUtility.systemCopyBuffer = PresenceFormatter.FormatPresenceString(Config.Format)); FeatureLogger.Notice("Copied lobby id to clipboard: " + text4); } } [MethodImpl(MethodImplOptions.NoInlining)] private static bool IsExternalMatchMakingActive() { return SNet.IsExternalMatchMakingActive; } public static void SetupViaInstance(CM_PageSettings pageSettings) { if (!((Object)(object)pageSettings == (Object)null)) { _CM_PageSettings_coppyLobbyIDButton = pageSettings.m_copyLobbyIdButton; if ((Object)(object)_CM_PageSettings_coppyLobbyIDButton != (Object)null) { FeatureLogger.Info("Hooking CM_PageSettings Copy Lobby ID Button ..."); SharedUtils.SetCMItemEvents(SharedUtils.RemoveCMItemEvents(_CM_PageSettings_coppyLobbyIDButton, true), (Action<int>)CopyLobbyIdToClipboard, (Action<int, bool>)null); } else { FeatureLogger.Warning("[CustomLobbyCodeString] copy lobby id button in CM_PageSettings wasn't found!!!"); } } } public static void SetupViaInstance(CM_PageLoadout pageLoadout) { if (!((Object)(object)pageLoadout == (Object)null)) { _CM_PageLoadout_coppyLobbyIDButton = pageLoadout.m_copyLobbyIdButton; if ((Object)(object)_CM_PageLoadout_coppyLobbyIDButton != (Object)null) { FeatureLogger.Info("Hooking CM_PageLoadout Copy Lobby ID Button ..."); SharedUtils.SetCMItemEvents(SharedUtils.RemoveCMItemEvents(_CM_PageLoadout_coppyLobbyIDButton, true), (Action<int>)CopyLobbyIdToClipboard, (Action<int, bool>)null); } else { FeatureLogger.Warning("[CustomLobbyCodeString] copy lobby id button in CM_PageLoadout wasn't found!!!"); } } } } [RundownConstraint(/*Could not decode attribute arguments.*/)] public class DisableBasePresence : Feature { private bool _firstTime = true; public override string Name => "Disable Built-in Rich Presence"; public override GroupBase Group => (GroupBase)(object)GroupManager.Presence; public override string Description => "Disables the Discord Rich Presence added to the game in Rundown 8."; public override void OnEnable() { if (Feature.DataBlocksReady) { DisableBaseDiscord(); } } public void OnGameStateChanged(eGameStateName state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)state == 4 && _firstTime) { DisableBaseDiscord(); _firstTime = false; } } public override void OnDisable() { if (!Feature.IsApplicationQuitting && Feature.DataBlocksReady) { EnableBaseDiscord(); } } public static void DisableBaseDiscord() { DiscordManager.Current.ToggleDiscord(false); } public static void EnableBaseDiscord() { DiscordManager.Current.ToggleDiscord(true); } } [EnableFeatureByDefault] public class DiscordRichPresence : Feature { public override string Name => "Archive Discord Rich Presence"; public override GroupBase Group => (GroupBase)(object)GroupManager.Presence; public override string Description => "Show the current game state in detail on discord."; public static IArchiveLogger FeatureLogger { get; set; } public override bool InlineSettingsIntoParentMenu => true; public override bool SkipInitialOnEnable => true; public override Type[] ExternalLocalizedTypes => new Type[1] { typeof(RichPresenceSettings) }; [FeatureConfig] public static RichPresenceSettings DiscordRPCSettings { get; set; } public override void Init() { DiscordRPCSettings = DiscordRPCSettings?.FillDefaultDictValues(); PresenceManager.UpdateGameState((PresenceGameState)0); } public override void OnEnable() { if (!Feature.DataBlocksReady || ArchiveDiscordManager.IsEnabled) { return; } try { ArchiveDiscordManager.RenewPartyGuid(); ArchiveDiscordManager.OnActivityJoin += DiscordManager_OnActivityJoin; ArchiveDiscordManager.Enable(DiscordRPCSettings); } catch (Exception ex) { FeatureLogger.Exception(ex); } } public void OnGameStateChanged(eGameStateName state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)state == 4) { FeatureLogger.Debug("Creating a new Party GUID."); ArchiveDiscordManager.RenewPartyGuid(); } } public override void OnDatablocksReady() { ((Feature)this).OnEnable(); } private static void DiscordManager_OnActivityJoin(string secret) { if (ulong.TryParse(secret, out var result) && result != 0L) { CM_Utils.JoinLobby(result, Action.op_Implicit((Action)delegate { FeatureLogger.Success("Successfully joined lobby \"" + secret + "\"!"); }), Action.op_Implicit((Action)delegate { FeatureLogger.Fail("Failed to join lobby \"" + secret + "\"."); })); } } public override void OnDisable() { if (!ArchiveDiscordManager.IsEnabled) { return; } try { ArchiveDiscordManager.OnActivityJoin -= DiscordManager_OnActivityJoin; ArchiveDiscordManager.Disable(); } catch (Exception ex) { FeatureLogger.Exception(ex); } } public override void Update() { ArchiveDiscordManager.Update(); } } [EnableFeatureByDefault] [HideInModSettings] public class ReactorDiscordRPC : Feature { public enum ReactorTypes { Error, Startup, Shutdown } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class LG_WardenObjective_Reactor_Start_Patch { public static void Postfix(LG_WardenObjective_Reactor __instance) { ReactorsInLevel.Add(__instance); FeatureLogger.Debug("Added Reactor to Set: " + GetReactorItemKey(__instance)); } } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class LG_WardenObjective_Reactor_OnDestroy_Patch { public static void Postfix(LG_WardenObjective_Reactor __instance) { LG_WardenObjective_Reactor val = ((IEnumerable<LG_WardenObjective_Reactor>)ReactorsInLevel).FirstOrDefault((Func<LG_WardenObjective_Reactor, bool>)((LG_WardenObjective_Reactor reactor) => GetReactorItemKey(reactor) == GetReactorItemKey(__instance))); ReactorsInLevel.Remove(val); FeatureLogger.Debug("Removed Reactor from Set: " + GetReactorItemKey(val)); } } private static IValueAccessor<LG_WardenObjective_Reactor, float> A_m_currentWaveProgress; private static IValueAccessor<LG_WardenObjective_Reactor, float> A_m_currentDuration; private static IValueAccessor<LG_WardenObjective_Reactor, int> A_m_currentWaveCount; private static IValueAccessor<LG_WardenObjective_Reactor, int> A_m_waveCountMax; private static IValueAccessor<LG_WardenObjective_Reactor, string> A_m_itemKey; private static IValueAccessor<LG_WardenObjective_Reactor, ReactorWaveData> A_m_currentWaveData; private static IValueAccessor<LG_WardenObjective_Reactor, pReactorState> A_m_currentState; private static readonly HashSet<eReactorStatus> _idleReactorStatuses = new HashSet<eReactorStatus> { Utils.GetEnumFromName<eReactorStatus>("Inactive_Idle"), Utils.GetEnumFromName<eReactorStatus>("Active_Idle") }; private static int _lastFrameCount = 0; private static LG_WardenObjective_Reactor _lastReturnedActiveReactor; public override string Name => "ReactorDiscordRPC"; public override GroupBase Group => (GroupBase)(object)GroupManager.Presence; public static IArchiveLogger FeatureLogger { get; set; } [PresenceFormatProvider("ReactorWaveCountMax")] public static int ReactorWaveCountMax => GetWaveCountMax(GetActiveReactor()); [PresenceFormatProvider("ReactorWaveCountCurrent")] public static int ReactorWaveCountCurrent => GetCurrentWaveCount(GetActiveReactor()); [PresenceFormatProvider("ReactorWaveSecondsRemaining")] public static float ReactorWaveSecondsRemaining => GetWaveSecondsRemaining(GetActiveReactor()); [PresenceFormatProvider("ReactorType")] public static string ReactorType => GetReactorType(GetActiveReactor()).ToString(); [PresenceFormatProvider("ReactorWaveEndTime")] public static long ReactorWaveEndTime => DateTimeOffset.UtcNow.ToUnixTimeSeconds() + (long)GetWaveSecondsRemaining(GetActiveReactor()); [PresenceFormatProvider("IsReactorActive")] public static bool IsReactorActive { get { LG_WardenObjective_Reactor activeReactor; return TryGetActiveReactor(out activeReactor); } } [PresenceFormatProvider("IsReactorInIntro")] public static bool IsReactorInIntro => IsReactorInStatus(GetActiveReactor(), (eReactorStatus)2, (eReactorStatus)6); [PresenceFormatProvider("IsReactorWaveOrChaosActive")] public static bool IsReactorWaveOrChaosActive => IsReactorInStatus(GetActiveReactor(), (eReactorStatus)3, (eReactorStatus)8); [PresenceFormatProvider("IsReactorAwaitingVerify")] public static bool IsReactorAwaitingVerify => IsReactorInStatus(GetActiveReactor(), (eReactorStatus)4, (eReactorStatus)7); [PresenceFormatProvider("IsReactorCompleted")] public static bool IsReactorCompleted => IsReactorInStatus(GetActiveReactor(), (eReactorStatus)5, (eReactorStatus)9); [PresenceFormatProvider("IsReactorTypeStartup")] public static bool IsReactorTypeStartup => GetReactorType(GetActiveReactor()) == ReactorTypes.Startup; [PresenceFormatProvider("IsReactorInVerifyFailState")] public static bool IsReactorInVerifyFailState { get { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!TryGetReactorState(GetActiveReactor(), out var state)) { return false; } return state.verifyFailed; } } [PresenceFormatProvider("ReactorVerificationString")] public static string ReactorVerificationString { get { bool isTerminal; string nextCodeOrTerminalSerial = GetNextCodeOrTerminalSerial(GetActiveReactor(), out isTerminal); if (isTerminal) { return "LOG in " + nextCodeOrTerminalSerial; } return "Free Code: " + nextCodeOrTerminalSerial.ToUpper(); } } public static HashSet<LG_WardenObjective_Reactor> ReactorsInLevel { get; } = new HashSet<LG_WardenObjective_Reactor>(); public override void Init() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) GameBuildInfo buildInfo = Feature.BuildInfo; if (RundownFlagsExtensions.IsIncludedIn(((GameBuildInfo)(ref buildInfo)).Rundown, (RundownFlags)1)) { A_m_currentWaveProgress = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, float>("m_currentStateProgress", false); A_m_currentDuration = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, float>("m_currentStateDuration", false); A_m_currentWaveCount = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, int>("m_currentStateCount", false); A_m_waveCountMax = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, int>("m_stateCountMax", false); } else { A_m_currentWaveProgress = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, float>("m_currentWaveProgress", false); A_m_currentDuration = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, float>("m_currentDuration", false); A_m_currentWaveCount = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, int>("m_currentWaveCount", false); A_m_waveCountMax = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, int>("m_waveCountMax", false); } A_m_itemKey = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, string>("m_itemKey", false); A_m_currentWaveData = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, ReactorWaveData>("m_currentWaveData", false); A_m_currentState = AccessorBase.GetValueAccessor<LG_WardenObjective_Reactor, pReactorState>("m_currentState", false); PresenceFormatter.RegisterAllPresenceFormatProviders(typeof(ReactorDiscordRPC), true); } public static string GetNextCodeOrTerminalSerial(LG_WardenObjective_Reactor reactor, out bool isTerminal) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) isTerminal = false; if ((Object)(object)reactor == (Object)null) { return "Error"; } GameBuildInfo buildInfo = Feature.BuildInfo; if (RundownFlagsExtensions.IsIncludedIn(((GameBuildInfo)(ref buildInfo)).Rundown, RundownFlagsExtensions.ToLatest((RundownFlags)2))) { return GetNextCodeOrTerminalR2(reactor, out isTerminal); } return reactor.CurrentStateOverrideCode; } public static string GetNextCodeOrTerminalR2(LG_WardenObjective_Reactor reactor, out bool isTerminal) { isTerminal = false; if ((Object)(object)reactor == (Object)null) { return "Error"; } ReactorWaveData val = A_m_currentWaveData.Get(reactor); if (val.HasVerificationTerminal) { isTerminal = true; return val.VerificationTerminalSerial; } return reactor.CurrentStateOverrideCode; } public static bool IsReactorInStatus(LG_WardenObjective_Reactor reactor, params eReactorStatus[] statuses) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between I4 and Unknown if (!TryGetReactorStatus(reactor, out var status)) { return false; } for (int i = 0; i < statuses.Length; i++) { if ((int)statuses[i] == (int)status) { return true; } } return false; } public static bool TryGetReactorStatus(LG_WardenObjective_Reactor reactor, out eReactorStatus status) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown if ((Object)(object)reactor == (Object)null || !TryGetReactorState(reactor, out var state)) { status = (eReactorStatus)0; return false; } status = (eReactorStatus)(int)state.status; return true; } public static int GetCurrentWaveCount(LG_WardenObjective_Reactor reactor) { if ((Object)(object)reactor == (Object)null) { return -1; } return A_m_currentWaveCount.Get(reactor); } public static int GetWaveCountMax(LG_WardenObjective_Reactor reactor) { if ((Object)(object)reactor == (Object)null) { return -1; } return A_m_waveCountMax.Get(reactor); } public static float GetWaveSecondsRemaining(LG_WardenObjective_Reactor reactor) { if ((Object)(object)reactor == (Object)null) { return -1f; } return (1f - A_m_currentWaveProgress.Get(reactor)) * A_m_currentDuration.Get(reactor); } public static ReactorTypes GetReactorType(LG_WardenObjective_Reactor reactor) { if (TryGetReactorState(reactor, out var state)) { if (((object)(eReactorStatus)(ref state.status)).ToString().StartsWith("Start")) { return ReactorTypes.Startup; } if (((object)(eReactorStatus)(ref state.status)).ToString().StartsWith("Shut")) { return ReactorTypes.Shutdown; } } return ReactorTypes.Error; } public static bool TryGetReactorState(LG_WardenObjective_Reactor reactor, out pReactorState state) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)reactor == (Object)null) { state = default(pReactorState); return false; } state = A_m_currentState.Get(reactor); return true; } public static LG_WardenObjective_Reactor GetActiveReactor() { int frameCount = Time.frameCount; if (_lastFrameCount == frameCount) { return _lastReturnedActiveReactor; } pReactorState state; LG_WardenObjective_Reactor? obj = ((IEnumerable<LG_WardenObjective_Reactor>)ReactorsInLevel).FirstOrDefault((Func<LG_WardenObjective_Reactor, bool>)((LG_WardenObjective_Reactor reactor) => TryGetReactorState(reactor, out state) && !_idleReactorStatuses.Contains(state.status))); _lastFrameCount = frameCount; _lastReturnedActiveReactor = obj; return obj; } public static bool TryGetActiveReactor(out LG_WardenObjective_Reactor activeReactor) { activeReactor = GetActiveReactor(); return (Object)(object)activeReactor != (Object)null; } public static string GetReactorItemKey(LG_WardenObjective_Reactor reactor) { return A_m_itemKey.Get(reactor); } } [EnableFeatureByDefault] [HideInModSettings] public class RichPresenceCore : Feature { [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class RundownManager__GetUniqueExpeditionKey__Patch { public static void Postfix(string rundownKey, eRundownTier tier, int expIndex) { ExpeditionNumber = expIndex + 1; } } public override string Name => "Rich Presence Core"; public override GroupBase Group => (GroupBase)(object)GroupManager.Presence; public override string Description => "Updates the Presence Game State and provides some values via patches."; [PresenceFormatProvider("EquippedMeleeWeaponName")] public static string EquippedMeleeWeaponName => ItemNameForSlot((InventorySlot)10); [PresenceFormatProvider("EquippedMeleeWeaponID")] public static string EquippedMeleeWeaponID => ItemIDForSlot((InventorySlot)10); [PresenceFormatProvider("EquippedToolName")] public static string EquippedToolName => ItemNameForSlot((InventorySlot)3); [PresenceFormatProvider("EquippedToolID")] public static string EquippedToolID => ItemIDForSlot((InventorySlot)3); private static PlayerAmmoStorage LocalAmmo { get { PlayerBackpack localBackpack = PlayerBackpackManager.LocalBackpack; if (localBackpack == null) { return null; } return localBackpack.AmmoStorage; } } [PresenceFormatProvider("HealthRaw")] public static float HealthRaw { get { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); float? obj; if (localPlayerAgent == null) { obj = null; } else { Dam_PlayerDamageBase damage = localPlayerAgent.Damage; obj = ((damage != null) ? new float?(((Dam_SyncedDamageBase)damage).Health) : null); } float? num = obj; return num.GetValueOrDefault(-1f); } } [PresenceFormatProvider("MaxHealthRaw")] public static float MaxHealthRaw { get { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); float? obj; if (localPlayerAgent == null) { obj = null; } else { Dam_PlayerDamageBase damage = localPlayerAgent.Damage; obj = ((damage != null) ? new float?(((Dam_SyncedDamageBase)damage).HealthMax) : null); } float? num = obj; return num.GetValueOrDefault(-1f); } } [PresenceFormatProvider("ToolAmmo")] public static int ToolAmmo { get { PlayerAmmoStorage localAmmo = LocalAmmo; int? obj; if (localAmmo == null) { obj = null; } else { InventorySlotAmmo classAmmo = localAmmo.ClassAmmo; obj = ((classAmmo != null) ? new int?(classAmmo.BulletsInPack) : null); } int? num = obj; return num.GetValueOrDefault(-1); } } [PresenceFormatProvider("MaxToolAmmo")] public static int MaxToolAmmo { get { PlayerAmmoStorage localAmmo = LocalAmmo; int? obj; if (localAmmo == null) { obj = null; } else { InventorySlotAmmo classAmmo = localAmmo.ClassAmmo; obj = ((classAmmo != null) ? new int?(classAmmo.BulletsMaxCap) : null); } int? num = obj; return num.GetValueOrDefault(-1); } } [PresenceFormatProvider("PrimaryAmmo")] public static int PrimaryAmmo { get { PlayerAmmoStorage localAmmo = LocalAmmo; int? obj; if (localAmmo == null) { obj = null; } else { InventorySlotAmmo standardAmmo = localAmmo.StandardAmmo; obj = ((standardAmmo != null) ? new int?(standardAmmo.BulletsInPack) : null); } int? num = obj; return num.GetValueOrDefault(-1) + GetClip((InventorySlot)1); } } [PresenceFormatProvider("MaxPrimaryAmmo")] public static int MaxPrimaryAmmo { get { PlayerAmmoStorage localAmmo = LocalAmmo; int? obj; if (localAmmo == null) { obj = null; } else { InventorySlotAmmo standardAmmo = localAmmo.StandardAmmo; obj = ((standardAmmo != null) ? new int?(standardAmmo.BulletsMaxCap) : null); } int? num = obj; return num.GetValueOrDefault(-1); } } [PresenceFormatProvider("SpecialAmmo")] public static int SpecialAmmo { get { PlayerAmmoStorage localAmmo = LocalAmmo; int? obj; if (localAmmo == null) { obj = null; } else { InventorySlotAmmo specialAmmo = localAmmo.SpecialAmmo; obj = ((specialAmmo != null) ? new int?(specialAmmo.BulletsInPack) : null); } int? num = obj; return num.GetValueOrDefault(-1) + GetClip((InventorySlot)2); } } [PresenceFormatProvider("MaxSpecialAmmo")] public static int MaxSpecialAmmo { get { PlayerAmmoStorage localAmmo = LocalAmmo; int? obj; if (localAmmo == null) { obj = null; } else { InventorySlotAmmo specialAmmo = localAmmo.SpecialAmmo; obj = ((specialAmmo != null) ? new int?(specialAmmo.BulletsMaxCap) : null); } int? num = obj; return num.GetValueOrDefault(-1); } } [PresenceFormatProvider("HasLobby")] public static bool HasLobby { get { SNet_Lobby lobby = SNet.Lobby; if (lobby == null) { return false; } SNet_LobbyIdentifier identifier = lobby.Identifier; if (identifier == null) { return false; } _ = identifier.ID; return true; } } [PresenceFormatProvider("LobbyID")] public static string LobbyID { get { SNet_Lobby lobby = SNet.Lobby; object obj; if (lobby == null) { obj = null; } else { SNet_LobbyIdentifier identifier = lobby.Identifier; obj = ((identifier != null) ? identifier.ID.ToString() : null); } if (obj == null) { obj = "0123456789"; } return (string)obj; } } [PresenceFormatProvider("LocalCharacterID")] public static int LocalCharacterID { get { try { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); return (localPlayerAgent != null) ? localPlayerAgent.CharacterID : 0; } catch (Exception) { return 0; } } } [PresenceFormatProvider("OpenSlots")] public static int OpenSlots => MaxPlayerSlots - GetPlayerCount(); [PresenceFormatProvider("MaxPlayerSlots")] public static int MaxPlayerSlots => GetMaxPlayers(); [PresenceFormatProvider("ExpeditionTier")] public static string ExpeditionTier { get { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) ExpeditionInTierData activeExpedition = RundownManager.ActiveExpedition; object obj; if (activeExpedition == null) { obj = null; } else { DescriptiveData descriptive = activeExpedition.Descriptive; obj = ((descriptive != null) ? descriptive.Prefix : null); } if (obj == null) { obj = "?"; } string text = (string)obj; GameBuildInfo buildInfo = Feature.BuildInfo; if (RundownFlagsExtensions.IsIncludedIn(((GameBuildInfo)(ref buildInfo)).Rundown, (RundownFlags)6) && text.Length > 2) { return text.Substring(2); } return text; } } [PresenceFormatProvider("ExpeditionTierIsSpecial")] public static bool ExpeditionTierIsSpecial { get { if (Is.R6OrLater) { return IsActiveExpeditionSpecial(); } return false; } } [PresenceFormatProvider("ExpeditionSkipExpNumberInName")] public static bool ExpeditionSkipExpNumberInName { get { try { if (Is.R6OrLater) { return ShouldSkipExpNumberInName(); } } catch { } return false; } } [PresenceFormatProvider("ExpeditionNumber")] public static int ExpeditionNumber { get; set; } [PresenceFormatProvider("ExpeditionName")] public static string ExpeditionName { get { ExpeditionInTierData activeExpedition = RundownManager.ActiveExpedition; object obj; if (activeExpedition == null) { obj = null; } else { DescriptiveData descriptive = activeExpedition.Descriptive; obj = ((descriptive != null) ? descriptive.PublicName : null); } if (obj == null) { obj = "???"; } return (string)obj; } } [PresenceFormatProvider("ZonePrefix")] public static string ZonePrefix { get { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); object obj; if (localPlayerAgent == null) { obj = null; } else { AIG_CourseNode courseNode = ((Agent)localPlayerAgent).CourseNode; if (courseNode == null) { obj = null; } else { LG_Zone zone = courseNode.m_zone; if (zone == null) { obj = null; } else { LG_NavInfo navInfo = zone.NavInfo; obj = ((navInfo != null) ? navInfo.PrefixShort : null); } } } if (obj == null) { obj = "?"; } return (string)obj; } } [PresenceFormatProvider("ZonePrefixLong")] public static string ZonePrefixLong { get { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); object obj; if (localPlayerAgent == null) { obj = null; } else { AIG_CourseNode courseNode = ((Agent)localPlayerAgent).CourseNode; if (courseNode == null) { obj = null; } else { LG_Zone zone = courseNode.m_zone; if (zone == null) { obj = null; } else { LG_NavInfo navInfo = zone.NavInfo; obj = ((navInfo != null) ? navInfo.PrefixLong : null); } } } if (obj == null) { obj = "?"; } return (string)obj; } } [PresenceFormatProvider("ZoneAlias")] public static string ZoneAlias { get { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); object obj; if (localPlayerAgent == null) { obj = null; } else { AIG_CourseNode courseNode = ((Agent)localPlayerAgent).CourseNode; if (courseNode == null) { obj = null; } else { LG_Zone zone = courseNode.m_zone; if (zone == null) { obj = null; } else { LG_NavInfo navInfo = zone.NavInfo; obj = ((navInfo != null) ? navInfo.Number.ToString() : null); } } } if (obj == null) { obj = "?"; } return (string)obj; } } [PresenceFormatProvider("AreaSuffix")] public static string AreaSuffix { get { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); object obj; if (localPlayerAgent == null) { obj = null; } else { AIG_CourseNode courseNode = ((Agent)localPlayerAgent).CourseNode; if (courseNode == null) { obj = null; } else { LG_Area area = courseNode.m_area; if (area == null) { obj = null; } else { LG_NavInfo navInfo = area.m_navInfo; obj = ((navInfo != null) ? navInfo.Suffix : null); } } } if (obj == null) { obj = "?"; } return (string)obj; } } [PresenceFormatProvider("RundownTitleFromDataBlocks")] public static string RundownTitleFromDataBlocks => SharedUtils.GetDataBlockRundownTitle(); public override void Init() { PresenceFormatter.RegisterAllPresenceFormatProviders(typeof(RichPresenceCore), true); } public void OnGameStateChanged(eGameStateName nextState) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected I4, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Invalid comparison between Unknown and I4 //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Invalid comparison between Unknown and I4 switch (nextState - 4) { case 0: PresenceManager.UpdateGameState((PresenceGameState)1, (int)PresenceManager.CurrentState == 0); break; case 1: case 12: PresenceManager.UpdateGameState((PresenceGameState)2); break; case 2: PresenceManager.UpdateGameState((PresenceGameState)3); break; case 3: PresenceManager.UpdateGameState((PresenceGameState)4, keepTimer: true); break; case 6: PresenceManager.UpdateGameState((PresenceGameState)5, (int)PresenceManager.CurrentState == 6); break; case 11: PresenceManager.UpdateGameState((PresenceGameState)6, keepTimer: true); break; case 10: PresenceManager.UpdateGameState((PresenceGameState)7, keepTimer: true); break; case 4: case 5: case 7: case 8: case 9: break; } } public static string ItemNameForSlot(InventorySlot slot) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) BackpackItem val = null; PlayerBackpack localBackpack = PlayerBackpackManager.LocalBackpack; if (localBackpack != null && localBackpack.TryGetBackpackItem(slot, ref val)) { if (val == null) { return null; } GearIDRange gearIDRange = val.GearIDRange; if (gearIDRange == null) { return null; } return gearIDRange.PublicGearName; } return null; } public static string ItemIDForSlot(InventorySlot slot) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) BackpackItem val = null; PlayerBackpack localBackpack = PlayerBackpackManager.LocalBackpack; if (localBackpack != null && localBackpack.TryGetBackpackItem(slot, ref val)) { if (val == null) { return null; } GearIDRange gearIDRange = val.GearIDRange; if (gearIDRange == null) { return null; } return gearIDRange.PlayfabItemId; } return null; } private static int GetClip(InventorySlot slot) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) BackpackItem val = null; PlayerBackpack localBackpack = PlayerBackpackManager.LocalBackpack; if (localBackpack != null && localBackpack.TryGetBackpackItem(slot, ref val)) { int? obj; if (val == null) { obj = null; } else { Item instance = val.Instance; if (instance == null) { obj = null; } else { ItemEquippable obj2 = ((Il2CppObjectBase)instance).TryCast<ItemEquippable>(); obj = ((obj2 != null) ? new int?(obj2.GetCurrentClip()) : null); } } int? num = obj; return num.GetValueOrDefault(-1); } return -1; } private static int GetPlayerCount() { if (Is.R6OrLater) { return GetPlayerCountR6Plus(); } SNet_Lobby lobby = SNet.Lobby; return ((lobby == null) ? null : lobby.Players?.Count).GetValueOrDefault(1); } private static int GetPlayerCountR6Plus() { SNet_Lobby lobby = SNet.Lobby; int? obj; if (lobby == null) { obj = null; } else { List<SNet_Player> players = lobby.Players; obj = ((players == null) ? null : SharedUtils.ToSystemList<SNet_Player>(players)?.Where((SNet_Player ply) => !ply.IsBot)?.Count()); } int? num = obj; return num.GetValueOrDefault(1); } private static int GetMaxPlayers() { if (Is.R6OrLater) { return GetMaxPlayersR6Plus(); } return ((Il2CppArrayBase<SNet_Slot>)(object)SNet.Slots.PlayerSlots).Count; } private static int GetMaxPlayersR6Plus() { int num = 0; int count = ((Il2CppArrayBase<SNet_Slot>)(object)SNet.Slots.PlayerSlots).Count; for (int i = 0; i < count; i++) { if (SNet.Slots.IsBotPermittedInSlot(i) || SNet.Slots.IsHumanPermittedInSlot(i)) { num++; } } if (num != 0) { return num; } return count; } [MethodImpl(MethodImplOptions.NoInlining)] public static bool IsActiveExpeditionSpecial() { ExpeditionInTierData activeExpedition = RundownManager.ActiveExpedition; bool? obj; if (activeExpedition == null) { obj = null; } else { DescriptiveData descriptive = activeExpedition.Descriptive; obj = ((descriptive != null) ? new bool?(descriptive.IsExtraExpedition) : null); } bool? flag = obj; return flag.GetValueOrDefault(); } [MethodImpl(MethodImplOptions.NoInlining)] public static bool ShouldSkipExpNumberInName() { ExpeditionInTierData activeExpedition = RundownManager.ActiveExpedition; bool? obj; if (activeExpedition == null) { obj = null; } else { DescriptiveData descriptive = activeExpedition.Descriptive; obj = ((descriptive != null) ? new bool?(descriptive.SkipExpNumberInName) : null); } bool? flag = obj; return flag.GetValueOrDefault(); } } [EnableFeatureByDefault] public class SteamRichPresenceTweaks : Feature { public class SteamRPCSettings { [FSDisplayName("Disable <color=orange>ALL</color> of Steam RPC")] [FSDescription("Enabling this will completely disable relaying your current in-game status as well as <b>prevent anyone from joining on you via steam</b>.")] public bool DisableSteamRPC { get; set; } [FSDisplayName("Custom Status Format")] public string CustomSteamRPCFormat { get; set; } = "%Rundown%%Expedition% \"%ExpeditionName%\""; } [ArchivePatch(/*Could not decode attribute arguments.*/)] internal static class SNet_Core_STEAM_SetFriendsDataPatch { public static void Prefix(FriendsDataType type, ref string data) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 if (Config.DisableSteamRPC) { data = string.Empty; } else if ((int)type == 1) { data = PresenceFormatter.FormatPresenceString(Config.CustomSteamRPCFormat) ?? ""; } } } public override string Name => "Steam Rich Presence Tweaks"; public override GroupBase Group => (GroupBase)(object)GroupManager.Presence; public override string Description => "Set a custom text for Steams' presence system."; [FeatureConfig] public static SteamRPCSettings Config { get; set; } } } namespace TheArchive.Core { public static class DRPIcons { public static class Weapons { private static string WeaponPrefix => "weapon_"; public static string HeavyDutyHammer => WeaponPrefix + "hammer"; public static string Knife => WeaponPrefix + "knife"; public static string Bat => WeaponPrefix + "bat"; public static string Spear => WeaponPrefix + "spear"; public static string Maul => WeaponPrefix + "maul"; public static string Sledge => WeaponPrefix + "sledge"; public static string Gavel => WeaponPrefix + "gavel"; public static string Mallet => WeaponPrefix + "mallet"; } public static class Tools { private static string ToolPrefix => "tool_"; public static string Biotracker => ToolPrefix + "bio"; public static string CFoamLauncher => ToolPrefix + "glue"; public static string MineDeployer => ToolPrefix + "mine"; public static string SentryGun => ToolPrefix + "sentry"; } public static class Resources { private static string ResourcePrefix => "res_"; public static string Ammo => ResourcePrefix + "ammo"; public static string Tool => ResourcePrefix + "tool"; public static string Meds => ResourcePrefix + "meds"; } public static class Expedition { public static string ElevatorDropping => "icon_dropping"; public static string Failed => "icon_failed"; public static string Survived => "icon_survived"; public static string Lobby => "icon_lobby"; public static string Reactor => "icon_reactor"; } public static class Characters { private static string CharacterPrefix => "char_"; public static string Woods => CharacterPrefix + "woods"; public static string Dauda => CharacterPrefix + "dauda"; public static string Hackett => CharacterPrefix + "hackett"; public static string Bishop => CharacterPrefix + "bishop"; } public static string GTFOIcon => "gtfo_icon"; public static string Debug => "please_just_work"; public static string EnemyPing => "icon_enemy"; } } namespace TheArchive.Core.Settings { public class RichPresenceSettings { public class GSTopActivity : GSActivity { public List<GSSubActivity> SubActivities = new List<GSSubActivity>(); [JsonIgnore] public bool HasSubActivities => (SubActivities?.Count ?? 0) > 0; public GSTopActivity FillDefaultDictValues(PresenceGameState state) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) RichPresenceSettings @default = Default; if (!base.HasActivities) { @default.DiscordRPCFormat.TryGetValue(state, out var value); Formats = value.Formats; if (!HasSubActivities && value.HasSubActivities) { foreach (GSSubActivity subActivity in value.SubActivities) { SubActivities.Add(subActivity); } } } return this; } } public class GSSubActivity : GSActivity { public List<string> DisplayConditions = new List<string>(); public bool DisplayConditionsAnyMode; } public class GSActivity { public List<GSActivityFormat> Formats = new List<GSActivityFormat>(); [JsonIgnore] public bool HasActivities { get { List<GSActivityFormat> formats = Formats; if (formats == null) { return false; } return formats.Count > 0; } } [JsonIgnore] public bool IsMultiFormat { get { List<GSActivityFormat> formats = Formats; if (formats == null) { return false; } return formats.Count > 1; } } [JsonIgnore] public int Count => Formats?.Count ?? 0; [JsonIgnore] public int MultiIndex { get; private set; } [JsonIgnore] public int CurrentMultiCycleCurrency { get; private set; } = 10; public int MultiFormatCycleTime { get; set; } = 10; public GSActivityFormat GetNext() { if (!IsMultiFormat) { return Formats[0]; } if (MultiIndex >= Count) { MultiIndex = 0; } GSActivityFormat result = Formats[MultiIndex]; CurrentMultiCycleCurrency -= 5; if (CurrentMultiCycleCurrency <= 0) { MultiIndex++; CurrentMultiCycleCurrency = MultiFormatCycleTime; } return result; } } public class GSActivityFormat { public string Details { get; set; } = "DefaultDetailsFormatString"; public string Status { get; set; } = "DefaultStatusFormatString"; public GSActivityAssets Assets { get; set; } public bool DisplayStateTimeElapsed { get; set; } = true; public string CustomTimeProvider { get; set; } = ""; public bool CustomProviderIsEndTime { get; set; } = true; public bool DisplayPartyInfo { get; set; } } public class GSActivityAssets { public string LargeImageKey { get; set; } = "please_just_work"; public string LargeTooltip { get; set; } = "Default Large Tooltip"; public string SmallImageKey { get; set; } public string SmallTooltip { get; set; } } public Dictionary<PresenceGameState, GSTopActivity> DiscordRPCFormat = new Dictionary<PresenceGameState, GSTopActivity> { { (PresenceGameState)0, new GSTopActivity { Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "Initializing ...", Status = "Waking prisoners ...", Assets = new GSActivityAssets { LargeImageKey = "gtfo_icon", LargeTooltip = "GTFO" } } } } }, { (PresenceGameState)1, new GSTopActivity { Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "%RundownWithNumberOrModdedPrefix%: \"%RundownTitle%\"", Status = "Deciding what to do", Assets = new GSActivityAssets { LargeImageKey = "gtfo_icon", LargeTooltip = "GTFO" } } } } }, { (PresenceGameState)2, new GSTopActivity { Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "%Rundown% %Expedition% \"%ExpeditionName%\"", Status = "In Lobby", Assets = new GSActivityAssets { LargeImageKey = "icon_lobby", LargeTooltip = "GTFO %Rundown% \"%RundownTitle%\"", SmallImageKey = "%CharacterImageKey%", SmallTooltip = "Playing as %CharacterName%" }, DisplayPartyInfo = true } } } }, { (PresenceGameState)3, new GSTopActivity { MultiFormatCycleTime = 5, Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "%Rundown% %Expedition% \"%ExpeditionName%\"", Status = "Dropping .", Assets = new GSActivityAssets { LargeImageKey = "icon_dropping", LargeTooltip = "Riding the elevator to hell ..." }, DisplayPartyInfo = true }, new GSActivityFormat { Details = "%Rundown% %Expedition% \"%ExpeditionName%\"", Status = "Dropping ..", Assets = new GSActivityAssets { LargeImageKey = "icon_dropping", LargeTooltip = "Riding the elevator to hell ..." }, DisplayPartyInfo = true }, new GSActivityFormat { Details = "%Rundown% %Expedition% \"%ExpeditionName%\"", Status = "Dropping ...", Assets = new GSActivityAssets { LargeImageKey = "icon_dropping", LargeTooltip = "Riding the elevator to hell ..." }, DisplayPartyInfo = true } } } }, { (PresenceGameState)4, new GSTopActivity { Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "%Rundown% %Expedition% \"%ExpeditionName%\"", Status = "Engaging brakes ...", Assets = new GSActivityAssets { LargeImageKey = "icon_dropping", LargeTooltip = "Next stop: Hell" }, DisplayPartyInfo = true } } } }, { (PresenceGameState)5, new GSTopActivity { Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "%Rundown% %Expedition% \"%ExpeditionName%\"", Status = "In Expedition", Assets = new GSActivityAssets { LargeImageKey = "gtfo_icon", LargeTooltip = "Exploring %CurrentZoneShort% Area %AreaSuffix%", SmallImageKey = "%MeleeWeaponKey%", SmallTooltip = "%EquippedMeleeWeaponName%" }, DisplayPartyInfo = true }, new GSActivityFormat { Details = "Health: %HealthPercent%%", Status = "In Expedition", Assets = new GSActivityAssets { LargeImageKey = "gtfo_icon", LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"", SmallImageKey = "res_meds", SmallTooltip = "Prisoner vitals" }, DisplayPartyInfo = true }, new GSActivityFormat { Details = "Primary: %PrimaryAmmoPercent%% Special: %SpecialAmmoPercent%%", Status = "In Expedition", Assets = new GSActivityAssets { LargeImageKey = "gtfo_icon", LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"", SmallImageKey = "res_ammo", SmallTooltip = "Ammo levels" }, DisplayPartyInfo = true }, new GSActivityFormat { Details = "Tool: %ToolAmmoPercentOrStatus%", Status = "In Expedition", Assets = new GSActivityAssets { LargeImageKey = "%ToolKey%", LargeTooltip = "%EquippedToolName%", SmallImageKey = "res_tool", SmallTooltip = "Resource level" }, DisplayPartyInfo = true } }, SubActivities = new List<GSSubActivity> { new GSSubActivity { DisplayConditions = new List<string> { "%IsReactorActive%", "%IsReactorInIntro%", "%IsReactorTypeStartup%", "!%IsReactorInVerifyFailState%" }, Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "Reactor %ReactorType% (%ReactorWaveCountCurrent%/%ReactorWaveCountMax%)", Status = "Warming Up!", Assets = new GSActivityAssets { LargeImageKey = DRPIcons.Expedition.Reactor, LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"" }, DisplayPartyInfo = true, DisplayStateTimeElapsed = false, CustomTimeProvider = "%ReactorWaveEndTime%" } } }, new GSSubActivity { DisplayConditions = new List<string> { "%IsReactorActive%", "%IsReactorInIntro%", "%IsReactorTypeStartup%", "%IsReactorInVerifyFailState%" }, Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "%ReactorType% Sequence Failed! (%ReactorWaveCountCurrent%/%ReactorWaveCountMax%)", Status = "Warming Up!", Assets = new GSActivityAssets { LargeImageKey = DRPIcons.Expedition.Reactor, LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"" }, DisplayPartyInfo = true, DisplayStateTimeElapsed = false, CustomTimeProvider = "%ReactorWaveEndTime%" } } }, new GSSubActivity { DisplayConditions = new List<string> { "%IsReactorActive%", "%IsReactorInIntro%", "!%IsReactorTypeStartup%" }, Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "Reactor %ReactorType% Sequence", Status = "Warning!", Assets = new GSActivityAssets { LargeImageKey = DRPIcons.Expedition.Reactor, LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"" }, DisplayPartyInfo = true } } }, new GSSubActivity { DisplayConditions = new List<string> { "%IsReactorActive%", "%IsReactorWaveOrChaosActive%", "%IsReactorTypeStartup%" }, Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "Reactor Wave (%ReactorWaveCountCurrent%/%ReactorWaveCountMax%)", Status = "High Intensive Test", Assets = new GSActivityAssets { LargeImageKey = DRPIcons.Expedition.Reactor, LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"", SmallImageKey = DRPIcons.EnemyPing, SmallTooltip = "Heavy Reactor Load" }, DisplayPartyInfo = true, DisplayStateTimeElapsed = false, CustomTimeProvider = "%ReactorWaveEndTime%" } } }, new GSSubActivity { DisplayConditions = new List<string> { "%IsReactorActive%", "%IsReactorWaveOrChaosActive%", "!%IsReactorTypeStartup%" }, Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "Reactor Shutdown Failure!", Status = "Alarm triggered!", Assets = new GSActivityAssets { LargeImageKey = DRPIcons.Expedition.Reactor, LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"", SmallImageKey = DRPIcons.EnemyPing, SmallTooltip = "Security System Malfunctioning" }, DisplayPartyInfo = true } } }, new GSSubActivity { DisplayConditions = new List<string> { "%IsReactorActive%", "%IsReactorAwaitingVerify%", "%IsReactorTypeStartup%" }, Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "%ReactorVerificationString% (%ReactorWaveCountCurrent%/%ReactorWaveCountMax%)", Status = "Verification Required!", Assets = new GSActivityAssets { LargeImageKey = DRPIcons.Expedition.Reactor, LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"" }, DisplayPartyInfo = true, DisplayStateTimeElapsed = false, CustomTimeProvider = "%ReactorWaveEndTime%" } } }, new GSSubActivity { DisplayConditions = new List<string> { "%IsReactorActive%", "%IsReactorAwaitingVerify%", "!%IsReactorTypeStartup%" }, Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "Initiating Shutdown Sequence ...", Status = "Verification Required!", Assets = new GSActivityAssets { LargeImageKey = DRPIcons.Expedition.Reactor, LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"" }, DisplayPartyInfo = true } } }, new GSSubActivity { DisplayConditions = new List<string> { "%IsReactorActive%", "%IsReactorCompleted%" }, Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "Reactor %ReactorType% Sequence", Status = "Complete!", Assets = new GSActivityAssets { LargeImageKey = DRPIcons.Expedition.Reactor, LargeTooltip = "%Rundown% %Expedition% \"%ExpeditionName%\"" }, DisplayPartyInfo = true } } } } } }, { (PresenceGameState)6, new GSTopActivity { Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "%Rundown% %Expedition% \"%ExpeditionName%\"", Status = "EXPD FAILED", Assets = new GSActivityAssets { LargeImageKey = "icon_failed", LargeTooltip = "Beep, Beep, Beeeeeeeeeeep", SmallImageKey = "%CharacterImageKey%", SmallTooltip = "\"%CharacterName%\", Status: DECEASED" }, DisplayPartyInfo = true } } } }, { (PresenceGameState)7, new GSTopActivity { Formats = new List<GSActivityFormat> { new GSActivityFormat { Details = "%Rundown% %Expedition% \"%ExpeditionName%\"", Status = "EXPD SURVIVED", Assets = new GSActivityAssets { LargeImageKey = "icon_survived", LargeTooltip = "Hydrostasis awaits ...", SmallImageKey = "%CharacterImageKey%", SmallTooltip = "\"%CharacterName%\", Status: ALIVE" }, DisplayPartyInfo = true } } } } }; [FSHide] [FSDisplayName("DEBUG Use Default Settings")] public bool DEBUG_UseDefaultSettings { get; set; } [FSIgnore] public bool DEBUG_RichPresenceLogSpam { get; set; } [JsonIgnore] [FSIgnore] internal static RichPresenceSettings Default => new RichPresenceSettings(); public RichPresenceSettings FillDefaultDictValues() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<PresenceGameState, GSTopActivity> item in new RichPresenceSettings().DiscordRPCFormat) { if (!DiscordRPCFormat.TryGetValue(item.Key, out var value) || value == null) { DiscordRPCFormat.Add(item.Key, item.Value.FillDefaultDictValues(item.Key)); } else { value.FillDefaultDictValues(item.Key); } } return this; } } } namespace TheArchive.Core.Managers { public class ArchiveDiscordManager { public static class DiscordClient { public const long DEFAULT_CLIENT_ID = 946141176338190346L; private static DiscordRpcClient _discordClient; private static ILogger _clientLogger; private static string _lastLobbyId; private static string _lastPartyHash; private static readonly RichPresence DefaultFallbackActivity = new RichPresence { Details = "???", State = "err:// no c0nnec7ion", Assets = new Assets { LargeImageKey = "gtfo_icon", LargeImageText = "GTFO" } }; public static long UsedClientID { get; private set; } public static void Initialize(long clientId = 946141176338190346L) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown UsedClientID = clientId; _clientLogger = (ILogger)(object)new DiscordLogger(LoaderWrapper.CreateLoggerInstance("DiscordClient", ConsoleColor.Magenta), (LogLevel)3); _discordClient = new DiscordRpcClient(clientId.ToString(), -1, _clientLogger, false, (INamedPipeClient)null); _discordClient.RegisterUriScheme(493520u.ToString(), (string)null); _discordClient.OnReady += new OnReadyEvent(OnReady); _discordClient.OnJoin += new OnJoinEvent(OnJoin); _discordClient.Subscribe((EventType)2); _discordClient.Initialize(); } private static void OnJoin(object sender, JoinMessage args) { Logger.Notice("OnJoin received! (" + args.Secret + ")"); ArchiveDiscordManager.OnActivityJoin?.Invoke(args.Secret); } private static void OnReady(object sender, ReadyMessage args) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) Logger.Notice("Discord is ready!"); float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup < _lastCheckedTime + 5f)) { _lastCheckedTime = realtimeSinceStartup; RichPresence val = BuildActivity(PresenceManager.CurrentState, PresenceManager.CurrentStateStartTime); if (TryUpdateActivity(val)) { _lastActivity = val; } } } private static Party GetParty(string partyId = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown return new Party { ID = partyId, Size = PresenceFormatter.Get<int>("MaxPlayerSlots") - PresenceFormatter.Get<int>("OpenSlots"), Max = PresenceFormatter.Get<int>("MaxPlayerSlots") }; } private static Secrets GetSecrets(string joinSecret = null) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown if (joinSecret == null) { return null; } return new Secrets { JoinSecret = joinSecret }; } private static Timestamps GetTimestamp(ulong? startTime = null, ulong? endTime = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown return new Timestamps { StartUnixMilliseconds = startTime * 1000, EndUnixMilliseconds = endTime * 1000 }; } internal static RichPresence BuildActivity(PresenceGameState state, DateTimeOffset startTime) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) if (!_settings.DiscordRPCFormat.TryGetValue(state, out var value)) { return DefaultFallbackActivity; } RichPresenceSettings.GSActivity gSActivity = value; if (!value.HasSubActivities) { return ActivityFromFormat(gSActivity.GetNext(), state, startTime); } foreach (RichPresenceSettings.GSSubActivity subActivity in value.SubActivities) { try { if (subActivity.DisplayConditionsAnyMode) { bool flag = false; foreach (string displayCondition in subActivity.DisplayConditions) { string text = PresenceFormatter.Format(displayCondition, Array.Empty<(string, string)>()); if (text == "True" || text == "!False") { flag = true; } } if (!flag) { throw null; } } else { foreach (string displayCondition2 in subActivity.DisplayConditions) { string text2 = PresenceFormatter.Format(displayCondition2, Array.Empty<(string, string)>()); if (text2 != "True" && text2 != "!False") { throw null; } } } gSActivity = subActivity; } catch { continue; } break; } return ActivityFromFormat(gSActivity.GetNext(), state, startTime); } private static RichPresence ActivityFromFormat(RichPresenceSettings.GSActivityFormat format, PresenceGameState state, DateTimeOffset startTime) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown if (format == null) { return DefaultFallbackActivity; } (string, string) tuple = ("state", ((object)(PresenceGameState)(ref state)).ToString()); RichPresence val = new RichPresence(); string details = format.Details; ((BaseRichPresence)val).Details = ((details != null) ? PresenceFormatter.Format(details, new(string, string)[1] { tuple }) : null); string status = format.Status; ((BaseRichPresence)val).State = ((status != null) ? PresenceFormatter.Format(status, new(string, string)[1] { tuple }) : null); RichPresence val2 = val; Assets val3 = new Assets(); string largeImageKey = format.Assets.LargeImageKey; val3.LargeImageKey = ((largeImageKey != null) ? PresenceFormatter.Format(largeImageKey, new(string, string)[1] { tuple }) : null); string largeTooltip = format.Assets.LargeTooltip; val3.LargeImageText = ((largeTooltip != null) ? PresenceFormatter.Format(largeTooltip, new(string, string)[1] { tuple }) : null); string smallImageKey = format.Assets.SmallImageKey; val3.SmallImageKey = ((smallImageKey != null) ? PresenceFormatter.Format(smallImageKey, new(string, string)[1] { tuple }) : null); string smallTooltip = format.Assets.SmallTooltip; val3.SmallImageText = ((smallTooltip != null) ? PresenceFormatter.Format(smallTooltip, new(string, string)[1] { tuple }) : null); ((BaseRichPresence)val2).Assets = val3; ulong result; if (format.DisplayStateTimeElapsed) { ((BaseRichPresence)val2).Timestamps = GetTimestamp((ulong)startTime.ToUnixTimeSeconds()); } else if (!string.IsNullOrWhiteSpace(format.CustomTimeProvider) && ulong.TryParse(PresenceFormatter.Format(format.CustomTimeProvider, Array.Empty<(string, string)>()), out result)) { if (format.CustomProviderIsEndTime) { ulong? endTime = result; ((BaseRichPresence)val2).Timestamps = GetTimestamp(null, endTime); } else { ((BaseRichPresence)val2).Timestamps = GetTimestamp(result); } } if (format.DisplayPartyInfo) { if (PresenceFormatter.Get<bool>("HasLobby")) { string text = PresenceFormatter.Get("LobbyID").ToString(); ((BaseRichPresence)val2).Party = GetParty(GetPartyID(text)); ((BaseRichPresence)val2).Secrets = GetSecrets(text); } else { ((BaseRichPresence)val2).Party = GetParty(PartyGuid.ToString()); } } return val2; } private static string GetPartyID(string lobbyId) { bool flag = _lastLobbyId != lobbyId; _lastLobbyId = lobbyId; if (string.IsNullOrWhiteSpace(lobbyId)) { return PartyGuid.ToString(); } if (!flag) { return _lastPartyHash; } string text = Convert.ToBase64String(SHA256.HashData(Encoding.UTF8.GetBytes(lobbyId))); if (text.Length > 128) { text = text.Substring(0, 128); } _lastPartyHash = text; return text; } internal static bool TryUpdateActivity(RichPresence activity) { if (_discordClient == null) { return false; } if (_settings.DEBUG_RichPresenceLogSpam) { Logger.Notice("Activity updated: Details:" + ((BaseRichPresence)activity).Details + " State:" + ((BaseRichPresence)activity).State); } _discordClient.SetPresence(activity); return true; } public static void Dispose() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if (_discordClient != null) { _discordClient.OnReady += new OnReadyEvent(OnReady); _discordClient.OnJoin += new OnJoinEvent(OnJoin); _discordClient.Dispose(); } _discordClient = null; } public static void RunCallbacks() { _discordClient.Invoke(); } } private static RichPresence _lastActivity; private static float _lastCheckedTime; private static RichPresenceSettings _settings; private static IArchiveLogger _logger; public static bool HasBeenSetup => _settings != null; public static bool IsEnabled { get; private set; } public static Guid PartyGuid { get; private set; } = Guid.NewGuid(); private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateLoggerInstance("ArchiveDiscordManager", ConsoleColor.Magenta)); public static event Action<string> OnActivityJoin; public static void Enable(RichPresenceSettings rpcSettings) { if (rpcSettings == null) { throw new ArgumentNullException("rpcSettings"); } if (rpcSettings.DEBUG_UseDefaultSettings) { _settings = RichPresenceSettings.Default; } else { _settings = rpcSettings; } try { DiscordClient.Initialize(); DiscordClient.RunCallbacks(); IsEnabled = true; } catch (Exception ex) { Logger.Error($"Exception has been thrown in {"ArchiveDiscordManager"}. {ex}: {ex.Message}"); Logger.Exception(ex); } } public static void Disable() { if (IsEnabled) { DiscordClient.Dispose(); IsEnabled = false; } } public static void Update() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (!IsEnabled) { return; } float time = Time.time; if (_lastCheckedTime + 5f <= time) { _lastCheckedTime = time; RichPresence val = DiscordClient.BuildActivity(PresenceManager.CurrentState, PresenceManager.CurrentStateStartTime); if (!((object)val).Equals((object?)_lastActivity) && DiscordClient.TryUpdateActivity(val)) { _lastActivity = val; } } DiscordClient.RunCallbacks(); } public static void RenewPartyGuid() { PartyGuid = Guid.NewGuid(); } } public class DiscordLogger : ILogger { private readonly IArchiveLogger _logger; public LogLevel Level { get; set; } public DiscordLogger(IArchiveLogger archiveLogger, LogLevel logLevel) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) _logger = archiveLogger; Level = logLevel; } public void Trace(string message, params object[] args) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Level <= 1) { string text = message; if (args.Length != 0) { text = string.Format(message, args); } _logger.Debug(text); } } public void Info(string message, params object[] args) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Level <= 2) { string text = message; if (args.Length != 0) { text = string.Format(message, args); } _logger.Msg(ConsoleColor.DarkMagenta, text); } } public void Warning(string message, params object[] args) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Level <= 3) { string text = message; if (args.Length != 0) { text = string.Format(message, args); } _logger.Warning(text); } } public void Error(string message, params object[] args) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Level <= 4) { string text = message; if (args.Length != 0) { text = string.Format(message, args); } _logger.Error(text); } } } public class PresenceManager { private static IArchiveLogger _logger; public static PresenceGameState LastState { get; private set; } = (PresenceGameState)0; public static PresenceGameState CurrentState { get; private set; } = (PresenceGameState)0; public static DateTimeOffset CurrentStateStartTime { get; private set; } = DateTimeOffset.UtcNow; private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateLoggerInstance("PresenceManager", ConsoleColor.DarkMagenta)); [FallbackPresenceFormatProvider("EquippedMeleeWeaponName", false)] public static string EquippedMeleeWeaponName => "None"; [FallbackPresenceFormatProvider("EquippedMeleeWeaponID", false)] public static string EquippedMeleeWeaponID => "None"; [PresenceFormatProvider("MeleeWeaponKey")] public static string MeleeWeaponKey { get { string text = PresenceFormatter.Get<string>("EquippedMeleeWeaponID")?.ToLower(); switch (text) { case "heavydutyhammer": text = "hammer"; goto default; case "sledgehammer": text = "sledge"; goto default; default: return "weapon_" + text; case null: return "please_just_work"; } } } [FallbackPresenceFormatProvider("EquippedToolName", false)] public static string EquippedToolName => "None"; [FallbackPresenceFormatProvider("EquippedToolID", false)] public static string EquippedToolID => "None"; [PresenceFormatProvider("ToolKey")] public static string ToolKey { get { string text = PresenceFormatter.Get<string>("EquippedToolID")?.ToLower(); if (text != null) { if (text.Contains("sentry")) { return "tool_sentry"; } if (text.Contains("bio")) { return "tool_bio"; } if (text.Contains("mine")) { return "tool_mine"; } if (text.Contains("glue")) { return "tool_glue"; } } return "please_just_work"; } } [FallbackPresenceFormatProvider("PrimaryAmmo", false)] public static int PrimaryAmmo => -1; [FallbackPresenceFormatProvider("MaxPrimaryAmmo", false)] public static int MaxPrimaryAmmo => -1; [PresenceFormatProvider("PrimaryAmmoPercent")] public static int PrimaryAmmoPercent => GetPercentFromInts("PrimaryAmmo", "MaxPrimaryAmmo"); [FallbackPresenceFormatProvider("SpecialAmmo", false)] public static int SpecialAmmo => -1; [FallbackPresenceFormatProvider("MaxSpecialAmmo", false)] public static int MaxSpecialAmmo => -1; [PresenceFormatProvider("SpecialAmmoPercent")] public static int SpecialAmmoPercent => GetPercentFromInts("SpecialAmmo", "MaxSpecialAmmo"); [FallbackPresenceFormatProvider("ToolAmmo", false)] public static int ToolAmmo => -1; [FallbackPresenceFormatProvider("MaxToolAmmo", false)] public static int MaxToolAmmo => -1; [PresenceFormatProvider("ToolAmmoOrStatus")] public static string ToolAmmoOrStatus { get { string text = PresenceFormatter.Get<string>("ToolKey"); int num = PresenceFormatter.Get<int>("ToolAmmo"); int value = PresenceFormatter.Get<int>("MaxToolAmmo"); if (!(text == "tool_bio")) { if (text == "tool_sentry") { if (num > 0) { return $"{num}/{value}"; } return "Deployed/Empty"; } if (num > 0) { return $"{num}/{value}"; } return "Empty"; } return "Infinite"; } } [PresenceFormatProvider("ToolAmmoPercent")] public static int ToolAmmoPercent => GetPercentFromInts("ToolAmmo", "MaxToolAmmo"); [PresenceFormatProvider("ToolAmmoPercentOrStatus")] public static string ToolAmmoPercentOrStatus { get { string text = PresenceFormatter.Get<string>("ToolKey"); float num = PresenceFormatter.Get<int>("ToolAmmo"); float max = PresenceFormatter.Get<int>("MaxToolAmmo"); if (!(text == "tool_bio")) { if (text == "tool_sentry") { if (num > 0f) { return $"{GetPercent(num, max)}%"; } return "Deployed/Empty"; } if (num > 0f) { return $"{GetPercent(num, max)}%"; } return "Empty"; } return "∞"; } } [FallbackPresenceFormatProvider("LocalCharacterID", false)] public static int LocalCharacterID { get; set; } = 0; [PresenceFormatProvider("CharacterImageKey")] public static string CharacterImageKey => PresenceFormatter.Get<int>("LocalCharacterID") switch { 0 => "char_woods", 1 => "char_dauda", 2 => "char_hackett", 3 => "char_bishop", _ => "please_just_work", }; [PresenceFormatProvider("CharacterName")] public static string CharacterName { get { int num = PresenceFormatter.Get<int>("LocalCharacterID"); return SNet.Core.GetBotNickname(num); } } [FallbackPresenceFormatProvider("HealthRaw", false)] public static float HealthRaw => -1f; [FallbackPresenceFormatProvider("MaxHealthRaw", false)] public static float MaxHealthRaw => 25f; [PresenceFormatProvider("HealthPercent")] public static int HealthPercent => GetPercentFromFloats("HealthRaw", "MaxHealthRaw"); [FallbackPresenceFormatProvider("HasLobby", false)] public static bool HasLobby => false; [FallbackPresenceFormatProvider("LobbyID", false)] public static string LobbyID => "0123456789"; [FallbackPresenceFormatProvider("OpenSlots", false)] public static int OpenSlots { get; set; } = 0; [FallbackPresenceFormatProvider("MaxPlayerSlots", true)] public static int MaxPlayerSlots { get; set; } = 4; [FallbackPresenceFormatProvider("ExpeditionTier", false)] public static string ExpeditionTier { get; set; } = string.Empty; [FallbackPresenceFormatProvider("ExpeditionTierIsSpecial", true)] public static bool ExpeditionTierIsSpecial { get; set; } = false; [FallbackPresenceFormatProvider("ExpeditionSkipExpNumberInName", true)] public static bool ExpeditionSkipExpNumberInName { get; set; } = false; [FallbackPresenceFormatProvider("ExpeditionNumber", false)] public static string ExpeditionNumber { get; set; } = string.Empty; [FallbackPresenceFormatProvider("ExpeditionName", false)] public static string ExpeditionName { get; set; } = string.Empty; [FallbackPresenceFormatProvider("ZonePrefix", false)] public static string ZonePrefix { get; set; } = string.Empty; [FallbackPresenceFormatProvider("ZonePrefixLong", false)] public static string ZonePrefixLong { get; set; } = string.Empty; [FallbackPresenceFormatProvider("ZoneAlias", false)] public static string ZoneAlias { get; set; } = string.Empty; [FallbackPresenceFormatProvider("AreaSuffix", false)] public static string AreaSuffix { get; set; } = string.Empty; [FallbackPresenceFormatProvider("RundownTitleFromDataBlocks", false)] public static string RundownTitleFromDataBlocks { get; set; } = "Unknown"; [PresenceFormatProvider("CurrentZoneShort")] public static string CurrentZoneShort => $"{PresenceFormatter.Get("ZonePrefix")}_{PresenceFormatter.Get("ZoneAlias")}"; [PresenceFormatProvider("CurrentZoneLong")] public static string CurrentZoneLong => $"{PresenceFormatter.Get("ZonePrefixLong")} {PresenceFormatter.Get("ZoneAlias")}"; [PresenceFormatProvider("CurrentArea")] public static string CurrentArea => $"Area {PresenceFormatter.Get("AreaSuffix")}"; [PresenceFormatProvider("ExpeditionWithNumber")] public static string ExpeditionWithNumber => $"{PresenceFormatter.Get("ExpeditionTier")}{PresenceFormatter.Get("ExpeditionNumber")}"; [PresenceFormatProvider("Expedition")] public static string Expedition { get { if (PresenceFormatter.Get<bool>("ExpeditionSkipExpNumberInName")) { return $"{PresenceFormatter.Get("ExpeditionTier")}"; } return ExpeditionWithNumber ?? ""; } } [PresenceFormatProvider("Rundown")] public static string Rundown { get { if (ArchiveMod.IsPlayingModded) { return "Mod"; } if (ArchiveMod.IsOnALTBuild) { string text = PresenceFormatter.Get("ExpeditionTier").ToString(); if (text.StartsWith("R7") || text.StartsWith("R8")) { return string.Empty; } return "Alt"; } return $"R{RundownNumber}"; } } [PresenceFormatProvider("RundownNumber")] public static int RundownNumber => (int)ArchiveMod.CurrentRundown; [PresenceFormatProvider("RundownName")] public static string RundownName => RundownTitle; [PresenceFormatProvider("RundownTitle")] public static string RundownTitle { get { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (ArchiveMod.IsPlayingModded || ArchiveMod.IsOnALTBuild) { return $"{PresenceFormatter.Get("RundownTitleFromDataBlocks")}"; } return Utils.GetRundownTitle(ArchiveMod.CurrentRundown); } } [PresenceFormatProvider("RundownWithNumberOrModdedPrefix")] public static string RundownWithNumberOrModdedPrefix { get { if (ArchiveMod.IsPlayingModded) { return "Modded"; } if (ArchiveMod.IsOnALTBuild) { return "GTFO"; } return $"Rundown {PresenceFormatter.Get("RundownNumber")}"; } } public static void UpdateGameState(PresenceGameState state, bool keepTimer = false) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) Logger.Msg(ConsoleColor.DarkMagenta, $"GameState has been updated: {CurrentState} --> {state}, keepTimer: {keepTimer}"); LastState = CurrentState; CurrentState = state; if (!keepTimer) { CurrentStateStartTime = DateTimeOffset.UtcNow; } } public static int GetPercentFromInts(string val, string max) { float val2 = PresenceFormatter.Get<int>(val); float max2 = PresenceFormatter.Get<int>(max); return GetPercent(val2, max2); } public static int GetPercentFromFloats(string val, string max) { float val2 = PresenceFormatter.Get<float>(val); float max2 = PresenceFormatter.Get<float>(max); return GetPercent(val2, max2); } public static int GetPercent(float val, float max) { return (int)Math.Round(val / max * 100f); } } }