Added wrapper around AppGameContainer to catch critical errors.

Critical game errors (detected in Game.updateAndRender()) are now passed through ErrorHandler.  Note that the actual exception is not displayed in the ErrorHandler window, only the log.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2015-01-18 14:29:48 -05:00
parent 2f56bca9f7
commit ee17b20b25
4 changed files with 126 additions and 22 deletions

View File

@ -0,0 +1,88 @@
/*
* 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 org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.Game;
import org.newdawn.slick.SlickException;
/**
* AppGameContainer extension that sends critical errors to ErrorHandler.
*/
public class Container extends AppGameContainer {
/**
* SlickException causing game failure.
*/
protected SlickException e = null;
/**
* Create a new container wrapping a game
*
* @param game The game to be wrapped
* @throws SlickException Indicates a failure to initialise the display
*/
public Container(Game game) throws SlickException {
super(game);
}
/**
* Create a new container wrapping a game
*
* @param game The game to be wrapped
* @param width The width of the display required
* @param height The height of the display required
* @param fullscreen True if we want fullscreen mode
* @throws SlickException Indicates a failure to initialise the display
*/
public Container(Game game, int width, int height, boolean fullscreen) throws SlickException {
super(game);
}
@Override
public void start() throws SlickException {
try {
setup();
getDelta();
while (running())
gameLoop();
} finally {
MusicController.reset(); // prevent loading tracks from re-initializing OpenAL
destroy();
if (e != null) {
ErrorHandler.error(null, e, true);
e = null;
}
}
if (forceExit)
System.exit(0);
}
@Override
protected void updateAndRender(int delta) throws SlickException {
try {
super.updateAndRender(delta);
} catch (SlickException e) {
this.e = e;
throw e;
}
}
}

View File

@ -34,7 +34,6 @@ import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.net.ServerSocket; import java.net.ServerSocket;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer; import org.newdawn.slick.GameContainer;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
@ -140,7 +139,7 @@ public class Opsu extends StateBasedGame {
// start the game // start the game
Opsu opsu = new Opsu("opsu!"); Opsu opsu = new Opsu("opsu!");
try { try {
AppGameContainer app = new AppGameContainer(opsu); Container app = new Container(opsu);
// basic game settings // basic game settings
Options.setDisplayMode(app); Options.setDisplayMode(app);
@ -173,7 +172,7 @@ public class Opsu extends StateBasedGame {
} }
Options.saveOptions(); Options.saveOptions();
((AppGameContainer) this.getContainer()).destroy(); ((Container) this.getContainer()).destroy();
closeSocket(); closeSocket();
return true; return true;
} }

View File

@ -82,24 +82,11 @@ public class MusicController {
/** /**
* Plays an audio file at the preview position. * Plays an audio file at the preview position.
*/ */
@SuppressWarnings("deprecation")
public static void play(final OsuFile osu, final boolean loop) { public static void play(final OsuFile osu, final boolean loop) {
boolean play = (lastOsu == null || !osu.audioFilename.equals(lastOsu.audioFilename)); boolean play = (lastOsu == null || !osu.audioFilename.equals(lastOsu.audioFilename));
lastOsu = osu; lastOsu = osu;
if (play) { if (play) {
themePlaying = false; reset();
// TODO: properly interrupt instead of using deprecated Thread.stop();
// interrupt the conversion/track loading
if (isTrackLoading())
// trackLoader.interrupt();
trackLoader.stop();
if (wavFile != null)
wavFile.delete();
// releases all sources from previous tracks
destroyOpenAL();
System.gc(); System.gc();
switch (OsuParser.getExtension(osu.audioFilename.getName())) { switch (OsuParser.getExtension(osu.audioFilename.getName())) {
@ -152,8 +139,6 @@ public class MusicController {
player.loop(); player.loop();
else else
player.play(); player.play();
pauseTime = 0f;
trackDimmed = false;
} }
} }
@ -322,6 +307,38 @@ public class MusicController {
trackDimmed = !trackDimmed; trackDimmed = !trackDimmed;
} }
/**
* Completely resets MusicController state.
* <p>
* Stops the current track, cancels track conversions, erases
* temporary files, releases OpenAL sources, and resets state.
*/
@SuppressWarnings("deprecation")
public static void reset() {
stop();
// TODO: properly interrupt instead of using deprecated Thread.stop();
// interrupt the conversion/track loading
if (isTrackLoading())
// trackLoader.interrupt();
trackLoader.stop();
trackLoader = null;
// delete temporary WAV file
if (wavFile != null) {
wavFile.delete();
wavFile = null;
}
// reset state
themePlaying = false;
pauseTime = 0f;
trackDimmed = false;
// releases all sources from previous tracks
destroyOpenAL();
}
/** /**
* Stops and releases all sources, clears each of the specified Audio * Stops and releases all sources, clears each of the specified Audio
* buffers, destroys the OpenAL context, and resets SoundStore for future use. * buffers, destroys the OpenAL context, and resets SoundStore for future use.

View File

@ -18,6 +18,7 @@
package itdelatrisu.opsu.states; package itdelatrisu.opsu.states;
import itdelatrisu.opsu.Container;
import itdelatrisu.opsu.ErrorHandler; import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.GameMod; import itdelatrisu.opsu.GameMod;
@ -43,7 +44,6 @@ import java.util.Locale;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer; import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics; import org.newdawn.slick.Graphics;
@ -1083,7 +1083,7 @@ public class Options extends BasicGameState {
* @param app the game container * @param app the game container
* @throws SlickException failure to set display mode * @throws SlickException failure to set display mode
*/ */
public static void setDisplayMode(AppGameContainer app) throws SlickException { public static void setDisplayMode(Container app) throws SlickException {
int screenWidth = app.getScreenWidth(); int screenWidth = app.getScreenWidth();
int screenHeight = app.getScreenHeight(); int screenHeight = app.getScreenHeight();