1414using ZkData ;
1515using System . Net . NetworkInformation ;
1616using Timer = System . Timers . Timer ;
17+ using System . IO ;
18+ using System . Drawing ;
19+ using System . Runtime . InteropServices ;
20+ using System . Drawing . Imaging ;
21+ using Newtonsoft . Json ;
1722
1823namespace ChobbyLauncher
1924{
@@ -36,6 +41,7 @@ public enum OverlayOption
3641
3742 private bool isDisposed ;
3843
44+ private Callback < PersonaStateChange_t > friendStateChangeCallback ;
3945 private Callback < GameLobbyJoinRequested_t > lobbyJoinRequestCallback ;
4046 private Callback < P2PSessionRequest_t > newConnectionCallback ;
4147 private Callback < GameOverlayActivated_t > overlayActivatedCallback ;
@@ -47,14 +53,20 @@ public enum OverlayOption
4753
4854 public string AuthToken { get ; private set ; }
4955
50- public List < SteamFriend > Friends { get ; private set ; }
56+ public ConcurrentDictionary < ulong , SteamFriend > Friends { get ; private set ; }
5157 public bool IsOnline { get ; private set ; }
5258 public ChobbylaLocalListener Listener { get ; set ; }
5359
5460 public ulong ? LobbyID { get ; set ; }
5561
5662 public string MySteamNameSanitized { get ; set ; }
5763
64+ private Chobbyla chobbyla ;
65+
66+ public SteamClientHelper ( Chobbyla chobbyla )
67+ {
68+ this . chobbyla = chobbyla ;
69+ }
5870
5971 public void Dispose ( )
6072 {
@@ -88,8 +100,8 @@ public void ConnectToSteam()
88100 foreach ( var f in GetFriendIDs ( ) )
89101 {
90102 FriendGameInfo_t gi ;
91- SteamFriends . GetFriendGamePlayed ( new CSteamID ( f ) , out gi ) ;
92- if ( gi . m_steamIDLobby . m_SteamID == lobbyID ) return f ;
103+ SteamFriends . GetFriendGamePlayed ( f , out gi ) ;
104+ if ( gi . m_steamIDLobby . m_SteamID == lobbyID ) return f . m_SteamID ;
93105 }
94106 return null ;
95107 }
@@ -118,6 +130,9 @@ public void InviteFriendToGame(ulong lobbyID, ulong friendID)
118130 if ( IsOnline ) SteamMatchmaking . InviteUserToLobby ( new CSteamID ( lobbyID ) , new CSteamID ( friendID ) ) ;
119131 }
120132
133+
134+ public event Action < SteamFriend > FriendListUpdate = ( freund ) => { } ;
135+
121136 public event Action < ulong > JoinFriendRequest = ( steamID ) => { } ;
122137
123138 public void OpenOverlaySection ( OverlayOption option )
@@ -241,26 +256,34 @@ private string GetClientAuthTokenHex()
241256 }
242257
243258
244- private List < SteamFriend > getFriends ( )
259+ private ConcurrentDictionary < ulong , SteamFriend > GetFriends ( )
245260 {
246261 if ( IsOnline )
247262 {
248- return GetFriendIDs ( ) . Select ( x => new SteamFriend ( )
263+ var dic = new ConcurrentDictionary < ulong , SteamFriend > ( GetFriendIDs ( ) . ToDictionary ( x => x . m_SteamID , x => new SteamFriend ( )
249264 {
250265 Name = SteamFriends . GetFriendPersonaName ( x ) ,
251- SteamID = x . ToString ( )
252- } ) . ToList ( ) ;
266+ CSteamID = x
267+ } ) ) ;
268+ dic . ForEach ( freund =>
269+ {
270+ var state = SteamFriends . GetFriendPersonaState ( freund . Value . CSteamID ) ;
271+ freund . Value . IsOnline = state . HasFlag ( EPersonaState . k_EPersonaStateOnline ) ;
272+ FriendGameInfo_t game ;
273+ freund . Value . IsPlaying = SteamFriends . GetFriendGamePlayed ( freund . Value . CSteamID , out game ) && ( game . m_gameID . AppID ( ) . m_AppId == GlobalConst . SteamAppID ) ;
274+ } ) ;
275+ return dic ;
253276 }
254277 return null ;
255278 }
256279
257- private List < ulong > GetFriendIDs ( )
280+ private List < CSteamID > GetFriendIDs ( )
258281 {
259282 if ( IsOnline )
260283 {
261- var ret = new List < ulong > ( ) ;
284+ var ret = new List < CSteamID > ( ) ;
262285 var cnt = SteamFriends . GetFriendCount ( EFriendFlags . k_EFriendFlagImmediate ) ;
263- for ( var i = 0 ; i < cnt ; i ++ ) ret . Add ( SteamFriends . GetFriendByIndex ( i , EFriendFlags . k_EFriendFlagImmediate ) . m_SteamID ) ;
286+ for ( var i = 0 ; i < cnt ; i ++ ) ret . Add ( SteamFriends . GetFriendByIndex ( i , EFriendFlags . k_EFriendFlagImmediate ) ) ;
264287 return ret ;
265288 }
266289 return null ;
@@ -278,13 +301,52 @@ private ulong GetSteamID()
278301 return 0 ;
279302 }
280303
304+ private void DownloadAvatars ( ICollection < CSteamID > users )
305+ {
306+ using ( var wc = new WebClient ( ) )
307+ {
308+ var targetDir = Path . Combine ( chobbyla . paths . WritableDirectory , "LuaUI" , "Configs" , "SteamAvatars" ) ;
309+ if ( ! Directory . Exists ( targetDir ) ) Directory . CreateDirectory ( targetDir ) ;
310+ foreach ( var user in users )
311+ {
312+ //download image via steamfriends
313+ int handle = SteamFriends . GetMediumFriendAvatar ( user ) ;
314+ uint imgW , imgH ;
315+ SteamUtils . GetImageSize ( handle , out imgW , out imgH ) ;
316+ byte [ ] imgBuffer = new byte [ 4 * imgW * imgH ] ;
317+ GCHandle pinnedArray = GCHandle . Alloc ( imgBuffer , GCHandleType . Pinned ) ; //please make a better implementation of this
318+ IntPtr pointer = pinnedArray . AddrOfPinnedObject ( ) ;
319+ SteamUtils . GetImageRGBA ( handle , imgBuffer , imgBuffer . Length ) ;
320+ var bitmap = new Bitmap ( ( int ) imgW , ( int ) imgH , 4 * ( int ) imgW , PixelFormat . Format32bppArgb , pointer ) ;
321+
322+ var path = Path . Combine ( targetDir , string . Format ( "{0}.png" , user . m_SteamID ) ) ;
323+ if ( File . Exists ( path ) ) File . Delete ( path ) ;
324+ bitmap . Save ( path , ImageFormat . Png ) ;
325+
326+ pinnedArray . Free ( ) ;
327+ }
328+ }
329+ }
330+
331+ private void OnFriendStateUpdate ( PersonaStateChange_t u )
332+ {
333+ if ( ! Friends . ContainsKey ( u . m_ulSteamID ) ) return ; //Needed because this method is called for the player himself (not part of friend list)
334+ var freund = Friends [ u . m_ulSteamID ] ;
335+ if ( u . m_nChangeFlags . HasFlag ( EPersonaChange . k_EPersonaChangeComeOnline ) ) freund . IsOnline = true ;
336+ if ( u . m_nChangeFlags . HasFlag ( EPersonaChange . k_EPersonaChangeGoneOffline ) ) freund . IsOnline = false ;
337+ FriendGameInfo_t game ;
338+ freund . IsPlaying = SteamFriends . GetFriendGamePlayed ( new CSteamID ( u . m_ulSteamID ) , out game ) && ( game . m_gameID . AppID ( ) . m_AppId == GlobalConst . SteamAppID ) ;
339+ FriendListUpdate ? . Invoke ( freund ) ;
340+ }
341+
281342 private void OnSteamOnline ( )
282343 {
283344 Trace . TraceInformation ( "Steam online" ) ;
284345
285346 lobbyJoinRequestCallback = new Callback < GameLobbyJoinRequested_t > ( t => { JoinFriendRequest ( t . m_steamIDFriend . m_SteamID ) ; } ) ;
286347 overlayActivatedCallback = new Callback < GameOverlayActivated_t > ( t => { OverlayActivated ( t . m_bActive != 0 ) ; } ) ;
287348 newConnectionCallback = Callback < P2PSessionRequest_t > . Create ( t => SteamNetworking . AcceptP2PSessionWithUser ( t . m_steamIDRemote ) ) ;
349+ friendStateChangeCallback = new Callback < PersonaStateChange_t > ( t => OnFriendStateUpdate ( t ) ) ;
288350 MySteamNameSanitized = Utils . StripInvalidLobbyNameChars ( GetMyName ( ) ) ;
289351
290352
@@ -296,9 +358,11 @@ private void OnSteamOnline()
296358 ev . Set ( ) ;
297359 } ) ;
298360 SteamNetworking . AllowP2PPacketRelay ( true ) ;
299- Friends = GetFriendIDs ( ) ;
361+ Friends = GetFriends ( ) ;
362+ Task . Factory . StartNew ( ( ) => DownloadAvatars ( Friends . Values . Select ( x => x . CSteamID ) . ToList ( ) ) ) ;
300363 ev . WaitOne ( 2000 ) ;
301364 SteamOnline ? . Invoke ( ) ;
365+ FriendListUpdate ? . Invoke ( null ) ;
302366 }
303367
304368 private void ProcessMessage ( ulong remoteUser , Dummy cmd )
@@ -439,10 +503,15 @@ private void TimerOnElapsed(object sender)
439503 }
440504 }
441505
442- class SteamFriend
506+ public class SteamFriend
443507 {
508+ private CSteamID _cSteamID ;
444509
445- public string SteamID ;
510+ [ JsonIgnore ]
511+ public CSteamID CSteamID { get { return _cSteamID ; } set { _cSteamID = value ; SteamID = value . m_SteamID ; } }
512+ public ulong SteamID { get ; private set ; }
446513 public string Name ;
514+ public bool IsOnline ;
515+ public bool IsPlaying ; //whether friend is playing zk right now
447516 }
448517}
0 commit comments