Support for main loop and confignsaving on exit.
This commit is contained in:
+133
-133
@@ -1,133 +1,133 @@
|
|||||||
package junk;
|
//package junk;
|
||||||
|
//
|
||||||
|
//
|
||||||
import java.io.File;
|
//import java.io.File;
|
||||||
import java.util.ArrayList;
|
//import java.util.ArrayList;
|
||||||
import java.util.List;
|
//import java.util.List;
|
||||||
import java.util.logging.Level;
|
//import java.util.logging.Level;
|
||||||
|
//
|
||||||
import mightypork.gamecore.core.AppBackend;
|
//import mightypork.gamecore.core.AppBackend;
|
||||||
import mightypork.gamecore.resources.ResourceInitializer;
|
//import mightypork.gamecore.resources.ResourceInitializer;
|
||||||
import mightypork.gamecore.resources.loading.AsyncResourceLoader;
|
//import mightypork.gamecore.resources.loading.AsyncResourceLoader;
|
||||||
import mightypork.gamecore.resources.loading.ResourceLoader;
|
//import mightypork.gamecore.resources.loading.ResourceLoader;
|
||||||
|
//
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* Init options holder class
|
// * Init options holder class
|
||||||
*/
|
// */
|
||||||
public class AppInitOptions {
|
//public class AppInitOptions {
|
||||||
|
//
|
||||||
String logDir = "log";
|
// String logDir = "log";
|
||||||
String logFilePrefix = "runtime";
|
// String logFilePrefix = "runtime";
|
||||||
|
//
|
||||||
String screenshotDir = "screenshots";
|
// String screenshotDir = "screenshots";
|
||||||
|
//
|
||||||
boolean busLogging = false;
|
// boolean busLogging = false;
|
||||||
|
//
|
||||||
String configFile = "settings.cfg";
|
// String configFile = "settings.cfg";
|
||||||
String configComment = "Main config file";
|
// String configComment = "Main config file";
|
||||||
|
//
|
||||||
final List<ResourceInitializer> resourceLists = new ArrayList<>();
|
// final List<ResourceInitializer> resourceLists = new ArrayList<>();
|
||||||
final List<KeySetup> keyLists = new ArrayList<>();
|
// final List<KeySetup> keyLists = new ArrayList<>();
|
||||||
final List<ConfigSetup> configLists = new ArrayList<>();
|
// final List<ConfigSetup> configLists = new ArrayList<>();
|
||||||
|
//
|
||||||
ResourceLoader resourceLoader = new AsyncResourceLoader();
|
// ResourceLoader resourceLoader = new AsyncResourceLoader();
|
||||||
Level logLevel = Level.ALL;
|
// Level logLevel = Level.ALL;
|
||||||
public boolean sigleInstance = true;
|
// public boolean sigleInstance = true;
|
||||||
Level logSoutLevel = Level.ALL;
|
// Level logSoutLevel = Level.ALL;
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setConfigFile(String filename, String comment)
|
// public void setConfigFile(String filename, String comment)
|
||||||
{
|
// {
|
||||||
configFile = filename;
|
// configFile = filename;
|
||||||
configComment = comment;
|
// configComment = comment;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void addConfig(ConfigSetup cfg)
|
// public void addConfig(ConfigSetup cfg)
|
||||||
{
|
// {
|
||||||
configLists.add(cfg);
|
// configLists.add(cfg);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void addKeys(KeySetup keys)
|
// public void addKeys(KeySetup keys)
|
||||||
{
|
// {
|
||||||
keyLists.add(keys);
|
// keyLists.add(keys);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void addResources(ResourceInitializer res)
|
// public void addResources(ResourceInitializer res)
|
||||||
{
|
// {
|
||||||
resourceLists.add(res);
|
// resourceLists.add(res);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setBackend(AppBackend backend)
|
// public void setBackend(AppBackend backend)
|
||||||
{
|
// {
|
||||||
this.backend = backend;
|
// this.backend = backend;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Set whether to run in single instance mode, or allow multiple instances.<br>
|
// * Set whether to run in single instance mode, or allow multiple instances.<br>
|
||||||
* Multiple instances running can cause various collisions (eg. when writing
|
// * Multiple instances running can cause various collisions (eg. when writing
|
||||||
* config file or logging).
|
// * config file or logging).
|
||||||
*
|
// *
|
||||||
* @param sigleInstance true to allow only one instance
|
// * @param sigleInstance true to allow only one instance
|
||||||
*/
|
// */
|
||||||
public void setSigleInstance(boolean sigleInstance)
|
// public void setSigleInstance(boolean sigleInstance)
|
||||||
{
|
// {
|
||||||
this.sigleInstance = sigleInstance;
|
// this.sigleInstance = sigleInstance;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Set working directory path. If not exists, it will be created.
|
// * Set working directory path. If not exists, it will be created.
|
||||||
*
|
// *
|
||||||
* @param workdir work dir path
|
// * @param workdir work dir path
|
||||||
*/
|
// */
|
||||||
public void setWorkdir(File workdir)
|
// public void setWorkdir(File workdir)
|
||||||
{
|
// {
|
||||||
this.workdir = workdir;
|
// this.workdir = workdir;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setBusLogging(boolean yes)
|
// public void setBusLogging(boolean yes)
|
||||||
{
|
// {
|
||||||
busLogging = yes;
|
// busLogging = yes;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setLogOptions(String logDir, String filePrefix, int archivedCount, Level logLevel)
|
// public void setLogOptions(String logDir, String filePrefix, int archivedCount, Level logLevel)
|
||||||
{
|
// {
|
||||||
this.logDir = logDir;
|
// this.logDir = logDir;
|
||||||
this.logFilePrefix = filePrefix;
|
// this.logFilePrefix = filePrefix;
|
||||||
this.logArchiveCount = archivedCount;
|
// this.logArchiveCount = archivedCount;
|
||||||
this.logLevel = logLevel;
|
// this.logLevel = logLevel;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setResourceLoader(ResourceLoader resLoader)
|
// public void setResourceLoader(ResourceLoader resLoader)
|
||||||
{
|
// {
|
||||||
resourceLoader = resLoader;
|
// resourceLoader = resLoader;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setScreenshotDir(String path)
|
// public void setScreenshotDir(String path)
|
||||||
{
|
// {
|
||||||
this.screenshotDir = path;
|
// this.screenshotDir = path;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setLockFile(String lockFile)
|
// public void setLockFile(String lockFile)
|
||||||
{
|
// {
|
||||||
this.lockFile = lockFile;
|
// this.lockFile = lockFile;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setLogLevel(Level logLevel, Level soutLevel)
|
// public void setLogLevel(Level logLevel, Level soutLevel)
|
||||||
{
|
// {
|
||||||
this.logLevel = logLevel;
|
// this.logLevel = logLevel;
|
||||||
this.logSoutLevel = soutLevel;
|
// this.logSoutLevel = soutLevel;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|||||||
+185
-184
@@ -1,184 +1,185 @@
|
|||||||
package junk;
|
//package junk;
|
||||||
|
//
|
||||||
|
//
|
||||||
import java.lang.Thread.UncaughtExceptionHandler;
|
//import java.lang.Thread.UncaughtExceptionHandler;
|
||||||
|
//
|
||||||
import mightypork.gamecore.core.App;
|
//import mightypork.gamecore.core.App;
|
||||||
import mightypork.gamecore.core.AppBackend;
|
//import mightypork.gamecore.core.AppBackend;
|
||||||
import mightypork.gamecore.core.MainLoop;
|
//import mightypork.gamecore.core.MainLoop;
|
||||||
import mightypork.gamecore.core.config.Config;
|
//import mightypork.gamecore.core.DeltaMainLoop;
|
||||||
import mightypork.gamecore.gui.screens.ScreenRegistry;
|
//import mightypork.gamecore.core.config.Config;
|
||||||
import mightypork.gamecore.gui.screens.impl.CrossfadeOverlay;
|
//import mightypork.gamecore.gui.screens.ScreenRegistry;
|
||||||
import mightypork.gamecore.resources.Res;
|
//import mightypork.gamecore.gui.screens.impl.CrossfadeOverlay;
|
||||||
import mightypork.gamecore.resources.ResourceInitializer;
|
//import mightypork.gamecore.resources.Res;
|
||||||
import mightypork.utils.files.WorkDir;
|
//import mightypork.gamecore.resources.ResourceInitializer;
|
||||||
import mightypork.utils.logging.Log;
|
//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.
|
// * Basic screen-based game with subsystems.<br>
|
||||||
*
|
// * This class takes care of the initialization sequence.
|
||||||
* @author Ondřej Hruška (MightyPork)
|
// *
|
||||||
*/
|
// * @author Ondřej Hruška (MightyPork)
|
||||||
public abstract class BaseApp extends App implements UncaughtExceptionHandler {
|
// */
|
||||||
|
//public abstract class BaseApp extends App implements UncaughtExceptionHandler {
|
||||||
// modules
|
//
|
||||||
private MainLoop gameLoop;
|
// // modules
|
||||||
private ScreenRegistry screenRegistry;
|
// private MainLoop gameLoop;
|
||||||
|
// private ScreenRegistry screenRegistry;
|
||||||
private boolean started = false;
|
//
|
||||||
private boolean lockObtained = false;
|
// private boolean started = false;
|
||||||
|
// private boolean lockObtained = false;
|
||||||
// init opt holder
|
//
|
||||||
private final AppInitOptions opt = new AppInitOptions();
|
// // init opt holder
|
||||||
|
// private final AppInitOptions opt = new AppInitOptions();
|
||||||
|
//
|
||||||
/**
|
//
|
||||||
* Get init options
|
// /**
|
||||||
*
|
// * Get init options
|
||||||
* @return opt holder
|
// *
|
||||||
*/
|
// * @return opt holder
|
||||||
public AppInitOptions getInitOptions()
|
// */
|
||||||
{
|
// public AppInitOptions getInitOptions()
|
||||||
if (started) {
|
// {
|
||||||
throw new IllegalStateException("Cannot alter init options after starting the App.");
|
// if (started) {
|
||||||
}
|
// throw new IllegalStateException("Cannot alter init options after starting the App.");
|
||||||
|
// }
|
||||||
return opt;
|
//
|
||||||
}
|
// return opt;
|
||||||
|
// }
|
||||||
|
//
|
||||||
public BaseApp(AppBackend backend)
|
//
|
||||||
{
|
// public BaseApp(AppBackend backend)
|
||||||
super(backend);
|
// {
|
||||||
}
|
// super(backend);
|
||||||
|
// }
|
||||||
|
//
|
||||||
/**
|
//
|
||||||
* Start the application
|
// /**
|
||||||
*/
|
// * Start the application
|
||||||
@Override
|
// */
|
||||||
public final void start()
|
// @Override
|
||||||
{
|
// public final void start()
|
||||||
initialize();
|
// {
|
||||||
|
// initialize();
|
||||||
Log.i("Starting main loop...");
|
//
|
||||||
|
// Log.i("Starting main loop...");
|
||||||
// open first screen !!!
|
//
|
||||||
started = true;
|
// // open first screen !!!
|
||||||
gameLoop.start();
|
// started = true;
|
||||||
}
|
// gameLoop.start();
|
||||||
|
// }
|
||||||
|
//
|
||||||
/**
|
//
|
||||||
* Init the app
|
// /**
|
||||||
*/
|
// * Init the app
|
||||||
protected void initialize()
|
// */
|
||||||
{
|
// protected void initialize()
|
||||||
WorkDir.setBaseDir(opt.workdir);
|
// {
|
||||||
|
// WorkDir.setBaseDir(opt.workdir);
|
||||||
if (opt.sigleInstance) initLock();
|
//
|
||||||
lockObtained = true;
|
// if (opt.sigleInstance) initLock();
|
||||||
|
// lockObtained = true;
|
||||||
for (final RouteSetup rs : opt.routeLists) {
|
//
|
||||||
WorkDir.registerRoutes(rs);
|
// for (final RouteSetup rs : opt.routeLists) {
|
||||||
}
|
// WorkDir.registerRoutes(rs);
|
||||||
WorkDir.addPath("_screenshot_dir", opt.screenshotDir);
|
// }
|
||||||
|
// WorkDir.addPath("_screenshot_dir", opt.screenshotDir);
|
||||||
// apply configurations
|
//
|
||||||
Config.init(WorkDir.getFile(opt.configFile), opt.configComment);
|
// // apply configurations
|
||||||
|
// Config.init(WorkDir.getFile(opt.configFile), opt.configComment);
|
||||||
// add keys to config
|
//
|
||||||
for (final KeySetup l : opt.keyLists) {
|
// // add keys to config
|
||||||
Config.registerKeys(l);
|
// for (final KeySetup l : opt.keyLists) {
|
||||||
}
|
// Config.registerKeys(l);
|
||||||
|
// }
|
||||||
// add options to config
|
//
|
||||||
for (final ConfigSetup c : opt.configLists) {
|
// // add options to config
|
||||||
Config.registerOptions(c);
|
// for (final ConfigSetup c : opt.configLists) {
|
||||||
}
|
// Config.registerOptions(c);
|
||||||
Config.load();
|
// }
|
||||||
|
// Config.load();
|
||||||
/*
|
//
|
||||||
* Display
|
// /*
|
||||||
*/
|
// * Display
|
||||||
Log.f2("Initializing Display System...");
|
// */
|
||||||
initDisplay(gfx());
|
// Log.f2("Initializing Display System...");
|
||||||
|
// initDisplay(gfx());
|
||||||
/*
|
//
|
||||||
* Audio
|
// /*
|
||||||
*/
|
// * Audio
|
||||||
Log.f2("Initializing Sound System...");
|
// */
|
||||||
soundSystem = new SoundSystem(this);
|
// Log.f2("Initializing Sound System...");
|
||||||
initSoundSystem(soundSystem);
|
// soundSystem = new SoundSystem(this);
|
||||||
|
// initSoundSystem(soundSystem);
|
||||||
/*
|
//
|
||||||
* Input
|
// /*
|
||||||
*/
|
// * Input
|
||||||
Log.f2("Initializing Input System...");
|
// */
|
||||||
inputSystem = new LwjglInputModule(this);
|
// Log.f2("Initializing Input System...");
|
||||||
initInputSystem(inputSystem);
|
// inputSystem = new LwjglInputModule(this);
|
||||||
|
// initInputSystem(inputSystem);
|
||||||
/*
|
//
|
||||||
* Prepare main loop
|
// /*
|
||||||
*/
|
// * Prepare main loop
|
||||||
Log.f1("Creating Screen Registry and Game Loop...");
|
// */
|
||||||
screenRegistry = new ScreenRegistry(this);
|
// Log.f1("Creating Screen Registry and Game Loop...");
|
||||||
gameLoop = createMainLoop();
|
// screenRegistry = new ScreenRegistry(this);
|
||||||
gameLoop.setRootRenderable(screenRegistry);
|
// gameLoop = createMainLoop();
|
||||||
|
// gameLoop.setRootRenderable(screenRegistry);
|
||||||
/*
|
//
|
||||||
* Load resources
|
// /*
|
||||||
*
|
// * Load resources
|
||||||
* Resources should be registered to registries, and AsyncResourceLoader will load them.
|
// *
|
||||||
*/
|
// * Resources should be registered to registries, and AsyncResourceLoader will load them.
|
||||||
Log.f1("Loading resources...");
|
// */
|
||||||
if (opt.resourceLoader != null) {
|
// Log.f1("Loading resources...");
|
||||||
opt.resourceLoader.setBaseDir(this);
|
// if (opt.resourceLoader != null) {
|
||||||
}
|
// opt.resourceLoader.setBaseDir(this);
|
||||||
|
// }
|
||||||
Res.setBaseDir(this);
|
//
|
||||||
|
// Res.setBaseDir(this);
|
||||||
for (final ResourceInitializer rl : opt.resourceLists) {
|
//
|
||||||
Res.load(rl);
|
// for (final ResourceInitializer rl : opt.resourceLists) {
|
||||||
}
|
// Res.load(rl);
|
||||||
|
// }
|
||||||
/*
|
//
|
||||||
* Screen registry
|
// /*
|
||||||
*
|
// * Screen registry
|
||||||
* Must be after resources, because screens can request them during instantiation.
|
// *
|
||||||
*/
|
// * Must be after resources, because screens can request them during instantiation.
|
||||||
Log.f2("Registering screens...");
|
// */
|
||||||
initScreens(screenRegistry);
|
// Log.f2("Registering screens...");
|
||||||
}
|
// initScreens(screenRegistry);
|
||||||
|
// }
|
||||||
|
//
|
||||||
/**
|
//
|
||||||
* Register game screens to the registry.
|
// /**
|
||||||
*
|
// * Register game screens to the registry.
|
||||||
* @param screens
|
// *
|
||||||
*/
|
// * @param screens
|
||||||
protected void initScreens(ScreenRegistry screens)
|
// */
|
||||||
{
|
// protected void initScreens(ScreenRegistry screens)
|
||||||
screens.addOverlay(new CrossfadeOverlay(this));
|
// {
|
||||||
}
|
// screens.addOverlay(new CrossfadeOverlay(this));
|
||||||
|
// }
|
||||||
|
//
|
||||||
/**
|
//
|
||||||
* Create game loop instance
|
// /**
|
||||||
*
|
// * Create game loop instance
|
||||||
* @return the game loop.
|
// *
|
||||||
*/
|
// * @return the game loop.
|
||||||
protected MainLoop createMainLoop()
|
// */
|
||||||
{
|
// protected MainLoop createMainLoop()
|
||||||
return new MainLoop(this);
|
// {
|
||||||
}
|
// return new DeltaMainLoop(this);
|
||||||
|
// }
|
||||||
|
//
|
||||||
protected void beforeShutdown()
|
//
|
||||||
{
|
// protected void beforeShutdown()
|
||||||
// ???
|
// {
|
||||||
if (lockObtained) Config.save();
|
// // ???
|
||||||
}
|
// if (lockObtained) Config.save();
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
|
|||||||
@@ -31,12 +31,16 @@ public class App extends BusNode {
|
|||||||
private final AppBackend backend;
|
private final AppBackend backend;
|
||||||
private final EventBus eventBus = new EventBus();
|
private final EventBus eventBus = new EventBus();
|
||||||
private boolean started = false;
|
private boolean started = false;
|
||||||
|
private boolean inited = false;
|
||||||
|
|
||||||
/** List of installed App plugins */
|
/** List of installed App plugins */
|
||||||
protected final DelegatingList plugins = new DelegatingList();
|
protected final DelegatingList plugins = new DelegatingList();
|
||||||
/** List of initializers */
|
/** List of initializers */
|
||||||
protected final List<InitTask> initializers = new ArrayList<>();
|
protected final List<InitTask> initializers = new ArrayList<>();
|
||||||
|
|
||||||
|
/** The used main loop instance */
|
||||||
|
protected MainLoop mainLoop;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an app with given backend.
|
* Create an app with given backend.
|
||||||
@@ -100,6 +104,17 @@ public class App extends BusNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the main loop implementation
|
||||||
|
*
|
||||||
|
* @param loop main loop impl
|
||||||
|
*/
|
||||||
|
public void setMainLoop(MainLoop loop)
|
||||||
|
{
|
||||||
|
this.mainLoop = loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current backend
|
* Get current backend
|
||||||
*
|
*
|
||||||
@@ -123,11 +138,31 @@ public class App extends BusNode {
|
|||||||
}
|
}
|
||||||
started = true;
|
started = true;
|
||||||
|
|
||||||
|
Log.i("Starting init...");
|
||||||
|
init();
|
||||||
|
|
||||||
|
if (mainLoop == null) {
|
||||||
|
throw new IllegalStateException("The app has no main loop assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i("Starting main loop...");
|
||||||
|
mainLoop.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (inited) {
|
||||||
|
throw new IllegalStateException("Already inited.");
|
||||||
|
}
|
||||||
|
inited = true;
|
||||||
|
|
||||||
// pre-init hook, just in case anyone wanted to have one.
|
// pre-init hook, just in case anyone wanted to have one.
|
||||||
Log.f2("Calling pre-init hook...");
|
Log.f2("Calling pre-init hook...");
|
||||||
preInit();
|
preInit();
|
||||||
|
|
||||||
Log.i("=== Starting initialization sequence ===");
|
Log.f2("Running init tasks...");
|
||||||
|
|
||||||
// sort initializers by order.
|
// sort initializers by order.
|
||||||
final List<InitTask> orderedInitializers = InitTask.inOrder(initializers);
|
final List<InitTask> orderedInitializers = InitTask.inOrder(initializers);
|
||||||
@@ -149,8 +184,6 @@ public class App extends BusNode {
|
|||||||
initTask.after();
|
initTask.after();
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i("=== Initialization sequence completed ===");
|
|
||||||
|
|
||||||
// user can now start the main loop etc.
|
// user can now start the main loop etc.
|
||||||
Log.f2("Calling post-init hook...");
|
Log.f2("Calling post-init hook...");
|
||||||
postInit();
|
postInit();
|
||||||
|
|||||||
@@ -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,43 +1,17 @@
|
|||||||
package mightypork.gamecore.core;
|
package mightypork.gamecore.core;
|
||||||
|
|
||||||
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
|
||||||
|
|
||||||
import mightypork.gamecore.graphics.Renderable;
|
import mightypork.gamecore.graphics.Renderable;
|
||||||
import mightypork.gamecore.gui.screens.ScreenRegistry;
|
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.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)
|
* @author Ondřej Hruška (MightyPork)
|
||||||
*/
|
*/
|
||||||
public class MainLoop extends BusNode implements Destroyable {
|
public interface MainLoop extends 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;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set primary renderable
|
* Set primary renderable
|
||||||
@@ -45,99 +19,27 @@ public class MainLoop extends BusNode implements Destroyable {
|
|||||||
* @param rootRenderable main {@link Renderable}, typically a
|
* @param rootRenderable main {@link Renderable}, typically a
|
||||||
* {@link ScreenRegistry}
|
* {@link ScreenRegistry}
|
||||||
*/
|
*/
|
||||||
public void setRootRenderable(Renderable rootRenderable)
|
public abstract void setRootRenderable(Renderable rootRenderable);
|
||||||
{
|
|
||||||
this.rootRenderable = rootRenderable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the loop
|
* Start the loop. The loop should be terminated when the destroy() method
|
||||||
|
* is called.
|
||||||
*/
|
*/
|
||||||
public void start()
|
public abstract 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
|
@Override
|
||||||
public void destroy()
|
public abstract void destroy();
|
||||||
{
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a task to queue to be executed in the main loop (in rendering
|
* 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 task task
|
||||||
* @param skipQueue true to skip the queue
|
* @param skipQueue true to skip the queue
|
||||||
*/
|
*/
|
||||||
public synchronized void queueTask(Runnable task, boolean skipQueue)
|
public abstract void queueTask(Runnable task, boolean skipQueue);
|
||||||
{
|
|
||||||
if (skipQueue) {
|
|
||||||
tasks.addFirst(task);
|
|
||||||
} else {
|
|
||||||
tasks.addLast(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ package mightypork.gamecore.core.config;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import mightypork.gamecore.core.events.ShutdownEvent;
|
||||||
|
import mightypork.gamecore.core.events.ShutdownListener;
|
||||||
import mightypork.gamecore.input.Key;
|
import mightypork.gamecore.input.Key;
|
||||||
import mightypork.gamecore.input.KeyStroke;
|
import mightypork.gamecore.input.KeyStroke;
|
||||||
import mightypork.utils.config.propmgr.Property;
|
import mightypork.utils.config.propmgr.Property;
|
||||||
@@ -19,7 +21,7 @@ import mightypork.utils.logging.Log;
|
|||||||
*
|
*
|
||||||
* @author Ondřej Hruška (MightyPork)
|
* @author Ondřej Hruška (MightyPork)
|
||||||
*/
|
*/
|
||||||
public class Config {
|
public class Config implements ShutdownListener {
|
||||||
|
|
||||||
/** Array of configs registered for the app */
|
/** Array of configs registered for the app */
|
||||||
protected static Map<String, Config> configs = new HashMap<>();
|
protected static Map<String, Config> configs = new HashMap<>();
|
||||||
@@ -265,4 +267,11 @@ public class Config {
|
|||||||
|
|
||||||
kp.getValue().setTo(key, mod);
|
kp.getValue().setTo(key, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onShutdown(ShutdownEvent event)
|
||||||
|
{
|
||||||
|
save(); // save changes done to the config
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package mightypork.gamecore.graphics.fonts;
|
package mightypork.gamecore.graphics.fonts;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Font style enum
|
* Font style enum
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user