@@ -256,6 +256,7 @@ public static void Init(AudienceConfig config)
256256 }
257257
258258 FireGameLaunch ( config , consentAtInit , skanRegistered , attributionContext ) ;
259+ TryIdentifySteamUser ( ) ;
259260
260261 CheckAndFireAttStatusChanged ( config , consentAtInit ) ;
261262
@@ -1089,6 +1090,130 @@ private static void RescheduleSendTimer(HttpTransport transport)
10891090 timer . Change ( nextMs , sendIntervalMs ) ;
10901091 }
10911092
1093+ // Resolves Steamworks.SteamAPI (Steamworks.NET) across all common install methods:
1094+ // UPM / OpenUPM package → com.rlabrecque.steamworks.net
1095+ // .dll plugin in Assets → Steamworks.NET
1096+ // source files in Assets → Assembly-CSharp
1097+ // Returns null when Steamworks.NET is not present.
1098+ private static System . Type ? ResolveSteamApiType ( ) =>
1099+ System . Type . GetType ( "Steamworks.SteamAPI, com.rlabrecque.steamworks.net" )
1100+ ?? System . Type . GetType ( "Steamworks.SteamAPI, Steamworks.NET" )
1101+ ?? System . Type . GetType ( "Steamworks.SteamAPI, Assembly-CSharp" ) ;
1102+
1103+ // Resolves Steamworks.SteamClient (Facepunch.Steamworks) across common install methods.
1104+ // Facepunch ships platform-specific DLLs; the assembly name encodes the platform:
1105+ // macOS/Linux → Facepunch.Steamworks.Posix
1106+ // Windows 64 → Facepunch.Steamworks.Win64
1107+ // Windows 32 → Facepunch.Steamworks.Win32
1108+ // Source in Assets → Assembly-CSharp
1109+ // Returns null when Facepunch.Steamworks is not present.
1110+ private static System . Type ? ResolveFacepunchSteamClientType ( ) =>
1111+ System . Type . GetType ( "Steamworks.SteamClient, Facepunch.Steamworks.Posix" )
1112+ ?? System . Type . GetType ( "Steamworks.SteamClient, Facepunch.Steamworks.Win64" )
1113+ ?? System . Type . GetType ( "Steamworks.SteamClient, Facepunch.Steamworks.Win32" )
1114+ ?? System . Type . GetType ( "Steamworks.SteamClient, Assembly-CSharp" ) ;
1115+
1116+ // Sets distribution_platform = "steam" when any supported Steam wrapper is
1117+ // detected as active. Config override wins afterward (line ~1195).
1118+ private static void TryDetectSteamPlatform ( Dictionary < string , object > properties )
1119+ {
1120+ try
1121+ {
1122+ if ( IsSteamworksNetRunning ( ) || IsFacepunchSteamValid ( ) )
1123+ properties [ "distribution_platform" ] = DistributionPlatforms . Steam ;
1124+ }
1125+ catch ( Exception ex )
1126+ {
1127+ Log . Warn ( AudienceLogs . SteamPlatformDetectionFailed ( ex ) ) ;
1128+ }
1129+ }
1130+
1131+ // Calls Identify with the logged-in SteamID64. Tries Steamworks.NET first,
1132+ // then Facepunch.Steamworks. No-ops if neither is present, Steam is not
1133+ // running, the ID is invalid, or consent is below Full.
1134+ private static void TryIdentifySteamUser ( )
1135+ {
1136+ try
1137+ {
1138+ if ( ! TryGetSteamworksNetId ( out var id ) && ! TryGetFacepunchId ( out id ) )
1139+ return ;
1140+ Log . Debug ( AudienceLogs . SteamAutoIdentified ( id ! ) ) ;
1141+ Identify ( id ! , IdentityType . Steam ) ;
1142+ }
1143+ catch ( Exception ex )
1144+ {
1145+ Log . Warn ( AudienceLogs . SteamIdentityCollectionFailed ( ex ) ) ;
1146+ }
1147+ }
1148+
1149+ // Returns true if Steamworks.NET's SteamAPI.IsSteamRunning() is true.
1150+ private static bool IsSteamworksNetRunning ( )
1151+ {
1152+ var steamApi = ResolveSteamApiType ( ) ;
1153+ if ( steamApi == null ) return false ;
1154+ return steamApi . GetMethod ( "IsSteamRunning" ) ? . Invoke ( null , null ) as bool ? == true ;
1155+ }
1156+
1157+ // Returns true if Facepunch.Steamworks's SteamClient.IsValid is true.
1158+ private static bool IsFacepunchSteamValid ( )
1159+ {
1160+ var steamClient = ResolveFacepunchSteamClientType ( ) ;
1161+ if ( steamClient == null ) return false ;
1162+ return steamClient . GetProperty ( "IsValid" ) ? . GetValue ( null ) as bool ? == true ;
1163+ }
1164+
1165+ // Reads the SteamID64 via Steamworks.NET (SteamUser.GetSteamID → m_SteamID ulong).
1166+ // Attempts SteamAPI.Init() first so the sample app works without manual Steam init;
1167+ // in shipping games Init() is already called before our SDK runs.
1168+ private static bool TryGetSteamworksNetId ( out string ? id )
1169+ {
1170+ id = null ;
1171+ var steamApi = ResolveSteamApiType ( ) ;
1172+ if ( steamApi == null ) return false ;
1173+ if ( steamApi . GetMethod ( "IsSteamRunning" ) ? . Invoke ( null , null ) as bool ? != true ) return false ;
1174+
1175+ // Safe to call even if already initialised; returns false but is otherwise a no-op.
1176+ steamApi . GetMethod ( "Init" ) ? . Invoke ( null , null ) ;
1177+
1178+ var steamUserType =
1179+ System . Type . GetType ( "Steamworks.SteamUser, com.rlabrecque.steamworks.net" )
1180+ ?? System . Type . GetType ( "Steamworks.SteamUser, Steamworks.NET" )
1181+ ?? System . Type . GetType ( "Steamworks.SteamUser, Assembly-CSharp" ) ;
1182+ if ( steamUserType == null ) return false ;
1183+
1184+ var steamId = steamUserType . GetMethod ( "GetSteamID" ) ? . Invoke ( null , null ) ;
1185+ if ( steamId == null ) return false ;
1186+
1187+ // CSteamID.m_SteamID == 0 means not logged in / not initialised.
1188+ var raw = steamId . GetType ( ) . GetField ( "m_SteamID" ) ? . GetValue ( steamId ) ;
1189+ if ( raw == null || ( ulong ) raw == 0 ) return false ;
1190+
1191+ id = ( ( ulong ) raw ) . ToString ( ) ;
1192+ return true ;
1193+ }
1194+
1195+ // Reads the SteamID64 via Facepunch.Steamworks (SteamClient.SteamId.Value ulong).
1196+ // Requires SteamClient.Init(appId) to have been called by the game already;
1197+ // the SDK cannot call it as the appId is unknown.
1198+ private static bool TryGetFacepunchId ( out string ? id )
1199+ {
1200+ id = null ;
1201+ var steamClient = ResolveFacepunchSteamClientType ( ) ;
1202+ if ( steamClient == null ) return false ;
1203+ if ( steamClient . GetProperty ( "IsValid" ) ? . GetValue ( null ) as bool ? != true ) return false ;
1204+
1205+ var steamIdProp = steamClient . GetProperty ( "SteamId" ) ;
1206+ var steamId = steamIdProp ? . GetValue ( null ) ;
1207+ if ( steamId == null ) return false ;
1208+
1209+ // Facepunch SteamId exposes Value as a public ulong field (not a property).
1210+ var raw = steamId . GetType ( ) . GetField ( "Value" ) ? . GetValue ( steamId ) ;
1211+ if ( raw == null || ( ulong ) raw == 0 ) return false ;
1212+
1213+ id = ( ( ulong ) raw ) . ToString ( ) ;
1214+ return true ;
1215+ }
1216+
10921217 // consentAtInit only gates the launch; Track still checks live _state via CanTrack.
10931218 private static void FireGameLaunch (
10941219 AudienceConfig config ,
@@ -1119,7 +1244,10 @@ private static void FireGameLaunch(
11191244 }
11201245 }
11211246
1122- // Config-supplied distributionPlatform overrides the provider value.
1247+ // Auto-detect distribution platform via reflection. Config override wins below.
1248+ TryDetectSteamPlatform ( properties ) ;
1249+
1250+ // Config-supplied distributionPlatform overrides the auto-detected value.
11231251 if ( config . DistributionPlatform != null )
11241252 properties [ "distribution_platform" ] = config . DistributionPlatform ;
11251253
0 commit comments