Update replay frames in a new thread.
This drops less frames, but is still pretty bad. See #42. - Changed some LinkedList classes to LinkedBlockingDeques and added some synchronized methods. - Slight modifications to OpenALStreamPlayer (may or may not be slightly more accurate). Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
39caf30770
commit
1f8c150e6c
|
@ -26,6 +26,7 @@ import itdelatrisu.opsu.audio.SoundEffect;
|
|||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
import org.newdawn.slick.Animation;
|
||||
import org.newdawn.slick.Color;
|
||||
|
@ -165,7 +166,7 @@ public class GameData {
|
|||
private int[] hitResultOffset;
|
||||
|
||||
/** List of hit result objects associated with hit objects. */
|
||||
private LinkedList<OsuHitObjectResult> hitResultList;
|
||||
private LinkedBlockingDeque<OsuHitObjectResult> hitResultList;
|
||||
|
||||
/**
|
||||
* Class to store hit error information.
|
||||
|
@ -198,7 +199,7 @@ public class GameData {
|
|||
}
|
||||
|
||||
/** List containing recent hit error information. */
|
||||
private LinkedList<HitErrorInfo> hitErrorList;
|
||||
private LinkedBlockingDeque<HitErrorInfo> hitErrorList;
|
||||
|
||||
/**
|
||||
* Hit result helper class.
|
||||
|
@ -332,8 +333,8 @@ public class GameData {
|
|||
health = 100f;
|
||||
healthDisplay = 100f;
|
||||
hitResultCount = new int[HIT_MAX];
|
||||
hitResultList = new LinkedList<OsuHitObjectResult>();
|
||||
hitErrorList = new LinkedList<HitErrorInfo>();
|
||||
hitResultList = new LinkedBlockingDeque<OsuHitObjectResult>();
|
||||
hitErrorList = new LinkedBlockingDeque<HitErrorInfo>();
|
||||
fullObjectCount = 0;
|
||||
combo = 0;
|
||||
comboMax = 0;
|
||||
|
|
|
@ -148,6 +148,9 @@ public class Game extends BasicGameState {
|
|||
/** Number of retries. */
|
||||
private int retries = 0;
|
||||
|
||||
/** Whether or not this game is a replay. */
|
||||
private boolean isReplay = false;
|
||||
|
||||
/** The replay, if any. */
|
||||
private Replay replay;
|
||||
|
||||
|
@ -163,6 +166,12 @@ public class Game extends BasicGameState {
|
|||
/** The replay skip time, or -1 if none. */
|
||||
private int replaySkipTime = -1;
|
||||
|
||||
/** The thread updating the replay frames. */
|
||||
private Thread replayThread;
|
||||
|
||||
/** Whether or not the replay thread should continue running. */
|
||||
private boolean replayThreadRunning;
|
||||
|
||||
/** The previous game mod state (before the replay). */
|
||||
private int previousMods = 0;
|
||||
|
||||
|
@ -385,7 +394,7 @@ public class Game extends BasicGameState {
|
|||
cursorCirclePulse.drawCentered(pausedMouseX, pausedMouseY);
|
||||
}
|
||||
|
||||
if (replay == null)
|
||||
if (!isReplay)
|
||||
UI.draw(g);
|
||||
else
|
||||
UI.draw(g, replayX, replayY, replayKeyPressed);
|
||||
|
@ -396,7 +405,7 @@ public class Game extends BasicGameState {
|
|||
throws SlickException {
|
||||
UI.update(delta);
|
||||
int mouseX, mouseY;
|
||||
if (replay == null) {
|
||||
if (!isReplay) {
|
||||
mouseX = input.getMouseX();
|
||||
mouseY = input.getMouseY();
|
||||
} else {
|
||||
|
@ -460,7 +469,7 @@ public class Game extends BasicGameState {
|
|||
else { // go to ranking screen
|
||||
((GameRanking) game.getState(Opsu.STATE_GAMERANKING)).setGameData(data);
|
||||
ScoreData score = data.getScoreData(osu);
|
||||
if (!GameMod.AUTO.isActive() && !GameMod.RELAX.isActive() && !GameMod.AUTOPILOT.isActive() && replay == null)
|
||||
if (!GameMod.AUTO.isActive() && !GameMod.RELAX.isActive() && !GameMod.AUTOPILOT.isActive() && !isReplay)
|
||||
ScoreDB.addScore(score);
|
||||
game.enterState(Opsu.STATE_GAMERANKING, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||
}
|
||||
|
@ -484,29 +493,12 @@ public class Game extends BasicGameState {
|
|||
}
|
||||
|
||||
// replays
|
||||
if (replay != null) {
|
||||
if (isReplay) {
|
||||
// skip intro
|
||||
if (replaySkipTime > 0 && trackPosition > replaySkipTime) {
|
||||
skipIntro();
|
||||
replaySkipTime = -1;
|
||||
}
|
||||
|
||||
// load next frame(s)
|
||||
int replayKey = ReplayFrame.KEY_NONE;
|
||||
while (replayIndex < replay.frames.length && trackPosition >= replay.frames[replayIndex].getTime()) {
|
||||
ReplayFrame frame = replay.frames[replayIndex];
|
||||
replayX = frame.getX();
|
||||
replayY = frame.getY();
|
||||
replayKeyPressed = frame.isKeyPressed();
|
||||
if (replayKeyPressed)
|
||||
replayKey = frame.getKeys();
|
||||
replayIndex++;
|
||||
}
|
||||
|
||||
// send a key press
|
||||
if (replayKey != ReplayFrame.KEY_NONE)
|
||||
gameKeyPressed(((replayKey & ReplayFrame.KEY_M1) > 0) ?
|
||||
Input.MOUSE_LEFT_BUTTON : Input.MOUSE_RIGHT_BUTTON, replayX, replayY);
|
||||
}
|
||||
|
||||
// song beginning
|
||||
|
@ -534,7 +526,7 @@ public class Game extends BasicGameState {
|
|||
}
|
||||
|
||||
// pause game if focus lost
|
||||
if (!container.hasFocus() && !GameMod.AUTO.isActive() && replay == null) {
|
||||
if (!container.hasFocus() && !GameMod.AUTO.isActive() && !isReplay) {
|
||||
if (pauseTime < 0) {
|
||||
pausedMouseX = mouseX;
|
||||
pausedMouseY = mouseY;
|
||||
|
@ -559,14 +551,14 @@ public class Game extends BasicGameState {
|
|||
}
|
||||
|
||||
// game over, force a restart
|
||||
if (replay == null) {
|
||||
if (!isReplay) {
|
||||
restart = Restart.LOSE;
|
||||
game.enterState(Opsu.STATE_GAMEPAUSEMENU);
|
||||
}
|
||||
}
|
||||
|
||||
// update objects (loop in unlikely event of any skipped indexes)
|
||||
boolean keyPressed = ((replay != null && replayKeyPressed) || Utils.isGameKeyPressed());
|
||||
boolean keyPressed = ((isReplay && replayKeyPressed) || Utils.isGameKeyPressed());
|
||||
while (objectIndex < hitObjects.length && trackPosition > osu.objects[objectIndex].getTime()) {
|
||||
// check if we've already passed the next object's start time
|
||||
boolean overlap = (objectIndex + 1 < hitObjects.length &&
|
||||
|
@ -588,7 +580,7 @@ public class Game extends BasicGameState {
|
|||
int trackPosition = MusicController.getPosition();
|
||||
|
||||
// game keys
|
||||
if (!Keyboard.isRepeatEvent() && replay == null) {
|
||||
if (!Keyboard.isRepeatEvent() && !isReplay) {
|
||||
if (key == Options.getGameKeyLeft())
|
||||
gameKeyPressed(Input.MOUSE_LEFT_BUTTON, input.getMouseX(), input.getMouseY());
|
||||
else if (key == Options.getGameKeyRight())
|
||||
|
@ -598,7 +590,7 @@ public class Game extends BasicGameState {
|
|||
switch (key) {
|
||||
case Input.KEY_ESCAPE:
|
||||
// "auto" mod or watching replay: go back to song menu
|
||||
if (GameMod.AUTO.isActive() || replay != null) {
|
||||
if (GameMod.AUTO.isActive() || isReplay) {
|
||||
game.closeRequested();
|
||||
break;
|
||||
}
|
||||
|
@ -696,7 +688,7 @@ public class Game extends BasicGameState {
|
|||
return;
|
||||
|
||||
// watching replay
|
||||
if (replay != null) {
|
||||
if (isReplay) {
|
||||
// only allow skip button
|
||||
if (button != Input.MOUSE_MIDDLE_BUTTON && skipButton.contains(x, y))
|
||||
skipIntro();
|
||||
|
@ -837,7 +829,7 @@ public class Game extends BasicGameState {
|
|||
}
|
||||
|
||||
// load replay frames
|
||||
if (replay != null) {
|
||||
if (isReplay) {
|
||||
// unhide cursor
|
||||
UI.showCursor();
|
||||
|
||||
|
@ -862,6 +854,44 @@ public class Game extends BasicGameState {
|
|||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
// run frame updates in another thread
|
||||
killReplayThread();
|
||||
replayThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (replayThreadRunning) {
|
||||
// update frames
|
||||
int trackPosition = MusicController.getPosition();
|
||||
int keys = ReplayFrame.KEY_NONE;
|
||||
while (replayIndex < replay.frames.length && trackPosition >= replay.frames[replayIndex].getTime()) {
|
||||
ReplayFrame frame = replay.frames[replayIndex];
|
||||
replayX = frame.getX();
|
||||
replayY = frame.getY();
|
||||
replayKeyPressed = frame.isKeyPressed();
|
||||
if (replayKeyPressed)
|
||||
keys = frame.getKeys();
|
||||
replayIndex++;
|
||||
}
|
||||
|
||||
// send a key press
|
||||
if (replayKeyPressed && keys != ReplayFrame.KEY_NONE)
|
||||
gameKeyPressed(((keys & ReplayFrame.KEY_M1) > 0) ?
|
||||
Input.MOUSE_LEFT_BUTTON : Input.MOUSE_RIGHT_BUTTON, replayX, replayY);
|
||||
|
||||
// out of frames
|
||||
if (replayIndex >= replay.frames.length)
|
||||
break;
|
||||
|
||||
// sleep execution
|
||||
try {
|
||||
Thread.sleep(0, 512000);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
replayThreadRunning = true;
|
||||
replayThread.start();
|
||||
}
|
||||
|
||||
leadInTime = osu.audioLeadIn + approachTime;
|
||||
|
@ -876,10 +906,11 @@ public class Game extends BasicGameState {
|
|||
throws SlickException {
|
||||
// container.setMouseGrabbed(false);
|
||||
|
||||
// reset previous mod state and re-hide cursor
|
||||
if (replay != null) {
|
||||
// replays
|
||||
if (isReplay) {
|
||||
GameMod.loadModState(previousMods);
|
||||
UI.hideCursor();
|
||||
killReplayThread();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -914,7 +945,7 @@ public class Game extends BasicGameState {
|
|||
* Skips the beginning of a track.
|
||||
* @return true if skipped, false otherwise
|
||||
*/
|
||||
private boolean skipIntro() {
|
||||
private synchronized boolean skipIntro() {
|
||||
int firstObjectTime = osu.objects[0].getTime();
|
||||
int trackPosition = MusicController.getPosition();
|
||||
if (objectIndex == 0 && trackPosition < firstObjectTime - SKIP_OFFSET) {
|
||||
|
@ -923,6 +954,8 @@ public class Game extends BasicGameState {
|
|||
MusicController.resume();
|
||||
}
|
||||
replaySkipTime = -1;
|
||||
if (replayThread != null && replayThread.isAlive())
|
||||
replayThread.interrupt();
|
||||
MusicController.setPosition(firstObjectTime - SKIP_OFFSET);
|
||||
SoundController.playSound(SoundEffect.MENUHIT);
|
||||
return true;
|
||||
|
@ -1052,9 +1085,23 @@ public class Game extends BasicGameState {
|
|||
*/
|
||||
public float getTimingPointMultiplier() { return beatLength / beatLengthBase; }
|
||||
|
||||
/**
|
||||
* Kills the running replay updating thread, if any.
|
||||
*/
|
||||
private void killReplayThread() {
|
||||
if (replayThread != null && replayThread.isAlive()) {
|
||||
replayThreadRunning = false;
|
||||
replayThread.interrupt();
|
||||
}
|
||||
replayThread = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a replay to view.
|
||||
* @param replay the replay
|
||||
*/
|
||||
public void setReplay(Replay replay) { this.replay = replay; }
|
||||
public void setReplay(Replay replay) {
|
||||
this.isReplay = true;
|
||||
this.replay = replay;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -239,6 +239,8 @@ public class OpenALStreamPlayer {
|
|||
return;
|
||||
}
|
||||
|
||||
long playedPos_ = playedPos;
|
||||
long lastUpdateTime_ = lastUpdateTime;
|
||||
int processed = AL10.alGetSourcei(source, AL10.AL_BUFFERS_PROCESSED);
|
||||
while (processed > 0) {
|
||||
unqueued.clear();
|
||||
|
@ -248,11 +250,11 @@ public class OpenALStreamPlayer {
|
|||
|
||||
int bufferLength = AL10.alGetBufferi(bufferIndex, AL10.AL_SIZE);
|
||||
|
||||
playedPos += bufferLength;
|
||||
lastUpdateTime = getTime();
|
||||
playedPos_ += bufferLength;
|
||||
lastUpdateTime_ = getTime();
|
||||
|
||||
if (musicLength > 0 && playedPos > musicLength)
|
||||
playedPos -= musicLength;
|
||||
if (musicLength > 0 && playedPos_ > musicLength)
|
||||
playedPos_ -= musicLength;
|
||||
|
||||
if (stream(bufferIndex)) {
|
||||
AL10.alSourceQueueBuffers(source, unqueued);
|
||||
|
@ -264,6 +266,8 @@ public class OpenALStreamPlayer {
|
|||
}
|
||||
processed--;
|
||||
}
|
||||
playedPos = playedPos_;
|
||||
lastUpdateTime = lastUpdateTime_;
|
||||
|
||||
int state = AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE);
|
||||
|
||||
|
@ -382,7 +386,7 @@ public class OpenALStreamPlayer {
|
|||
* @return The current position in seconds.
|
||||
*/
|
||||
public float getPosition() {
|
||||
float playedTime = ((float) playedPos / (float) sampleSize) / sampleRate;
|
||||
float playedTime = ((float) playedPos / sampleSize) / sampleRate;
|
||||
float timePosition = playedTime + (getTime() - lastUpdateTime) / 1000f;
|
||||
// + AL10.alGetSourcef(source, AL11.AL_SEC_OFFSET);
|
||||
return timePosition;
|
||||
|
|
Loading…
Reference in New Issue
Block a user