11using System ;
22using System . IO ;
33using System . Linq ;
4+ using System . Threading ;
45using System . Net . Http ;
56using System . Threading . Tasks ;
67using System . Collections . Generic ;
78using HearthMirror ;
89using Hearthstone_Deck_Tracker ;
910using Hearthstone_Deck_Tracker . Enums ;
10- using Hearthstone_Deck_Tracker . Utility . Logging ;
1111
1212namespace HDT_BGrank
1313{
@@ -21,7 +21,10 @@ public class BGrank
2121 private bool namesReady = false ;
2222 private bool playersReady = false ;
2323 private bool leaderBoardReady = false ;
24+ private int leaderBoardRequested = 0 ;
25+ private int nameErrors = 0 ;
2426
27+ private readonly Mirror mirror ;
2528 private readonly HttpClient client ;
2629 private List < string > oppNames = null ;
2730 private Dictionary < string , string > leaderBoard = null ;
@@ -30,6 +33,8 @@ public BGrank()
3033 {
3134 client = new HttpClient ( ) ;
3235 client . DefaultRequestHeaders . Add ( "User-Agent" , "User-Agent-Here" ) ;
36+ client . Timeout = TimeSpan . FromSeconds ( 10 ) ;
37+ mirror = new Mirror { ImageName = "Hearthstone" } ;
3338 }
3439
3540 private void Reset ( )
@@ -39,6 +44,8 @@ private void Reset()
3944 playersReady = false ;
4045 failToGetData = false ;
4146 leaderBoardReady = false ;
47+ leaderBoardRequested = 0 ;
48+ nameErrors = 0 ;
4249 ClearMemory ( ) ;
4350 }
4451
@@ -47,16 +54,17 @@ public void ClearMemory()
4754 oppDict = null ;
4855 oppNames = null ;
4956 leaderBoard = null ;
57+ mirror . Clean ( ) ;
5058 }
5159
5260 public void OnGameStart ( )
5361 {
54- GetLeaderBoard ( ) ;
62+ _ = GetLeaderBoard ( ) ;
5563 }
5664
5765 public void OnTurnStart ( ActivePlayer player )
5866 {
59- GetLeaderBoard ( ) ;
67+ _ = GetLeaderBoard ( ) ;
6068 playersReady = true ;
6169 }
6270
@@ -77,13 +85,21 @@ public void OnUpdate()
7785 else if ( ! namesReady ) { GetOppNames ( ) ; }
7886 else if ( leaderBoardReady )
7987 {
80- Dictionary < string , int > unsortDict = new Dictionary < string , int > ( ) ;
81- oppDict = new Dictionary < string , string > ( ) ;
88+ Dictionary < string , int > unsortDict = new Dictionary < string , int > ( oppNames ? . Count ?? 0 ) ;
89+ oppDict = new Dictionary < string , string > ( oppNames ? . Count ?? 0 ) ;
8290 foreach ( string name in oppNames )
8391 {
8492 if ( leaderBoard . TryGetValue ( name , out string value ) )
8593 {
86- unsortDict . Add ( name , int . Parse ( value ) ) ;
94+ if ( int . TryParse ( value , out int rate ) )
95+ {
96+ unsortDict . Add ( name , rate ) ;
97+ }
98+ else
99+ {
100+ FileLogger . Instance . Warn ( $ "Parse MMR failed (unexpected), set MMR as 0. player:'{ name } ' MMR:'{ value } '") ;
101+ unsortDict . Add ( name , 0 ) ;
102+ }
87103 }
88104 else
89105 {
@@ -117,6 +133,8 @@ private async Task GetLeaderBoard()
117133 {
118134 if ( ! Core . Game . IsBattlegroundsMatch || leaderBoardReady || failToGetData ) { return ; }
119135
136+ if ( Interlocked . CompareExchange ( ref leaderBoardRequested , 1 , 0 ) != 0 ) { return ; }
137+
120138 // Get the leaderboard information from web, see https://github.com/IBM5100o/BGrank_bot
121139 leaderBoard = new Dictionary < string , string > ( ) ;
122140 string region = GetRegionStr ( ) ;
@@ -126,12 +144,12 @@ private async Task GetLeaderBoard()
126144
127145 if ( Core . Game . IsBattlegroundsSoloMatch )
128146 {
129- path = Path . Combine ( Config . AppDataPath , $ "LeaderBoard_{ region } .txt") ;
147+ path = Path . Combine ( Config . AppDataPath , "BGrank" , $ "LeaderBoard_{ region } .txt") ;
130148 url = $ "https://bgrank.fly.dev/{ region } /";
131149 }
132150 else
133151 {
134- path = Path . Combine ( Config . AppDataPath , $ "LeaderBoard_{ region } _duo.txt") ;
152+ path = Path . Combine ( Config . AppDataPath , "BGrank" , $ "LeaderBoard_{ region } _duo.txt") ;
135153 url = $ "https://bgrank.fly.dev/{ region } _duo/";
136154 }
137155
@@ -140,11 +158,11 @@ private async Task GetLeaderBoard()
140158 num_tries ++ ;
141159 try
142160 {
143- Log . Info ( $ "Try to get the leaderboard from { url } (try { num_tries } /{ max_tries } )") ;
144- string response = await client . GetStringAsync ( url ) ;
145- if ( string . IsNullOrEmpty ( response ) )
161+ FileLogger . Instance . Info ( $ "Try to get the leaderboard from { url } (try { num_tries } /{ max_tries } )") ;
162+ string response = await client . GetStringAsync ( url ) . ConfigureAwait ( false ) ;
163+ if ( string . IsNullOrWhiteSpace ( response ) )
146164 {
147- if ( num_tries < max_tries ) { await Task . Delay ( 10000 ) ; }
165+ if ( num_tries < max_tries ) { await Task . Delay ( 5000 ) ; }
148166 continue ;
149167 }
150168
@@ -157,34 +175,43 @@ private async Task GetLeaderBoard()
157175 {
158176 string name = tmp [ 0 ] ;
159177 string rating = tmp [ 1 ] ;
160- if ( string . IsNullOrEmpty ( name ) || string . IsNullOrEmpty ( rating ) ) { continue ; }
178+ if ( string . IsNullOrWhiteSpace ( name ) || string . IsNullOrWhiteSpace ( rating ) ) { continue ; }
161179 if ( ! leaderBoard . ContainsKey ( name ) ) { leaderBoard . Add ( name , rating ) ; }
162180 }
163181 }
164-
165- if ( leaderBoard . Count != 0 )
182+ if ( leaderBoard . Count != 0 )
166183 {
167- Log . Info ( "Success to get the leaderboard from web! " ) ;
184+ FileLogger . Instance . Info ( $ "Success to get the leaderboard from { url } ") ;
168185 leaderBoardReady = true ;
169- using ( StreamWriter writer = new StreamWriter ( path ) )
186+ }
187+ }
188+ catch ( Exception ex )
189+ {
190+ FileLogger . Instance . Error ( $ "Failed to get the leaderboard from { url } ", ex ) ;
191+ if ( num_tries < max_tries ) { await Task . Delay ( 5000 ) ; }
192+ }
193+ }
194+
195+ if ( leaderBoardReady )
196+ {
197+ try
198+ {
199+ using ( StreamWriter writer = new StreamWriter ( path ) )
200+ {
201+ foreach ( var player in leaderBoard )
170202 {
171- foreach ( var player in leaderBoard )
172- {
173- writer . WriteLine ( $ "{ player . Key } { player . Value } ") ;
174- }
203+ writer . WriteLine ( $ "{ player . Key } { player . Value } ") ;
175204 }
176205 }
177206 }
178207 catch ( Exception ex )
179208 {
180- Log . Error ( ex ) ;
181- if ( num_tries < max_tries ) { await Task . Delay ( 10000 ) ; }
209+ FileLogger . Instance . Error ( "Failed to save the leaderboard to local" , ex ) ;
182210 }
183211 }
184-
185- if ( ! leaderBoardReady )
212+ else
186213 {
187- Log . Info ( "Fail to get the leaderboard from web, try to get it from local..." ) ;
214+ FileLogger . Instance . Info ( "Failed to get the leaderboard from web, try to get it from local..." ) ;
188215 if ( File . Exists ( path ) )
189216 {
190217 try
@@ -198,25 +225,26 @@ private async Task GetLeaderBoard()
198225 leaderBoard . Add ( tmp [ 0 ] , tmp [ 1 ] ) ;
199226 }
200227 }
201- if ( leaderBoard . Count != 0 )
228+ if ( leaderBoard . Count != 0 )
202229 {
203- Log . Info ( "Success to get the leaderboard from local!" ) ;
230+ FileLogger . Instance . Info ( "Success to get the leaderboard from local!" ) ;
204231 leaderBoardReady = true ;
205232 }
206233 else { failToGetData = true ; }
207234 }
208235 catch ( Exception ex )
209236 {
210- Log . Error ( ex ) ;
237+ FileLogger . Instance . Error ( "Failed to read the local file" , ex ) ;
211238 failToGetData = true ;
212239 }
213240 }
214241 else
215242 {
243+ FileLogger . Instance . Info ( "Local file not exist" ) ;
216244 failToGetData = true ;
217245 }
218246 }
219- if ( failToGetData ) { Log . Info ( "Fail to get the leaderboard from local , no data for this match" ) ; }
247+ if ( failToGetData ) { FileLogger . Instance . Info ( "Failed to get the leaderboard, no data for this match" ) ; }
220248 }
221249
222250 private string GetRegionStr ( )
@@ -242,21 +270,20 @@ private void GetOppNames()
242270 try
243271 {
244272 string myName = Reflection . Client ? . GetBattleTag ( ) ? . Name ;
245- if ( string . IsNullOrEmpty ( myName ) ) { return ; }
246- Mirror mirror = new Mirror { ImageName = "Hearthstone" } ;
273+ if ( string . IsNullOrWhiteSpace ( myName ) ) { return ; }
247274 var leaderboardMgr = mirror . Root ? [ "PlayerLeaderboardManager" ] ? [ "s_instance" ] ;
248275 if ( leaderboardMgr == null ) { return ; }
249276 dynamic [ ] playerTiles = GetPlayerTiles ( leaderboardMgr ) ;
250- var numberOfPlayerTiles = playerTiles ? . Length ?? 0 ;
277+ int numberOfPlayerTiles = playerTiles ? . Length ?? 0 ;
251278 if ( numberOfPlayerTiles == 0 ) { return ; }
252- List < string > tmpNames = new List < string > ( ) ;
279+ List < string > tmpNames = new List < string > ( numberOfPlayerTiles ) ;
253280
254281 for ( int i = 0 ; i < numberOfPlayerTiles ; i ++ )
255282 {
256283 var playerTile = playerTiles [ i ] ;
257284 // Info not available until the player mouses over the tile in the leaderboard, and there is no other way to get it
258285 string playerName = playerTile [ "m_overlay" ] ? [ "m_heroActor" ] ? [ "m_playerNameText" ] ? [ "m_Text" ] ;
259- if ( string . IsNullOrEmpty ( playerName ) ) { return ; }
286+ if ( string . IsNullOrWhiteSpace ( playerName ) ) { return ; }
260287 if ( playerName != myName && ! tmpNames . Contains ( playerName ) ) { tmpNames . Add ( playerName ) ; }
261288 }
262289
@@ -272,9 +299,17 @@ private void GetOppNames()
272299 oppNames = new List < string > ( tmpNames ) ;
273300 namesReady = true ;
274301 }
275- catch ( Exception ex )
302+ catch ( Exception ex )
276303 {
277- Log . Error ( ex ) ;
304+ nameErrors ++ ;
305+ if ( nameErrors < 5 )
306+ {
307+ FileLogger . Instance . Error ( "Fail to get opponent name" , ex ) ;
308+ }
309+ else if ( nameErrors == 5 )
310+ {
311+ FileLogger . Instance . Error ( "Fail to get opponent name, further errors will be suppressed" , ex ) ;
312+ }
278313 }
279314 }
280315
0 commit comments