Added a beatmap cache database.
- New database ".opsu.db" stores a cached copy of all parsed beatmaps. All data will be read from this database unless the last modified time of a beatmap file does not match the one in the table. - OsuParser inserts all new entries to the database in batch after parsing. - Added *toString()/*fromString() methods for 'breaks', 'timingPoints', and 'combo' fields in OsuFile for use with the database. - For any database format changes, update the DATABASE_VERSION field in OsuDB. - Reloading beatmaps (F5) will now clear the beatmap cache. Related changes: - Added small DBController class for convenience. - Changed 'bg' field of OsuFile to only contain the image file name, instead of the full path. - Deleted printDatabase() method from ScoreDB. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
5626cd1699
commit
bf04083ebd
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@
|
||||||
/Songs/
|
/Songs/
|
||||||
/.opsu.log
|
/.opsu.log
|
||||||
/.opsu.cfg
|
/.opsu.cfg
|
||||||
|
/.opsu.db
|
||||||
/.opsu_scores.db
|
/.opsu_scores.db
|
||||||
|
|
||||||
# Eclipse
|
# Eclipse
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package itdelatrisu.opsu;
|
package itdelatrisu.opsu;
|
||||||
|
|
||||||
import itdelatrisu.opsu.audio.MusicController;
|
import itdelatrisu.opsu.audio.MusicController;
|
||||||
|
import itdelatrisu.opsu.db.DBController;
|
||||||
import itdelatrisu.opsu.downloads.DownloadList;
|
import itdelatrisu.opsu.downloads.DownloadList;
|
||||||
import itdelatrisu.opsu.states.ButtonMenu;
|
import itdelatrisu.opsu.states.ButtonMenu;
|
||||||
import itdelatrisu.opsu.states.DownloadsMenu;
|
import itdelatrisu.opsu.states.DownloadsMenu;
|
||||||
|
@ -132,8 +133,8 @@ public class Opsu extends StateBasedGame {
|
||||||
ResourceLoader.addResourceLocation(new FileSystemLocation(new File(".")));
|
ResourceLoader.addResourceLocation(new FileSystemLocation(new File(".")));
|
||||||
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
|
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
|
||||||
|
|
||||||
// initialize score database
|
// initialize databases
|
||||||
ScoreDB.init();
|
DBController.init();
|
||||||
|
|
||||||
// start the game
|
// start the game
|
||||||
try {
|
try {
|
||||||
|
@ -200,8 +201,8 @@ public class Opsu extends StateBasedGame {
|
||||||
* Closes all resources and exits the application.
|
* Closes all resources and exits the application.
|
||||||
*/
|
*/
|
||||||
public static void exit() {
|
public static void exit() {
|
||||||
// close scores database
|
// close databases
|
||||||
ScoreDB.closeConnection();
|
DBController.closeConnections();
|
||||||
|
|
||||||
// cancel all downloads
|
// cancel all downloads
|
||||||
DownloadList.get().cancelAllDownloads();
|
DownloadList.get().cancelAllDownloads();
|
||||||
|
|
|
@ -61,6 +61,9 @@ public class Options {
|
||||||
new File(DATA_DIR, "Songs/").getPath()
|
new File(DATA_DIR, "Songs/").getPath()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Cached beatmap database name. */
|
||||||
|
public static final File OSU_DB = new File(DATA_DIR, ".opsu.db");
|
||||||
|
|
||||||
/** Score database name. */
|
/** Score database name. */
|
||||||
public static final File SCORE_DB = new File(DATA_DIR, ".opsu_scores.db");
|
public static final File SCORE_DB = new File(DATA_DIR, ".opsu_scores.db");
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ package itdelatrisu.opsu;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
import org.newdawn.slick.Color;
|
import org.newdawn.slick.Color;
|
||||||
import org.newdawn.slick.Image;
|
import org.newdawn.slick.Image;
|
||||||
|
@ -164,10 +165,10 @@ public class OsuFile implements Comparable<OsuFile> {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** All timing points. */
|
/** All timing points. */
|
||||||
public ArrayList<OsuTimingPoint> timingPoints;
|
public ArrayList<OsuTimingPoint> timingPoints = new ArrayList<OsuTimingPoint>();
|
||||||
|
|
||||||
/** Song BPM range. */
|
/** Song BPM range. */
|
||||||
int bpmMin = 0, bpmMax = 0;
|
public int bpmMin = 0, bpmMax = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [Colours]
|
* [Colours]
|
||||||
|
@ -264,7 +265,7 @@ public class OsuFile implements Comparable<OsuFile> {
|
||||||
if (bgImage == null) {
|
if (bgImage == null) {
|
||||||
if (bgImageMap.size() > MAX_CACHE_SIZE)
|
if (bgImageMap.size() > MAX_CACHE_SIZE)
|
||||||
clearImageCache();
|
clearImageCache();
|
||||||
bgImage = new Image(bg);
|
bgImage = new Image(new File(file.getParentFile(), bg).getAbsolutePath());
|
||||||
bgImageMap.put(this, bgImage);
|
bgImageMap.put(this, bgImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,4 +318,114 @@ public class OsuFile implements Comparable<OsuFile> {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s - %s [%s]", getArtist(), getTitle(), version);
|
return String.format("%s - %s [%s]", getArtist(), getTitle(), version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link #breaks} field formatted as a string,
|
||||||
|
* or null if the field is null.
|
||||||
|
*/
|
||||||
|
public String breaksToString() {
|
||||||
|
if (breaks == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i : breaks) {
|
||||||
|
sb.append(i);
|
||||||
|
sb.append(',');
|
||||||
|
}
|
||||||
|
if (sb.length() > 0)
|
||||||
|
sb.setLength(sb.length() - 1);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link #breaks} field from a string.
|
||||||
|
* @param s the string
|
||||||
|
*/
|
||||||
|
public void breaksFromString(String s) {
|
||||||
|
if (s == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.breaks = new ArrayList<Integer>();
|
||||||
|
String[] tokens = s.split(",");
|
||||||
|
for (int i = 0; i < tokens.length; i++)
|
||||||
|
breaks.add(Integer.parseInt(tokens[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link #timingPoints} field formatted as a string,
|
||||||
|
* or null if the field is null.
|
||||||
|
*/
|
||||||
|
public String timingPointsToString() {
|
||||||
|
if (timingPoints == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (OsuTimingPoint p : timingPoints) {
|
||||||
|
sb.append(p.toString());
|
||||||
|
sb.append('|');
|
||||||
|
}
|
||||||
|
if (sb.length() > 0)
|
||||||
|
sb.setLength(sb.length() - 1);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link #timingPoints} field from a string.
|
||||||
|
* @param s the string
|
||||||
|
*/
|
||||||
|
public void timingPointsFromString(String s) {
|
||||||
|
if (s == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String[] tokens = s.split("\\|");
|
||||||
|
for (int i = 0; i < tokens.length; i++) {
|
||||||
|
try {
|
||||||
|
timingPoints.add(new OsuTimingPoint(tokens[i]));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.warn(String.format("Failed to read timing point '%s'.", tokens[i]), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timingPoints.trimToSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link #combo} field formatted as a string,
|
||||||
|
* or null if the field is null.
|
||||||
|
*/
|
||||||
|
public String comboToString() {
|
||||||
|
if (combo == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < combo.length; i++) {
|
||||||
|
Color c = combo[i];
|
||||||
|
sb.append(c.getRed());
|
||||||
|
sb.append(',');
|
||||||
|
sb.append(c.getGreen());
|
||||||
|
sb.append(',');
|
||||||
|
sb.append(c.getBlue());
|
||||||
|
sb.append('|');
|
||||||
|
}
|
||||||
|
if (sb.length() > 0)
|
||||||
|
sb.setLength(sb.length() - 1);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link #combo} field from a string.
|
||||||
|
* @param s the string
|
||||||
|
*/
|
||||||
|
public void comboFromString(String s) {
|
||||||
|
if (s == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LinkedList<Color> colors = new LinkedList<Color>();
|
||||||
|
String[] tokens = s.split("\\|");
|
||||||
|
for (int i = 0; i < tokens.length; i++) {
|
||||||
|
String[] rgb = tokens[i].split(",");
|
||||||
|
colors.add(new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])));
|
||||||
|
}
|
||||||
|
if (!colors.isEmpty())
|
||||||
|
this.combo = colors.toArray(new Color[colors.size()]);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package itdelatrisu.opsu;
|
package itdelatrisu.opsu;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.db.OsuDB;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
@ -29,6 +31,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.newdawn.slick.Color;
|
import org.newdawn.slick.Color;
|
||||||
import org.newdawn.slick.util.Log;
|
import org.newdawn.slick.util.Log;
|
||||||
|
@ -52,6 +55,9 @@ public class OsuParser {
|
||||||
/** The total number of directories to parse. */
|
/** The total number of directories to parse. */
|
||||||
private static int totalDirectories = -1;
|
private static int totalDirectories = -1;
|
||||||
|
|
||||||
|
/** Whether or not the database is currently being updated. */
|
||||||
|
private static boolean updatingDatabase = false;
|
||||||
|
|
||||||
// This class should not be instantiated.
|
// This class should not be instantiated.
|
||||||
private OsuParser() {}
|
private OsuParser() {}
|
||||||
|
|
||||||
|
@ -82,7 +88,11 @@ public class OsuParser {
|
||||||
currentDirectoryIndex = 0;
|
currentDirectoryIndex = 0;
|
||||||
totalDirectories = dirs.length;
|
totalDirectories = dirs.length;
|
||||||
|
|
||||||
|
// get last modified map from database
|
||||||
|
Map<String, Long> map = OsuDB.getLastModifiedMap();
|
||||||
|
|
||||||
// parse directories
|
// parse directories
|
||||||
|
LinkedList<OsuFile> parsedOsuFiles = new LinkedList<OsuFile>();
|
||||||
OsuGroupNode lastNode = null;
|
OsuGroupNode lastNode = null;
|
||||||
for (File dir : dirs) {
|
for (File dir : dirs) {
|
||||||
currentDirectoryIndex++;
|
currentDirectoryIndex++;
|
||||||
|
@ -104,9 +114,24 @@ public class OsuParser {
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
currentFile = file;
|
currentFile = file;
|
||||||
|
|
||||||
|
// check if beatmap is cached
|
||||||
|
String path = String.format("%s/%s", dir.getName(), file.getName());
|
||||||
|
if (map.containsKey(path)) {
|
||||||
|
// check last modified times
|
||||||
|
long lastModified = map.get(path);
|
||||||
|
if (lastModified == file.lastModified()) {
|
||||||
|
osuFiles.add(OsuDB.getOsuFile(dir, file));
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
OsuDB.delete(dir.getName(), file.getName());
|
||||||
|
}
|
||||||
|
|
||||||
// Parse hit objects only when needed to save time/memory.
|
// Parse hit objects only when needed to save time/memory.
|
||||||
// Change boolean to 'true' to parse them immediately.
|
// Change boolean to 'true' to parse them immediately.
|
||||||
parseFile(file, dir, osuFiles, false);
|
OsuFile osu = parseFile(file, dir, osuFiles, false);
|
||||||
|
|
||||||
|
if (osu != null)
|
||||||
|
parsedOsuFiles.add(osu);
|
||||||
}
|
}
|
||||||
if (!osuFiles.isEmpty()) { // add entry if non-empty
|
if (!osuFiles.isEmpty()) { // add entry if non-empty
|
||||||
osuFiles.trimToSize();
|
osuFiles.trimToSize();
|
||||||
|
@ -122,6 +147,11 @@ public class OsuParser {
|
||||||
// clear string DB
|
// clear string DB
|
||||||
stringdb = new HashMap<String, String>();
|
stringdb = new HashMap<String, String>();
|
||||||
|
|
||||||
|
// add entries to database
|
||||||
|
updatingDatabase = true;
|
||||||
|
OsuDB.insert(parsedOsuFiles);
|
||||||
|
updatingDatabase = false;
|
||||||
|
|
||||||
currentFile = null;
|
currentFile = null;
|
||||||
currentDirectoryIndex = -1;
|
currentDirectoryIndex = -1;
|
||||||
totalDirectories = -1;
|
totalDirectories = -1;
|
||||||
|
@ -140,10 +170,6 @@ public class OsuParser {
|
||||||
OsuFile osu = new OsuFile(file);
|
OsuFile osu = new OsuFile(file);
|
||||||
|
|
||||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
|
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
|
||||||
|
|
||||||
// initialize timing point list
|
|
||||||
osu.timingPoints = new ArrayList<OsuTimingPoint>();
|
|
||||||
|
|
||||||
String line = in.readLine();
|
String line = in.readLine();
|
||||||
String tokens[] = null;
|
String tokens[] = null;
|
||||||
while (line != null) {
|
while (line != null) {
|
||||||
|
@ -362,7 +388,7 @@ public class OsuParser {
|
||||||
tokens[2] = tokens[2].replaceAll("^\"|\"$", "");
|
tokens[2] = tokens[2].replaceAll("^\"|\"$", "");
|
||||||
String ext = OsuParser.getExtension(tokens[2]);
|
String ext = OsuParser.getExtension(tokens[2]);
|
||||||
if (ext.equals("jpg") || ext.equals("png"))
|
if (ext.equals("jpg") || ext.equals("png"))
|
||||||
osu.bg = getDBString(file.getParent() + File.separator + tokens[2]);
|
osu.bg = getDBString(tokens[2]);
|
||||||
break;
|
break;
|
||||||
case "2": // break periods
|
case "2": // break periods
|
||||||
try {
|
try {
|
||||||
|
@ -623,7 +649,10 @@ public class OsuParser {
|
||||||
* Returns the name of the current file being parsed, or null if none.
|
* Returns the name of the current file being parsed, or null if none.
|
||||||
*/
|
*/
|
||||||
public static String getCurrentFileName() {
|
public static String getCurrentFileName() {
|
||||||
return (currentFile != null) ? currentFile.getName() : null;
|
if (updatingDatabase)
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return (currentFile != null) ? currentFile.getName() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -637,13 +666,18 @@ public class OsuParser {
|
||||||
return currentDirectoryIndex * 100 / totalDirectories;
|
return currentDirectoryIndex * 100 / totalDirectories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the beatmap database is currently being updated.
|
||||||
|
*/
|
||||||
|
public static boolean isUpdatingDatabase() { return updatingDatabase; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the String object in the database for the given String.
|
* Returns the String object in the database for the given String.
|
||||||
* If none, insert the String into the database and return the original String.
|
* If none, insert the String into the database and return the original String.
|
||||||
* @param s the string to retrieve
|
* @param s the string to retrieve
|
||||||
* @return the string object
|
* @return the string object
|
||||||
*/
|
*/
|
||||||
private static String getDBString(String s) {
|
public static String getDBString(String s) {
|
||||||
String DBString = stringdb.get(s);
|
String DBString = stringdb.get(s);
|
||||||
if (DBString == null) {
|
if (DBString == null) {
|
||||||
stringdb.put(s, s);
|
stringdb.put(s, s);
|
||||||
|
|
|
@ -142,4 +142,16 @@ public class OsuTimingPoint {
|
||||||
* @return true if active
|
* @return true if active
|
||||||
*/
|
*/
|
||||||
public boolean isKiaiTimeActive() { return kiai; }
|
public boolean isKiaiTimeActive() { return kiai; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (inherited)
|
||||||
|
return String.format("%d,%d,%d,%d,%d,%d,%d,%d",
|
||||||
|
time, velocity, meter, (int) sampleType,
|
||||||
|
(int) sampleTypeCustom, sampleVolume, 1, (kiai) ? 1: 0);
|
||||||
|
else
|
||||||
|
return String.format("%d,%g,%d,%d,%d,%d,%d,%d",
|
||||||
|
time, beatLength, meter, (int) sampleType,
|
||||||
|
(int) sampleTypeCustom, sampleVolume, 0, (kiai) ? 1: 0);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -587,7 +587,7 @@ public class Utils {
|
||||||
text = "Unpacking new beatmaps...";
|
text = "Unpacking new beatmaps...";
|
||||||
progress = OszUnpacker.getUnpackerProgress();
|
progress = OszUnpacker.getUnpackerProgress();
|
||||||
} else if ((file = OsuParser.getCurrentFileName()) != null) {
|
} else if ((file = OsuParser.getCurrentFileName()) != null) {
|
||||||
text = "Loading beatmaps...";
|
text = (OsuParser.isUpdatingDatabase()) ? "Updating database..." : "Loading beatmaps...";
|
||||||
progress = OsuParser.getParserProgress();
|
progress = OsuParser.getParserProgress();
|
||||||
} else if ((file = SoundController.getCurrentFileName()) != null) {
|
} else if ((file = SoundController.getCurrentFileName()) != null) {
|
||||||
text = "Loading sounds...";
|
text = "Loading sounds...";
|
||||||
|
|
53
src/itdelatrisu/opsu/db/DBController.java
Normal file
53
src/itdelatrisu/opsu/db/DBController.java
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* opsu! - an open-source osu! client
|
||||||
|
* Copyright (C) 2014, 2015 Jeffrey Han
|
||||||
|
*
|
||||||
|
* opsu! is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* opsu! is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package itdelatrisu.opsu.db;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.ErrorHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database controller.
|
||||||
|
*/
|
||||||
|
public class DBController {
|
||||||
|
// This class should not be instantiated.
|
||||||
|
private DBController() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes all databases.
|
||||||
|
*/
|
||||||
|
public static void init() {
|
||||||
|
// load the sqlite-JDBC driver using the current class loader
|
||||||
|
try {
|
||||||
|
Class.forName("org.sqlite.JDBC");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
ErrorHandler.error("Could not load sqlite-JDBC driver.", e, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the databases
|
||||||
|
OsuDB.init();
|
||||||
|
ScoreDB.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes all database connections.
|
||||||
|
*/
|
||||||
|
public static void closeConnections() {
|
||||||
|
OsuDB.closeConnection();
|
||||||
|
ScoreDB.closeConnection();
|
||||||
|
}
|
||||||
|
}
|
352
src/itdelatrisu/opsu/db/OsuDB.java
Normal file
352
src/itdelatrisu/opsu/db/OsuDB.java
Normal file
|
@ -0,0 +1,352 @@
|
||||||
|
/*
|
||||||
|
* opsu! - an open-source osu! client
|
||||||
|
* Copyright (C) 2014, 2015 Jeffrey Han
|
||||||
|
*
|
||||||
|
* opsu! is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* opsu! is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package itdelatrisu.opsu.db;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.ErrorHandler;
|
||||||
|
import itdelatrisu.opsu.Options;
|
||||||
|
import itdelatrisu.opsu.OsuFile;
|
||||||
|
import itdelatrisu.opsu.OsuParser;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles connections and queries with the cached beatmap database.
|
||||||
|
*/
|
||||||
|
public class OsuDB {
|
||||||
|
/**
|
||||||
|
* Current database version.
|
||||||
|
* This value should be changed whenever the database format changes.
|
||||||
|
*/
|
||||||
|
private static final String DATABASE_VERSION = "2014-03-04";
|
||||||
|
|
||||||
|
/** Database connection. */
|
||||||
|
private static Connection connection;
|
||||||
|
|
||||||
|
/** Query statements. */
|
||||||
|
private static PreparedStatement insertStmt, selectStmt, lastModStmt, deleteStmt;
|
||||||
|
|
||||||
|
// This class should not be instantiated.
|
||||||
|
private OsuDB() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the database connection.
|
||||||
|
*/
|
||||||
|
public static void init() {
|
||||||
|
// create a database connection
|
||||||
|
try {
|
||||||
|
connection = DriverManager.getConnection(String.format("jdbc:sqlite:%s", Options.OSU_DB.getPath()));
|
||||||
|
} catch (SQLException e) {
|
||||||
|
// if the error message is "out of memory", it probably means no database file is found
|
||||||
|
ErrorHandler.error("Could not connect to beatmap database.", e, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the database
|
||||||
|
createDatabase();
|
||||||
|
|
||||||
|
// check the database version
|
||||||
|
checkVersion();
|
||||||
|
|
||||||
|
// prepare sql statements
|
||||||
|
try {
|
||||||
|
insertStmt = connection.prepareStatement(
|
||||||
|
"INSERT INTO beatmaps VALUES (" +
|
||||||
|
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " +
|
||||||
|
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
lastModStmt = connection.prepareStatement("SELECT dir, file, lastModified FROM beatmaps");
|
||||||
|
selectStmt = connection.prepareStatement("SELECT * FROM beatmaps WHERE dir = ? AND file = ?");
|
||||||
|
deleteStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ? AND file = ?");
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Failed to prepare beatmap statements.", e, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the database, if it does not exist.
|
||||||
|
*/
|
||||||
|
private static void createDatabase() {
|
||||||
|
try (Statement stmt = connection.createStatement()) {
|
||||||
|
String sql =
|
||||||
|
"CREATE TABLE IF NOT EXISTS beatmaps (" +
|
||||||
|
"dir TEXT, file TEXT, lastModified INTEGER, " +
|
||||||
|
"MID INTEGER, MSID INTEGER, " +
|
||||||
|
"title TEXT, titleUnicode TEXT, artist TEXT, artistUnicode TEXT, " +
|
||||||
|
"creator TEXT, version TEXT, source TEXT, tags TEXT, " +
|
||||||
|
"circles INTEGER, sliders INTEGER, spinners INTEGER, " +
|
||||||
|
"hp REAL, cs REAL, od REAL, ar REAL, sliderMultiplier REAL, sliderTickRate REAL, " +
|
||||||
|
"bpmMin INTEGER, bpmMax INTEGER, endTime INTEGER, " +
|
||||||
|
"audioFile TEXT, audioLeadIn INTEGER, previewTime INTEGER, countdown INTEGER, sampleSet TEXT, stackLeniency REAL, " +
|
||||||
|
"mode INTEGER, letterboxInBreaks BOOLEAN, widescreenStoryboard BOOLEAN, epilepsyWarning BOOLEAN, " +
|
||||||
|
"bg TEXT, timingPoints TEXT, breaks TEXT, combo TEXT" +
|
||||||
|
"); " +
|
||||||
|
"CREATE TABLE IF NOT EXISTS info (" +
|
||||||
|
"key TEXT NOT NULL UNIQUE, value TEXT" +
|
||||||
|
")";
|
||||||
|
stmt.executeUpdate(sql);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Could not create beatmap database.", e, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the stored table version, clears the beatmap database if different
|
||||||
|
* from the current version, then updates the version field.
|
||||||
|
*/
|
||||||
|
private static void checkVersion() {
|
||||||
|
try (Statement stmt = connection.createStatement()) {
|
||||||
|
// get the stored version
|
||||||
|
String sql = "SELECT value FROM info WHERE key = 'version'";
|
||||||
|
ResultSet rs = stmt.executeQuery(sql);
|
||||||
|
String version = (rs.next()) ? rs.getString(1) : "";
|
||||||
|
rs.close();
|
||||||
|
|
||||||
|
// if different from current version, clear the database
|
||||||
|
if (!version.equals(DATABASE_VERSION))
|
||||||
|
clearDatabase();
|
||||||
|
|
||||||
|
// update version
|
||||||
|
PreparedStatement ps = connection.prepareStatement("REPLACE INTO info (key, value) VALUES ('version', ?)");
|
||||||
|
ps.setString(1, DATABASE_VERSION);
|
||||||
|
ps.executeUpdate();
|
||||||
|
ps.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Beatmap database version checks failed.", e, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the database.
|
||||||
|
*/
|
||||||
|
public static void clearDatabase() {
|
||||||
|
// drop the table, then recreate it
|
||||||
|
try (Statement stmt = connection.createStatement()) {
|
||||||
|
String sql = "DROP TABLE beatmaps";
|
||||||
|
stmt.executeUpdate(sql);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Could not drop beatmap database.", e, true);
|
||||||
|
}
|
||||||
|
createDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the OsuFile to the database.
|
||||||
|
* @param osu the OsuFile object
|
||||||
|
*/
|
||||||
|
public static void insert(OsuFile osu) {
|
||||||
|
try {
|
||||||
|
setStatementFields(insertStmt, osu);
|
||||||
|
insertStmt.executeUpdate();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Failed to add beatmap to database.", e, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the OsuFiles to the database in a batch.
|
||||||
|
* @param batch a list of OsuFile objects
|
||||||
|
*/
|
||||||
|
public static void insert(List<OsuFile> batch) {
|
||||||
|
try {
|
||||||
|
// turn off auto-commit mode
|
||||||
|
boolean autoCommit = connection.getAutoCommit();
|
||||||
|
connection.setAutoCommit(false);
|
||||||
|
|
||||||
|
// batch insert
|
||||||
|
for (OsuFile osu : batch) {
|
||||||
|
setStatementFields(insertStmt, osu);
|
||||||
|
insertStmt.addBatch();
|
||||||
|
}
|
||||||
|
insertStmt.executeBatch();
|
||||||
|
connection.commit();
|
||||||
|
|
||||||
|
// restore previous auto-commit mode
|
||||||
|
connection.setAutoCommit(autoCommit);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Failed to add beatmaps to database.", e, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all statement fields using a given OsuFile object.
|
||||||
|
* @param stmt the statement to set fields for
|
||||||
|
* @param osu the OsuFile
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
private static void setStatementFields(PreparedStatement stmt, OsuFile osu)
|
||||||
|
throws SQLException {
|
||||||
|
stmt.setString(1, osu.getFile().getParentFile().getName());
|
||||||
|
stmt.setString(2, osu.getFile().getName());
|
||||||
|
stmt.setLong(3, osu.getFile().lastModified());
|
||||||
|
stmt.setInt(4, osu.beatmapID);
|
||||||
|
stmt.setInt(5, osu.beatmapSetID);
|
||||||
|
stmt.setString(6, osu.title);
|
||||||
|
stmt.setString(7, osu.titleUnicode);
|
||||||
|
stmt.setString(8, osu.artist);
|
||||||
|
stmt.setString(9, osu.artistUnicode);
|
||||||
|
stmt.setString(10, osu.creator);
|
||||||
|
stmt.setString(11, osu.version);
|
||||||
|
stmt.setString(12, osu.source);
|
||||||
|
stmt.setString(13, osu.tags);
|
||||||
|
stmt.setInt(14, osu.hitObjectCircle);
|
||||||
|
stmt.setInt(15, osu.hitObjectSlider);
|
||||||
|
stmt.setInt(16, osu.hitObjectSpinner);
|
||||||
|
stmt.setFloat(17, osu.HPDrainRate);
|
||||||
|
stmt.setFloat(18, osu.circleSize);
|
||||||
|
stmt.setFloat(19, osu.overallDifficulty);
|
||||||
|
stmt.setFloat(20, osu.approachRate);
|
||||||
|
stmt.setFloat(21, osu.sliderMultiplier);
|
||||||
|
stmt.setFloat(22, osu.sliderTickRate);
|
||||||
|
stmt.setInt(23, osu.bpmMin);
|
||||||
|
stmt.setInt(24, osu.bpmMax);
|
||||||
|
stmt.setInt(25, osu.endTime);
|
||||||
|
stmt.setString(26, osu.audioFilename.getName());
|
||||||
|
stmt.setInt(27, osu.audioLeadIn);
|
||||||
|
stmt.setInt(28, osu.previewTime);
|
||||||
|
stmt.setByte(29, osu.countdown);
|
||||||
|
stmt.setString(30, osu.sampleSet);
|
||||||
|
stmt.setFloat(31, osu.stackLeniency);
|
||||||
|
stmt.setByte(32, osu.mode);
|
||||||
|
stmt.setBoolean(33, osu.letterboxInBreaks);
|
||||||
|
stmt.setBoolean(34, osu.widescreenStoryboard);
|
||||||
|
stmt.setBoolean(35, osu.epilepsyWarning);
|
||||||
|
stmt.setString(36, osu.bg);
|
||||||
|
stmt.setString(37, osu.timingPointsToString());
|
||||||
|
stmt.setString(38, osu.breaksToString());
|
||||||
|
stmt.setString(39, osu.comboToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an OsuFile from the database, or null if any error occurred.
|
||||||
|
* @param dir the directory
|
||||||
|
* @param file the file
|
||||||
|
*/
|
||||||
|
public static OsuFile getOsuFile(File dir, File file) {
|
||||||
|
try {
|
||||||
|
OsuFile osu = new OsuFile(file);
|
||||||
|
selectStmt.setString(1, dir.getName());
|
||||||
|
selectStmt.setString(2, file.getName());
|
||||||
|
ResultSet rs = selectStmt.executeQuery();
|
||||||
|
while (rs.next()) {
|
||||||
|
osu.beatmapID = rs.getInt(4);
|
||||||
|
osu.beatmapSetID = rs.getInt(5);
|
||||||
|
osu.title = OsuParser.getDBString(rs.getString(6));
|
||||||
|
osu.titleUnicode = OsuParser.getDBString(rs.getString(7));
|
||||||
|
osu.artist = OsuParser.getDBString(rs.getString(8));
|
||||||
|
osu.artistUnicode = OsuParser.getDBString(rs.getString(9));
|
||||||
|
osu.creator = OsuParser.getDBString(rs.getString(10));
|
||||||
|
osu.version = OsuParser.getDBString(rs.getString(11));
|
||||||
|
osu.source = OsuParser.getDBString(rs.getString(12));
|
||||||
|
osu.tags = OsuParser.getDBString(rs.getString(13));
|
||||||
|
osu.hitObjectCircle = rs.getInt(14);
|
||||||
|
osu.hitObjectSlider = rs.getInt(15);
|
||||||
|
osu.hitObjectSpinner = rs.getInt(16);
|
||||||
|
osu.HPDrainRate = rs.getFloat(17);
|
||||||
|
osu.circleSize = rs.getFloat(18);
|
||||||
|
osu.overallDifficulty = rs.getFloat(19);
|
||||||
|
osu.approachRate = rs.getFloat(20);
|
||||||
|
osu.sliderMultiplier = rs.getFloat(21);
|
||||||
|
osu.sliderTickRate = rs.getFloat(22);
|
||||||
|
osu.bpmMin = rs.getInt(23);
|
||||||
|
osu.bpmMax = rs.getInt(24);
|
||||||
|
osu.endTime = rs.getInt(25);
|
||||||
|
osu.audioFilename = new File(dir, OsuParser.getDBString(rs.getString(26)));
|
||||||
|
osu.audioLeadIn = rs.getInt(27);
|
||||||
|
osu.previewTime = rs.getInt(28);
|
||||||
|
osu.countdown = rs.getByte(29);
|
||||||
|
osu.sampleSet = OsuParser.getDBString(rs.getString(30));
|
||||||
|
osu.stackLeniency = rs.getFloat(31);
|
||||||
|
osu.mode = rs.getByte(32);
|
||||||
|
osu.letterboxInBreaks = rs.getBoolean(33);
|
||||||
|
osu.widescreenStoryboard = rs.getBoolean(34);
|
||||||
|
osu.epilepsyWarning = rs.getBoolean(35);
|
||||||
|
osu.bg = OsuParser.getDBString(rs.getString(36));
|
||||||
|
osu.timingPointsFromString(rs.getString(37));
|
||||||
|
osu.breaksFromString(rs.getString(38));
|
||||||
|
osu.comboFromString(rs.getString(39));
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
return osu;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Failed to get OsuFile from database.", e, true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of file paths ({dir}/{file}) to last modified times, or
|
||||||
|
* null if any error occurred.
|
||||||
|
*/
|
||||||
|
public static Map<String, Long> getLastModifiedMap() {
|
||||||
|
try {
|
||||||
|
Map<String, Long> map = new HashMap<String, Long>();
|
||||||
|
ResultSet rs = lastModStmt.executeQuery();
|
||||||
|
while (rs.next()) {
|
||||||
|
String path = String.format("%s/%s", rs.getString(1), rs.getString(2));
|
||||||
|
long lastModified = rs.getLong(3);
|
||||||
|
map.put(path, lastModified);
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
return map;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Failed to get last modified map from database.", e, true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the entry from the database.
|
||||||
|
* @param dir the directory
|
||||||
|
* @param file the file
|
||||||
|
*/
|
||||||
|
public static void delete(String dir, String file) {
|
||||||
|
try {
|
||||||
|
deleteStmt.setString(1, dir);
|
||||||
|
deleteStmt.setString(2, file);
|
||||||
|
deleteStmt.executeUpdate();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Failed to delete entry from database.", e, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the connection to the database.
|
||||||
|
*/
|
||||||
|
public static void closeConnection() {
|
||||||
|
if (connection != null) {
|
||||||
|
try {
|
||||||
|
insertStmt.close();
|
||||||
|
lastModStmt.close();
|
||||||
|
selectStmt.close();
|
||||||
|
deleteStmt.close();
|
||||||
|
connection.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ErrorHandler.error("Failed to close beatmap database.", e, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,12 @@
|
||||||
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package itdelatrisu.opsu;
|
package itdelatrisu.opsu.db;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.ErrorHandler;
|
||||||
|
import itdelatrisu.opsu.Options;
|
||||||
|
import itdelatrisu.opsu.OsuFile;
|
||||||
|
import itdelatrisu.opsu.ScoreData;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
|
@ -51,16 +56,9 @@ public class ScoreDB {
|
||||||
private ScoreDB() {}
|
private ScoreDB() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the score database connection.
|
* Initializes the database connection.
|
||||||
*/
|
*/
|
||||||
public static void init() {
|
public static void init() {
|
||||||
// load the sqlite-JDBC driver using the current class loader
|
|
||||||
try {
|
|
||||||
Class.forName("org.sqlite.JDBC");
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
ErrorHandler.error("Could not load sqlite-JDBC driver.", e, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a database connection
|
// create a database connection
|
||||||
try {
|
try {
|
||||||
connection = DriverManager.getConnection(String.format("jdbc:sqlite:%s", Options.SCORE_DB.getPath()));
|
connection = DriverManager.getConnection(String.format("jdbc:sqlite:%s", Options.SCORE_DB.getPath()));
|
||||||
|
@ -96,12 +94,12 @@ public class ScoreDB {
|
||||||
"geki = ? AND katu = ? AND miss = ? AND score = ? AND combo = ? AND perfect = ? AND mods = ?"
|
"geki = ? AND katu = ? AND miss = ? AND score = ? AND combo = ? AND perfect = ? AND mods = ?"
|
||||||
);
|
);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
ErrorHandler.error("Failed to prepare score insertion statement.", e, true);
|
ErrorHandler.error("Failed to prepare score statements.", e, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the score database, if it does not exist.
|
* Creates the database, if it does not exist.
|
||||||
*/
|
*/
|
||||||
private static void createDatabase() {
|
private static void createDatabase() {
|
||||||
try (Statement stmt = connection.createStatement()) {
|
try (Statement stmt = connection.createStatement()) {
|
||||||
|
@ -265,7 +263,7 @@ public class ScoreDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the connection to the score database.
|
* Closes the connection to the database.
|
||||||
*/
|
*/
|
||||||
public static void closeConnection() {
|
public static void closeConnection() {
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
|
@ -279,19 +277,4 @@ public class ScoreDB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the entire database (for debugging purposes).
|
|
||||||
*/
|
|
||||||
protected static void printDatabase() {
|
|
||||||
try (
|
|
||||||
Statement stmt = connection.createStatement();
|
|
||||||
ResultSet rs = stmt.executeQuery("SELECT * FROM scores ORDER BY timestamp ASC");
|
|
||||||
) {
|
|
||||||
while (rs.next())
|
|
||||||
System.out.println(new ScoreData(rs));
|
|
||||||
} catch (SQLException e) {
|
|
||||||
ErrorHandler.error(null, e, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -28,13 +28,13 @@ import itdelatrisu.opsu.Options;
|
||||||
import itdelatrisu.opsu.OsuFile;
|
import itdelatrisu.opsu.OsuFile;
|
||||||
import itdelatrisu.opsu.OsuHitObject;
|
import itdelatrisu.opsu.OsuHitObject;
|
||||||
import itdelatrisu.opsu.OsuTimingPoint;
|
import itdelatrisu.opsu.OsuTimingPoint;
|
||||||
import itdelatrisu.opsu.ScoreDB;
|
|
||||||
import itdelatrisu.opsu.ScoreData;
|
import itdelatrisu.opsu.ScoreData;
|
||||||
import itdelatrisu.opsu.Utils;
|
import itdelatrisu.opsu.Utils;
|
||||||
import itdelatrisu.opsu.audio.HitSound;
|
import itdelatrisu.opsu.audio.HitSound;
|
||||||
import itdelatrisu.opsu.audio.MusicController;
|
import itdelatrisu.opsu.audio.MusicController;
|
||||||
import itdelatrisu.opsu.audio.SoundController;
|
import itdelatrisu.opsu.audio.SoundController;
|
||||||
import itdelatrisu.opsu.audio.SoundEffect;
|
import itdelatrisu.opsu.audio.SoundEffect;
|
||||||
|
import itdelatrisu.opsu.db.ScoreDB;
|
||||||
import itdelatrisu.opsu.objects.Circle;
|
import itdelatrisu.opsu.objects.Circle;
|
||||||
import itdelatrisu.opsu.objects.HitObject;
|
import itdelatrisu.opsu.objects.HitObject;
|
||||||
import itdelatrisu.opsu.objects.Slider;
|
import itdelatrisu.opsu.objects.Slider;
|
||||||
|
|
|
@ -30,7 +30,6 @@ import itdelatrisu.opsu.OsuGroupList;
|
||||||
import itdelatrisu.opsu.OsuGroupNode;
|
import itdelatrisu.opsu.OsuGroupNode;
|
||||||
import itdelatrisu.opsu.OsuParser;
|
import itdelatrisu.opsu.OsuParser;
|
||||||
import itdelatrisu.opsu.OszUnpacker;
|
import itdelatrisu.opsu.OszUnpacker;
|
||||||
import itdelatrisu.opsu.ScoreDB;
|
|
||||||
import itdelatrisu.opsu.ScoreData;
|
import itdelatrisu.opsu.ScoreData;
|
||||||
import itdelatrisu.opsu.SongSort;
|
import itdelatrisu.opsu.SongSort;
|
||||||
import itdelatrisu.opsu.Utils;
|
import itdelatrisu.opsu.Utils;
|
||||||
|
@ -39,6 +38,8 @@ import itdelatrisu.opsu.audio.MultiClip;
|
||||||
import itdelatrisu.opsu.audio.MusicController;
|
import itdelatrisu.opsu.audio.MusicController;
|
||||||
import itdelatrisu.opsu.audio.SoundController;
|
import itdelatrisu.opsu.audio.SoundController;
|
||||||
import itdelatrisu.opsu.audio.SoundEffect;
|
import itdelatrisu.opsu.audio.SoundEffect;
|
||||||
|
import itdelatrisu.opsu.db.OsuDB;
|
||||||
|
import itdelatrisu.opsu.db.ScoreDB;
|
||||||
import itdelatrisu.opsu.states.ButtonMenu.MenuState;
|
import itdelatrisu.opsu.states.ButtonMenu.MenuState;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -1034,6 +1035,9 @@ public class SongMenu extends BasicGameState {
|
||||||
reloadThread = new Thread() {
|
reloadThread = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
// clear the beatmap cache
|
||||||
|
OsuDB.clearDatabase();
|
||||||
|
|
||||||
// invoke unpacker and parser
|
// invoke unpacker and parser
|
||||||
File beatmapDir = Options.getBeatmapDir();
|
File beatmapDir = Options.getBeatmapDir();
|
||||||
OszUnpacker.unpackAllFiles(Options.getOSZDir(), beatmapDir);
|
OszUnpacker.unpackAllFiles(Options.getOSZDir(), beatmapDir);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user