using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using REPOStats_Mod.Data;
using REPOStats_Mod.Patches;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.danos.repostats")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+87c3acc4d409d2010a6a9c77e3a2c20c972d9e15")]
[assembly: AssemblyProduct("REPOStats-Mod")]
[assembly: AssemblyTitle("com.danos.repostats")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
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;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace REPOStats_Mod
{
public static class DanosRepoStatsPluginInfo
{
public const string PLUGIN_GUID = "com.danos.repostats";
public const string PLUGIN_NAME = "repostats";
public const string PLUGIN_VERSION = "0.5.2";
}
[BepInPlugin("com.danos.repostats", "repostats", "0.5.2")]
public class REPOStats_Mod : BaseUnityPlugin
{
private static readonly HashSet<Type> AppliedPatches = new HashSet<Type>();
private static readonly HashSet<string> AppliedDynamicPatches = new HashSet<string>();
private static bool _additionalPatchesApplied = false;
public static REPOStats_Mod Instance { get; private set; } = null;
internal static ManualLogSource Logger { get; private set; } = null;
internal static Harmony? Harmony { get; set; }
private void Awake()
{
Logger = ((BaseUnityPlugin)this).Logger;
Instance = this;
Patch();
Logger.LogInfo((object)"com.danos.repostats v0.5.2 has loaded!");
}
internal static void Patch()
{
//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_0018: Expected O, but got Unknown
if (Harmony == null)
{
Harmony = new Harmony("com.danos.repostats");
}
Logger.LogDebug((object)"Patching...");
Harmony.PatchAll(typeof(PrivacyPolicyPatch));
Logger.LogDebug((object)"Finished patching!");
}
internal static void Unpatch()
{
Logger.LogDebug((object)"Unpatching...");
Harmony? harmony = Harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
Logger.LogDebug((object)"Finished unpatching!");
}
public static void ApplyAdditionalPatches()
{
if (_additionalPatchesApplied)
{
Logger.LogDebug((object)"Additional patches already applied.");
return;
}
ApplyPatch(typeof(RunPatches));
ApplyPatch(typeof(RoundDirectorPatches));
ApplyPatch(typeof(DeathPatches));
List<DanosPatchConfiguration> patchConfigurations = DanosPatchManager.GetPatchConfigurations();
foreach (DanosPatchConfiguration item in patchConfigurations)
{
ApplyDynamicPatch(item);
}
_additionalPatchesApplied = true;
Logger.LogDebug((object)"RepoStats is loaded!");
}
private static void ApplyDynamicPatch(DanosPatchConfiguration patchConfig)
{
//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
//IL_01c2: Expected O, but got Unknown
try
{
string text = patchConfig.TargetClass + "." + patchConfig.TargetMethod + "." + patchConfig.PostfixMethod;
if (AppliedDynamicPatches.Contains(text))
{
Logger.LogDebug((object)("Patch already applied: " + text));
return;
}
Type type = AccessTools.TypeByName(patchConfig.TargetClass);
if (type == null)
{
Logger.LogDebug((object)("Target class '" + patchConfig.TargetClass + "' not found. Skipping patch."));
return;
}
MethodInfo methodInfo = AccessTools.Method(type, patchConfig.TargetMethod, (Type[])null, (Type[])null);
if (methodInfo == null)
{
Logger.LogDebug((object)("Target method '" + patchConfig.TargetMethod + "' not found in class '" + patchConfig.TargetClass + "'. Skipping patch."));
return;
}
string[] array = patchConfig.PostfixMethod.Split('.');
string name = array.Last();
string text2 = string.Join('.', array.Take(array.Length - 1));
Type type2 = Type.GetType(text2);
if (type2 == null)
{
Logger.LogDebug((object)("Postfix class '" + text2 + "' not found. Skipping patch."));
return;
}
MethodInfo method = type2.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (method == null)
{
Logger.LogDebug((object)("Postfix method '" + patchConfig.PostfixMethod + "' not found. Skipping patch."));
return;
}
Harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
AppliedDynamicPatches.Add(text);
Logger.LogDebug((object)("Successfully patched " + patchConfig.TargetClass + "." + patchConfig.TargetMethod));
}
catch (Exception ex)
{
Logger.LogDebug((object)("Failed to apply patch for " + patchConfig.TargetClass + "." + patchConfig.TargetMethod + ": " + ex.Message));
}
}
private static void ApplyPatch(Type patchType)
{
if (!AppliedPatches.Contains(patchType))
{
Harmony.PatchAll(patchType);
AppliedPatches.Add(patchType);
Logger.LogDebug((object)("Patch applied: " + patchType.Name));
}
else
{
Logger.LogDebug((object)("Patch already applied: " + patchType.Name));
}
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "com.danos.repostats";
public const string PLUGIN_NAME = "REPOStats-Mod";
public const string PLUGIN_VERSION = "1.0.0";
}
}
namespace REPOStats_Mod.Patches
{
[HarmonyPatch]
public class DeathPatches
{
private static readonly FieldRef<Enemy, EnemyParent> enemyRef = AccessTools.FieldRefAccess<Enemy, EnemyParent>("EnemyParent");
[HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")]
[HarmonyPostfix]
public static void PlayerDeathRPCPostfix(PlayerAvatar __instance, int enemyIndex)
{
string text = SemiFunc.PlayerGetSteamID(__instance);
if (text == null || !DanosUtils.GetMySteamID().Equals(text))
{
return;
}
string causeOfDeath = "Unknown";
Enemy val = SemiFunc.EnemyGetFromIndex(enemyIndex);
if ((Object)(object)val != (Object)null)
{
causeOfDeath = ((Object)val).name;
EnemyParent val2 = enemyRef.Invoke(val);
if ((Object)(object)val2 != (Object)null)
{
causeOfDeath = val2.enemyName;
}
}
DanosDeathContainer danosDeathContainer = new DanosDeathContainer();
danosDeathContainer.DeathTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
danosDeathContainer.CauseOfDeath = causeOfDeath;
DanosStaticStore.statsStore.Deaths.Add(danosDeathContainer);
}
}
[HarmonyPatch]
public class PrivacyPolicyPatch
{
public class PolicyLoader : MonoBehaviour
{
private async void Start()
{
await LoadPolicyAndShowPopup();
Object.Destroy((Object)(object)((Component)this).gameObject);
}
private async Task LoadPolicyAndShowPopup()
{
policy = await GetPolicy();
Debug.Log((object)("Policy version: " + policy.Version));
if (HasUserAcceptedPolicy(policy.Version))
{
Debug.Log((object)"User has already accepted the current policy version.");
REPOStats_Mod.ApplyAdditionalPatches();
return;
}
Debug.Log((object)"User has not accepted the current policy version. Showing the popup.");
ShowPopup(delegate
{
Debug.Log((object)"User accepted REPOStats terms.");
SaveUserResponse(accepted: true, policy.Version);
REPOStats_Mod.ApplyAdditionalPatches();
}, delegate
{
Debug.Log((object)"User declined REPOStats terms. Closing the game.");
Application.Quit();
});
}
}
public static PolicyVersion policy = new PolicyVersion();
public static bool running = false;
[HarmonyPatch(typeof(MenuManager), "Start")]
[HarmonyPrefix]
public static bool StartPrefix()
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
Debug.Log((object)"Checking if user has accepted the current policy version.");
if ((Object)(object)Object.FindObjectOfType<PolicyLoader>() != (Object)null)
{
return true;
}
new GameObject("PolicyLoader").AddComponent<PolicyLoader>();
return true;
}
private static bool HasUserAcceptedPolicy(string policyVersion)
{
try
{
string location = Assembly.GetExecutingAssembly().Location;
string directoryName = Path.GetDirectoryName(location);
string path = Path.Combine(directoryName, "REPOStatsPrivacyResponse.txt");
if (!File.Exists(path))
{
return false;
}
string[] array = File.ReadAllLines(path);
string[] array2 = array;
foreach (string text in array2)
{
if (text.Contains("Policy Version: " + policyVersion))
{
return true;
}
}
}
catch (Exception ex)
{
Debug.Log((object)("Error checking user response: " + ex.Message));
}
return false;
}
private static void SaveUserResponse(bool accepted, string policyVersion)
{
try
{
string location = Assembly.GetExecutingAssembly().Location;
string directoryName = Path.GetDirectoryName(location);
string text = Path.Combine(directoryName, "REPOStatsPrivacyResponse.txt");
string arg = (accepted ? "Accepted" : "Declined");
string contents = $"User Response: {arg}\nPolicy Version: {policyVersion}\nDate: {DateTime.Now}\n\nDelete this file to revoke RepoStats Privacy policy consent for this mod profile. \nYou will need to do this for each mod profile that has RepoStats installed if you are using a mod manager like r2modman. ";
File.WriteAllText(text, contents);
Debug.Log((object)("User response saved to: " + text));
}
catch (Exception ex)
{
Debug.Log((object)("Error saving user response: " + ex.Message));
}
}
[HarmonyPatch(typeof(MenuManager), "Update")]
[HarmonyPrefix]
public static void UpdatePostfix()
{
GlobalInputManager.Update();
}
public static void ShowPopup(Action onAccept, Action onDecline)
{
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Expected O, but got Unknown
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Expected O, but got Unknown
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_019f: Unknown result type (might be due to invalid IL or missing references)
//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
//IL_0221: Unknown result type (might be due to invalid IL or missing references)
//IL_024e: Unknown result type (might be due to invalid IL or missing references)
Action onAccept2 = onAccept;
Action onDecline2 = onDecline;
Debug.Log((object)"Creating fullscreen popup...");
GameObject popupCanvas = new GameObject("RepoStatsPopupCanvas");
Canvas val = popupCanvas.AddComponent<Canvas>();
val.renderMode = (RenderMode)0;
val.sortingOrder = 9999;
CanvasScaler val2 = popupCanvas.AddComponent<CanvasScaler>();
val2.uiScaleMode = (ScaleMode)1;
popupCanvas.AddComponent<GraphicRaycaster>();
GameObject val3 = new GameObject("Panel");
val3.transform.SetParent(popupCanvas.transform, false);
Image val4 = val3.AddComponent<Image>();
((Graphic)val4).color = new Color(0f, 0f, 0f, 0.95f);
RectTransform component = val3.GetComponent<RectTransform>();
component.anchorMin = Vector2.zero;
component.anchorMax = Vector2.one;
component.sizeDelta = Vector2.zero;
CreateText(val3, "REPOStats", new Vector2(0f, 150f), 36, (TextAnchor)1, "#6ac4a7");
string message = "Welcome to REPOStats! By using this mod, you agree to the privacy policy at https://repo.splitstats.io/privacypolicy.\n\nGame stats will be collected and uploaded during gameplay. Your response to the policy will be saved in the mod folder until you uninstall the mod or the policy changes.\n\nManage or delete your data using tools on our website. Decline to close the game and uninstall the mod.";
string message2 = "We do not store contact information, so we cannot directly inform you of any changes, however this prompt will popup any time we make any.\n\nVersion: " + policy.Version + "\nLast Updated: " + policy.LastUpdated.ToShortDateString() + "\nChanges: " + policy.DescriptionOfChanges + "\nLast Updated: " + policy.LastUpdatedDaysAgo + ".";
CreateText(val3, message, new Vector2(0f, 75f), 8, (TextAnchor)4);
CreateText(val3, message2, new Vector2(0f, -25f), 5, (TextAnchor)4);
CreateText(val3, "[F5] Open Privacy Policy", new Vector2(0f, -60f), 24, (TextAnchor)4, "#6ac4a7");
CreateText(val3, "[F6] Accept", new Vector2(-100f, -120f), 24, (TextAnchor)4, "#6ac4a7");
CreateText(val3, "[F7] Decline", new Vector2(100f, -120f), 24, (TextAnchor)4, "#FF6A6A");
GlobalInputManager.ListenForKeys((KeyCode)286, delegate
{
Application.OpenURL("https://repo.splitstats.io/privacypolicy");
});
GlobalInputManager.ListenForKeys((KeyCode)287, delegate
{
Object.Destroy((Object)(object)popupCanvas);
GlobalInputManager.StopListeningForKeys((KeyCode)286);
GlobalInputManager.StopListeningForKeys((KeyCode)287);
GlobalInputManager.StopListeningForKeys((KeyCode)288);
onAccept2?.Invoke();
});
GlobalInputManager.ListenForKeys((KeyCode)288, delegate
{
Object.Destroy((Object)(object)popupCanvas);
GlobalInputManager.StopListeningForKeys((KeyCode)286);
GlobalInputManager.StopListeningForKeys((KeyCode)287);
GlobalInputManager.StopListeningForKeys((KeyCode)288);
onDecline2?.Invoke();
});
}
private static async Task<PolicyVersion> GetPolicy()
{
PolicyVersion policy = new PolicyVersion();
using (HttpClient client = new HttpClient())
{
Debug.Log((object)"Getting policy from: https://repo.splitstats.io/api/Privacy/current");
HttpResponseMessage response = await client.GetAsync("https://repo.splitstats.io/api/Privacy/current");
Debug.Log((object)("Response: " + response.StatusCode));
if (response.IsSuccessStatusCode)
{
policy = DeserializeJson<PolicyVersion>(await response.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false));
}
}
return policy;
}
private static T DeserializeJson<T>(string json)
{
using MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(T));
return (T)dataContractJsonSerializer.ReadObject(stream);
}
private static void CreateText(GameObject parent, string message, Vector2 position, int fontSize = 20, TextAnchor alignment = 4, string hexColor = "#FFFFFF", Color? backgroundColor = null)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Expected O, but got Unknown
//IL_0035: 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_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: 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_0096: Expected O, but got Unknown
//IL_00a3: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("TextElement");
Text val2 = val.AddComponent<Text>();
val2.text = message;
val2.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
val2.fontSize = fontSize;
val2.alignment = alignment;
((Graphic)val2).color = HexToColor(hexColor);
RectTransform component = ((Component)val2).GetComponent<RectTransform>();
component.sizeDelta = new Vector2(460f, 100f);
component.anchoredPosition = position;
((Transform)component).SetParent(parent.transform, false);
if (backgroundColor.HasValue)
{
GameObject val3 = new GameObject("Background");
Image val4 = val3.AddComponent<Image>();
((Graphic)val4).color = backgroundColor.Value;
RectTransform component2 = val3.GetComponent<RectTransform>();
component2.sizeDelta = new Vector2(220f, 60f);
component2.anchoredPosition = position;
((Transform)component2).SetParent(parent.transform, false);
((Component)val2).transform.SetParent(val3.transform, false);
}
}
private static Color HexToColor(string hex)
{
//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_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: 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)
Color result = default(Color);
if (ColorUtility.TryParseHtmlString(hex, ref result))
{
return result;
}
return Color.white;
}
}
public static class GlobalInputManager
{
private static readonly Dictionary<KeyCode, Action> keyActions = new Dictionary<KeyCode, Action>();
public static void ListenForKeys(KeyCode key, Action action)
{
//IL_0006: 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 (!keyActions.ContainsKey(key))
{
keyActions[key] = action;
}
}
public static void StopListeningForKeys(KeyCode key)
{
//IL_0006: 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)
if (keyActions.ContainsKey(key))
{
keyActions.Remove(key);
}
}
public static void StopAllListening()
{
keyActions.Clear();
}
public static void Update()
{
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
if (keyActions.Count == 0)
{
return;
}
foreach (KeyValuePair<KeyCode, Action> keyAction in keyActions)
{
if (Input.GetKeyDown(keyAction.Key))
{
keyAction.Value?.Invoke();
}
}
}
}
[HarmonyPatch]
public class RoundDirectorPatches
{
private static readonly FieldRef<RoundDirector, int> extractionPointsCompletedRef = AccessTools.FieldRefAccess<RoundDirector, int>("extractionPointsCompleted");
private static readonly FieldRef<RoundDirector, int> extractionPointsRef = AccessTools.FieldRefAccess<RoundDirector, int>("extractionPoints");
[HarmonyPatch(typeof(RoundDirector), "ExtractionCompleted")]
[HarmonyPostfix]
public static void ExtractionCompletedPostfix(RoundDirector __instance)
{
DanosStaticStore.statsStore.RunStats.extractions_completed = extractionPointsCompletedRef.Invoke(__instance);
}
[HarmonyPatch(typeof(RoundDirector), "StartRoundLogic")]
[HarmonyPostfix]
public static void StartRoundLogicPostfix(RoundDirector __instance, int value)
{
DanosStaticStore.statsStore.RunStats.extractions_on_map = extractionPointsRef.Invoke(__instance);
}
[HarmonyPatch(typeof(ExtractionPoint), "HaulGoalSetRPC")]
[HarmonyPostfix]
public static void StartRoundLogicPostfix(ExtractionPoint __instance, int value)
{
DanosRunStats runStats = DanosStaticStore.statsStore.RunStats;
runStats.extraction_goals_csv = runStats.extraction_goals_csv + value + ",";
}
}
[HarmonyPatch]
public class RunPatches
{
[HarmonyPatch(typeof(RunManager), "UpdateLevel")]
[HarmonyPostfix]
public static void UpdateLevelPostFix(string _levelName, int _levelsCompleted, bool _gameOver)
{
if (!DanosUtils.GetHostSteamID().Equals(DanosUtils.GetMySteamID()))
{
CombinedLevelLogic(_levelName, _levelsCompleted, _gameOver);
}
}
[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
[HarmonyPostfix]
public static void ChangeLevelPostFix(bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType = 0)
{
if (DanosUtils.GetHostSteamID().Equals(DanosUtils.GetMySteamID()))
{
RunManager instance = RunManager.instance;
if ((Object)(object)instance != (Object)null)
{
CombinedLevelLogic(((Object)instance.levelCurrent).name, instance.levelsCompleted, (Object)(object)RunManager.instance.levelCurrent == (Object)(object)RunManager.instance.levelArena && _levelFailed && (Object)(object)RunManager.instance.levelCurrent != (Object)(object)RunManager.instance.levelShop && (Object)(object)RunManager.instance.levelCurrent != (Object)(object)RunManager.instance.levelLobby);
}
}
}
[HarmonyPatch(typeof(TruckScreenText), "Start")]
[HarmonyPostfix]
public static void Postfix(TruckScreenText __instance)
{
string mySteamID = DanosUtils.GetMySteamID();
if (string.IsNullOrEmpty(mySteamID))
{
Debug.LogError((object)"Could not get my steam id");
return;
}
if (!long.TryParse(mySteamID, out var _))
{
Debug.LogError((object)"Could not parse steam id as long");
return;
}
string hostSteamID = DanosUtils.GetHostSteamID();
long result2;
if (string.IsNullOrEmpty(hostSteamID))
{
Debug.LogError((object)"Could not get host steam id");
}
else if (!long.TryParse(hostSteamID, out result2))
{
Debug.LogError((object)"Could not parse host steam id as long");
}
else
{
DanosStaticStore.InitializeStatsStore();
}
}
public static void CombinedLevelLogic(string _levelName, int _levelsCompleted, bool _gameOver)
{
if (DanosStaticStore.statsStore != null)
{
DanosStaticStore.statsStore.RunStats.completed_levels = _levelsCompleted;
}
if ((Object)(object)RunManager.instance == (Object)null)
{
Debug.LogError((object)"RunManager instance is null");
}
else if (RunManager.instance.levels == null)
{
Debug.LogError((object)"RunManager levels is null");
}
else if (_levelName == ((Object)RunManager.instance.levelLobbyMenu).name)
{
DanosStaticStore.ResetStatsStore();
}
else if (_levelName == ((Object)RunManager.instance.levelShop).name)
{
RoundEnded("Success");
}
else if (_levelName == ((Object)RunManager.instance.levelArena).name)
{
RoundEnded("GameOver");
}
else
{
if (_levelName == ((Object)RunManager.instance.levelRecording).name)
{
return;
}
Debug.Log((object)("Level: " + _levelName));
foreach (Level level in RunManager.instance.levels)
{
if (((Object)level).name == _levelName)
{
DanosStaticStore.ResetStatsStore();
break;
}
}
}
}
public static void RoundEnded(string a_reason)
{
DanosStaticStore.statsStore.RoundEnded = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
DanosStaticStore.statsStore.RunEndReason = a_reason;
DanosStaticStore.SendStatsToAPI();
DanosStaticStore.ResetStatsStore();
}
}
}
namespace REPOStats_Mod.Data
{
[DataContract]
public class DanosDeathContainer
{
[DataMember]
public string CauseOfDeath { get; set; } = "";
[DataMember]
public long DeathTime { get; set; }
}
public class DanosPatchConfiguration
{
public string TargetClass { get; set; }
public string TargetMethod { get; set; }
public string PostfixMethod { get; set; }
}
public static class DanosPatchManager
{
public static List<DanosPatchConfiguration> GetPatchConfigurations()
{
return new List<DanosPatchConfiguration>();
}
}
[DataContract]
public class DanosRunStats
{
[DataMember]
public int extractions_completed { get; set; } = 0;
[DataMember]
public int extractions_on_map { get; set; } = 0;
[DataMember]
public bool failed { get; set; } = false;
[DataMember]
public int top_extraction_target { get; set; } = 0;
[DataMember]
public int take_home_money { get; set; } = 0;
[DataMember]
public int total_money { get; set; } = 0;
[DataMember]
public int completed_levels { get; set; } = 0;
[DataMember]
public string extraction_goals_csv { get; set; } = "";
}
public static class DanosStaticStore
{
public static long lastSentStats = 0L;
public static DanosStatsStore statsStore = new DanosStatsStore();
public static void SendStatsToAPI()
{
if (CheckStatsStore())
{
Debug.Log((object)"Sending stats to API");
DanosStatSender.Instance.SendStats(statsStore);
}
Debug.Log((object)"Resetting stats store");
ResetStatsStore();
lastSentStats = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
}
public static bool CheckStatsStore()
{
if (statsStore == null)
{
return false;
}
if (statsStore.MySteamId <= 1000)
{
return false;
}
if (statsStore.RoundStarted <= 0)
{
return false;
}
if (statsStore.RoundEnded <= 0)
{
return false;
}
if (statsStore.RoundEnded < statsStore.RoundStarted)
{
return false;
}
return true;
}
public static void ResetStatsStore()
{
statsStore = new DanosStatsStore();
}
public static void InitializeStatsStore()
{
ResetStatsStore();
string mySteamID = DanosUtils.GetMySteamID();
if (string.IsNullOrEmpty(mySteamID))
{
Debug.LogError((object)"Could not get my steam id");
return;
}
if (!long.TryParse(mySteamID, out var result))
{
Debug.LogError((object)"Could not parse steam id as long");
return;
}
string hostSteamID = DanosUtils.GetHostSteamID();
Debug.Log((object)("Host: " + hostSteamID));
if (string.IsNullOrEmpty(hostSteamID))
{
Debug.LogError((object)"Could not get host steam id");
return;
}
if (!long.TryParse(hostSteamID, out var result2))
{
Debug.LogError((object)"Could not parse host steam id as long");
return;
}
statsStore.MySteamId = result;
statsStore.LobbyHash = DanosUtils.GetLobbyHash();
statsStore.isHost = result == result2;
statsStore.RoundStarted = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
statsStore.Deaths = new List<DanosDeathContainer>();
statsStore.RunStats = new DanosRunStats();
RunManager instance = RunManager.instance;
if ((Object)(object)instance != (Object)null)
{
statsStore.LevelName = ((Object)instance.levelCurrent).name;
}
}
}
public class DanosStatSender : MonoBehaviour
{
[CompilerGenerated]
private sealed class <>c__DisplayClass4_0
{
public Task<HttpResponseMessage> requestTask;
internal bool <PostStatsCoroutine>b__0()
{
return requestTask.IsCompleted;
}
}
[CompilerGenerated]
private sealed class <PostStatsCoroutine>d__4 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public DanosStatsStore stats;
public DanosStatSender <>4__this;
private string <encodedUrl>5__1;
private string <apiUrl>5__2;
private string <json>5__3;
private HttpClient <client>5__4;
private <>c__DisplayClass4_0 <>8__5;
private HttpContent <content>5__6;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <PostStatsCoroutine>d__4(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<encodedUrl>5__1 = null;
<apiUrl>5__2 = null;
<json>5__3 = null;
<client>5__4 = null;
<>8__5 = null;
<content>5__6 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0139: Expected O, but got Unknown
try
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
Debug.Log((object)"Posting stats to API");
<encodedUrl>5__1 = "aHR0cHM6Ly9yZXBvLWFwaS5zcGxpdHN0YXRzLmlvL2FwaS9wb3N0Z2FtZS9zZW5kc3RhdHM=";
<apiUrl>5__2 = Encoding.UTF8.GetString(Convert.FromBase64String(<encodedUrl>5__1));
<json>5__3 = <>4__this.SerializeToJson(stats);
<client>5__4 = new HttpClient();
<>1__state = -3;
<>8__5 = new <>c__DisplayClass4_0();
<client>5__4.DefaultRequestHeaders.Accept.Clear();
<client>5__4.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
<client>5__4.DefaultRequestHeaders.Add("XAuthCode", "cmVwb3N0YXRzbGt0b3A0MzI=");
<content>5__6 = new StringContent(<json>5__3, Encoding.UTF8, "application/json");
<>8__5.requestTask = <client>5__4.PostAsync(<apiUrl>5__2, <content>5__6);
<>2__current = (object)new WaitUntil((Func<bool>)(() => <>8__5.requestTask.IsCompleted));
<>1__state = 1;
return true;
case 1:
<>1__state = -3;
<>8__5 = null;
<content>5__6 = null;
<>m__Finally1();
<client>5__4 = 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 (<client>5__4 != null)
{
((IDisposable)<client>5__4).Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private static DanosStatSender _instance;
public static DanosStatSender Instance
{
get
{
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected O, but got Unknown
if ((Object)(object)_instance == (Object)null)
{
_instance = Object.FindObjectOfType<DanosStatSender>();
if ((Object)(object)_instance != (Object)null)
{
return _instance;
}
GameObject val = new GameObject("DanosStatSender");
_instance = val.AddComponent<DanosStatSender>();
Object.DontDestroyOnLoad((Object)(object)val);
}
return _instance;
}
}
public void SendStats(DanosStatsStore stats)
{
((MonoBehaviour)this).StartCoroutine(PostStatsCoroutine(stats));
}
[IteratorStateMachine(typeof(<PostStatsCoroutine>d__4))]
private IEnumerator PostStatsCoroutine(DanosStatsStore stats)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <PostStatsCoroutine>d__4(0)
{
<>4__this = this,
stats = stats
};
}
private string SerializeToJson(DanosStatsStore stats)
{
using MemoryStream memoryStream = new MemoryStream();
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(DanosStatsStore));
dataContractJsonSerializer.WriteObject(memoryStream, stats);
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
}
[DataContract]
public class DanosStatsStore
{
[DataMember]
public long RoundStarted { get; set; } = 0L;
[DataMember]
public long RoundEnded { get; set; } = 0L;
[DataMember]
public string LevelName { get; set; } = "";
[DataMember]
public string LobbyHash { get; set; } = "";
[DataMember]
public string ModVersion { get; set; } = "0.5.2";
[DataMember]
public string GameVersion { get; set; } = "Unknown";
[DataMember]
public string RunEndReason { get; set; } = "";
[DataMember]
public long MySteamId { get; set; } = 0L;
[DataMember]
public bool isHost { get; set; } = false;
[DataMember]
public List<DanosDeathContainer> Deaths { get; set; } = new List<DanosDeathContainer>();
[DataMember]
public DanosRunStats RunStats { get; set; } = new DanosRunStats();
}
public static class DanosUtils
{
public static string GetHostSteamID()
{
try
{
List<PlayerAvatar> list = SemiFunc.PlayerGetAll();
if (list == null || list.Count == 0)
{
return string.Empty;
}
foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
{
if ((Object)(object)item != (Object)null && (Object)(object)item.photonView != (Object)null && item.photonView.Owner.IsMasterClient)
{
return SemiFunc.PlayerGetSteamID(item);
}
}
return string.Empty;
}
catch (Exception)
{
return string.Empty;
}
}
public static string GetMySteamID()
{
foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
{
if ((Object)(object)item != (Object)null && item.photonView.IsMine)
{
return SemiFunc.PlayerGetSteamID(item);
}
}
return string.Empty;
}
public static string GetLobbyHash()
{
List<string> list = new List<string>();
string hostSteamID = GetHostSteamID();
foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
{
if ((Object)(object)item != (Object)null)
{
string text = SemiFunc.PlayerGetSteamID(item);
if (!string.IsNullOrEmpty(text) && text != hostSteamID)
{
list.Add(text);
}
}
}
if (!string.IsNullOrEmpty(hostSteamID))
{
list.Insert(0, hostSteamID);
}
list.Sort();
string input = string.Join(",", list);
return ComputeSHA256(input);
}
private static string ComputeSHA256(string input)
{
using SHA256 sHA = SHA256.Create();
byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(input));
return BitConverter.ToString(array).Replace("-", "").ToLower();
}
}
[DataContract]
public class PolicyVersion
{
[DataMember(Name = "version")]
public string Version { get; set; } = "0.0.0";
[DataMember(Name = "lastUpdated")]
public string LastUpdatedRaw { get; set; } = "2025-03-06";
[DataMember(Name = "descriptionOfChanges")]
public string DescriptionOfChanges { get; set; } = "Newly drafted policy to clear some things up.";
public DateTime LastUpdated
{
get
{
if (DateTime.TryParseExact(LastUpdatedRaw, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
{
return result;
}
return DateTime.MinValue;
}
}
public string LastUpdatedDaysAgo => $"{(DateTime.UtcNow - LastUpdated).Days} day(s) ago";
}
}