parent
af8535620b
commit
dbc32c4d0a
@ -1,133 +1,133 @@ |
||||
package junk; |
||||
|
||||
|
||||
import java.io.File; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.logging.Level; |
||||
|
||||
import mightypork.gamecore.core.AppBackend; |
||||
import mightypork.gamecore.resources.ResourceInitializer; |
||||
import mightypork.gamecore.resources.loading.AsyncResourceLoader; |
||||
import mightypork.gamecore.resources.loading.ResourceLoader; |
||||
|
||||
|
||||
/** |
||||
* Init options holder class
|
||||
*/ |
||||
public class AppInitOptions { |
||||
|
||||
String logDir = "log"; |
||||
String logFilePrefix = "runtime"; |
||||
|
||||
String screenshotDir = "screenshots"; |
||||
|
||||
boolean busLogging = false; |
||||
|
||||
String configFile = "settings.cfg"; |
||||
String configComment = "Main config file"; |
||||
|
||||
final List<ResourceInitializer> resourceLists = new ArrayList<>(); |
||||
final List<KeySetup> keyLists = new ArrayList<>(); |
||||
final List<ConfigSetup> configLists = new ArrayList<>(); |
||||
|
||||
ResourceLoader resourceLoader = new AsyncResourceLoader(); |
||||
Level logLevel = Level.ALL; |
||||
public boolean sigleInstance = true; |
||||
Level logSoutLevel = Level.ALL; |
||||
|
||||
|
||||
public void setConfigFile(String filename, String comment) |
||||
{ |
||||
configFile = filename; |
||||
configComment = comment; |
||||
} |
||||
|
||||
|
||||
public void addConfig(ConfigSetup cfg) |
||||
{ |
||||
configLists.add(cfg); |
||||
} |
||||
|
||||
|
||||
public void addKeys(KeySetup keys) |
||||
{ |
||||
keyLists.add(keys); |
||||
} |
||||
|
||||
|
||||
public void addResources(ResourceInitializer res) |
||||
{ |
||||
resourceLists.add(res); |
||||
} |
||||
|
||||
|
||||
public void setBackend(AppBackend backend) |
||||
{ |
||||
this.backend = backend; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Set whether to run in single instance mode, or allow multiple instances.<br> |
||||
* Multiple instances running can cause various collisions (eg. when writing |
||||
* config file or logging). |
||||
* |
||||
* @param sigleInstance true to allow only one instance |
||||
*/ |
||||
public void setSigleInstance(boolean sigleInstance) |
||||
{ |
||||
this.sigleInstance = sigleInstance; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Set working directory path. If not exists, it will be created. |
||||
* |
||||
* @param workdir work dir path |
||||
*/ |
||||
public void setWorkdir(File workdir) |
||||
{ |
||||
this.workdir = workdir; |
||||
} |
||||
|
||||
|
||||
public void setBusLogging(boolean yes) |
||||
{ |
||||
busLogging = yes; |
||||
} |
||||
|
||||
|
||||
public void setLogOptions(String logDir, String filePrefix, int archivedCount, Level logLevel) |
||||
{ |
||||
this.logDir = logDir; |
||||
this.logFilePrefix = filePrefix; |
||||
this.logArchiveCount = archivedCount; |
||||
this.logLevel = logLevel; |
||||
} |
||||
|
||||
|
||||
public void setResourceLoader(ResourceLoader resLoader) |
||||
{ |
||||
resourceLoader = resLoader; |
||||
} |
||||
|
||||
|
||||
public void setScreenshotDir(String path) |
||||
{ |
||||
this.screenshotDir = path; |
||||
} |
||||
|
||||
|
||||
public void setLockFile(String lockFile) |
||||
{ |
||||
this.lockFile = lockFile; |
||||
} |
||||
|
||||
|
||||
public void setLogLevel(Level logLevel, Level soutLevel) |
||||
{ |
||||
this.logLevel = logLevel; |
||||
this.logSoutLevel = soutLevel; |
||||
} |
||||
} |
||||
//package junk;
|
||||
//
|
||||
//
|
||||
//import java.io.File;
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.List;
|
||||
//import java.util.logging.Level;
|
||||
//
|
||||
//import mightypork.gamecore.core.AppBackend;
|
||||
//import mightypork.gamecore.resources.ResourceInitializer;
|
||||
//import mightypork.gamecore.resources.loading.AsyncResourceLoader;
|
||||
//import mightypork.gamecore.resources.loading.ResourceLoader;
|
||||
//
|
||||
//
|
||||
///**
|
||||
// * Init options holder class
|
||||
// */
|
||||
//public class AppInitOptions {
|
||||
//
|
||||
// String logDir = "log";
|
||||
// String logFilePrefix = "runtime";
|
||||
//
|
||||
// String screenshotDir = "screenshots";
|
||||
//
|
||||
// boolean busLogging = false;
|
||||
//
|
||||
// String configFile = "settings.cfg";
|
||||
// String configComment = "Main config file";
|
||||
//
|
||||
// final List<ResourceInitializer> resourceLists = new ArrayList<>();
|
||||
// final List<KeySetup> keyLists = new ArrayList<>();
|
||||
// final List<ConfigSetup> configLists = new ArrayList<>();
|
||||
//
|
||||
// ResourceLoader resourceLoader = new AsyncResourceLoader();
|
||||
// Level logLevel = Level.ALL;
|
||||
// public boolean sigleInstance = true;
|
||||
// Level logSoutLevel = Level.ALL;
|
||||
//
|
||||
//
|
||||
// public void setConfigFile(String filename, String comment)
|
||||
// {
|
||||
// configFile = filename;
|
||||
// configComment = comment;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void addConfig(ConfigSetup cfg)
|
||||
// {
|
||||
// configLists.add(cfg);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void addKeys(KeySetup keys)
|
||||
// {
|
||||
// keyLists.add(keys);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void addResources(ResourceInitializer res)
|
||||
// {
|
||||
// resourceLists.add(res);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setBackend(AppBackend backend)
|
||||
// {
|
||||
// this.backend = backend;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Set whether to run in single instance mode, or allow multiple instances.<br>
|
||||
// * Multiple instances running can cause various collisions (eg. when writing
|
||||
// * config file or logging).
|
||||
// *
|
||||
// * @param sigleInstance true to allow only one instance
|
||||
// */
|
||||
// public void setSigleInstance(boolean sigleInstance)
|
||||
// {
|
||||
// this.sigleInstance = sigleInstance;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Set working directory path. If not exists, it will be created.
|
||||
// *
|
||||
// * @param workdir work dir path
|
||||
// */
|
||||
// public void setWorkdir(File workdir)
|
||||
// {
|
||||
// this.workdir = workdir;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setBusLogging(boolean yes)
|
||||
// {
|
||||
// busLogging = yes;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setLogOptions(String logDir, String filePrefix, int archivedCount, Level logLevel)
|
||||
// {
|
||||
// this.logDir = logDir;
|
||||
// this.logFilePrefix = filePrefix;
|
||||
// this.logArchiveCount = archivedCount;
|
||||
// this.logLevel = logLevel;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setResourceLoader(ResourceLoader resLoader)
|
||||
// {
|
||||
// resourceLoader = resLoader;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setScreenshotDir(String path)
|
||||
// {
|
||||
// this.screenshotDir = path;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setLockFile(String lockFile)
|
||||
// {
|
||||
// this.lockFile = lockFile;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setLogLevel(Level logLevel, Level soutLevel)
|
||||
// {
|
||||
// this.logLevel = logLevel;
|
||||
// this.logSoutLevel = soutLevel;
|
||||
// }
|
||||
//}
|
||||
|
@ -1,184 +1,185 @@ |
||||
package junk; |
||||
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler; |
||||
|
||||
import mightypork.gamecore.core.App; |
||||
import mightypork.gamecore.core.AppBackend; |
||||
import mightypork.gamecore.core.MainLoop; |
||||
import mightypork.gamecore.core.config.Config; |
||||
import mightypork.gamecore.gui.screens.ScreenRegistry; |
||||
import mightypork.gamecore.gui.screens.impl.CrossfadeOverlay; |
||||
import mightypork.gamecore.resources.Res; |
||||
import mightypork.gamecore.resources.ResourceInitializer; |
||||
import mightypork.utils.files.WorkDir; |
||||
import mightypork.utils.logging.Log; |
||||
|
||||
|
||||
/** |
||||
* Basic screen-based game with subsystems.<br> |
||||
* This class takes care of the initialization sequence. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public abstract class BaseApp extends App implements UncaughtExceptionHandler { |
||||
|
||||
// modules
|
||||
private MainLoop gameLoop; |
||||
private ScreenRegistry screenRegistry; |
||||
|
||||
private boolean started = false; |
||||
private boolean lockObtained = false; |
||||
|
||||
// init opt holder
|
||||
private final AppInitOptions opt = new AppInitOptions(); |
||||
|
||||
|
||||
/** |
||||
* Get init options |
||||
* |
||||
* @return opt holder |
||||
*/ |
||||
public AppInitOptions getInitOptions() |
||||
{ |
||||
if (started) { |
||||
throw new IllegalStateException("Cannot alter init options after starting the App."); |
||||
} |
||||
|
||||
return opt; |
||||
} |
||||
|
||||
|
||||
public BaseApp(AppBackend backend) |
||||
{ |
||||
super(backend); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Start the application |
||||
*/ |
||||
@Override |
||||
public final void start() |
||||
{ |
||||
initialize(); |
||||
|
||||
Log.i("Starting main loop..."); |
||||
|
||||
// open first screen !!!
|
||||
started = true; |
||||
gameLoop.start(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Init the app |
||||
*/ |
||||
protected void initialize() |
||||
{ |
||||
WorkDir.setBaseDir(opt.workdir); |
||||
|
||||
if (opt.sigleInstance) initLock(); |
||||
lockObtained = true; |
||||
|
||||
for (final RouteSetup rs : opt.routeLists) { |
||||
WorkDir.registerRoutes(rs); |
||||
} |
||||
WorkDir.addPath("_screenshot_dir", opt.screenshotDir); |
||||
|
||||
// apply configurations
|
||||
Config.init(WorkDir.getFile(opt.configFile), opt.configComment); |
||||
|
||||
// add keys to config
|
||||
for (final KeySetup l : opt.keyLists) { |
||||
Config.registerKeys(l); |
||||
} |
||||
|
||||
// add options to config
|
||||
for (final ConfigSetup c : opt.configLists) { |
||||
Config.registerOptions(c); |
||||
} |
||||
Config.load(); |
||||
|
||||
/* |
||||
* Display |
||||
*/ |
||||
Log.f2("Initializing Display System..."); |
||||
initDisplay(gfx()); |
||||
|
||||
/* |
||||
* Audio |
||||
*/ |
||||
Log.f2("Initializing Sound System..."); |
||||
soundSystem = new SoundSystem(this); |
||||
initSoundSystem(soundSystem); |
||||
|
||||
/* |
||||
* Input |
||||
*/ |
||||
Log.f2("Initializing Input System..."); |
||||
inputSystem = new LwjglInputModule(this); |
||||
initInputSystem(inputSystem); |
||||
|
||||
/* |
||||
* Prepare main loop |
||||
*/ |
||||
Log.f1("Creating Screen Registry and Game Loop..."); |
||||
screenRegistry = new ScreenRegistry(this); |
||||
gameLoop = createMainLoop(); |
||||
gameLoop.setRootRenderable(screenRegistry); |
||||
|
||||
/* |
||||
* Load resources |
||||
* |
||||
* Resources should be registered to registries, and AsyncResourceLoader will load them. |
||||
*/ |
||||
Log.f1("Loading resources..."); |
||||
if (opt.resourceLoader != null) { |
||||
opt.resourceLoader.setBaseDir(this); |
||||
} |
||||
|
||||
Res.setBaseDir(this); |
||||
|
||||
for (final ResourceInitializer rl : opt.resourceLists) { |
||||
Res.load(rl); |
||||
} |
||||
|
||||
/* |
||||
* Screen registry |
||||
* |
||||
* Must be after resources, because screens can request them during instantiation. |
||||
*/ |
||||
Log.f2("Registering screens..."); |
||||
initScreens(screenRegistry); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Register game screens to the registry. |
||||
* |
||||
* @param screens |
||||
*/ |
||||
protected void initScreens(ScreenRegistry screens) |
||||
{ |
||||
screens.addOverlay(new CrossfadeOverlay(this)); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Create game loop instance |
||||
* |
||||
* @return the game loop. |
||||
*/ |
||||
protected MainLoop createMainLoop() |
||||
{ |
||||
return new MainLoop(this); |
||||
} |
||||
|
||||
|
||||
protected void beforeShutdown() |
||||
{ |
||||
// ???
|
||||
if (lockObtained) Config.save(); |
||||
} |
||||
} |
||||
//package junk;
|
||||
//
|
||||
//
|
||||
//import java.lang.Thread.UncaughtExceptionHandler;
|
||||
//
|
||||
//import mightypork.gamecore.core.App;
|
||||
//import mightypork.gamecore.core.AppBackend;
|
||||
//import mightypork.gamecore.core.MainLoop;
|
||||
//import mightypork.gamecore.core.DeltaMainLoop;
|
||||
//import mightypork.gamecore.core.config.Config;
|
||||
//import mightypork.gamecore.gui.screens.ScreenRegistry;
|
||||
//import mightypork.gamecore.gui.screens.impl.CrossfadeOverlay;
|
||||
//import mightypork.gamecore.resources.Res;
|
||||
//import mightypork.gamecore.resources.ResourceInitializer;
|
||||
//import mightypork.utils.files.WorkDir;
|
||||
//import mightypork.utils.logging.Log;
|
||||
//
|
||||
//
|
||||
///**
|
||||
// * Basic screen-based game with subsystems.<br>
|
||||
// * This class takes care of the initialization sequence.
|
||||
// *
|
||||
// * @author Ondřej Hruška (MightyPork)
|
||||
// */
|
||||
//public abstract class BaseApp extends App implements UncaughtExceptionHandler {
|
||||
//
|
||||
// // modules
|
||||
// private MainLoop gameLoop;
|
||||
// private ScreenRegistry screenRegistry;
|
||||
//
|
||||
// private boolean started = false;
|
||||
// private boolean lockObtained = false;
|
||||
//
|
||||
// // init opt holder
|
||||
// private final AppInitOptions opt = new AppInitOptions();
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Get init options
|
||||
// *
|
||||
// * @return opt holder
|
||||
// */
|
||||
// public AppInitOptions getInitOptions()
|
||||
// {
|
||||
// if (started) {
|
||||
// throw new IllegalStateException("Cannot alter init options after starting the App.");
|
||||
// }
|
||||
//
|
||||
// return opt;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public BaseApp(AppBackend backend)
|
||||
// {
|
||||
// super(backend);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Start the application
|
||||
// */
|
||||
// @Override
|
||||
// public final void start()
|
||||
// {
|
||||
// initialize();
|
||||
//
|
||||
// Log.i("Starting main loop...");
|
||||
//
|
||||
// // open first screen !!!
|
||||
// started = true;
|
||||
// gameLoop.start();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Init the app
|
||||
// */
|
||||
// protected void initialize()
|
||||
// {
|
||||
// WorkDir.setBaseDir(opt.workdir);
|
||||
//
|
||||
// if (opt.sigleInstance) initLock();
|
||||
// lockObtained = true;
|
||||
//
|
||||
// for (final RouteSetup rs : opt.routeLists) {
|
||||
// WorkDir.registerRoutes(rs);
|
||||
// }
|
||||
// WorkDir.addPath("_screenshot_dir", opt.screenshotDir);
|
||||
//
|
||||
// // apply configurations
|
||||
// Config.init(WorkDir.getFile(opt.configFile), opt.configComment);
|
||||
//
|
||||
// // add keys to config
|
||||
// for (final KeySetup l : opt.keyLists) {
|
||||
// Config.registerKeys(l);
|
||||
// }
|
||||
//
|
||||
// // add options to config
|
||||
// for (final ConfigSetup c : opt.configLists) {
|
||||
// Config.registerOptions(c);
|
||||
// }
|
||||
// Config.load();
|
||||
//
|
||||
// /*
|
||||
// * Display
|
||||
// */
|
||||
// Log.f2("Initializing Display System...");
|
||||
// initDisplay(gfx());
|
||||
//
|
||||
// /*
|
||||
// * Audio
|
||||
// */
|
||||
// Log.f2("Initializing Sound System...");
|
||||
// soundSystem = new SoundSystem(this);
|
||||
// initSoundSystem(soundSystem);
|
||||
//
|
||||
// /*
|
||||
// * Input
|
||||
// */
|
||||
// Log.f2("Initializing Input System...");
|
||||
// inputSystem = new LwjglInputModule(this);
|
||||
// initInputSystem(inputSystem);
|
||||
//
|
||||
// /*
|
||||
// * Prepare main loop
|
||||
// */
|
||||
// Log.f1("Creating Screen Registry and Game Loop...");
|
||||
// screenRegistry = new ScreenRegistry(this);
|
||||
// gameLoop = createMainLoop();
|
||||
// gameLoop.setRootRenderable(screenRegistry);
|
||||
//
|
||||
// /*
|
||||
// * Load resources
|
||||
// *
|
||||
// * Resources should be registered to registries, and AsyncResourceLoader will load them.
|
||||
// */
|
||||
// Log.f1("Loading resources...");
|
||||
// if (opt.resourceLoader != null) {
|
||||
// opt.resourceLoader.setBaseDir(this);
|
||||
// }
|
||||
//
|
||||
// Res.setBaseDir(this);
|
||||
//
|
||||
// for (final ResourceInitializer rl : opt.resourceLists) {
|
||||
// Res.load(rl);
|
||||
// }
|
||||
//
|
||||
// /*
|
||||
// * Screen registry
|
||||
// *
|
||||
// * Must be after resources, because screens can request them during instantiation.
|
||||
// */
|
||||
// Log.f2("Registering screens...");
|
||||
// initScreens(screenRegistry);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Register game screens to the registry.
|
||||
// *
|
||||
// * @param screens
|
||||
// */
|
||||
// protected void initScreens(ScreenRegistry screens)
|
||||
// {
|
||||
// screens.addOverlay(new CrossfadeOverlay(this));
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Create game loop instance
|
||||
// *
|
||||
// * @return the game loop.
|
||||
// */
|
||||
// protected MainLoop createMainLoop()
|
||||
// {
|
||||
// return new DeltaMainLoop(this);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// protected void beforeShutdown()
|
||||
// {
|
||||
// // ???
|
||||
// if (lockObtained) Config.save();
|
||||
// }
|
||||
//}
|
||||
|
@ -0,0 +1,128 @@ |
||||
package mightypork.gamecore.core; |
||||
|
||||
|
||||
import java.util.Deque; |
||||
import java.util.concurrent.ConcurrentLinkedDeque; |
||||
|
||||
import mightypork.gamecore.graphics.Renderable; |
||||
import mightypork.utils.annotations.Stub; |
||||
import mightypork.utils.eventbus.clients.BusNode; |
||||
import mightypork.utils.eventbus.events.UpdateEvent; |
||||
import mightypork.utils.logging.Log; |
||||
import mightypork.utils.math.timing.Profiler; |
||||
import mightypork.utils.math.timing.TimerDelta; |
||||
|
||||
|
||||
/** |
||||
* Delta-timed game loop with task queue etc. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public class DeltaMainLoop extends BusNode implements MainLoop { |
||||
|
||||
/** |
||||
* Max time spent on main loop tasks per cycle (s) |
||||
*/ |
||||
protected double MAX_TIME_TASKS = 1 / 30D; |
||||
|
||||
/** |
||||
* Max delta time (s) per frame.<br> |
||||
* If delta is larger than this, it's clamped to it. |
||||
*/ |
||||
protected double MAX_DELTA = 1 / 20D; |
||||
|
||||
private final Deque<Runnable> tasks = new ConcurrentLinkedDeque<>(); |
||||
private TimerDelta timer; |
||||
private Renderable rootRenderable; |
||||
private volatile boolean running = true; |
||||
|
||||
|
||||
@Override |
||||
public void setRootRenderable(Renderable rootRenderable) |
||||
{ |
||||
this.rootRenderable = rootRenderable; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void start() |
||||
{ |
||||
timer = new TimerDelta(); |
||||
|
||||
while (running) { |
||||
App.gfx().beginFrame(); |
||||
|
||||
double delta = timer.getDelta(); |
||||
if (delta > MAX_DELTA) { |
||||
Log.f3("(timing) Clamping delta: was " + delta + " s, MAX_DELTA = " + MAX_DELTA + " s"); |
||||
delta = MAX_DELTA; |
||||
} |
||||
|
||||
// dispatch update event
|
||||
App.bus().sendDirect(new UpdateEvent(delta)); |
||||
|
||||
// run main loop tasks
|
||||
Runnable r; |
||||
final long t = Profiler.begin(); |
||||
|
||||
while ((r = tasks.poll()) != null) { |
||||
Log.f3(" * Main loop task."); |
||||
r.run(); |
||||
|
||||
if (Profiler.end(t) > MAX_TIME_TASKS) { |
||||
Log.f3("! Time's up, postponing task to next cycle."); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
beforeRender(); |
||||
|
||||
if (rootRenderable != null) { |
||||
rootRenderable.render(); |
||||
} |
||||
|
||||
afterRender(); |
||||
|
||||
App.gfx().endFrame(); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Called before render |
||||
*/ |
||||
@Stub |
||||
protected void beforeRender() |
||||
{ |
||||
//
|
||||
} |
||||
|
||||
|
||||
/** |
||||
* Called after render |
||||
*/ |
||||
@Stub |
||||
protected void afterRender() |
||||
{ |
||||
//
|
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void destroy() |
||||
{ |
||||
running = false; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public synchronized void queueTask(Runnable task, boolean skipQueue) |
||||
{ |
||||
if (skipQueue) { |
||||
tasks.addFirst(task); |
||||
} else { |
||||
tasks.addLast(task); |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,143 +1,45 @@ |
||||
package mightypork.gamecore.core; |
||||
|
||||
|
||||
import java.util.Deque; |
||||
import java.util.concurrent.ConcurrentLinkedDeque; |
||||
|
||||
import mightypork.gamecore.graphics.Renderable; |
||||
import mightypork.gamecore.gui.screens.ScreenRegistry; |
||||
import mightypork.utils.annotations.Stub; |
||||
import mightypork.utils.eventbus.clients.BusNode; |
||||
import mightypork.utils.eventbus.events.UpdateEvent; |
||||
import mightypork.utils.interfaces.Destroyable; |
||||
import mightypork.utils.logging.Log; |
||||
import mightypork.utils.math.timing.Profiler; |
||||
import mightypork.utils.math.timing.TimerDelta; |
||||
|
||||
|
||||
/** |
||||
* Delta-timed game loop with task queue etc. |
||||
* A main loop of the app. |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public class MainLoop extends BusNode implements Destroyable { |
||||
|
||||
/** |
||||
* Max time spent on main loop tasks per cycle (s) |
||||
*/ |
||||
protected double MAX_TIME_TASKS = 1 / 30D; |
||||
|
||||
/** |
||||
* Max delta time (s) per frame.<br> |
||||
* If delta is larger than this, it's clamped to it. |
||||
*/ |
||||
protected double MAX_DELTA = 1 / 20D; |
||||
|
||||
private final Deque<Runnable> tasks = new ConcurrentLinkedDeque<>(); |
||||
private TimerDelta timer; |
||||
private Renderable rootRenderable; |
||||
private volatile boolean running = true; |
||||
|
||||
|
||||
public interface MainLoop extends Destroyable { |
||||
|
||||
/** |
||||
* Set primary renderable |
||||
* |
||||
* @param rootRenderable main {@link Renderable}, typically a |
||||
* {@link ScreenRegistry} |
||||
*/ |
||||
public void setRootRenderable(Renderable rootRenderable) |
||||
{ |
||||
this.rootRenderable = rootRenderable; |
||||
} |
||||
|
||||
|
||||
public abstract void setRootRenderable(Renderable rootRenderable); |
||||
|
||||
|
||||
/** |
||||
* Start the loop |
||||
* Start the loop. The loop should be terminated when the destroy() method |
||||
* is called. |
||||
*/ |
||||
public void start() |
||||
{ |
||||
timer = new TimerDelta(); |
||||
|
||||
while (running) { |
||||
App.gfx().beginFrame(); |
||||
|
||||
double delta = timer.getDelta(); |
||||
if (delta > MAX_DELTA) { |
||||
Log.f3("(timing) Clamping delta: was " + delta + " s, MAX_DELTA = " + MAX_DELTA + " s"); |
||||
delta = MAX_DELTA; |
||||
} |
||||
|
||||
// dispatch update event
|
||||
App.bus().sendDirect(new UpdateEvent(delta)); |
||||
|
||||
// run main loop tasks
|
||||
Runnable r; |
||||
final long t = Profiler.begin(); |
||||
|
||||
while ((r = tasks.poll()) != null) { |
||||
Log.f3(" * Main loop task."); |
||||
r.run(); |
||||
|
||||
if (Profiler.end(t) > MAX_TIME_TASKS) { |
||||
Log.f3("! Time's up, postponing task to next cycle."); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
beforeRender(); |
||||
|
||||
if (rootRenderable != null) { |
||||
rootRenderable.render(); |
||||
} |
||||
|
||||
afterRender(); |
||||
|
||||
App.gfx().endFrame(); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Called before render |
||||
*/ |
||||
@Stub |
||||
protected void beforeRender() |
||||
{ |
||||
//
|
||||
} |
||||
|
||||
|
||||
/** |
||||
* Called after render |
||||
*/ |
||||
@Stub |
||||
protected void afterRender() |
||||
{ |
||||
//
|
||||
} |
||||
|
||||
|
||||
public abstract void start(); |
||||
|
||||
|
||||
@Override |
||||
public void destroy() |
||||
{ |
||||
running = false; |
||||
} |
||||
|
||||
|
||||
public abstract void destroy(); |
||||
|
||||
|
||||
/** |
||||
* Add a task to queue to be executed in the main loop (in rendering |
||||
* context) |
||||
* context). This may be eg. loading textures. |
||||
* |
||||
* @param task task |
||||
* @param skipQueue true to skip the queue |
||||
*/ |
||||
public synchronized void queueTask(Runnable task, boolean skipQueue) |
||||
{ |
||||
if (skipQueue) { |
||||
tasks.addFirst(task); |
||||
} else { |
||||
tasks.addLast(task); |
||||
} |
||||
} |
||||
|
||||
public abstract void queueTask(Runnable task, boolean skipQueue); |
||||
|
||||
} |
||||
|
@ -0,0 +1,40 @@ |
||||
package mightypork.gamecore.core.init; |
||||
|
||||
|
||||
import mightypork.gamecore.core.MainLoop; |
||||
|
||||
|
||||
/** |
||||
* Task to add a resource loader.<br> |
||||
* By default the async resource loader is used |
||||
* |
||||
* @author Ondřej Hruška (MightyPork) |
||||
*/ |
||||
public abstract class InitTaskMainLoop extends InitTask { |
||||
|
||||
/** The loader. */ |
||||
protected MainLoop loop; |
||||
|
||||
|
||||
@Override |
||||
public void run() |
||||
{ |
||||
loop = getLoopImpl(); |
||||
app.setMainLoop(loop); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Create a loader impl |
||||
* |
||||
* @return loader |
||||
*/ |
||||
protected abstract MainLoop getLoopImpl(); |
||||
|
||||
|
||||
@Override |
||||
public String getName() |
||||
{ |
||||
return "resource_loader"; |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue