using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ModdingUtils.Utils;
using Photon.Pun;
using UnboundLib;
using UnboundLib.GameModes;
using UnboundLib.Networking;
using UnityEngine;
using UnityEngine.Networking;
[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("rounds-tracker")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+fe6c0319ebd8c995e3254bdcfa0cd8c295a86a7f")]
[assembly: AssemblyProduct("rounds-tracker")]
[assembly: AssemblyTitle("rounds-tracker")]
[assembly: AssemblyVersion("1.0.0.0")]
[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.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace RoundsTracker
{
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("com.rounds.tracker", "Rounds Tracker", "1.0.1")]
[BepInProcess("Rounds.exe")]
public class RT : BaseUnityPlugin
{
[CompilerGenerated]
private sealed class <OnGameStart>d__27 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public IGameModeHandler gm;
public RT <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <OnGameStart>d__27(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
if (<>1__state != 0)
{
return false;
}
<>1__state = -1;
ResetSession();
ReplaceCardTracker.ClearAll();
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <OnPointEnd>d__28 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public IGameModeHandler gm;
public RT <>4__this;
private Player <winner>5__1;
private RoundResultData <roundData>5__2;
private Exception <ex>5__3;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <OnPointEnd>d__28(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<winner>5__1 = null;
<roundData>5__2 = null;
<ex>5__3 = null;
<>1__state = -2;
}
private bool MoveNext()
{
if (<>1__state != 0)
{
return false;
}
<>1__state = -1;
if (!EnableTracking.Value || !TrackRoundResults.Value)
{
return false;
}
if (PhotonNetwork.OfflineMode)
{
return false;
}
try
{
_roundNumber++;
<winner>5__1 = PlayerManager.instance.GetLastPlayerAlive();
if ((Object)(object)<winner>5__1 == (Object)null)
{
LogDebug("OnPointEnd: No winner found");
return false;
}
if (!<winner>5__1.data.view.IsMine)
{
LogDebug("OnPointEnd: Local client is not winner, skipping send");
return false;
}
<roundData>5__2 = new RoundResultData
{
session_id = GetSessionId(),
round_number = _roundNumber,
winner_steam_id = GetSteamIdForPlayer(<winner>5__1),
ended_at = DateTime.UtcNow.ToString("o")
};
ApiClient.SendRoundResult(<roundData>5__2);
Log($"Round {_roundNumber} ended: Winner {<roundData>5__2.winner_steam_id}");
<winner>5__1 = null;
<roundData>5__2 = null;
}
catch (Exception ex)
{
<ex>5__3 = ex;
LogError("OnPointEnd error: " + <ex>5__3.Message);
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
public const string ModId = "com.rounds.tracker";
public const string ModName = "Rounds Tracker";
public const string Version = "1.0.1";
public static ConfigEntry<bool> EnableTracking;
public static ConfigEntry<string> ApiUrl;
public static ConfigEntry<bool> TrackPickedCards;
public static ConfigEntry<bool> TrackAddedCards;
public static ConfigEntry<bool> TrackRemovedCards;
public static ConfigEntry<bool> TrackRoundResults;
public static ConfigEntry<bool> DebugLogging;
private static string _sessionId;
private static int _pickNumber;
private static int _roundNumber;
private static string _steamId;
private static string _steamNickname;
private static bool _steamInfoTried = false;
public static string ApiUrlValue = "http://77.246.107.74:8000";
private static Harmony _harmonyInstance;
public static RT Instance { get; private set; }
private void Awake()
{
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
Instance = this;
EnableTracking = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableTracking", true, "Enable or disable card tracking");
TrackPickedCards = ((BaseUnityPlugin)this).Config.Bind<bool>("Tracking", "TrackPickedCards", true, "Track cards picked from card choice");
TrackAddedCards = ((BaseUnityPlugin)this).Config.Bind<bool>("Tracking", "TrackAddedCards", true, "Track cards added programmatically");
TrackRemovedCards = ((BaseUnityPlugin)this).Config.Bind<bool>("Tracking", "TrackRemovedCards", true, "Track cards removed from players");
TrackRoundResults = ((BaseUnityPlugin)this).Config.Bind<bool>("Tracking", "TrackRoundResults", true, "Track round results (who won each round)");
DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", false, "Enable detailed debug logging");
new Harmony("com.rounds.tracker").PatchAll();
}
private void Start()
{
ResetSession();
GameModeManager.AddHook("GameStart", (Func<IGameModeHandler, IEnumerator>)OnGameStart);
GameModeManager.AddHook("PointEnd", (Func<IGameModeHandler, IEnumerator>)OnPointEnd);
ApplyReplaceCardPatches();
Log(string.Format("{0} v{1} initialized. Tracking: {2}, API: {3}", "Rounds Tracker", "1.0.1", EnableTracking.Value, ApiUrlValue));
}
private void ApplyReplaceCardPatches()
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Expected O, but got Unknown
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_012c: Expected O, but got Unknown
try
{
_harmonyInstance = new Harmony("com.rounds.tracker.cardremovalpatches");
Type typeFromHandle = typeof(Cards);
MethodInfo methodInfo = AccessTools.Method(typeFromHandle, "RemoveAllCardsFromPlayer", new Type[2]
{
typeof(Player),
typeof(bool)
}, (Type[])null);
if (methodInfo != null)
{
HarmonyMethod val = new HarmonyMethod(typeof(ReplaceCardTracker), "RemoveAllCardsPrefix", (Type[])null);
_harmonyInstance.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Log("Patched RemoveAllCardsFromPlayer");
}
else
{
LogError("RemoveAllCardsFromPlayer method not found");
}
MethodInfo methodInfo2 = AccessTools.Method(typeFromHandle, "AddCardsToPlayer", new Type[7]
{
typeof(Player),
typeof(CardInfo[]),
typeof(bool[]),
typeof(string[]),
typeof(float[]),
typeof(float[]),
typeof(bool)
}, (Type[])null);
if (methodInfo2 != null)
{
HarmonyMethod val2 = new HarmonyMethod(typeof(ReplaceCardTracker), "AddCardsToPlayerPostfix", (Type[])null);
_harmonyInstance.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Log("Patched AddCardsToPlayer");
}
else
{
LogError("AddCardsToPlayer method not found");
}
}
catch (Exception ex)
{
LogError("Failed to apply card removal patches: " + ex.Message);
}
}
[UnboundRPC]
public static void RPCA_CardRemoved(int playerID, string cardName, string cardObjectName)
{
if (!EnableTracking.Value || !TrackRemovedCards.Value)
{
return;
}
try
{
Player val = PlayerManager.instance.players.Find((Player p) => p.playerID == playerID);
if ((Object)(object)val == (Object)null)
{
LogDebug($"RPCA_CardRemoved: Player {playerID} not found");
return;
}
if (!val.data.view.IsMine)
{
LogDebug($"RPCA_CardRemoved: Player {playerID} is not mine, skipping");
return;
}
CardData cardDataFromNames = DataCollector.GetCardDataFromNames(cardName, cardObjectName);
if (cardDataFromNames == null || string.IsNullOrWhiteSpace(cardDataFromNames.card_name))
{
LogDebug("RPCA_CardRemoved: Could not get card data for " + cardName);
return;
}
PickData data = new PickData
{
pick_id = Guid.NewGuid().ToString(),
source_type = "removed",
tracker_version = "1.0.1",
player = DataCollector.GetPlayer(val),
session = DataCollector.GetSession(),
game_state = DataCollector.GetGameState(),
picked_card = cardDataFromNames,
picked_at = DateTime.UtcNow.ToString("o")
};
ApiClient.Send(data);
Log($"Removed (synced): {cardDataFromNames.card_name} from player {playerID}");
}
catch (Exception ex)
{
LogError("RPCA_CardRemoved error: " + ex.Message);
}
}
private static void EnsureSteamInfo()
{
if (_steamInfoTried)
{
return;
}
_steamInfoTried = true;
try
{
Type type = Type.GetType("SteamManager, Assembly-CSharp");
if (type == null)
{
return;
}
PropertyInfo property = type.GetProperty("Initialized", BindingFlags.Static | BindingFlags.Public);
if (property == null || !(bool)property.GetValue(null))
{
return;
}
Type type2 = Type.GetType("Steamworks.SteamUser, Assembly-CSharp-firstpass") ?? Type.GetType("Steamworks.SteamUser, com.rlabrecque.steamworks.net");
if (type2 != null)
{
MethodInfo method = type2.GetMethod("GetSteamID", BindingFlags.Static | BindingFlags.Public);
if (method != null)
{
object obj = method.Invoke(null, null);
if (obj != null)
{
_steamId = obj.ToString();
}
}
}
Type type3 = Type.GetType("Steamworks.SteamFriends, Assembly-CSharp-firstpass") ?? Type.GetType("Steamworks.SteamFriends, com.rlabrecque.steamworks.net");
if (type3 != null)
{
MethodInfo method2 = type3.GetMethod("GetPersonaName", BindingFlags.Static | BindingFlags.Public);
if (method2 != null)
{
object obj2 = method2.Invoke(null, null);
if (obj2 != null)
{
_steamNickname = obj2.ToString();
}
}
}
if (!string.IsNullOrEmpty(_steamId))
{
Log("Steam ID: " + _steamId + ", Name: " + _steamNickname);
}
}
catch (Exception ex)
{
LogError("Failed to get Steam info: " + ex.Message);
}
}
[IteratorStateMachine(typeof(<OnGameStart>d__27))]
private IEnumerator OnGameStart(IGameModeHandler gm)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <OnGameStart>d__27(0)
{
<>4__this = this,
gm = gm
};
}
[IteratorStateMachine(typeof(<OnPointEnd>d__28))]
private IEnumerator OnPointEnd(IGameModeHandler gm)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <OnPointEnd>d__28(0)
{
<>4__this = this,
gm = gm
};
}
private static string GetSteamIdForPlayer(Player player)
{
CharacterData data = player.data;
if (data != null)
{
PhotonView view = data.view;
if (((view != null) ? new bool?(view.IsMine) : null).GetValueOrDefault())
{
return GetSteamId();
}
}
try
{
CharacterData data2 = player.data;
int? obj;
if (data2 == null)
{
obj = null;
}
else
{
PhotonView view2 = data2.view;
obj = ((view2 != null) ? new int?(view2.OwnerActorNr) : null);
}
int? num = obj;
int valueOrDefault = num.GetValueOrDefault(-1);
if (valueOrDefault > 0)
{
return $"PHOTON_{valueOrDefault}";
}
}
catch
{
}
return "Unknown";
}
private static string GetCurrentGameMode()
{
try
{
if (GameModeManager.CurrentHandlerID != null)
{
return GameModeManager.CurrentHandlerID;
}
}
catch
{
}
return "Unknown";
}
public static void ResetSession()
{
_sessionId = Guid.NewGuid().ToString();
_pickNumber = 0;
_roundNumber = 0;
LogDebug("Session reset");
}
public static string GetSessionId()
{
return (!string.IsNullOrEmpty(_sessionId)) ? _sessionId : (_sessionId = Guid.NewGuid().ToString());
}
public static int GetAndIncrementPickNumber()
{
return ++_pickNumber;
}
public static int GetRoundNumber()
{
return _roundNumber;
}
public static string GetSteamId()
{
EnsureSteamInfo();
return _steamId;
}
public static string GetSteamNickname()
{
EnsureSteamInfo();
return _steamNickname;
}
public static void Log(string msg)
{
RT instance = Instance;
if (instance != null)
{
((BaseUnityPlugin)instance).Logger.LogInfo((object)msg);
}
}
public static void LogError(string msg)
{
RT instance = Instance;
if (instance != null)
{
((BaseUnityPlugin)instance).Logger.LogError((object)msg);
}
}
public static void LogDebug(string msg)
{
ConfigEntry<bool> debugLogging = DebugLogging;
if (debugLogging != null && debugLogging.Value)
{
RT instance = Instance;
if (instance != null)
{
((BaseUnityPlugin)instance).Logger.LogInfo((object)("[DEBUG] " + msg));
}
}
}
}
[HarmonyPatch(typeof(CardChoice), "Pick")]
internal class CardChoicePatch
{
private static FieldInfo SpawnedCardsField = AccessTools.Field(typeof(CardChoice), "spawnedCards");
private static FieldInfo PickerTypeField = AccessTools.Field(typeof(CardChoice), "pickerType");
private static void Postfix(CardChoice __instance, GameObject pickedCard, int ___pickrID)
{
if (!RT.EnableTracking.Value || !RT.TrackPickedCards.Value)
{
return;
}
try
{
if ((Object)(object)pickedCard == (Object)null || PhotonNetwork.OfflineMode)
{
return;
}
List<GameObject> list = (List<GameObject>)(SpawnedCardsField?.GetValue(__instance));
Player player = GetPlayer(__instance, ___pickrID);
if ((Object)(object)player == (Object)null || !player.data.view.IsMine)
{
return;
}
CardData card = DataCollector.GetCard(pickedCard);
if (card == null || string.IsNullOrWhiteSpace(card.card_name))
{
return;
}
List<CardData> list2 = new List<CardData>();
int position = 0;
for (int i = 0; i < list.Count; i++)
{
GameObject val = list[i];
if ((Object)(object)val == (Object)null)
{
continue;
}
CardData card2 = DataCollector.GetCard(val);
if (card2 != null && !string.IsNullOrWhiteSpace(card2.card_name))
{
card2.position = i;
list2.Add(card2);
if ((Object)(object)val == (Object)(object)pickedCard)
{
position = i;
}
}
}
card.position = position;
PickData data = new PickData
{
pick_id = Guid.NewGuid().ToString(),
source_type = "picked",
tracker_version = "1.0.1",
player = DataCollector.GetPlayer(player),
session = DataCollector.GetSession(),
game_state = DataCollector.GetGameState(),
picked_card = card,
offered_cards = list2,
picked_at = DateTime.UtcNow.ToString("o")
};
ApiClient.Send(data);
RT.Log("Picked: " + card.card_name);
}
catch (Exception ex)
{
RT.LogError("CardChoice.Pick patch error: " + ex.Message);
}
}
private static Player GetPlayer(CardChoice instance, int pickerId)
{
//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)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Invalid comparison between Unknown and I4
try
{
PickerType val = (PickerType)(PickerTypeField?.GetValue(instance));
if ((int)val == 0)
{
Player[] playersInTeam = PlayerManager.instance.GetPlayersInTeam(pickerId);
return (playersInTeam != null && playersInTeam.Length != 0) ? playersInTeam[0] : null;
}
}
catch
{
}
if (pickerId < PlayerManager.instance.players.Count)
{
return PlayerManager.instance.players[pickerId];
}
return null;
}
}
[HarmonyPatch]
internal class AddCardPatch
{
private static MethodBase TargetMethod()
{
Type type = Type.GetType("ModdingUtils.Utils.Cards, ModdingUtils");
if (type == null)
{
RT.LogDebug("AddCardPatch: ModdingUtils.Utils.Cards type not found");
return null;
}
MethodInfo methodInfo = AccessTools.Method(type, "RPCA_AssignCard", new Type[7]
{
typeof(string),
typeof(int),
typeof(bool),
typeof(string),
typeof(float),
typeof(float),
typeof(bool)
}, (Type[])null);
if (methodInfo == null)
{
methodInfo = AccessTools.Method(type, "RPCA_AssignCard", new Type[6]
{
typeof(string),
typeof(int),
typeof(bool),
typeof(string),
typeof(float),
typeof(float)
}, (Type[])null);
}
if (methodInfo != null)
{
RT.LogDebug($"AddCardPatch: Found RPCA_AssignCard with {methodInfo.GetParameters().Length} params");
}
else
{
RT.LogDebug("AddCardPatch: RPCA_AssignCard method not found");
}
return methodInfo;
}
private static void Postfix(string cardObjectName, int playerID, bool reassign)
{
if (!RT.EnableTracking.Value || !RT.TrackAddedCards.Value)
{
return;
}
try
{
if (reassign)
{
RT.LogDebug("AddCardPatch: Skipping reassign for " + cardObjectName);
}
else
{
if (PhotonNetwork.OfflineMode)
{
return;
}
Player val = PlayerManager.instance.players.Find((Player p) => p.playerID == playerID);
if ((Object)(object)val == (Object)null)
{
RT.LogDebug($"AddCardPatch: Player {playerID} not found");
return;
}
if (!val.data.view.IsMine)
{
RT.LogDebug($"AddCardPatch: Player {playerID} is not mine, skipping");
return;
}
CardData cardFromObjectName = DataCollector.GetCardFromObjectName(cardObjectName);
if (cardFromObjectName == null || string.IsNullOrWhiteSpace(cardFromObjectName.card_name))
{
RT.LogDebug("AddCardPatch: Could not get card data for " + cardObjectName);
return;
}
PickData data = new PickData
{
pick_id = Guid.NewGuid().ToString(),
source_type = "added",
tracker_version = "1.0.1",
player = DataCollector.GetPlayer(val),
session = DataCollector.GetSession(),
game_state = DataCollector.GetGameState(),
picked_card = cardFromObjectName,
picked_at = DateTime.UtcNow.ToString("o")
};
ApiClient.Send(data);
RT.Log($"Added (synced): {cardFromObjectName.card_name} to player {playerID}");
}
}
catch (Exception ex)
{
RT.LogError("RPCA_AssignCard patch error: " + ex.Message);
}
}
}
internal static class ReplaceCardTracker
{
private static Dictionary<int, Stack<List<CardInfo>>> _savedCards = new Dictionary<int, Stack<List<CardInfo>>>();
public static void ClearAll()
{
_savedCards.Clear();
RT.LogDebug("ReplaceCardTracker: Cleared all saved cards");
}
public static void RemoveAllCardsPrefix(Player player, bool clearBar)
{
if (!RT.EnableTracking.Value || !RT.TrackRemovedCards.Value || (Object)(object)player == (Object)null)
{
return;
}
try
{
List<CardInfo> list = new List<CardInfo>();
if (player.data?.currentCards != null)
{
foreach (CardInfo currentCard in player.data.currentCards)
{
if ((Object)(object)currentCard != (Object)null)
{
list.Add(currentCard);
}
}
}
if (!_savedCards.ContainsKey(player.playerID))
{
_savedCards[player.playerID] = new Stack<List<CardInfo>>();
}
_savedCards[player.playerID].Push(list);
RT.LogDebug($"ReplaceCardTracker: Saved {list.Count} cards for player {player.playerID}");
}
catch (Exception ex)
{
RT.LogError("ReplaceCardTracker.RemoveAllCardsPrefix error: " + ex.Message);
}
}
public static void AddCardsToPlayerPostfix(Player player, CardInfo[] cards, bool[] reassigns, string[] twoLetterCodes, float[] forceDisplays, float[] forceDisplayDelays, bool addToCardBar)
{
if ((Object)(object)player == (Object)null)
{
return;
}
List<CardInfo> list = null;
if (_savedCards.TryGetValue(player.playerID, out var value) && value.Count > 0)
{
list = value.Pop();
}
if (!RT.EnableTracking.Value || !RT.TrackRemovedCards.Value || cards == null || list == null || list.Count == 0 || PhotonNetwork.OfflineMode)
{
return;
}
try
{
if (!PhotonNetwork.IsMasterClient)
{
RT.LogDebug("ReplaceCardTracker: Not master client, skipping");
return;
}
HashSet<string> hashSet = new HashSet<string>();
for (int i = 0; i < cards.Length; i++)
{
if ((Object)(object)cards[i] != (Object)null && reassigns != null && i < reassigns.Length && reassigns[i])
{
hashSet.Add(((Object)cards[i]).name);
}
}
foreach (CardInfo item in list)
{
if (!hashSet.Contains(((Object)item).name))
{
RT.LogDebug($"ReplaceCardTracker: Detected removed/replaced card {item.cardName} for player {player.playerID}");
GameObject gameObject = ((Component)item).gameObject;
string text = ((gameObject != null) ? ((Object)gameObject).name : null) ?? item.cardName;
NetworkingManager.RPC(typeof(RT), "RPCA_CardRemoved", new object[3] { player.playerID, item.cardName, text });
}
}
}
catch (Exception ex)
{
RT.LogError("ReplaceCardTracker.AddCardsToPlayerPostfix error: " + ex.Message);
}
}
}
internal static class DataCollector
{
private static Dictionary<string, string> _modVersionCache = new Dictionary<string, string>();
public static PlayerData GetPlayer(Player player)
{
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
string steam_id = "Unknown";
string text = "Unknown";
if (!string.IsNullOrEmpty(RT.GetSteamId()))
{
steam_id = RT.GetSteamId();
text = RT.GetSteamNickname() ?? text;
}
PlayerSkin playerSkinColors = PlayerSkinBank.GetPlayerSkinColors(player.playerID);
return new PlayerData
{
steam_id = steam_id,
nickname = text,
color = "#" + ColorUtility.ToHtmlStringRGB(playerSkinColors.color)
};
}
public static SessionData GetSession()
{
string game_mode = "Unknown";
try
{
if (GameModeManager.CurrentHandlerID != null)
{
game_mode = GameModeManager.CurrentHandlerID;
}
}
catch
{
}
return new SessionData
{
session_id = RT.GetSessionId(),
game_mode = game_mode,
player_count = PlayerManager.instance.players.Count
};
}
public static GameStateData GetGameState()
{
GameStateData gameStateData = new GameStateData
{
pick_number = RT.GetAndIncrementPickNumber()
};
try
{
IGameModeHandler currentHandler = GameModeManager.CurrentHandler;
if (((currentHandler != null) ? currentHandler.Settings : null) != null)
{
object value = default(object);
if (currentHandler.Settings.TryGetValue("pointsToWinRound", ref value))
{
gameStateData.points_to_win_round = Convert.ToInt32(value);
}
object value2 = default(object);
if (currentHandler.Settings.TryGetValue("roundsToWinGame", ref value2))
{
gameStateData.rounds_to_win_game = Convert.ToInt32(value2);
}
}
}
catch
{
}
return gameStateData;
}
public static CardData GetCard(GameObject cardObject)
{
if ((Object)(object)cardObject == (Object)null)
{
return null;
}
CardInfo component = cardObject.GetComponent<CardInfo>();
return ((Object)(object)component != (Object)null) ? GetCardFromInfo(component, ((Object)cardObject).name) : null;
}
public static CardData GetCardFromObjectName(string objectName)
{
if (string.IsNullOrEmpty(objectName))
{
return null;
}
try
{
Type type = Type.GetType("ModdingUtils.Utils.Cards, ModdingUtils");
if (type != null)
{
object obj = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
MethodInfo method = type.GetMethod("GetCardWithObjectName", BindingFlags.Instance | BindingFlags.Public);
if (method != null && obj != null)
{
object? obj2 = method.Invoke(obj, new object[1] { objectName });
CardInfo val = (CardInfo)((obj2 is CardInfo) ? obj2 : null);
if ((Object)(object)val != (Object)null)
{
return GetCardFromInfo(val, objectName);
}
}
}
}
catch (Exception ex)
{
RT.LogDebug("GetCardFromObjectName error: " + ex.Message);
}
string cardName = ExtractCardNameFromObjectName(objectName);
return GetCardDataFromNames(cardName, objectName);
}
private static string ExtractCardNameFromObjectName(string objectName)
{
if (string.IsNullOrEmpty(objectName))
{
return objectName;
}
if (objectName.StartsWith("__"))
{
string[] array = objectName.Split(new string[1] { "__" }, StringSplitOptions.RemoveEmptyEntries);
if (array.Length >= 2)
{
return array[1];
}
}
return objectName;
}
public static CardData GetCardDataFromNames(string cardName, string objectName)
{
string text = "Vanilla";
string text2 = objectName ?? cardName ?? "";
if (text2.StartsWith("__"))
{
string[] array = text2.Split(new string[1] { "__" }, StringSplitOptions.RemoveEmptyEntries);
if (array.Length >= 1)
{
text = array[0];
}
}
string text3 = cardName ?? "";
if (text3.StartsWith("__"))
{
text3 = ExtractCardNameFromObjectName(text3);
}
return new CardData
{
card_name = text3,
mod_name = text,
mod_version = GetModVersion(text),
description = "",
rarity = "Common",
color_theme = "TechWhite",
categories = new List<string>(),
stats = new List<StatData>()
};
}
public static CardData GetCardFromInfo(CardInfo cardInfo, string objName = null)
{
if ((Object)(object)cardInfo == (Object)null)
{
return null;
}
string text = "Vanilla";
string text2 = null;
object obj = objName;
if (obj == null)
{
GameObject gameObject = ((Component)cardInfo).gameObject;
obj = ((gameObject != null) ? ((Object)gameObject).name : null) ?? "";
}
string text3 = (string)obj;
if (text3.StartsWith("__"))
{
string[] array = text3.Split(new string[1] { "__" }, StringSplitOptions.RemoveEmptyEntries);
if (array.Length >= 1)
{
text = array[0];
}
}
text2 = GetModVersion(text);
string rarity = "Common";
try
{
rarity = ((object)(Rarity)(ref cardInfo.rarity)).ToString();
}
catch
{
}
string color_theme = "TechWhite";
try
{
color_theme = ((object)(CardThemeColorType)(ref cardInfo.colorTheme)).ToString();
}
catch
{
}
List<StatData> list = new List<StatData>();
try
{
if (cardInfo.cardStats != null)
{
CardInfoStat[] cardStats = cardInfo.cardStats;
foreach (CardInfoStat val in cardStats)
{
list.Add(new StatData
{
stat = (val.stat ?? ""),
amount = (val.amount ?? ""),
positive = val.positive
});
}
}
}
catch
{
}
List<string> list2 = new List<string>();
if (cardInfo.categories != null)
{
CardCategory[] categories = cardInfo.categories;
foreach (CardCategory val2 in categories)
{
if ((Object)(object)val2 != (Object)null)
{
list2.Add(((Object)val2).name);
}
}
}
return new CardData
{
card_name = (cardInfo.cardName ?? ""),
mod_name = text,
mod_version = GetModVersion(text),
description = (cardInfo.cardDestription ?? ""),
rarity = rarity,
color_theme = color_theme,
categories = list2,
stats = list
};
}
private static string GetModVersion(string modName)
{
if (string.IsNullOrEmpty(modName) || modName == "Vanilla")
{
return null;
}
if (_modVersionCache.TryGetValue(modName, out var value))
{
return value;
}
try
{
foreach (PluginInfo value2 in Chainloader.PluginInfos.Values)
{
BepInPlugin metadata = value2.Metadata;
if (metadata.GUID.ToUpper().Contains(modName) || metadata.Name.ToUpper().Contains(modName) || metadata.Name.ToUpper().Replace(" ", "").Contains(modName))
{
_modVersionCache[modName] = metadata.Version.ToString();
return metadata.Version.ToString();
}
}
}
catch
{
}
_modVersionCache[modName] = null;
return null;
}
}
internal static class ApiClient
{
[CompilerGenerated]
private sealed class <SendPickCoroutine>d__3 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public PickData data;
public int retryCount;
private string <json>5__1;
private string <url>5__2;
private UnityWebRequest <req>5__3;
private byte[] <body>5__4;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <SendPickCoroutine>d__3(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || (uint)(num - 1) <= 1u)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<json>5__1 = null;
<url>5__2 = null;
<req>5__3 = null;
<body>5__4 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Expected O, but got Unknown
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Expected O, but got Unknown
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Expected O, but got Unknown
//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
//IL_01d1: Expected O, but got Unknown
try
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<json>5__1 = JsonSerializer.Serialize(data);
<url>5__2 = RT.ApiUrlValue + "/api/picks";
RT.LogDebug("Sending to " + <url>5__2 + ": " + data.source_type + " " + data.picked_card?.card_name);
<req>5__3 = new UnityWebRequest(<url>5__2, "POST");
<>1__state = -3;
<body>5__4 = Encoding.UTF8.GetBytes(<json>5__1);
<req>5__3.uploadHandler = (UploadHandler)new UploadHandlerRaw(<body>5__4);
<req>5__3.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
<req>5__3.SetRequestHeader("Content-Type", "application/json");
<req>5__3.timeout = 10;
<>2__current = <req>5__3.SendWebRequest();
<>1__state = 1;
return true;
case 1:
<>1__state = -3;
if (<req>5__3.isNetworkError || <req>5__3.isHttpError)
{
RT.LogError($"Send failed: {<req>5__3.error} (attempt {retryCount + 1})");
if (retryCount < 3)
{
<>2__current = (object)new WaitForSeconds(2f * (float)(retryCount + 1));
<>1__state = 2;
return true;
}
}
else
{
RT.LogDebug("Send success: " + <req>5__3.downloadHandler.text);
}
break;
case 2:
<>1__state = -3;
((MonoBehaviour)RT.Instance).StartCoroutine(SendPickCoroutine(data, retryCount + 1));
break;
}
<body>5__4 = null;
<>m__Finally1();
<req>5__3 = 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 (<req>5__3 != null)
{
((IDisposable)<req>5__3).Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <SendRoundCoroutine>d__4 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public RoundResultData data;
public int retryCount;
private string <json>5__1;
private string <url>5__2;
private UnityWebRequest <req>5__3;
private byte[] <body>5__4;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <SendRoundCoroutine>d__4(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || (uint)(num - 1) <= 1u)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<json>5__1 = null;
<url>5__2 = null;
<req>5__3 = null;
<body>5__4 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Expected O, but got Unknown
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Expected O, but got Unknown
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: Expected O, but got Unknown
//IL_0193: Unknown result type (might be due to invalid IL or missing references)
//IL_019d: Expected O, but got Unknown
try
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<json>5__1 = JsonSerializer.SerializeRound(data);
<url>5__2 = RT.ApiUrlValue + "/api/rounds";
RT.LogDebug($"Sending round result to {<url>5__2}: Round {data.round_number}");
<req>5__3 = new UnityWebRequest(<url>5__2, "POST");
<>1__state = -3;
<body>5__4 = Encoding.UTF8.GetBytes(<json>5__1);
<req>5__3.uploadHandler = (UploadHandler)new UploadHandlerRaw(<body>5__4);
<req>5__3.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
<req>5__3.SetRequestHeader("Content-Type", "application/json");
<req>5__3.timeout = 10;
<>2__current = <req>5__3.SendWebRequest();
<>1__state = 1;
return true;
case 1:
<>1__state = -3;
if (<req>5__3.isNetworkError || <req>5__3.isHttpError)
{
RT.LogError($"Send round failed: {<req>5__3.error} (attempt {retryCount + 1})");
if (retryCount < 3)
{
<>2__current = (object)new WaitForSeconds(2f * (float)(retryCount + 1));
<>1__state = 2;
return true;
}
}
else
{
RT.LogDebug("Round send success: " + <req>5__3.downloadHandler.text);
}
break;
case 2:
<>1__state = -3;
((MonoBehaviour)RT.Instance).StartCoroutine(SendRoundCoroutine(data, retryCount + 1));
break;
}
<body>5__4 = null;
<>m__Finally1();
<req>5__3 = 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 (<req>5__3 != null)
{
((IDisposable)<req>5__3).Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private const int MaxRetries = 3;
public static void Send(PickData data)
{
if (RT.EnableTracking.Value)
{
((MonoBehaviour)RT.Instance).StartCoroutine(SendPickCoroutine(data, 0));
}
}
public static void SendRoundResult(RoundResultData data)
{
if (RT.EnableTracking.Value)
{
((MonoBehaviour)RT.Instance).StartCoroutine(SendRoundCoroutine(data, 0));
}
}
[IteratorStateMachine(typeof(<SendPickCoroutine>d__3))]
private static IEnumerator SendPickCoroutine(PickData data, int retryCount)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <SendPickCoroutine>d__3(0)
{
data = data,
retryCount = retryCount
};
}
[IteratorStateMachine(typeof(<SendRoundCoroutine>d__4))]
private static IEnumerator SendRoundCoroutine(RoundResultData data, int retryCount)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <SendRoundCoroutine>d__4(0)
{
data = data,
retryCount = retryCount
};
}
}
internal static class JsonSerializer
{
public static string Serialize(PickData d)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("{");
stringBuilder.Append("\"pick_id\":\"" + Esc(d.pick_id) + "\",");
stringBuilder.Append("\"source_type\":\"" + Esc(d.source_type) + "\",");
stringBuilder.Append("\"tracker_version\":\"" + Esc(d.tracker_version) + "\",");
stringBuilder.Append("\"picked_at\":\"" + Esc(d.picked_at) + "\",");
stringBuilder.Append("\"player\":");
SerializePlayer(stringBuilder, d.player);
stringBuilder.Append(",");
stringBuilder.Append("\"session\":");
SerializeSession(stringBuilder, d.session);
stringBuilder.Append(",");
stringBuilder.Append("\"game_state\":");
SerializeGameState(stringBuilder, d.game_state);
stringBuilder.Append(",");
stringBuilder.Append("\"picked_card\":");
SerializeCard(stringBuilder, d.picked_card);
if (d.offered_cards != null && d.offered_cards.Count > 0)
{
stringBuilder.Append(",\"offered_cards\":[");
for (int i = 0; i < d.offered_cards.Count; i++)
{
if (i > 0)
{
stringBuilder.Append(",");
}
SerializeCard(stringBuilder, d.offered_cards[i]);
}
stringBuilder.Append("]");
}
stringBuilder.Append("}");
return stringBuilder.ToString();
}
private static void SerializePlayer(StringBuilder sb, PlayerData p)
{
sb.Append("{");
sb.Append("\"steam_id\":\"" + Esc(p.steam_id) + "\",");
sb.Append("\"nickname\":\"" + Esc(p.nickname) + "\",");
sb.Append("\"color\":\"" + Esc(p.color) + "\"");
sb.Append("}");
}
private static void SerializeSession(StringBuilder sb, SessionData s)
{
sb.Append("{");
sb.Append("\"session_id\":\"" + Esc(s.session_id) + "\",");
sb.Append("\"game_mode\":\"" + Esc(s.game_mode) + "\",");
sb.Append($"\"player_count\":{s.player_count}");
sb.Append("}");
}
private static void SerializeGameState(StringBuilder sb, GameStateData g)
{
sb.Append("{");
sb.Append($"\"pick_number\":{g.pick_number},");
sb.Append("\"points_to_win_round\":" + Null(g.points_to_win_round) + ",");
sb.Append("\"rounds_to_win_game\":" + Null(g.rounds_to_win_game));
sb.Append("}");
}
private static void SerializeCard(StringBuilder sb, CardData c)
{
sb.Append("{");
sb.Append("\"card_name\":\"" + Esc(c.card_name) + "\",");
sb.Append("\"mod_name\":\"" + Esc(c.mod_name) + "\",");
sb.Append("\"mod_version\":" + NullStr(c.mod_version) + ",");
sb.Append("\"description\":\"" + Esc(c.description) + "\",");
sb.Append("\"rarity\":\"" + Esc(c.rarity) + "\",");
sb.Append("\"color_theme\":\"" + Esc(c.color_theme) + "\",");
sb.Append($"\"position\":{c.position},");
sb.Append("\"categories\":[");
for (int i = 0; i < c.categories.Count; i++)
{
if (i > 0)
{
sb.Append(",");
}
sb.Append("\"" + Esc(c.categories[i]) + "\"");
}
sb.Append("],\"stats\":[");
for (int j = 0; j < c.stats.Count; j++)
{
if (j > 0)
{
sb.Append(",");
}
StatData statData = c.stats[j];
sb.Append("{\"stat\":\"" + Esc(statData.stat) + "\",\"amount\":\"" + Esc(statData.amount) + "\",\"positive\":" + Bool(statData.positive) + "}");
}
sb.Append("]}");
}
private static string Null(int? v)
{
return v.HasValue ? v.Value.ToString() : "null";
}
private static string NullStr(string v)
{
return string.IsNullOrEmpty(v) ? "null" : ("\"" + Esc(v) + "\"");
}
private static string Bool(bool v)
{
return v ? "true" : "false";
}
private static string Esc(string s)
{
return string.IsNullOrEmpty(s) ? "" : s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n")
.Replace("\r", "\\r")
.Replace("\t", "\\t");
}
public static string SerializeRound(RoundResultData r)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("{");
stringBuilder.Append("\"session_id\":\"" + Esc(r.session_id) + "\",");
stringBuilder.Append($"\"round_number\":{r.round_number},");
stringBuilder.Append("\"winner_steam_id\":" + NullStr(r.winner_steam_id) + ",");
stringBuilder.Append("\"ended_at\":\"" + Esc(r.ended_at) + "\"");
stringBuilder.Append("}");
return stringBuilder.ToString();
}
}
internal class PickData
{
public string pick_id;
public string source_type;
public string tracker_version;
public PlayerData player;
public SessionData session;
public GameStateData game_state;
public CardData picked_card;
public List<CardData> offered_cards;
public string picked_at;
}
internal class PlayerData
{
public string steam_id;
public string nickname;
public string color;
}
internal class SessionData
{
public string session_id;
public string game_mode;
public int player_count;
}
internal class GameStateData
{
public int pick_number;
public int? points_to_win_round;
public int? rounds_to_win_game;
}
internal class CardData
{
public string card_name;
public string mod_name;
public string mod_version;
public string description;
public string rarity;
public string color_theme;
public int position;
public List<string> categories = new List<string>();
public List<StatData> stats = new List<StatData>();
}
internal class StatData
{
public string stat;
public string amount;
public bool positive;
}
internal class RoundResultData
{
public string session_id;
public int round_number;
public string winner_steam_id;
public string ended_at;
}
}