Project

General

Profile

Download (13.7 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / scores / RubikScoresDownloader.java @ 4895fff6

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.scores;
21

    
22
import android.support.v4.app.FragmentActivity;
23

    
24
import org.distorted.magic.R;
25
import org.distorted.object.RubikObjectList;
26

    
27
import java.io.InputStream;
28
import java.net.HttpURLConnection;
29
import java.net.URL;
30
import java.net.UnknownHostException;
31

    
32
import static org.distorted.uistate.RubikStatePlay.MAX_SCRAMBLE;
33

    
34
///////////////////////////////////////////////////////////////////////////////////////////////////
35

    
36
public class RubikScoresDownloader implements Runnable
37
  {
38
  public interface Receiver
39
    {
40
    void receive(String[][][] country, String[][][] name, float[][][] time);
41
    void message(String mess);
42
    }
43

    
44
  public static final int MAX_PLACES = 10;
45

    
46
  private static final int DOWNLOAD   = 0;
47
  private static final int SUBMIT     = 1;
48
  private static final int IDLE       = 2;
49

    
50
  private final String[] hex = {
51
    "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
52
    "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
53
    "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
54
    "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
55
    "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
56
    "%28", "%29", "%2a", "%2b", "%2c", "%2d", "%2e", "%2f",
57
    "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37",
58
    "%38", "%39", "%3a", "%3b", "%3c", "%3d", "%3e", "%3f",
59
    "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47",
60
    "%48", "%49", "%4a", "%4b", "%4c", "%4d", "%4e", "%4f",
61
    "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57",
62
    "%58", "%59", "%5a", "%5b", "%5c", "%5d", "%5e", "%5f",
63
    "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67",
64
    "%68", "%69", "%6a", "%6b", "%6c", "%6d", "%6e", "%6f",
65
    "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77",
66
    "%78", "%79", "%7a", "%7b", "%7c", "%7d", "%7e", "%7f",
67
    "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
68
    "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
69
    "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
70
    "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
71
    "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
72
    "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
73
    "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
74
    "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
75
    "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
76
    "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
77
    "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
78
    "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
79
    "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
80
    "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
81
    "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
82
    "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
83
    };
84

    
85
  private static boolean mRunning = false;
86
  private static int mMode = IDLE;
87
  private static Receiver mReceiver;
88
  private static String mVersion;
89

    
90
  private static int mTotal = RubikObjectList.getTotal();
91
  private static String mScores = "";
92
  private static String[][][] mCountry = new String[mTotal][MAX_SCRAMBLE][MAX_PLACES];
93
  private static String[][][] mName    = new String[mTotal][MAX_SCRAMBLE][MAX_PLACES];
94
  private static  float[][][] mTime    = new  float[mTotal][MAX_SCRAMBLE][MAX_PLACES];
95

    
96
  private static int[][] mPlaces = new int[mTotal][MAX_SCRAMBLE];
97
  private static RubikScoresDownloader mThis;
98

    
99
///////////////////////////////////////////////////////////////////////////////////////////////////
100
// TODO
101

    
102
  private static int computeHash(String name, String veri, String country, String lvllist,
103
                                 String objlist, String timelist, int runnings)
104
    {
105
    return 0;
106
    }
107

    
108
///////////////////////////////////////////////////////////////////////////////////////////////////
109
// TODO: handle the fact that the server might return 'error' (3) or 'name taken' (2)
110

    
111
  private void fillSubmittedValues()
112
    {
113
    fillDownloadedValues();
114
    }
115

    
116
///////////////////////////////////////////////////////////////////////////////////////////////////
117

    
118
  private void fillDownloadedValues()
119
    {
120
    int begin=-1 ,end, len = mScores.length();
121

    
122
    for(int i=0; i<mTotal; i++)
123
      for(int j=0; j<MAX_SCRAMBLE; j++)
124
        {
125
        mPlaces[i][j] = 0;
126
        }
127

    
128
    while( begin<len )
129
      {
130
      end = mScores.indexOf('\n', begin+1);
131
      if( end<0 ) end = len;
132

    
133
      try
134
        {
135
        fillRow(mScores.substring(begin+1,end));
136
        }
137
      catch(Exception ex)
138
        {
139
        // faulty row - ignore
140
        }
141

    
142
      begin = end;
143
      }
144
    }
145

    
146
///////////////////////////////////////////////////////////////////////////////////////////////////
147

    
148
  private void fillRow(String row)
149
    {
150
    int s1 = row.indexOf(' ');
151
    int s2 = row.indexOf(' ',s1+1);
152
    int s3 = row.indexOf(' ',s2+1);
153
    int s4 = row.indexOf(' ',s3+1);
154
    int s5 = row.length();
155

    
156
    if( s5>s4 && s4>s3 && s3>s2 && s2>s1 && s1>0 )
157
      {
158
      int object = RubikObjectList.unpackObjectFromString( row.substring(0,s1) );
159

    
160
      if( object>=0 && object<mTotal )
161
        {
162
        int level      = Integer.parseInt( row.substring(s1+1,s2) );
163
        String name    = row.substring(s2+1, s3);
164
        int time       = Integer.parseInt( row.substring(s3+1,s4) );
165
        String country = row.substring(s4+1, s5);
166

    
167
        if(level>0 && level<=MAX_SCRAMBLE)
168
          {
169
          int p = mPlaces[object][level];
170
          mPlaces[object][level]++;
171

    
172
          mCountry[object][level-1][p] = country;
173
          mName   [object][level-1][p] = name;
174
          mTime   [object][level-1][p] = ((float)(time/100))/10.0f;
175
          }
176
        }
177
      }
178
    }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

    
182
  private String URLencode(String s)
183
    {
184
    StringBuilder sbuf = new StringBuilder();
185
    int len = s.length();
186

    
187
    for (int i = 0; i < len; i++)
188
      {
189
      int ch = s.charAt(i);
190

    
191
           if ('A' <= ch && ch <= 'Z') sbuf.append((char)ch);
192
      else if ('a' <= ch && ch <= 'z') sbuf.append((char)ch);
193
      else if ('0' <= ch && ch <= '9') sbuf.append((char)ch);
194
      else if (ch == ' '             ) sbuf.append('+');
195
      else if (ch == '-' || ch == '_'
196
            || ch == '.' || ch == '!'
197
            || ch == '~' || ch == '*'
198
            || ch == '\'' || ch == '('
199
            || ch == ')'             ) sbuf.append((char)ch);
200
      else if (ch <= 0x007f)           sbuf.append(hex[ch]);
201
      else if (ch <= 0x07FF)
202
        {
203
        sbuf.append(hex[0xc0 | (ch >> 6)]);
204
        sbuf.append(hex[0x80 | (ch & 0x3F)]);
205
        }
206
      else
207
        {
208
        sbuf.append(hex[0xe0 | (ch >> 12)]);
209
        sbuf.append(hex[0x80 | ((ch >> 6) & 0x3F)]);
210
        sbuf.append(hex[0x80 | (ch & 0x3F)]);
211
        }
212
      }
213

    
214
    return sbuf.toString();
215
    }
216

    
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218

    
219
  private boolean network(String url)
220
    {
221
    android.util.Log.e("down", "url: "+url);
222

    
223
    try
224
      {
225
      java.net.URL connectURL = new URL(url);
226
      HttpURLConnection conn = (HttpURLConnection)connectURL.openConnection();
227

    
228
      conn.setDoInput(true);
229
      conn.setDoOutput(true);
230
      conn.setUseCaches(false);
231
      conn.setRequestMethod("GET");
232
      conn.connect();
233
      conn.getOutputStream().flush();
234

    
235
      try( InputStream is = conn.getInputStream() )
236
        {
237
        int ch;
238
        StringBuilder sb = new StringBuilder();
239
        while( ( ch = is.read() ) != -1 )
240
          {
241
          sb.append( (char)ch );
242
          }
243
        mScores = sb.toString();
244
        }
245
      catch( final Exception e)
246
        {
247
        mReceiver.message("Failed to get an answer from the High Scores server");
248
        return false;
249
        }
250
      }
251
    catch( final UnknownHostException e )
252
      {
253
      mReceiver.message("No access to Internet");
254
      return false;
255
      }
256
    catch( final SecurityException e )
257
      {
258
      mReceiver.message("Application not authorized to connect to the Internet");
259
      return false;
260
      }
261
    catch( final Exception e )
262
      {
263
      mReceiver.message(e.getMessage());
264
      return false;
265
      }
266

    
267
    if( mScores.length()==0 )
268
      {
269
      mReceiver.message("Failed to download scores");
270
      return false;
271
      }
272

    
273
    return true;
274
    }
275

    
276
///////////////////////////////////////////////////////////////////////////////////////////////////
277

    
278
  private boolean gottaDownload()
279
    {
280
    return ((mScores.length()==0) && !mRunning);
281
    }
282

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284

    
285
  @Override
286
  public void run()
287
    {
288
    boolean success=true;
289

    
290
    if( mMode==DOWNLOAD && gottaDownload() )
291
      {
292
      mRunning = true;
293

    
294
      try
295
        {
296
        RubikScores scores = RubikScores.getInstance();
297
        String name = URLencode(scores.getName());
298
        String veri = scores.isVerified() ? name : "";
299
        int numRuns = scores.getNumRuns();
300
        int numPlay = scores.getNumPlays();
301

    
302
        String url="https://distorted.org/magic/cgi-bin/download.cgi";
303
        url += "?n="+name+"&v="+veri+"&r="+numRuns+"&p="+numPlay+"&e="+mVersion+"d";
304
        url += "&o="+RubikObjectList.getObjectList()+"&min=0&max="+MAX_SCRAMBLE+"&l="+MAX_PLACES;
305

    
306
        success = network(url);
307
        }
308
      catch( Exception e )
309
        {
310
        mReceiver.message("Exception downloading records: "+e.getMessage() );
311
        }
312

    
313
      fillDownloadedValues();
314
      }
315

    
316
    if( mMode==SUBMIT )
317
      {
318
      mRunning = true;
319

    
320
      try
321
        {
322
        RubikScores scores = RubikScores.getInstance();
323
        String name = URLencode(scores.getName());
324
        String veri = scores.isVerified() ? name : "";
325
        int numRuns = scores.getNumRuns();
326
        int numPlay = scores.getNumPlays();
327
        int deviceID= scores.getDeviceID();
328
        String objlist = scores.getUnsubmittedObjlist();
329
        String lvllist = scores.getUnsubmittedLevellist();
330
        String timlist = scores.getUnsubmittedTimelist();
331
        String country = scores.getCountry();
332
        int hash = computeHash(name,veri,country,lvllist,objlist,timlist,numRuns);
333

    
334
        String url="https://distorted.org/magic/cgi-bin/submit.cgi";
335
        url += "?n="+name+"&v="+veri+"&r="+numRuns+"&p="+numPlay+"&i="+deviceID+"&e="+mVersion+"d";
336
        url += "&o="+objlist+"&l="+lvllist+"&t="+timlist+"&c="+country+"&h="+hash;
337
        url += "&oo="+RubikObjectList.getObjectList()+"&min=0&max="+MAX_SCRAMBLE+"&lo="+MAX_PLACES;
338

    
339
        success = network(url);
340
        }
341
      catch( Exception e )
342
        {
343
        mReceiver.message("Exception submitting records: "+e.getMessage() );
344
        }
345

    
346
      fillSubmittedValues();
347
      }
348

    
349
    mRunning = false;
350

    
351
    if( success )
352
      {
353
      mReceiver.receive(mCountry, mName, mTime);
354
      }
355
    }
356

    
357
///////////////////////////////////////////////////////////////////////////////////////////////////
358

    
359
  private RubikScoresDownloader()
360
    {
361

    
362
    }
363

    
364
///////////////////////////////////////////////////////////////////////////////////////////////////
365
// PUBLIC API
366
///////////////////////////////////////////////////////////////////////////////////////////////////
367

    
368
  public static void onPause()
369
    {
370
    mRunning = false;
371
    }
372

    
373
///////////////////////////////////////////////////////////////////////////////////////////////////
374

    
375
  public static RubikScoresDownloader getInstance()
376
    {
377
    if( mThis==null )
378
      {
379
      mThis = new RubikScoresDownloader();
380
      }
381

    
382
    return mThis;
383
    }
384

    
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386

    
387
  public void download(Receiver receiver, FragmentActivity act)
388
    {
389
    mReceiver = receiver;
390
    mMode     = DOWNLOAD;
391
    mVersion  = act.getString(R.string.app_version);
392

    
393
    Thread networkThrd = new Thread(this);
394
    networkThrd.start();
395
    }
396

    
397
///////////////////////////////////////////////////////////////////////////////////////////////////
398

    
399
  public void submit(Receiver receiver, FragmentActivity act)
400
    {
401
    mReceiver = receiver;
402
    mMode     = SUBMIT;
403
    mVersion  = act.getString(R.string.app_version);
404

    
405
    Thread networkThrd = new Thread(this);
406
    networkThrd.start();
407
    }
408
}
(2-2/2)