opsu-dance/src/itdelatrisu/opsu/Opsu.java
Jeffrey Han 0bd72e731a Save game scores to an SQLite database. [Incomplete!]
Implemented basic features (mostly stable).  The remaining features are mostly graphical.
- Added package org.xerial.sqlite-jdbc.  All scores are saved to .opsu_scores.db on table `scores` after a game completes.
- Added "Scores" class to handle all game score data (including database connections).  The "Score" subclass encapsulates all database fields.
- Added "score viewing" constructor to GameData, for use only in the ranking screen.
- Draw the grade of the highest score next to expanded song buttons in the song menu.
- Added "bit" and "abbrev" fields to GameMod, used in storing/displaying scores.
- Hide the retry/exit buttons in the ranking screen when viewing a score.

Other changes:
- Removed "objectCount" field in GameData (no longer necessary).
- Removed "getID()" method in GameMod (no longer used).
- Moved most drawing in GameRanking state to GameData.
- Removed File parameter of "GameData.loadImages()" (leftover, no longer used).

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
2015-01-28 03:47:24 -05:00

211 lines
6.3 KiB
Java

/*
* 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;
import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.states.GamePauseMenu;
import itdelatrisu.opsu.states.GameRanking;
import itdelatrisu.opsu.states.MainMenu;
import itdelatrisu.opsu.states.MainMenuExit;
import itdelatrisu.opsu.states.OptionsMenu;
import itdelatrisu.opsu.states.SongMenu;
import itdelatrisu.opsu.states.Splash;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.FadeOutTransition;
import org.newdawn.slick.util.ClasspathLocation;
import org.newdawn.slick.util.DefaultLogSystem;
import org.newdawn.slick.util.FileSystemLocation;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
/**
* Main class.
* <p>
* Creates game container, adds all other states, and initializes song data.
*/
public class Opsu extends StateBasedGame {
/** Game states. */
public static final int
STATE_SPLASH = 0,
STATE_MAINMENU = 1,
STATE_MAINMENUEXIT = 2,
STATE_SONGMENU = 3,
STATE_GAME = 4,
STATE_GAMEPAUSEMENU = 5,
STATE_GAMERANKING = 6,
STATE_OPTIONSMENU = 7;
/** Server socket for restricting the program to a single instance. */
private static ServerSocket SERVER_SOCKET;
/**
* Constructor.
* @param name the program name
*/
public Opsu(String name) {
super(name);
}
@Override
public void initStatesList(GameContainer container) throws SlickException {
addState(new Splash(STATE_SPLASH));
addState(new MainMenu(STATE_MAINMENU));
addState(new MainMenuExit(STATE_MAINMENUEXIT));
addState(new SongMenu(STATE_SONGMENU));
addState(new Game(STATE_GAME));
addState(new GamePauseMenu(STATE_GAMEPAUSEMENU));
addState(new GameRanking(STATE_GAMERANKING));
addState(new OptionsMenu(STATE_OPTIONSMENU));
}
/**
* Launches opsu!.
*/
public static void main(String[] args) {
// log all errors to a file
Log.setVerbose(false);
try {
DefaultLogSystem.out = new PrintStream(new FileOutputStream(Options.LOG_FILE, true));
} catch (FileNotFoundException e) {
Log.error(e);
}
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
if (!(e instanceof ThreadDeath)) { // TODO: see MusicController
ErrorHandler.error("** Uncaught Exception! **", e, true);
}
}
});
// parse configuration file
Options.parseOptions();
// only allow a single instance
try {
SERVER_SOCKET = new ServerSocket(Options.getPort());
} catch (IOException e) {
ErrorHandler.error(String.format("Another program is already running on port %d.", Options.getPort()), e, false);
System.exit(1);
}
// set path for lwjgl natives - NOT NEEDED if using JarSplice
File nativeDir = new File("./target/natives/");
if (nativeDir.isDirectory())
System.setProperty("org.lwjgl.librarypath", nativeDir.getAbsolutePath());
// set the resource paths
ResourceLoader.removeAllResourceLocations();
ResourceLoader.addResourceLocation(new FileSystemLocation(Options.getSkinDir()));
ResourceLoader.addResourceLocation(new ClasspathLocation());
ResourceLoader.addResourceLocation(new FileSystemLocation(new File(".")));
ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/")));
// clear the cache
if (!Options.TMP_DIR.mkdir()) {
for (File tmp : Options.TMP_DIR.listFiles())
tmp.delete();
}
Options.TMP_DIR.deleteOnExit();
// initialize score database
Scores.init();
// start the game
try {
// loop until force exit
while (true) {
Opsu opsu = new Opsu("opsu!");
Container app = new Container(opsu);
// basic game settings
Options.setDisplayMode(app);
String[] icons = { "icon16.png", "icon32.png" };
app.setIcons(icons);
app.setForceExit(true);
app.start();
}
} catch (SlickException e) {
// JARs will not run properly inside directories containing '!'
// http://bugs.java.com/view_bug.do?bug_id=4523159
if (new File("").getAbsolutePath().indexOf('!') != -1)
ErrorHandler.error("Cannot run JAR from path containing '!'.", null, false);
else
ErrorHandler.error("Error while creating game container.", e, true);
}
Opsu.exit();
}
@Override
public boolean closeRequested() {
int id = this.getCurrentStateID();
// intercept close requests in game-related states and return to song menu
if (id == STATE_GAME || id == STATE_GAMEPAUSEMENU || id == STATE_GAMERANKING) {
// start playing track at preview position
SongMenu songMenu = (SongMenu) this.getState(Opsu.STATE_SONGMENU);
songMenu.resetGameDataOnLoad();
if (id == STATE_GAME) {
MusicController.pause();
MusicController.resume();
} else
songMenu.resetTrackOnLoad();
Utils.resetCursor();
this.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
return false;
}
return true;
}
/**
* Closes all resources and exits the application.
*/
public static void exit() {
// close scores database
Scores.closeConnection();
// close server socket
if (SERVER_SOCKET != null) {
try {
SERVER_SOCKET.close();
} catch (IOException e) {
ErrorHandler.error("Failed to close server socket.", e, false);
}
}
System.exit(0);
}
}