22using SteamQueryNet . Utils ;
33using System ;
44using System . Collections . Generic ;
5+ using System . Globalization ;
56using System . Linq ;
67using System . Net ;
78using System . Net . NetworkInformation ;
89using System . Net . Sockets ;
910using System . Reflection ;
1011using System . Runtime . InteropServices ;
1112using System . Text ;
13+ using System . Threading ;
14+ using System . Threading . Tasks ;
1215
1316namespace SteamQueryNet
1417{
@@ -18,7 +21,14 @@ public interface IServerQuery
1821 /// <summary>
1922 /// Renews the server challenge code of the ServerQuery instance in order to be able to execute further operations.
2023 /// </summary>
21- void RenewChallenge ( ) ;
24+ /// <returns>The new created challenge.</returns>
25+ int RenewChallenge ( ) ;
26+
27+ /// <summary>
28+ /// Renews the server challenge code of the ServerQuery instance in order to be able to execute further operations.
29+ /// </summary>
30+ /// <returns>The new created challenge.</returns>
31+ Task < int > RenewChallengeAsync ( ) ;
2232
2333 /// <summary>
2434 /// Configures and Connects the created instance of SteamQuery UDP socket for Steam Server Query Operations.
@@ -34,19 +44,39 @@ public interface IServerQuery
3444 /// <returns>Serialized ServerInfo instance.</returns>
3545 ServerInfo GetServerInfo ( ) ;
3646
47+ /// <summary>
48+ /// Requests and serializes the server information.
49+ /// </summary>
50+ /// <returns>Serialized ServerInfo instance.</returns>
51+ Task < ServerInfo > GetServerInfoAsync ( ) ;
52+
3753 /// <summary>
3854 /// Requests and serializes the list of player information.
3955 /// </summary>
4056 /// <returns>Serialized list of Player instances.</returns>
4157 List < Player > GetPlayers ( ) ;
4258
59+ /// <summary>
60+ /// Requests and serializes the list of player information.
61+ /// </summary>
62+ /// <returns>Serialized list of Player instances.</returns>
63+ Task < List < Player > > GetPlayersAsync ( ) ;
64+
4365 /// <summary>
4466 /// Requests and serializes the list of rules defined by the server.
4567 /// Warning: CS:GO Rules reply is broken since update CSGO 1.32.3.0 (Feb 21, 2014).
4668 /// Before the update rules got truncated when exceeding MTU, after the update rules reply is not sent at all.
4769 /// </summary>
4870 /// <returns>Serialized list of Rule instances.</returns>
4971 List < Rule > GetRules ( ) ;
72+
73+ /// <summary>
74+ /// Requests and serializes the list of rules defined by the server.
75+ /// Warning: CS:GO Rules reply is broken since update CSGO 1.32.3.0 (Feb 21, 2014).
76+ /// Before the update rules got truncated when exceeding MTU, after the update rules reply is not sent at all.
77+ /// </summary>
78+ /// <returns>Serialized list of Rule instances.</returns>
79+ Task < List < Rule > > GetRulesAsync ( ) ;
5080 }
5181
5282 public class ServerQuery : IServerQuery , IDisposable
@@ -104,15 +134,15 @@ public IServerQuery Connect(string serverAddress, int port)
104134 }
105135
106136 /// <inheritdoc/>
107- public ServerInfo GetServerInfo ( )
137+ public async Task < ServerInfo > GetServerInfoAsync ( )
108138 {
109139 const string requestPayload = "Source Engine Query\0 " ;
110140 var sInfo = new ServerInfo
111141 {
112142 Ping = new Ping ( ) . Send ( _ipEndpoint . Address ) . RoundtripTime
113143 } ;
114144
115- byte [ ] response = SendRequest ( RequestHeaders . A2S_INFO , Encoding . UTF8 . GetBytes ( requestPayload ) ) ;
145+ byte [ ] response = await SendRequestAsync ( RequestHeaders . A2S_INFO , Encoding . UTF8 . GetBytes ( requestPayload ) ) ;
116146 if ( response . Length > 0 )
117147 {
118148 ExtractData ( sInfo , response , nameof ( sInfo . EDF ) , true ) ;
@@ -122,24 +152,38 @@ public ServerInfo GetServerInfo()
122152 }
123153
124154 /// <inheritdoc/>
125- public void RenewChallenge ( )
155+ public ServerInfo GetServerInfo ( )
156+ {
157+ return RunSync ( GetServerInfoAsync ) ;
158+ }
159+
160+ /// <inheritdoc/>
161+ public async Task < int > RenewChallengeAsync ( )
126162 {
127- byte [ ] response = SendRequest ( RequestHeaders . A2S_PLAYER , BitConverter . GetBytes ( - 1 ) ) ;
163+ byte [ ] response = await SendRequestAsync ( RequestHeaders . A2S_PLAYER , BitConverter . GetBytes ( - 1 ) ) ;
128164 if ( response . Length > 0 )
129165 {
130166 _currentChallenge = BitConverter . ToInt32 ( response . Skip ( RESPONSE_CODE_INDEX ) . Take ( sizeof ( int ) ) . ToArray ( ) , 0 ) ;
131167 }
168+
169+ return _currentChallenge ;
132170 }
133171
134172 /// <inheritdoc/>
135- public List < Player > GetPlayers ( )
173+ public int RenewChallenge ( )
174+ {
175+ return RunSync ( RenewChallengeAsync ) ;
176+ }
177+
178+ /// <inheritdoc/>
179+ public async Task < List < Player > > GetPlayersAsync ( )
136180 {
137181 if ( _currentChallenge == 0 )
138182 {
139- RenewChallenge ( ) ;
183+ await RenewChallengeAsync ( ) ;
140184 }
141185
142- byte [ ] response = SendRequest ( RequestHeaders . A2S_PLAYER , BitConverter . GetBytes ( _currentChallenge ) ) ;
186+ byte [ ] response = await SendRequestAsync ( RequestHeaders . A2S_PLAYER , BitConverter . GetBytes ( _currentChallenge ) ) ;
143187 if ( response . Length > 0 )
144188 {
145189 return ExtractListData < Player > ( response ) ;
@@ -151,14 +195,20 @@ public List<Player> GetPlayers()
151195 }
152196
153197 /// <inheritdoc/>
154- public List < Rule > GetRules ( )
198+ public List < Player > GetPlayers ( )
199+ {
200+ return RunSync ( GetPlayersAsync ) ;
201+ }
202+
203+ /// <inheritdoc/>
204+ public async Task < List < Rule > > GetRulesAsync ( )
155205 {
156206 if ( _currentChallenge == 0 )
157207 {
158- RenewChallenge ( ) ;
208+ await RenewChallengeAsync ( ) ;
159209 }
160210
161- byte [ ] response = SendRequest ( RequestHeaders . A2S_RULES , BitConverter . GetBytes ( _currentChallenge ) ) ;
211+ byte [ ] response = await SendRequestAsync ( RequestHeaders . A2S_RULES , BitConverter . GetBytes ( _currentChallenge ) ) ;
162212 if ( response . Length > 0 )
163213 {
164214 var rls = ExtractListData < Rule > ( response ) ;
@@ -170,6 +220,12 @@ public List<Rule> GetRules()
170220 }
171221 }
172222
223+ /// <inheritdoc/>
224+ public List < Rule > GetRules ( )
225+ {
226+ return RunSync ( GetRulesAsync ) ;
227+ }
228+
173229 /// <summary>
174230 /// Disposes the object and its disposables.
175231 /// </summary>
@@ -247,11 +303,12 @@ private List<TObject> ExtractListData<TObject>(byte[] rawSource)
247303 return objectList ;
248304 }
249305
250- private byte [ ] SendRequest ( byte requestHeader , byte [ ] payload = null )
306+ private async Task < byte [ ] > SendRequestAsync ( byte requestHeader , byte [ ] payload = null )
251307 {
252308 var request = BuildRequest ( requestHeader , payload ) ;
253- _client . Send ( request , request . Length ) ;
254- return _client . Receive ( ref _ipEndpoint ) ;
309+ await _client . SendAsync ( request , request . Length ) ;
310+ UdpReceiveResult result = await _client . ReceiveAsync ( ) ;
311+ return result . Buffer ;
255312 }
256313
257314 private byte [ ] BuildRequest ( byte headerCode , byte [ ] extraParams = null )
@@ -261,7 +318,9 @@ private byte[] BuildRequest(byte headerCode, byte[] extraParams = null)
261318 var request = new byte [ ] { 0xFF , 0xFF , 0xFF , 0xFF , headerCode } ;
262319
263320 // If we have any extra payload, concatenate those into our requestHeaders and return;
264- return extraParams != null ? request . Concat ( extraParams ) . ToArray ( ) : request ;
321+ return extraParams != null
322+ ? request . Concat ( extraParams ) . ToArray ( )
323+ : request ;
265324 }
266325
267326 private IEnumerable < byte > ExtractData < TObject > ( TObject objectRef , byte [ ] dataSource , string edfPropName = "" , bool stripHeaders = false )
@@ -270,7 +329,9 @@ private IEnumerable<byte> ExtractData<TObject>(TObject objectRef, byte[] dataSou
270329 IEnumerable < byte > takenBytes = new List < byte > ( ) ;
271330
272331 // We can be a good guy and ask for any extra jobs :)
273- IEnumerable < byte > enumerableSource = stripHeaders ? dataSource . Skip ( RESPONSE_HEADER_COUNT ) : dataSource ;
332+ IEnumerable < byte > enumerableSource = stripHeaders
333+ ? dataSource . Skip ( RESPONSE_HEADER_COUNT )
334+ : dataSource ;
274335
275336 // We get every property that does not contain ParseCustom and NotParsable attributes on them to iterate through all and parse/assign their values.
276337 IEnumerable < PropertyInfo > propsOfObject = typeof ( TObject ) . GetProperties ( )
@@ -320,14 +381,18 @@ private IEnumerable<byte> ExtractData<TObject>(TObject objectRef, byte[] dataSou
320381 else
321382 {
322383 // Is the property an Enum ? if yes we should be getting the underlying type since it might differ.
323- Type typeOfProperty = property . PropertyType . IsEnum ? property . PropertyType . GetEnumUnderlyingType ( ) : property . PropertyType ;
384+ Type typeOfProperty = property . PropertyType . IsEnum
385+ ? property . PropertyType . GetEnumUnderlyingType ( )
386+ : property . PropertyType ;
324387
325388 // Extract the value and the size from the source.
326389 ( object result , int size ) = ExtractMarshalType ( enumerableSource , typeOfProperty ) ;
327390
328391 /* If the property is an enum we should parse it first then assign its value,
329392 * if not we can just give it to SetValue since it was converted by ExtractMarshalType already.*/
330- property . SetValue ( objectRef , property . PropertyType . IsEnum ? Enum . Parse ( property . PropertyType , result . ToString ( ) ) : result ) ;
393+ property . SetValue ( objectRef , property . PropertyType . IsEnum
394+ ? Enum . Parse ( property . PropertyType , result . ToString ( ) )
395+ : result ) ;
331396
332397 // Update the source by skipping the amount of bytes taken from the source.
333398 enumerableSource = enumerableSource . Skip ( size ) ;
@@ -338,6 +403,18 @@ private IEnumerable<byte> ExtractData<TObject>(TObject objectRef, byte[] dataSou
338403 return enumerableSource ;
339404 }
340405
406+ private TResult RunSync < TResult > ( Func < Task < TResult > > func )
407+ {
408+ var cultureUi = CultureInfo . CurrentUICulture ;
409+ var culture = CultureInfo . CurrentCulture ;
410+ return new TaskFactory ( ) . StartNew ( ( ) =>
411+ {
412+ Thread . CurrentThread . CurrentCulture = culture ;
413+ Thread . CurrentThread . CurrentUICulture = cultureUi ;
414+ return func ( ) ;
415+ } ) . Unwrap ( ) . GetAwaiter ( ) . GetResult ( ) ;
416+ }
417+
341418 private ( object , int ) ExtractMarshalType ( IEnumerable < byte > source , Type type )
342419 {
343420 // Get the size of the given type.
0 commit comments