22
33import java .sql .*;
44import java .util .*;
5+
6+ import bms .player .beatoraja .select .QueryScoreContext ;
7+ import bms .player .beatoraja .song .SongData ;
58import org .slf4j .Logger ;
69import org .slf4j .LoggerFactory ;
710
811import org .apache .commons .dbutils .QueryRunner ;
912import org .apache .commons .dbutils .ResultSetHandler ;
1013import org .apache .commons .dbutils .handlers .BeanListHandler ;
1114import org .sqlite .SQLiteConfig ;
12- import org .sqlite .SQLiteDataSource ;
1315import org .sqlite .SQLiteConfig .SynchronousMode ;
16+ import org .sqlite .SQLiteDataSource ;
17+
18+ import javax .management .Query ;
19+ import java .sql .Connection ;
20+ import java .sql .SQLException ;
21+ import java .util .List ;
1422
1523/**
1624 * スコアデータログデータベースアクセサ
@@ -24,9 +32,10 @@ public class ScoreDataLogDatabaseAccessor extends SQLiteDatabaseAccessor {
2432 private final ResultSetHandler <List <ScoreData >> scoreHandler = new BeanListHandler <ScoreData >(ScoreData .class );
2533
2634 private final QueryRunner qr ;
35+ private static final int LOAD_CHUNK_SIZE = 1000 ;
2736
2837 public ScoreDataLogDatabaseAccessor (String path ) throws ClassNotFoundException {
29- super ( new Table ("scoredatalog" ,
38+ super ( new Table ("scoredatalog" ,
3039 new Column ("sha256" , "TEXT" , 1 , 1 ),
3140 new Column ("mode" , "INTEGER" ,0 ,1 ),
3241 new Column ("clear" , "INTEGER" ),
@@ -56,7 +65,40 @@ public ScoreDataLogDatabaseAccessor(String path) throws ClassNotFoundException {
5665 new Column ("date" , "INTEGER" ),
5766 new Column ("state" , "INTEGER" ),
5867 new Column ("scorehash" , "TEXT" )
59- ));
68+ ),
69+ new Table ( "eddatalog" ,
70+ new Column ("sha256" , "TEXT" , 1 , 0 ),
71+ new Column ("mode" , "INTEGER" ),
72+ new Column ("clear" , "INTEGER" ),
73+ new Column ("epg" , "INTEGER" ),
74+ new Column ("lpg" , "INTEGER" ),
75+ new Column ("egr" , "INTEGER" ),
76+ new Column ("lgr" , "INTEGER" ),
77+ new Column ("egd" , "INTEGER" ),
78+ new Column ("lgd" , "INTEGER" ),
79+ new Column ("ebd" , "INTEGER" ),
80+ new Column ("lbd" , "INTEGER" ),
81+ new Column ("epr" , "INTEGER" ),
82+ new Column ("lpr" , "INTEGER" ),
83+ new Column ("ems" , "INTEGER" ),
84+ new Column ("lms" , "INTEGER" ),
85+ new Column ("notes" , "INTEGER" ),
86+ new Column ("combo" , "INTEGER" ),
87+ new Column ("minbp" , "INTEGER" ),
88+ new Column ("avgjudge" , "INTEGER" , 1 , 0 , String .valueOf (Integer .MAX_VALUE )),
89+ new Column ("playcount" , "INTEGER" ),
90+ new Column ("clearcount" , "INTEGER" ),
91+ new Column ("trophy" , "TEXT" ),
92+ new Column ("ghost" , "TEXT" ),
93+ new Column ("option" , "INTEGER" ),
94+ new Column ("seed" , "INTEGER" ),
95+ new Column ("random" , "INTEGER" ),
96+ new Column ("date" , "INTEGER" ),
97+ new Column ("state" , "INTEGER" ),
98+ new Column ("scorehash" , "TEXT" ),
99+ new Column ("rate" , "INTEGER" ),
100+ new Column ("overridejudge" , "INTEGER" )
101+ ));
60102
61103 Class .forName ("org.sqlite.JDBC" );
62104 SQLiteConfig conf = new SQLiteConfig ();
@@ -83,10 +125,129 @@ public void setScoreDataLog(ScoreData[] scores) {
83125 con .setAutoCommit (false );
84126 for (ScoreData score : scores ) {
85127 this .insert (qr , con , "scoredatalog" , score );
128+ this .insert (qr , con , "eddatalog" , score );
86129 }
87130 con .commit ();
88131 } catch (Exception e ) {
89- logger .error ("スコア更新時の例外:" + e .getMessage ());
132+ logger .error ("スコア更新時の例外:{}" , e .getMessage ());
133+ }
134+ }
135+
136+ public List <ScoreData > getScoreDataLog (String sha256 ) {
137+ List <ScoreData > result = null ;
138+ try {
139+ // TODO: One day we shall use prepared statement instead
140+ result = Validatable .removeInvalidElements (qr .query (String .format ("SELECT * FROM eddatalog WHERE sha256 = '%s'" , sha256 ), scoreHandler ));
141+ } catch (Exception e ) {
142+ logger .error ("Failed to query table eddatalog: {}" , e .getMessage ());
143+ }
144+ return result ;
145+ }
146+
147+ public List <ScoreData > getScoreDataLog (String sha256 , QueryScoreContext ctx ) {
148+ List <ScoreData > result = null ;
149+ try (Connection con = qr .getDataSource ().getConnection ()) {
150+ PreparedStatement ps = con .prepareStatement ("SELECT * FROM eddatalog WHERE sha256 = ? AND rate = ? AND overridejudge = ?" );
151+ ps .setString (1 , sha256 );
152+ ps .setInt (2 , ctx .freqValue () != null ? ctx .freqValue () : 0 );
153+ ps .setInt (3 , ctx .overrideJudge () != null ? ctx .overrideJudge () : -1 );
154+ result = Validatable .removeInvalidElements (scoreHandler .handle (ps .executeQuery ()));
155+ } catch (Exception e ) {
156+ logger .error ("Failed to query table eddatalog: {}" , e .getMessage ());
157+ }
158+ return result ;
159+ }
160+
161+ /**
162+ * TODO: Maybe we should make the definition of "best" programmable?
163+ */
164+ public ScoreData getBestScoreDataLog (String sha256 , QueryScoreContext ctx ) {
165+ List <ScoreData > rawLogs = getScoreDataLog (sha256 , ctx );
166+ if (rawLogs .isEmpty ()) {
167+ return null ;
168+ }
169+ ScoreData result = rawLogs .get (0 );
170+ for (ScoreData score : rawLogs ) {
171+ if (score .getClear () > result .getClear ()) {
172+ result = score ;
173+ } else if (score .getClear () == result .getClear () && score .getExscore () > result .getExscore ()) {
174+ result = score ;
175+ }
176+ }
177+ return result ;
178+ }
179+
180+ public void getBestScoreDataLogs (ScoreDatabaseAccessor .ScoreDataCollector collector , SongData [] songs , QueryScoreContext ctx ) {
181+ StringBuilder str = new StringBuilder (songs .length * 68 );
182+ getBestScoreDataLogs (collector , songs , ctx .lnMode (), str , true , ctx );
183+ str .setLength (0 );
184+ getBestScoreDataLogs (collector , songs , 0 , str , false , ctx );
185+ }
186+
187+ public void getBestScoreDataLogs (ScoreDatabaseAccessor .ScoreDataCollector collector , SongData [] songs , int mode , StringBuilder str , boolean hasLN , QueryScoreContext ctx ) {
188+ try (Connection con = qr .getDataSource ().getConnection ()) {
189+ int songLength = songs .length ;
190+ int chunkLength = (songLength + LOAD_CHUNK_SIZE - 1 ) / LOAD_CHUNK_SIZE ;
191+ List <ScoreData > scores = new ArrayList <>();
192+ for (int i = 0 ; i < chunkLength ;++i ) {
193+ // [i * CHUNK_SIZE, min(length, (i + 1) * CHUNK_SIZE)
194+ final int chunkStart = i * LOAD_CHUNK_SIZE ;
195+ final int chunkEnd = Math .min (songLength , (i + 1 ) * LOAD_CHUNK_SIZE );
196+ for (int j = chunkStart ; j < chunkEnd ; ++j ) {
197+ SongData song = songs [j ];
198+ if ((hasLN && song .hasUndefinedLongNote ()) || (!hasLN && !song .hasUndefinedLongNote ())) {
199+ if (str .length () > 0 ) {
200+ str .append (',' );
201+ }
202+ str .append ('\'' ).append (song .getSha256 ()).append ('\'' );
203+ }
204+ }
205+
206+ PreparedStatement ps = con .prepareStatement ("SELECT * FROM eddatalog WHERE sha256 in (?) AND mode = ? AND rate = ? AND overridejudge = ?" );
207+ ps .setString (1 , str .toString ());
208+ ps .setInt (2 , mode );
209+ ps .setInt (3 , ctx .freqValue () != null ? ctx .freqValue () : 0 );
210+ ps .setInt (4 , ctx .overrideJudge () != null ? ctx .overrideJudge () : -1 );
211+
212+ List <ScoreData > subScores = Validatable .removeInvalidElements (scoreHandler .handle (ps .executeQuery ()));
213+ str .setLength (0 );
214+ scores .addAll (subScores );
215+ }
216+ scores .sort (Comparator .comparing (ScoreData ::getSha256 ));
217+ // For every chart, we calculate it's best score
218+ List <ScoreData > bestScores = new ArrayList <>();
219+ for (int i = 0 ; i <scores .size (); ++i ) {
220+ int j = i ;
221+ ScoreData bestScore = scores .get (i );
222+ while (j + 1 < scores .size () && scores .get (j + 1 ).getSha256 ().equals (bestScore .getSha256 ())) {
223+ ScoreData next = scores .get (j + 1 );
224+ if (next .getClear () > bestScore .getClear ()) {
225+ bestScore = next ;
226+ } else if (next .getClear () == bestScore .getClear () && next .getExscore () > bestScore .getExscore ()) {
227+ bestScore = next ;
228+ }
229+ j ++;
230+ }
231+ bestScores .add (bestScore );
232+ i = j ;
233+ }
234+ for (SongData song : songs ) {
235+ if ((hasLN && song .hasUndefinedLongNote ()) || (!hasLN && !song .hasUndefinedLongNote ())) {
236+ boolean b = true ;
237+ for (ScoreData score : bestScores ) {
238+ if (song .getSha256 ().equals (score .getSha256 ())) {
239+ collector .collect (song , score );
240+ b = false ;
241+ break ;
242+ }
243+ }
244+ if (b ) {
245+ collector .collect (song , null );
246+ }
247+ }
248+ }
249+ } catch (Exception e ) {
250+ logger .error ("Failed to query table eddatalog: {}" , e .getMessage ());
90251 }
91252 }
92253}
0 commit comments