using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
using System.Timers;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CG.Profile;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using Steamworks;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("RecentPlayers")]
[assembly: AssemblyProduct("RecentPlayers")]
[assembly: AssemblyDescription("A mod for Void Crew that sets Steam recently played with")]
[assembly: AssemblyCompany("Runtime Evil Inc")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: ComVisible(false)]
[assembly: AssemblyFileVersion("1.2.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.2.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.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 RecentPlayers
{
internal class PhotonInRoomCallbacks : IInRoomCallbacks
{
public void OnMasterClientSwitched(Player newMasterClient)
{
}
public void OnPlayerEnteredRoom(Player newPlayer)
{
RecentPlayers.SetPlayedWith(newPlayer);
}
public void OnPlayerLeftRoom(Player otherPlayer)
{
}
public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
{
}
public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
{
}
}
internal class PhotonMatchmakingCallbacks : IMatchmakingCallbacks
{
public void OnCreatedRoom()
{
}
public void OnCreateRoomFailed(short returnCode, string message)
{
}
public void OnFriendListUpdate(List<FriendInfo> friendList)
{
}
public void OnJoinedRoom()
{
RecentPlayers.SetLobbyPlayedWith();
}
public void OnJoinRandomFailed(short returnCode, string message)
{
}
public void OnJoinRoomFailed(short returnCode, string message)
{
}
public void OnLeftRoom()
{
}
}
[BepInPlugin("dev.zkxs.voidcrew.recentplayers", "RecentPlayers", "1.2.0")]
public class RecentPlayers : BaseUnityPlugin
{
private static class HarmonyPatches
{
internal static void MatchmakingHandlerAwake(LoadBalancingClient ___client)
{
AddLoadBalancingClientCallbacks(___client);
LogDebug(() => "Hooked MatchmakingHandler.Awake");
}
internal static void AddCallbacksOnce()
{
if (PhotonNetwork.IsConnected && Interlocked.CompareExchange(ref initialized, 1, 0) == 0)
{
ModdingUtils.RegisterLocalMod();
AddLoadBalancingClientCallbacks(PhotonNetwork.NetworkingClient);
SetTimer();
LogDebug(() => "Hooked PhotonService.Connect");
}
}
internal static void SetSteamPlayerProperties()
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
//IL_0040: Expected O, but got Unknown
try
{
object oldSteamId = PhotonNetwork.LocalPlayer.CustomProperties[(object)"STEAM_ID"];
Player localPlayer = PhotonNetwork.LocalPlayer;
Hashtable val = new Hashtable();
((Dictionary<object, object>)val).Add((object)"STEAM_ID", (object)PlayerProfile.GetSteamID());
localPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
LogDebug(() => $"Changed our STEAM_ID property from \"{oldSteamId}\" to \"{PlayerProfile.GetSteamID()}\". Note that our UNITY_CLOUD_ID is \"{PlayerProfile.GetUnityCloudID()}\" and our Photon UserId is \"{PhotonNetwork.LocalPlayer.UserId}\"");
}
catch (Exception arg)
{
Logger.LogError((object)$"Some mystery error happened when setting our SteamID: {arg}");
}
}
}
internal const string GUID = "dev.zkxs.voidcrew.recentplayers";
internal const string MOD_NAME = "RecentPlayers";
internal const string MOD_VERSION = "1.2.0";
private const double TIMER_INTERVAL_MS = 120000.0;
internal static ManualLogSource? Logger;
private static int initialized = 0;
private static ConditionalWeakTable<LoadBalancingClient, object?> hookedLoadBalancingClients = new ConditionalWeakTable<LoadBalancingClient, object>();
private static ConditionalWeakTable<Player, object?> naughtyPlayers = new ConditionalWeakTable<Player, object>();
private static System.Timers.Timer? timer = null;
private static ConfigEntry<bool>? runTimer;
private static ConfigEntry<bool>? debugLogs;
private void Awake()
{
//IL_0050: 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_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Expected O, but got Unknown
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Expected O, but got Unknown
//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
//IL_010c: Expected O, but got Unknown
try
{
Logger = ((BaseUnityPlugin)this).Logger;
runTimer = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Timer", true, "Periodically resend the played-with-user event to Steam. Without this, the event is only sent on lobby join.");
debugLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Debug Logs", false, "Emit LogLevel.Debug logs. In the default BepInEx.cfg these go nowhere. If you have BepInEx debug logging enabled and this mod's logs are bothering you, set this config to false.");
Harmony val = new Harmony("dev.zkxs.voidcrew.recentplayers");
val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(MatchmakingHandler), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(HarmonyPatches), "MatchmakingHandlerAwake", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
val.Patch((MethodBase)GetAsyncMethodBody(AccessTools.DeclaredMethod(typeof(PhotonService), "Connect", (Type[])null, (Type[])null)), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(HarmonyPatches), "AddCallbacksOnce", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(PhotonService), "SetSteamPlayerProperties", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(HarmonyPatches), "SetSteamPlayerProperties", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Logger.LogInfo((object)"successfully installed patches!");
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)$"Something has gone terribly wrong:\n{ex}");
throw ex;
}
}
private static void LogDebug(Func<string> messageFunc)
{
if (debugLogs.Value)
{
Logger.LogDebug((object)messageFunc());
}
}
private static void SetTimer()
{
timer = new System.Timers.Timer(120000.0);
timer.Elapsed += OnTimerEvent;
timer.Start();
}
private static void OnTimerEvent(object source, ElapsedEventArgs eventArgs)
{
if (runTimer.Value)
{
SetLobbyPlayedWith();
}
}
public static void SetLobbyPlayedWith()
{
Room currentRoom = PhotonNetwork.CurrentRoom;
Dictionary<int, Player>.ValueCollection valueCollection = ((currentRoom == null) ? null : currentRoom.Players?.Values);
if (valueCollection == null)
{
return;
}
foreach (Player item in valueCollection)
{
SetPlayedWith(item);
}
}
public static void SetPlayedWith(Player player)
{
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
Player player2 = player;
if (player2.IsLocal)
{
return;
}
if (!naughtyPlayers.TryGetValue(player2, out object _))
{
try
{
SteamFriends.SetPlayedWith(GetSteamID(player2));
LogDebug(() => $"SetPlayedWith({player2})");
return;
}
catch (SteamIdException ex)
{
SteamIdException ex2 = ex;
SteamIdException e1 = ex2;
naughtyPlayers.Add(player2, null);
LogDebug(() => $"Could not get SteamID for \"{player2}\" from primary source, which is expected as the primary source is known to be bugged. Will try again with fallback source. {e1}");
}
}
try
{
SteamFriends.SetPlayedWith(GetSteamIDFallback(player2));
LogDebug(() => $"SetPlayedWith({player2})");
}
catch (SteamIdException arg)
{
Logger.LogWarning((object)$"Could not get SteamID for \"{player2}\". {arg}");
}
}
private static CSteamID GetSteamID(Player player)
{
//IL_0053: 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)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
Hashtable customProperties = player.CustomProperties;
object obj = ((customProperties != null) ? customProperties[(object)"STEAM_ID"] : null) ?? throw new SteamIdException("unset");
try
{
CSteamID result = default(CSteamID);
((CSteamID)(ref result))..ctor(ulong.Parse((string)obj));
if (!((CSteamID)(ref result)).BIndividualAccount())
{
throw new SteamIdException($"\"{obj}\" is not an individual account");
}
return result;
}
catch (InvalidCastException innerException)
{
throw new SteamIdException($"\"{obj}\" is not a string", innerException);
}
catch (FormatException innerException2)
{
throw new SteamIdException($"\"{obj}\" could not be parsed", innerException2);
}
catch (OverflowException innerException3)
{
throw new SteamIdException($"\"{obj}\" overflows", innerException3);
}
}
private static CSteamID GetSteamIDFallback(Player player)
{
//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_006a: Unknown result type (might be due to invalid IL or missing references)
object obj = ((player != null) ? player.UserId : null) ?? throw new SteamIdException("unset");
try
{
return new CSteamID(ulong.Parse((string)obj));
}
catch (InvalidCastException innerException)
{
throw new SteamIdException($"\"{obj}\" is not a string", innerException);
}
catch (FormatException innerException2)
{
throw new SteamIdException($"\"{obj}\" could not be parsed", innerException2);
}
catch (OverflowException innerException3)
{
throw new SteamIdException($"\"{obj}\" overflows", innerException3);
}
}
private static MethodInfo GetAsyncMethodBody(MethodInfo asyncMethod)
{
MethodInfo methodInfo = AccessTools.DeclaredMethod((asyncMethod.GetCustomAttribute<AsyncStateMachineAttribute>() ?? throw new ReflectionException($"Could not find AsyncStateMachine for {asyncMethod}")).StateMachineType, "MoveNext", (Type[])null, (Type[])null);
if (methodInfo == null)
{
throw new ReflectionException($"Could not find async method body for {asyncMethod}");
}
return methodInfo;
}
private static void AddLoadBalancingClientCallbacks(LoadBalancingClient client)
{
bool existingMapping = true;
hookedLoadBalancingClients.GetValue(client, delegate(LoadBalancingClient client)
{
existingMapping = false;
try
{
client.AddCallbackTarget((object)new PhotonInRoomCallbacks());
client.AddCallbackTarget((object)new PhotonMatchmakingCallbacks());
}
catch (Exception arg)
{
Logger.LogError((object)$"Some mystery error happened when adding photon callbacks: {arg}");
}
return null;
});
if (existingMapping)
{
LogDebug(() => "We encountered a LoadBalancingClient more than once. This is unexpected, but handled.");
}
}
}
[Serializable]
internal class ReflectionException : Exception
{
public ReflectionException(string message)
: base(message)
{
}
}
internal class SteamIdException : Exception
{
public SteamIdException(string message)
: base(message)
{
}
public SteamIdException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}