

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, rusing 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);
[FSDisplayNameusing 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;
}
}
}using 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);
}
}
}