modules/squirrel-quarrel/src/main/java/net/multiphasicapps/squirrelquarrel/game/GameLooper.java
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
// Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------
package net.multiphasicapps.squirrelquarrel.game;
import cc.squirreljme.runtime.cldc.debug.Debugging;
import java.io.InputStream;
import java.io.OutputStream;
import net.multiphasicapps.squirrelquarrel.ui.FrameSync;
import net.multiphasicapps.squirrelquarrel.util.ReplayEventSource;
import net.multiphasicapps.squirrelquarrel.util.ReplayInputStream;
import net.multiphasicapps.squirrelquarrel.util.ReplayOutputStream;
/**
* This manages and runs the game loop.
*
* @since 2018/03/19
*/
public final class GameLooper
{
/** The output for replay recordings. */
protected final ReplayOutputStream record;
/** The game to loop for. */
protected final Game game;
/** The source where events come from. */
private EventSource _events;
/** The speed the game runs at. */
private volatile GameSpeed _speed =
GameSpeed.NORMAL;
/**
* Initializes the game looper with the default settings.
*
* @param __out The stream to write replay data to.
* @param __evs The source where events come from.
* @throws NullPointerException On null arguments.
* @since 2018/03/18
*/
public GameLooper(OutputStream __out, EventSource __evs)
throws NullPointerException
{
this(__out, __evs, new InitialSettingsBuilder().build());
}
/**
* Initializes the game looper.
*
* @param __out The stream to write replay data to.
* @param __evs The source where events come from.
* @param __i The initial settings for the game.
* @throws NullPointerException On null arguments.
* @since 2018/03/18
*/
public GameLooper(OutputStream __out, EventSource __evs,
InitialSettings __i)
throws NullPointerException
{
if (__out == null || __i == null)
throw new NullPointerException("NARG");
// Setup replay output for consistent game recording
ReplayOutputStream record = new ReplayOutputStream(__out);
this.record = record;
__i.demoRecord(record);
// Initialize the game with basic settings
this.game = new Game(__i);
// Set event source which is used per-frame to control players
this._events = __evs;
}
/**
* Returns the event source in use.
*
* @return The event source in use.
* @since 2019/03/24
*/
public final EventSource eventSource()
{
return this._events;
}
/**
* Returns the game this provides a loop for.
*
* @return The game being looped.
* @since 2018/03/18
*/
public final Game game()
{
return this.game;
}
/**
* Runs multiple game frames.
*
* @param __fs Callback for when game frames are updated.
* @throws NullPointerException On null arguments.
* @since 2017/02/10
*/
public void run(FrameSync __fs)
throws NullPointerException
{
if (__fs == null)
throw new NullPointerException("NARG");
Game game = this.game;
for (;;)
{
// Get the current game speed and entry time
GameSpeed speed = this._speed;
long enter = System.nanoTime();
// Run a single game cycle
int nowframe = game.frameCount();
game.run();
// Debug
Debugging.debugNote("Ran %d", nowframe);
// Request a repaint if there is enough time to draw
long exit = System.nanoTime();
if ((exit - enter) < speed.nanoFrameTime())
__fs.frameRepaintRequest(nowframe);
// Delay thread for the next frame
exit = System.nanoTime();
long durr = (speed.nanoFrameTime() - (exit - enter)) / 1_000_000L;
if (durr > 0)
try
{
Thread.sleep(durr);
}
catch (InterruptedException e)
{
}
}
}
/**
* Runs a single game frame.
*
* @since 2018/03/19
*/
public void runFrame()
{
}
/**
* Sets the event source.
*
* @param __es The event source to use.
* @throws NullPointerException On null arguments.
* @since 2019/03/24
*/
public final void setEventSource(EventSource __es)
throws NullPointerException
{
if (__es == null)
throw new NullPointerException("NARG");
this._events = __es;
}
/**
* Initialize a game loop which either resumes the game from the given
* point or plays it back in a replay.
*
* @param __out The output for replay data.
* @param __rm How will the game be resumed?
* @param __in The input stream for replay/save data.
* @return The loop.
* @throws NullPointerException On null arguments.
* @since 2018/03/19
*/
public static final GameLooper resume(OutputStream __out,
ResumeMode __rm, InputStream __in)
throws NullPointerException
{
if (__out == null || __rm == null || __in == null)
throw new NullPointerException("NARG");
// Setup input replay strema
ReplayInputStream replay = new ReplayInputStream(__in);
// Read initial settings from the replay and initialize the game
InitialSettings init = InitialSettings.demoReplay(replay);
GameLooper rv = new GameLooper(__out,
new ReplayEventSource(replay), init);
return rv;
}
}