Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions core/src/bms/player/beatoraja/PlayDataAccessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.util.*;

import bms.player.beatoraja.select.QueryScoreContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.stream.Stream;
Expand All @@ -21,6 +23,7 @@
import bms.player.beatoraja.ScoreDatabaseAccessor.ScoreDataCollector;
import bms.player.beatoraja.ScoreLogDatabaseAccessor.ScoreLog;
import bms.player.beatoraja.ir.LR2IRConnection;
import bms.player.beatoraja.modmenu.SongManagerMenu;
import bms.player.beatoraja.song.SongData;

import com.badlogic.gdx.utils.Json;
Expand Down Expand Up @@ -171,6 +174,13 @@ public ScoreData readScoreData(String hash, boolean ln, int lnmode) {
return scoredb.getScoreData(hash, ln ? lnmode : 0);
}

public ScoreData readScoreData(String hash, boolean ln, QueryScoreContext ctx) {
if (!ctx.isQueryModdedScore()) {
return readScoreData(hash, ln, ctx.lnMode());
}
return scoredatalogdb.getBestScoreDataLog(hash, ctx);
}

/**
* スコアデータをまとめて読み込み、collectorに渡す
* @param collector スコアデータのcollector
Expand All @@ -181,6 +191,14 @@ public void readScoreDatas(ScoreDataCollector collector, SongData[] songs, int l
scoredb.getScoreDatas(collector, songs, lnmode);
}

public void readScoreDatas(ScoreDataCollector collector, SongData[] songs, QueryScoreContext ctx) {
if (!ctx.isQueryModdedScore()) {
readScoreDatas(collector, songs, ctx.lnMode());
}
scoredatalogdb.getBestScoreDataLogs(collector, songs, ctx);
}


public List<ScoreData> readScoreDatas(String sql) {
return scoredb.getScoreDatas(sql);
}
Expand Down Expand Up @@ -285,6 +303,7 @@ public void writeScoreData(ScoreData newscore, BMSModel model, int lnmode, boole
newscore.setClearcount(score.getClearcount());
newscore.setScorehash(getScoreHash(newscore));

SongManagerMenu.invalidCache(newscore.getSha256());
scoredatalogdb.setScoreDataLog(newscore);
}

Expand Down Expand Up @@ -359,6 +378,16 @@ public ScoreData readScoreData(String[] hashes, boolean ln, int lnmode, int opti
return readScoreData(hash, ln, lnmode, option, constraint);
}

/**
* Load one specific chart's play history
*
* @param hash chart's hash
* @return play records
*/
public List<ScoreData> readScoreDataLog(String hash) {
return scoredatalogdb.getScoreDataLog(hash);
}

/**
* コーススコアデータを書き込む
*/
Expand Down Expand Up @@ -432,6 +461,19 @@ public void writeScoreData(ScoreData newscore, BMSModel[] models, int lnmode, in
log.setDate(score.getDate());
scorelogdb.setScoreLog(log);
}
if (log.getSha256() != null && scoredatalogdb != null) {
// TODO: I don't know why newscore doesn't have course's sha256 here, pls kill me
newscore.setSha256(log.getSha256());
newscore.setTrophy("");
newscore.setMode(score.getMode());
newscore.setDate(score.getDate());
newscore.setPlaycount(score.getPlaycount());
newscore.setClearcount(score.getClearcount());
newscore.setScorehash(getScoreHash(newscore));

SongManagerMenu.invalidCache(newscore.getSha256());
scoredatalogdb.setScoreDataLog(newscore);
}

logger.info("スコアデータベース更新完了 ");

Expand Down
22 changes: 20 additions & 2 deletions core/src/bms/player/beatoraja/PlayerResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
*/
public final class PlayerResource {
private static final Logger logger = LoggerFactory.getLogger(PlayerResource.class);

/**
* 選曲中のBMS
*/
Expand Down Expand Up @@ -132,8 +132,10 @@ public final class PlayerResource {
private String tablelevel = "";
private String tablefull;
private boolean freqOn;
private int freqValue;
private String freqString;
private boolean forceNoIRSend;
private int overrideJudgeRank;
// Full list of difficult tables that contains current song
private List<String> reverseLookup = new ArrayList<>();

Expand Down Expand Up @@ -672,4 +674,20 @@ public void setForceNoIRSend(boolean forceNoIRSend) {
public Future<BMSLoudnessAnalyzer.AnalysisResult> getAnalysisTask() {
return analysisTask;
}
}

public int getFreqValue() {
return freqValue;
}

public void setFreqValue(int freqValue) {
this.freqValue = freqValue;
}

public int getOverrideJudgeRank() {
return overrideJudgeRank;
}

public void setOverrideJudgeRank(int overrideJudgeRank) {
this.overrideJudgeRank = overrideJudgeRank;
}
}
20 changes: 20 additions & 0 deletions core/src/bms/player/beatoraja/ScoreData.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ public class ScoreData implements Validatable {
* プレイゲージ
*/
private int gauge;
/**
* Rate percentage, e.g. 120 == 1.2x
*/
private int rate;
/**
* Override judgement, -1 as didn't use
*/
private int overridejudge = -1;
/**
* 入力デバイス
*/
Expand Down Expand Up @@ -236,6 +244,18 @@ public int getLms() {
public void setLms(int lms) {
this.lms = lms;
}
public int getRate() {
return rate;
}
public void setRate(int rate) {
this.rate = rate;
}
public int getOverridejudge() {
return overridejudge;
}
public void setOverridejudge(int overridejudge) {
this.overridejudge = overridejudge;
}

public int getJudgeCount(int judge) {
return getJudgeCount(judge, true) + getJudgeCount(judge, false);
Expand Down
169 changes: 165 additions & 4 deletions core/src/bms/player/beatoraja/ScoreDataLogDatabaseAccessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,23 @@

import java.sql.*;
import java.util.*;

import bms.player.beatoraja.select.QueryScoreContext;
import bms.player.beatoraja.song.SongData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.sqlite.SQLiteConfig;
import org.sqlite.SQLiteDataSource;
import org.sqlite.SQLiteConfig.SynchronousMode;
import org.sqlite.SQLiteDataSource;

import javax.management.Query;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

/**
* スコアデータログデータベースアクセサ
Expand All @@ -24,9 +32,10 @@ public class ScoreDataLogDatabaseAccessor extends SQLiteDatabaseAccessor {
private final ResultSetHandler<List<ScoreData>> scoreHandler = new BeanListHandler<ScoreData>(ScoreData.class);

private final QueryRunner qr;
private static final int LOAD_CHUNK_SIZE = 1000;

public ScoreDataLogDatabaseAccessor(String path) throws ClassNotFoundException {
super( new Table("scoredatalog",
super( new Table("scoredatalog",
new Column("sha256", "TEXT", 1, 1),
new Column("mode", "INTEGER",0,1),
new Column("clear", "INTEGER"),
Expand Down Expand Up @@ -56,7 +65,40 @@ public ScoreDataLogDatabaseAccessor(String path) throws ClassNotFoundException {
new Column("date", "INTEGER"),
new Column("state", "INTEGER"),
new Column("scorehash", "TEXT")
));
),
new Table( "eddatalog",
new Column("sha256", "TEXT", 1, 0),
new Column("mode", "INTEGER"),
new Column("clear", "INTEGER"),
new Column("epg", "INTEGER"),
new Column("lpg", "INTEGER"),
new Column("egr", "INTEGER"),
new Column("lgr", "INTEGER"),
new Column("egd", "INTEGER"),
new Column("lgd", "INTEGER"),
new Column("ebd", "INTEGER"),
new Column("lbd", "INTEGER"),
new Column("epr", "INTEGER"),
new Column("lpr", "INTEGER"),
new Column("ems", "INTEGER"),
new Column("lms", "INTEGER"),
new Column("notes", "INTEGER"),
new Column("combo", "INTEGER"),
new Column("minbp", "INTEGER"),
new Column("avgjudge", "INTEGER", 1, 0, String.valueOf(Integer.MAX_VALUE)),
new Column("playcount", "INTEGER"),
new Column("clearcount", "INTEGER"),
new Column("trophy", "TEXT"),
new Column("ghost", "TEXT"),
new Column("option", "INTEGER"),
new Column("seed", "INTEGER"),
new Column("random", "INTEGER"),
new Column("date", "INTEGER"),
new Column("state", "INTEGER"),
new Column("scorehash", "TEXT"),
new Column("rate", "INTEGER"),
new Column("overridejudge", "INTEGER")
));

Class.forName("org.sqlite.JDBC");
SQLiteConfig conf = new SQLiteConfig();
Expand All @@ -83,10 +125,129 @@ public void setScoreDataLog(ScoreData[] scores) {
con.setAutoCommit(false);
for (ScoreData score : scores) {
this.insert(qr, con, "scoredatalog", score);
this.insert(qr, con, "eddatalog", score);
}
con.commit();
} catch (Exception e) {
logger.error("スコア更新時の例外:" + e.getMessage());
logger.error("スコア更新時の例外:{}", e.getMessage());
}
}

public List<ScoreData> getScoreDataLog(String sha256) {
List<ScoreData> result = null;
try {
// TODO: One day we shall use prepared statement instead
result = Validatable.removeInvalidElements(qr.query(String.format("SELECT * FROM eddatalog WHERE sha256 = '%s'", sha256), scoreHandler));
} catch (Exception e) {
logger.error("Failed to query table eddatalog: {}", e.getMessage());
}
return result;
}

public List<ScoreData> getScoreDataLog(String sha256, QueryScoreContext ctx) {
List<ScoreData> result = null;
try (Connection con = qr.getDataSource().getConnection()) {
PreparedStatement ps = con.prepareStatement("SELECT * FROM eddatalog WHERE sha256 = ? AND rate = ? AND overridejudge = ?");
ps.setString(1, sha256);
ps.setInt(2, ctx.freqValue() != null ? ctx.freqValue() : 0);
ps.setInt(3, ctx.overrideJudge() != null ? ctx.overrideJudge() : -1);
result = Validatable.removeInvalidElements(scoreHandler.handle(ps.executeQuery()));
} catch (Exception e) {
logger.error("Failed to query table eddatalog: {}", e.getMessage());
}
return result;
}

/**
* TODO: Maybe we should make the definition of "best" programmable?
*/
public ScoreData getBestScoreDataLog(String sha256, QueryScoreContext ctx) {
List<ScoreData> rawLogs = getScoreDataLog(sha256, ctx);
if (rawLogs.isEmpty()) {
return null;
}
ScoreData result = rawLogs.get(0);
for (ScoreData score : rawLogs) {
if (score.getClear() > result.getClear()) {
result = score;
} else if (score.getClear() == result.getClear() && score.getExscore() > result.getExscore()) {
result = score;
}
}
return result;
}

public void getBestScoreDataLogs(ScoreDatabaseAccessor.ScoreDataCollector collector, SongData[] songs, QueryScoreContext ctx) {
StringBuilder str = new StringBuilder(songs.length * 68);
getBestScoreDataLogs(collector, songs, ctx.lnMode(), str, true, ctx);
str.setLength(0);
getBestScoreDataLogs(collector, songs, 0, str, false, ctx);
}

public void getBestScoreDataLogs(ScoreDatabaseAccessor.ScoreDataCollector collector, SongData[] songs, int mode, StringBuilder str, boolean hasLN, QueryScoreContext ctx) {
try (Connection con = qr.getDataSource().getConnection()) {
int songLength = songs.length;
int chunkLength = (songLength + LOAD_CHUNK_SIZE - 1) / LOAD_CHUNK_SIZE;
List<ScoreData> scores = new ArrayList<>();
for (int i = 0; i < chunkLength;++i) {
// [i * CHUNK_SIZE, min(length, (i + 1) * CHUNK_SIZE)
final int chunkStart = i * LOAD_CHUNK_SIZE;
final int chunkEnd = Math.min(songLength, (i + 1) * LOAD_CHUNK_SIZE);
for (int j = chunkStart; j < chunkEnd; ++j) {
SongData song = songs[j];
if((hasLN && song.hasUndefinedLongNote()) || (!hasLN && !song.hasUndefinedLongNote())) {
if (str.length() > 0) {
str.append(',');
}
str.append('\'').append(song.getSha256()).append('\'');
}
}

PreparedStatement ps = con.prepareStatement("SELECT * FROM eddatalog WHERE sha256 in (?) AND mode = ? AND rate = ? AND overridejudge = ?");
ps.setString(1, str.toString());
ps.setInt(2, mode);
ps.setInt(3, ctx.freqValue() != null ? ctx.freqValue() : 0);
ps.setInt(4, ctx.overrideJudge() != null ? ctx.overrideJudge() : -1);

List<ScoreData> subScores = Validatable.removeInvalidElements(scoreHandler.handle(ps.executeQuery()));
str.setLength(0);
scores.addAll(subScores);
}
scores.sort(Comparator.comparing(ScoreData::getSha256));
// For every chart, we calculate it's best score
List<ScoreData> bestScores = new ArrayList<>();
for (int i = 0; i <scores.size(); ++i) {
int j = i;
ScoreData bestScore = scores.get(i);
while (j + 1 < scores.size() && scores.get(j + 1).getSha256().equals(bestScore.getSha256())) {
ScoreData next = scores.get(j + 1);
if (next.getClear() > bestScore.getClear()) {
bestScore = next;
} else if (next.getClear() == bestScore.getClear() && next.getExscore() > bestScore.getExscore()) {
bestScore = next;
}
j++;
}
bestScores.add(bestScore);
i = j;
}
for(SongData song : songs) {
if((hasLN && song.hasUndefinedLongNote()) || (!hasLN && !song.hasUndefinedLongNote())) {
boolean b = true;
for (ScoreData score : bestScores) {
if(song.getSha256().equals(score.getSha256())) {
collector.collect(song, score);
b = false;
break;
}
}
if(b) {
collector.collect(song, null);
}
}
}
} catch (Exception e) {
logger.error("Failed to query table eddatalog: {}", e.getMessage());
}
}
}
Loading