Skip to content

Commit

Permalink
Added local users system.
Browse files Browse the repository at this point in the history
You can now add multiple users, and stats will be recorded for each play (including failures).

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
  • Loading branch information
itdelatrisu committed Jan 31, 2017
1 parent 722d4fb commit 0fd7c35
Show file tree
Hide file tree
Showing 19 changed files with 1,526 additions and 30 deletions.
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The images included in opsu! belong to their respective authors.
* kouyang
* teinecthel
* Teddy Kelley - https://unsplash.com/photos/weuWmzv7xnU (main menu background)
* User icons ("User", "Male User", "Female User") by To Uyen from the Noun Project

Projects
--------
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The following files and folders will be created by opsu! as needed:
* `.opsu.cfg`: The configuration file. Most (but not all) of the settings can
be changed through the options menu.
* `.opsu.db`: The beatmap cache database.
* `.opsu_scores.db`: The scores database.
* `.opsu_scores.db`: The scores and player database.
* `.opsu.log`: The error log. All critical errors displayed in-game are also
logged to this file, and other warnings not shown are logged as well.
* `Songs/`: The beatmap directory (not used if an osu! installation is detected).
Expand Down
Binary file added res/user0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/user1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/user2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 11 additions & 4 deletions src/itdelatrisu/opsu/GameData.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import itdelatrisu.opsu.user.UserList;

import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.Random;
import java.util.concurrent.LinkedBlockingDeque;

import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
Expand Down Expand Up @@ -1128,6 +1129,11 @@ public boolean isAlive() {
*/
public void changeHealth(float value) { health.changeHealth(value); }

/**
* Returns the raw score.
*/
public long getScore() { return score; }

/**
* Changes score by a raw value (not affected by other modifiers).
* @param value the score value
Expand All @@ -1153,7 +1159,7 @@ public static float getScorePercent(int hit300, int hit100, int hit50, int miss)
/**
* Returns the raw score percentage.
*/
private float getScorePercent() {
public float getScorePercent() {
return getScorePercent(
hitResultCount[HIT_300], hitResultCount[HIT_100],
hitResultCount[HIT_50], hitResultCount[HIT_MISS]
Expand Down Expand Up @@ -1609,7 +1615,8 @@ public ScoreData getCurrentScoreData(Beatmap beatmap, boolean slidingScore) {
sd.perfect = (comboMax == fullObjectCount);
sd.mods = GameMod.getModState();
sd.replayString = (replay == null) ? null : replay.getReplayFilename();
sd.playerName = null; // TODO
sd.playerName = GameMod.AUTO.isActive() ?
UserList.AUTO_USER_NAME : UserList.get().getCurrentUser().getName();
return sd;
}

Expand All @@ -1631,7 +1638,7 @@ public Replay getReplay(ReplayFrame[] frames, Beatmap beatmap) {
replay.mode = Beatmap.MODE_OSU;
replay.version = Updater.get().getBuildDate();
replay.beatmapHash = (beatmap == null) ? "" : beatmap.md5Hash;
replay.playerName = ""; // TODO
replay.playerName = UserList.get().getCurrentUser().getName();
replay.replayHash = Long.toString(System.currentTimeMillis()); // TODO
replay.hit300 = (short) hitResultCount[HIT_300];
replay.hit100 = (short) hitResultCount[HIT_100];
Expand Down
16 changes: 16 additions & 0 deletions src/itdelatrisu/opsu/GameImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ protected Image process_sub(Image img, int w, int h) {
CONTROL_CHECK_ON ("control-check-on", "png", false, false),
CONTROL_CHECK_OFF ("control-check-off", "png", false, false),

// User selection menu
USER ("user", "user%d", "png", false, false),

// TODO: ensure this image hasn't been modified (checksum?)
ALPHA_MAP ("alpha", "png", false, false);

Expand Down Expand Up @@ -543,6 +546,19 @@ private static String[] getSuffixes() {
this.preload = preload;
}

/**
* Constructor for an array of general images.
* @param filename the image file name
* @param filenameFormat the formatted file name string (for loading multiple images)
* @param type the file types (separated by '|')
* @param beatmapSkinnable whether or not the image is beatmap-skinnable
* @param preload whether or not to preload the image
*/
GameImage(String filename, String filenameFormat, String type, boolean beatmapSkinnable, boolean preload) {
this(filename, type, beatmapSkinnable, preload);
this.filenameFormat = filenameFormat;
}

/**
* Returns whether or not the image is beatmap-skinnable.
* @return true if beatmap-skinnable
Expand Down
2 changes: 1 addition & 1 deletion src/itdelatrisu/opsu/ScoreData.java
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ public void drawSmall(Graphics g, int vPos, int rank, float position, GameData d

// player name
if (playerName != null)
Fonts.MEDIUMBOLD.drawString(xPaddingLeft, yPos + yPadding, playerName, white);
Fonts.MEDIUM.drawString(xPaddingLeft, yPos + yPadding, playerName, white);

// score
Fonts.DEFAULT.drawString(
Expand Down
8 changes: 8 additions & 0 deletions src/itdelatrisu/opsu/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.Fonts;
import itdelatrisu.opsu.ui.UI;
import itdelatrisu.opsu.user.UserButton;
import itdelatrisu.opsu.user.UserList;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
Expand Down Expand Up @@ -154,6 +156,12 @@ public static void init(GameContainer container, StateBasedGame game) {

// initialize UI components
UI.init(container, game);

// build user list
UserList.create();

// initialize user button
UserButton.init(width, height);
}

/**
Expand Down
119 changes: 118 additions & 1 deletion src/itdelatrisu/opsu/db/ScoreDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.ScoreData;
import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.user.User;

import java.sql.Connection;
import java.sql.PreparedStatement;
Expand All @@ -45,7 +46,7 @@ public class ScoreDB {
* This value should be changed whenever the database format changes.
* Add any update queries to the {@link #getUpdateQueries(int)} method.
*/
private static final int DATABASE_VERSION = 20150401;
private static final int DATABASE_VERSION = 20170131;

/**
* Returns a list of SQL queries to apply, in order, to update from
Expand Down Expand Up @@ -77,6 +78,9 @@ private static List<String> getUpdateQueries(int version) {
/** Score deletion statement. */
private static PreparedStatement deleteSongStmt, deleteScoreStmt;

/** User-related statements. */
private static PreparedStatement setCurrentUserStmt, insertUserStmt, deleteUserStmt;

// This class should not be instantiated.
private ScoreDB() {}

Expand Down Expand Up @@ -123,6 +127,9 @@ public static void init() {
"(playerName = ? OR (playerName IS NULL AND ? IS NULL))"
// TODO: extra playerName checks not needed if name is guaranteed not null
);
setCurrentUserStmt = connection.prepareStatement("INSERT OR REPLACE INTO info VALUES ('user', ?)");
insertUserStmt = connection.prepareStatement("INSERT OR REPLACE INTO users VALUES (?, ?, ?, ?, ?, ?)");
deleteUserStmt = connection.prepareStatement("DELETE FROM users WHERE name = ?");
} catch (SQLException e) {
ErrorHandler.error("Failed to prepare score statements.", e, true);
}
Expand All @@ -147,6 +154,12 @@ private static void createDatabase() {
"replay TEXT, " +
"playerName TEXT"+
");" +
"CREATE TABLE IF NOT EXISTS users (" +
"name TEXT NOT NULL UNIQUE, " +
"score INTEGER, accuracy REAL, " +
"playsPassed INTEGER, playsTotal INTEGER, " +
"icon INTEGER" +
");" +
"CREATE TABLE IF NOT EXISTS info (" +
"key TEXT NOT NULL UNIQUE, value TEXT" +
"); " +
Expand Down Expand Up @@ -390,6 +403,105 @@ private static ScoreData[] getSortedArray(List<ScoreData> list) {
return scores;
}

/**
* Retrieves all users.
* @return a list containing all users
*/
public static List<User> getUsers() {
List<User> users = new ArrayList<User>();

if (connection == null)
return users;

try (Statement stmt = connection.createStatement()) {
String sql = "SELECT * FROM users";
ResultSet rs = stmt.executeQuery(sql);
while (rs.next())
users.add(new User(
rs.getString(1), rs.getLong(2), rs.getDouble(3),
rs.getInt(4), rs.getInt(5), rs.getInt(6)
));
rs.close();
} catch (SQLException e) {
ErrorHandler.error("Failed to read users from database.", e, true);
}
return users;
}

/**
* Retrieves the current user.
* @return the current user's name, or null if not set.
*/
public static String getCurrentUser() {
if (connection == null)
return null;

try (Statement stmt = connection.createStatement()) {
String sql = "SELECT value FROM info WHERE key = 'user'";
ResultSet rs = stmt.executeQuery(sql);
String name = (rs.next()) ? rs.getString(1) : null;
rs.close();
return name;
} catch (SQLException e) {
ErrorHandler.error("Failed to read current user from database.", e, true);
return null;
}
}

/**
* Sets the current user.
* @param user the user's name
*/
public static void setCurrentUser(String user) {
if (connection == null)
return;

try {
setCurrentUserStmt.setString(1, user);
setCurrentUserStmt.executeUpdate();
} catch (SQLException e) {
ErrorHandler.error("Failed to set current user in database.", e, true);
}
}

/**
* Updates a user entry, or creates one if it does not exist.
* @param user the user
*/
public static void updateUser(User user) {
if (connection == null)
return;

try {
insertUserStmt.setString(1, user.getName());
insertUserStmt.setLong(2, user.getScore());
insertUserStmt.setDouble(3, user.getAccuracy());
insertUserStmt.setInt(4, user.getPassedPlays());
insertUserStmt.setInt(5, user.getTotalPlays());
insertUserStmt.setInt(6, user.getIconId());
insertUserStmt.executeUpdate();
} catch (SQLException e) {
ErrorHandler.error("Failed to update user in database.", e, true);
return;
}
}

/**
* Deletes a user.
* @param user the user's name
*/
public static void deleteUser(String user) {
if (connection == null)
return;

try {
deleteUserStmt.setString(1, user);
deleteUserStmt.executeUpdate();
} catch (SQLException e) {
ErrorHandler.error("Failed to delete user from database.", e, true);
}
}

/**
* Closes the connection to the database.
*/
Expand All @@ -401,6 +513,11 @@ public static void closeConnection() {
insertStmt.close();
selectMapStmt.close();
selectMapSetStmt.close();
deleteSongStmt.close();
deleteScoreStmt.close();
setCurrentUserStmt.close();
insertUserStmt.close();
deleteUserStmt.close();
connection.close();
connection = null;
} catch (SQLException e) {
Expand Down
15 changes: 13 additions & 2 deletions src/itdelatrisu/opsu/states/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
import itdelatrisu.opsu.ui.UI;
import itdelatrisu.opsu.ui.animations.AnimatedValue;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import itdelatrisu.opsu.user.User;
import itdelatrisu.opsu.user.UserList;
import itdelatrisu.opsu.video.FFmpeg;
import itdelatrisu.opsu.video.Video;

Expand Down Expand Up @@ -925,9 +927,13 @@ else if (replayFrames != null) {
ScoreData score = data.getScoreData(beatmap);
data.setGameplay(!isReplay);

// add score to database
if (!unranked && !isReplay)
// add score to database and user stats
if (!unranked && !isReplay) {
ScoreDB.addScore(score);
User user = UserList.get().getCurrentUser();
user.add(data.getScore(), data.getScorePercent());
ScoreDB.updateUser(user);
}
}

// start timer
Expand Down Expand Up @@ -1012,6 +1018,11 @@ else if (replayFrames != null) {
rotations = new IdentityHashMap<GameObject, Float>();
SoundController.playSound(SoundEffect.FAIL);

// record to stats
User user = UserList.get().getCurrentUser();
user.add(data.getScore());
ScoreDB.updateUser(user);

// fade to pause menu
game.enterState(Opsu.STATE_GAMEPAUSEMENU,
new DelayedFadeOutTransition(Color.black, MUSIC_FADEOUT_TIME, MUSIC_FADEOUT_TIME - LOSE_FADEOUT_TIME),
Expand Down
Loading

0 comments on commit 0fd7c35

Please # to comment.